From 740ca40c5cf968a4cc3b4cc6b08a177c3185ee72 Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Sat, 24 May 2025 15:41:45 -0700 Subject: [PATCH] Lots of changes --- .../src/NewApp/Components/BackstoryLayout.tsx | 35 ++- .../src/NewApp/Components/CandidateInfo.tsx | 56 ++-- .../NewApp/Components/streamQueryResponse.tsx | 2 - .../src/NewApp/Pages/CandidateListingPage.tsx | 23 +- .../src/NewApp/Pages/GenerateCandidate.tsx | 60 ++-- src/server.py | 8 + src/utils/agents/persona_generator.py | 264 ++++++++++++------ src/utils/profile_image.py | 6 +- src/utils/user.py | 12 + users/eliza/info.json | 9 - .../jobs/plant-conrervation-specialist.md | 63 ----- .../rag-content/jobs/research-assistant.md | 75 ----- .../rag-content/jobs/restoration-botanist.md | 55 ---- .../resume/Eliza_Morgan_Resume.docx | Bin 38520 -> 0 bytes .../rag-content/resume/Eliza_Morgan_Resume.md | 94 ------- 15 files changed, 287 insertions(+), 475 deletions(-) delete mode 100644 users/eliza/info.json delete mode 100644 users/eliza/rag-content/jobs/plant-conrervation-specialist.md delete mode 100644 users/eliza/rag-content/jobs/research-assistant.md delete mode 100644 users/eliza/rag-content/jobs/restoration-botanist.md delete mode 100755 users/eliza/rag-content/resume/Eliza_Morgan_Resume.docx delete mode 100644 users/eliza/rag-content/resume/Eliza_Morgan_Resume.md diff --git a/frontend/src/NewApp/Components/BackstoryLayout.tsx b/frontend/src/NewApp/Components/BackstoryLayout.tsx index 64eb702..c969da7 100644 --- a/frontend/src/NewApp/Components/BackstoryLayout.tsx +++ b/frontend/src/NewApp/Components/BackstoryLayout.tsx @@ -1,20 +1,13 @@ -import React, { ReactElement, useEffect, useState, useRef, useCallback, createContext, useContext } from 'react'; +import React, { ReactElement, useEffect, useState } from 'react'; import { Outlet, useLocation, Routes } from "react-router-dom"; import { Box, Container, Paper } from '@mui/material'; import { useNavigate } from "react-router-dom"; -import DashboardIcon from '@mui/icons-material/Dashboard'; -import PersonIcon from '@mui/icons-material/Person'; import ChatIcon from '@mui/icons-material/Chat'; -import HistoryIcon from '@mui/icons-material/History'; import DescriptionIcon from '@mui/icons-material/Description'; -import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer'; import BarChartIcon from '@mui/icons-material/BarChart'; import SettingsIcon from '@mui/icons-material/Settings'; -import SearchIcon from '@mui/icons-material/Search'; -import BookmarkIcon from '@mui/icons-material/Bookmark'; import WorkIcon from '@mui/icons-material/Work'; import InfoIcon from '@mui/icons-material/Info'; -import BusinessIcon from '@mui/icons-material/Business'; import { SxProps, Theme } from '@mui/material'; @@ -22,7 +15,7 @@ import {Header} from './Header'; import { Scrollable } from '../../Components/Scrollable'; import { Footer } from './Footer'; import { Snack, SetSnackType } from '../../Components/Snack'; -import { UserProvider, useUser, UserInfo } from './UserContext'; +import { useUser, UserInfo } from './UserContext'; import { getBackstoryDynamicRoutes } from './BackstoryRoutes'; import { LoadingComponent } from "../Components/LoadingComponent"; @@ -100,9 +93,11 @@ const BackstoryPageContainer = (props : BackstoryPageContainerProps) => { sx={{ display: "flex", flexGrow: 1, - pt: 1, - pb: 1, - width: "100%", + p: { xs: 0, sm: 0.5 }, // Zero padding on mobile (xs), 0.5 on larger screens (sm and up) + mt: 0, + mb: 0, + // width: "100%", + maxWidth: { xs: '100%', md: '700px', lg: '1024px' }, ...sx }}> { sx={{ display: "flex", flexGrow: 1, - p: 3, - backgroundColor: 'background.paper', - borderRadius: 2, - minHeight: '80vh', - width: "100%", - flexDirection: "column", + p: 0.5, + backgroundColor: 'background.paper', + borderRadius: 0.5, + minHeight: '80vh', + maxWidth: { xs: '100%', md: '700px', lg: '1024px' }, + flexDirection: "column", }}> {children} @@ -165,8 +160,10 @@ const BackstoryLayout: React.FC<{ backgroundColor: "#D3CDBF", /* Warm Gray */ }}> ({ - display: "flex", - flexDirection: "column", - padding: theme.spacing(2), - marginBottom: theme.spacing(2), - borderRadius: theme.shape.borderRadius, - boxShadow: theme.shadows[2], -})); interface CandidateInfoProps { sessionId: string; @@ -37,7 +26,6 @@ const CandidateInfo: React.FC = (props: CandidateInfoProps) action = '', sessionId, } = props; - const location = useLocation(); const navigate = useNavigate(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); @@ -72,11 +60,20 @@ const CandidateInfo: React.FC = (props: CandidateInfoProps) - + = (props: CandidateInfoProps) - + - .MuiTypography-root": { m: 0 } }}> - {action !== '' && {action}} - + .MuiTypography-root": { m: 0 } + }}> + { + action !== '' && + {action} + } + {candidate.full_name} - /u/{candidate.username} + {`/u/${candidate.username}`} { event.stopPropagation() }} tooltip="Copy link" content={`${window.location.origin}/u/{candidate.username}`} /> @@ -134,4 +148,4 @@ const CandidateInfo: React.FC = (props: CandidateInfoProps) ); }; -export { CandidateInfo }; \ No newline at end of file +export { CandidateInfo }; diff --git a/frontend/src/NewApp/Components/streamQueryResponse.tsx b/frontend/src/NewApp/Components/streamQueryResponse.tsx index eadbb4f..daa790f 100644 --- a/frontend/src/NewApp/Components/streamQueryResponse.tsx +++ b/frontend/src/NewApp/Components/streamQueryResponse.tsx @@ -30,8 +30,6 @@ const streamQueryResponse = (options: StreamQueryOptions) => { const run = async () => { query.prompt = query.prompt.trim(); - if (!query.prompt) return; - let data: any = query; if (type === "job_description") { data = { diff --git a/frontend/src/NewApp/Pages/CandidateListingPage.tsx b/frontend/src/NewApp/Pages/CandidateListingPage.tsx index 4f70c0a..3aa2589 100644 --- a/frontend/src/NewApp/Pages/CandidateListingPage.tsx +++ b/frontend/src/NewApp/Pages/CandidateListingPage.tsx @@ -1,27 +1,16 @@ -import React, { forwardRef, useEffect, useState, MouseEventHandler } from 'react'; +import React, { useEffect, useState } from 'react'; import { useNavigate } from "react-router-dom"; import Button from '@mui/material/Button'; -import useMediaQuery from '@mui/material/useMediaQuery'; import Box from '@mui/material/Box'; -import { useTheme } from '@mui/material/styles'; -import MuiMarkdown from 'mui-markdown'; import { BackstoryPageProps } from '../../Components/BackstoryTab'; -import { Conversation, ConversationHandle } from '../Components/Conversation'; -import { ChatQuery, Tunables } from '../../Components/ChatQuery'; -import { MessageList } from '../../Components/Message'; import { CandidateInfo } from 'NewApp/Components/CandidateInfo'; import { connectionBase } from '../../Global'; -import { LoadingComponent } from 'NewApp/Components/LoadingComponent'; -import { useUser, UserInfo } from "../Components/UserContext"; -import { Navigate } from 'react-router-dom'; +import { UserInfo } from "../Components/UserContext"; const CandidateListingPage = (props: BackstoryPageProps) => { const navigate = useNavigate(); - const { sessionId, setSnack, submitQuery } = props; - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('md')); - const { user } = useUser(); + const { sessionId, setSnack } = props; const [users, setUsers] = useState(undefined); useEffect(() => { @@ -60,15 +49,15 @@ const CandidateListingPage = (props: BackstoryPageProps) => { }; fetchUsers(); - }, [users]); + }, [users, sessionId, setSnack]); return ( - + Not seeing a candidate you like? diff --git a/frontend/src/NewApp/Pages/GenerateCandidate.tsx b/frontend/src/NewApp/Pages/GenerateCandidate.tsx index e62b62c..60792cf 100644 --- a/frontend/src/NewApp/Pages/GenerateCandidate.tsx +++ b/frontend/src/NewApp/Pages/GenerateCandidate.tsx @@ -10,6 +10,7 @@ import SendIcon from '@mui/icons-material/Send'; import PropagateLoader from 'react-spinners/PropagateLoader'; import { CandidateInfo } from '../Components/CandidateInfo'; import { Query } from '../../Components/ChatQuery' +import { Quote } from 'NewApp/Components/Quote'; import { streamQueryResponse, StreamQueryController } from '../Components/streamQueryResponse'; import { connectionBase } from 'Global'; import { UserInfo } from '../Components/UserContext'; @@ -18,8 +19,6 @@ import { BackstoryTextField, BackstoryTextFieldRef } from 'Components/BackstoryT import { jsonrepair } from 'jsonrepair'; import { StyledMarkdown } from 'Components/StyledMarkdown'; import { Scrollable } from 'Components/Scrollable'; -import { useForkRef } from '@mui/material'; -import { BackstoryMessage } from 'Components/Message'; import { Pulse } from 'NewApp/Components/Pulse'; const emptyUser : UserInfo = { @@ -44,7 +43,7 @@ const GenerateCandidate = (props: BackstoryElementProps) => { const {sessionId, setSnack, submitQuery} = props; const [streaming, setStreaming] = useState(''); const [processing, setProcessing] = useState(false); - const [user, setUser] = useState(emptyUser); + const [user, setUser] = useState(null); const [prompt, setPrompt] = useState(''); const [resume, setResume] = useState(''); const [canGenImage, setCanGenImage] = useState(false); @@ -156,8 +155,8 @@ const GenerateCandidate = (props: BackstoryElementProps) => { // Effect to trigger profile generation when user data is ready useEffect(() => { - console.log("useEffect triggered - shouldGenerateProfile:", shouldGenerateProfile, "user:", user.username, user.first_name); - if (shouldGenerateProfile && user.username !== "[blank]" && user.first_name !== "[blank]") { + console.log("useEffect triggered - shouldGenerateProfile:", shouldGenerateProfile, "user:", user?.username, user?.first_name); + if (shouldGenerateProfile && user?.username !== "[blank]" && user?.first_name !== "[blank]") { console.log("Triggering profile generation with updated user data:", user); if (controllerRef.current) { console.log("Controller already active, skipping profile generation"); @@ -165,12 +164,12 @@ const GenerateCandidate = (props: BackstoryElementProps) => { } // Don't generate if we still have blank user data - if (user.username === "[blank]" || user.first_name === "[blank]") { + if (user?.username === "[blank]" || user?.first_name === "[blank]") { console.log("Cannot generate profile: user data not ready"); return; } - const imagePrompt = `A photorealistic profile picture of a ${user.age} year old ${user.gender} ${user.ethnicity} person. ${prompt}` + const imagePrompt = `A photorealistic profile picture of a ${user?.age} year old ${user?.gender?.toLocaleLowerCase()} ${user?.ethnicity?.toLocaleLowerCase()} person. ${prompt}` setStatus('Staring image generation...'); setProcessing(true); setCanGenImage(false); @@ -181,7 +180,7 @@ const GenerateCandidate = (props: BackstoryElementProps) => { query: { prompt: imagePrompt, agent_options: { - username: user.username, + username: user?.username, filename: "profile.png" } }, @@ -199,7 +198,10 @@ const GenerateCandidate = (props: BackstoryElementProps) => { setState(0); setCanGenImage(true); setShouldGenerateProfile(false); - setUser({ ...user, has_profile: true }); + setUser({ + ...(user ? user : emptyUser), + has_profile: true + }); } break; case "error": @@ -268,7 +270,8 @@ const GenerateCandidate = (props: BackstoryElementProps) => { display: "flex", flexDirection: "column", flexGrow: 1, - gap: 1, width: { xs: '100%', md: '700px', lg: '1024px' } + gap: 1, + maxWidth: { xs: '100%', md: '700px', lg: '1024px' }, }}> {user && { { prompt && Persona prompt - {prompt} + } {processing && @@ -302,28 +305,35 @@ const GenerateCandidate = (props: BackstoryElementProps) => { } - - + + - {processing && } + {processing && } - - - + {user?.has_profile ? 'Re-': ''}Generate Picture + @@ -349,7 +359,7 @@ const GenerateCandidate = (props: BackstoryElementProps) => { variant="contained" disabled={sessionId === undefined || processing} onClick={handleSendClick}> - Send + Generate New Persona diff --git a/src/server.py b/src/server.py index f0096bb..dc10250 100644 --- a/src/server.py +++ b/src/server.py @@ -583,6 +583,10 @@ class WebServer: "full_name": user.full_name, "description": user.description, "contact_info": user.contact_info, + "title": user.title, + "phone": user.phone, + "location": user.location, + "email": user.email, "rag_content_size": user.rag_content_size, "has_profile": user.has_profile, "questions": [ q.model_dump(mode='json') for q in user.user_questions], @@ -826,6 +830,10 @@ class WebServer: "first_name": user.first_name, "last_name": user.last_name, "full_name": user.full_name, + "title": user.title, + "phone": user.phone, + "location": user.location, + "email": user.email, "description": user.description, "contact_info": user.contact_info, "rag_content_size": user.rag_content_size, diff --git a/src/utils/agents/persona_generator.py b/src/utils/agents/persona_generator.py index ad6de57..3e697b3 100644 --- a/src/utils/agents/persona_generator.py +++ b/src/utils/agents/persona_generator.py @@ -6,6 +6,7 @@ from typing import ( ClassVar, cast, Any, + Tuple, AsyncGenerator, List, Optional @@ -63,6 +64,9 @@ You will be provided with defaults to use if not specified by the user: "age": number, "gender": "male" | "female", "ethnicity": string, +"full_name": string, +"first_name": string, +"last_name": string, } ``` @@ -77,8 +81,6 @@ Provide all information in English ONLY, with no other commentary: ```json { "username": string, # A likely-to-be unique username, no more than 15 characters (can include numbers and letters but no special characters) -"first_name": string, -"last_name": string, "description": string, # One to two sentence description of their job "location": string, # In the location, provide ALL of: City, State/Region, and Country "phone": string, # Location appropriate phone number with area code @@ -115,9 +117,25 @@ Use that information to invent a full career resume. Include sections such as: Provide the resume in Markdown format. DO NOT provide any commentary before or after the resume. """ +# Debug version to identify the issue +import random +import logging + +# Set up logging to see what's happening +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + class EthnicNameGenerator: def __init__(self): - self.nd = NameDataset() + try: + from names_dataset import NameDataset # type: ignore + self.nd = NameDataset() + except ImportError: + logger.error("NameDataset not available. Please install: pip install names-dataset") + self.nd = None + except Exception as e: + logger.error(f"Error initializing NameDataset: {e}") + self.nd = None # US Census 2020 approximate ethnic distribution self.ethnic_weights = { @@ -130,89 +148,136 @@ class EthnicNameGenerator: 'Mixed/Other': 0.026 } - # Map ethnicities to name origins/countries that approximate those populations - self.ethnic_name_mapping = { - 'White': ['United States', 'United Kingdom', 'Germany', 'Ireland', 'Italy', 'Poland'], - 'Hispanic': ['Mexico', 'Spain', 'Colombia', 'Peru', 'Argentina', 'Cuba', 'Puerto Rico'], - 'Black': ['United States'], # African American names - 'Asian': ['China', 'India', 'Philippines', 'Vietnam', 'Korea', 'Japan'], - 'Native American': ['United States'], # Limited options in dataset - 'Pacific Islander': ['United States'], # Limited options in dataset - 'Mixed/Other': ['United States'] # Default to US names + # Map ethnicities to countries (using alpha-2 codes that NameDataset uses) + self.ethnic_country_mapping = { + 'White': ['US', 'GB', 'DE', 'IE', 'IT', 'PL', 'FR', 'CA', 'AU'], + 'Hispanic': ['MX', 'ES', 'CO', 'PE', 'AR', 'CU', 'VE', 'CL'], + 'Black': ['US'], # African American names + 'Asian': ['CN', 'IN', 'PH', 'VN', 'KR', 'JP', 'TH', 'MY'], + 'Native American': ['US'], + 'Pacific Islander': ['US'], + 'Mixed/Other': ['US'] } - def get_weighted_ethnicity(self): + def get_weighted_ethnicity(self) -> str: """Select ethnicity based on US demographic weights""" ethnicities = list(self.ethnic_weights.keys()) weights = list(self.ethnic_weights.values()) return random.choices(ethnicities, weights=weights)[0] - def get_name_by_ethnicity(self, ethnicity, gender='random'): - """Generate a name based on ethnicity""" + def get_names_by_criteria(self, countries: List[str], gender: Optional[str] = None, + n: int = 50, use_first_names: bool = True) -> List[str]: + """Get names matching criteria using NameDataset's get_top_names method""" + if not self.nd: + return [] + + all_names = [] + for country_code in countries: + try: + # Get top names for this country + top_names = self.nd.get_top_names( + n=n, + use_first_names=use_first_names, + country_alpha2=country_code, + gender=gender + ) + + if country_code in top_names: + if use_first_names and gender: + # For first names with gender specified + gender_key = 'M' if gender.upper() in ['M', 'MALE'] else 'F' + if gender_key in top_names[country_code]: + all_names.extend(top_names[country_code][gender_key]) + elif use_first_names: + # For first names without gender (get both) + for gender_names in top_names[country_code].values(): + all_names.extend(gender_names) + else: + # For last names + all_names.extend(top_names[country_code]) + + except Exception as e: + logger.debug(f"Error getting names for {country_code}: {e}") + continue + + return list(set(all_names)) # Remove duplicates + + def get_name_by_ethnicity(self, ethnicity: str, gender: str = 'random') -> Tuple[str, str, str, str]: + """Generate a name based on ethnicity using the correct NameDataset API""" if gender == 'random': gender = random.choice(['Male', 'Female']) - countries = self.ethnic_name_mapping.get(ethnicity, ['United States']) + countries = self.ethnic_country_mapping.get(ethnicity, ['US']) - # Try to get names from the mapped countries - first_name = None - last_name = None + # Get first names + first_names = self.get_names_by_criteria( + countries=countries, + gender=gender, + use_first_names=True + ) - for country in countries: - try: - # Get first names - if country in self.nd.first_names: - country_first_names = self.nd.first_names[country] - if gender.lower() in country_first_names: - gender_names = country_first_names[gender.lower()] - if gender_names: - first_name = random.choice(gender_names) - break - - except (KeyError, AttributeError): - continue + # Get last names + last_names = self.get_names_by_criteria( + countries=countries, + use_first_names=False + ) - # Fallback to US names if no ethnicity-specific name found - if not first_name: - try: - us_names = self.nd.first_names.get('United States', {}) - gender_names = us_names.get(gender.lower(), []) - if gender_names: - first_name = random.choice(gender_names) - else: - # Ultimate fallback - first_name = random.choice(['John', 'Jane', 'Michael', 'Sarah']) - except: - first_name = random.choice(['John', 'Jane', 'Michael', 'Sarah']) + # Select names or use fallbacks + if first_names: + first_name = random.choice(first_names) + else: + first_name = self._get_fallback_first_name(gender, ethnicity) + logger.info(f"Using fallback first name for {ethnicity} {gender}") - # Get last name - for country in countries: - try: - if country in self.nd.last_names and self.nd.last_names[country]: - last_name = random.choice(self.nd.last_names[country]) - break - except (KeyError, AttributeError): - continue - - # Fallback for last name - if not last_name: - try: - us_last_names = self.nd.last_names.get('United States', []) - if us_last_names: - last_name = random.choice(us_last_names) - else: - last_name = random.choice(['Smith', 'Johnson', 'Williams', 'Brown']) - except: - last_name = random.choice(['Smith', 'Johnson', 'Williams', 'Brown']) + if last_names: + last_name = random.choice(last_names) + else: + last_name = self._get_fallback_last_name(ethnicity) + logger.info(f"Using fallback last name for {ethnicity}") return first_name, last_name, ethnicity, gender - def generate_random_name(self, gender='random'): + def _get_fallback_first_name(self, gender: str, ethnicity: str) -> str: + """Provide culturally appropriate fallback first names""" + fallback_names = { + 'White': { + 'Male': ['James', 'Robert', 'John', 'Michael', 'William', 'David', 'Richard', 'Joseph'], + 'Female': ['Mary', 'Patricia', 'Jennifer', 'Linda', 'Elizabeth', 'Barbara', 'Susan', 'Jessica'] + }, + 'Hispanic': { + 'Male': ['José', 'Luis', 'Miguel', 'Juan', 'Francisco', 'Alejandro', 'Antonio', 'Carlos'], + 'Female': ['María', 'Guadalupe', 'Juana', 'Margarita', 'Francisca', 'Teresa', 'Rosa', 'Ana'] + }, + 'Black': { + 'Male': ['James', 'Robert', 'John', 'Michael', 'William', 'David', 'Richard', 'Charles'], + 'Female': ['Mary', 'Patricia', 'Linda', 'Elizabeth', 'Barbara', 'Susan', 'Jessica', 'Sarah'] + }, + 'Asian': { + 'Male': ['Wei', 'Ming', 'Chen', 'Li', 'Kumar', 'Raj', 'Hiroshi', 'Takeshi'], + 'Female': ['Mei', 'Lin', 'Ling', 'Priya', 'Yuki', 'Soo', 'Hana', 'Anh'] + } + } + + ethnicity_names = fallback_names.get(ethnicity, fallback_names['White']) + return random.choice(ethnicity_names.get(gender, ethnicity_names['Male'])) + + def _get_fallback_last_name(self, ethnicity: str) -> str: + """Provide culturally appropriate fallback last names""" + fallback_surnames = { + 'White': ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Wilson', 'Moore'], + 'Hispanic': ['García', 'Rodríguez', 'Martínez', 'López', 'González', 'Pérez', 'Sánchez', 'Ramírez'], + 'Black': ['Johnson', 'Williams', 'Brown', 'Jones', 'Davis', 'Miller', 'Wilson', 'Moore'], + 'Asian': ['Li', 'Wang', 'Zhang', 'Liu', 'Chen', 'Yang', 'Huang', 'Zhao'] + } + + return random.choice(fallback_surnames.get(ethnicity, fallback_surnames['White'])) + + def generate_random_name(self, gender: str = 'random') -> Tuple[str, str, str, str]: """Generate a random name with ethnicity based on US demographics""" ethnicity = self.get_weighted_ethnicity() return self.get_name_by_ethnicity(ethnicity, gender) - def generate_multiple_names(self, count=10, gender='random'): + def generate_multiple_names(self, count: int = 10, gender: str = 'random') -> List[Dict]: """Generate multiple random names""" names = [] for _ in range(count): @@ -225,18 +290,19 @@ class EthnicNameGenerator: 'gender': actual_gender }) return names - + class PersonaGenerator(Agent): agent_type: Literal["persona"] = "persona" # type: ignore _agent_type: ClassVar[str] = agent_type # Add this for registration agent_persist: bool = False system_prompt: str = generate_persona_system_prompt - age: int = Field(default_factory=lambda: random.randint(22, 67)) - gender: str = Field(default_factory=lambda: random.choice(["male", "female"])) + age: int = 0 + gender: str = "" username: str = "" first_name: str = "" last_name: str = "" + full_name: str = "" ethnicity: str = "" generator: Any = Field(default=EthnicNameGenerator(), exclude=True) @@ -247,6 +313,8 @@ class PersonaGenerator(Agent): self.age = random.randint(22, 67) # Use random.choices with explicit type casting to satisfy Literal type self.first_name, self.last_name, self.ethnicity, self.gender = self.generator.generate_random_name() + self.full_name = f"{self.first_name} {self.last_name}" + async def prepare_message(self, message: Message) -> AsyncGenerator[Message, None]: logger.info(f"{self.agent_type} - {inspect.stack()[0].function}") @@ -258,18 +326,6 @@ class PersonaGenerator(Agent): message.tunables.enable_rag = False message.tunables.enable_context = False - self.randomize() - - message.prompt = f"""\ -```json -{json.dumps({ - "age": self.age, - "gender": self.gender, - "ethnicity": self.ethnicity -})} -``` -{message.prompt} -""" message.status = "done" yield message return @@ -283,7 +339,6 @@ class PersonaGenerator(Agent): self.llm = llm self.model = model - original_prompt = message.prompt spinner: List[str] = ["\\", "|", "/", "-"] tick: int = 0 @@ -302,12 +357,33 @@ class PersonaGenerator(Agent): self.context.processing = True try: + self.randomize() + + original_prompt = message.prompt + + prompt = f"""\ +```json +{json.dumps({ +"age": self.age, +"gender": self.gender, +"ethnicity": self.ethnicity, +"full_name": self.full_name, +"first_name": self.first_name, +"last_name": self.last_name, +})} +``` +""" + + if original_prompt: + prompt += f""" +Incorporate the following into the job description: {original_prompt} +""" # # Generate the persona # async for message in self.call_llm( - message=message, system_prompt=self.system_prompt, prompt=original_prompt + message=message, system_prompt=self.system_prompt, prompt=prompt ): if message.status != "done": yield message @@ -316,13 +392,16 @@ class PersonaGenerator(Agent): json_str = self.extract_json_from_text(message.response) try: - persona = json.loads(json_str) | { - "age": self.age, - "gender": self.gender, - "ethnicity": self.ethnicity - } - if not persona.get("full_name", None): - persona["full_name"] = f"{persona['first_name']} {persona['last_name']}" + persona = { + "age": self.age, + "ethnicity": self.ethnicity, + "age": self.age, + "gender": self.gender, + "ethnicity": self.ethnicity, + "full_name": self.full_name, + "first_name": self.first_name, + "last_name": self.last_name, + } | json.loads(json_str) self.username = persona.get("username", None) if not self.username: raise ValueError("LLM did not generate a username") @@ -375,6 +454,9 @@ class PersonaGenerator(Agent): }} ``` """ + if original_prompt: + prompt += f""" +Make sure at least one of the candidate's job descriptions take into account the following: {original_prompt}.""" try: async for message in self.call_llm( message=message, system_prompt=generate_resume_system_prompt, prompt=prompt @@ -442,13 +524,11 @@ class PersonaGenerator(Agent): logger.error(message.response) message.response = f"Error in persona generation: {str(e)}" logger.error(message.response) - self.randomize() # Randomize for next generation yield message return # Done processing, add message to conversation self.context.processing = False - self.randomize() # Randomize for next generation # Return the final message yield message return @@ -461,7 +541,7 @@ class PersonaGenerator(Agent): LLMMessage(role="user", content=prompt), ] message.metadata.options = { - "seed": 8911, + "seed": int(time.time()), "num_ctx": self.context_size, "temperature": temperature, # Higher temperature to encourage tool usage } diff --git a/src/utils/profile_image.py b/src/utils/profile_image.py index b33acdf..e40d71d 100644 --- a/src/utils/profile_image.py +++ b/src/utils/profile_image.py @@ -61,7 +61,7 @@ def flux_worker(pipe: Any, params: ImageRequest, status_queue: queue.Queue, task estimated_gen_time = estimates["per_step"] * params.iterations * resolution_scale status_queue.put({ "status": "running", - "message": f"Starting Flux image generation with {params.iterations} inference steps", + "message": f"Initializing image generation...", "estimated_time_remaining": estimated_gen_time, "progress": 0 }) @@ -248,10 +248,10 @@ async def generate_image(message: Message, request: ImageRequest) -> AsyncGenera # Initialize or get cached pipeline start_time = time.time() - yield status(message, f"Loading {model_type} model: {request.model}") + yield status(message, f"Loading generative image model...") pipe = await model_cache.get_pipeline(request.model, device) load_time = time.time() - start_time - yield status(message, f"Model loaded in {load_time:.1f} seconds. Generating image with {request.iterations} inference steps", progress=10) + yield status(message, f"Model loaded in {load_time:.1f} seconds.", progress=10) async for update in async_generate_image(pipe, request): message.status = update.get("status", "thinking") diff --git a/src/utils/user.py b/src/utils/user.py index 5172fba..927b28c 100644 --- a/src/utils/user.py +++ b/src/utils/user.py @@ -41,6 +41,10 @@ class User(BaseModel): description: str = "" rag_content_size : int = 0 contact_info : Dict[str, str] = {} + title: str = "" + phone: str = "" + location: str = "" + email: str = "" user_questions : List[Question] = [] has_profile: bool = False is_ai: bool = False @@ -177,6 +181,10 @@ class User(BaseModel): sanitized["username"] = user.get("username", "default") sanitized["first_name"] = user.get("first_name", sanitized["username"]) sanitized["last_name"] = user.get("last_name", "") + sanitized["title"] = user.get("title", "") + sanitized["phone"] = user.get("phone", "") + sanitized["location"] = user.get("location", "") + sanitized["email"] = user.get("email", "") sanitized["full_name"] = user.get("full_name", f"{sanitized["first_name"]} {sanitized["last_name"]}") sanitized["description"] = user.get("description", "") profile_image = os.path.join(defines.user_dir, sanitized["username"], "profile.png") @@ -276,6 +284,10 @@ class User(BaseModel): self.last_name = info.get("last_name", "") self.full_name = info.get("full_name", f"{self.first_name} {self.last_name}") self.description = info.get("description", "") + self.title = info.get("title", "") + self.phone = info.get("phone", "") + self.email = info.get("email", "") + self.location = info.get("location", "") self.contact_info = info.get("contact_info", {}) profile_image = os.path.join(defines.user_dir, self.username, "profile.png") self.has_profile = os.path.exists(profile_image) diff --git a/users/eliza/info.json b/users/eliza/info.json deleted file mode 100644 index 4411cef..0000000 --- a/users/eliza/info.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "first_name": "Eliza", - "last_name": "Morgan", - "description": "Eliza Morgan is an AI generated persona. In addition, she is a conservation botanist with over a decade of experience in leading ecological restoration projects, managing native plant programs, and advancing rare plant propagation methods across the Pacific Northwest. Her proven record of scientific innovation, effective stakeholder engagement, and successful grant writing are key to her professional strengths.", - "questions": [ - "Is Eliza real?", - "What are Eliza's skills?" - ] -} diff --git a/users/eliza/rag-content/jobs/plant-conrervation-specialist.md b/users/eliza/rag-content/jobs/plant-conrervation-specialist.md deleted file mode 100644 index b3933b2..0000000 --- a/users/eliza/rag-content/jobs/plant-conrervation-specialist.md +++ /dev/null @@ -1,63 +0,0 @@ -# Plant Conservation Specialist - -**Organization:** Oregon Botanical Gardens -**Location:** Portland, Oregon -**Duration:** April 2017 - May 2020 - -## Position Overview -As Plant Conservation Specialist at the Oregon Botanical Gardens, I managed the institution's ex-situ conservation program for rare and endangered plant species native to the Pacific Northwest. This position bridged scientific research, hands-on horticulture, and public education. - -## Key Responsibilities - -### Ex-situ Conservation Program -- Coordinated conservation collections for 45 rare and endangered plant species -- Developed and maintained comprehensive database of accession records, phenology data, and propagation histories -- Established genetic management protocols to ensure maximum diversity in conservation collections -- Collaborated with Center for Plant Conservation on national rare plant conservation initiatives - -### Propagation & Cultivation -- Designed specialized growing environments for challenging species with specific habitat requirements -- Experimented with various propagation techniques including tissue culture, specialized seed treatments, and vegetative methods -- Maintained detailed documentation of successful and unsuccessful propagation attempts -- Achieved first-ever successful cultivation of three critically endangered Oregon wildflowers - -### Reintroduction Planning -- Collaborated with federal and state agencies on plant reintroduction strategies -- Conducted site assessments to evaluate habitat suitability for reintroductions -- Developed monitoring protocols to track survival and reproduction of reintroduced populations -- Prepared detailed reintroduction plans for 8 endangered species - -### Research Projects -- Designed and implemented germination studies for 15 rare species with unknown propagation requirements -- Conducted pollination biology investigations for several endangered plant species -- Collaborated with university researchers on seed viability and longevity studies -- Maintained comprehensive records of phenological patterns across multiple growing seasons - -### Education & Outreach -- Developed educational materials explaining the importance of plant conservation -- Led specialized tours focusing on rare plant conservation for visitors and donors -- Trained volunteers in proper care of sensitive plant collections -- Created interpretive signage for conservation garden displays - -## Notable Projects - -1. **Willamette Valley Prairie Species Recovery** - - Established seed bank of 25 declining prairie species - - Developed germination protocols that improved propagation success from 30% to 75% - - Produced over 5,000 plants for restoration projects throughout the region - -2. **Alpine Rare Plant Conservation Initiative** - - Created specialized growing facilities mimicking alpine conditions - - Successfully propagated 8 high-elevation rare species never before cultivated - - Documented critical temperature and moisture requirements for germination - -3. **Serpentine Soils Conservation Collection** - - Developed custom soil mixes replicating challenging serpentine conditions - - Maintained living collection of 12 rare serpentine endemic species - - Created public display educating visitors about specialized plant adaptations - -## Achievements -- Received "Conservation Innovation Award" from the American Public Gardens Association (2019) -- Developed propagation protocol for Kincaid's lupine that doubled germination success rates -- Established Oregon Botanical Gardens' first dedicated conservation nursery facility -- Created seed banking protocols adopted by three other botanical institutions \ No newline at end of file diff --git a/users/eliza/rag-content/jobs/research-assistant.md b/users/eliza/rag-content/jobs/research-assistant.md deleted file mode 100644 index 92d82af..0000000 --- a/users/eliza/rag-content/jobs/research-assistant.md +++ /dev/null @@ -1,75 +0,0 @@ -# Research Assistant - -**Organization:** Institute for Applied Ecology -**Location:** Corvallis, Oregon -**Duration:** January 2015 - March 2017 - -## Position Overview -As Research Assistant at the Institute for Applied Ecology, I supported multiple research projects focused on native plant ecology and restoration techniques. This position provided foundational experience in applying scientific methods to practical conservation challenges. - -## Key Responsibilities - -### Field Surveys -- Conducted comprehensive botanical surveys in diverse ecosystems throughout western Oregon -- Documented population sizes, health metrics, and habitat conditions for threatened plant species -- Established long-term monitoring plots using standardized protocols -- Collected voucher specimens for herbarium collections following strict ethical guidelines -- Mapped plant populations using GPS and GIS technologies - -### Greenhouse Operations -- Assisted with propagation of native plants for restoration experiments and projects -- Maintained detailed records of seed treatments, germination rates, and growth parameters -- Implemented and monitored experimental growing conditions for research projects -- Managed irrigation systems and pest control for approximately 10,000 plants -- Prepared plant materials for outplanting at restoration sites - -### Data Collection & Analysis -- Collected vegetation data using quadrat, transect, and plot-based sampling methods -- Processed and organized large datasets for long-term monitoring studies -- Performed statistical analyses using R to assess restoration treatment effectiveness -- Created data visualization graphics for reports and publications -- Maintained research databases ensuring data quality and accessibility - -### Research Projects -- **Prairie Restoration Techniques:** - - Compared effectiveness of different site preparation methods on native plant establishment - - Monitored post-treatment recovery of native species diversity - - Documented invasive species response to various control techniques - -- **Rare Plant Demography:** - - Tracked population dynamics of three endangered Willamette Valley plant species - - Monitored individual plant survival, growth, and reproductive output - - Assessed impacts of management interventions on population trends - -- **Seed Viability Studies:** - - Tested germination requirements for 30+ native species - - Evaluated effects of smoke, scarification, and stratification on dormancy - - Documented optimal storage conditions for maintaining seed viability - -### Publication Support -- Co-authored three peer-reviewed publications on prairie restoration techniques -- Prepared figures, tables, and data appendices for manuscripts -- Conducted literature reviews on specialized ecological topics -- Assisted with manuscript revisions based on peer review feedback - -## Key Projects - -1. **Willamette Valley Wet Prairie Restoration** - - Implemented experimental plots testing 4 restoration techniques - - Collected 3 years of post-treatment vegetation data - - Documented successful establishment of 15 target native species - -2. **Endangered Butterfly Habitat Enhancement** - - Propagated host and nectar plants for Fender's blue butterfly habitat - - Monitored plant-insect interactions in restoration sites - - Assessed habitat quality improvements following restoration treatments - -3. **Native Seed Production Research** - - Tested cultivation methods for improving seed yields of 10 native species - - Documented pollination requirements for optimal seed production - - Developed harvest timing recommendations based on seed maturation patterns - -## Publications -- Johnson, T., **Morgan, E.**, et al. (2016). "Comparative effectiveness of site preparation techniques for prairie restoration." *Restoration Ecology*, 24(4), 472-481. -- Williams, R., **Morgan, E.**, & Smith, B. (2016). "Germination requirements of Willamette Valley wet prairie species." *Native Plants Journal*, 17(2), 99-112. -- **Morgan, E.**, Johnson, T., & Davis, A. (2017). "Long-term vegetation response to restoration treatments in degraded oak savanna." *Northwest Science*, 91(1), 27-39. \ No newline at end of file diff --git a/users/eliza/rag-content/jobs/restoration-botanist.md b/users/eliza/rag-content/jobs/restoration-botanist.md deleted file mode 100644 index 27a3b71..0000000 --- a/users/eliza/rag-content/jobs/restoration-botanist.md +++ /dev/null @@ -1,55 +0,0 @@ -# Senior Restoration Botanist - -**Organization:** Pacific Northwest Conservation Alliance -**Location:** Portland, Oregon -**Duration:** June 2020 - Present - -## Position Overview -As Senior Restoration Botanist at the Pacific Northwest Conservation Alliance, I lead complex restoration projects aimed at preserving endangered plant communities throughout the Cascade Range. This role combines technical botanical expertise with project management and leadership responsibilities. - -## Key Responsibilities - -### Project Leadership -- Design and implement comprehensive restoration plans for degraded ecosystems with emphasis on rare plant conservation -- Lead field operations across multiple concurrent restoration sites covering over 2,000 acres -- Establish measurable success criteria and monitoring protocols for all restoration projects -- Conduct regular site assessments to track progress and adapt management strategies - -### Native Plant Propagation -- Oversee native plant nursery operations producing 75,000+ plants annually -- Develop specialized propagation protocols for difficult-to-grow rare species -- Maintain detailed records of germination rates, growth metrics, and treatment effects -- Coordinate seed collection expeditions throughout diverse ecosystems of the Pacific Northwest - -### Team Management -- Supervise a core team of 5 field botanists and up to 12 seasonal restoration technicians -- Conduct staff training on plant identification, restoration techniques, and field safety -- Facilitate weekly team meetings and monthly progress reviews -- Mentor junior staff and provide professional development opportunities - -### Funding & Partnerships -- Secured $750,000 in grant funding for riparian habitat restoration projects -- Authored major sections of successful proposals to state and federal agencies -- Manage project budgets ranging from $50,000 to $250,000 -- Cultivate partnerships with government agencies, tribes, and conservation NGOs - -### Notable Projects -1. **Willamette Valley Prairie Restoration Initiative** - - Restored 350 acres of native prairie habitat - - Reintroduced 12 threatened plant species with 85% establishment success - - Developed innovative seeding techniques that increased native diversity by 40% - -2. **Mount Hood Meadow Rehabilitation** - - Led post-wildfire recovery efforts in alpine meadow ecosystems - - Implemented erosion control measures using native plant materials - - Achieved 90% reduction in invasive species cover within treatment areas - -3. **Columbia River Gorge Rare Plant Recovery** - - Established new populations of 5 federally listed plant species - - Developed habitat suitability models to identify optimal reintroduction sites - - Created monitoring protocols adopted by multiple conservation organizations - -## Achievements -- Received Excellence in Ecological Restoration Award from the Society for Ecological Restoration, Northwest Chapter (2023) -- Featured in Oregon Public Broadcasting documentary on native plant conservation (2022) -- Published 2 peer-reviewed articles on restoration techniques developed during project work \ No newline at end of file diff --git a/users/eliza/rag-content/resume/Eliza_Morgan_Resume.docx b/users/eliza/rag-content/resume/Eliza_Morgan_Resume.docx deleted file mode 100755 index f3e6832ce697c9b2fb3b30b49ecf2f11d478b13a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38520 zcmagEWmp{Bwl0hZcM0z9?(PJ43GVLh?h@P~xVyVU&|m?AyE~1`?X0!eIeVXT@Av(w zetK4oSH`IBIcnBa`~nVv4gvxK4MGLhpk1w8{4E6(1Y{fv1OyE@sv~M|=VEH-qOa=d zVCt;P;9+aioFb>NDuf(%`G%3oC_v;biiA|9qjTceN!yws;~y@27ujB{W78?p3~)Gq$QFjQSz zwh#6U2Po5S-5`%XYUrg%zIITG<`YO9Wp%tdD&a^dv@-uVWJGP@Ea&$wQU_J#lQGI& zT8sU0MYp#}e|M56(E#z>w`B$j^&IOg*p5K1^hkp%HHk2Trool(5Q@6_Bk){}4?Euc(^Yj^i!N=g zI8|TElVB+vuLn`Y?9;v=YRX1p-DIdtmah0}pOA;ahX?B7crt;fwl~!Vl3G}}%m9Fm#jz5qTyr9eJfhhh* z6+J1w2t5L;w*dwM0tfumcQUncW@PwttWKPg0cS=IxD*f{BQLdUQ4=d#(Gxq8D-!5S zpS&os^OGw1-P5fkrmKnBPjGm-Yi#x_s}OgYw!|h_!$@l}==)N?<|g%ZQ>XqKbSg|& zAhCz)PKcIGW;B2lQOK0yfx}FlNPu<&ZCHo8@nTlEMnp`X##RckETlXMC+UP88oJ*u?2eb2@{>QS)8Mam#R=p?}bMI}Stgc*yg2vOP*?M_tsa3-!Ip*<((G3w4Ogyos-LwryAPfS!zWO2cc~%ylDJ$=_jy;Ebt0 z?NYlre%TlL$E*&=<$oq9A`DK&5_p@>0uzJ+OpuAav7(c`gEOPCy_4ymn>;6RLVl15 zS^U9Ad_hh<@)j9dT$%>-BuYjKJ#e}Go{e43W+InkYIC>9Ry&{8jbM{^^wvROowKo*?0oKv{*9GL+rh)LFKo&YhhcH)q_ybt~ap!{HHAvA@rrmsJS` zeTQDtaoeyTDy|=0SlC&kcP0-IbTMgTtwVcjnc$*KGxW<||Bg87&Tk$Llau^#cWud> zl`M^3Dx3`=-61tc*E3ac)ad^q`HsDeNBhWm4e|NJ98r&Y-d*(lb>If~KkIV0x{7rQ ztcwi<2nh1O*TvAm;jgl&PS~w7A$PsdLg>0gS$+~$`kWZ_OL$gv9j7iayLDllMCKbr zJM)g8?^6T@cNilelDCofrD^7*>n4;r4Y;$eY=)vmLA&E0Y1!Osf z`Y?*FqyEfz^&%8Kj7x@wV$HZWNu$x^HNhtbX9e0Bm*0p&8%^FNo$?MoKVMCS#LwL7 zABV0|6q}bf(v_GLqAJ1PYAq@~%Vzl5C3gWQTB#`rh!S9iZ>$g#o|TqVD-jY{azwby z2HjVa77$i&-Y$R!lVg;8b6XuOzIjriQvy1Nn|b)hA=o`Ehq$yU0_WgG#d76Q1jtOn|jpe)vh+pmz zA)a|fmR~W&Xnu=+(#B6nYRN$JuOGL+J>%qpPpZBF*948a*?T$2sY6Q^vcy~IKq2#; zo)r+UpQx*p*=) zgiN;P|EwtKroBV>V1e_7%y4bXe7N=G$`5zq4#nly)(At8b`E3n_9JG3Ah6A5Gc846 zN@J6ZH#{3l&D%!dTiVd7v|zDVKV1L}4ivn9Qd(u(?JiE}KQlyXp61 zCy23uPlA1(lier&s8e|HeoEv=m`oszL>*02OA(s}IkLOkzXQEH=v1&Az#PylU;Huu zrhZ6`Ml)Bh+L%--=-*}0<1=2hOLGvZzO#2Mvs}8F^S(dTKzK@}5b6YvntaaKcvQsf z&T!hg%8?nJysAz^T3DUipT#n@K3CVht34Zl3ojgvv(wm5c zv+bwqlYM_}Y=5hut$YDq(%v)8^_k*b_vedqX8YO8RtPIb?NfUA?L8J%fvJ&jfND$~w+Vnx~X&~R3c*(1(5 zo|a_0X|_zXT{=vZc*uDb3IWprDK_eU)c$fMW|U*G854+D3C-}3tDpqN-l(w^{*hfs z4HP+c;1t=v9~Z_d0H>m|eZW(aL1RX(;UkfR0fm)v%sQwt!9`wSYR9k~A&D9}zCjvu zORn1b`Mwt!)=g|ovjGb`#O8LsNSVjRDZYO%M4_v^h$Q{QJ;V%N45=jI7`@gKD)TBf z{S;#G=qIZHTFCm((cs1$1LU-%oFE_R`%emqI!4FdT{`_XeFHYr9;xKYLwij9a%`KG z9Sl@6H_~90;@Oh#w$_q(y>yb(OV_U~*Xx6wJEJ1fw#%;M7#o<}&8%pb;>qjdQ^JW- zCW*_&DdXJ|o^J`X{T(@W;cri){s|CQ6~C*ie@v6H42N=6Rf{hW`{r1#^P`__9!qPE zmuV8A7*}Q7M!IxSp--1s9Hj>U@~y2X;7dZ*AW4ea6Xju7JCN|??&DZ;%`{|!ScovS z3N&)}g)bc$KP%J2eJ)p>PBLj$yW*Om!lE~M84n_pGjlv>{gr({<3x;@3Lg{TI;ZS(Q&p$i< zkVdFE601^{SB;q+87d`&k}nj|EJ9l3BvI(n#X89Goh~(2bja%5%Gz_%kSu*r<6Q~Qv=)Y(#+Pj!eT_2K3$H>KF>J~2tnHV1xYPe*#8^=Qd zpSm{W+heIQ$>o29~tg8Jl_65Gui#bf162OZh~Tdby3J=qwOyhP*`hz8_@oXYy~$Zvcy z2mt0yJhd`k`MfAB&4b&zFCz-~_p|O2=CK&rpX~SXzDpjX8a<&44vPv75swQHntQD| za-f1|YP@}KahOIflmLRhChQ7KKcs9b5Q@x@`LS}4pGw~@-8tV52|t?Q_aS{P)X>#s zJ4`Fa5eQlyVz)E#4b_A!epY`kJ@<>(Arbopq8I_)ZxvrApk@cke69=X-D-uuXAoE- zT6?I%S!9=5?kiGObSum!&NMl+sau-X63h@jZV5qP(rsp=1*egK-{=bgne{uO0|5 z#rnZcsX5gO5c7d?YSxjA{ zP}I;iAu8%1bq#APecF9jI1mr|=V1N6A6;}4+?0urz3e!tbTOo9x>%tgUy!;Do5Sgi z{E1py_8Rf3rjZHGA7C9wd_&Zle-{@76)I~5Z>dH>Fpp1(ROOq7Ju5lq=qQ=y1Ue6| zIr?n>fbG1!-#B5||7Pmj=M5r~*;REI%-cQG!N+h#Obb0(Js+>|5(*z-@r4wJ(P{WaB~Lb$(~n%d~cM&(q@y^NH5;Qqklcm`RSw5ni&eTU)7CbWJ} zTBB!>enZ<{SaU?a!)p1ar}S~e6X1enZAS<#?kdo9u(pGsE{2+_H(A(l7kNex$Oyz&6TVL#A2SZAa56BnKj)Mm%9gxgeUbal^^2bnz1Fz8n{d2DM;PC@UpuHh}# z+$KJBJVE<sJIT4+RYxT>Op*2W`+}7>iY|nO434nV<&glGG{oMN<6Ycrr*X8z-W(z0HCJw7 z?A!Ng*V$((d@&54Qjy{o;<2au&MKlEP?koy>0&`}Mg+t+s?a*0#8Fn0(Osyoda=>R zX@$%OX4o)~IlA6XkX+X-DMwv$1Cl&H?Clz0Ej9UbFgZbxmcJ$od+MU7wbnS(99q2l zw+G|MmV%@xp1LNKjT~F~_`Uh85?Fr=?LTs=a|P_eKdv~itxYr21^Ak5AtWpN_|6@< zFoa3gA~N9YSAP4RmQy%A+h6CyJwozvzjbBtF{WQ>-pKsq;yJf`>4wDnE`qn|&4|Ri zO~aQJNVQcxkG(+{MjgfjPoVGiO3D>De{9BuGCzK_Mma6L+jdG=s@*xhJIN`ciLv0R z-9txw-KNtuvT`>!mTnS^5LXm!u-~ub$xYa`-5d6S*2MWS&G+^{S~X#P{bV=FC4R$UMe*zv&@ciFC>A{*c zE#irm><7NeeT_xchfsgW1|MX7G_V}sHTz`@BIe#JnkLFpT$in~4SrL~8ZFF4-jJyh zVjYS-WzA)EH=8`iyI8=j$ecW(V9s~qe6+0UO?7V+o${^Z0$glfua>M|9H4ZH)nn2OZ< z8aYX-rf+X~{#`bge)@!QS#KBXoP)gxJ?%P|>Mx~J7q^}Ees7Y z-Y&8PmqzX1pW;8BUY0K7N(Ea_cXc11)~`F$7CT=K=Z^uM;o-X{o^K6&@3tQu8=;JT zzX|nHORr`I55v>;waQB__*B7Vm6T4Q8PoHHFK6$K!7|c3How{GIn*wn}%Zfzr7z0n)yS&-L1d5 zWPW|E^IPfK zuiYz=i`&zoBf;@R{oKdl^}tL{`Tn?i;Kq3f$6bCs{LvYKM{#c??WxCD__-HX9X4|Z zx*>g62EnirX}b)GS($J^jnJq{U`Wh2g4!oy-p|#o=Wg`ea+nfUPxUD>XkOK?V2d;* zkE%^05(ttYu~NnznSf@912GSyc8Mk8;)ivSfIx zN^LciVg4BW(WP7SYv$#A{@LNd@oiN4{#Nk(QXpGNqFHxw;cD>U_V)N<&^eopHnbhD zSR?jh@W7*jAp4d|Ef{X)=l&zP{}thqLRM>9v=zk92y5D?x(abFA(U^RVg!aoEIoa1 zoqgA&xY27Lnx;O1FL`A#_8RewIhI`stx=&%nmle;FX!)9$F{WD1S4}OJ{ZLJ&~w5* z`1(}RF~y`@NdlKae&J} zD`&xyL-ZetITD{F6^76?YblE){C1%xhBn_o@6(6AEeSvm@l+9l{oO@P1ZTTZl~X7M ztxQUb8sw1J8bFl2r^_4v4Y??{t1@(p+#Xbm>xYgIviuDua!67a}E&oP^jWvj-La*Y!2cIOGfvjGk~~1JWB{|6rf9B?3cU6vgWyt$6gUF=mx|ObNR#jXOX7b+C3xM`UaM<* zPoVx<{@R4UI#c>&V8sK|D3cS)+FCq%HvQYtbe3(M@x(M24jemIMkj3E>0KuI;*Z!`{NLgG-Zc-z}CsZgn0<=JRT|J!+2^1r!eV4}KJEKgMnf>b3&J2;=N_ z7*>Q*zQ$d$f5Z8Baex=4_na|6rdun=Cwo9e(sD%8sGl{iL4vHGIh?338#OI^@W0e_ zZ(2X=`C5}Fsm;fj6u;i&<~ho?LZ8q~wd?5P&Xw`$kdyMITYh5H_~v8crGO1>!}#&= z^!@Q-!;w<@LaO@^dAsmbr=*2fD%l0Qwl1E|t=0r9!}%?^Gdia5Osyn>A(A)daRf)pk1j~jCL1dTo zc$TuvMAp`8Vg)B8wdk))IcZn|md1HcUZIpP=fC{3#ZWcKN)}dzSg#g;^i(a?3WnYg zGWfqDXTCos0Fp)lrEkOw0j!bTyC)pitM~S$W4eBizTOzgZs}1|eG?z4Hc)YTdv;LM zJ~u7cTpd2E@UI^4mpiXwmC)dN7jtfs{cLVikA=(v@NfK3VXYG=5OHS5V%FtT!$}h< z3kN&WH+$hXiqrtI=u>IT5=t$w+@UdT0Jla1XUBMZm;_fEnrxVJ{NYzm4JYiHJ(9M> zDOYmb`4k&b-5)1mwA4J2gRZEb=B4pKDce!#ZGA_6Et+#2eza^0lh%BSW#YG3KtOW3 zyUveE(~QHo#aF@%$(!U1PZsJ@S}$DqO&>fI-X|9xKy9*COaxB+0JrrzRnm^43>ys6!m0$GPOwFDkP7nQnt= z6wi;SMmz&fO|sG{w<++A6q)YA@()CDFYv=_;r^SYWybP9_($IZv6s8&UEUqX@4xNGMg!blc5hxU-e^CnR{^E-Pm9|I10HXV{ygXU zk9qNMcdwuX4VxW@DQJJZ7sey3wCGT|CRz z_=}y=pf>Y$D<7UH{rsN_Kz6O09xEemECXxld@Ha!R8PneY*KKRMOf+<*}*y0>C@Os zb*|l7)D?5LbEFKcl(8dk zis;0vVQ|XAtFbq7i6zi#d)hy+kTqAAqbrZtda28`9A=AdL|bfLvfAic!eW|RG_9M9 zffSw~&g1$pZlw3S&h09b3{rghRk=kss03=w1z)wjbr2MT;rU*WiPGRV>W;>r6f~l4 zHtH`{ZohteBQeFkR4D@~ZoPs?RL1G}L`tJ4HgWiAQKk1xbml?9e}q2fb_TcG!Ore0 zpv*F@8$P_D%ZUTm3$3<`jH!tQZfSv(_ZH%7aaq{C+BpW#4mE_;!46OJDydqnsxiOt zQzDyXN7y1FLz=4@UN)CavO?Yt=zmTPKM2@B&qb()Qz|F4?9-jnN1e( z!p7Lc&z#dN&?Y1wUBh7sh1HS$VNIvA1D=$Jb>`+veDzkYy4HN-;!-;le*M~&FPvq? zbhe)3=pHd|rRPjXlpz)+oza^lyKdB={Pvvd%azV{cXiu(?lyJ*(z(}cw&+kCv@O`v z+rvHzc{&G92|vxqL4aVRra4s>goY7GlZlAfLUY!kcCGTtPpO0U!z6Qagkqk|Fc&^2{Yt?3fu>ge07rF+%qhN8L1lBbuO ziAI~2h8vH`+`OP%B3&7Z#;MWFhR@mOIEp%8Heye$(4 zh$L*p1<%GRFAil@^h4P8)y*Q*1OD#B5^%_K z>+lksl3;x6;DKz#ZqUuSjEGn*2!b`J9*p};hscKij)szzFDGG2!G-0fl}gT8A1~o( zqQ>thE462or%XD6aykk6^4R%EurEDHpN9m`_6y4F+`U;!?p3?#O~HERViZ-)kc{*+ z(g1vjb6y1DxY^QsCQHgEPbsUrPrb1=49QJ9WeZWHWVCS|2E)&-X41c>>6>$+O?X@3 zTCBa2a*D`Di&ODPwVc)*zwFq%B(d!lEySRavC4NCjJR=RN&j}%wwj?%iifB7;nCNp8fRJ=vQDQbjg z9rx&5W!|vMxZc$;g#x_IyFDG(0;9M3i{Gn14bm-FEy$@-&c}$VdxWcAaaK%EiX5D0 zW(nQ+ep9<0LvFX?#FkAoB~Wt~h)~-1jj1dZ5j5Wqp>2i~Vs-BT_#8aWpCeRwH4uC8 zgthzQg0Y2}VFu6kZCe~P2(YHLFRn+`)*4~inueAKp{oJvIbVoq*iXQxH(b(BkZYwV+5|I4CShCo<1*;MhTHp8OGT zuy17cax&byPx*9^yKt#XL!4$COByAGja(Xe%K36IzWkMUj2xv;j`O0lurRQ zGNmd=SVQ>S#Eqx$-SIM`#)Ei$MNb=~*CZt@5tp~KkEXq$Vi9dlrO&RhdWzqP`h$ih zZNhoUe?r%P>}Yqppg6pVnA>cf;YY=KqZMA&w(h#dve1HPLYNo){QFfaV7tqye&AKs zc>9#S>GIZfK-YF@LX==5#NY3?jd20in_e}+5LRQOiOcy{?{)$rGP`C?{>z8(UawjL zDz+E5)MRHF;@|suU^K2o{Z<0dyj^CR!qP zBSL61_h6ed!bz{$E9Pfm6-459Bfr5$h3wCA95M@A%Z)o*248B*M33aQzA#IH)#9c8 z%!+gF3gFD2x&~{2?*3*9(~S)-|4I}>*}B4vAm5Jacx>p7xS#uiEej@O> zgLHG>t{83K5TIxG(T0=XmF(g%ZXtM77FL*|c#%$^da-`W)_kWMW7wjZ&ghn~PyYi` z^V!Q;PRzdNoB=BP_bNT3D@ZrwZuzMLOEsLrR>~l%*SupK))iC}x1JE@cg_a7@0`Yc z(pyh)X`6}}d8{unz!Z_iZg71Plz84E_@Dxtr%`AdxvVVYtw{X6QftqeFb5^LU{K_^ zl}1V}{F=0Mx9bGhzcu{8G?Rg%|55a1dnl#R(yOw|29^_CmH&K`z`o`}IbC$1Yji8d z44(e0G%RD{RAvh{^nm)EY~+lVs$IHBx_a|v;D(%vkbpw9W=Djrv-(sE>Je*4aR3&q zhe$hH_|dj9pg^|)Kk-~uk+F7376&kZJH@1QX(ZsFTUuMD(V!o)F=ZyCr$BGh5#jYG z+~A1yW`Wo~eV1cDPx{T9&(PkoEpimFi<9h(* z!WZWV0tPk46Y^E!6?sU&jh9HV5@Q!J`ggs>9tJPY(4gwm@9HY}V%^-8W;(@p*Z}1| zI7q(ldYPhk4&|9kfW&hLo6;aJ&+ewVGOP`$XvJD9LA>rP%fPN&Z5LM*z(G}}E%Utj z!WU3R^s5jrOT^j1;uG<16JD}a>B0?vd=3S?Cfr^K$o4dhX$Yrs;mEP{_FVt6!uK#9 z8c;`WAx~)F5(zWTUfXXQ0SOvk_m76x%l9ik%uGrgQ+%VC6~{)|-XqGTf~CAdP8Pv- zo?PX$!>m%d0x5BHYcnwfZm>95=&ZRUW17-=^ub5IQ&Fvo-3Vb-B z*)*fYxrw%`vIRXIpT&BY9OrW!=9e;$9pniN!CdEm&CuO6+C5`iRo4`3THRp+{3=zu z3jM4Tl!V3kwjIfhlb;8bVFve(4lr+BVC~YZi8!vnQ1PRuGq2R z7nF5qSEbPn`Bve4K6sq&EduV;#+%IqbVVpHCEgb_b?iX$-)x8~ zMgPe*Q(pN0#U>^^0@oEO*dCY7FUjkxHVXtq zV^u<8;0VW+v#7~WQ$?A7)Yg!7e{5q(@ zDgbB0Uyy=yDoA0KglI*6&W!~Udm;~VMc$@0a9)F*9xTzmNd~}n-;U!AmN;{~?d*yU zsvAhMwSQ$y14e=ldVFN{U*=Sg%4})C+`5ho?OdFr13E%lwHHOH_{yP|<^P}A6ceVn z@)^bbDk+a;vNrjDDPFh|#H-_5lc)F7RhJL!Wt|7x)zg0Go@=OcAftC}`Kvqa6Jo(; zS97CeaGpvZAIQ?LV$%Sb^EqXr6n9JFIzVuuQP~cq#NON zO$WO0;#8MP_YZ0&b(kG+lOj#j60gnD=={E7-# z61=`>;kya=t-EG3Do;`A%1Cp}k`Px9mh5D*3ABnIKTN-Y)uW50vgFP{!Ce@pXrg@* zgEct`7z33hKxQUtAz_Sol+k#E6*uGkriBGDh7Xs`&(tw98H%?KB1s7~|G0A5;rMw< zzH2FQ7#&IPu>SMo_34li?1}?Q%%QP-p)Q;)Y@ifqjtf)*IQKI=5tLgABG~-dm>#{B?Dd`|L3Cgq;TO7+1cbwT zBj#-v*rwSM*sl81!^0eT&J3!;) z=_EGM2(yfsy>htA$fKzVd`FF$ufVS&hS3|g+0rKuvNPj?IYNbs0uZwy#sag2V35I$ zm~Vn2hFwC36-`>uGF$@#)_$O_uu4Avn?|k0g(#M8M7cSj@0p8jpl=v~A)}~MYs%=r z!@9<8=)Zi^%Rr3<(#b$Y1fEBK!xW<{p`{BWY0Md`vegG}m~(5f^!hiItelyDR6ghb zQ7O;|R7naARLMIncNok}iQ8eJ0xbqxwn`|3SoB6H1b_A!+){YRsAIs0ie)u&vO`Fy zrq#UV)cW5LOSa|6nYw?gTMrx2xB~_`Z+YLy>DT6?7+R6|&Zq!)5UH_X6JP}2!l0Il z^7km(VR+Ik%W`Zt+XmQ{ zbM{fRNZTYWn=L6PP8!kPW+J0)4zUPa%k9dB5!4QGSu4>y%I{>aLtzhb1LnI~4y)dc zRJwJ!{|pXsccvziQCNfPNT3}8gB<1m4&wgbK{gEb<(z52w_MptT;@QkAro7JVEdhH z%@N9J%j_RNcT>2`=_|;A=Nngvg1JilIy4TlQzSt(Pb5K+56S+FTT@FLHE`77^E%4Y z?V=sBO`9BD%ValVE}{a@uh=FCXR8RV(>hO&2jkKKf^j39{~B*jCTooR(RJ`N#9bew zkUh_p^nH}paavpT%O54J!cdO#Z!Wcy+_dp4bqnz;ME|7_{+#Ku416cs;%|j@|65_& zVYZ-6+^bphY&0x_mE=^ymsHVxo&-w76g%NuH}a`;>X||s0$4X__2v?uF(b5H!l1Vc zmzdX!zh>x}kBfKHr_HzRH@cVssU4mWsm5Ki48K{08u3~|NqZANCs)2=h8a!;TvIlk zB;P=8{;B(l*q#?u6QX-=4_eYn(Ubp4^^?Pf6IZUpV7vW#b1=bw0{KPJ zUJW7DA?x165T?r*sULN(nS4d}BSRCfU$o0fGl)EF5S19t9XGWJo7Xpt9O?ugDfO=| zd#Y`{(Ci=bT2UZ*77y}YQXZr<;tC~NN%UCeed2!)_L zJ6)ei@Me8-Zlf08+sGbn!mR?xh58v~Rcq>XQr3yP(Nl5m`GQd3U3crG1^Ysd?z zKWm|pr5LdegqJPD3M$@!3#8KeM3vEJX7;32Ta7 zCrj!@VZf`xzR4w=lv}}baucS~bpMiQAwPGbfjrBGELZtBAluCeKih=(lP&N7@y1zS z2cJ|ZzjU+P8NR@>;eeBXXHj~dY&X{;Q-V#d&#lC&$1#(FU(I!4AEDIp{K0ZAyJWGJ zsKAmaQ+k2cE85)?VK zx}5S%`|J{F1vPwE&v{GjQ{PjUQp;xA2n(Eul9UI;rUma$sB)(ua%dLs^ zSnhYL(gXA=#O8T~<{>hP!7yK27YtEa<_nj)%@F?a*ug2s@NZyQn^_dRe@x1iT2?=> z|H>&@tofHoo!`or^m2cjxi6d5|LbNaN?=2t7eput%W~mEz*v6Fkg9 zmH-+D6~wED04`k{Jz4!vP%&5tLZLknWH2#!Xwp7N=>iB(Vg7y?t7=N#7<2;yQO&?( z)5dGQtD){HxuyWGN-K}RRbRGftgUrP7)EsO49MN5^hW6oqcHw5V;mviG)W1ZCNl_x z;Gr3TXgiR9(BQ22fAYj&0nt1H8#ygFa@z;=eo^QNb*r&=_1Ole4Z~e=c!XfV8Va^z zMZ=i4mQ2BWL+b@d1#Uq`;DP7`M1XEXwh>8p5iyoAdI*@Y%N@ z-#2hvgPl}YY(v@!meA=1JQ$O7DcTHR$(lJM$~mMPB3S`GC4gC}z$Jhmg8|!&0XPTY zL=Ry-MTM&%ZH)-TUC8uNWIY3~6~1&N?n-j4e-j6v2oyI$6oM6{FDj1-K4TdE7J+@A z4}b%q{&V0?03Mj@0B{Dv4X0Q|kN`+VDqDpMCcOYp_-)Ahf`D(FH!GW7^<7nq0J!wl ziZHA;-i1aF&>qp8D-p-uRTqKJ&I22!Vf+>v_(HH2l3YTFCWGYvBY7`-|0M6+^mf>0 zBsb(Ag`f4P^beu+H@GaJ2o%AyctGWbMTLKU*-t<&Q|6`uNmGr30V577lp%?;Iy?pM zvhZ0gR)!6j`rL#21}m`ZK*QgxWOoo;aILocfMvZ` zlb{Pr&Hs)fcihsH!P7|0TezWYvhyoQHlQ#FztGONb<^RK)L}?Vr_RahdgP{Sz$SOU z3D@C8BP0u7ps?_{XGM7E-EKJn#;*WLm#-^vylT7mA*aoCu004Xhmh)hdI^#~@s9-8 z{I)SdK1jn18*7A)>gdNKkH76JzfV7@D#3butqWeLb)C=9AI9=JiTx7sZfH}um?4_!ig+A+O+F76)yNY(OB!ZTVnXb=t&A*A}Cu8IX6`*6} zr^^P$EQr^sxv`cNT?)X=(0@TlT*%0ykpYc|V?oXl);sG-Ya^t_D$XWWKsd6IAAoC5v)e{w1liu6+HPt=C-+G&Z(YmF=(OCTqbb89yuN zhNFFFNPdEWhqdC;&7DNiYvm%;X@SsbEwF$Hp3yA&5WjSLYykqGbw3Pz%$ zRLRR@br9sNEjEd$*qxLv6`_MD9NbJz)Zvl^zm&;{D8jn-Kcn9AEksbE!P7Zi@#2FX zWos)eX}FSRUW@@Mtx>9<{cIVucO)we7m;rzrv0Cvh17f@mb`c{wUFg>)#Q#Dj#zwm zlw3)w2xzXAg7Q)IN5A+~XGa#o&2b7Im5+rp z6(7pyj*Wn%Jlz5OR7$!wxCAktW$Ev}9}4+)2gvxAjg%PWmCwgl;99z6Vn3;$+Vptu z3#bxm&_1RR&Y4vb&PhND%AlxToqF`wrWF2k>3=!+#E26)w79H##p?R3bkSF|M4Y;(Q{DJ0K(!TALq@@CsJX^!To^efcn zmj)3I6cNOf)(jp90bYCJxC$pE? z_E6b^Z9+kL$SY2i`#ija)=|4lYkfWR1geFR$wfU+VZd7`91$vtxesm*q*N4C3Cb!A zZiB%M5U4m+q2a&8h>K& z{s!8A#bMBP9v4LIorMK@jR{r+>V5YqinDl9FTnAuDNWz?&bLKKE<}Nv%q@?+t+p!| zH;7S~ICqq|&eVNP4oVQ{xK@YUAmEBWsU33A?#ODPphFk6={;E7E{zIk92QbCU7zEX zVIsun3&d#XOd2tlyhB&C!`k)w_hGvR`$i|8{_9Kx7T{Vxy;BTa|8>ps{{V>hrU3#d zDf|u4*^7op$+aQQW%udcVd#Bi-f-U~XqO$=1f6UCT6T>HVl=i@(tpcCW&8{hZ&a-W z43CjuL@{7@EBG^X+yhJ;+=ChUzZBraP+X*=fk2GvXz~hVbIrH62}8%>+Q+~5U6&-U zz;VS@%0hDK*`$00Msxi5o1V86~4@YAE$!p7#@{9d6B;0I4hj2?d51s*w)yuY>nV_#N#u-<()fNsw zX-S1Z;<XS7vXpc+I5XRt2@2dmBq+p`CX3FtQ$I=A#_NQfLHZsCDHTpH66|<7V6A@kR zRG;nStTM3g)NNA*3XVoN{{zP&#zsJk&gYxAZC=vU$Zg+ksC|lN1|Kag2giOri-*68 zktszT3vj-ylEn-yh?S|5HCD%z^_e+z?WZ?$Gcp|-=jj_CF}60WU+QCep+u!klB(t2 zKL9&*YS&Z$6%ZJia%_UYrhX8Aki_j>NNg=)la%7wZ^LKIIAFF;Q_te5u6|}eVQhNE z(K3^_abMZ7tcM{VOA_bzdJarU43v_V(*$ZnVp?ZC(mJ-%M8I zrWH4>iaFD8Y9AjI_$9n7JXA(M^p?9P6e&nG5xfLu{r6ZIcnNbT<3C-1oM{@LOI-gL zis{WL}s{Z&2p zAaheywTzuPI=+Qa5Sc>U*zk3!brNDv1n9TX+3Y6@Eua7aZY5ZaADEF?t&B@ZyT06z zhH5U}(Qyc)G)*l@tqgWIb7MF0)Rll#%?vTGDQZqm@~;d_U7srbz|m-dh4k>Q`#N*G zl9i~1W>o22C$TYRo}~M$8dwl>(_yG>K6R)|H2Es{j1(9LR8MsZ5Ul^G=wsk+Bjw~7 zrv)&x4QjAPrt5~L&hC4N%Ri%6!qfkqabg^YiK(6?rz&t{JgEI}awUgZ6U`{AiY}ee zEZccbs}*>Qgrta5vRs#IzS1Q7>AJb0y`9{F%EfZN)wu3Z^Y{F)g6x*_^)_oPMLS5{ z7%niY%I^FQze9b-)4LH%;&edM%kzt!n2auBk|auq4Y=SdXue7ZHCt;?(r}H`@=dD` z{XJr%OxLYYQC4xikIOfew9A`0FvKq)1@y7 zSLrGeny7AXSq5@>E1~$EQ1ScIhOTcgtl`ijTBL7YvuNlVf@!{pp*4#zWDsnJ5aiNB z_cwRzNMuVez?9aFAry;Q9{3&yapc{&j`67b(BDZo*&rld6?=IU?Z0(i!G<$Yb z;25yJpJ8Z_RewUzcm#58pCudbSEzp<7=z<_p-ykrrc|Y}y!#dgd&NKqfdQOZwU5?* za+V=$8hiH%6o<5n^thEJbbh~)!-K$pra1z`sEVk9JFHM&qM7s&tQ)p(mM>Fmc;Q39 z*wUGSp}7O2X{yU7C%`JT1}?gU&(&nw@mrK4=6!Ivknw*h6A$ ztos2DN8mFm4M}KXcYZMX*;BfiA-#Qt}PVhm?#MQUunO1hTnW z7@1p^`JD!btJs$@%`xjXs zPnRFh@2BpaZ+SBl>$*2DfQ!!BTBOU%ynEml{*CTcem;7A%+B8KDuR}*^@I6$d!(#1 z_bi1xb)*S*ea_WU_h|zzuGikB^8>^7Rq~O8XuxIk`dfK>8iKwxV|4iKhbvUD^v#2n zujTH297pYhtJBTA*-OJRrN5mZW$&_|r}rC&<*jSIzVBE5+I#;J+ov=$KY#y^7F~aL ztKHQJ-=f|phW5ZqKYvdF{jTzkdym4>5AF}63FoBIypXK$*0g;u|F&-T)qQ8D!7RXq zCBW;PbLCfJlER?`4I$TUoxXl}?(h8Y)#0T*{~CmwwfEg?NyeE`-da0HuC34Cnj$9{ zJ?x0>Z+$4Wu6$EYGi?t2)K6D??K$mhmwu^RRll{pJ6Y0dL5cn5iB-Cvb+h8U-L|~W zl+8~Izw~?U+OWYQ%uC{x@p@$t-+S}_SX$R@;N!O=7}zfzUcL28BjCnceQ)5$=Oy4F z8bLkn!CMQqqip?r}p>zO~QtU4DLH-=BIbR z`nUVRv)=;`mn`cJ&!kIAk%1cOo86etb`KpMn)r4m*OtAqzgkQlRVrsH(b=Y#1wXE9 zTU{nG?O){H8T1<6zj0;|P{W^Ww?F(Kx!-QXBFCZ=1ir<5Z18!lQ;_fK71UHfe193$ zZ>gN(&|2HvR46SCCQN0lBvg-&p8&kPY(EVE=1&}-K5|n}{V!AFmv%pJ2@gKXhr_2< zz5IRhzkK4qBq$X8KZLylbS3N7H5{wsj&0lOpkv$V*tR>iZQHhOJ008UxRZ47?esbK ze)rt}{oa4I*IUN!54uibgilhC>EybemyP^I>9h6QPAD*@dZ;l1ylMZpA_8 zhxA}#g?g(N#jNZN{-~cO>`;5ZSK)FVy`25=yRtWW0s*s*w*j*-0JE4M?)f~_aNfF} zcEn&=yQ?0i$A8>giPdIgJ*2klpsfuaUF_bU&>euY7oDa(T|L-MyUYvvrN7l|St~qf z8jRaPSqjrv%Z6{5tJ*~Bb9hOsZ*FWgmk(=rVbAYC|*1&e*M-LjPFMCObXg-v|qevtz(nBpW8xX3sLp`N`DL{D8I*2alxB^!~ePbvfQJ z_-osvU8hc!%Q!c0JRP{l8HPv;xK{D)~TZ_<;X|@}CX<6Xkzqfx+4gZkv+#1B63r)*2WG z!mJqx-ah~U#`+Jge^dGaa!QM2184>fXlC(uvmP7kPumQ_jUPB0ARMrBA2|Qn;6F6i zh%|sNXcBD#njr(4eMp11urC*XhJU!diTWLQKzN`@_EIe#-vQ+1x!_I9N83Dp4?28C zi=r|;O{JT(3wNhm|71V>OWa4(4~NmCJbJo(a;LC_&jvQaVoUe^=;bo_;2?9X@7L!D zY;iuj%Pf!>39w)EsnO&$nneCgu zPw1*3o7xS-va0Z4cU$?K_Riq&A{Mq*542^zFc@x4-eWhFzea}}R=l;+b?)5XM1MaS zJz9Jof7)NYdoin>)UwA7xeZioz2wN4@wQ zn{GAteRJPuYThLwnf224{?IDhOXz9-=X5f6>-yJA;<5GR%B|$@Yi*CRgUyvEqjNTz z4&5DIYiW2>-fL-52A9!pm)H1nuiGy)ReWK^kYM;fxX)34{zMO!-gW2Lz?kR6Jc=;n z&CW!TG33q4H1s%I5QNcnF6%uv1*<0pJA8RI0UD9bhIH`rTp^6{xHiNHFe0G$ZP0-? z%SM~CW-a&@8RPsL%&gcZrpUKg6;4)`nCARz5cQ z{SAK*`Xsm1hG8N6DhN_FD98kl>YJ(B>82yw+qDW^I1*L?#)$`q^(m3 z61pe@`i-IU6v9!+**86Qn)9ZK!ZLdgTzi{J)=Y~jYeOd}MMP$qFUC0b6C7WLq<$j_ z2n4j;L)ncY?Sc?4LdiRhM^!5_Lf*Sd`13iNwk%;{8*kkzuI1?X5A)2o%hmV)oX5E> zb{Tt<=qza8OUmj>aKM-{=)Rp=c5(Bd@!E!W#q5JdIa{c8)8xr^jl8zjf*RNr!rMysUjX19LWQ> zYd_MM=+IuJ-`vx>-Akb8HU-(HK~7x%e!}(V*-Bz+_~f^1QypeqU>6Q5-{QT*zWM8h zN;}RKCBG!=LIWv(!1G@9D^3uA`@d-yqpC1PQf(;>2@iBmtO>W>4XGDT3CN4mbe^xr zb7}+@ZY_KP*W-YGa;nSicaP3A z{yz4_#7TCJRxUYr?{eh^UddNx6#I0U9L-HM^WF#NXD&Nvb~v`vvsWHrF6$P8Q>HXR z4c3knU!R0FMmKN62=!hC`MxV{M_G$d_O{WSw%bjaQ?8)QRif(sO zeS*Dxo{;!)Z<-O=<@G^Z@!+$A#>oAZ3#b(7MPgj#p^{L+4xIxoUlx2zo8@`Kk`bxJ zQSP`<(qvJ)B}Yobl1cB{?QNaD{bnW|L-{Xjr>Omr=;W>BPq>qgCX4G|wDs0es!rsK z5AZ6}eiY-eS_LiNm8@w*zj?YE73K-bqN^=t<|nn+n~&jFn|h6LD{f^bea=k7yVty& zEu-{4ogFIf(1?!GJezf!yEA`PK?dh^JUKwFZ)+HHGMu{)a-DB&FFSmF!rC+Lk}GB_ zm@?*WG(XkhT5K4lUnqqf+v2Z&Gq|qVNC@AgKRH2$-n~)8^^=#@xkSfzX?0@Zw0*Pt z;d{c|W$irL#5ZNBn5YULylU%kU$|#E(srXTjO*HwEK?__?N)jg>54xt>8j|Z+4_(v zro=u;`q1|-I-pJ?ld)moaJa=+$WQ@!8 zouAwS?!JEVXw|+4iRT=DMjS|6X5u}3V<4&zA>b0ZKgNj7H&{S$WJ%lOw_SgIw)-_Y zyzz6m1v~!NSH#bI?ott>jEoRuX_CZ*P$A-92*~z*Qtm(zgN%$2g@zjNFcs|$rGERu zdc@<@;9frM#yq7(CI!!n6BVal*ZnMkKzFZm)-qD6Md-v-tFUKEP>t1l5*-vAj1H^b z0y%F^udBY6Ju*~<4r0!ZR456hAjPd-!A^z&ns{r;AU#J)q$h2?gC7LnRie-Nn!>G$ zGD9|FWRt5>?4bF4w=n;BLdk6u!%P|dDzzaP7$xW&x2H2!p1Kn^3MY12D>3YAA0L37 ziE@_7C>Z~{<=joZGX|}^W2UmWAInv z%5=rR^D||~@0WA;Pjsu6*`H5!Hgj)#O}7&rP2b{AoBO=PV6M_OyJ(GtRyKaLQa#Bj zdC^syWX!e6pt^}M8D&F>4q4e4jK`rp5eKhPAtGnrm18RuzXNvc9>BlUzxbbh56$ELFnS8Xl{z~yP@8B*H0s_ ziM3Mil4cL8VfBYBlcI&1XOQhrk4|Z5J}*NC)-m#aRtE1KyFa z$*)IF+r8f$Ckfv}<$Q6yP49M|IimvJa|C_``O+BN-4Bw&GxG(QWzxQx+Y~)s9>13RdxCsSd$!e_jgn%!~Q~FNkJHO zs{(~{c$)k~TMM7wCPt{`qU8!M$9|45n2NTM#qD)99$2tTX)~|SK^}^+hXvS|lM4>t zm#ID0^1CnLjx-tO+8KUovf0{fZ^{X(U^k+)gN~Pwu4C)gSi$ynp0HL&0bbIlqpqH$ zdKBH)S?h=awVpmF9wkcAeh5hw#(F-Mzn5^2_L3C$RjSIjnl6-s9kU83Ut`l=`(~-yE zC&Y4&%=>!!XwNTW8TIaxcJ_>m{!U*HL3nkmg+Ef0Y;FEX{Z+Kw!4=Uj&Z2i?PVa(p z8ClrRzr7g&ww`XuHZRl0erdZa4fm>L4Xw$XVlK0pDKRX$g@Y`&cb$iwpb~FS~ zUyr>{E$pi>@|lh{M9bL-5_#;)(s|yO(mYwu+M{&pG(^<(pB$PHenDKqaT+$tOV)DN zL}>JN0z<3~5v-?c-=165BE%o9o0EI9 zqg#3EJ9=BQY>HWs#eXfbwk6{2_InS?zqPnN-ng~9Nj7DAumx|g`?VzBg8Zm6V@PbC z&f>2}F3|er;GGQ=KoR z3a3Nkp}sd-Fi4(7SXyR8j>n|ePi`?sPQ6W=-T7Ht{j+weXObx{XAbrhlXT~CA9wv6 zcRvn;BYeX}Aut9lsNkYzxl~W){Srl;O|pl@tuEkv7X|5R>Da*_T$WFe$!1*U3&}%J z^HM>xN{=QlCS^w>SqJM=!7`VE273ley8CQkvvFZ_5U1`jj_H~vD1!z>P+7A|sw~f0 zr98>!;O3;xifg5<-TCPlq1`$~1h=uBO4gG~)T^ZFN*J3;Xd7wX;N~dU4Y(tqVr|Er zO7)XUJ$MW*$R7_&KoLp4xu=!NMWWO07AUQ=JC#QC7Ca8Ek9W)@^lj%0fB{}OVPi%TBx{Nq<{uZ`8IX30_7X&mbhCau5Q+EVhNYI zxLT}^W62KCT@%$|nr#{t1aGRiabh0&-My3tW3^y7YjqMsT8-V^lA1a z(g9F(wYY)ab1Lw+;n?B1&--LEDPaTW&@;n1)6@*1+W-qoX>;*&ZP6ex*_osIGQ;R| zd=IA}*IUy3H9i)-($UQPiw#ynd?=(o=1#Hqkj9V9ec*E?14I?i2%&QlLZ6{XC5h*#3g zWxcc7%^L$htQ1Kcj5-l2yI#2-S$V$D6ga*oM2SAZBe)Fc6T207{;5x?$xq`kv67e3 z^Q~Q}NFYlexCR#pyN7J6uQ1Uw>s02%DPb{_;KGDn(1~2IjJvkO<{Jc9GWL3rYqBs{3?BJ)v^T6&YY0s5e>}Jrucq*q}Rv6MV;>p~eD5LLeYPW`+Tj`5Cx9 zinf)p<;qFC3mFC=sD-Z%SO*L$J{^-iK>XHxBTm6kRFYxFf?z15!bn6T+Cg!7K?x{E zPbY1#P;__{VQHF=h(tBtKGzhNeKQN`$*6PI03UKLqazZU*+mgq)Sl1{fI=h!!mY8R zJy8k)>Y0IsWxQtz6J$3;cO1pPkO}$Ekchzl3VF6-0to4J{wE~YL(oF4$=@L}AP|Z8 zLwew)d$JV93JF0R%qcBUG1e&WBM}9{6;tSibf-~TLnP@+5Z-(;uhMz4uYzC{_+)UC;;j) zymNfQHg?JAuvzRka=m7OTPdP%3vlnC|2)uFFes#zTxi>Aj16j)i!@~{B1Gm~ht)YM zhMQ_DC?rQinAVi1B`nF}nx&Z{H|$Ou18fcd*)@wAU_h<+ z1k1QYS8jq}ykEh{f}nP<7k)|19H75|U1QSCwN1RBnd#^|hqKV55+Sk^w7A-0{Wl*8 zK`?zXM>)KQ-n`5rKVjF){Q{&2U7t zIa)qC%`4!T#jwwMoss;M=4%k9t{qE|!i4maZ0-&JMA$;6O>JlH2Vo9bYc|pE{|;*r>=1 z$_c{}=PwZplq1bVq4U^@O0=!Xtxp86!&--R!x5;xQY%y%CxTxi3o}P64+pZfANCv% zr>kFj%h~73@geJbI|}RzA-c6kGbU834dwL9RqMzPHpVpqyb<2JklwYuBv`se8fqq3 zx@Ry1H#=OJV5_2I;9`5{ZHDHD*Uu-{1BSQShVk3%6czG*inVydn zOUF<{%{WcRG$Pk#o&ZWj^@u`pOUtPUqB^_(>@Q)np&ul=RNj^sDE!U(c1*ZdQFwdfxIz;hk@BbDc)oe8RvwuCJ@( zrZA5({=F@R{x{U@6s6KSqZvxy;ZgmW4!D%gM^S;38vY-8PT>zeY4`q9kBv8`Bykhm z*%^t(@%^pQb-vhU^8N14{wL0-{yI2HSygivg(>H4fj)nddxF+$4?$jaNebY;Pe5hr zmGYpKNFhfAp1=2&JSlw={M!#yGl<0`(KFE2Q9q-LFNp!GM$&oV0x99@yYo%OZF(2~ z=FTX1r&>BYPhI$=Rn%=J-Nf+Lx-`Dfn*)qt+MvDT@Y=|sk|@>iJqrkhB8OiZ_j8%; ziJVGHfL2l`tUcyF(}>Zl*3KY?ZnOmSSB84@BE$)Tl-1T3Cm@B)7~8uj79nPb0n!lT zdmzx%AX5kw4AL~aE)5ZN0<=8!2c{5X#7Tm*dxt=D1D`Kt1JJV*+9_XI{7sFIZkBg*a1rht`}{J$|aEpn*v1S{lWNK z#3{cZ%ZCU6tN&d@GcCgu;$ zXCDJ5%0~EaRZ_{+bQ^1~^_xHEIBPxBD!ruJ?_OWWclpvU5b6!>#BIz{8Qxc|8%MGt zWQON%WlsVpba>AnQu}*P?H0qbOW=$LuxSPL6zpq*YT0pvD@<&$HvLgOR?Fm-P;_qA z1O+cS`T1c%#PKx<%H5u;x0`YCDLu}Y7v10AHX|opgc`rZfMg#~fqU;aCOB;i*nUD! z6S4i|#3B3LRxw1;y?56Vp9Gzx87as2kctWC(yPgmM8F9+K;+o^ILHo#5mdVgSvxYi zK*p{7Q|lomy)de(+-6^3k^JUY)XMd`T|_zUsRsc!0XJ-5EQ)HP`JGFXaS`MMbbix- z8xgxc#MRY-8_|p04lI1}%o~+ty>UhDFf7ZQj=)dS`5l2+7_CNwq&XcK7ydVXH*6p* za@xX0{TtIskt7uKJ~OZf_xTh!j8C~1)CY^d#7+~M+d?F=ZWNT{fDdT3OtB7V4Qr-o zsqugh4lMB#&=WpX+)y#${-weVIKWujGNfNz>E-R_Mb25_Zyky`M*tmf4*$~eHo9KN zMD)LqaLQB9@E*K>mP=C5{{F{`&nX{PJShAN;-Lg!#pnOUiXpxIz`=cpqu{#u8*@(* zx3Tf-Rjk2f^DaHu<}Y4A&XqB*H0kCuX%pt05)lnnR;^&|-BTMsQFJC&Us<o}E#(b+1`rLbuuhy6-m#gcEkq;nR9-P0f<#wGV$NiK z2AgSf?T^fQubnmR3z5i+F+vrto=_^oUwn2WxBI;d6sItb;)wA!kqU2gC1+f z(AuB^1wW2u#sYYMnpTli$Pm^HC|XpD0pu}F0qSMp?&e+6Nw4EgnFGiKvY!%iG~IiH zg-J9EB?$)ovSfI!0jwYiUFpR!>u}ad1keF2c&J8_CEEoyCkl)7uK?2o6;zco&M*`j zm6NpcDQE_;WCD`HSoIIcD{~URp<4_D!Ro^eVA1xR4wt0UE|s7l3@Op$I)<=DXg3#a z+W%j&SSS{6UEjW_sQOA&6HvafSbs}D$;jJ*oAb6*KJvWcaPjAWWGK~*>R>EQR@|Q) z9Y8y%L{+&7I)7aX=;ST?g7DNR>hNu_uZ*se?K{ogy63P;VuB;IdVZTwB;nI}+^Vav z=ZkSrrCoj;FZ{3lG}t-sjChOntmc_FP|6zqs*<_CZ{)s#VPDp-_ zxKwfQG|bSgOiVr&R*BIAQyRD06(>T{9-nJ5JV)spEUJS-VwV!^Sc18HEx9l%G!2Y` zuzGk-*mJqK50&wipa?4JAx24FtP*2{$zJO8mtM=9u#e*xmCCZbO{m z{`S;SeB6Yj^*s@VAZp^osGKmVKeCeiXcmexNezVNHFR*%FD!`GzsZ1?1@{24gWnHa5l`(xjB?tOPrq+F~Oz)=vuB9{~o>-vB zNTH}GHuz7}0_cCD62J$M;xE*YrNAluZGlz|z=GIMXdf0#wui+(5!|hWZ85k*Ch8b%xI2_Stv^1fRj!XKO+r|yy3-ayhinHiL`{udrzT5 zsliPEd2Epf!vxa)LX6hjK0+23kl>4k>ve3EFKAEO2l(*O7=#+4@PyApIGRVdDT~Z zOuGzuK@qLEVqd4kR>>kVQ(kK!0~SXEdZH#t~yXcAEk(0$Ob-ZwC`0K(tuNf2NRX z1^y#h+>m;vLu*q*4>Zrr{0Wx7??gyN<1=Z7D{`Rx$c9dgEy0df(5CG$4l;ZU2z2q! zZM!}K*vBUyaO9{jZoT59IMz&b^gCC#-$ATupZ>&*9o@*C*oTLPM3!-z8v&eXkW${Q zgBZk&P*cscDPhTfC=G%EC?!z>D3y`p1&y)gilyBYgEm2tmDtqKzegv*5#vbCWnX6u z4gf6xP~vo*qlqTu7V6yxMHAzASU4j{v~3w}=?Lhb{srB2X5oe6LA+zDq-IA!UBTby z8=!c)CkxS)EDoNisnh{tfS7D6q>&tJEA%5d##SgTInH(f4oZR}1A<`3F)u7U7CGwk zAR{-%z$yKQQ*?qhEd~JkV!i?M4p&0?3{fjtOz{Y61UoB=qTxz?J7jw|$nbnys*uK? z@>j5-gY$1v1=XKmk>N=)ZVV|u269)!$4YuINbGl@8t4)~iROGy)#3n|96#toVk1cV z6g&+?k|Cp{$}Ha(+P4y2y#5LfFl)-_7|vyaa0iGo#?~D^%N7k#v|<{PCdT%>)J8o0 zQ*d&`xFpAmXEp)4%kvLmY{%Y`mM~qjQ=-NpaOVW za(OtFC4U1fRW`5~M`nq;d0xP0(OOmHLc{;B&)lwkrdV}?C{nz-z5xcZ8)gd7!Rdd; z1xlHW);}V}iNfJPDYYp|oxh-9+nDFPIp&A;+Xcw{B3&PAP48#JK?Gds6Lxi;Wf2E?_~r|Wl9=%I5$DF$w+Y;|=S!dCt{H;%|M z`pNrf6zq!+x&Ix0alKUzJc1H-lzFEyZ1L(3rUZQu;bwot)Ashs| zJ|Tk5MDV%TL!_lENDe^Uf)l;`=E@~&nc4LrTm+{x-;!Y*(CmWI$dtTy;h%pcW%Z%N zLP|1(t$?jOiUPBJxBYNks_&xdqUk6jb zZ^XP3;+B{2>>_NNgzz;;RUHCer)BVa3!X2!Y%YYs1j)N6qtRrF=*{QRAC5!#1^60r zo-ynZ5an%sDY#crxYkstzDSQ;f2#z=H(<=Eg^fmGk^bvCx zfi}Jza2Z4*%-ijSr%ZylF9Sf|VJVCAPdv75ln*bYhADOta!`H{;tcdM;Mf8@`wuC^ zzK{J-e>ukgl3RWaNuD`|jXPT{)0d*LY;BI$kf2B*dJhn#CjY+FEY>>?By0OZKQyduIM*2l@Zs#&w|BJ{BC zA{bB50{~lAyq?S&*XsS@PgdLe%?>#FU1K;mWHJb4llDzjqZ~2O+I?Iz(vAH4y#v*o zblz&G;fjnCTS()kxO5s*VOcN8isJ6vElXNs!Fx;zm;#8&AaE>j>%$nQ@qS-5xN6PC zXe3=3joek^3R{`hBftOyqhS0{_+$>Z!|-4)pTqCR3tsH24BTI$Q`5s>jv=Dn{dlLa zSu@1P+a2TLM7vGVFW)pE|FApCpsQLDR1T|(Sye}4Td@(0J01D`!6Vr=33G47%w+Xm z^g1CE4D8Z=di{W{1}L~y5)?XlHOp7YF%4Iyc-40vuuHKOyYXVc@ifh!8wP)tVa_n- znEbvH`hEBsr`b^0XMUZ&*zL5n>+ISeSjNF6^AG@2`~g$^7tA-A;wOc1+>iA%GY-uV z!SX2`Hd|MB0szpMG7k98`t&!!@a=uI<3B=oSX@@j@O~=}XWstLU|(d4InQSM%!1}E z1Lr3c<{J()RdrfzvB~`#UCSUE-Hg*QmAzpL=BEJ>4!qEWG z=J@-}noI~#GY{iVGuUOA1&m}kCT-KwozmPSl6wHeV2fMnxvw1o&yN{VgJc|faZbjb zO+%imL41a_O=~}6Y4!!@nEZw%#xeN-TTn9ZsRqf{i7Q3=iE_<^SI5z%OJc)ds%VRC z%7E_bXkOb+F=j+bCly@gI&LAbLA_XpVt-O!YTfhJsM5ia(P2)pS-Tf{5xcsgKvW^> z-no^C-Cn~0CdZGeUt{I<4uAgV6dv~y42}%p!VJC0Lyb=#3Xfl*C&Zi>Yo2o5L4cu^ z>v;Ykxw=5(m=!D=duCZQjif6je5IbC3B;D_*3L8}W_~KOnbZX?Ob25FZqMXG@APF+k`q;qQ;>gzBnO1#JV~}kmd#_JDmQU$hyKdx zYY%cD1Z!hVMZQ~%(+t!>PG+BAdF){hg5Qyv ze3l{w>>NypCdkO&9(kz#I^xxh;w&~NBPBN@VF80iQHDYS?kgvymT88)0tIsnF`Txw zTe7?iOyawJWqaU~QNrig2U5V~FgN2)iW74%J_MI{WO>lno~R8Li{ST#s0)~RX?{A(h!UqL#69SyE{#xANkRMhYklYcoZD`MP%H=s1v+EmC_`Qoo6EP2IXM|n z)a2{oabFhD#>7<dEdASWedl?H&)f?D8Tl!SzE5LIP_95FEugbKbx z114@13+l1nzGhjjkRET`X z&BaR<4OWB_+qoQJulb8&xgA#1zc7Da_O#A&QFjf(Ip`A4Q}qP8=rTT$yIqQH5@6W2 zqjD1_Q)u?vEFn=2oR)u*c?4V{Y0Yd=BBgfnV)c5A{^hIn2C1Nzn-K^*FG*RB;g^I) z2S$5gpv;M=_}Ax~h=3F;;>RV-xP;gyg?WY$k?WZE*fi2&r^dXx{e(O@lo7L?uQBty zXqUj8;D0`gMkxDApq!Dr0a`ApFjl7^EUyq0|-u3%uzd*Ob-xYSt*j@K$DT!`SdwKf?|SV2@wGqr-1uC zTZK6B;Kp1v0U1#}tvDf{C$Y)6`8K6?b$f|e6ZHKCTfz}D{M#(QjIdTr0~ZE^D3aT8 zlRu=&ZV`Mk{4%I8!8sQNWHOaZ_c!G%35s`U!t|5>GGrC@gHIv=;g2CjNuU5e;>+m) z%+5TKsWU4=_8NeG;_MWshPQ{YFEa$VBziN-a3dj+S<8lkJz{nvHXrBbI3a58lkxVw z-MxaaFj#V-wd50BT6auLcpX{V<@yvkEYXGiYe(|L>?{y-QZDxkK0PkrSZf(36|z91 zz>}JN|IJimPWv*8DneOZHj~1ezXuI=oYlfs_C%(Q3$x&z!vtE`OJPGuehsvAMrf)) z=AQkTf_7cGX-af9rzmm;GC#&Nt+%%`!roPVu+mHFKRqHDZ7zP1BQ%|0z z7$>5qzx7}Qt@B%R&Ff{cRM4CE!8U?OV{|g$XtNt32bew+^ zD&Wp=ReWeCp3nmNZX9cMm-z6DNOXrnKiO6HKh$CReW(*e{J-ifVFBtmpgMhCPf`C3 zX1M?C5bId{7mzg?CgkmEgmmk_NHNje+<3=lzP$feWz>8=5uI*OAfO{aEy0h%pMQ$a zI2buPnpvCtdCsa)WyAgl2TIpgv2M$bF?5ri)E?ytNzJA-nI+)m13-1(fvoP><6v`T zw}{umMw5(FF3wSJo~F9H90rDE=`-M*bKny7d12`NW`Tq#sj1j*XP1jDU-VcQl0w25 zD0O+cd@#SayBrvkepk{uj)wlt~~#)nci_k^~_z<~K#lycTSma>1AxpSx- zD%=lK(~uLn&_f0nU^-tPd8%0=y3mT?q-|uex5^Z!TDH*m`fHE&j?+#vzL3TOzK;!w z91G)+AaGT|sDu4Yr<1VdUdH(+YVF>{*+u_$g#9lC)oQe(f+N_3BvK52^np~_m@r=3 z7yta_6?BYt>RP+NL%o@d=LtA&@>n$YoItW?+YE5}`S!?|G!)oF!3ks6$cSe$QvCCI zT!ABSjC6VK3=W|1*t2!yPf*z45uEPuk`Q2-7xR&1IH?DufEN{5C@}vP3R{;r*qF-8 z2^^=LEojJb5SF-Wis>^S?a(HDPjdF9SzH-d>WjhUF<+5i;hURV!qO0hOWY`Bi+R1~ zhp%fA72Ax0=0XcFsI@8Sjzet$F_)h1j?R@uUVD%s2vw{!1DCBDZT-S9QZtm{<&Q1D zu{@-3w*uQkZ+N8*LZG!jEf!wv?*|)lmimgKL0(tPZ%;7+*gLmm1hW%%&0D|R)_&P_4|y&Z2F4W%w~pUa26 z5uL`@rGH6V73z#`K(JjHZz6+_J8u!=8&MzQ0Wq%>Ug_6PqVkf1=9>tJkt33*{+ICoA8$Qq7rpsHv|9rgiSmQtu2vBOO3Gnw( z%5KLae#j7L}Bew^Z-VFcsXI3^6-_5^_@1X~X_)6AXIj9&KC^ve+F+D3{gOFLq(#cox>zttQBaN zfj`544T&5e(l2GI6F)-K8sr?sjL5;kT~WiC#YWOUCASXX%iNI3vm+|eHbbp?siMG} zi@a{8;E0mwV;-vq#$Ix}6c9zCogABuZd%WyJ%&7z2a)d>KDL>LGV)HndBZnMxD(?D z;=!x8YEZE2pjjF@PQX;}ltJq}W@Rx8m}AqU@scx*9ACQ*w0ZIoh0Sc~`Rrr|2M1Y5 zh-UG2v}Sb!{!hn#bLKc00E#02h5;0f`j=zIfEwURdis`sDkEaX>)h`tZp&zfiyHCvJF6J?3+f3HU_U5KM?o=A zLeBT;C;j)kS6q!|7n(XUAu6htpFLQ%G<~3q1)lrd_|Ki=K?anOPl%#^pSMzx4%VCK zHRg>eT2zu+Gt0?7d3sbhtvh~0jezbiBh&AT#DE#CVN15(XQJUFyEYhZ4k!Q6&Ht&zm`bX6;CdwISz}8 zNGkzespy@enrYr~2EUjnwNdq3D6akH8_0R$uW3D01gn_MlvmMgXx{l5_V%?S=1JR^ z%N3aCR?v7~`3j_X`~Jr{l#_cWTy<5c7CTQ%|5;I(syj4uwt<@yBX)|gik-&ICAdZSgR@&bvH1+rxaEnJYULdK>-cx$Hqh zg#MV#SUo3$J~;jvpk<>-=yx#0FU^eY9C>7B$^vJFks)&;xq83Bi%4R z^xqs9-y2)HbtbyAd99^jLtmzE$dJWDE5@)H+_+;l%x;qSnzXl-bZz&0lWuvF6o)Hn zc5T2x=qGD`9()g-a(YSbB0DQ~m#z{i`+ogjHK7v(h+2jK<>=d~fPhf{wMjaf8d(|9 z|CuuWsbr=hZL`LP(e+Cev2B0hdCe)TP56A}VyOrYxmhr}K}ge*LZ0A+qzmrCuE$4j zqoyC`ys5vYlK<2B+-sUOZWH{m4hH3F7=^t3;h^*nQpjPVCmzpk9fhmKvtT^n-ywEV zuMZjr3T|)&TSf2>!xxPn{%?azSlb}a@FWYO7mbv=7R%M&T41W7(A}WKTBfUenhs!) zv?vo>XoCs6g+TYmTak?LP;O!)CE@)c|f>^v3w8Mg9g z=`Vuw6gAQ&`D<2brdXJ1#=O$+H}3V;1!iQXT7Ivcp2(<;C;amS$P9V=#*C7Zbqxx$ z)%goz8P0Y@Q6=f=+FMJW?kyLk*Z%T+u(&r|jrXbr)(!n?hx~Tvtd zDX8VZ9ZNr4VJ^2~1*kazLwrs8!V*S1@(!kQ|;J)Zv%i*CK(j7U1siYOl@ zDmEHS&cP8Om*@pFg7w`}FI`?n6ojWI2h^^X5Ym-m;y8UJJ}L`sj80IZ{&?y8Om2Z~ zzjQ;jpkdorg%)U{G<55pP#^7Dl^J635i5%Iufop76H7S5fv`o4L@0MS18#(mbm`_) z8*zqhVhq9-;Tit-SxJl{djjhm=p0<3yMn_rW-z%6A4yjY8!OIF}I`yQiPJtnjIH-x{_{u309&$~46; zZ}GnNaBT!N=;_rJ|6cpb-&XEK>2MW1yTGs z5<8mXuD^d%<7NpPjc$LARpdUdsMkG6E52$aQe5p;lV$JRtob?k@UK|w-%bvB#h{gz z8c7xp8C9R>AR@x0M>aw@v=W=>uHlNx*iOet%Nv!=7Bv<$5`QU&ACDsLn6i9rVi0lD zr)qR!F`#O9Btl$s$^>N~`P>9r=yWR2#g#G+s_yjGW9VBt&7@3!bqJE=_0y)rVdtwD z2>KL^40ZRWkmG7Z$bqa!Z_drE?U+b;-M0(eA9Tfa8Ecn!R_x`tBciP222h&XG8kZG z91XN%0g^`TpR_E0rFP7xC?%)>)um8^0)2hG;uHK`I>ebwzB_FxxvwmBzRjqs#-_K! z6wov{vH5PjEV1aC1NCXeGF~ch;>TBY3WgMukOw5p68%jj>pCB98=2L%@*M{dHr9&e zrJa`d?oQ;Tt%FiI#ayqr;BEI=Why&FO+H#H+1-h3xdL~oW@T(};mdBkLr@B>9Nv95 z80K^W>ijOQYJ}sc32sCkWuFQ%bj|uXJOuc{NDcMMg-!GDUq6Y=<%W*)>YtK`kSsWV zZCO@i1`tt)GzJkaz&?HN5K}ZL@EinWnL78igs7Gk1&BZ8`u&c_Q@0!Br3?$dzddhKQKHJt6Q02w6Nk_!VKQw z$7EJbHi%MpOg$*2GKSwGq1PpcNF|Okq#%LC%M{k94{FkBl}&D#1<6iQJ)9>sxC-#3 zf=|9OJ0HAy$sW}%+{@Li;eOvqrGd7O=6J3C_6YgUTrA6nYq=Qkg9ib~UOxlA4V)Yt zZLHL+Ea}Z0jjaCs>{Uk8!t^nq0R8e39*r9<;tD~TS3o33%7v*CfBpdx*AZ8s|GpX0 zz3u8^JA7y*{RV>1!X?uzEbd?n<8dJ(ga7OHkpC7j@GMXOGNTI7RMA9K-KoVDoKO}N zooaLxWlxI{Z2?Rr0K~+$6z;e(1p0Nv7Nf!MbJ?I@xTR{62U#NvmMHz98EN;bw+b$X z9MUsg6$FRgMI~+425De*3rxD9eZb=Cf9c{KJ6P-l=phH_!uyA=zpB9gTiu@$u#wZX zfczX0V!Kn8u)(y<$b=$S?Ex8N^{XvmdCYWENa5!;NwLkYJVj7UrcK=UUk+B@2<0sT zWYM!FMP-RT$F_HNY^NqG-qwi(O?C~`A`GX%1<#lHUwX}34Nb2@g#?H&TN#RNDo1G% zX_Qk4lEwo#)#wbeoa!$1O=i;D-sYaV3ERmPaYTr3^(+f8@Srzq?b15@aoa3>M(C*; z+K~-7g6~smS;Be`F;~Q<5#Lm#m8DACRrp?)UqSxap^fcQ(mMdb`T*e={}BEmSjyL9#4?2a<#fSIC%hyhVrEJ}RCb@d^&BwiDWEPL~i=3+G%cByi_ zT;#^#I|#7Wt(c)ApCu!KQYz=p4a(@~W@RBB;i}@|H3R2t^(P%B;&61{vKy|PhoFNe zxxqU~oxDC??=YEYA{Mh|cZWFW*Tx>r4KmLco`IWH`=OPagCXzm z9bsoAce;-}n~WYTb;V zke$aPKYMR+zIn>LfIq%m`NwU-hoU&JUHWSlZwb;IN=_{8z5Pl)COiM70KJNgJT=qe z&2RR;^AV2z;(n(cn9Ab~@ z!8vE#Z9~=2ZAJ;jLFPb9NX7D;{1+xRn7x}hrwq9b0~NcD>Jajf((SNMFokWX7J-P> zYx^R0WOBRUWNLUUs&cdELrW?(^1XJ7i@A)%$~~r{;;9p7_P1ZdbA>`A!zKus@oXsd znW(4HLQ4ENCD}PRPnQy;!P!a0FM?#*+GTyVrtVUGJXmp{=_$Qa{V7JNt4+YBz@tpV zVC=-9ZI)@MIln(XOcrpWx@^d-@qurViL$N#pSI3C8mj(}<6|4kXwZ;EQWz1DCCsmE zsq{#cq{vRAAz4Nk+cepk82c_#{g9bd!($m)njTS>5+Nfs*~YH4Ow4oJ9=WIc$J}%8 zx$oEa-tRfz@BMr}?=#TEgUE8KRpXR`0vCHlUL&LwbB;$cg#M|@{RZAd2? zZWWoa9H~lwo87=yeXYAe(+#rhsCwtFtV%bR7&6wZE}7QKwyVC{tw#C;8{EF=Z)TNq zT@tx7uk*Qrg_%@7t|`YdTsJwT0Pd`lo>+!danBK6RDEG0Z)dUlc#^V$L^JHmak|0f zAR@5%K@q`{~#NeFkO~r20gQtft$l0@2FNPTB z6fO)6EDi*hjH}b3$SO3EUdTSChds+VkT}Dpy0Uyfy4EAB#)Cmjm`0* z+LyS6o7|@??CxKqXNoFxsjMu`MXqw-_qWBn#_6S%ozfvzec7}Sb`5q+bHeoemvh`& zWkppICli2ZH`*@<`S#V=%#%FJl7X;6qTiflx7()uBTrF_R0mzQKqD0CL>sI0gN_~7 zW=$C7hV37bs$ZbmpuY-`;YZ0aM27j^fTkl%RoojArXR_3$-llz8(`4xK>_5w5WBpCD zPwp6RP}OK{*z&STIKa!fSPF`9KA`nPKYBokQ_SGN*3VcaZGC7O9|RI52Z2ZeGZyfH z?(fqSka+>))5qn!_il`@2YTj|moLIb+@L;GSNXzI(Vc4I{4MpGO{HokySVqBAH12q zw^WVd7jq=d0+EY!33{G+tbp_d@*#F#7^h17BsSmAs)v9&anR^6zbH(6^P?S@1)Y83 zPCN^*hD!ylqDW4i)58NRjrmK%0bTl`<31(-lsL6^FK!GK)DY(J2sVCyzp=xIO7_*O z_-k5XFBURUQ`<5M@#6LrZZEml>uOi$U3+8%rkX_1Zi~^*vKNxnmyy6NeUs$9DPWCN|FmgrwALFFKXwPO*H0fw#Bqlpqi$g7Q4oWn9}>A_7My84tq~ukR6! zr|3fGuw}(KE?0Qvuu`VJTSM#2T^sqLxUk66)mChGrs2UYn8XxIKIhmFda9jG7h`Cu z;tXLQqV^y4AureEK4%Ay3{=*Pw}6`OBW?C@szfOCEvaZxa9K!a8+@Nh6>%B%d9zcg z>ZYPyS-IsG7W66&yl!lXMcCH0Z*99rq96`*s_3A})&l9M7oT1+?o7wMYx=;?lOcgQ z>ME*m;}Cr}8`Iuf>zCD5pv-JBlS}9#u=t(eM0pqO(!=h0g&fH4r1in4OJ8%uJV z;>31D0?y--VF9s1n0(w;Px^V3*`_^dVGZLcLYi6PDJB}E&rAc&Wby0NlqXWzEHk?z za^NMoPL(+4ljZipnI8s7`44e3Z-gGOo6^R~xm)XYqqi4Y`tuvLz#nEjPh*YrFR!+l zCdobvvsYX-QWP~KOQks6t_5R&Ssi%g#_(ofRu^)2iwr9IAZo6#@k5Vi z-Wc#BYVufP2FzvP?LDVRtB{^cz8pm9m8N9kpe!nc>k3_CP@~z(d$U)z`~S@zYPn1K zZ}5mwo{-u<&q(bzC(}hb=B4JQUVD|**}eZ#e5TCk1WG3AMgKJCrMG4bb0b@)z8XLK z?r#(Ha|u>LQ)yoLuHJW3?aImHxAF*$CSil*!N6JRlX41WsjPhRG^);aavDijx(s}e zSaZK;W$`?w$7wFjBIMkXx3ySAId9tjiNZZknL@lr6U;T^2?}u*1T?gJ?i$Y239B-q zi(E2lL8|j+vtfm2_z~6b#PI!!_}e*x_VB`Zc}L!f+;>;wEUL7MTpA%M4WC1%C|Fpb z#jTQ_#9+rsUGB3CQR3>b6w#4ESg_=thMTgDs;Qr75SZjM>uo3S8Uszw2ZFoY4vEZf zMDmOHwx7XMBjomkdU(&=eR)x00KS*(TsZOiO}O8hL;O(;>> zKS}Jge5+K>gzVF+IQjW@kfa-!!2m^ftXZ z^_ga9NT9{c8^uq!VcBN;xg|Fk)8y+|Zf!H4a zioqT}Sf`UAz8(P%-=Z@xAOvY{^a;3>0o0LUztq4U&-DmE{D|?thxTA$Tnx_G8_xzh;=6MIAObG{&;BR54_`7ZzQJYP_aTi}!hB&RU;If+^rsYmI`9``Ih&5-+eG91pJPMUs9S aV(X(rD|0?z1brJ-Vh|T#cyOhDJNg&30`aK; diff --git a/users/eliza/rag-content/resume/Eliza_Morgan_Resume.md b/users/eliza/rag-content/resume/Eliza_Morgan_Resume.md deleted file mode 100644 index 429b2a0..0000000 --- a/users/eliza/rag-content/resume/Eliza_Morgan_Resume.md +++ /dev/null @@ -1,94 +0,0 @@ -Eliza Morgan -Portland, Oregon | eliza.morgan@nomail.com | (555) 867-5309 -linkedin.com/in/elizamorgan18383 - -**Professional Summary** - -I am a ficticious persona generated by AI to seed the Backstory system for -testing, evaluation, and demo purposes. - -Conservation botanist with over a decade of experience leading ecological restoration projects, advancing rare plant propagation methods, and managing native plant programs across the Pacific Northwest. Proven record of scientific innovation, collaborative project leadership, and effective stakeholder engagement. Passionate about preserving botanical diversity through applied research, restoration, and public education. - -**Professional Experience** - -**Senior Restoration Botanist** - -Pacific Northwest Conservation Alliance, Portland, OR | June 2020 – Present - -- Directed restoration efforts across 2,000+ acres of degraded habitat with a focus on endangered plant communities. -- Managed propagation and deployment of 75,000+ native plants annually. -- Supervised and mentored a cross-functional team of botanists and technicians. -- Secured $750,000+ in grant funding and led stakeholder engagement with tribal, governmental, and NGO partners. -- Developed seeding and reintroduction techniques increasing native species diversity by 40%. - -* Key Projects & Achievements: - -- Willamette Valley Prairie Restoration: Reintroduced 12 threatened species; 85% establishment success. - -- Mount Hood Meadow Rehabilitation: Reduced invasive cover by 90% post-wildfire. - -- Columbia River Gorge Rare Plant Recovery: Created new populations of 5 federally listed species. - -- Award: Excellence in Ecological Restoration, Society for Ecological Restoration (2023) - -- Media: Featured in OPB documentary on native plant conservation (2022) - -- Publications: 2 peer-reviewed articles on restoration innovations. - -**Plant Conservation Specialist** - -Oregon Botanical Gardens, Portland, OR | April 2017 – May 2020 - -- Led ex-situ conservation for 45 endangered Pacific Northwest plant species. -- Developed genetic management protocols and created Oregon Botanical’s first dedicated conservation nursery. -- Collaborated with the Center for Plant Conservation and state agencies on reintroduction planning. -- Authored seed banking protocols adopted by three other institutions. - -* Key Contributions: - -- Propagated 3 species never before cultivated; pioneered tissue culture and seed treatments. - -- Developed Kincaid’s lupine protocol doubling germination success. - -- Designed alpine and serpentine plant displays for public education. - -- Award: Conservation Innovation Award, American Public Gardens Association (2019) - -**Research Assistant** - -Institute for Applied Ecology, Corvallis, OR | January 2015 – March 2017 - -- Conducted botanical field surveys, greenhouse propagation, and data analysis for native plant research projects. -- Co-authored 3 peer-reviewed studies on prairie restoration, seed viability, and plant demography. -- Supported endangered species monitoring and habitat restoration for the Fender’s blue butterfly. - -* Highlighted Projects: - -- Wet Prairie Restoration: Established 15 native species across experimental plots. - -- Seed Viability Studies: Tested dormancy-breaking treatments for 30+ species. - -- Publications: - -- Morgan, E. et al. (2017). \*Northwest Science\* - -- Morgan, E. et al. (2016). \*Restoration Ecology\*, \*Native Plants Journal\* - -**Education** - -B.S. in Botany | Oregon State University, Corvallis, OR | Graduated: 2014 - -**Skills & Expertise** - -- Native plant propagation & nursery management -- Ecological restoration & reintroduction planning -- Grant writing & budget management -- GIS & GPS mapping | R statistical analysis -- Team leadership & staff mentoring -- Science communication & outreach - -**Professional Affiliations** - -- Society for Ecological Restoration (SER) -- American Public Gardens Association (APGA) -- Center for Plant Conservation (CPC) \ No newline at end of file