kensho-kfinance 2.9.0__py3-none-any.whl → 3.0.1__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.
Potentially problematic release.
This version of kensho-kfinance might be problematic. Click here for more details.
- {kensho_kfinance-2.9.0.dist-info → kensho_kfinance-3.0.1.dist-info}/METADATA +1 -1
- kensho_kfinance-3.0.1.dist-info/RECORD +111 -0
- kfinance/CHANGELOG.md +6 -0
- kfinance/__init__.py +1 -0
- kfinance/client/README.md +9 -0
- kfinance/{batch_request_handling.py → client/batch_request_handling.py} +63 -27
- kfinance/{fetch.py → client/fetch.py} +23 -29
- kfinance/{kfinance.py → client/kfinance.py} +37 -33
- kfinance/{meta_classes.py → client/meta_classes.py} +26 -35
- kfinance/{decimal_with_unit.py → client/models/decimal_with_unit.py} +1 -1
- kfinance/{tests → client/models/tests}/test_decimal_with_unit.py +1 -1
- kfinance/client/permission_models.py +16 -0
- kfinance/client/tests/__init__.py +0 -0
- kfinance/{tests → client/tests}/test_batch_requests.py +8 -6
- kfinance/{tests → client/tests}/test_client.py +25 -19
- kfinance/{tests → client/tests}/test_fetch.py +11 -29
- kfinance/{tests → client/tests}/test_group_objects.py +1 -1
- kfinance/{tests → client/tests}/test_objects.py +33 -29
- kfinance/{tests/conftest.py → conftest.py} +14 -2
- kfinance/domains/README.md +14 -0
- kfinance/domains/__init__.py +0 -0
- kfinance/domains/business_relationships/__init__.py +0 -0
- kfinance/{models → domains/business_relationships}/business_relationship_models.py +10 -0
- kfinance/domains/business_relationships/business_relationship_tools.py +74 -0
- kfinance/domains/business_relationships/tests/__init__.py +0 -0
- kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +55 -0
- kfinance/domains/capitalizations/__init__.py +0 -0
- kfinance/{models → domains/capitalizations}/capitalization_models.py +24 -17
- kfinance/domains/capitalizations/capitalization_tools.py +89 -0
- kfinance/domains/capitalizations/tests/__init__.py +0 -0
- kfinance/{tests/test_models → domains/capitalizations/tests}/test_capitalization_models.py +8 -10
- kfinance/domains/capitalizations/tests/test_capitalization_tools.py +85 -0
- kfinance/domains/companies/__init__.py +0 -0
- kfinance/domains/companies/company_identifiers.py +175 -0
- kfinance/domains/companies/company_models.py +27 -0
- kfinance/domains/companies/company_tools.py +66 -0
- kfinance/domains/companies/tests/__init__.py +0 -0
- kfinance/domains/companies/tests/test_company_tools.py +26 -0
- kfinance/domains/competitors/__init__.py +0 -0
- kfinance/{models → domains/competitors}/competitor_models.py +7 -0
- kfinance/domains/competitors/competitor_tools.py +62 -0
- kfinance/domains/competitors/tests/__init__.py +0 -0
- kfinance/domains/competitors/tests/test_competitor_tools.py +45 -0
- kfinance/domains/cusip_and_isin/__init__.py +0 -0
- kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +80 -0
- kfinance/domains/cusip_and_isin/tests/__init__.py +0 -0
- kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +57 -0
- kfinance/domains/earnings/__init__.py +0 -0
- kfinance/domains/earnings/earning_models.py +41 -0
- kfinance/domains/earnings/earning_tools.py +174 -0
- kfinance/domains/earnings/tests/__init__.py +0 -0
- kfinance/domains/earnings/tests/test_earnings_tools.py +195 -0
- kfinance/domains/line_items/__init__.py +0 -0
- kfinance/domains/line_items/line_item_tools.py +117 -0
- kfinance/domains/line_items/tests/__init__.py +0 -0
- kfinance/domains/line_items/tests/test_line_item_tools.py +86 -0
- kfinance/domains/mergers_and_acquisitions/__init__.py +0 -0
- kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +176 -0
- kfinance/domains/mergers_and_acquisitions/tests/__init__.py +0 -0
- kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +124 -0
- kfinance/domains/prices/__init__.py +0 -0
- kfinance/{models → domains/prices}/price_models.py +1 -1
- kfinance/domains/prices/price_tools.py +165 -0
- kfinance/domains/prices/tests/__init__.py +0 -0
- kfinance/{tests/test_models → domains/prices/tests}/test_price_models.py +2 -2
- kfinance/domains/prices/tests/test_price_tools.py +141 -0
- kfinance/domains/segments/__init__.py +0 -0
- kfinance/domains/segments/segment_tools.py +91 -0
- kfinance/domains/segments/tests/__init__.py +0 -0
- kfinance/domains/segments/tests/test_segment_tools.py +80 -0
- kfinance/domains/statements/__init__.py +0 -0
- kfinance/domains/statements/statement_tools.py +116 -0
- kfinance/domains/statements/tests/__init__.py +0 -0
- kfinance/domains/statements/tests/test_statement_tools.py +73 -0
- kfinance/integrations/README.md +8 -0
- kfinance/integrations/__init__.py +0 -0
- kfinance/integrations/mcp/__init__.py +0 -0
- kfinance/{mcp.py → integrations/mcp/mcp.py} +2 -2
- kfinance/integrations/tests/__init__.py +0 -0
- kfinance/{tests → integrations/tests}/test_example_notebook.py +4 -4
- kfinance/{tool_calling → integrations/tool_calling}/README.md +2 -2
- kfinance/integrations/tool_calling/__init__.py +0 -0
- kfinance/integrations/tool_calling/all_tools.py +55 -0
- kfinance/{tool_calling → integrations/tool_calling}/prompts.py +3 -2
- kfinance/integrations/tool_calling/static_tools/README.md +4 -0
- kfinance/integrations/tool_calling/static_tools/__init__.py +0 -0
- kfinance/{tool_calling → integrations/tool_calling/static_tools}/get_latest.py +3 -3
- kfinance/{tool_calling → integrations/tool_calling/static_tools}/get_n_quarters_ago.py +3 -3
- kfinance/integrations/tool_calling/static_tools/tests/__init__.py +0 -0
- kfinance/integrations/tool_calling/static_tools/tests/test_get_lastest.py +30 -0
- kfinance/integrations/tool_calling/static_tools/tests/test_get_n_quarters_ago.py +24 -0
- kfinance/integrations/tool_calling/tests/__init__.py +0 -0
- kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +69 -0
- kfinance/{tool_calling/shared_models.py → integrations/tool_calling/tool_calling_models.py} +37 -7
- kfinance/models/permission_models.py +1 -0
- kfinance/version.py +2 -2
- kensho_kfinance-2.9.0.dist-info/RECORD +0 -70
- kfinance/models/id_models.py +0 -7
- kfinance/prompt.py +0 -526
- kfinance/pydantic_models.py +0 -33
- kfinance/tests/test_tools.py +0 -804
- kfinance/tool_calling/__init__.py +0 -53
- kfinance/tool_calling/get_advisors_for_company_in_transaction_from_identifier.py +0 -42
- kfinance/tool_calling/get_business_relationship_from_identifier.py +0 -30
- kfinance/tool_calling/get_capitalization_from_identifier.py +0 -35
- kfinance/tool_calling/get_competitors_from_identifier.py +0 -25
- kfinance/tool_calling/get_cusip_from_ticker.py +0 -20
- kfinance/tool_calling/get_earnings.py +0 -33
- kfinance/tool_calling/get_financial_line_item_from_identifier.py +0 -48
- kfinance/tool_calling/get_financial_statement_from_identifier.py +0 -44
- kfinance/tool_calling/get_history_metadata_from_identifier.py +0 -17
- kfinance/tool_calling/get_info_from_identifier.py +0 -16
- kfinance/tool_calling/get_isin_from_ticker.py +0 -20
- kfinance/tool_calling/get_latest_earnings.py +0 -30
- kfinance/tool_calling/get_merger_info_from_transaction_id.py +0 -69
- kfinance/tool_calling/get_mergers_from_identifier.py +0 -44
- kfinance/tool_calling/get_next_earnings.py +0 -30
- kfinance/tool_calling/get_prices_from_identifier.py +0 -46
- kfinance/tool_calling/get_segments_from_identifier.py +0 -44
- kfinance/tool_calling/get_transcript.py +0 -23
- kfinance/tool_calling/resolve_identifier.py +0 -18
- {kensho_kfinance-2.9.0.dist-info → kensho_kfinance-3.0.1.dist-info}/WHEEL +0 -0
- {kensho_kfinance-2.9.0.dist-info → kensho_kfinance-3.0.1.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-2.9.0.dist-info → kensho_kfinance-3.0.1.dist-info}/licenses/LICENSE +0 -0
- {kensho_kfinance-2.9.0.dist-info → kensho_kfinance-3.0.1.dist-info}/top_level.txt +0 -0
- /kfinance/{models → client}/__init__.py +0 -0
- /kfinance/{models → client}/industry_models.py +0 -0
- /kfinance/{tests → client/models}/__init__.py +0 -0
- /kfinance/{models → client/models}/currency_models.py +0 -0
- /kfinance/{models → client/models}/date_and_period_models.py +0 -0
- /kfinance/{tests/test_models → client/models/tests}/__init__.py +0 -0
- /kfinance/{server_thread.py → client/server_thread.py} +0 -0
- /kfinance/{models → domains/line_items}/line_item_models.py +0 -0
- /kfinance/{models → domains/segments}/segment_models.py +0 -0
- /kfinance/{models → domains/statements}/statement_models.py +0 -0
kfinance/tests/test_tools.py
DELETED
|
@@ -1,804 +0,0 @@
|
|
|
1
|
-
import contextlib
|
|
2
|
-
from contextlib import nullcontext as does_not_raise
|
|
3
|
-
from datetime import date, datetime
|
|
4
|
-
|
|
5
|
-
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
6
|
-
from pydantic import BaseModel, ValidationError
|
|
7
|
-
import pytest
|
|
8
|
-
from pytest import raises
|
|
9
|
-
from requests_mock import Mocker
|
|
10
|
-
import time_machine
|
|
11
|
-
|
|
12
|
-
from kfinance.kfinance import Client, NoEarningsDataError
|
|
13
|
-
from kfinance.models.business_relationship_models import BusinessRelationshipType
|
|
14
|
-
from kfinance.models.capitalization_models import Capitalization
|
|
15
|
-
from kfinance.models.competitor_models import CompetitorSource
|
|
16
|
-
from kfinance.models.segment_models import SegmentType
|
|
17
|
-
from kfinance.models.statement_models import StatementType
|
|
18
|
-
from kfinance.tests.conftest import SPGI_COMPANY_ID, SPGI_SECURITY_ID, SPGI_TRADING_ITEM_ID
|
|
19
|
-
from kfinance.tests.test_objects import MOCK_COMPANY_DB, MOCK_MERGERS_DB, ordered
|
|
20
|
-
from kfinance.tool_calling import (
|
|
21
|
-
GetCompetitorsFromIdentifier,
|
|
22
|
-
GetEarnings,
|
|
23
|
-
GetFinancialLineItemFromIdentifier,
|
|
24
|
-
GetFinancialStatementFromIdentifier,
|
|
25
|
-
GetHistoryMetadataFromIdentifier,
|
|
26
|
-
GetInfoFromIdentifier,
|
|
27
|
-
GetIsinFromTicker,
|
|
28
|
-
GetLatest,
|
|
29
|
-
GetLatestEarnings,
|
|
30
|
-
GetNextEarnings,
|
|
31
|
-
GetNQuartersAgo,
|
|
32
|
-
GetPricesFromIdentifier,
|
|
33
|
-
GetTranscript,
|
|
34
|
-
ResolveIdentifier,
|
|
35
|
-
)
|
|
36
|
-
from kfinance.tool_calling.get_advisors_for_company_in_transaction_from_identifier import (
|
|
37
|
-
GetAdvisorsForCompanyInTransactionFromIdentifier,
|
|
38
|
-
GetAdvisorsForCompanyInTransactionFromIdentifierArgs,
|
|
39
|
-
)
|
|
40
|
-
from kfinance.tool_calling.get_business_relationship_from_identifier import (
|
|
41
|
-
GetBusinessRelationshipFromIdentifier,
|
|
42
|
-
GetBusinessRelationshipFromIdentifierArgs,
|
|
43
|
-
)
|
|
44
|
-
from kfinance.tool_calling.get_capitalization_from_identifier import (
|
|
45
|
-
GetCapitalizationFromIdentifier,
|
|
46
|
-
GetCapitalizationFromIdentifierArgs,
|
|
47
|
-
)
|
|
48
|
-
from kfinance.tool_calling.get_competitors_from_identifier import (
|
|
49
|
-
GetCompetitorsFromIdentifierArgs,
|
|
50
|
-
)
|
|
51
|
-
from kfinance.tool_calling.get_cusip_from_ticker import GetCusipFromTicker, GetCusipFromTickerArgs
|
|
52
|
-
from kfinance.tool_calling.get_financial_line_item_from_identifier import (
|
|
53
|
-
GetFinancialLineItemFromIdentifierArgs,
|
|
54
|
-
)
|
|
55
|
-
from kfinance.tool_calling.get_financial_statement_from_identifier import (
|
|
56
|
-
GetFinancialStatementFromIdentifierArgs,
|
|
57
|
-
)
|
|
58
|
-
from kfinance.tool_calling.get_isin_from_ticker import GetIsinFromTickerArgs
|
|
59
|
-
from kfinance.tool_calling.get_latest import GetLatestArgs
|
|
60
|
-
from kfinance.tool_calling.get_merger_info_from_transaction_id import (
|
|
61
|
-
GetMergerInfoFromTransactionId,
|
|
62
|
-
GetMergerInfoFromTransactionIdArgs,
|
|
63
|
-
)
|
|
64
|
-
from kfinance.tool_calling.get_mergers_from_identifier import GetMergersFromIdentifier
|
|
65
|
-
from kfinance.tool_calling.get_n_quarters_ago import GetNQuartersAgoArgs
|
|
66
|
-
from kfinance.tool_calling.get_prices_from_identifier import GetPricesFromIdentifierArgs
|
|
67
|
-
from kfinance.tool_calling.get_segments_from_identifier import (
|
|
68
|
-
GetSegmentsFromIdentifier,
|
|
69
|
-
GetSegmentsFromIdentifierArgs,
|
|
70
|
-
)
|
|
71
|
-
from kfinance.tool_calling.get_transcript import GetTranscriptArgs
|
|
72
|
-
from kfinance.tool_calling.shared_models import ToolArgsWithIdentifier, ValidQuarter
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class TestGetCompaniesAdvisingCompanyInTransactionFromIdentifier:
|
|
76
|
-
def test_get_companies_advising_company_in_transaction_from_identifier(
|
|
77
|
-
self, requests_mock: Mocker, mock_client: Client
|
|
78
|
-
):
|
|
79
|
-
expected_response = {
|
|
80
|
-
"advisors": [
|
|
81
|
-
{
|
|
82
|
-
"advisor_company_id": 251994106,
|
|
83
|
-
"advisor_company_name": "Kensho Technologies, Inc.",
|
|
84
|
-
"advisor_type_name": "Professional Mongo Enjoyer",
|
|
85
|
-
}
|
|
86
|
-
]
|
|
87
|
-
}
|
|
88
|
-
transaction_id = 517414
|
|
89
|
-
requests_mock.get(
|
|
90
|
-
url=f"https://kfinance.kensho.com/api/v1/merger/info/{transaction_id}/advisors/21835",
|
|
91
|
-
json=expected_response,
|
|
92
|
-
)
|
|
93
|
-
tool = GetAdvisorsForCompanyInTransactionFromIdentifier(kfinance_client=mock_client)
|
|
94
|
-
args = GetAdvisorsForCompanyInTransactionFromIdentifierArgs(
|
|
95
|
-
identifier="MSFT", transaction_id=transaction_id
|
|
96
|
-
)
|
|
97
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
98
|
-
assert response == expected_response["advisors"]
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class TestGetMergerInfoFromTransactionId:
|
|
102
|
-
def test_get_merger_info_from_transaction_id(self, requests_mock: Mocker, mock_client: Client):
|
|
103
|
-
expected_response = MOCK_MERGERS_DB["517414"]
|
|
104
|
-
transaction_id = 517414
|
|
105
|
-
requests_mock.get(
|
|
106
|
-
url=f"https://kfinance.kensho.com/api/v1/merger/info/{transaction_id}",
|
|
107
|
-
json=expected_response,
|
|
108
|
-
)
|
|
109
|
-
tool = GetMergerInfoFromTransactionId(kfinance_client=mock_client)
|
|
110
|
-
args = GetMergerInfoFromTransactionIdArgs(transaction_id=transaction_id)
|
|
111
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
112
|
-
assert ordered(response) == ordered(expected_response)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class TestGetMergersFromIdentifier:
|
|
116
|
-
def test_get_mergers_from_identifier(self, requests_mock: Mocker, mock_client: Client):
|
|
117
|
-
expected_response = MOCK_COMPANY_DB["21835"]["mergers"]
|
|
118
|
-
company_id = 21835
|
|
119
|
-
requests_mock.get(
|
|
120
|
-
url=f"https://kfinance.kensho.com/api/v1/mergers/{company_id}", json=expected_response
|
|
121
|
-
)
|
|
122
|
-
tool = GetMergersFromIdentifier(kfinance_client=mock_client)
|
|
123
|
-
args = ToolArgsWithIdentifier(identifier="MSFT")
|
|
124
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
125
|
-
assert ordered(response) == ordered(expected_response)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
class TestGetBusinessRelationshipFromIdentifier:
|
|
129
|
-
def test_get_business_relationship_from_identifier(
|
|
130
|
-
self, requests_mock: Mocker, mock_client: Client
|
|
131
|
-
):
|
|
132
|
-
"""
|
|
133
|
-
GIVEN the GetBusinessRelationshipFromIdentifier tool
|
|
134
|
-
WHEN we request SPGI suppliers
|
|
135
|
-
THEN we get back the SPGI suppliers
|
|
136
|
-
"""
|
|
137
|
-
supplier_resp = {"current": [883103], "previous": [472898, 8182358]}
|
|
138
|
-
|
|
139
|
-
requests_mock.get(
|
|
140
|
-
url=f"https://kfinance.kensho.com/api/v1/relationship/{SPGI_COMPANY_ID}/supplier",
|
|
141
|
-
json=supplier_resp,
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
tool = GetBusinessRelationshipFromIdentifier(kfinance_client=mock_client)
|
|
145
|
-
args = GetBusinessRelationshipFromIdentifierArgs(
|
|
146
|
-
identifier="SPGI", business_relationship=BusinessRelationshipType.supplier
|
|
147
|
-
)
|
|
148
|
-
resp = tool.run(args.model_dump(mode="json"))
|
|
149
|
-
# Companies is a set, so we have to sort the result
|
|
150
|
-
resp["previous"].sort()
|
|
151
|
-
assert resp == supplier_resp
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
class TestGetCapitalizationFromIdentifier:
|
|
155
|
-
def test_get_capitalization_from_identifier(self, requests_mock: Mocker, mock_client: Client):
|
|
156
|
-
"""
|
|
157
|
-
GIVEN the GetCapitalizationFromIdentifier tool
|
|
158
|
-
WHEN we request the SPGI market cap
|
|
159
|
-
THEN we get back the SPGI market cap
|
|
160
|
-
"""
|
|
161
|
-
|
|
162
|
-
requests_mock.get(
|
|
163
|
-
url=f"https://kfinance.kensho.com/api/v1/market_cap/{SPGI_COMPANY_ID}/none/none",
|
|
164
|
-
json={
|
|
165
|
-
"currency": "USD",
|
|
166
|
-
"market_caps": [
|
|
167
|
-
{
|
|
168
|
-
"date": "2024-04-10",
|
|
169
|
-
"market_cap": "132766738270.000000",
|
|
170
|
-
"tev": "147455738270.000000",
|
|
171
|
-
"shares_outstanding": 313099562,
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
"date": "2024-04-11",
|
|
175
|
-
"market_cap": "132416066761.000000",
|
|
176
|
-
"tev": "147105066761.000000",
|
|
177
|
-
"shares_outstanding": 313099562,
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
},
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
expected_response = {
|
|
184
|
-
"market_cap": [
|
|
185
|
-
{"2024-04-10": {"unit": "USD", "value": "132766738270.00"}},
|
|
186
|
-
{"2024-04-11": {"unit": "USD", "value": "132416066761.00"}},
|
|
187
|
-
]
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
tool = GetCapitalizationFromIdentifier(kfinance_client=mock_client)
|
|
191
|
-
args = GetCapitalizationFromIdentifierArgs(
|
|
192
|
-
identifier="SPGI", capitalization=Capitalization.market_cap
|
|
193
|
-
)
|
|
194
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
195
|
-
assert response == expected_response
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
class TestGetCusipFromTicker:
|
|
199
|
-
def test_get_cusip_from_ticker(self, requests_mock: Mocker, mock_client: Client):
|
|
200
|
-
"""
|
|
201
|
-
GIVEN the GetCusipFromTicker tool
|
|
202
|
-
WHEN we pass args with the SPGI ticker
|
|
203
|
-
THEN we get back the SPGI cusip
|
|
204
|
-
"""
|
|
205
|
-
|
|
206
|
-
spgi_cusip = "78409V104"
|
|
207
|
-
requests_mock.get(
|
|
208
|
-
url=f"https://kfinance.kensho.com/api/v1/cusip/{SPGI_SECURITY_ID}",
|
|
209
|
-
json={"cusip": spgi_cusip},
|
|
210
|
-
)
|
|
211
|
-
tool = GetCusipFromTicker(kfinance_client=mock_client)
|
|
212
|
-
resp = tool.run(GetCusipFromTickerArgs(ticker_str="SPGI").model_dump(mode="json"))
|
|
213
|
-
assert resp == spgi_cusip
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
class TestGetFinancialLineItemFromIdentifier:
|
|
217
|
-
def test_get_financial_line_item_from_identifier(
|
|
218
|
-
self, mock_client: Client, requests_mock: Mocker
|
|
219
|
-
):
|
|
220
|
-
"""
|
|
221
|
-
GIVEN the GetFinancialLineItemFromIdentifier tool
|
|
222
|
-
WHEN we request SPGI revenue
|
|
223
|
-
THEN we get back the SPGI revenue
|
|
224
|
-
"""
|
|
225
|
-
|
|
226
|
-
requests_mock.get(
|
|
227
|
-
url=f"https://kfinance.kensho.com/api/v1/line_item/{SPGI_COMPANY_ID}/revenue/none/none/none/none/none",
|
|
228
|
-
json={
|
|
229
|
-
"line_item": {
|
|
230
|
-
"2020": "7442000000.000000",
|
|
231
|
-
"2021": "8297000000.000000",
|
|
232
|
-
"2022": "11181000000.000000",
|
|
233
|
-
"2023": "12497000000.000000",
|
|
234
|
-
"2024": "14208000000.000000",
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
)
|
|
238
|
-
expected_response = "| | 2020 | 2021 | 2022 | 2023 | 2024 |\n|:--------|----------:|----------:|-----------:|-----------:|-----------:|\n| revenue | 7.442e+09 | 8.297e+09 | 1.1181e+10 | 1.2497e+10 | 1.4208e+10 |"
|
|
239
|
-
|
|
240
|
-
tool = GetFinancialLineItemFromIdentifier(kfinance_client=mock_client)
|
|
241
|
-
args = GetFinancialLineItemFromIdentifierArgs(identifier="SPGI", line_item="revenue")
|
|
242
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
243
|
-
assert response == expected_response
|
|
244
|
-
|
|
245
|
-
def test_line_items_and_aliases_included_in_schema(self, mock_client: Client):
|
|
246
|
-
"""
|
|
247
|
-
GIVEN a GetFinancialLineItemFromIdentifier tool
|
|
248
|
-
WHEN we generate an openai schema from the tool
|
|
249
|
-
THEN all line items and aliases are included in the line item enum
|
|
250
|
-
"""
|
|
251
|
-
tool = GetFinancialLineItemFromIdentifier(kfinance_client=mock_client)
|
|
252
|
-
oai_schema = convert_to_openai_tool(tool)
|
|
253
|
-
line_items = oai_schema["function"]["parameters"]["properties"]["line_item"]["enum"]
|
|
254
|
-
# revenue is a line item
|
|
255
|
-
assert "revenue" in line_items
|
|
256
|
-
# normal_revenue is an alias for revenue
|
|
257
|
-
assert "normal_revenue" in line_items
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
class TestGetFinancialStatementFromIdentifier:
|
|
261
|
-
def test_get_financial_statement_from_identifier(
|
|
262
|
-
self, mock_client: Client, requests_mock: Mocker
|
|
263
|
-
):
|
|
264
|
-
"""
|
|
265
|
-
GIVEN the GetFinancialLineItemFromIdentifier tool
|
|
266
|
-
WHEN we request the SPGI income statement
|
|
267
|
-
THEN we get back the SPGI income statement
|
|
268
|
-
"""
|
|
269
|
-
|
|
270
|
-
requests_mock.get(
|
|
271
|
-
url=f"https://kfinance.kensho.com/api/v1/statements/{SPGI_COMPANY_ID}/income_statement/none/none/none/none/none",
|
|
272
|
-
# truncated from the original API response
|
|
273
|
-
json={
|
|
274
|
-
"statements": {
|
|
275
|
-
"2020": {"Revenues": "7442000000.000000", "Total Revenues": "7442000000.000000"}
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
)
|
|
279
|
-
expected_response = "| | 2020 |\n|:---------------|----------:|\n| Revenues | 7.442e+09 |\n| Total Revenues | 7.442e+09 |"
|
|
280
|
-
|
|
281
|
-
tool = GetFinancialStatementFromIdentifier(kfinance_client=mock_client)
|
|
282
|
-
args = GetFinancialStatementFromIdentifierArgs(
|
|
283
|
-
identifier="SPGI", statement=StatementType.income_statement
|
|
284
|
-
)
|
|
285
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
286
|
-
assert response == expected_response
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
class TestGetSegmentsFromIdentifier:
|
|
290
|
-
def test_get_segments_from_identifier(self, mock_client: Client, requests_mock: Mocker):
|
|
291
|
-
"""
|
|
292
|
-
GIVEN the GetSegmentsFromIdentifier tool
|
|
293
|
-
WHEN we request the SPGI business segment
|
|
294
|
-
THEN we get back the SPGI business segment
|
|
295
|
-
"""
|
|
296
|
-
|
|
297
|
-
segments_response = {
|
|
298
|
-
"segments": {
|
|
299
|
-
"2020": {
|
|
300
|
-
"Commodity Insights": {
|
|
301
|
-
"CAPEX": -7000000.0,
|
|
302
|
-
"D&A": 17000000.0,
|
|
303
|
-
},
|
|
304
|
-
"Unallocated Assets Held for Sale": None,
|
|
305
|
-
},
|
|
306
|
-
"2021": {
|
|
307
|
-
"Commodity Insights": {
|
|
308
|
-
"CAPEX": -2000000.0,
|
|
309
|
-
"D&A": 12000000.0,
|
|
310
|
-
},
|
|
311
|
-
"Unallocated Assets Held for Sale": {"Total Assets": 321000000.0},
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
}
|
|
315
|
-
requests_mock.get(
|
|
316
|
-
url=f"https://kfinance.kensho.com/api/v1/segments/{SPGI_COMPANY_ID}/business/none/none/none/none/none",
|
|
317
|
-
# truncated from the original API response
|
|
318
|
-
json=segments_response,
|
|
319
|
-
)
|
|
320
|
-
|
|
321
|
-
tool = GetSegmentsFromIdentifier(kfinance_client=mock_client)
|
|
322
|
-
args = GetSegmentsFromIdentifierArgs(identifier="SPGI", segment_type=SegmentType.business)
|
|
323
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
324
|
-
assert response == segments_response["segments"]
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
class TestGetHistoryMetadataFromIdentifier:
|
|
328
|
-
def test_get_history_metadata_from_identifier(self, mock_client: Client, requests_mock: Mocker):
|
|
329
|
-
"""
|
|
330
|
-
GIVEN the GetHistoryMetadataFromIdentifier tool
|
|
331
|
-
WHEN request history metadata for SPGI
|
|
332
|
-
THEN we get back the SPGI history metadata
|
|
333
|
-
"""
|
|
334
|
-
|
|
335
|
-
metadata_resp = {
|
|
336
|
-
"currency": "USD",
|
|
337
|
-
"exchange_name": "NYSE",
|
|
338
|
-
"first_trade_date": "1968-01-02",
|
|
339
|
-
"instrument_type": "Equity",
|
|
340
|
-
"symbol": "SPGI",
|
|
341
|
-
}
|
|
342
|
-
expected_resp = {
|
|
343
|
-
"currency": "USD",
|
|
344
|
-
"exchange_name": "NYSE",
|
|
345
|
-
"first_trade_date": date(1968, 1, 2),
|
|
346
|
-
"instrument_type": "Equity",
|
|
347
|
-
"symbol": "SPGI",
|
|
348
|
-
}
|
|
349
|
-
requests_mock.get(
|
|
350
|
-
url=f"https://kfinance.kensho.com/api/v1/pricing/{SPGI_TRADING_ITEM_ID}/metadata",
|
|
351
|
-
json=metadata_resp,
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
tool = GetHistoryMetadataFromIdentifier(kfinance_client=mock_client)
|
|
355
|
-
resp = tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
356
|
-
assert resp == expected_resp
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
class TestGetInfoFromIdentifier:
|
|
360
|
-
def test_get_info_from_identifier(self, mock_client: Client, requests_mock: Mocker):
|
|
361
|
-
"""
|
|
362
|
-
GIVEN the GetInfoFromIdentifier tool
|
|
363
|
-
WHEN request info for SPGI
|
|
364
|
-
THEN we get back info for SPGI
|
|
365
|
-
"""
|
|
366
|
-
|
|
367
|
-
# truncated from the original
|
|
368
|
-
info_resp = {"name": "S&P Global Inc.", "status": "Operating"}
|
|
369
|
-
requests_mock.get(
|
|
370
|
-
url=f"https://kfinance.kensho.com/api/v1/info/{SPGI_COMPANY_ID}",
|
|
371
|
-
json=info_resp,
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
tool = GetInfoFromIdentifier(kfinance_client=mock_client)
|
|
375
|
-
resp = tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
376
|
-
assert resp == str(info_resp)
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
class TestGetIsinFromTicker:
|
|
380
|
-
def test_get_isin_from_ticker(self, requests_mock: Mocker, mock_client: Client):
|
|
381
|
-
"""
|
|
382
|
-
GIVEN the GetIsinFromTicker tool
|
|
383
|
-
WHEN we pass args with the SPGI ticker
|
|
384
|
-
THEN we get back the SPGI isin
|
|
385
|
-
"""
|
|
386
|
-
|
|
387
|
-
spgi_isin = "US78409V1044"
|
|
388
|
-
requests_mock.get(
|
|
389
|
-
url=f"https://kfinance.kensho.com/api/v1/isin/{SPGI_SECURITY_ID}",
|
|
390
|
-
json={"isin": spgi_isin},
|
|
391
|
-
)
|
|
392
|
-
|
|
393
|
-
tool = GetIsinFromTicker(kfinance_client=mock_client)
|
|
394
|
-
resp = tool.run(GetIsinFromTickerArgs(ticker_str="SPGI").model_dump(mode="json"))
|
|
395
|
-
assert resp == spgi_isin
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
class TestGetLatest:
|
|
399
|
-
@time_machine.travel(datetime(2025, 1, 1, 12, tzinfo=datetime.now().astimezone().tzinfo))
|
|
400
|
-
def test_get_latest(self, mock_client: Client):
|
|
401
|
-
"""
|
|
402
|
-
GIVEN the GetLatest tool
|
|
403
|
-
WHEN request latest info
|
|
404
|
-
THEN we get back latest info
|
|
405
|
-
"""
|
|
406
|
-
|
|
407
|
-
expected_resp = {
|
|
408
|
-
"annual": {"latest_year": 2024},
|
|
409
|
-
"now": {
|
|
410
|
-
"current_date": "2025-01-01",
|
|
411
|
-
"current_month": 1,
|
|
412
|
-
"current_quarter": 1,
|
|
413
|
-
"current_year": 2025,
|
|
414
|
-
},
|
|
415
|
-
"quarterly": {"latest_quarter": 4, "latest_year": 2024},
|
|
416
|
-
}
|
|
417
|
-
tool = GetLatest(kfinance_client=mock_client)
|
|
418
|
-
resp = tool.run(GetLatestArgs().model_dump(mode="json"))
|
|
419
|
-
assert resp == expected_resp
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
class TestGetNQuartersAgo:
|
|
423
|
-
@time_machine.travel(datetime(2025, 1, 1, 12, tzinfo=datetime.now().astimezone().tzinfo))
|
|
424
|
-
def test_get_n_quarters_ago(self, mock_client: Client):
|
|
425
|
-
"""
|
|
426
|
-
GIVEN the GetNQuartersAgo tool
|
|
427
|
-
WHEN we request 3 quarters ago
|
|
428
|
-
THEN we get back 3 quarters ago
|
|
429
|
-
"""
|
|
430
|
-
|
|
431
|
-
expected_resp = {"quarter": 2, "year": 2024}
|
|
432
|
-
tool = GetNQuartersAgo(kfinance_client=mock_client)
|
|
433
|
-
resp = tool.run(GetNQuartersAgoArgs(n=3).model_dump(mode="json"))
|
|
434
|
-
assert resp == expected_resp
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
class TestPricesFromIdentifier:
|
|
438
|
-
def test_get_prices_from_identifier(self, mock_client: Client, requests_mock: Mocker):
|
|
439
|
-
"""
|
|
440
|
-
GIVEN the GetPricesFromIdentifier tool
|
|
441
|
-
WHEN we request prices for SPGI
|
|
442
|
-
THEN we get back prices for SPGI
|
|
443
|
-
"""
|
|
444
|
-
|
|
445
|
-
requests_mock.get(
|
|
446
|
-
url=f"https://kfinance.kensho.com/api/v1/pricing/{SPGI_TRADING_ITEM_ID}/none/none/day/adjusted",
|
|
447
|
-
# truncated response
|
|
448
|
-
json={
|
|
449
|
-
"currency": "USD",
|
|
450
|
-
"prices": [
|
|
451
|
-
{
|
|
452
|
-
"date": "2024-04-11",
|
|
453
|
-
"open": "424.260000",
|
|
454
|
-
"high": "425.990000",
|
|
455
|
-
"low": "422.040000",
|
|
456
|
-
"close": "422.920000",
|
|
457
|
-
"volume": "1129158",
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
"date": "2024-04-12",
|
|
461
|
-
"open": "419.230000",
|
|
462
|
-
"high": "421.940000",
|
|
463
|
-
"low": "416.450000",
|
|
464
|
-
"close": "417.810000",
|
|
465
|
-
"volume": "1182229",
|
|
466
|
-
},
|
|
467
|
-
],
|
|
468
|
-
},
|
|
469
|
-
)
|
|
470
|
-
expected_response = {
|
|
471
|
-
"prices": [
|
|
472
|
-
{
|
|
473
|
-
"date": "2024-04-11",
|
|
474
|
-
"open": {"value": "424.26", "unit": "USD"},
|
|
475
|
-
"high": {"value": "425.99", "unit": "USD"},
|
|
476
|
-
"low": {"value": "422.04", "unit": "USD"},
|
|
477
|
-
"close": {"value": "422.92", "unit": "USD"},
|
|
478
|
-
"volume": {"value": "1129158", "unit": "Shares"},
|
|
479
|
-
},
|
|
480
|
-
{
|
|
481
|
-
"date": "2024-04-12",
|
|
482
|
-
"open": {"value": "419.23", "unit": "USD"},
|
|
483
|
-
"high": {"value": "421.94", "unit": "USD"},
|
|
484
|
-
"low": {"value": "416.45", "unit": "USD"},
|
|
485
|
-
"close": {"value": "417.81", "unit": "USD"},
|
|
486
|
-
"volume": {"value": "1182229", "unit": "Shares"},
|
|
487
|
-
},
|
|
488
|
-
]
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
tool = GetPricesFromIdentifier(kfinance_client=mock_client)
|
|
492
|
-
response = tool.run(GetPricesFromIdentifierArgs(identifier="SPGI").model_dump(mode="json"))
|
|
493
|
-
assert response == expected_response
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
class TestResolveIdentifier:
|
|
497
|
-
def test_resolve_identifier(self, mock_client: Client):
|
|
498
|
-
"""
|
|
499
|
-
GIVEN the ResolveIdentifier tool
|
|
500
|
-
WHEN request to resolve SPGI
|
|
501
|
-
THEN we get back a dict with the SPGI company id, security id, and trading item id
|
|
502
|
-
"""
|
|
503
|
-
tool = ResolveIdentifier(kfinance_client=mock_client)
|
|
504
|
-
resp = tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
505
|
-
assert resp == {
|
|
506
|
-
"company_id": SPGI_COMPANY_ID,
|
|
507
|
-
"security_id": SPGI_SECURITY_ID,
|
|
508
|
-
"trading_item_id": SPGI_TRADING_ITEM_ID,
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
class TestGetLatestEarnings:
|
|
513
|
-
def test_get_latest_earnings(self, requests_mock: Mocker, mock_client: Client):
|
|
514
|
-
"""
|
|
515
|
-
GIVEN the GetLatestEarnings tool
|
|
516
|
-
WHEN we request the latest earnings for SPGI
|
|
517
|
-
THEN we get back the latest SPGI earnings
|
|
518
|
-
"""
|
|
519
|
-
earnings_data = {
|
|
520
|
-
"earnings": [
|
|
521
|
-
{
|
|
522
|
-
"name": "SPGI Q4 2024 Earnings Call",
|
|
523
|
-
"datetime": "2025-02-11T13:30:00Z",
|
|
524
|
-
"keydevid": 12345,
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
"name": "SPGI Q3 2024 Earnings Call",
|
|
528
|
-
"datetime": "2024-10-30T12:30:00Z",
|
|
529
|
-
"keydevid": 12344,
|
|
530
|
-
},
|
|
531
|
-
]
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
requests_mock.get(
|
|
535
|
-
url=f"https://kfinance.kensho.com/api/v1/earnings/{SPGI_COMPANY_ID}",
|
|
536
|
-
json=earnings_data,
|
|
537
|
-
)
|
|
538
|
-
|
|
539
|
-
expected_response = {
|
|
540
|
-
"name": "SPGI Q4 2024 Earnings Call",
|
|
541
|
-
"key_dev_id": 12345,
|
|
542
|
-
"datetime": "2025-02-11T13:30:00+00:00",
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
tool = GetLatestEarnings(kfinance_client=mock_client)
|
|
546
|
-
response = tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
547
|
-
assert response == expected_response
|
|
548
|
-
|
|
549
|
-
def test_get_latest_earnings_no_data(self, requests_mock: Mocker, mock_client: Client):
|
|
550
|
-
"""
|
|
551
|
-
GIVEN the GetLatestEarnings tool
|
|
552
|
-
WHEN we request the latest earnings for a company with no data
|
|
553
|
-
THEN we get a NoEarningsDataError exception
|
|
554
|
-
"""
|
|
555
|
-
earnings_data = {"earnings": []}
|
|
556
|
-
|
|
557
|
-
requests_mock.get(
|
|
558
|
-
url=f"https://kfinance.kensho.com/api/v1/earnings/{SPGI_COMPANY_ID}",
|
|
559
|
-
json=earnings_data,
|
|
560
|
-
)
|
|
561
|
-
|
|
562
|
-
tool = GetLatestEarnings(kfinance_client=mock_client)
|
|
563
|
-
with raises(NoEarningsDataError, match="Latest earnings for SPGI not found"):
|
|
564
|
-
tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
class TestGetNextEarnings:
|
|
568
|
-
def test_get_next_earnings_(self, requests_mock: Mocker, mock_client: Client):
|
|
569
|
-
"""
|
|
570
|
-
GIVEN the GetNextEarnings tool
|
|
571
|
-
WHEN we request the next earnings for SPGI
|
|
572
|
-
THEN we get back the next SPGI earnings
|
|
573
|
-
"""
|
|
574
|
-
earnings_data = {
|
|
575
|
-
"earnings": [
|
|
576
|
-
{
|
|
577
|
-
"name": "SPGI Q1 2025 Earnings Call",
|
|
578
|
-
"datetime": "2025-04-29T12:30:00Z",
|
|
579
|
-
"keydevid": 12346,
|
|
580
|
-
},
|
|
581
|
-
{
|
|
582
|
-
"name": "SPGI Q4 2024 Earnings Call",
|
|
583
|
-
"datetime": "2025-02-11T13:30:00Z",
|
|
584
|
-
"keydevid": 12345,
|
|
585
|
-
},
|
|
586
|
-
]
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
requests_mock.get(
|
|
590
|
-
url=f"https://kfinance.kensho.com/api/v1/earnings/{SPGI_COMPANY_ID}",
|
|
591
|
-
json=earnings_data,
|
|
592
|
-
)
|
|
593
|
-
|
|
594
|
-
expected_response = {
|
|
595
|
-
"name": "SPGI Q1 2025 Earnings Call",
|
|
596
|
-
"key_dev_id": 12346,
|
|
597
|
-
"datetime": "2025-04-29T12:30:00+00:00",
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
with time_machine.travel("2025-03-01T00:00:00+00:00"):
|
|
601
|
-
tool = GetNextEarnings(kfinance_client=mock_client)
|
|
602
|
-
response = tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
603
|
-
assert response == expected_response
|
|
604
|
-
|
|
605
|
-
def test_get_next_earnings_no_data(self, requests_mock: Mocker, mock_client: Client):
|
|
606
|
-
"""
|
|
607
|
-
GIVEN the GetNextEarnings tool
|
|
608
|
-
WHEN we request the next earnings for a company with no data
|
|
609
|
-
THEN we get a NoEarningsDataError exception
|
|
610
|
-
"""
|
|
611
|
-
earnings_data = {"earnings": []}
|
|
612
|
-
|
|
613
|
-
requests_mock.get(
|
|
614
|
-
url=f"https://kfinance.kensho.com/api/v1/earnings/{SPGI_COMPANY_ID}",
|
|
615
|
-
json=earnings_data,
|
|
616
|
-
)
|
|
617
|
-
|
|
618
|
-
with time_machine.travel("2025-03-01T00:00:00+00:00"):
|
|
619
|
-
tool = GetNextEarnings(kfinance_client=mock_client)
|
|
620
|
-
with raises(NoEarningsDataError, match="Next earnings for SPGI not found"):
|
|
621
|
-
tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
class TestGetEarnings:
|
|
625
|
-
def test_get_earnings(self, requests_mock: Mocker, mock_client: Client):
|
|
626
|
-
"""
|
|
627
|
-
GIVEN the GetEarnings tool
|
|
628
|
-
WHEN we request all earnings for SPGI
|
|
629
|
-
THEN we get back all SPGI earnings
|
|
630
|
-
"""
|
|
631
|
-
earnings_data = {
|
|
632
|
-
"earnings": [
|
|
633
|
-
{
|
|
634
|
-
"name": "SPGI Q1 2025 Earnings Call",
|
|
635
|
-
"datetime": "2025-04-29T12:30:00Z",
|
|
636
|
-
"keydevid": 12346,
|
|
637
|
-
},
|
|
638
|
-
{
|
|
639
|
-
"name": "SPGI Q4 2024 Earnings Call",
|
|
640
|
-
"datetime": "2025-02-11T13:30:00Z",
|
|
641
|
-
"keydevid": 12345,
|
|
642
|
-
},
|
|
643
|
-
]
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
requests_mock.get(
|
|
647
|
-
url=f"https://kfinance.kensho.com/api/v1/earnings/{SPGI_COMPANY_ID}",
|
|
648
|
-
json=earnings_data,
|
|
649
|
-
)
|
|
650
|
-
|
|
651
|
-
expected_response = [
|
|
652
|
-
{
|
|
653
|
-
"name": "SPGI Q1 2025 Earnings Call",
|
|
654
|
-
"key_dev_id": 12346,
|
|
655
|
-
"datetime": "2025-04-29T12:30:00+00:00",
|
|
656
|
-
},
|
|
657
|
-
{
|
|
658
|
-
"name": "SPGI Q4 2024 Earnings Call",
|
|
659
|
-
"key_dev_id": 12345,
|
|
660
|
-
"datetime": "2025-02-11T13:30:00+00:00",
|
|
661
|
-
},
|
|
662
|
-
]
|
|
663
|
-
|
|
664
|
-
tool = GetEarnings(kfinance_client=mock_client)
|
|
665
|
-
response = tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
666
|
-
assert response == expected_response
|
|
667
|
-
|
|
668
|
-
def test_get_earnings_no_data(self, requests_mock: Mocker, mock_client: Client):
|
|
669
|
-
"""
|
|
670
|
-
GIVEN the GetEarnings tool
|
|
671
|
-
WHEN we request all earnings for a company with no data
|
|
672
|
-
THEN we get a NoEarningslDataError exception
|
|
673
|
-
"""
|
|
674
|
-
earnings_data = {"earnings": []}
|
|
675
|
-
|
|
676
|
-
requests_mock.get(
|
|
677
|
-
url=f"https://kfinance.kensho.com/api/v1/earnings/{SPGI_COMPANY_ID}",
|
|
678
|
-
json=earnings_data,
|
|
679
|
-
)
|
|
680
|
-
|
|
681
|
-
tool = GetEarnings(kfinance_client=mock_client)
|
|
682
|
-
with raises(NoEarningsDataError, match="Earnings for SPGI not found"):
|
|
683
|
-
tool.run(ToolArgsWithIdentifier(identifier="SPGI").model_dump(mode="json"))
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
class TestGetTranscript:
|
|
687
|
-
def test_get_transcript(self, requests_mock: Mocker, mock_client: Client):
|
|
688
|
-
"""
|
|
689
|
-
GIVEN the GetTranscript tool
|
|
690
|
-
WHEN we request a transcript by key_dev_id
|
|
691
|
-
THEN we get back the transcript text
|
|
692
|
-
"""
|
|
693
|
-
transcript_data = {
|
|
694
|
-
"transcript": [
|
|
695
|
-
{
|
|
696
|
-
"person_name": "Operator",
|
|
697
|
-
"text": "Good morning, everyone.",
|
|
698
|
-
"component_type": "speech",
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
"person_name": "CEO",
|
|
702
|
-
"text": "Thank you for joining us today.",
|
|
703
|
-
"component_type": "speech",
|
|
704
|
-
},
|
|
705
|
-
]
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
requests_mock.get(
|
|
709
|
-
url="https://kfinance.kensho.com/api/v1/transcript/12345",
|
|
710
|
-
json=transcript_data,
|
|
711
|
-
)
|
|
712
|
-
|
|
713
|
-
expected_response = (
|
|
714
|
-
"Operator: Good morning, everyone.\n\nCEO: Thank you for joining us today."
|
|
715
|
-
)
|
|
716
|
-
|
|
717
|
-
tool = GetTranscript(kfinance_client=mock_client)
|
|
718
|
-
response = tool.run(GetTranscriptArgs(key_dev_id=12345).model_dump(mode="json"))
|
|
719
|
-
assert response == expected_response
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
class TestGetCompetitorsFromIdentifier:
|
|
723
|
-
def test_get_competitors_from_identifier(self, mock_client: Client, requests_mock: Mocker):
|
|
724
|
-
"""
|
|
725
|
-
GIVEN the GetCompetitorsFromIdentifier tool
|
|
726
|
-
WHEN we request the SPGI competitors that are named by competitors
|
|
727
|
-
THEN we get back the SPGI competitors that are named by competitors
|
|
728
|
-
"""
|
|
729
|
-
expected_competitors_response = {
|
|
730
|
-
"companies": [
|
|
731
|
-
{"company_id": 35352, "company_name": "The Descartes Systems Group Inc."},
|
|
732
|
-
{"company_id": 4003514, "company_name": "London Stock Exchange Group plc"},
|
|
733
|
-
]
|
|
734
|
-
}
|
|
735
|
-
requests_mock.get(
|
|
736
|
-
url=f"https://kfinance.kensho.com/api/v1/competitors/{SPGI_COMPANY_ID}/named_by_competitor",
|
|
737
|
-
# truncated from the original API response
|
|
738
|
-
json=expected_competitors_response,
|
|
739
|
-
)
|
|
740
|
-
|
|
741
|
-
tool = GetCompetitorsFromIdentifier(kfinance_client=mock_client)
|
|
742
|
-
args = GetCompetitorsFromIdentifierArgs(
|
|
743
|
-
identifier="SPGI", competitor_source=CompetitorSource.named_by_competitor
|
|
744
|
-
)
|
|
745
|
-
response = tool.run(args.model_dump(mode="json"))
|
|
746
|
-
assert response == expected_competitors_response
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
class TestGetEndpointsFromToolCallsWithGrounding:
|
|
750
|
-
def test_get_info_from_identifier_with_grounding(
|
|
751
|
-
self, mock_client: Client, requests_mock: Mocker
|
|
752
|
-
):
|
|
753
|
-
"""
|
|
754
|
-
GIVEN a KfinanceTool tool
|
|
755
|
-
WHEN we run the tool with `run_with_grounding`
|
|
756
|
-
THEN we get back endpoint urls in addition to the usual tool response.
|
|
757
|
-
"""
|
|
758
|
-
|
|
759
|
-
# truncated from the original
|
|
760
|
-
resp_data = "{'name': 'S&P Global Inc.', 'status': 'Operating'}"
|
|
761
|
-
resp_endpoint = [
|
|
762
|
-
"https://kfinance.kensho.com/api/v1/id/SPGI",
|
|
763
|
-
"https://kfinance.kensho.com/api/v1/info/21719",
|
|
764
|
-
]
|
|
765
|
-
expected_resp = {"data": resp_data, "endpoint_urls": resp_endpoint}
|
|
766
|
-
|
|
767
|
-
requests_mock.get(
|
|
768
|
-
url=f"https://kfinance.kensho.com/api/v1/info/{SPGI_COMPANY_ID}",
|
|
769
|
-
json=resp_data,
|
|
770
|
-
)
|
|
771
|
-
|
|
772
|
-
tool = GetInfoFromIdentifier(kfinance_client=mock_client)
|
|
773
|
-
resp = tool.run_with_grounding(identifier="SPGI")
|
|
774
|
-
assert resp == expected_resp
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
class TestValidQuarter:
|
|
778
|
-
class QuarterModel(BaseModel):
|
|
779
|
-
quarter: ValidQuarter | None
|
|
780
|
-
|
|
781
|
-
@pytest.mark.parametrize(
|
|
782
|
-
"input_quarter, expectation, expected_quarter",
|
|
783
|
-
[
|
|
784
|
-
pytest.param(1, does_not_raise(), 1, id="int input works"),
|
|
785
|
-
pytest.param("1", does_not_raise(), 1, id="str input works"),
|
|
786
|
-
pytest.param(None, does_not_raise(), None, id="None input works"),
|
|
787
|
-
pytest.param(5, pytest.raises(ValidationError), None, id="invalid int raises"),
|
|
788
|
-
pytest.param("5", pytest.raises(ValidationError), None, id="invalid str raises"),
|
|
789
|
-
],
|
|
790
|
-
)
|
|
791
|
-
def test_valid_quarter(
|
|
792
|
-
self,
|
|
793
|
-
input_quarter: int | str | None,
|
|
794
|
-
expectation: contextlib.AbstractContextManager,
|
|
795
|
-
expected_quarter: int | None,
|
|
796
|
-
) -> None:
|
|
797
|
-
"""
|
|
798
|
-
GIVEN a model that uses `ValidQuarter`
|
|
799
|
-
WHEN we deserialize with int, str, or None
|
|
800
|
-
THEN valid str get coerced to int. Invalid values raise.
|
|
801
|
-
"""
|
|
802
|
-
with expectation:
|
|
803
|
-
res = self.QuarterModel.model_validate(dict(quarter=input_quarter))
|
|
804
|
-
assert res.quarter == expected_quarter
|