kensho-kfinance 3.2.5__py3-none-any.whl → 3.2.7__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.

Files changed (43) hide show
  1. {kensho_kfinance-3.2.5.dist-info → kensho_kfinance-3.2.7.dist-info}/METADATA +1 -1
  2. {kensho_kfinance-3.2.5.dist-info → kensho_kfinance-3.2.7.dist-info}/RECORD +43 -43
  3. kfinance/CHANGELOG.md +6 -0
  4. kfinance/client/fetch.py +7 -5
  5. kfinance/client/kfinance.py +38 -39
  6. kfinance/client/models/date_and_period_models.py +8 -7
  7. kfinance/client/tests/test_fetch.py +3 -1
  8. kfinance/client/tests/test_objects.py +38 -95
  9. kfinance/domains/business_relationships/business_relationship_tools.py +4 -4
  10. kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +18 -16
  11. kfinance/domains/capitalizations/capitalization_models.py +3 -3
  12. kfinance/domains/capitalizations/capitalization_tools.py +7 -5
  13. kfinance/domains/capitalizations/tests/test_capitalization_tools.py +46 -36
  14. kfinance/domains/companies/company_models.py +8 -2
  15. kfinance/domains/companies/company_tools.py +8 -12
  16. kfinance/domains/companies/tests/test_company_tools.py +21 -9
  17. kfinance/domains/competitors/competitor_tools.py +2 -3
  18. kfinance/domains/competitors/tests/test_competitor_tools.py +22 -19
  19. kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +4 -6
  20. kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +13 -8
  21. kfinance/domains/earnings/earning_tools.py +12 -9
  22. kfinance/domains/earnings/tests/test_earnings_tools.py +52 -43
  23. kfinance/domains/line_items/line_item_tools.py +2 -3
  24. kfinance/domains/line_items/tests/test_line_item_tools.py +20 -23
  25. kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_models.py +46 -2
  26. kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +13 -68
  27. kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +61 -59
  28. kfinance/domains/prices/price_tools.py +4 -7
  29. kfinance/domains/prices/tests/test_price_tools.py +47 -39
  30. kfinance/domains/segments/segment_tools.py +2 -3
  31. kfinance/domains/segments/tests/test_segment_tools.py +16 -11
  32. kfinance/domains/statements/statement_tools.py +2 -3
  33. kfinance/domains/statements/tests/test_statement_tools.py +40 -35
  34. kfinance/integrations/tool_calling/static_tools/get_n_quarters_ago.py +5 -0
  35. kfinance/integrations/tool_calling/static_tools/tests/test_get_lastest.py +13 -10
  36. kfinance/integrations/tool_calling/static_tools/tests/test_get_n_quarters_ago.py +2 -1
  37. kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +8 -2
  38. kfinance/integrations/tool_calling/tool_calling_models.py +11 -5
  39. kfinance/version.py +2 -2
  40. {kensho_kfinance-3.2.5.dist-info → kensho_kfinance-3.2.7.dist-info}/WHEEL +0 -0
  41. {kensho_kfinance-3.2.5.dist-info → kensho_kfinance-3.2.7.dist-info}/licenses/AUTHORS.md +0 -0
  42. {kensho_kfinance-3.2.5.dist-info → kensho_kfinance-3.2.7.dist-info}/licenses/LICENSE +0 -0
  43. {kensho_kfinance-3.2.5.dist-info → kensho_kfinance-3.2.7.dist-info}/top_level.txt +0 -0
@@ -7,10 +7,13 @@ from kfinance.client.kfinance import Client
7
7
  from kfinance.conftest import SPGI_COMPANY_ID
8
8
  from kfinance.domains.earnings.earning_tools import (
9
9
  GetEarningsFromIdentifiers,
10
+ GetEarningsFromIdentifiersResp,
10
11
  GetLatestEarningsFromIdentifiers,
11
12
  GetNextEarningsFromIdentifiers,
13
+ GetNextOrLatestEarningsFromIdentifiersResp,
12
14
  GetTranscriptFromKeyDevId,
13
15
  GetTranscriptFromKeyDevIdArgs,
16
+ GetTranscriptFromKeyDevIdResp,
14
17
  )
