File size: 8,280 Bytes
68e9b80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
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