ask-candid / ask_candid /tools /letter_gen.py
brainsqueeze's picture
Update issuelab query technique
68e9b80 verified
raw
history blame
8.28 kB
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