15
18
  from kfinance.integrations.tool_calling.tool_calling_models import ToolArgsWithIdentifiers
16
19
 
@@ -43,27 +46,29 @@ class TestGetEarnings:
43
46
  json=self.earnings_response,
44
47
  )
45
48
 
46
- expected_response = {
47
- "results": {
48
- "SPGI": {
49
- "earnings_calls": [
50
- {
51
- "name": "SPGI Q1 2025 Earnings Call",
52
- "key_dev_id": 12346,
53
- "datetime": "2025-04-29T12:30:00Z",
54
- },
55
- {
56
- "name": "SPGI Q4 2024 Earnings Call",
57
- "key_dev_id": 12345,
58
- "datetime": "2025-02-11T13:30:00Z",
59
- },
60
- ]
61
- }
62
- },
63
- "errors": [
64
- "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
65
- ],
66
- }
49
+ expected_response = GetEarningsFromIdentifiersResp.model_validate(
50
+ {
51
+ "results": {
52
+ "SPGI": {
53
+ "earnings": [
54
+ {
55
+ "name": "SPGI Q1 2025 Earnings Call",
56
+ "key_dev_id": 12346,
57
+ "datetime": "2025-04-29T12:30:00Z",
58
+ },
59
+ {
60
+ "name": "SPGI Q4 2024 Earnings Call",
61
+ "key_dev_id": 12345,
62
+ "datetime": "2025-02-11T13:30:00Z",
63
+ },
64
+ ]
65
+ }
66
+ },
67
+ "errors": [
68
+ "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
69
+ ],
70
+ }
71
+ )
67
72
 
68
73
  tool = GetEarningsFromIdentifiers(kfinance_client=mock_client)
69
74
  response = tool.run(
@@ -95,16 +100,18 @@ class TestGetEarnings:
95
100
  json={"earnings": []},
96
101
  )
97
102
 
98
- expected_response = {
99
- "results": {
100
- "SPGI": {
101
- "name": "SPGI Q1 2025 Earnings Call",
102
- "key_dev_id": 12346,
103
- "datetime": "2025-04-29T12:30:00Z",
104
- }
105
- },
106
- "errors": ["No latest earnings available for private_company."],
107
- }
103
+ expected_response = GetNextOrLatestEarningsFromIdentifiersResp.model_validate(
104
+ {
105
+ "results": {
106
+ "SPGI": {
107
+ "name": "SPGI Q1 2025 Earnings Call",
108
+ "key_dev_id": 12346,
109
+ "datetime": "2025-04-29T12:30:00Z",
110
+ }
111
+ },
112
+ "errors": ["No latest earnings available for private_company."],
113
+ }
114
+ )
108
115
 
109
116
  tool = GetLatestEarningsFromIdentifiers(kfinance_client=mock_client)
110
117
  response = tool.run(
@@ -136,16 +143,18 @@ class TestGetEarnings:
136
143
  json={"earnings": []},
137
144
  )
138
145
 
139
- expected_response = {
140
- "results": {
141
- "SPGI": {
142
- "datetime": "2025-04-29T12:30:00Z",
143
- "key_dev_id": 12346,
144
- "name": "SPGI Q1 2025 Earnings Call",
145
- }
146
- },
147
- "errors": ["No next earnings available for private_company."],
148
- }
146
+ expected_response = GetNextOrLatestEarningsFromIdentifiersResp.model_validate(
147
+ {
148
+ "results": {
149
+ "SPGI": {
150
+ "datetime": "2025-04-29T12:30:00Z",
151
+ "key_dev_id": 12346,
152
+ "name": "SPGI Q1 2025 Earnings Call",
153
+ }
154
+ },
155
+ "errors": ["No next earnings available for private_company."],
156
+ }
157
+ )
149
158
 
150
159
  tool = GetNextEarningsFromIdentifiers(kfinance_client=mock_client)
151
160
  response = tool.run(
@@ -181,8 +190,8 @@ class TestGetTranscript:
181
190
  json=transcript_data,
182
191
  )
183
192
 
