kensho-kfinance 3.2.4__py3-none-any.whl → 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/METADATA +3 -3
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/RECORD +57 -56
- kfinance/CHANGELOG.md +51 -0
- kfinance/client/batch_request_handling.py +3 -1
- kfinance/client/fetch.py +127 -54
- kfinance/client/kfinance.py +38 -39
- kfinance/client/meta_classes.py +50 -20
- kfinance/client/models/date_and_period_models.py +32 -7
- kfinance/client/models/decimal_with_unit.py +14 -2
- kfinance/client/models/response_models.py +33 -0
- kfinance/client/models/tests/test_decimal_with_unit.py +9 -0
- kfinance/client/tests/test_batch_requests.py +5 -4
- kfinance/client/tests/test_fetch.py +134 -58
- kfinance/client/tests/test_objects.py +207 -145
- kfinance/conftest.py +10 -0
- kfinance/domains/business_relationships/business_relationship_tools.py +17 -8
- kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +18 -16
- kfinance/domains/capitalizations/capitalization_models.py +7 -5
- kfinance/domains/capitalizations/capitalization_tools.py +38 -20
- kfinance/domains/capitalizations/tests/test_capitalization_tools.py +66 -36
- kfinance/domains/companies/company_models.py +22 -2
- kfinance/domains/companies/company_tools.py +49 -16
- kfinance/domains/companies/tests/test_company_tools.py +27 -9
- kfinance/domains/competitors/competitor_tools.py +19 -5
- kfinance/domains/competitors/tests/test_competitor_tools.py +22 -19
- kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +29 -8
- kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +13 -8
- kfinance/domains/earnings/earning_tools.py +73 -29
- kfinance/domains/earnings/tests/test_earnings_tools.py +52 -43
- kfinance/domains/line_items/line_item_models.py +372 -16
- kfinance/domains/line_items/line_item_tools.py +198 -46
- kfinance/domains/line_items/tests/test_line_item_tools.py +305 -39
- kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_models.py +46 -2
- kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +55 -74
- kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +61 -59
- kfinance/domains/prices/price_models.py +7 -6
- kfinance/domains/prices/price_tools.py +24 -16
- kfinance/domains/prices/tests/test_price_tools.py +47 -39
- kfinance/domains/segments/segment_models.py +17 -3
- kfinance/domains/segments/segment_tools.py +102 -42
- kfinance/domains/segments/tests/test_segment_tools.py +166 -37
- kfinance/domains/statements/statement_models.py +17 -3
- kfinance/domains/statements/statement_tools.py +130 -46
- kfinance/domains/statements/tests/test_statement_tools.py +251 -49
- kfinance/integrations/local_mcp/kfinance_mcp.py +1 -1
- kfinance/integrations/tests/test_example_notebook.py +57 -16
- kfinance/integrations/tool_calling/all_tools.py +5 -1
- kfinance/integrations/tool_calling/static_tools/get_n_quarters_ago.py +5 -0
- kfinance/integrations/tool_calling/static_tools/tests/test_get_lastest.py +13 -10
- kfinance/integrations/tool_calling/static_tools/tests/test_get_n_quarters_ago.py +2 -1
- kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +15 -4
- kfinance/integrations/tool_calling/tool_calling_models.py +18 -6
- kfinance/version.py +2 -2
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/WHEEL +0 -0
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/licenses/LICENSE +0 -0
- {kensho_kfinance-3.2.4.dist-info → kensho_kfinance-4.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,21 +1,46 @@
|
|
|
1
1
|
from requests_mock import Mocker
|
|
2
2
|
|
|
3
3
|
from kfinance.client.kfinance import Client
|
|
4
|
-
from kfinance.conftest import SPGI_COMPANY_ID
|
|
5
4
|
from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
|
|
6
5
|
from kfinance.domains.statements.statement_models import StatementType
|
|
7
6
|
from kfinance.domains.statements.statement_tools import (
|
|
8
7
|
GetFinancialStatementFromIdentifiers,
|
|
9
8
|
GetFinancialStatementFromIdentifiersArgs,
|
|
9
|
+
GetFinancialStatementFromIdentifiersResp,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class TestGetFinancialStatementFromIdentifiers:
|
|
14
14
|
statement_resp = {
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
15
|
+
"currency": "USD",
|
|
16
|
+
"periods": {
|
|
17
|
+
"CY2020": {
|
|
18
|
+
"period_end_date": "2020-12-31",
|
|
19
|
+
"num_months": 12,
|
|
20
|
+
"statements": [
|
|
21
|
+
{
|
|
22
|
+
"name": "Income Statement",
|
|
23
|
+
"line_items": [
|
|
24
|
+
{"name": "Revenues", "value": "7442000000.000000", "sources": []},
|
|
25
|
+
{"name": "Total Revenues", "value": "7442000000.000000", "sources": []},
|
|
26
|
+
],
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
"CY2021": {
|
|
31
|
+
"period_end_date": "2021-12-31",
|
|
32
|
+
"num_months": 12,
|
|
33
|
+
"statements": [
|
|
34
|
+
{
|
|
35
|
+
"name": "Income Statement",
|
|
36
|
+
"line_items": [
|
|
37
|
+
{"name": "Revenues", "value": "8243000000.000000", "sources": []},
|
|
38
|
+
{"name": "Total Revenues", "value": "8243000000.000000", "sources": []},
|
|
39
|
+
],
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
19
44
|
}
|
|
20
45
|
|
|
21
46
|
def test_get_financial_statement_from_identifiers(
|
|
@@ -27,33 +52,88 @@ class TestGetFinancialStatementFromIdentifiers:
|
|
|
27
52
|
THEN we get back the SPGI income statement and an error for the non-existent company.
|
|
28
53
|
"""
|
|
29
54
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
55
|
+
# Mock the unified_fetch_id_triples response
|
|
56
|
+
requests_mock.post(
|
|
57
|
+
url="https://kfinance.kensho.com/api/v1/ids",
|
|
58
|
+
json={
|
|
59
|
+
"identifiers_to_id_triples": {
|
|
60
|
+
"SPGI": {
|
|
61
|
+
"company_id": 21719,
|
|
62
|
+
"security_id": 2629107,
|
|
63
|
+
"trading_item_id": 2629108,
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"errors": {
|
|
67
|
+
"NON-EXISTENT": "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Mock the fetch_statement response
|
|
73
|
+
requests_mock.post(
|
|
74
|
+
url="https://kfinance.kensho.com/api/v1/statements/",
|
|
75
|
+
json={"results": {"21719": self.statement_resp}, "errors": {}},
|
|
33
76
|
)
|
|
34
|
-
expected_response =
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
77
|
+
expected_response = GetFinancialStatementFromIdentifiersResp.model_validate(
|
|
78
|
+
{
|
|
79
|
+
"results": {
|
|
80
|
+
"SPGI": {
|
|
81
|
+
"currency": "USD",
|
|
82
|
+
"periods": {
|
|
83
|
+
"CY2020": {
|
|
84
|
+
"period_end_date": "2020-12-31",
|
|
85
|
+
"num_months": 12,
|
|
86
|
+
"statements": [
|
|
87
|
+
{
|
|
88
|
+
"name": "Income Statement",
|
|
89
|
+
"line_items": [
|
|
90
|
+
{
|
|
91
|
+
"name": "Revenues",
|
|
92
|
+
"value": "7442000000.000000",
|
|
93
|
+
"sources": [],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "Total Revenues",
|
|
97
|
+
"value": "7442000000.000000",
|
|
98
|
+
"sources": [],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
"CY2021": {
|
|
105
|
+
"period_end_date": "2021-12-31",
|
|
106
|
+
"num_months": 12,
|
|
107
|
+
"statements": [
|
|
108
|
+
{
|
|
109
|
+
"name": "Income Statement",
|
|
110
|
+
"line_items": [
|
|
111
|
+
{
|
|
112
|
+
"name": "Revenues",
|
|
113
|
+
"value": "8243000000.000000",
|
|
114
|
+
"sources": [],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "Total Revenues",
|
|
118
|
+
"value": "8243000000.000000",
|
|
119
|
+
"sources": [],
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
},
|
|
45
125
|
},
|
|
46
126
|
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
127
|
+
},
|
|
128
|
+
"errors": [
|
|
129
|
+
"No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
130
|
+
],
|
|
131
|
+
}
|
|
132
|
+
)
|
|
53
133
|
|
|
54
134
|
tool = GetFinancialStatementFromIdentifiers(kfinance_client=mock_client)
|
|
55
135
|
args = GetFinancialStatementFromIdentifiersArgs(
|
|
56
|
-
identifiers=["SPGI", "
|
|
136
|
+
identifiers=["SPGI", "NON-EXISTENT"], statement=StatementType.income_statement
|
|
57
137
|
)
|
|
58
138
|
response = tool.run(args.model_dump(mode="json"))
|
|
59
139
|
assert response == expected_response
|
|
@@ -66,32 +146,154 @@ class TestGetFinancialStatementFromIdentifiers:
|
|
|
66
146
|
"""
|
|
67
147
|
|
|
68
148
|
company_ids = [1, 2]
|
|
69
|
-
expected_response =
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
149
|
+
expected_response = GetFinancialStatementFromIdentifiersResp.model_validate(
|
|
150
|
+
{
|
|
151
|
+
"results": {
|
|
152
|
+
"C_1": {
|
|
153
|
+
"currency": "USD",
|
|
154
|
+
"periods": {
|
|
155
|
+
"CY2021": {
|
|
156
|
+
"period_end_date": "2021-12-31",
|
|
157
|
+
"num_months": 12,
|
|
158
|
+
"statements": [
|
|
159
|
+
{
|
|
160
|
+
"name": "Income Statement",
|
|
161
|
+
"line_items": [
|
|
162
|
+
{
|
|
163
|
+
"name": "Revenues",
|
|
164
|
+
"value": "8243000000.000000",
|
|
165
|
+
"sources": [],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "Total Revenues",
|
|
169
|
+
"value": "8243000000.000000",
|
|
170
|
+
"sources": [],
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
"C_2": {
|
|
179
|
+
"currency": "USD",
|
|
180
|
+
"periods": {
|
|
181
|
+
"CY2021": {
|
|
182
|
+
"period_end_date": "2021-12-31",
|
|
183
|
+
"num_months": 12,
|
|
184
|
+
"statements": [
|
|
185
|
+
{
|
|
186
|
+
"name": "Income Statement",
|
|
187
|
+
"line_items": [
|
|
188
|
+
{
|
|
189
|
+
"name": "Revenues",
|
|
190
|
+
"value": "8243000000.000000",
|
|
191
|
+
"sources": [],
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"name": "Total Revenues",
|
|
195
|
+
"value": "8243000000.000000",
|
|
196
|
+
"sources": [],
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Mock the unified_fetch_id_triples response
|
|
209
|
+
requests_mock.post(
|
|
210
|
+
url="https://kfinance.kensho.com/api/v1/ids",
|
|
211
|
+
json={
|
|
212
|
+
"identifiers_to_id_triples": {
|
|
213
|
+
"C_1": {"company_id": 1, "security_id": 101, "trading_item_id": 201},
|
|
214
|
+
"C_2": {"company_id": 2, "security_id": 102, "trading_item_id": 202},
|
|
86
215
|
},
|
|
216
|
+
"errors": {},
|
|
217
|
+
},
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Mock the fetch_statement response
|
|
221
|
+
requests_mock.post(
|
|
222
|
+
url="https://kfinance.kensho.com/api/v1/statements/",
|
|
223
|
+
json={"results": {"1": self.statement_resp, "2": self.statement_resp}, "errors": {}},
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
tool = GetFinancialStatementFromIdentifiers(kfinance_client=mock_client)
|
|
227
|
+
args = GetFinancialStatementFromIdentifiersArgs(
|
|
228
|
+
identifiers=[f"{COMPANY_ID_PREFIX}{company_id}" for company_id in company_ids],
|
|
229
|
+
statement=StatementType.income_statement,
|
|
230
|
+
)
|
|
231
|
+
response = tool.run(args.model_dump(mode="json"))
|
|
232
|
+
assert response == expected_response
|
|
233
|
+
|
|
234
|
+
def test_empty_most_recent_request(self, requests_mock: Mocker, mock_client: Client) -> None:
|
|
235
|
+
"""
|
|
236
|
+
GIVEN the GetFinancialStatementFromIdentifiers tool
|
|
237
|
+
WHEN we request most recent statement for multiple companies
|
|
238
|
+
THEN we only get back the most recent statement for each company
|
|
239
|
+
UNLESS no statements exist
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
company_ids = [1, 2]
|
|
243
|
+
expected_response = GetFinancialStatementFromIdentifiersResp.model_validate(
|
|
244
|
+
{
|
|
245
|
+
"results": {
|
|
246
|
+
"C_1": {"currency": "USD", "periods": {}},
|
|
247
|
+
"C_2": {
|
|
248
|
+
"currency": "USD",
|
|
249
|
+
"periods": {
|
|
250
|
+
"CY2021": {
|
|
251
|
+
"period_end_date": "2021-12-31",
|
|
252
|
+
"num_months": 12,
|
|
253
|
+
"statements": [
|
|
254
|
+
{
|
|
255
|
+
"name": "Income Statement",
|
|
256
|
+
"line_items": [
|
|
257
|
+
{
|
|
258
|
+
"name": "Revenues",
|
|
259
|
+
"value": "8243000000.000000",
|
|
260
|
+
"sources": [],
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"name": "Total Revenues",
|
|
264
|
+
"value": "8243000000.000000",
|
|
265
|
+
"sources": [],
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
}
|
|
87
274
|
}
|
|
88
|
-
|
|
275
|
+
)
|
|
89
276
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
277
|
+
# Mock the unified_fetch_id_triples response
|
|
278
|
+
requests_mock.post(
|
|
279
|
+
url="https://kfinance.kensho.com/api/v1/ids",
|
|
280
|
+
json={
|
|
281
|
+
"identifiers_to_id_triples": {
|
|
282
|
+
"C_1": {"company_id": 1, "security_id": 101, "trading_item_id": 201},
|
|
283
|
+
"C_2": {"company_id": 2, "security_id": 102, "trading_item_id": 202},
|
|
284
|
+
},
|
|
285
|
+
"errors": {},
|
|
286
|
+
},
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# Mock the fetch_statement response with different data for different companies
|
|
290
|
+
requests_mock.post(
|
|
291
|
+
url="https://kfinance.kensho.com/api/v1/statements/",
|
|
292
|
+
json={
|
|
293
|
+
"results": {"1": {"currency": "USD", "periods": {}}, "2": self.statement_resp},
|
|
294
|
+
"errors": {},
|
|
295
|
+
},
|
|
296
|
+
)
|
|
95
297
|
|
|
96
298
|
tool = GetFinancialStatementFromIdentifiers(kfinance_client=mock_client)
|
|
97
299
|
args = GetFinancialStatementFromIdentifiersArgs(
|
|
@@ -19,4 +19,4 @@ class KfinanceMcp(FastMCP):
|
|
|
19
19
|
call_tool(validate_input=False) turns off the mcp sdk validation.
|
|
20
20
|
"""
|
|
21
21
|
super()._setup_handlers()
|
|
22
|
-
self._mcp_server.call_tool(validate_input=False)(self.
|
|
22
|
+
self._mcp_server.call_tool(validate_input=False)(self._call_tool_mcp)
|
|
@@ -55,6 +55,7 @@ def test_run_notebook(jupyter_kernel_name: str):
|
|
|
55
55
|
# - mocks for all calls made by the client while executing the notebook
|
|
56
56
|
startup_cell_code = dedent("""
|
|
57
57
|
from datetime import datetime
|
|
58
|
+
from urllib.parse import quote
|
|
58
59
|
from kfinance.client.kfinance import Client
|
|
59
60
|
kfinance_client = Client(refresh_token="foo")
|
|
60
61
|
api_client = kfinance_client.kfinance_api_client
|
|
@@ -85,22 +86,44 @@ def test_run_notebook(jupyter_kernel_name: str):
|
|
|
85
86
|
)
|
|
86
87
|
|
|
87
88
|
balance_sheet_resp = {
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
"
|
|
89
|
+
"currency": "USD",
|
|
90
|
+
"periods": {
|
|
91
|
+
"CY2022Q3": {
|
|
92
|
+
"period_end_date": "2022-09-30",
|
|
93
|
+
"num_months": 3,
|
|
94
|
+
"statements": [
|
|
95
|
+
{
|
|
96
|
+
"name": "Balance Sheet",
|
|
97
|
+
"line_items": [
|
|
98
|
+
{"name": "Cash And Equivalents", "value": "1387000000.000000", "sources": []}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"CY2022Q4": {
|
|
104
|
+
"period_end_date": "2022-12-31",
|
|
105
|
+
"num_months": 3,
|
|
106
|
+
"statements": [
|
|
107
|
+
{
|
|
108
|
+
"name": "Balance Sheet",
|
|
109
|
+
"line_items": [
|
|
110
|
+
{"name": "Cash And Equivalents", "value": "1286000000.000000", "sources": []}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
91
115
|
}
|
|
92
116
|
}
|
|
93
117
|
|
|
94
|
-
# spgi.balance_sheet()
|
|
95
|
-
mocker.
|
|
96
|
-
url="https://kfinance.kensho.com/api/v1/statements/
|
|
97
|
-
json=
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
json=balance_sheet_resp
|
|
118
|
+
# spgi.balance_sheet() and spgi.balance_sheet(period_type=PeriodType.annual, start_year=2010, end_year=2019)
|
|
119
|
+
mocker.post(
|
|
120
|
+
url="https://kfinance.kensho.com/api/v1/statements/",
|
|
121
|
+
json={
|
|
122
|
+
"results": {
|
|
123
|
+
"21719": balance_sheet_resp
|
|
124
|
+
},
|
|
125
|
+
"errors": {}
|
|
126
|
+
}
|
|
104
127
|
)
|
|
105
128
|
|
|
106
129
|
# kfinance_client.ticker("JPM").balance_sheet()
|
|
@@ -112,9 +135,27 @@ def test_run_notebook(jupyter_kernel_name: str):
|
|
|
112
135
|
)
|
|
113
136
|
|
|
114
137
|
# spgi.net_income(period_type=PeriodType.annual, start_year=2010, end_year=2019)
|
|
115
|
-
mocker.
|
|
116
|
-
url="https://kfinance.kensho.com/api/v1/line_item/
|
|
117
|
-
json={
|
|
138
|
+
mocker.post(
|
|
139
|
+
url="https://kfinance.kensho.com/api/v1/line_item/",
|
|
140
|
+
json={
|
|
141
|
+
"results": {
|
|
142
|
+
"21719": {
|
|
143
|
+
"currency": "USD",
|
|
144
|
+
"periods": {
|
|
145
|
+
"CY2010": {
|
|
146
|
+
"period_end_date": "2010-12-31",
|
|
147
|
+
"num_months": 12,
|
|
148
|
+
"line_item": {
|
|
149
|
+
"name": "Net Income",
|
|
150
|
+
"value": "828000000.000000",
|
|
151
|
+
"sources": []
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"errors": {}
|
|
158
|
+
}
|
|
118
159
|
)
|
|
119
160
|
|
|
120
161
|
prices_resp = {
|
|
@@ -25,7 +25,10 @@ from kfinance.domains.mergers_and_acquisitions.merger_and_acquisition_tools impo
|
|
|
25
25
|
GetMergerInfoFromTransactionId,
|
|
26
26
|
GetMergersFromIdentifiers,
|
|
27
27
|
)
|
|
28
|
-
from kfinance.domains.prices.price_tools import
|
|
28
|
+
from kfinance.domains.prices.price_tools import (
|
|
29
|
+
GetHistoryMetadataFromIdentifiers,
|
|
30
|
+
GetPricesFromIdentifiers,
|
|
31
|
+
)
|
|
29
32
|
from kfinance.domains.segments.segment_tools import GetSegmentsFromIdentifiers
|
|
30
33
|
from kfinance.domains.statements.statement_tools import GetFinancialStatementFromIdentifiers
|
|
31
34
|
from kfinance.integrations.tool_calling.static_tools.get_latest import GetLatest
|
|
@@ -61,6 +64,7 @@ ALL_TOOLS: list[type[KfinanceTool]] = [
|
|
|
61
64
|
GetFinancialLineItemFromIdentifiers,
|
|
62
65
|
# Prices
|
|
63
66
|
GetPricesFromIdentifiers,
|
|
67
|
+
GetHistoryMetadataFromIdentifiers,
|
|
64
68
|
# Segments
|
|
65
69
|
GetSegmentsFromIdentifiers,
|
|
66
70
|
# Statements
|
|
@@ -11,6 +11,11 @@ class GetNQuartersAgoArgs(BaseModel):
|
|
|
11
11
|
n: int = Field(description="Number of quarters before the current quarter")
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
class GetNQuartersAgoResp(BaseModel):
|
|
15
|
+
year: int
|
|
16
|
+
quarter: int
|
|
17
|
+
|
|
18
|
+
|
|
14
19
|
class GetNQuartersAgo(KfinanceTool):
|
|
15
20
|
name: str = "get_n_quarters_ago"
|
|
16
21
|
description: str = (
|
|
@@ -3,6 +3,7 @@ from datetime import datetime
|
|
|
3
3
|
import time_machine
|
|
4
4
|
|
|
5
5
|
from kfinance.client.kfinance import Client
|
|
6
|
+
from kfinance.client.models.date_and_period_models import LatestPeriods
|
|
6
7
|
from kfinance.integrations.tool_calling.static_tools.get_latest import GetLatest, GetLatestArgs
|
|
7
8
|
|
|
8
9
|
|
|
@@ -15,16 +16,18 @@ class TestGetLatest:
|
|
|
15
16
|
THEN we get back latest info
|
|
16
17
|
"""
|
|
17
18
|
|
|
18
|
-
expected_resp =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
expected_resp = LatestPeriods.model_validate(
|
|
20
|
+
{
|
|
21
|
+
"annual": {"latest_year": 2024},
|
|
22
|
+
"now": {
|
|
23
|
+
"current_date": "2025-01-01",
|
|
24
|
+
"current_month": 1,
|
|
25
|
+
"current_quarter": 1,
|
|
26
|
+
"current_year": 2025,
|
|
27
|
+
},
|
|
28
|
+
"quarterly": {"latest_quarter": 4, "latest_year": 2024},
|
|
29
|
+
}
|
|
30
|
+
)
|
|
28
31
|
tool = GetLatest(kfinance_client=mock_client)
|
|
29
32
|
resp = tool.run(GetLatestArgs().model_dump(mode="json"))
|
|
30
33
|
assert resp == expected_resp
|
|
@@ -3,6 +3,7 @@ from datetime import datetime
|
|
|
3
3
|
import time_machine
|
|
4
4
|
|
|
5
5
|
from kfinance.client.kfinance import Client
|
|
6
|
+
from kfinance.client.models.date_and_period_models import YearAndQuarter
|
|
6
7
|
from kfinance.integrations.tool_calling.static_tools.get_n_quarters_ago import (
|
|
7
8
|
GetNQuartersAgo,
|
|
8
9
|
GetNQuartersAgoArgs,
|
|
@@ -18,7 +19,7 @@ class TestGetNQuartersAgo:
|
|
|
18
19
|
THEN we get back 3 quarters ago
|
|
19
20
|
"""
|
|
20
21
|
|
|
21
|
-
expected_resp =
|
|
22
|
+
expected_resp = YearAndQuarter(year=2024, quarter=2)
|
|
22
23
|
tool = GetNQuartersAgo(kfinance_client=mock_client)
|
|
23
24
|
resp = tool.run(GetNQuartersAgoArgs(n=3).model_dump(mode="json"))
|
|
24
25
|
assert resp == expected_resp
|
|
@@ -7,7 +7,11 @@ from requests_mock import Mocker
|
|
|
7
7
|
|
|
8
8
|
from kfinance.client.kfinance import Client
|
|
9
9
|
from kfinance.conftest import SPGI_COMPANY_ID
|
|
10
|
-
from kfinance.domains.companies.
|
|
10
|
+
from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
|
|
11
|
+
from kfinance.domains.companies.company_tools import (
|
|
12
|
+
GetInfoFromIdentifiers,
|
|
13
|
+
GetInfoFromIdentifiersResp,
|
|
14
|
+
)
|
|
11
15
|
from kfinance.integrations.tool_calling.tool_calling_models import ValidQuarter
|
|
12
16
|
|
|
13
17
|
|
|
@@ -22,13 +26,20 @@ class TestGetEndpointsFromToolCallsWithGrounding:
|
|
|
22
26
|
"""
|
|
23
27
|
|
|
24
28
|
# truncated from the original
|
|
25
|
-
resp_data = {
|
|
29
|
+
resp_data = {
|
|
30
|
+
"name": "S&P Global Inc.",
|
|
31
|
+
"status": "Operating",
|
|
32
|
+
"company_id": f"{COMPANY_ID_PREFIX}{SPGI_COMPANY_ID}",
|
|
33
|
+
}
|
|
26
34
|
resp_endpoint = [
|
|
27
35
|
"https://kfinance.kensho.com/api/v1/ids",
|
|
28
36
|
"https://kfinance.kensho.com/api/v1/info/21719",
|
|
29
37
|
]
|
|
30
|
-
expected_resp = {
|
|
31
|
-
|
|
38
|
+
expected_resp = {
|
|
39
|
+
"data": GetInfoFromIdentifiersResp.model_validate({"results": {"SPGI": resp_data}}),
|
|
40
|
+
"endpoint_urls": resp_endpoint,
|
|
41
|
+
}
|
|
42
|
+
del resp_data["company_id"]
|
|
32
43
|
requests_mock.get(
|
|
33
44
|
url=f"https://kfinance.kensho.com/api/v1/info/{SPGI_COMPANY_ID}",
|
|
34
45
|
json=resp_data,
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import abc
|
|
1
2
|
from typing import Annotated, Any, Callable, Dict, Literal, Type
|
|
2
3
|
|
|
3
4
|
from langchain_core.tools import BaseTool
|
|
4
|
-
from pydantic import
|
|
5
|
+
from pydantic import (
|
|
6
|
+
BaseModel,
|
|
7
|
+
BeforeValidator,
|
|
8
|
+
ConfigDict,
|
|
9
|
+
Field,
|
|
10
|
+
model_serializer,
|
|
11
|
+
)
|
|
5
12
|
|
|
6
13
|
from kfinance.client.kfinance import Client
|
|
7
14
|
from kfinance.client.permission_models import Permission
|
|
@@ -22,7 +29,7 @@ class KfinanceTool(BaseTool):
|
|
|
22
29
|
|
|
23
30
|
model_config = ConfigDict(extra="forbid")
|
|
24
31
|
|
|
25
|
-
def run_without_langchain(self, *args: Any, **kwargs: Any) ->
|
|
32
|
+
def run_without_langchain(self, *args: Any, **kwargs: Any) -> dict:
|
|
26
33
|
"""Execute a Kfinance tool without langchain.
|
|
27
34
|
|
|
28
35
|
Langchain converts json input params into the pydantic args_schema, which means that
|
|
@@ -38,7 +45,8 @@ class KfinanceTool(BaseTool):
|
|
|
38
45
|
# This behavior matches the langchain handling. See
|
|
39
46
|
# https://github.com/langchain-ai/langchain/blob/ca39680d2ab0d786bc035930778a5787e7bb5e01/libs/core/langchain_core/tools/base.py#L595-L597
|
|
40
47
|
args_dict = {k: v for k, v in args_dict.items() if k in kwargs}
|
|
41
|
-
|
|
48
|
+
result_model = self._run(**args_dict)
|
|
49
|
+
return result_model.model_dump(mode="json", exclude_none=True)
|
|
42
50
|
|
|
43
51
|
def run_with_grounding(self, *args: Any, **kwargs: Any) -> Any:
|
|
44
52
|
"""Execute a Kfinance tool with grounding support.
|
|
@@ -47,7 +55,10 @@ class KfinanceTool(BaseTool):
|
|
|
47
55
|
support, for returning the endpoint urls along with the data as citation info for the LRA Data Agent.
|
|
48
56
|
"""
|
|
49
57
|
with self.kfinance_client.kfinance_api_client.endpoint_tracker() as endpoint_tracker_queue:
|
|
50
|
-
|
|
58
|
+
args_model = self.args_schema.model_validate(kwargs)
|
|
59
|
+
args_dict = args_model.model_dump()
|
|
60
|
+
args_dict = {k: v for k, v in args_dict.items() if k in kwargs}
|
|
61
|
+
result_model = self._run(**args_dict)
|
|
51
62
|
|
|
52
63
|
# After completion of tool data fetching and within the endpoint_tracker context manager scope, dequeue the endpoint_tracker_queue
|
|
53
64
|
endpoint_urls = []
|
|
@@ -55,11 +66,12 @@ class KfinanceTool(BaseTool):
|
|
|
55
66
|
endpoint_urls.append(endpoint_tracker_queue.get())
|
|
56
67
|
|
|
57
68
|
return {
|
|
58
|
-
"data":
|
|
69
|
+
"data": result_model,
|
|
59
70
|
"endpoint_urls": endpoint_urls,
|
|
60
71
|
}
|
|
61
72
|
|
|
62
|
-
|
|
73
|
+
@abc.abstractmethod
|
|
74
|
+
def _run(self, *args: Any, **kwargs: Any) -> BaseModel:
|
|
63
75
|
"""The code to execute the tool.
|
|
64
76
|
|
|
65
77
|
Where feasible and useful, tools should use batch processing to parallelize
|
kfinance/version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '
|
|
32
|
-
__version_tuple__ = version_tuple = (
|
|
31
|
+
__version__ = version = '4.0.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (4, 0, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|