From 613dc7fd19ff3065736c4cae154b6e745bfe16a9 Mon Sep 17 00:00:00 2001 From: Muhammad Talha Imran <92talhaimran@gmail.com> Date: Sun, 13 Jul 2025 19:27:28 +0200 Subject: [PATCH 1/7] Refactor lead generation agent for local usage --- Agents/Crawler/README.md | 21 ++-- Agents/Crawler/ai_lead_generation_agent.py | 113 +++++++-------------- Agents/Crawler/requirements.txt | 10 +- 3 files changed, 53 insertions(+), 91 deletions(-) diff --git a/Agents/Crawler/README.md b/Agents/Crawler/README.md index 8916d11..5c0adfe 100644 --- a/Agents/Crawler/README.md +++ b/Agents/Crawler/README.md @@ -1,13 +1,12 @@ -## 🎯 AI Lead Generation Agent - Powered by Firecrawl's Extract Endpoint +## 🎯 AI Lead Generation Agent -The AI Lead Generation Agent automates the process of finding and qualifying potential leads from Quora. It uses Firecrawl's search and the new Extract endpoint to identify relevant user profiles, extract valuable information, and organize it into a structured format in Google Sheets. This agent helps sales and marketing teams efficiently build targeted lead lists while saving hours of manual research. +This Streamlit application searches Quora using DuckDuckGo, extracts user interactions with a local Mistral model and saves the results to an Excel file. ### Features -- **Targeted Search**: Uses Firecrawl's search endpoint to find relevant Quora URLs based on your search criteria -- **Intelligent Extraction**: Leverages Firecrawl's new Extract endpoint to pull user information from Quora profiles -- **Automated Processing**: Formats extracted user information into a clean, structured format -- **Google Sheets Integration**: Automatically creates and populates Google Sheets with lead information -- **Customizable Criteria**: Allows you to define specific search parameters to find your ideal leads for your niche +- Searches Quora links via DuckDuckGo +- Uses a local `mistral:7b-instruct` model for extraction +- Outputs the collected data to an Excel spreadsheet +- Choose how many links to process ### Setup @@ -17,4 +16,10 @@ The AI Lead Generation Agent automates the process of finding and qualifying pot pip install -r requirements.txt ``` -2. When running the Streamlit app you'll be prompted for a **HuggingFace Repo ID**. Provide the repo for the chat model you would like to use (e.g. `meta-llama/Llama-3-8B-Instruct`). +2. Run the Streamlit app: + + ```bash + streamlit run ai_lead_generation_agent.py + ``` + +When prompted, provide the Excel filename and describe the leads you are looking for. diff --git a/Agents/Crawler/ai_lead_generation_agent.py b/Agents/Crawler/ai_lead_generation_agent.py index df232e1..df75f15 100644 --- a/Agents/Crawler/ai_lead_generation_agent.py +++ b/Agents/Crawler/ai_lead_generation_agent.py @@ -1,14 +1,13 @@ import streamlit as st -from agno.agent import Agent from duckduckgo_search import ddg -from langchain_community.chat_models.huggingface import ChatHuggingFaceHub +from langchain_community.chat_models import ChatOllama from langchain_community.document_loaders import PlaywrightURLLoader from langchain.chains import LLMChain from langchain.prompts import ChatPromptTemplate from langchain.output_parsers import PydanticOutputParser from pydantic import BaseModel, Field from typing import List -from composio_phidata import Action, ComposioToolSet +import pandas as pd import json class QuoraUserInteractionSchema(BaseModel): @@ -27,16 +26,12 @@ def search_for_urls(company_description: str, num_links: int) -> List[str]: results = ddg(query, max_results=num_links) or [] return [r.get("href") for r in results if r.get("href")] -def extract_user_info_from_urls(urls: List[str], hf_api_token: str) -> List[dict]: +def extract_user_info_from_urls(urls: List[str]) -> List[dict]: user_info_list = [] loader = PlaywrightURLLoader(urls, continue_on_failure=True) docs = loader.load() - llm = ChatHuggingFaceHub( - repo_id="mistralai/Mistral-7B-Instruct-v0.1", - model_kwargs={"temperature": 0.0, "max_new_tokens": 512}, - huggingfacehub_api_token=hf_api_token, - ) + llm = ChatOllama(model="mistral:7b-instruct") parser = PydanticOutputParser(pydantic_object=QuoraPageSchema) prompt = ChatPromptTemplate.from_messages( @@ -87,51 +82,15 @@ def format_user_info_to_flattened_json(user_info_list: List[dict]) -> List[dict] return flattened_data -def create_google_sheets_agent(composio_api_key: str, hf_api_token: str) -> Agent: - composio_toolset = ComposioToolSet(api_key=composio_api_key) - google_sheets_tool = composio_toolset.get_tools(actions=[Action.GOOGLESHEETS_SHEET_FROM_JSON])[0] - - google_sheets_agent = Agent( - model=ChatHuggingFaceHub( - repo_id="mistralai/Mistral-7B-Instruct-v0.1", - model_kwargs={"temperature": 0.0, "max_new_tokens": 512}, - huggingfacehub_api_token=hf_api_token, - ), - tools=[google_sheets_tool], - show_tool_calls=True, - system_prompt="You are an expert at creating and updating Google Sheets. You will be given user information in JSON format, and you need to write it into a new Google Sheet.", - markdown=True - ) - return google_sheets_agent -def write_to_google_sheets(flattened_data: List[dict], composio_api_key: str, hf_api_token: str) -> str: - google_sheets_agent = create_google_sheets_agent(composio_api_key, hf_api_token) - - try: - message = ( - "Create a new Google Sheet with this data. " - "The sheet should have these columns: Website URL, Username, Bio, Post Type, Timestamp, Upvotes, and Links in the same order as mentioned. " - "Here's the data in JSON format:\n\n" - f"{json.dumps(flattened_data, indent=2)}" - ) - - create_sheet_response = google_sheets_agent.run(message) - - if "https://docs.google.com/spreadsheets/d/" in create_sheet_response.content: - google_sheets_link = create_sheet_response.content.split("https://docs.google.com/spreadsheets/d/")[1].split(" ")[0] - return f"https://docs.google.com/spreadsheets/d/{google_sheets_link}" - except Exception: - pass - return None - -def create_prompt_transformation_agent(hf_api_token: str) -> Agent: - return Agent( - model=ChatHuggingFaceHub( - repo_id="mistralai/Mistral-7B-Instruct-v0.1", - model_kwargs={"temperature": 0.0, "max_new_tokens": 512}, - huggingfacehub_api_token=hf_api_token, - ), - system_prompt="""You are an expert at transforming detailed user queries into concise company descriptions. +def write_to_excel(flattened_data: List[dict], path: str) -> None: + df = pd.DataFrame(flattened_data) + df.to_excel(path, index=False) + + +def transform_query(user_query: str) -> str: + llm = ChatOllama(model="mistral:7b-instruct") + system_prompt = """You are an expert at transforming detailed user queries into concise company descriptions. Your task is to extract the core business/product focus in 3-4 words. Examples: @@ -147,22 +106,23 @@ def create_prompt_transformation_agent(hf_api_token: str) -> Agent: Input: "Need to find businesses interested in implementing machine learning solutions for fraud detection" Output: "ML fraud detection" -Always focus on the core product/service and keep it concise but clear.""", - markdown=True - ) +Always focus on the core product/service and keep it concise but clear.""" + prompt = ChatPromptTemplate.from_messages([ + ("system", system_prompt), + ("human", "{query}"), + ]) + chain = LLMChain(llm=llm, prompt=prompt) + return chain.predict(query=user_query).strip() def main(): st.title("🎯 AI Lead Generation Agent") st.info("Generate leads from Quora by searching for relevant posts and extracting user information.") with st.sidebar: - st.header("API Keys") - hf_api_token = st.text_input("HuggingFace API Token", type="password") - composio_api_key = st.text_input("Composio API Key", type="password") - st.caption(" Get your Composio API key from [Composio's website](https://composio.ai)") - + st.header("Configuration") + output_file = st.text_input("Output Excel filename", value="leads.xlsx") num_links = st.number_input("Number of links to search", min_value=1, max_value=10, value=3) - + if st.button("Reset"): st.session_state.clear() st.experimental_rerun() @@ -174,16 +134,15 @@ def main(): ) if st.button("Generate Leads"): - if not all([hf_api_token, composio_api_key, user_query]): - st.error("Please fill in all the API keys and describe what leads you're looking for.") + if not all([user_query, output_file]): + st.error("Please provide a description and output filename.") else: with st.spinner("Processing your query..."): - transform_agent = create_prompt_transformation_agent(hf_api_token) - company_description = transform_agent.run(f"Transform this query into a concise 3-4 word company description: {user_query}") - st.write("🎯 Searching for:", company_description.content) + company_description = transform_query(user_query) + st.write("🎯 Searching for:", company_description) with st.spinner("Searching for relevant URLs..."): - urls = search_for_urls(company_description.content, num_links) + urls = search_for_urls(company_description, num_links) if urls: st.subheader("Quora Links Used:") @@ -191,22 +150,18 @@ def main(): st.write(url) with st.spinner("Extracting user info from URLs..."): - user_info_list = extract_user_info_from_urls(urls, hf_api_token) + user_info_list = extract_user_info_from_urls(urls) with st.spinner("Formatting user info..."): flattened_data = format_user_info_to_flattened_json(user_info_list) - with st.spinner("Writing to Google Sheets..."): - google_sheets_link = write_to_google_sheets(flattened_data, composio_api_key, hf_api_token) - - if google_sheets_link: - st.success("Lead generation and data writing to Google Sheets completed successfully!") - st.subheader("Google Sheets Link:") - st.markdown(f"[View Google Sheet]({google_sheets_link})") - else: - st.error("Failed to retrieve the Google Sheets link.") + with st.spinner("Writing to Excel..."): + write_to_excel(flattened_data, output_file) + st.success("Lead generation completed successfully!") + st.subheader("Saved File:") + st.write(output_file) else: st.warning("No relevant URLs found.") if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/Agents/Crawler/requirements.txt b/Agents/Crawler/requirements.txt index 1eb65a0..2f45468 100644 --- a/Agents/Crawler/requirements.txt +++ b/Agents/Crawler/requirements.txt @@ -1,5 +1,7 @@ -agno -composio-phidata -composio==0.1.1 +duckduckgo_search +langchain_community +playwright +pandas +openpyxl pydantic==2.10.5 -streamlit \ No newline at end of file +streamlit From c95273f7135292d2db547febb03700c39fa0ffe7 Mon Sep 17 00:00:00 2001 From: Muhammad Talha Imran <92talhaimran@gmail.com> Date: Sun, 13 Jul 2025 19:32:58 +0200 Subject: [PATCH 2/7] Remove pydantic dependency --- Agents/Crawler/ai_lead_generation_agent.py | 35 +++++++++------------- Agents/Crawler/requirements.txt | 1 - 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/Agents/Crawler/ai_lead_generation_agent.py b/Agents/Crawler/ai_lead_generation_agent.py index df75f15..aac2803 100644 --- a/Agents/Crawler/ai_lead_generation_agent.py +++ b/Agents/Crawler/ai_lead_generation_agent.py @@ -4,22 +4,10 @@ from langchain_community.document_loaders import PlaywrightURLLoader from langchain.chains import LLMChain from langchain.prompts import ChatPromptTemplate -from langchain.output_parsers import PydanticOutputParser -from pydantic import BaseModel, Field from typing import List import pandas as pd import json -class QuoraUserInteractionSchema(BaseModel): - username: str = Field(description="The username of the user who posted the question or answer") - bio: str = Field(description="The bio or description of the user") - post_type: str = Field(description="The type of post, either 'question' or 'answer'") - timestamp: str = Field(description="When the question or answer was posted") - upvotes: int = Field(default=0, description="Number of upvotes received") - links: List[str] = Field(default_factory=list, description="Any links included in the post") - -class QuoraPageSchema(BaseModel): - interactions: List[QuoraUserInteractionSchema] = Field(description="List of all user interactions (questions and answers) on the page") def search_for_urls(company_description: str, num_links: int) -> List[str]: query = f"site:quora.com {company_description}" @@ -33,10 +21,17 @@ def extract_user_info_from_urls(urls: List[str]) -> List[dict]: llm = ChatOllama(model="mistral:7b-instruct") - parser = PydanticOutputParser(pydantic_object=QuoraPageSchema) prompt = ChatPromptTemplate.from_messages( [ - ("system", "Extract all user interactions from this Quora page. {format_instructions}"), + ( + "system", + ( + "Extract all user interactions from this Quora page as JSON. " + "Return a dictionary with an 'interactions' key containing a list " + "of objects with 'username', 'bio', 'post_type', 'timestamp', " + "'upvotes' and 'links'." + ), + ), ("human", "{page_content}"), ] ) @@ -44,16 +39,14 @@ def extract_user_info_from_urls(urls: List[str]) -> List[dict]: for url, doc in zip(urls, docs): try: - result = chain.predict( - page_content=doc.page_content, - format_instructions=parser.get_format_instructions(), - ) - parsed = parser.parse(result) - if parsed.interactions: + result = chain.predict(page_content=doc.page_content) + parsed = json.loads(result) + interactions = parsed.get("interactions", []) + if interactions: user_info_list.append( { "website_url": url, - "user_info": [i.dict() for i in parsed.interactions], + "user_info": interactions, } ) except Exception: diff --git a/Agents/Crawler/requirements.txt b/Agents/Crawler/requirements.txt index 2f45468..f2c8c20 100644 --- a/Agents/Crawler/requirements.txt +++ b/Agents/Crawler/requirements.txt @@ -3,5 +3,4 @@ langchain_community playwright pandas openpyxl -pydantic==2.10.5 streamlit From 84f3a1de171c00c58870b3b11dda276f191b7695 Mon Sep 17 00:00:00 2001 From: Muhammad Talha Imran <92talhaimran@gmail.com> Date: Sun, 13 Jul 2025 19:39:59 +0200 Subject: [PATCH 3/7] Fix DuckDuckGo import --- Agents/Crawler/ai_lead_generation_agent.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Agents/Crawler/ai_lead_generation_agent.py b/Agents/Crawler/ai_lead_generation_agent.py index aac2803..2e025d0 100644 --- a/Agents/Crawler/ai_lead_generation_agent.py +++ b/Agents/Crawler/ai_lead_generation_agent.py @@ -1,5 +1,5 @@ import streamlit as st -from duckduckgo_search import ddg +from duckduckgo_search import DDGS from langchain_community.chat_models import ChatOllama from langchain_community.document_loaders import PlaywrightURLLoader from langchain.chains import LLMChain @@ -11,8 +11,9 @@ def search_for_urls(company_description: str, num_links: int) -> List[str]: query = f"site:quora.com {company_description}" - results = ddg(query, max_results=num_links) or [] - return [r.get("href") for r in results if r.get("href")] + with DDGS() as ddgs: + results = ddgs.text(query, max_results=num_links) or [] + return [r.get("href") or r.get("url") for r in results if r.get("href") or r.get("url")] def extract_user_info_from_urls(urls: List[str]) -> List[dict]: user_info_list = [] From 9af48f319dac966e99bc462d89f275fc9ba229a3 Mon Sep 17 00:00:00 2001 From: Muhammad Talha Imran <92talhaimran@gmail.com> Date: Sun, 13 Jul 2025 19:48:02 +0200 Subject: [PATCH 4/7] Remove Playwright loader --- Agents/Crawler/README.md | 3 ++- Agents/Crawler/ai_lead_generation_agent.py | 19 ++++++++++++++----- Agents/Crawler/requirements.txt | 3 ++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Agents/Crawler/README.md b/Agents/Crawler/README.md index 5c0adfe..6376d90 100644 --- a/Agents/Crawler/README.md +++ b/Agents/Crawler/README.md @@ -1,9 +1,10 @@ ## 🎯 AI Lead Generation Agent -This Streamlit application searches Quora using DuckDuckGo, extracts user interactions with a local Mistral model and saves the results to an Excel file. +This Streamlit application searches Quora using DuckDuckGo, downloads each page with simple HTTP requests, extracts user interactions with a local Mistral model and saves the results to an Excel file. ### Features - Searches Quora links via DuckDuckGo +- Fetches pages directly via HTTP requests - Uses a local `mistral:7b-instruct` model for extraction - Outputs the collected data to an Excel spreadsheet - Choose how many links to process diff --git a/Agents/Crawler/ai_lead_generation_agent.py b/Agents/Crawler/ai_lead_generation_agent.py index 2e025d0..465980b 100644 --- a/Agents/Crawler/ai_lead_generation_agent.py +++ b/Agents/Crawler/ai_lead_generation_agent.py @@ -1,12 +1,13 @@ import streamlit as st from duckduckgo_search import DDGS from langchain_community.chat_models import ChatOllama -from langchain_community.document_loaders import PlaywrightURLLoader from langchain.chains import LLMChain from langchain.prompts import ChatPromptTemplate from typing import List import pandas as pd import json +import requests +from bs4 import BeautifulSoup def search_for_urls(company_description: str, num_links: int) -> List[str]: @@ -15,10 +16,17 @@ def search_for_urls(company_description: str, num_links: int) -> List[str]: results = ddgs.text(query, max_results=num_links) or [] return [r.get("href") or r.get("url") for r in results if r.get("href") or r.get("url")] + +def load_page_text(url: str) -> str: + """Fetch page HTML and return plain text.""" + headers = {"User-Agent": "Mozilla/5.0"} + resp = requests.get(url, headers=headers, timeout=10) + resp.raise_for_status() + soup = BeautifulSoup(resp.text, "html.parser") + return soup.get_text(separator=" ", strip=True) + def extract_user_info_from_urls(urls: List[str]) -> List[dict]: user_info_list = [] - loader = PlaywrightURLLoader(urls, continue_on_failure=True) - docs = loader.load() llm = ChatOllama(model="mistral:7b-instruct") @@ -38,9 +46,10 @@ def extract_user_info_from_urls(urls: List[str]) -> List[dict]: ) chain = LLMChain(llm=llm, prompt=prompt) - for url, doc in zip(urls, docs): + for url in urls: try: - result = chain.predict(page_content=doc.page_content) + page_content = load_page_text(url) + result = chain.predict(page_content=page_content) parsed = json.loads(result) interactions = parsed.get("interactions", []) if interactions: diff --git a/Agents/Crawler/requirements.txt b/Agents/Crawler/requirements.txt index f2c8c20..2fd7315 100644 --- a/Agents/Crawler/requirements.txt +++ b/Agents/Crawler/requirements.txt @@ -1,6 +1,7 @@ duckduckgo_search langchain_community -playwright pandas openpyxl +requests +beautifulsoup4 streamlit From be7aa2c5e5d7fd454309e9f63c6a4d6b54b53ce5 Mon Sep 17 00:00:00 2001 From: Muhammad Talha Imran <92talhaimran@gmail.com> Date: Sun, 13 Jul 2025 20:58:17 +0200 Subject: [PATCH 5/7] updated gitignore --- Agents/AI Service/.gitignore | 3 ++- Agents/Crawler/leads.xlsx | Bin 0 -> 5378 bytes 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Agents/Crawler/leads.xlsx diff --git a/Agents/AI Service/.gitignore b/Agents/AI Service/.gitignore index f821b65..f376201 100644 --- a/Agents/AI Service/.gitignore +++ b/Agents/AI Service/.gitignore @@ -1,2 +1,3 @@ # ignore all outputs -*.pdf \ No newline at end of file +*.pdf +*.xlsx \ No newline at end of file diff --git a/Agents/Crawler/leads.xlsx b/Agents/Crawler/leads.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..928af4a12a9d2e46b7e29c0fa74931a18bf8b2de GIT binary patch literal 5378 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 Date: Mon, 14 Jul 2025 10:01:34 +0200 Subject: [PATCH 6/7] moved gitignore --- Agents/{AI Service => }/.gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Agents/{AI Service => }/.gitignore (100%) diff --git a/Agents/AI Service/.gitignore b/Agents/.gitignore similarity index 100% rename from Agents/AI Service/.gitignore rename to Agents/.gitignore From 6437eb5411afbaab411f8db4ca4602bb6e57eccf Mon Sep 17 00:00:00 2001 From: Muhammad Talha Imran <92talhaimran@gmail.com> Date: Mon, 14 Jul 2025 10:13:36 +0200 Subject: [PATCH 7/7] udpated xlsx file --- Agents/Crawler/leads.xlsx | Bin 5378 -> 4784 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Agents/Crawler/leads.xlsx b/Agents/Crawler/leads.xlsx index 928af4a12a9d2e46b7e29c0fa74931a18bf8b2de..45e420dd95ec5f2c0607074097f56e4966738c3c 100644 GIT binary patch delta 1496 zcmZqD+MvoC;LXe;!oa}5!LTRb-9+9496&0H<=unn6R&F4TfKeZ5ICK?L)gQ^hpDhz zD8D=7pN3)9_itNLU$;D9v^e(f>F4y5Yx|tqTh42`_@o%d^RD|G`C>;R`yvg7O?C@{ zcghIY<-Bt$>N=Kw%Q@ti^IO5JbJrhCm=d@ofk~`T(ded>Zi$nt;@l5z`agcm+$EW2 zlF-g)o4Vo@*Ni!8$Lb$n*jm8oV{qunr*g)#PIFe*Cq3ue5L|Sk%=2q){I}JX=I;Uy z-oE_!^q#!LTD7O9efu|wmh|jMe13BM>fOKJGEX*LKfCF^$kngY?sMJiNc(HI^WTTv z9J>?Vocovjc=Ljshu33I=%WNc)XbPU$^vW*40F^O7=$#`+&t5c#X!B5z)x%iTii|S z^l6)`TH((uCWMJ;9siTiQ&L}j!9uL&?DIo`TV;ipnu6P~Kr$^_J}oVBaTz9`IDncdf2CjP;!^vx1AlT^}oavqb1%D1-pXpmHT~I<~}LxXVylI;By1Q&^+BTid84ZhLxWApcYPNc3{Umnv!;$Nj?sEIg#p}L$ zO|o2&pa+}xf9*r3S0qkbEU(*JnsFw%<-?1ED?_rp5~jKy&bwUrzSX!ofJt5AzGKdm z_;s==1x0^$#n`48E7~ZWIJnDg$8M{|ncD*weRU}KRB~+j_Sn7!-@}?Oc+_obYnRFE z31ryrzVM21-wXHZ=fCSOZMyOLINR+r8?Mae;OtE*5f=UOfBE(ozvtiGrMALWZEI->4amnP7(yaQbkh?Dh zT-cagWc5|{xPE)+r7WVJl*wYK#W$7lXs3?Wx&I;OXRT3B3dwt|vPsP{dy;UZ<`O=0 zQT4McA9#gFg}Me#3vqvKHPg}e>spN{&DK+&iLm-jJ=I&)oVjF`21~i&-o>e& zCCiUk+sePv)$Cg3rQEU9Sc8k};P#1bD|igpxiq^z$ku$eJ(YidWzE0k+kc~$R+HWM zo-qS+=VpEWC`J%ta*x0xut1@pCq!VckTtwOnH!<*n3(_o delta 2060 zcmZ8ic{J2(82&Mmbqr&7%_5jQGmrb5nHfV<($v-^=Bz_y3>jlL8;fGtBSC$$&b=@ zjwv)JAeP8yQqs%ub0;}w5qp;QXeSCfE_(Auk65}8J->VQy6B|ya*$M?)NccD&NxR#!gv9ItO5Y=@-;3r zFLZk#c9oHbg&_ctCPFgglLw#QKkxeqEzYd7N{y|#7Y#%PDLUkM89nK++oZPR)ZkhT zMexTjA{TpEP`&)l{e|il_u@$8Yo)AGm6SY`l*ry4cC8ML{vWS8R@5@elfp>(c5jZ=8WWd4G{S?6iCos_2~$& z^fSBoWbgj!18Pk|G?P9_WR4^5G~uXj6Kc2;>H6uaZRXoi=rQ^$kVYH!dV36Oh> z-#0mg$prLTcYQUfS;j^+S@KE_m*EVuKPwrQNF`RBYHbO?Pb}r(!t&l(nLTAF8TZoP zxxJRKi2q)V3$dJ!)y#N2b}BAPz@B&t-Ik<`n9t=L-FJb^0{_qxdl+inA{=LN^6rp7 zKf^0_YG%)bt@nsf8Sg0Jb)KNBzI#miB8l@^h{^mjfA4?*Eq5v&U6PpS2~g%c`kxY;P> zbu?dZiOtIxFrqkjz)&xo8hOjNqj9~4a}R}`^rJxv4qw;5lYSxI9X{Ao^u;r4A(1`R zJieGy(kL^@C!@UT`Jv3Pxb3nhnkktH9P6N&l#9GP45!;LYXUr{&p_bxdh@j{F(oWj z%WYdmaIoje-!gbYP>5$So!3Yy=G`F~P=^YY*TK+l79{5IdwrOEGsQBcp7ooN&ot_) zH>DDg8@TUQqUu;F9ZWfse#39F>&i3MO5-?=5H#}v>n^MMI=MO!XN7zm!vIrSX>A$)8}b zdv$@5EIax%Gk?k*t}oxxmZ6&|{l#Trn%WS6jCOyF9jnF2j8@rR`ZR-Ro7=cC7R~!e z|HUvgZ82$)B2hh_+*uT;^I-VW`+S=&b>nBfT?^j6yi?5{X^$pOU;e=^w}^tgn8TJK z9SPvmFILmkAJ;pow+Kjm2g&hzC;~_cOMzk#>-P+^KBA5@-?Ie&m$w(ayoUV zSDb9eSc>ZoEl0EJusv=u?CSI@BW*%>2KV5Axggz_p|!C`V`8YSLK{LpzPeEQ`oOG* znWk+-c#%rD+6p-i8qps#zUK7qhL72SRRuws;~K*~kv=EUc|~A}1JlPJ$5TG%?F40p z-Uk%Dl{qmy%dApg8zO{+HV^IWw+uQ>cx9rdvQY7-$7%lcf(=;ztlYVkSIcE}V*3twDN zg18jj3p@;)JFuI&TK5c>5Opde!T zH{b;``Zp&@G}!)Mx4yv+{XZ-8L=WuRj*tKB006>UVf)V_auEbc38IK!{320=kpUC{ z&ifO*1GKfacROn?ZgI%J@$4*9g(yaI0RVE#S|b05Y*iwcl1Pe}+%K73VhC;!Mlu#t f{neU~6q+KvMPY5F7yLJhhbZZ$7>buBwvGM;cX81x