184
- expected_response = (
185
- "Operator: Good morning, everyone.\n\nCEO: Thank you for joining us today."
193
+ expected_response = GetTranscriptFromKeyDevIdResp(
194
+ transcript="Operator: Good morning, everyone.\n\nCEO: Thank you for joining us today."
186
195
  )
187
196
 
188
197
  tool = GetTranscriptFromKeyDevId(kfinance_client=mock_client)
@@ -63,7 +63,7 @@ class GetFinancialLineItemFromIdentifiers(KfinanceTool):
63
63
  end_year: int | None = None,
64
64
  start_quarter: Literal[1, 2, 3, 4] | None = None,
65
65
  end_quarter: Literal[1, 2, 3, 4] | None = None,
66
- ) -> dict:
66
+ ) -> GetFinancialLineItemFromIdentifiersResp:
67
67
  """Sample response:
68
68
 
69
69
  {
@@ -113,7 +113,6 @@ class GetFinancialLineItemFromIdentifiers(KfinanceTool):
113
113
  most_recent_year_data = line_item_response.line_item[most_recent_year]
114
114
  line_item_response.line_item = {most_recent_year: most_recent_year_data}
115
115
 
116
- output_model = GetFinancialLineItemFromIdentifiersResp(
116
+ return GetFinancialLineItemFromIdentifiersResp(
117
117
  results=line_item_responses, errors=list(id_triple_resp.errors.values())
118
118
  )
119
- return output_model.model_dump(mode="json")
@@ -1,12 +1,16 @@
1
+ from decimal import Decimal
2
+
1
3
  from langchain_core.utils.function_calling import convert_to_openai_tool
2
4
  from requests_mock import Mocker
3
5
 
4
6
  from kfinance.client.kfinance import Client
5
7
  from kfinance.conftest import SPGI_COMPANY_ID
6
8
  from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
9
+ from kfinance.domains.line_items.line_item_models import LineItemResponse
7
10
  from kfinance.domains.line_items.line_item_tools import (
8
11
  GetFinancialLineItemFromIdentifiers,
9
12
  GetFinancialLineItemFromIdentifiersArgs,
13
+ GetFinancialLineItemFromIdentifiersResp,
10
14
  )
11
15
 
12
16
 
@@ -28,27 +32,20 @@ class TestGetFinancialLineItemFromCompanyIds:
28
32
  THEN we get back the SPGI revenue and an error for the non-existent company
29
33
  """
30
34
 
