kensho-kfinance 3.1.0__py3-none-any.whl → 3.2.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.
Potentially problematic release.
This version of kensho-kfinance might be problematic. Click here for more details.
- {kensho_kfinance-3.1.0.dist-info → kensho_kfinance-3.2.0.dist-info}/METADATA +1 -1
- {kensho_kfinance-3.1.0.dist-info → kensho_kfinance-3.2.0.dist-info}/RECORD +16 -16
- kfinance/CHANGELOG.md +6 -0
- kfinance/client/fetch.py +18 -1
- kfinance/client/meta_classes.py +55 -0
- kfinance/client/permission_models.py +1 -0
- kfinance/client/tests/test_fetch.py +77 -1
- kfinance/domains/companies/company_models.py +22 -0
- kfinance/domains/companies/company_tools.py +128 -0
- kfinance/domains/companies/tests/test_company_tools.py +93 -1
- kfinance/integrations/tool_calling/prompts.py +21 -14
- kfinance/version.py +2 -2
- {kensho_kfinance-3.1.0.dist-info → kensho_kfinance-3.2.0.dist-info}/WHEEL +0 -0
- {kensho_kfinance-3.1.0.dist-info → kensho_kfinance-3.2.0.dist-info}/licenses/AUTHORS.md +0 -0
- {kensho_kfinance-3.1.0.dist-info → kensho_kfinance-3.2.0.dist-info}/licenses/LICENSE +0 -0
- {kensho_kfinance-3.1.0.dist-info → kensho_kfinance-3.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kensho-kfinance
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Python CLI for kFinance
|
|
5
5
|
Author-email: Luke Brown <luke.brown@kensho.com>, Michelle Keoy <michelle.keoy@kensho.com>, Keith Page <keith.page@kensho.com>, Matthew Rosen <matthew.rosen@kensho.com>, Nick Roshdieh <nick.roshdieh@kensho.com>
|
|
6
6
|
Project-URL: source, https://github.com/kensho-technologies/kfinance
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
kensho_kfinance-3.
|
|
2
|
-
kensho_kfinance-3.
|
|
3
|
-
kfinance/CHANGELOG.md,sha256=
|
|
1
|
+
kensho_kfinance-3.2.0.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
|
|
2
|
+
kensho_kfinance-3.2.0.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
|
|
3
|
+
kfinance/CHANGELOG.md,sha256=q1nGxRpIJgjeQ-p2WEEBvPfC93NQ0GSaof7wprmBvqY,2659
|
|
4
4
|
kfinance/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
5
5
|
kfinance/conftest.py,sha256=O9e1ddyTfqnZ1T-9ehnkXwK8PfxPdCHAI7-fke52Ouk,3281
|
|
6
6
|
kfinance/mcp.py,sha256=LTzCIlqsDKRD-0Xcpa_P99-JmJ8duAneO_-GzM43kjw,424
|
|
7
7
|
kfinance/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
kfinance/version.py,sha256=
|
|
8
|
+
kfinance/version.py,sha256=y06zal8IRedOSeT6uowixE-C3eIqYMoBwLenNv0a-Zc,511
|
|
9
9
|
kfinance/client/README.md,sha256=DA5vg4uz1JmJNiqvYywrj46YNhOr584WO8L83Ysx_Mk,372
|
|
10
10
|
kfinance/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
kfinance/client/batch_request_handling.py,sha256=opwJJAU2JtqH-s4vt8wRqhH34MiU4tQP6Ng7K6b3upA,6561
|
|
12
|
-
kfinance/client/fetch.py,sha256=
|
|
12
|
+
kfinance/client/fetch.py,sha256=JKw09G8P9jMw6qwVY4E-SAOW1YhZqVzOkirh0Nu8g9o,29336
|
|
13
13
|
kfinance/client/industry_models.py,sha256=ydB_cTIsNsfCUeaO7K2qqLUuHdG6WfUCoAzBigKVV8M,243
|
|
14
14
|
kfinance/client/kfinance.py,sha256=9BMhXfmw5WzNl-xFdLvkvgJIr585F5rlM7YePwtlW7s,72684
|
|
15
|
-
kfinance/client/meta_classes.py,sha256=
|
|
16
|
-
kfinance/client/permission_models.py,sha256=
|
|
15
|
+
kfinance/client/meta_classes.py,sha256=O0qoz1ALveS25GgOVOfMjjsw0gl9gojpiLCfF-a8xsY,23622
|
|
16
|
+
kfinance/client/permission_models.py,sha256=6pvoxRz5QGYvkSih_rrlBh4Yf5ltxTD1aeP5a6wCuRw,703
|
|
17
17
|
kfinance/client/server_thread.py,sha256=jUnt1YGoYDkqqz1MbCwd44zJs1T_Z2BCgvj75bdtLgA,2574
|
|
18
18
|
kfinance/client/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
kfinance/client/models/currency_models.py,sha256=JfDZf-nATCyjThBw-Ky2BNvM-n2oHepBLIHMCIyCRUE,19201
|
|
@@ -24,7 +24,7 @@ kfinance/client/models/tests/test_decimal_with_unit.py,sha256=7WlmVXFOFNHFqwsvXQ
|
|
|
24
24
|
kfinance/client/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
kfinance/client/tests/test_batch_requests.py,sha256=KcE5rNJHBP27xaZOmwg8cVyUMZAAPt6jDiCeq94wUUk,11661
|
|
26
26
|
kfinance/client/tests/test_client.py,sha256=GlK89ZOGL9bwrAUFUk8nua2ooKt0JYm7o_rpAfajFXA,4312
|
|
27
|
-
kfinance/client/tests/test_fetch.py,sha256=
|
|
27
|
+
kfinance/client/tests/test_fetch.py,sha256=nFVQ3hHn7gqe7ecTYRviRxLuf06QyFZegfDLLOSUpR8,21569
|
|
28
28
|
kfinance/client/tests/test_group_objects.py,sha256=wb7MEyGm-Qya3CXWM5Xz3kUhKFWdHT8FF3cQcjt1WxQ,1696
|
|
29
29
|
kfinance/client/tests/test_objects.py,sha256=eAEy1kAHS6JQDoXVU5uSyTQpV099F3xFZsbvgspWMn8,42623
|
|
30
30
|
kfinance/domains/README.md,sha256=ehD15VwLupdXbz5M7hHUI7Y-2SCGxaP5W7Q4NuMcN64,664
|
|
@@ -42,10 +42,10 @@ kfinance/domains/capitalizations/tests/test_capitalization_models.py,sha256=SSFx
|
|
|
42
42
|
kfinance/domains/capitalizations/tests/test_capitalization_tools.py,sha256=LDjFiEEax2O65HQ65CY73J3y4RvuKjw5pqBO5imoUyY,4187
|
|
43
43
|
kfinance/domains/companies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
44
|
kfinance/domains/companies/company_identifiers.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
-
kfinance/domains/companies/company_models.py,sha256=
|
|
46
|
-
kfinance/domains/companies/company_tools.py,sha256=
|
|
45
|
+
kfinance/domains/companies/company_models.py,sha256=ez87FsQlpYvAmd668sAgSbdgFum4PjMisN59J4vkqNo,4819
|
|
46
|
+
kfinance/domains/companies/company_tools.py,sha256=wwhB98fg8mKOJp1kvN-KKFhKurLx6F6KX3BfLPJkWP0,8984
|
|
47
47
|
kfinance/domains/companies/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
-
kfinance/domains/companies/tests/test_company_tools.py,sha256=
|
|
48
|
+
kfinance/domains/companies/tests/test_company_tools.py,sha256=i9_P5mf92wZv_mcnWhK5L8BjbBLfsuBtxPLcgPOyHzE,4895
|
|
49
49
|
kfinance/domains/competitors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
50
|
kfinance/domains/competitors/competitor_models.py,sha256=HAEn2AzKZNusclsFzyAufpS-627RfRGL4Vw1AiT_Ek0,747
|
|
51
51
|
kfinance/domains/competitors/competitor_tools.py,sha256=W_vRMng5UG1csJWDZKDxibrvXzUcwwnN9hw5cSg4EHI,2551
|
|
@@ -95,7 +95,7 @@ kfinance/integrations/tests/test_example_notebook.py,sha256=NrqYFn_XyOn0YlV9QYEn
|
|
|
95
95
|
kfinance/integrations/tool_calling/README.md,sha256=TSk-AJddoEctzc0HXQvGNac2CckbnWxHcRDtEifSe1w,2029
|
|
96
96
|
kfinance/integrations/tool_calling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
97
97
|
kfinance/integrations/tool_calling/all_tools.py,sha256=MbvAZX2yEyovGtBNcr5f6n2G_lV5Zc9nnSn7xgH3zjQ,2115
|
|
98
|
-
kfinance/integrations/tool_calling/prompts.py,sha256=
|
|
98
|
+
kfinance/integrations/tool_calling/prompts.py,sha256=4qeW_VLLcxutUFuXnMKa0QD-_jq9qufo03BY-Slz-XU,1718
|
|
99
99
|
kfinance/integrations/tool_calling/tool_calling_models.py,sha256=o1LGGwx8ikwEiZaHEALS4rvymF9O1Mp5dO9LcfFheUI,5856
|
|
100
100
|
kfinance/integrations/tool_calling/static_tools/README.md,sha256=lWQWdLY1tkGxqE8o4f61gc-RCX0doaCnZM0GmcNO6Jo,97
|
|
101
101
|
kfinance/integrations/tool_calling/static_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -107,7 +107,7 @@ kfinance/integrations/tool_calling/static_tools/tests/test_get_n_quarters_ago.py
|
|
|
107
107
|
kfinance/integrations/tool_calling/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
108
|
kfinance/integrations/tool_calling/tests/test_tool_calling_models.py,sha256=Ts1pvaYjq9BTR71dMZ73X__bQ7gQynPkQjy-9x2CuyQ,2605
|
|
109
109
|
kfinance/models/permission_models.py,sha256=G0so8ZOsD1YDsCM4he0z5R9M_shUbSRwD3hUDJdqZl0,635
|
|
110
|
-
kensho_kfinance-3.
|
|
111
|
-
kensho_kfinance-3.
|
|
112
|
-
kensho_kfinance-3.
|
|
113
|
-
kensho_kfinance-3.
|
|
110
|
+
kensho_kfinance-3.2.0.dist-info/METADATA,sha256=2qQ3tv6IrgkR2Hv3n3VdzlN6J6iccWG-j9Sim4_wthc,6202
|
|
111
|
+
kensho_kfinance-3.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
112
|
+
kensho_kfinance-3.2.0.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
|
|
113
|
+
kensho_kfinance-3.2.0.dist-info/RECORD,,
|
kfinance/CHANGELOG.md
CHANGED
kfinance/client/fetch.py
CHANGED
|
@@ -17,7 +17,12 @@ from kfinance.domains.business_relationships.business_relationship_models import
|
|
|
17
17
|
RelationshipResponse,
|
|
18
18
|
)
|
|
19
19
|
from kfinance.domains.capitalizations.capitalization_models import Capitalizations
|
|
20
|
-
from kfinance.domains.companies.company_models import
|
|
20
|
+
from kfinance.domains.companies.company_models import (
|
|
21
|
+
CompanyDescriptions,
|
|
22
|
+
CompanyOtherNames,
|
|
23
|
+
IdentificationTriple,
|
|
24
|
+
UnifiedIdTripleResponse,
|
|
25
|
+
)
|
|
21
26
|
from kfinance.domains.competitors.competitor_models import CompetitorResponse, CompetitorSource
|
|
22
27
|
from kfinance.domains.earnings.earning_models import EarningsCallResp
|
|
23
28
|
from kfinance.domains.line_items.line_item_models import LineItemResponse
|
|
@@ -602,6 +607,18 @@ class KFinanceApiClient:
|
|
|
602
607
|
url = f"{self.url_base}transcript/{key_dev_id}"
|
|
603
608
|
return self.fetch(url)
|
|
604
609
|
|
|
610
|
+
def fetch_company_descriptions(self, company_id: int) -> CompanyDescriptions:
|
|
611
|
+
"""Get the short description (summary) and long description for a company"""
|
|
612
|
+
url = f"{self.url_base}info/{company_id}/descriptions"
|
|
613
|
+
result = self.fetch(url)
|
|
614
|
+
return CompanyDescriptions.model_validate(result)
|
|
615
|
+
|
|
616
|
+
def fetch_company_other_names(self, company_id: int) -> CompanyOtherNames:
|
|
617
|
+
"""Get the alternate, historical, and native names for a company"""
|
|
618
|
+
url = f"{self.url_base}info/{company_id}/names"
|
|
619
|
+
result = self.fetch(url)
|
|
620
|
+
return CompanyOtherNames.model_validate(result)
|
|
621
|
+
|
|
605
622
|
def fetch_competitors(
|
|
606
623
|
self, company_id: int, competitor_source: CompetitorSource
|
|
607
624
|
) -> CompetitorResponse:
|
kfinance/client/meta_classes.py
CHANGED
|
@@ -13,6 +13,11 @@ from kfinance.domains.business_relationships.business_relationship_models import
|
|
|
13
13
|
BusinessRelationshipType,
|
|
14
14
|
)
|
|
15
15
|
from kfinance.domains.capitalizations.capitalization_models import Capitalization
|
|
16
|
+
from kfinance.domains.companies.company_models import (
|
|
17
|
+
CompanyDescriptions,
|
|
18
|
+
CompanyOtherNames,
|
|
19
|
+
NativeName,
|
|
20
|
+
)
|
|
16
21
|
from kfinance.domains.competitors.competitor_models import CompetitorSource
|
|
17
22
|
from kfinance.domains.line_items.line_item_models import LINE_ITEMS
|
|
18
23
|
from kfinance.domains.segments.segment_models import SegmentType
|
|
@@ -27,6 +32,11 @@ logger = logging.getLogger(__name__)
|
|
|
27
32
|
class CompanyFunctionsMetaClass:
|
|
28
33
|
kfinance_api_client: KFinanceApiClient
|
|
29
34
|
|
|
35
|
+
def __init__(self) -> None:
|
|
36
|
+
"""Initialize the CompanyFunctionsMetaClass object"""
|
|
37
|
+
self._company_descriptions: CompanyDescriptions | None = None
|
|
38
|
+
self._company_other_names: CompanyOtherNames | None = None
|
|
39
|
+
|
|
30
40
|
@property
|
|
31
41
|
@abstractmethod
|
|
32
42
|
def company_id(self) -> Any:
|
|
@@ -415,6 +425,51 @@ class CompanyFunctionsMetaClass:
|
|
|
415
425
|
end_quarter=end_quarter,
|
|
416
426
|
)
|
|
417
427
|
|
|
428
|
+
@property
|
|
429
|
+
def summary(self) -> str:
|
|
430
|
+
"""Lazily fetch and return a company's summary"""
|
|
431
|
+
if not self._company_descriptions:
|
|
432
|
+
self._company_descriptions = self.kfinance_api_client.fetch_company_descriptions(
|
|
433
|
+
company_id=self.company_id
|
|
434
|
+
)
|
|
435
|
+
return self._company_descriptions.summary
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
def description(self) -> str:
|
|
439
|
+
"""Lazily fetch and return a company's description"""
|
|
440
|
+
if not self._company_descriptions:
|
|
441
|
+
self._company_descriptions = self.kfinance_api_client.fetch_company_descriptions(
|
|
442
|
+
company_id=self.company_id
|
|
443
|
+
)
|
|
444
|
+
return self._company_descriptions.description
|
|
445
|
+
|
|
446
|
+
@property
|
|
447
|
+
def alternate_names(self) -> list[str]:
|
|
448
|
+
"""Lazily fetch and return a company's alternate names"""
|
|
449
|
+
if not self._company_other_names:
|
|
450
|
+
self._company_other_names = self.kfinance_api_client.fetch_company_other_names(
|
|
451
|
+
company_id=self.company_id
|
|
452
|
+
)
|
|
453
|
+
return self._company_other_names.alternate_names
|
|
454
|
+
|
|
455
|
+
@property
|
|
456
|
+
def historical_names(self) -> list[str]:
|
|
457
|
+
"""Lazily fetch and return a company's historical names"""
|
|
458
|
+
if not self._company_other_names:
|
|
459
|
+
self._company_other_names = self.kfinance_api_client.fetch_company_other_names(
|
|
460
|
+
company_id=self.company_id
|
|
461
|
+
)
|
|
462
|
+
return self._company_other_names.historical_names
|
|
463
|
+
|
|
464
|
+
@property
|
|
465
|
+
def native_names(self) -> list[NativeName]:
|
|
466
|
+
"""Lazily fetch and return a company's native names"""
|
|
467
|
+
if not self._company_other_names:
|
|
468
|
+
self._company_other_names = self.kfinance_api_client.fetch_company_other_names(
|
|
469
|
+
company_id=self.company_id
|
|
470
|
+
)
|
|
471
|
+
return self._company_other_names.native_names
|
|
472
|
+
|
|
418
473
|
def competitors(
|
|
419
474
|
self, competitor_source: CompetitorSource = CompetitorSource.all
|
|
420
475
|
) -> "Companies":
|
|
@@ -13,7 +13,11 @@ from kfinance.domains.business_relationships.business_relationship_models import
|
|
|
13
13
|
BusinessRelationshipType,
|
|
14
14
|
RelationshipResponse,
|
|
15
15
|
)
|
|
16
|
-
from kfinance.domains.companies.company_models import
|
|
16
|
+
from kfinance.domains.companies.company_models import (
|
|
17
|
+
CompanyDescriptions,
|
|
18
|
+
CompanyIdAndName,
|
|
19
|
+
CompanyOtherNames,
|
|
20
|
+
)
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
def build_mock_api_client() -> KFinanceApiClient:
|
|
@@ -383,3 +387,75 @@ class TestFetchCompaniesFromBusinessRelationship:
|
|
|
383
387
|
company_id=SPGI_COMPANY_ID, relationship_type=BusinessRelationshipType.supplier
|
|
384
388
|
)
|
|
385
389
|
assert resp == expected_result
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class TestFetchCompanyDescriptions:
|
|
393
|
+
def test_fetch_company_descriptions(self, requests_mock: Mocker, mock_client: Client) -> None:
|
|
394
|
+
"""
|
|
395
|
+
GIVEN a request to fetch company descriptions
|
|
396
|
+
WHEN the api returns a response
|
|
397
|
+
THEN the response can successfully be parsed into a CompanyDescriptions object.
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
# Truncated from actual http response
|
|
401
|
+
http_resp = {
|
|
402
|
+
"summary": "S&P Global Inc., together... [summary]",
|
|
403
|
+
"description": "S&P Global Inc. (S&P Global), together... [description]",
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
expected_result = CompanyDescriptions(
|
|
407
|
+
summary="S&P Global Inc., together... [summary]",
|
|
408
|
+
description="S&P Global Inc. (S&P Global), together... [description]",
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
requests_mock.get(
|
|
412
|
+
url=f"{mock_client.kfinance_api_client.url_base}info/{SPGI_COMPANY_ID}/descriptions",
|
|
413
|
+
json=http_resp,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
resp = mock_client.kfinance_api_client.fetch_company_descriptions(
|
|
417
|
+
company_id=SPGI_COMPANY_ID
|
|
418
|
+
)
|
|
419
|
+
assert resp == expected_result
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class TestFetchCompanyOtherNames:
|
|
423
|
+
def test_fetch_company_other_names(self, requests_mock: Mocker, mock_client: Client) -> None:
|
|
424
|
+
"""
|
|
425
|
+
GIVEN a request to fetch a company's other names (alternate, historical, and native)
|
|
426
|
+
WHEN the api returns a response
|
|
427
|
+
THEN the response can be successfully parsed into a CompanyOtherNames object
|
|
428
|
+
"""
|
|
429
|
+
alternate_names = ["S&P Global", "S&P Global, Inc.", "S&P"]
|
|
430
|
+
historical_names = [
|
|
431
|
+
"McGraw-Hill Publishing Company, Inc.",
|
|
432
|
+
"McGraw-Hill Book Company",
|
|
433
|
+
"McGraw Hill Financial, Inc.",
|
|
434
|
+
"The McGraw-Hill Companies, Inc.",
|
|
435
|
+
]
|
|
436
|
+
native_names = [
|
|
437
|
+
{"name": "KLab Venture Partners 株式会社", "language": "Japanese"},
|
|
438
|
+
{"name": "株式会社ANOBAKA", "language": "Japanese"},
|
|
439
|
+
{"name": "株式会社KVP", "language": "Japanese"},
|
|
440
|
+
]
|
|
441
|
+
|
|
442
|
+
http_resp = {
|
|
443
|
+
"alternate_names": alternate_names,
|
|
444
|
+
"historical_names": historical_names,
|
|
445
|
+
"native_names": native_names,
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
expected_resp = CompanyOtherNames(
|
|
449
|
+
alternate_names=alternate_names,
|
|
450
|
+
historical_names=historical_names,
|
|
451
|
+
native_names=native_names,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
requests_mock.get(
|
|
455
|
+
url=f"{mock_client.kfinance_api_client.url_base}info/{SPGI_COMPANY_ID}/names",
|
|
456
|
+
json=http_resp,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
resp = mock_client.kfinance_api_client.fetch_company_other_names(company_id=SPGI_COMPANY_ID)
|
|
460
|
+
|
|
461
|
+
assert resp == expected_resp
|
|
@@ -118,3 +118,25 @@ class UnifiedIdTripleResponse(BaseModel):
|
|
|
118
118
|
f"{identifier} is a private company without a trading_item_id."
|
|
119
119
|
)
|
|
120
120
|
self.identifiers_to_id_triples.pop(identifier)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class CompanyDescriptions(BaseModel):
|
|
124
|
+
"""A company summary and description"""
|
|
125
|
+
|
|
126
|
+
summary: str
|
|
127
|
+
description: str
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class NativeName(BaseModel):
|
|
131
|
+
"""A company's native name's name and language"""
|
|
132
|
+
|
|
133
|
+
name: str
|
|
134
|
+
language: str
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class CompanyOtherNames(BaseModel):
|
|
138
|
+
"""A company's alternate, historical, and native names"""
|
|
139
|
+
|
|
140
|
+
alternate_names: list[str]
|
|
141
|
+
historical_names: list[str]
|
|
142
|
+
native_names: list[NativeName]
|
|
@@ -5,6 +5,7 @@ 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 CompanyDescriptions, CompanyOtherNames
|
|
8
9
|
from kfinance.integrations.tool_calling.tool_calling_models import (
|
|
9
10
|
KfinanceTool,
|
|
10
11
|
ToolArgsWithIdentifiers,
|
|
@@ -68,3 +69,130 @@ class GetInfoFromIdentifiers(KfinanceTool):
|
|
|
68
69
|
results=info_responses, errors=list(id_triple_resp.errors.values())
|
|
69
70
|
)
|
|
70
71
|
return resp_model.model_dump(mode="json")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class GetCompanyOtherNamesFromIdentifiersResp(ToolRespWithErrors):
|
|
75
|
+
results: dict[str, CompanyOtherNames]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class GetCompanyOtherNamesFromIdentifiers(KfinanceTool):
|
|
79
|
+
name: str = "get_company_other_names_from_identifiers"
|
|
80
|
+
description: str = dedent("""
|
|
81
|
+
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
|
+
|
|
83
|
+
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
84
|
+
""").strip()
|
|
85
|
+
args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
|
|
86
|
+
accepted_permissions: set[Permission] | None = {Permission.CompanyIntelligencePermission}
|
|
87
|
+
|
|
88
|
+
def _run(
|
|
89
|
+
self,
|
|
90
|
+
identifiers: list[str],
|
|
91
|
+
) -> dict:
|
|
92
|
+
api_client = self.kfinance_client.kfinance_api_client
|
|
93
|
+
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
94
|
+
tasks = [
|
|
95
|
+
Task(
|
|
96
|
+
func=api_client.fetch_company_other_names,
|
|
97
|
+
kwargs=dict(company_id=id_triple.company_id),
|
|
98
|
+
result_key=identifier,
|
|
99
|
+
)
|
|
100
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
|
|
101
|
+
]
|
|
102
|
+
info_responses: dict[str, CompanyOtherNames] = process_tasks_in_thread_pool_executor(
|
|
103
|
+
api_client=api_client, tasks=tasks
|
|
104
|
+
)
|
|
105
|
+
resp_model = GetCompanyOtherNamesFromIdentifiersResp(
|
|
106
|
+
results=info_responses, errors=list(id_triple_resp.errors.values())
|
|
107
|
+
)
|
|
108
|
+
return resp_model.model_dump(mode="json")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class GetCompanySummaryFromIdentifiersResp(ToolRespWithErrors):
|
|
112
|
+
results: dict[str, str]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class GetCompanySummaryFromIdentifiers(KfinanceTool):
|
|
116
|
+
name: str = "get_company_summary_from_identifiers"
|
|
117
|
+
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."
|
|
119
|
+
|
|
120
|
+
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
121
|
+
""").strip()
|
|
122
|
+
args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
|
|
123
|
+
accepted_permissions: set[Permission] | None = {Permission.CompanyIntelligencePermission}
|
|
124
|
+
|
|
125
|
+
def _run(
|
|
126
|
+
self,
|
|
127
|
+
identifiers: list[str],
|
|
128
|
+
) -> dict:
|
|
129
|
+
api_client = self.kfinance_client.kfinance_api_client
|
|
130
|
+
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
131
|
+
|
|
132
|
+
tasks = [
|
|
133
|
+
Task(
|
|
134
|
+
func=api_client.fetch_company_descriptions,
|
|
135
|
+
kwargs=dict(company_id=id_triple.company_id),
|
|
136
|
+
result_key=identifier,
|
|
137
|
+
)
|
|
138
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
|
|
139
|
+
]
|
|
140
|
+
company_description_responses: dict[str, CompanyDescriptions] = (
|
|
141
|
+
process_tasks_in_thread_pool_executor(api_client=api_client, tasks=tasks)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Extract only the summary field
|
|
145
|
+
summary_results = {
|
|
146
|
+
identifier: descriptions.summary
|
|
147
|
+
for identifier, descriptions in company_description_responses.items()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
resp_model = GetCompanySummaryFromIdentifiersResp(
|
|
151
|
+
results=summary_results, errors=list(id_triple_resp.errors.values())
|
|
152
|
+
)
|
|
153
|
+
return resp_model.model_dump(mode="json")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class GetCompanyDescriptionFromIdentifiersResp(ToolRespWithErrors):
|
|
157
|
+
results: dict[str, str]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class GetCompanyDescriptionFromIdentifiers(KfinanceTool):
|
|
161
|
+
name: str = "get_company_description_from_identifiers"
|
|
162
|
+
description: str = dedent("""
|
|
163
|
+
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
|
+
|
|
165
|
+
- When possible, pass multiple identifiers in a single call rather than making multiple calls.
|
|
166
|
+
""").strip()
|
|
167
|
+
args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
|
|
168
|
+
accepted_permissions: set[Permission] | None = {Permission.CompanyIntelligencePermission}
|
|
169
|
+
|
|
170
|
+
def _run(
|
|
171
|
+
self,
|
|
172
|
+
identifiers: list[str],
|
|
173
|
+
) -> dict:
|
|
174
|
+
api_client = self.kfinance_client.kfinance_api_client
|
|
175
|
+
id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
|
|
176
|
+
|
|
177
|
+
tasks = [
|
|
178
|
+
Task(
|
|
179
|
+
func=api_client.fetch_company_descriptions,
|
|
180
|
+
kwargs=dict(company_id=id_triple.company_id),
|
|
181
|
+
result_key=identifier,
|
|
182
|
+
)
|
|
183
|
+
for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
|
|
184
|
+
]
|
|
185
|
+
company_description_responses: dict[str, CompanyDescriptions] = (
|
|
186
|
+
process_tasks_in_thread_pool_executor(api_client=api_client, tasks=tasks)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Extract only the description field
|
|
190
|
+
description_results = {
|
|
191
|
+
identifier: descriptions.description
|
|
192
|
+
for identifier, descriptions in company_description_responses.items()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
resp_model = GetCompanyDescriptionFromIdentifiersResp(
|
|
196
|
+
results=description_results, errors=list(id_triple_resp.errors.values())
|
|
197
|
+
)
|
|
198
|
+
return resp_model.model_dump(mode="json")
|
|
@@ -2,7 +2,12 @@ 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_tools import
|
|
5
|
+
from kfinance.domains.companies.company_tools import (
|
|
6
|
+
GetCompanyDescriptionFromIdentifiers,
|
|
7
|
+
GetCompanyOtherNamesFromIdentifiers,
|
|
8
|
+
GetCompanySummaryFromIdentifiers,
|
|
9
|
+
GetInfoFromIdentifiers,
|
|
10
|
+
)
|
|
6
11
|
from kfinance.integrations.tool_calling.tool_calling_models import ToolArgsWithIdentifiers
|
|
7
12
|
|
|
8
13
|
|
|
@@ -31,3 +36,90 @@ class TestGetInfoFromIdentifiers:
|
|
|
31
36
|
ToolArgsWithIdentifiers(identifiers=["SPGI", "non-existent"]).model_dump(mode="json")
|
|
32
37
|
)
|
|
33
38
|
assert resp == expected_response
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestGetCompanyDescriptions:
|
|
42
|
+
description = "S&P Global Inc. (S&P Global), together... [description]"
|
|
43
|
+
summary = "S&P Global Inc., together... [summary]"
|
|
44
|
+
descriptions_data = {
|
|
45
|
+
"summary": summary,
|
|
46
|
+
"description": description,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
def test_get_company_summary_from_identifier(self, mock_client: Client, requests_mock: Mocker):
|
|
50
|
+
"""
|
|
51
|
+
GIVEN the GetCompanySummaryFromIdentifier tool
|
|
52
|
+
WHEN we request the company summary (short description) for SPGI
|
|
53
|
+
THEN we get back SPGI company's summary (short description)
|
|
54
|
+
"""
|
|
55
|
+
requests_mock.get(
|
|
56
|
+
url=f"https://kfinance.kensho.com/api/v1/info/{SPGI_COMPANY_ID}/descriptions",
|
|
57
|
+
# truncated from the original API response
|
|
58
|
+
json=self.descriptions_data,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
tool = GetCompanySummaryFromIdentifiers(kfinance_client=mock_client)
|
|
62
|
+
args = ToolArgsWithIdentifiers(identifiers=["SPGI"])
|
|
63
|
+
response = tool.run(args.model_dump(mode="json"))
|
|
64
|
+
expected_response = {"results": {"SPGI": self.summary}}
|
|
65
|
+
assert response == expected_response
|
|
66
|
+
|
|
67
|
+
def test_get_company_description_from_identifier(
|
|
68
|
+
self, mock_client: Client, requests_mock: Mocker
|
|
69
|
+
):
|
|
70
|
+
"""
|
|
71
|
+
GIVEN the GetCompanyDescriptionFromIdentifier tool
|
|
72
|
+
WHEN we request the company description for SPGI
|
|
73
|
+
THEN we get back SPGI company's description
|
|
74
|
+
"""
|
|
75
|
+
requests_mock.get(
|
|
76
|
+
url=f"https://kfinance.kensho.com/api/v1/info/{SPGI_COMPANY_ID}/descriptions",
|
|
77
|
+
# truncated from the original API response
|
|
78
|
+
json=self.descriptions_data,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
tool = GetCompanyDescriptionFromIdentifiers(kfinance_client=mock_client)
|
|
82
|
+
args = ToolArgsWithIdentifiers(identifiers=["SPGI"])
|
|
83
|
+
response = tool.run(args.model_dump(mode="json"))
|
|
84
|
+
expected_response = {"results": {"SPGI": self.description}}
|
|
85
|
+
assert response == expected_response
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class TestGetCompanyOtherNames:
|
|
89
|
+
alternate_names = ["S&P Global", "S&P Global, Inc.", "S&P"]
|
|
90
|
+
historical_names = [
|
|
91
|
+
"McGraw-Hill Publishing Company, Inc.",
|
|
92
|
+
"McGraw-Hill Book Company",
|
|
93
|
+
"McGraw Hill Financial, Inc.",
|
|
94
|
+
"The McGraw-Hill Companies, Inc.",
|
|
95
|
+
]
|
|
96
|
+
native_names = [
|
|
97
|
+
{"name": "KLab Venture Partners 株式会社", "language": "Japanese"},
|
|
98
|
+
{"name": "株式会社ANOBAKA", "language": "Japanese"},
|
|
99
|
+
{"name": "株式会社KVP", "language": "Japanese"},
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
company_other_names_info = {
|
|
103
|
+
"alternate_names": alternate_names,
|
|
104
|
+
"historical_names": historical_names,
|
|
105
|
+
"native_names": native_names,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
def test_get_company_other_names_from_identifier(
|
|
109
|
+
self, mock_client: Client, requests_mock: Mocker
|
|
110
|
+
):
|
|
111
|
+
"""
|
|
112
|
+
GIVEN the GetCompanyOtherNamesFromIdentifier tool
|
|
113
|
+
WHEN we request the other names for SPGI
|
|
114
|
+
THEN we get back SPGI's other names
|
|
115
|
+
"""
|
|
116
|
+
requests_mock.get(
|
|
117
|
+
url=f"https://kfinance.kensho.com/api/v1/info/{SPGI_COMPANY_ID}/names",
|
|
118
|
+
json=self.company_other_names_info,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
tool = GetCompanyOtherNamesFromIdentifiers(kfinance_client=mock_client)
|
|
122
|
+
args = ToolArgsWithIdentifiers(identifiers=["SPGI"])
|
|
123
|
+
response = tool.run(args.model_dump(mode="json"))
|
|
124
|
+
expected_response = {"results": {"SPGI": self.company_other_names_info}}
|
|
125
|
+
assert response == expected_response
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
BASE_PROMPT = f"""
|
|
2
|
+
You are an LLM designed to help financial analysts. Use the supplied tools to assist the user.
|
|
3
|
+
CRITICAL RULES FOR TOOL USAGE
|
|
3
4
|
|
|
5
|
+
Time Handling:
|
|
6
|
+
- Always select the most recent complete period when the user does not specify a time.
|
|
7
|
+
- Use the get_latest function to determine the latest annual year, latest completed quarter, and current date.
|
|
8
|
+
- For annual data, use the latest completed year. For quarterly data, use the latest completed quarter and year.
|
|
9
|
+
- If the user specifies a time period (year, quarter, or date range), use it exactly as provided.
|
|
10
|
+
- For relative time references (such as "3 quarters ago"), always use get_n_quarters_ago to resolve the correct year and quarter.
|
|
11
|
+
- For price or history tools, if the user does not specify a date range, use the most recent period as determined by get_latest.
|
|
12
|
+
- "Last year" or "last quarter" refers to the previous completed period from the current date.
|
|
13
|
+
- For quarterly data requests without specific quarters, assume the most recent completed quarter.
|
|
4
14
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
15
|
+
Tool Selection:
|
|
16
|
+
- Use get_latest before any other tool when dates are ambiguous, unspecified, or when you need to determine the most recent period.
|
|
17
|
+
- Use get_n_quarters_ago for relative quarter references such as "3 quarters ago".
|
|
18
|
+
- Always make tool calls when financial data is requested—never skip them.
|
|
19
|
+
- For identifier resolution, use the exact identifiers provided by the user. Do not add or modify suffixes unless explicitly required.
|
|
8
20
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- If the tools do not respond with data that answers the question, then respond by saying that
|
|
14
|
-
you don't have the data available.
|
|
15
|
-
- Keep calling tools until you have the answer or the tool says the data is not available.
|
|
16
|
-
- Label large numbers with "million" or "billion" and currency symbols if appropriate.
|
|
17
|
-
"""
|
|
21
|
+
Identifier Handling:
|
|
22
|
+
- Use the exact identifiers provided by the user. Do not add or modify suffixes such as ".PA" or ".DE" unless the user specifies the exchange or market.
|
|
23
|
+
- Never invent or guess identifiers. Only use those explicitly provided.
|
|
24
|
+
"""
|
kfinance/version.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|