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
|
@@ -18,14 +18,17 @@ class GetCapitalizationFromIdentifiersArgs(ToolArgsWithIdentifiers):
|
|
|
18
18
|
# no description because the description for enum fields comes from the enum docstring.
|
|
19
19
|
capitalization: Capitalization
|
|
20
20
|
start_date: date | None = Field(
|
|
21
|
-
description="The start date for historical capitalization retrieval",
|
|
21
|
+
description="The start date for historical capitalization retrieval. Use null for latest values. For annual data, use January 1st of the year.",
|
|
22
|
+
default=None,
|
|
22
23
|
)
|
|
23
24
|
end_date: date | None = Field(
|
|
24
|
-
description="The end date for historical capitalization retrieval",
|
|
25
|
+
description="The end date for historical capitalization retrieval. Use null for latest values. For annual data, use December 31st of the year.",
|
|
26
|
+
default=None,
|
|
25
27
|
)
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
class GetCapitalizationFromIdentifiersResp(ToolRespWithErrors):
|
|
31
|
+
capitalization: Capitalization
|
|
29
32
|
results: dict[str, Capitalizations]
|
|
30
33
|
|
|
31
34
|
|
|
@@ -35,11 +38,20 @@ class GetCapitalizationFromIdentifiers(KfinanceTool):
|
|
|
35
38
|
Get the historical market cap, tev (Total Enterprise Value), or shares outstanding for a group of identifiers between inclusive start_date and inclusive end date.
|
|
36
39
|
|
|
37
40
|
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
38
|
-
- When requesting the most recent values, leave start_date and end_date
|
|
41
|
+
- When requesting the most recent values, leave start_date and end_date null.
|
|
42
|
+
- For annual data (e.g., "market cap in 2020", "FY2021 values"), use the full year range: start_date as January 1st and end_date as December 31st.
|
|
43
|
+
- For "latest" or "current" values, always leave dates null to get the most recent data point.
|
|
44
|
+
- Only specify date ranges when the user explicitly requests historical data over a specific period.
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
Query: "What are the market caps of Visa and Mastercard?"
|
|
48
|
+
Function: get_capitalization_from_identifiers(capitalization="market_cap", identifiers=["Visa", "Mastercard"], start_date=null, end_date=null)
|
|
49
|
+
|
|
50
|
+
Query: "What was MDT's market cap in 2020?"
|
|
51
|
+
Function: get_capitalization_from_identifiers(capitalization="market_cap", identifiers=["MDT"], start_date="2020-01-01", end_date="2020-12-31")
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Function: get_capitalization_from_identifiers(capitalization=Capitalization.market_cap, identifiers=["AAPL", "WMT"])
|
|
53
|
+
Query: "Market cap trends for MSFT from Q1 2020 to Q3 2021"
|
|
54
|
+
Function: get_capitalization_from_identifiers(capitalization="market_cap", identifiers=["MSFT"], start_date="2020-01-01", end_date="2021-09-30")
|
|
43
55
|
""").strip()
|
|
44
56
|
args_schema: Type[BaseModel] = GetCapitalizationFromIdentifiersArgs
|
|
45
57
|
accepted_permissions: set[Permission] | None = {Permission.PricingPermission}
|
|
@@ -50,13 +62,13 @@ class GetCapitalizationFromIdentifiers(KfinanceTool):
|
|
|
50
62
|
capitalization: Capitalization,
|
|
51
63
|
start_date: str | None = None,
|
|
52
64
|
end_date: str | None = None,
|
|
53
|
-
) ->
|
|
65
|
+
) -> GetCapitalizationFromIdentifiersResp:
|
|
54
66
|
"""Sample response:
|
|
55
67
|
|
|
56
68
|
{
|
|
69
|
+
'capitalization': 'market_cap'
|
|
57
70
|
'results': {
|
|
58
71
|
'SPGI': {
|
|
59
|
-
'capitalizations': [
|
|
60
72
|
{'date': '2024-04-10', 'market_cap': {'value': '132766738270.00', 'unit': 'USD'}},
|
|
61
73
|
{'date': '2024-04-11', 'market_cap': {'value': '132416066761.00', 'unit': 'USD'}}
|
|
62
74
|
]
|
|
@@ -83,7 +95,11 @@ class GetCapitalizationFromIdentifiers(KfinanceTool):
|
|
|
83
95
|
api_client=api_client, tasks=tasks
|
|
84
96
|
)
|
|
85
97
|
|
|
86
|
-
for capitalization_response in capitalization_responses.
|
|
98
|
+
for identifier, capitalization_response in capitalization_responses.items():
|
|
99
|
+
# If we get an empty response for a company, assign an empty object
|
|
100
|
+
if not capitalization_response:
|
|
101
|
+
capitalization_responses[identifier] = Capitalizations(capitalizations=list())
|
|
102
|
+
capitalization_response = capitalization_responses[identifier]
|
|
87
103
|
# If we return results for more than one company and the start and end dates are unset,
|
|
88
104
|
# truncate data to only return the most recent datapoint.
|
|
89
105
|
if len(capitalization_responses) > 1 and start_date is None and end_date is None:
|
|
@@ -92,15 +108,17 @@ class GetCapitalizationFromIdentifiers(KfinanceTool):
|
|
|
92
108
|
]
|
|
93
109
|
# Set capitalizations that were not requested to None.
|
|
94
110
|
# That way, they can be skipped for serialization via `exclude_none=True`
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
if capitalization_response.capitalizations:
|
|
112
|
+
for daily_capitalization in capitalization_response.capitalizations:
|
|
113
|
+
if capitalization is not Capitalization.market_cap:
|
|
114
|
+
daily_capitalization.market_cap = None
|
|
115
|
+
if capitalization is not Capitalization.tev:
|
|
116
|
+
daily_capitalization.tev = None
|
|
117
|
+
if capitalization is not Capitalization.shares_outstanding:
|
|
118
|
+
daily_capitalization.shares_outstanding = None
|
|
119
|
+
|
|
120
|
+
return GetCapitalizationFromIdentifiersResp(
|
|
121
|
+
capitalization=capitalization,
|
|
122
|
+
results=capitalization_responses,
|
|
123
|
+
errors=list(id_triple_resp.errors.values()),
|
|
105
124
|
)
|
|
106
|
-
return resp_model.model_dump(mode="json", exclude_none=True)
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
1
4
|
from requests_mock import Mocker
|
|
2
5
|
|
|
3
6
|
from kfinance.client.kfinance import Client
|
|
7
|
+
from kfinance.client.models.decimal_with_unit import Money
|
|
4
8
|
from kfinance.conftest import SPGI_COMPANY_ID
|
|
5
|
-
from kfinance.domains.capitalizations.capitalization_models import
|
|
9
|
+
from kfinance.domains.capitalizations.capitalization_models import (
|
|
10
|
+
Capitalization,
|
|
11
|
+
Capitalizations,
|
|
12
|
+
DailyCapitalization,
|
|
13
|
+
)
|
|
6
14
|
from kfinance.domains.capitalizations.capitalization_tools import (
|
|
7
15
|
GetCapitalizationFromIdentifiers,
|
|
8
16
|
GetCapitalizationFromIdentifiersArgs,
|
|
17
|
+
GetCapitalizationFromIdentifiersResp,
|
|
9
18
|
)
|
|
10
19
|
from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
|
|
11
20
|
|
|
@@ -40,25 +49,30 @@ class TestGetCapitalizationFromCompanyIds:
|
|
|
40
49
|
json=self.market_caps_resp,
|
|
41
50
|
)
|
|
42
51
|
|
|
43
|
-
expected_response =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
expected_response = GetCapitalizationFromIdentifiersResp(
|
|
53
|
+
capitalization=Capitalization.market_cap,
|
|
54
|
+
results={
|
|
55
|
+
"SPGI": Capitalizations(
|
|
56
|
+
market_caps=[
|
|
57
|
+
DailyCapitalization(
|
|
58
|
+
date=date(2024, 4, 10),
|
|
59
|
+
market_cap=Money(value=Decimal(132766738270), unit="USD"),
|
|
60
|
+
tev=None,
|
|
61
|
+
shares_outstanding=None,
|
|
62
|
+
),
|
|
63
|
+
DailyCapitalization(
|
|
64
|
+
date=date(2024, 4, 11),
|
|
65
|
+
market_cap=Money(value=Decimal(132416066761), unit="USD"),
|
|
66
|
+
tev=None,
|
|
67
|
+
shares_outstanding=None,
|
|
68
|
+
),
|
|
55
69
|
]
|
|
56
|
-
|
|
70
|
+
),
|
|
57
71
|
},
|
|
58
|
-
|
|
72
|
+
errors=[
|
|
59
73
|
"No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
60
74
|
],
|
|
61
|
-
|
|
75
|
+
)
|
|
62
76
|
|
|
63
77
|
tool = GetCapitalizationFromIdentifiers(kfinance_client=mock_client)
|
|
64
78
|
args = GetCapitalizationFromIdentifiersArgs(
|
|
@@ -67,32 +81,48 @@ class TestGetCapitalizationFromCompanyIds:
|
|
|
67
81
|
response = tool.run(args.model_dump(mode="json"))
|
|
68
82
|
assert response == expected_response
|
|
69
83
|
|
|
84
|
+
def test_get_capitalization_from_identifiers_property_400(
|
|
85
|
+
self, requests_mock: Mocker, mock_client: Client
|
|
86
|
+
) -> None:
|
|
87
|
+
requests_mock.get(
|
|
88
|
+
url=f"https://kfinance.kensho.com/api/v1/market_cap/1/none/none",
|
|
89
|
+
status_code=400,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
expected_response = GetCapitalizationFromIdentifiersResp(
|
|
93
|
+
capitalization=Capitalization.market_cap,
|
|
94
|
+
results={"C_1": Capitalizations(market_caps=list())},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
tool = GetCapitalizationFromIdentifiers(kfinance_client=mock_client)
|
|
98
|
+
args = GetCapitalizationFromIdentifiersArgs(
|
|
99
|
+
identifiers=["C_1"], capitalization=Capitalization.market_cap
|
|
100
|
+
)
|
|
101
|
+
response = tool.run(args.model_dump(mode="json"))
|
|
102
|
+
assert response == expected_response
|
|
103
|
+
|
|
70
104
|
def test_most_recent_request(self, requests_mock: Mocker, mock_client: Client) -> None:
|
|
71
105
|
"""
|
|
72
106
|
GIVEN the GetCapitalizationFromIdentifiers tool
|
|
73
107
|
WHEN we request most recent market caps for multiple companies
|
|
74
108
|
THEN we only get back the most recent market cap for each company
|
|
75
109
|
"""
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
]
|
|
93
|
-
},
|
|
94
|
-
}
|
|
95
|
-
}
|
|
110
|
+
|
|
111
|
+
capitalization = Capitalizations(
|
|
112
|
+
market_caps=[
|
|
113
|
+
DailyCapitalization(
|
|
114
|
+
date=date(2024, 4, 11),
|
|
115
|
+
market_cap=Money(value=Decimal(132416066761), unit="USD"),
|
|
116
|
+
tev=None,
|
|
117
|
+
shares_outstanding=None,
|
|
118
|
+
)
|
|
119
|
+
]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
expected_response = GetCapitalizationFromIdentifiersResp(
|
|
123
|
+
capitalization=Capitalization.market_cap,
|
|
124
|
+
results={"C_1": capitalization, "C_2": capitalization},
|
|
125
|
+
)
|
|
96
126
|
|
|
97
127
|
company_ids = [1, 2]
|
|
98
128
|
for company_id in company_ids:
|
|
@@ -84,14 +84,20 @@ class UnifiedIdTripleResponse(BaseModel):
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
"""
|
|
87
|
-
|
|
87
|
+
# Separate successful and failed resolutions for kfinance api responses
|
|
88
88
|
if isinstance(data, dict) and "data" in data:
|
|
89
|
+
output: dict[str, dict] = dict(identifiers_to_id_triples=dict(), errors=dict())
|
|
90
|
+
|
|
89
91
|
for key, val in data["data"].items():
|
|
90
92
|
if "error" in val:
|
|
91
93
|
output["errors"][key] = val["error"]
|
|
92
94
|
else:
|
|
93
95
|
output["identifiers_to_id_triples"][key] = val
|
|
94
|
-
|
|
96
|
+
return output
|
|
97
|
+
# In all other cases (e.g. UnifiedIdTripleResponse directly initialized),
|
|
98
|
+
# just return the data.
|
|
99
|
+
else:
|
|
100
|
+
return data
|
|
95
101
|
|
|
96
102
|
def filter_out_companies_without_security_ids(self) -> None:
|
|
97
103
|
"""Filter out companies that don't have a security_id and add an error for them."""
|
|
@@ -119,6 +125,20 @@ class UnifiedIdTripleResponse(BaseModel):
|
|
|
119
125
|
)
|
|
120
126
|
self.identifiers_to_id_triples.pop(identifier)
|
|
121
127
|
|
|
128
|
+
def get_identifier_from_company_id(self, company_id: int) -> str:
|
|
129
|
+
"""Return the (originally passed) identifier from a company id."""
|
|
130
|
+
if not hasattr(self, "_company_id_to_identifier"):
|
|
131
|
+
self._company_id_to_identifier = {
|
|
132
|
+
id_triple.company_id: identifier
|
|
133
|
+
for identifier, id_triple in self.identifiers_to_id_triples.items()
|
|
134
|
+
}
|
|
135
|
+
return self._company_id_to_identifier[company_id]
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def company_ids(self) -> list[int]:
|
|
139
|
+
"""Returns a list of all company ids in the response."""
|
|
140
|
+
return [id_triple.company_id for id_triple in self.identifiers_to_id_triples.values()]
|
|
141
|
+
|
|
122
142
|
|
|
123
143
|
class CompanyDescriptions(BaseModel):
|
|
124
144
|
"""A company summary and description"""
|
|
@@ -5,7 +5,11 @@ from pydantic import BaseModel
|
|
|
5
5
|
|
|
6
6
|
from kfinance.client.batch_request_handling import Task, process_tasks_in_thread_pool_executor
|
|
7
7
|
from kfinance.client.permission_models import Permission
|
|
8
|
-
from kfinance.domains.companies.company_models import
|
|
8
|
+
from kfinance.domains.companies.company_models import (
|
|
9
|
+
COMPANY_ID_PREFIX,
|
|
10
|
+
CompanyDescriptions,
|
|
11
|
+
CompanyOtherNames,
|
|
12
|
+
)
|
|
9
13
|
from kfinance.integrations.tool_calling.tool_calling_models import (
|
|
10
14
|
KfinanceTool,
|
|
11
15
|
ToolArgsWithIdentifiers,
|
|
@@ -20,14 +24,21 @@ class GetInfoFromIdentifiersResp(ToolRespWithErrors):
|
|
|
20
24
|
class GetInfoFromIdentifiers(KfinanceTool):
|
|
21
25
|
name: str = "get_info_from_identifiers"
|
|
22
26
|
description: str = dedent("""
|
|
23
|
-
Get the information associated with a list of identifiers. Info includes company name, status, type, simple industry, number of employees (if available), founding date, webpage, HQ address, HQ city, HQ zip code, HQ state, HQ country,
|
|
27
|
+
Get the information associated with a list of identifiers. Info includes company name, status, type, simple industry, number of employees (if available), founding date, webpage, HQ address, HQ city, HQ zip code, HQ state, HQ country, HQ country iso code, and CIQ company_id.
|
|
24
28
|
|
|
25
29
|
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
Query: "What's the company information for Northrop Grumman and Lockheed Martin?"
|
|
33
|
+
Function: get_info_from_identifiers(identifiers=["Northrop Grumman", "Lockheed Martin"])
|
|
34
|
+
|
|
35
|
+
Query: "Get company info for UBER and LYFT"
|
|
36
|
+
Function: get_info_from_identifiers(identifiers=["UBER", "LYFT"])
|
|
26
37
|
""").strip()
|
|
27
38
|
args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
|
|
28
39
|
accepted_permissions: set[Permission] | None = None
|
|
29
40
|
|
|
30
|
-
def _run(self, identifiers: list[str]) ->
|
|
41
|
+
def _run(self, identifiers: list[str]) -> GetInfoFromIdentifiersResp:
|
|
31
42
|
"""Sample response:
|
|
32
43
|
|
|
33
44
|
{ "results": {
|
|
@@ -44,7 +55,8 @@ class GetInfoFromIdentifiers(KfinanceTool):
|
|
|
44
55
|
"zip_code": "10041-0001",
|
|
45
56
|
"state": "New York",
|
|
46
57
|
"country": "United States",
|
|
47
|
-
"iso_country": "USA"
|
|
58
|
+
"iso_country": "USA",
|
|
59
|
+
"company_id": "C_21719"
|
|
48
60
|
}
|
|
49
61
|
},
|
|
50
62
|
"errors": [['No identification triple found for the provided identifier: NON-EXISTENT of type: ticker']
|
|
@@ -65,10 +77,13 @@ class GetInfoFromIdentifiers(KfinanceTool):
|
|
|
65
77
|
info_responses: dict[str, dict] = process_tasks_in_thread_pool_executor(
|
|
66
78
|
api_client=api_client, tasks=tasks
|
|
67
79
|
)
|
|
68
|
-
|
|
80
|
+
|
|
81
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items():
|
|
82
|
+
info_responses[identifier]["company_id"] = f"{COMPANY_ID_PREFIX}{id_triple.company_id}"
|
|
83
|
+
|
|
84
|
+
return GetInfoFromIdentifiersResp(
|
|
69
85
|
results=info_responses, errors=list(id_triple_resp.errors.values())
|
|
70
86
|
)
|
|
71
|
-
return resp_model.model_dump(mode="json")
|
|
72
87
|
|
|
73
88
|
|
|
74
89
|
class GetCompanyOtherNamesFromIdentifiersResp(ToolRespWithErrors):
|
|
@@ -81,6 +96,13 @@ class GetCompanyOtherNamesFromIdentifiers(KfinanceTool):
|
|
|
81
96
|
Given a list of identifiers, fetch the alternate, historical, and native names associated with each identifier. Alternate names are additional names a company might go by (for example, Hewlett-Packard Company also goes by the name HP). Historical names are previous names for the company if it has changed over time. Native names are primary non-Latin character native names for global companies, including languages such as Arabic, Russian, Greek, Japanese, etc. This also includes limited history on native name changes.
|
|
82
97
|
|
|
83
98
|
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
Query: "What are the alternate names for Meta and Alphabet?"
|
|
102
|
+
Function: get_company_other_names_from_identifiers(identifiers=["Meta", "Alphabet"])
|
|
103
|
+
|
|
104
|
+
Query: "Get other names for NSRGY"
|
|
105
|
+
Function: get_company_other_names_from_identifiers(identifiers=["NSRGY"])
|
|
84
106
|
""").strip()
|
|
85
107
|
args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
|
|
86
108
|
accepted_permissions: set[Permission] | None = {Permission.CompanyIntelligencePermission}
|
|
@@ -88,7 +110,7 @@ class GetCompanyOtherNamesFromIdentifiers(KfinanceTool):
|
|
|
88
110
|
def _run(
|
|
89
111
|
self,
|
|
90
112
|
identifiers: list[str],
|
|
91
|
-
) ->
|
|
113
|
+
) -> GetCompanyOtherNamesFromIdentifiersResp:
|
|
92
114
|
api_client = self.kfinance_client.kfinance_api_client
|
|
93
115
|
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
94
116
|
tasks = [
|
|
@@ -102,10 +124,9 @@ class GetCompanyOtherNamesFromIdentifiers(KfinanceTool):
|
|
|
102
124
|
info_responses: dict[str, CompanyOtherNames] = process_tasks_in_thread_pool_executor(
|
|
103
125
|
api_client=api_client, tasks=tasks
|
|
104
126
|
)
|
|
105
|
-
|
|
127
|
+
return GetCompanyOtherNamesFromIdentifiersResp(
|
|
106
128
|
results=info_responses, errors=list(id_triple_resp.errors.values())
|
|
107
129
|
)
|
|
108
|
-
return resp_model.model_dump(mode="json")
|
|
109
130
|
|
|
110
131
|
|
|
111
132
|
class GetCompanySummaryFromIdentifiersResp(ToolRespWithErrors):
|
|
@@ -115,9 +136,16 @@ class GetCompanySummaryFromIdentifiersResp(ToolRespWithErrors):
|
|
|
115
136
|
class GetCompanySummaryFromIdentifiers(KfinanceTool):
|
|
116
137
|
name: str = "get_company_summary_from_identifiers"
|
|
117
138
|
description: str = dedent("""
|
|
118
|
-
Get one paragraph summary/short descriptions of companies, including information about the company's primary business, products and services offered and their applications, business segment details, client/customer groups served, geographic markets served, distribution channels, strategic alliances/partnerships, founded/incorporated year, latest former name, and headquarters and additional offices.
|
|
139
|
+
Get one paragraph summary/short descriptions of companies, including information about the company's primary business, products and services offered and their applications, business segment details, client/customer groups served, geographic markets served, distribution channels, strategic alliances/partnerships, founded/incorporated year, latest former name, and headquarters and additional offices.
|
|
119
140
|
|
|
120
141
|
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
142
|
+
|
|
143
|
+
Examples:
|
|
144
|
+
Query: "Give me summaries of Tesla and General Motors"
|
|
145
|
+
Function: get_company_summary_from_identifiers(identifiers=["Tesla", "General Motors"])
|
|
146
|
+
|
|
147
|
+
Query: "What are the summaries for F and STLA?"
|
|
148
|
+
Function: get_company_summary_from_identifiers(identifiers=["F", "STLA"])
|
|
121
149
|
""").strip()
|
|
122
150
|
args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
|
|
123
151
|
accepted_permissions: set[Permission] | None = {Permission.CompanyIntelligencePermission}
|
|
@@ -125,7 +153,7 @@ class GetCompanySummaryFromIdentifiers(KfinanceTool):
|
|
|
125
153
|
def _run(
|
|
126
154
|
self,
|
|
127
155
|
identifiers: list[str],
|
|
128
|
-
) ->
|
|
156
|
+
) -> GetCompanySummaryFromIdentifiersResp:
|
|
129
157
|
api_client = self.kfinance_client.kfinance_api_client
|
|
130
158
|
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
131
159
|
|
|
@@ -147,10 +175,9 @@ class GetCompanySummaryFromIdentifiers(KfinanceTool):
|
|
|
147
175
|
for identifier, descriptions in company_description_responses.items()
|
|
148
176
|
}
|
|
149
177
|
|
|
150
|
-
|
|
178
|
+
return GetCompanySummaryFromIdentifiersResp(
|
|
151
179
|
results=summary_results, errors=list(id_triple_resp.errors.values())
|
|
152
180
|
)
|
|
153
|
-
return resp_model.model_dump(mode="json")
|
|
154
181
|
|
|
155
182
|
|
|
156
183
|
class GetCompanyDescriptionFromIdentifiersResp(ToolRespWithErrors):
|
|
@@ -163,6 +190,13 @@ class GetCompanyDescriptionFromIdentifiers(KfinanceTool):
|
|
|
163
190
|
Get detailed descriptions of companies, broken down into sections, which may include information about the company's Primary business, Segments (including Products and Services for each), Competition, Significant events, and History. Within the text, four spaces represent a new paragraph. Note that the description is divided into sections with headers, where each section has a new paragraph (four spaces) before and after the section header.
|
|
164
191
|
|
|
165
192
|
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
193
|
+
|
|
194
|
+
Examples:
|
|
195
|
+
Query: "Get detailed descriptions for Netflix and Disney"
|
|
196
|
+
Function: get_company_description_from_identifiers(identifiers=["Netflix", "Disney"])
|
|
197
|
+
|
|
198
|
+
Query: "What are the detailed company descriptions for KO and PEP?"
|
|
199
|
+
Function: get_company_description_from_identifiers(identifiers=["KO", "PEP"])
|
|
166
200
|
""").strip()
|
|
167
201
|
args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
|
|
168
202
|
accepted_permissions: set[Permission] | None = {Permission.CompanyIntelligencePermission}
|
|
@@ -170,7 +204,7 @@ class GetCompanyDescriptionFromIdentifiers(KfinanceTool):
|
|
|
170
204
|
def _run(
|
|
171
205
|
self,
|
|
172
206
|
identifiers: list[str],
|
|
173
|
-
) ->
|
|
207
|
+
) -> GetCompanyDescriptionFromIdentifiersResp:
|
|
174
208
|
api_client = self.kfinance_client.kfinance_api_client
|
|
175
209
|
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
176
210
|
|
|
@@ -192,7 +226,6 @@ class GetCompanyDescriptionFromIdentifiers(KfinanceTool):
|
|
|
192
226
|
for identifier, descriptions in company_description_responses.items()
|
|
193
227
|
}
|
|
194
228
|
|
|
195
|
-
|
|
229
|
+
return GetCompanyDescriptionFromIdentifiersResp(
|
|
196
230
|
results=description_results, errors=list(id_triple_resp.errors.values())
|
|
197
231
|
)
|
|
198
|
-
return resp_model.model_dump(mode="json")
|
|
@@ -2,11 +2,16 @@ from requests_mock import Mocker
|
|
|
2
2
|
|
|
3
3
|
from kfinance.client.kfinance import Client
|
|
4
4
|
from kfinance.conftest import SPGI_COMPANY_ID
|
|
5
|
+
from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
|
|
5
6
|
from kfinance.domains.companies.company_tools import (
|
|
6
7
|
GetCompanyDescriptionFromIdentifiers,
|
|
8
|
+
GetCompanyDescriptionFromIdentifiersResp,
|
|
7
9
|
GetCompanyOtherNamesFromIdentifiers,
|
|
10
|
+
GetCompanyOtherNamesFromIdentifiersResp,
|
|
8
11
|
GetCompanySummaryFromIdentifiers,
|
|
12
|
+
GetCompanySummaryFromIdentifiersResp,
|
|
9
13
|
GetInfoFromIdentifiers,
|
|
14
|
+
GetInfoFromIdentifiersResp,
|
|
10
15
|
)
|
|
11
16
|
from kfinance.integrations.tool_calling.tool_calling_models import ToolArgsWithIdentifiers
|
|
12
17
|
|
|
@@ -19,13 +24,20 @@ class TestGetInfoFromIdentifiers:
|
|
|
19
24
|
THEN we get back info for SPGI and an error for the non-existent company
|
|
20
25
|
"""
|
|
21
26
|
|
|
22
|
-
info_resp = {
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
27
|
-
],
|
|
27
|
+
info_resp = {
|
|
28
|
+
"name": "S&P Global Inc.",
|
|
29
|
+
"status": "Operating",
|
|
30
|
+
"company_id": f"{COMPANY_ID_PREFIX}{SPGI_COMPANY_ID}",
|
|
28
31
|
}
|
|
32
|
+
expected_response = GetInfoFromIdentifiersResp.model_validate(
|
|
33
|
+
{
|
|
34
|
+
"results": {"SPGI": info_resp},
|
|
35
|
+
"errors": [
|
|
36
|
+
"No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
37
|
+
],
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
del info_resp["company_id"]
|
|
29
41
|
requests_mock.get(
|
|
30
42
|
url=f"https://kfinance.kensho.com/api/v1/info/{SPGI_COMPANY_ID}",
|
|
31
43
|
json=info_resp,
|
|
@@ -61,7 +73,9 @@ class TestGetCompanyDescriptions:
|
|
|
61
73
|
tool = GetCompanySummaryFromIdentifiers(kfinance_client=mock_client)
|
|
62
74
|
args = ToolArgsWithIdentifiers(identifiers=["SPGI"])
|
|
63
75
|
response = tool.run(args.model_dump(mode="json"))
|
|
64
|
-
expected_response =
|
|
76
|
+
expected_response = GetCompanySummaryFromIdentifiersResp.model_validate(
|
|
77
|
+
{"results": {"SPGI": self.summary}}
|
|
78
|
+
)
|
|
65
79
|
assert response == expected_response
|
|
66
80
|
|
|
67
81
|
def test_get_company_description_from_identifier(
|
|
@@ -81,7 +95,9 @@ class TestGetCompanyDescriptions:
|
|
|
81
95
|
tool = GetCompanyDescriptionFromIdentifiers(kfinance_client=mock_client)
|
|
82
96
|
args = ToolArgsWithIdentifiers(identifiers=["SPGI"])
|
|
83
97
|
response = tool.run(args.model_dump(mode="json"))
|
|
84
|
-
expected_response =
|
|
98
|
+
expected_response = GetCompanyDescriptionFromIdentifiersResp.model_validate(
|
|
99
|
+
{"results": {"SPGI": self.description}}
|
|
100
|
+
)
|
|
85
101
|
assert response == expected_response
|
|
86
102
|
|
|
87
103
|
|
|
@@ -121,5 +137,7 @@ class TestGetCompanyOtherNames:
|
|
|
121
137
|
tool = GetCompanyOtherNamesFromIdentifiers(kfinance_client=mock_client)
|
|
122
138
|
args = ToolArgsWithIdentifiers(identifiers=["SPGI"])
|
|
123
139
|
response = tool.run(args.model_dump(mode="json"))
|
|
124
|
-
expected_response =
|
|
140
|
+
expected_response = GetCompanyOtherNamesFromIdentifiersResp.model_validate(
|
|
141
|
+
{"results": {"SPGI": self.company_other_names_info}}
|
|
142
|
+
)
|
|
125
143
|
assert response == expected_response
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
2
|
+
from typing import Type
|
|
3
|
+
|
|
1
4
|
from kfinance.client.batch_request_handling import Task, process_tasks_in_thread_pool_executor
|
|
2
5
|
from kfinance.client.permission_models import Permission
|
|
3
6
|
from kfinance.domains.competitors.competitor_models import CompetitorResponse, CompetitorSource
|
|
@@ -19,15 +22,27 @@ class GetCompetitorsFromIdentifiersResp(ToolRespWithErrors):
|
|
|
19
22
|
|
|
20
23
|
class GetCompetitorsFromIdentifiers(KfinanceTool):
|
|
21
24
|
name: str = "get_competitors_from_identifiers"
|
|
22
|
-
description: str = "
|
|
23
|
-
|
|
25
|
+
description: str = dedent("""
|
|
26
|
+
Retrieves a list of company_id and company_name that are competitors for a list of companies, filtered by the source of the competitor information.
|
|
27
|
+
|
|
28
|
+
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
29
|
+
- Available competitor sources: all, filing (from SEC filings), key_dev (from key developments), contact (from contact relationships), third_party (from third-party sources), self_identified (self-identified), named_by_competitor (from competitor's perspective)
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
Query: "Who are Microsoft's competitors from SEC filings?"
|
|
33
|
+
Function: get_competitors_from_identifiers(identifiers=["Microsoft"], competitor_source="filing")
|
|
34
|
+
|
|
35
|
+
Query: "Get all competitors of AAPL and GOOGL"
|
|
36
|
+
Function: get_competitors_from_identifiers(identifiers=["AAPL", "GOOGL"], competitor_source="all")
|
|
37
|
+
""").strip()
|
|
38
|
+
args_schema: Type[GetCompetitorsFromIdentifiersArgs] = GetCompetitorsFromIdentifiersArgs
|
|
24
39
|
accepted_permissions: set[Permission] | None = {Permission.CompetitorsPermission}
|
|
25
40
|
|
|
26
41
|
def _run(
|
|
27
42
|
self,
|
|
28
43
|
identifiers: list[str],
|
|
29
44
|
competitor_source: CompetitorSource,
|
|
30
|
-
) ->
|
|
45
|
+
) -> GetCompetitorsFromIdentifiersResp:
|
|
31
46
|
"""Sample response:
|
|
32
47
|
|
|
33
48
|
{
|
|
@@ -56,7 +71,6 @@ class GetCompetitorsFromIdentifiers(KfinanceTool):
|
|
|
56
71
|
competitor_responses: dict[str, CompetitorResponse] = process_tasks_in_thread_pool_executor(
|
|
57
72
|
api_client=api_client, tasks=tasks
|
|
58
73
|
)
|
|
59
|
-
|
|
74
|
+
return GetCompetitorsFromIdentifiersResp(
|
|
60
75
|
results=competitor_responses, errors=list(id_triple_resp.errors.values())
|
|
61
76
|
)
|
|
62
|
-
return resp_model.model_dump(mode="json")
|
|
@@ -6,6 +6,7 @@ from kfinance.domains.competitors.competitor_models import CompetitorSource
|
|
|
6
6
|
from kfinance.domains.competitors.competitor_tools import (
|
|
7
7
|
GetCompetitorsFromIdentifiers,
|
|
8
8
|
GetCompetitorsFromIdentifiersArgs,
|
|
9
|
+
GetCompetitorsFromIdentifiersResp,
|
|
9
10
|
)
|
|
10
11
|
|
|
11
12
|
|
|
@@ -24,25 +25,27 @@ class TestGetCompetitorsFromIdentifiers:
|
|
|
24
25
|
{"company_id": 4003514, "company_name": "London Stock Exchange Group plc"},
|
|
25
26
|
]
|
|
26
27
|
}
|
|
27
|
-
expected_response =
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
expected_response = GetCompetitorsFromIdentifiersResp.model_validate(
|
|
29
|
+
{
|
|
30
|
+
"results": {
|
|
31
|
+
"SPGI": {
|
|
32
|
+
"competitors": [
|
|
33
|
+
{
|
|
34
|
+
"company_id": 35352,
|
|
35
|
+
"company_name": "The Descartes Systems Group Inc.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"company_id": 4003514,
|
|
39
|
+
"company_name": "London Stock Exchange Group plc",
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"errors": [
|
|
45
|
+
"No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
|
|
46
|
+
],
|
|
47
|
+
}
|
|
48
|
+
)
|
|
46
49
|
|
|
47
50
|
requests_mock.get(
|
|
48
51
|
url=f"https://kfinance.kensho.com/api/v1/competitors/{SPGI_COMPANY_ID}/named_by_competitor",
|