31
- expected_response = {
32
- "SPGI": {
33
- "2022": {"revenue": 11181000000.0},
34
- "2023": {"revenue": 12497000000.0},
35
- "2024": {"revenue": 14208000000.0},
36
- }
37
- }
38
- expected_response = {
39
- "results": {
40
- "SPGI": {
41
- "line_item": {
42
- "2022": "11181000000.000000",
43
- "2023": "12497000000.000000",
44
- "2024": "14208000000.000000",
35
+ expected_response = GetFinancialLineItemFromIdentifiersResp(
36
+ results={
37
+ "SPGI": LineItemResponse(
38
+ line_item={
39
+ "2022": Decimal(11181000000),
40
+ "2023": Decimal(12497000000),
41
+ "2024": Decimal(14208000000),
45
42
  }
46
- }
43
+ )
47
44
  },
48
- "errors": [
45
+ errors=[
49
46
  "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
50
47
  ],
51
- }
48
+ )
52
49
 
53
50
  requests_mock.get(
54
51
  url=f"https://kfinance.kensho.com/api/v1/line_item/{SPGI_COMPANY_ID}/revenue/none/none/none/none/none",
@@ -70,12 +67,12 @@ class TestGetFinancialLineItemFromCompanyIds:
70
67
  """
71
68
 
72
69
  company_ids = [1, 2]
73
- expected_response = {
74
- "results": {
75
- "C_1": {"line_item": {"2024": "14208000000.000000"}},
76
- "C_2": {"line_item": {"2024": "14208000000.000000"}},
77
- }
78
- }
70
+
71
+ line_item_resp = LineItemResponse(line_item={"2024": Decimal(14208000000)})
72
+ expected_response = GetFinancialLineItemFromIdentifiersResp(
73
+ results={"C_1": line_item_resp, "C_2": line_item_resp},
74
+ )
75
+
79
76
  for company_id in company_ids:
80
77
  requests_mock.get(
81
78
  url=f"https://kfinance.kensho.com/api/v1/line_item/{company_id}/revenue/none/none/none/none/none",
@@ -1,6 +1,9 @@
1
1
  from datetime import date
2
+ from decimal import Decimal
2
3
 
3
- from pydantic import BaseModel
4
+ from pydantic import BaseModel, field_serializer
5
+
6
+ from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX, CompanyIdAndName
4
7
 
5
8
 
6
9
  class MergerSummary(BaseModel):
@@ -16,6 +19,47 @@ class MergersResp(BaseModel):
16
19
 
17
20
 
18
21
  class AdvisorResp(BaseModel):
19
- advisor_company_id: str
22
+ advisor_company_id: int
20
23
  advisor_company_name: str
21
24
  advisor_type_name: str | None
25
+
26
+ @field_serializer("advisor_company_id")
27
+ def serialize_with_prefix(self, company_id: int) -> str:
28
+ """Serialize the advisor_company_id with a prefix ("C_<company_id>").
29
+
30
+ Including the prefix allows us to distinguish tickers and company_ids.
31
+ """
32
+ return f"{COMPANY_ID_PREFIX}{company_id}"
33
+
34
+
35
+ class MergerTimelineElement(BaseModel):
36
+ status: str
37
+ date: date
38
+
39
+
40
+ class MergerParticipants(BaseModel):
41
+ target: CompanyIdAndName
42
+ buyers: list[CompanyIdAndName]
43
+ sellers: list[CompanyIdAndName]
44
+
45
+
46
+ class MergerConsiderationDetail(BaseModel):
47
+ scenario: str | None = None
48
+ subtype: str | None = None
49
+ cash_or_cash_equivalent_per_target_share_unit: Decimal | None = None
50
+ number_of_target_shares_sought: Decimal | None = None
51
+ current_calculated_gross_value_of_consideration: Decimal | None = None
52
+
53
+
54
+ class MergerConsideration(BaseModel):
55
+ currency_name: str | None = None
56
+ current_calculated_gross_total_transaction_value: Decimal | None = None
57
+ current_calculated_implied_equity_value: Decimal | None = None
58
+ current_calculated_implied_enterprise_value: Decimal | None = None
59
+ details: list[MergerConsiderationDetail]
60
+
61
+
62
+ class MergerInfo(BaseModel):
63
+ timeline: list[MergerTimelineElement]
64
+ participants: MergerParticipants
65
+ consideration: MergerConsideration
@@ -4,11 +4,11 @@ from typing import Type
4
4
  from pydantic import BaseModel, Field
5
5
 
6
6
  from kfinance.client.batch_request_handling import Task, process_tasks_in_thread_pool_executor
7
- from kfinance.client.kfinance import Company, MergerOrAcquisition, ParticipantInMerger
7
+ from kfinance.client.kfinance import Company, ParticipantInMerger
8
8
  from kfinance.client.permission_models import Permission
9
- from kfinance.domains.companies.company_models import prefix_company_id
10
9
  from kfinance.domains.mergers_and_acquisitions.merger_and_acquisition_models import (
11
10
  AdvisorResp,
11
+ MergerInfo,
12
12
  MergersResp,
13
13
  )
14
14
  from kfinance.integrations.tool_calling.tool_calling_models import (
@@ -31,7 +31,7 @@ class GetMergersFromIdentifiers(KfinanceTool):
31
31
  args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
32
32
  accepted_permissions: set[Permission] | None = {Permission.MergersPermission}
33
33
 
34
- def _run(self, identifiers: list[str]) -> dict:
34
+ def _run(self, identifiers: list[str]) -> GetMergersFromIdentifiersResp:
35
35
  """Sample Response:
36
36
 
37
37
  {
@@ -79,10 +79,9 @@ class GetMergersFromIdentifiers(KfinanceTool):
79
79
  merger_responses: dict[str, MergersResp] = process_tasks_in_thread_pool_executor(
80
80
  api_client=api_client, tasks=tasks
81
81
  )
82
- output_model = GetMergersFromIdentifiersResp(
82
+ return GetMergersFromIdentifiersResp(
83
83
  results=merger_responses, errors=list(id_triple_resp.errors.values())
84
84
  )
85
- return output_model.model_dump(mode="json")
86
85
 
87
86
 
88
87
  class GetMergerInfoFromTransactionIdArgs(BaseModel):
@@ -97,64 +96,10 @@ class GetMergerInfoFromTransactionId(KfinanceTool):
97
96
  args_schema: Type[BaseModel] = GetMergerInfoFromTransactionIdArgs
98
97
  accepted_permissions: set[Permission] | None = {Permission.MergersPermission}
99
98
 
100
- def _run(self, transaction_id: int) -> dict:
101
- merger_or_acquisition = MergerOrAcquisition(
102
- kfinance_api_client=self.kfinance_client.kfinance_api_client,
103
- transaction_id=transaction_id,
104
- merger_title=None,
105
- closed_date=None,
99
+ def _run(self, transaction_id: int) -> MergerInfo:
100
+ return self.kfinance_client.kfinance_api_client.fetch_merger_info(
101
+ transaction_id=transaction_id
106
102
  )
107
- merger_timeline = merger_or_acquisition.get_timeline
108
- merger_participants = merger_or_acquisition.get_participants
109
- merger_consideration = merger_or_acquisition.get_consideration
110
-
111
- return {
112
- "timeline": [
113
- {"status": timeline["status"], "date": timeline["date"].strftime("%Y-%m-%d")}
114
- for timeline in merger_timeline.to_dict(orient="records")
115
- ]
116
- if merger_timeline is not None
117
- else None,
118
- "participants": {
119
- "target": {
120
- "company_id": prefix_company_id(
121
- merger_participants["target"].company.company_id
122
- ),
123
- "company_name": merger_participants["target"].company.name,
124
- },
125
- "buyers": [
126
- {
127
- "company_id": prefix_company_id(buyer.company.company_id),
128
- "company_name": buyer.company.name,
129
- }
130
- for buyer in merger_participants["buyers"]
131
- ],
132
- "sellers": [
133
- {
134
- "company_id": prefix_company_id(seller.company.company_id),
135
- "company_name": seller.company.name,
136
- }
137
- for seller in merger_participants["sellers"]
138
- ],
139
- }
140
- if merger_participants is not None
141
- else None,
142
- "consideration": {
143
- "currency_name": merger_consideration["currency_name"],
144
- "current_calculated_gross_total_transaction_value": merger_consideration[
145
- "current_calculated_gross_total_transaction_value"
146
- ],
147
- "current_calculated_implied_equity_value": merger_consideration[
148
- "current_calculated_implied_equity_value"
149
- ],
150
- "current_calculated_implied_enterprise_value": merger_consideration[
151
- "current_calculated_implied_enterprise_value"
152
- ],
153
- "details": merger_consideration["details"].to_dict(orient="records"),
154
- }
155
- if merger_consideration is not None
156
- else None,
157
- }
158
103
 
159
104
 
160
105
  class GetAdvisorsForCompanyInTransactionFromIdentifierArgs(ToolArgsWithIdentifier):
@@ -173,15 +118,16 @@ class GetAdvisorsForCompanyInTransactionFromIdentifier(KfinanceTool):
173
118
  args_schema: Type[BaseModel] = GetAdvisorsForCompanyInTransactionFromIdentifierArgs
174
119
  accepted_permissions: set[Permission] | None = {Permission.MergersPermission}
175
120
 
176
- def _run(self, identifier: str, transaction_id: int) -> dict:
121
+ def _run(
122
+ self, identifier: str, transaction_id: int
123
+ ) -> GetAdvisorsForCompanyInTransactionFromIdentifierResp:
177
124
  api_client = self.kfinance_client.kfinance_api_client
178
125
  id_triple_resp = api_client.unified_fetch_id_triples(identifiers=[identifier])
179
126
  # If the identifier cannot be resolved, return the associated error.
180
127
  if id_triple_resp.errors:
181
- output_model = GetAdvisorsForCompanyInTransactionFromIdentifierResp(
128
+ return GetAdvisorsForCompanyInTransactionFromIdentifierResp(
182
129
  results=[], errors=list(id_triple_resp.errors.values())
183
130
  )
184
- return output_model.model_dump(mode="json")
185
131
 
186
132
  id_triple = id_triple_resp.identifiers_to_id_triples[identifier]
187
133
 
@@ -201,13 +147,12 @@ class GetAdvisorsForCompanyInTransactionFromIdentifier(KfinanceTool):
201
147
  for advisor in advisors:
202
148
  advisors_response.append(
203
149
  AdvisorResp(
204
- advisor_company_id=prefix_company_id(advisor.company.company_id),
150
+ advisor_company_id=advisor.company.company_id,
205
151
  advisor_company_name=advisor.company.name,
206
152
  advisor_type_name=advisor.advisor_type_name,
207
153
  )
208
154
  )
209
155
 
210
- output_model = GetAdvisorsForCompanyInTransactionFromIdentifierResp(
156
+ return GetAdvisorsForCompanyInTransactionFromIdentifierResp(
211
157
  results=advisors_response, errors=list(id_triple_resp.errors.values())
212
158
  )
213
- return output_model.model_dump(mode="json")
@@ -8,13 +8,18 @@ from kfinance.client.tests.test_objects import (
8
8
  ordered,
9
9
  )
10
10
  from kfinance.conftest import SPGI_COMPANY_ID
11
- from kfinance.domains.companies.company_models import COMPANY_ID_PREFIX
11
+ from kfinance.domains.mergers_and_acquisitions.merger_and_acquisition_models import (
12
+ AdvisorResp,
13
+ MergerInfo,
14
+ )
12
15
  from kfinance.domains.mergers_and_acquisitions.merger_and_acquisition_tools import (
13
16
  GetAdvisorsForCompanyInTransactionFromIdentifier,
14
17
  GetAdvisorsForCompanyInTransactionFromIdentifierArgs,
18
+ GetAdvisorsForCompanyInTransactionFromIdentifierResp,
15
19
  GetMergerInfoFromTransactionId,
16
20
  GetMergerInfoFromTransactionIdArgs,
17
21
  GetMergersFromIdentifiers,
22
+ GetMergersFromIdentifiersResp,
18
23
  )
19
24
  from kfinance.integrations.tool_calling.tool_calling_models import ToolArgsWithIdentifiers
20
25
 
@@ -27,12 +32,14 @@ class TestGetMergersFromIdentifiers:
27
32
  THEN we get back the SPGI mergers and an error for the non-existent company"""
28
33
 
29
34
  merger_data = MERGERS_RESP.model_dump(mode="json")
30
- expected_response = {
31
- "results": {"SPGI": merger_data},
32
- "errors": [
33
- "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
34
- ],
35
- }
35
+ expected_response = GetMergersFromIdentifiersResp.model_validate(
36
+ {
37
+ "results": {"SPGI": merger_data},
38
+ "errors": [
39
+ "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
40
+ ],
41
+ }
42
+ )
36
43
  requests_mock.get(
37
44
  url=f"https://kfinance.kensho.com/api/v1/mergers/{SPGI_COMPANY_ID}", json=merger_data
38
45
  )
@@ -51,13 +58,21 @@ class TestGetCompaniesAdvisingCompanyInTransactionFromIdentifier:
51
58
  "advisor_company_name": "Kensho Technologies, Inc.",
52
59
  "advisor_type_name": "Professional Mongo Enjoyer",
53
60
  }
54
- api_response = {"advisors": [deepcopy(advisor_data)]}
55
- expected_response = {"results": [deepcopy(advisor_data)]}
56
- expected_response["results"][0]["advisor_company_id"] = f"{COMPANY_ID_PREFIX}251994106"
61
+ expected_response = GetAdvisorsForCompanyInTransactionFromIdentifierResp(
62
+ results=[
63
+ AdvisorResp(
64
+ advisor_company_id=251994106,
65
+ advisor_company_name="Kensho Technologies, Inc.",
66
+ advisor_type_name="Professional Mongo Enjoyer",
67
+ )
68
+ ],
69
+ errors=[],
70
+ )
71
+
57
72
  transaction_id = 554979212
58
73
  requests_mock.get(
59
74
  url=f"https://kfinance.kensho.com/api/v1/merger/info/{transaction_id}/advisors/{SPGI_COMPANY_ID}",
60
- json=api_response,
75
+ json={"advisors": [deepcopy(advisor_data)]},
61
76
  )
62
77
  tool = GetAdvisorsForCompanyInTransactionFromIdentifier(kfinance_client=mock_client)
63
78
  args = GetAdvisorsForCompanyInTransactionFromIdentifierArgs(
@@ -67,14 +82,14 @@ class TestGetCompaniesAdvisingCompanyInTransactionFromIdentifier:
67
82
  assert response == expected_response
68
83
 
69
84
  def test_get_companies_advising_company_in_transaction_from_bad_identifier(
70
- self, requests_mock: Mocker, mock_client: Client
85
+ self, mock_client: Client
71
86
  ):
72
- expected_response = {
73
- "results": [],
74
- "errors": [
87
+ expected_response = GetAdvisorsForCompanyInTransactionFromIdentifierResp(
88
+ results=[],
89
+ errors=[
75
90
  "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
76
91
  ],
77
- }
92
+ )
78
93
  transaction_id = 554979212
79
94
  tool = GetAdvisorsForCompanyInTransactionFromIdentifier(kfinance_client=mock_client)
80
95
  args = GetAdvisorsForCompanyInTransactionFromIdentifierArgs(
@@ -86,30 +101,11 @@ class TestGetCompaniesAdvisingCompanyInTransactionFromIdentifier:
86
101
 
87
102
  class TestGetMergerInfoFromTransactionId:
88
103
  def test_get_merger_info_from_transaction_id(self, requests_mock: Mocker, mock_client: Client):
89
- response_base = {
90
- "timeline": [
91
- {"status": "Announced", "date": "2000-09-12"},
92
- {"status": "Closed", "date": "2000-09-12"},
93
- ],
94
- "participants": {},
95
- "consideration": {
96
- "currency_name": "US Dollar",
97
- "current_calculated_gross_total_transaction_value": "51609375.000000",
98
- "current_calculated_implied_equity_value": "51609375.000000",
99
- "current_calculated_implied_enterprise_value": "51609375.000000",
100
- "details": [
101
- {
102
- "scenario": "Stock Lump Sum",
103
- "subtype": "Common Equity",
104
- "cash_or_cash_equivalent_per_target_share_unit": None,
105
- "number_of_target_shares_sought": "1000000.000000",
106
- "current_calculated_gross_value_of_consideration": "51609375.000000",
107
- }
108
- ],
109
- },
110
- }
111
-
112
- participants_api_response = {
104
+ timeline_resp = [
105
+ {"status": "Announced", "date": "2000-09-12"},
106
+ {"status": "Closed", "date": "2000-09-12"},
107
+ ]
108
+ participants_resp = {
113
109
  "target": {"company_id": 31696, "company_name": "MongoMusic, Inc."},
114
110
  "buyers": [{"company_id": 21835, "company_name": "Microsoft Corporation"}],
115
111
  "sellers": [
@@ -117,33 +113,39 @@ class TestGetMergerInfoFromTransactionId:
117
113
  {"company_id": 20087, "company_name": "Draper Richards, L.P."},
118
114
  ],
119
115
  }
120
- api_response = deepcopy(response_base)
121
- api_response["participants"] = participants_api_response
122
116
 
123
- # Returned company IDs should be prefixed
124
- expected_participants_tool_response = {
125
- "target": {
126
- "company_id": f"{COMPANY_ID_PREFIX}31696",
127
- "company_name": "MongoMusic, Inc.",
128
- },
129
- "buyers": [
130
- {"company_id": f"{COMPANY_ID_PREFIX}21835", "company_name": "Microsoft Corporation"}
131
- ],
132
- "sellers": [
133
- {"company_id": f"{COMPANY_ID_PREFIX}18805", "company_name": "Angel Investors L.P."},
117
+ consideration_resp = {
118
+ "currency_name": "US Dollar",
119
+ "current_calculated_gross_total_transaction_value": "51609375.000000",
120
+ "current_calculated_implied_equity_value": "51609375.000000",
121
+ "current_calculated_implied_enterprise_value": "51609375.000000",
122
+ "details": [
134
123
  {
135
- "company_id": f"{COMPANY_ID_PREFIX}20087",
136
- "company_name": "Draper Richards, L.P.",
137
- },
124
+ "scenario": "Stock Lump Sum",
125
+ "subtype": "Common Equity",
126
+ "cash_or_cash_equivalent_per_target_share_unit": None,
127
+ "number_of_target_shares_sought": "1000000.000000",
128
+ "current_calculated_gross_value_of_consideration": "51609375.000000",
129
+ }
138
130
  ],
139
131
  }
140
- expected_response = deepcopy(response_base)
141
- expected_response["participants"] = expected_participants_tool_response
132
+
133
+ expected_response = MergerInfo.model_validate(
134
+ {
135
+ "timeline": timeline_resp,
136
+ "participants": participants_resp,
137
+ "consideration": consideration_resp,
138
+ }
139
+ )
142
140
 
143
141
  transaction_id = 517414
144
142
  requests_mock.get(
145
143
  url=f"https://kfinance.kensho.com/api/v1/merger/info/{transaction_id}",
146
- json=api_response,
144
+ json={
145
+ "timeline": timeline_resp,
146
+ "participants": participants_resp,
147
+ "consideration": consideration_resp,
148
+ },
147
149
  )
148
150
  tool = GetMergerInfoFromTransactionId(kfinance_client=mock_client)
149
151
  args = GetMergerInfoFromTransactionIdArgs(transaction_id=transaction_id)
@@ -60,7 +60,7 @@ class GetPricesFromIdentifiers(KfinanceTool):
60
60
  end_date: date | None = None,
61
61
  periodicity: Periodicity = Periodicity.day,
62
62
  adjusted: bool = True,
63
- ) -> dict:
63
+ ) -> GetPricesFromIdentifiersResp:
64
64
  """Sample Response:
65
65
 
66
66
  {
@@ -116,10 +116,9 @@ class GetPricesFromIdentifiers(KfinanceTool):
116
116
  for price_response in price_responses.values():
117
117
  price_response.prices = price_response.prices[-1:]
118
118
 
119
- output_model = GetPricesFromIdentifiersResp(
119
+ return GetPricesFromIdentifiersResp(
120
120
  results=price_responses, errors=list(id_triple_resp.errors.values())
121
121
  )
122
- return output_model.model_dump(mode="json")
123
122
 
124
123
 
125
124
  class GetHistoryMetadataFromIdentifiersResp(ToolRespWithErrors):
@@ -136,7 +135,7 @@ class GetHistoryMetadataFromIdentifiers(KfinanceTool):
136
135
  args_schema: Type[BaseModel] = ToolArgsWithIdentifiers
137
136
  accepted_permissions: set[Permission] | None = None
138
137
 
139
- def _run(self, identifiers: list[str]) -> dict:
138
+ def _run(self, identifiers: list[str]) -> GetHistoryMetadataFromIdentifiersResp:
140
139
  """Sample response:
141
140
 
142
141
  {
@@ -169,8 +168,6 @@ class GetHistoryMetadataFromIdentifiers(KfinanceTool):
169
168
  history_metadata_responses: dict[str, HistoryMetadataResp] = (
170
169
  process_tasks_in_thread_pool_executor(api_client=api_client, tasks=tasks)
171
170
  )
172
- output_model = GetHistoryMetadataFromIdentifiersResp(
171
+ return GetHistoryMetadataFromIdentifiersResp(
173
172
  results=history_metadata_responses, errors=list(id_triple_resp.errors.values())
174
173
  )
175
-
176
- return output_model.model_dump(mode="json")