from dataclasses import dataclass, field from langchain_core.tools import tool import httpx from ask_candid.base.config.rest import GOLDEN_ORG, LOI_WRITER @dataclass class LetterOfInterest: opening: str = field(default="") org_desc: str = field(default="") need: str = field(default="") project: str = field(default="") request: str = field(default="") conclusion: str = field(default="") @property def letter(self): return f"""{self.opening} {self.org_desc} {self.need} {self.project} {self.request} {self.conclusion} """ @tool def estimate_budget( nonprofit_id: int, funder_id: int, project_description: str, # ctx: Context ) -> str: """This is an optional tool for estimating project budgets. Some users will already know what their budget is, or know how much money they are seeking from a grant, in which case this tool should not be used. This tool also provides guidance on setting a budget, and ultimately the user needs to decided based on the output from this tool Parameters ---------- nonprofit_id : int The unique identifier of the requesting organization. This will need to be found from a search using inputs elicited from the requeter funder_id : int The unique identifier of the funding organization which may be awarding a grant to the requester. This will need to be found from a search using inputs elicited from the requeter, or from recommendations project_description : str Natural language text describing the project/program that the user is requesting funding for Returns ------- str Budget guidance, including context on the funder's ability to provide the budget in question """ recip_data = httpx.get( url=GOLDEN_ORG.endpoint("v1/organization"), params={"id": nonprofit_id}, headers={**GOLDEN_ORG.header}, # type: ignore timeout=30 ).json().get("document_data", {}).get("preferred_data", {}).get("data", {}) funder_data = httpx.get( url=GOLDEN_ORG.endpoint("v1/organization"), params={"id": funder_id}, headers={**GOLDEN_ORG.header}, # type: ignore timeout=30 ).json().get("document_data", {}).get("preferred_data", {}).get("data", {}) return httpx.post( url=LOI_WRITER.endpoint("budget"), json={ "recipient_candid_entity_id": nonprofit_id, "program_description": project_description, "recipient_data": recip_data, "funder_data": funder_data }, headers={**LOI_WRITER.header}, # type: ignore timeout=30 ).json().get("response", "No budget could be estimated") @tool def draft_loi( nonprofit_id: int, funder_id: int, project_description: str, budget: int, ) -> str: """Generate a letter of interest/intent from a chain-of-thought prompt chain using Candid's golden data and any inputs specified by the user, and/or recommended content. The output of this tool is the actual letter draft, please do not make changes to it other than adding headers and/or footers. Parameters ---------- nonprofit_id : int The unique identifier of the requesting organization. This will need to be found from a search using inputs elicited from the requeter funder_id : int The unique identifier of the funding organization which may be awarding a grant to the requester. This will need to be found from a search using inputs elicited from the requeter, or from recommendations project_description : str Natural language text describing the project/program that the user is requesting funding for budget : int The dollar amount (in USD) that the user is requesting for funding. This should be specified by the user, prompt for this if needed. Returns ------- str Formatted letter of interest """ client = httpx.Client(headers={**LOI_WRITER.header}, timeout=30, base_url=LOI_WRITER.url) # type: ignore def _make_request(ept: str, payload: dict): # return httpx.get( # url=LOI_WRITER.endpoint(ept), # params=payload, # headers={**LOI_WRITER.header}, # type: ignore # timeout=30 # ).json().get("response", "") return client.get(url=LOI_WRITER.endpoint(ept), params=payload).json().get("response", "") data = _make_request( ept="organization/autofill", payload={"recipient_candid_entity_id": nonprofit_id, "funder_candid_entity_id": funder_id} ) recip: dict = data.get("recipient_data", {}) funder: dict = data.get("funder_data", {}) pair_history: str = data.get("funding_history_text", "") sections = ( ("opening", "writer/opening"), ("organization description", "writer/org"), ("need statement", "writer/need"), ("project description", "writer/project"), ("funding request", "writer/fund"), ("conclusion", "writer/conclusion") ) output = LetterOfInterest() for _, (section, endpoint) in enumerate(sections, start=1): if section == "opening": output.opening = _make_request( ept=endpoint, payload={ "funder_name": [ n["name"] for n in funder.get("org_data", {}).get("names", []) if n["name_type"] == "main" ][0], "recipient_name": [ n["name"] for n in recip.get("org_data", {}).get("names", []) if n["name_type"] == "main" ][0], "project_purpose": project_description, "amount": budget, "prior_contact": None, "connection": None } ) elif section == "organization description": output.org_desc = _make_request( ept=endpoint, payload={ "opening": output.opening, "history": pair_history, "recipient_mission_statement": recip.get("mission_statement_text", ""), "capacity": recip.get("capacity_text", ""), "path": None, "accomplishment": recip.get("data_text", "") } ) elif section == "need statement": output.need = httpx.get( url=GOLDEN_ORG.endpoint(endpoint), params={ "recipient_desc": output.org_desc, "funder_mission_statement": funder.get("mission_statement_text", ""), "target": None, "data": None, }, headers={**GOLDEN_ORG.header}, # type: ignore timeout=30 ).json().get("response", "") elif section == "project description": output.project = _make_request( ept=endpoint, payload={ "need": output.need, "projects": project_description, "desired_objectives": None, "major_activities": None, "key_staff": None, "stand_out": None, "success": None } ) elif section == "funding request": output.request = _make_request( ept=endpoint, payload={ "project_desc": output.project, "amount": budget, "funding_history": pair_history, "other_funding": None, } ) elif section == "conclusion": output.conclusion = _make_request( ept=endpoint, payload={ "funding_request": output.request, "project_desc": output.project, "follow_up": recip.get("contact_text", ""), } ) client.close() return output.letter