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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kensho-kfinance
3
- Version: 2.4.1
3
+ Version: 2.4.3
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,28 +1,28 @@
1
- kensho_kfinance-2.4.1.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
2
- kensho_kfinance-2.4.1.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
3
- kfinance/CHANGELOG.md,sha256=a6wxwkKHkXVFy50em1v27v8zB6kwU9TWdsZFe5wSYTA,1487
1
+ kensho_kfinance-2.4.3.dist-info/licenses/AUTHORS.md,sha256=0h9ClbI0pu1oKj1M28ROUsaxrbZg-6ukQGl6X4y9noI,68
2
+ kensho_kfinance-2.4.3.dist-info/licenses/LICENSE,sha256=bsY4blvSgq6o0FMQ3RXa2NCgco--nHCCchLXzxr6kms,83
3
+ kfinance/CHANGELOG.md,sha256=2EpxIzLz1uuod_1rJk6xxGLW_Y2-kupUSWJsREi4esI,1578
4
4
  kfinance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  kfinance/batch_request_handling.py,sha256=p6p_G4_BL06GgeKlh7P1k9CUqOMahWCLEw1NoBwbLvU,5698
6
- kfinance/constants.py,sha256=UuFzqL253-2tRQfma785K9tfaZGv-o821tO2tVLwc5Q,48813
7
- kfinance/fetch.py,sha256=yaCih8PAkOhVHb3tvmBW0x2w4QmXJiyUATu6Yx-xzP4,23851
6
+ kfinance/constants.py,sha256=vVE_WlLNUKBn85eL8QBnk_zUSNDBRLcWutaQy-5wvGA,48768
7
+ kfinance/fetch.py,sha256=yLqKMYYP5MwHhRZBuY_ihudHeSpNvzA8u1meYrEYX90,23923
8
8
  kfinance/kfinance.py,sha256=9lcarUW4fLHJA_U7_1ihCMbNJWs0AztvKf_XEd0qupk,59889
9
- kfinance/mcp.py,sha256=57OoTrxo5x_v7u1s3WFQNG0LySXyLEGc6CKSVpJLJVw,3717
9
+ kfinance/mcp.py,sha256=MbktclVfBOEwfe-eR7kPaTXopMJmn_8RMlf4Jx5CXKU,3689
10
10
  kfinance/meta_classes.py,sha256=3V0nSXDDoake5o7kXnrqXuqNIiwI75KR4IYxFqSPhTE,20736
11
11
  kfinance/prompt.py,sha256=PtVB8c_FcSlVdyGgByAnIFGzuUuBaEjciCqnBJl1hSQ,25133
12
12
  kfinance/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  kfinance/pydantic_models.py,sha256=avpbPqwrAyLqsCbrmFpK_B8_fj1nPlBHrnPxRcBaSkE,774
14
14
  kfinance/server_thread.py,sha256=jUnt1YGoYDkqqz1MbCwd44zJs1T_Z2BCgvj75bdtLgA,2574
15
- kfinance/version.py,sha256=H3hbvip-BJTNbNG0ITRXctaII3Vc2949yckZEGuAGg8,511
15
+ kfinance/version.py,sha256=xLAVxNld0iPK6tqrofzfozu9ZHg4aby5Rg0HcLuAYIU,511
16
16
  kfinance/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  kfinance/tests/conftest.py,sha256=voB-w8P_6L3Nel3rdgylXKe5WWaS1q7nCFt1O04uqoY,948
18
+ kfinance/tests/scratch.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
19
  kfinance/tests/test_batch_requests.py,sha256=uXJF2IcRdyBm5SthwIUHMKtkGZ21MY84pg_k1JeSNOY,11430
19
20
  kfinance/tests/test_client.py,sha256=O7icZCSDhlQ9WGhzoXlpiSvbuA-mQNJHBYVsilyP_dE,2209
20
21
  kfinance/tests/test_example_notebook.py,sha256=XHwDKw2avyMonTmi3snCcFWNfZhEJOkpBGOZNrMLrhk,6470
21
22
  kfinance/tests/test_fetch.py,sha256=nfnz_ZxE-W3KMzpDaRClX55fQJrRjwLTha-rHTallmE,16713
22
23
  kfinance/tests/test_group_objects.py,sha256=SoMEZmkG4RYdgWOAwxLHHtzIQho92KM01YbQXPUg578,1689
23
- kfinance/tests/test_mcp.py,sha256=-pmeZ0PDp8GmXWcrWmaNNqMe7OVZONZLrjA0Y4b82os,594
24
24
  kfinance/tests/test_objects.py,sha256=0nDmCFrVcfI8VBo1Ph3YqXNo3uPLsSUiQkjEEHsax1M,30416
25
- kfinance/tests/test_tools.py,sha256=EWWWyPlGGES8Cn43_VaDAT7Vdp1nlIETE5dtOrP163o,24447
25
+ kfinance/tests/test_tools.py,sha256=GMyReX2GBy_fozMZvA_yvZ6_bl1EPvJ2Eza9G5f4CEM,25753
26
26
  kfinance/tool_calling/README.md,sha256=omJq7Us6r4U45QB7hRpLjRJ5BMalCkZkh4uXBjTbJXc,2022
27
27
  kfinance/tool_calling/__init__.py,sha256=V5BVcJkLT1zC0QKZIQjLb-cZiQYV4T9Egj64uH807WE,2215
28
28
  kfinance/tool_calling/get_business_relationship_from_identifier.py,sha256=CipXvyqEjPm6BXYP0CA9Kp1BIyiIEm7abp85x1zXRV4,1472
@@ -30,8 +30,8 @@ kfinance/tool_calling/get_capitalization_from_identifier.py,sha256=TdWdJDeI-jSL-
30
30
  kfinance/tool_calling/get_cusip_from_ticker.py,sha256=houhGCYXoSzaaTtCvOBf3pPsYiSbcV1Ej5nAyGuMWcU,644
31
31
  kfinance/tool_calling/get_earnings.py,sha256=7xavYvqwq4fO0vpks25eDM87bCYwZxEQSwgT0I8jAok,1165
32
32
  kfinance/tool_calling/get_earnings_call_datetimes_from_identifier.py,sha256=Zp7gHP-z2ePu484wWx4xw89hl0z7NLcG3qliJT-6rUo,782
33
- kfinance/tool_calling/get_financial_line_item_from_identifier.py,sha256=bMK-GUMz_wmJ-7iFS72sM8AvvUvR83JYRWzYtr9Fofw,2153
34
- kfinance/tool_calling/get_financial_statement_from_identifier.py,sha256=9ouzC43skA78uI3dh3bvtM86db5RGxuWQAAJ24y-kBM,1906
33
+ kfinance/tool_calling/get_financial_line_item_from_identifier.py,sha256=TgZCHOKwMVUc0c7k5RGN5FWfXEM9cmJXTrDWiGudad8,2153
34
+ kfinance/tool_calling/get_financial_statement_from_identifier.py,sha256=QM3DfctPq8gMJzRZl508Qc184MWq_o6gwGNZEwbYopg,1906
35
35
  kfinance/tool_calling/get_history_metadata_from_identifier.py,sha256=kKIInfRC1Plf-Wnx9cmaJ46RRABm6zn3F5_8qAdqrBg,728
36
36
  kfinance/tool_calling/get_info_from_identifier.py,sha256=PRhSYpYs_iUcIVFGYCMN_7QJyhewua7c7pqngPIp-qg,766
37
37
  kfinance/tool_calling/get_isin_from_ticker.py,sha256=2fJBcA-rNGbVOQmQ7qJEYxqejQwJ6nyWOBSFlzxG7dY,638
@@ -40,12 +40,12 @@ kfinance/tool_calling/get_latest_earnings.py,sha256=pQdExGdztGY3pHcak0bd6ULNf_RO
40
40
  kfinance/tool_calling/get_n_quarters_ago.py,sha256=A0ilwPKUqU0YYQSz3gNsVF0Jy4YttXrSaDhYj7y8GHA,713
41
41
  kfinance/tool_calling/get_next_earnings.py,sha256=knzQw-m-hscCvTuDUXG9v_dObKJBGn5BbDZWGKHKQcw,1097
42
42
  kfinance/tool_calling/get_prices_from_identifier.py,sha256=ViJkwLDvStB7grc8RuoKSDXQM399Wru4-OY3E8k1l_U,1882
43
- kfinance/tool_calling/get_segments_from_identifier.py,sha256=5schxaVyPT4-LqtkmqHMXUF4PiouEzS8disolJ8VAXE,1865
43
+ kfinance/tool_calling/get_segments_from_identifier.py,sha256=xDi6GF0GgZwATaxw9me64BDXVrmopKQed6j3HfXSASA,1865
44
44
  kfinance/tool_calling/get_transcript.py,sha256=eB-IsRwD-mllsMOYRZbH35caQ1Y3teKft0tmI9nVL-A,756
45
45
  kfinance/tool_calling/prompts.py,sha256=Yw1DJIMh90cjL-8q6_RMRiSjCtFDXvJAy7QiV5_uAU8,911
46
46
  kfinance/tool_calling/resolve_identifier.py,sha256=npslr6bBCu0qEDV1-8d24F5OC3nQ1KBMphuMbHVC1AU,626
47
- kfinance/tool_calling/shared_models.py,sha256=K-NPQyE_7Ew6Cs0zxG1xO2O47gp5uDHdHtWD7wUDZX4,2132
48
- kensho_kfinance-2.4.1.dist-info/METADATA,sha256=auRXUJ2d7DGgvj9qg199sB1dLufCa1zdGg1VfXh445k,5191
49
- kensho_kfinance-2.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
- kensho_kfinance-2.4.1.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
51
- kensho_kfinance-2.4.1.dist-info/RECORD,,
47
+ kfinance/tool_calling/shared_models.py,sha256=loNoVFVyQnnpHLUIjkR6P9flGNbzp6kkGpL5hT37Sdg,2666
48
+ kensho_kfinance-2.4.3.dist-info/METADATA,sha256=1Q6uTDsyuQqiSoEJ10HF83D5JHvupCBhyK0HobXrzyA,5191
49
+ kensho_kfinance-2.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
+ kensho_kfinance-2.4.3.dist-info/top_level.txt,sha256=kT_kNwVhfQoOAecY8W7uYah5xaHMoHoAdBIvXh6DaKM,9
51
+ kensho_kfinance-2.4.3.dist-info/RECORD,,
kfinance/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.4.3
4
+ - Improve MCP tool build process
5
+
6
+ ## v2.4.2
7
+ - Update missing permission warning
8
+
3
9
  ## v2.4.1
4
10
  - Fix SegmentsPermission typo
5
11
 
kfinance/constants.py CHANGED
@@ -56,10 +56,8 @@ class StatementType(StrEnum):
56
56
  """The type of financial statement"""
57
57
 
58
58
  balance_sheet = "balance_sheet"
59
- bs = "balance_sheet"
60
59
  income_statement = "income_statement"
61
60
  cashflow = "cashflow"
62
- cf = "cashflow"
63
61
 
64
62
 
65
63
  class SegmentType(StrEnum):
kfinance/fetch.py CHANGED
@@ -200,9 +200,9 @@ class KFinanceApiClient:
200
200
  self._user_permissions.add(Permission[permission_str])
201
201
  except KeyError:
202
202
  logger.warning(
203
- "Could not resolve %s to a member of the Permission enum. "
204
- "%s may be a new type of permission that still needs to be "
205
- "added to the enum.",
203
+ "You have access to functions using %s. However, functions using "
204
+ "%s have not yet been added in this version of the client. To access "
205
+ "all functions that you have access to, you may need to update the client.",
206
206
  permission_str,
207
207
  permission_str,
208
208
  )
kfinance/mcp.py CHANGED
@@ -1,44 +1,36 @@
1
- from textwrap import dedent
2
1
  from typing import Literal, Optional
3
2
 
4
3
  import click
5
4
  from fastmcp import FastMCP
5
+ from fastmcp.tools import FunctionTool
6
6
  from fastmcp.utilities.logging import get_logger
7
+ from langchain_core.utils.function_calling import convert_to_openai_tool
7
8
 
8
9
  from kfinance.kfinance import Client
9
- from kfinance.tool_calling.shared_models import KfinanceTool
10
+ from kfinance.tool_calling import KfinanceTool
10
11
 
11
12
 
12
13
  logger = get_logger(__name__)
13
14
 
14
15
 
15
- def build_doc_string(tool: KfinanceTool) -> str:
16
- """Build a formatted documentation string for a Kfinance tool.
16
+ def build_mcp_tool_from_kfinance_tool(kfinance_tool: KfinanceTool) -> FunctionTool:
17
+ """Build an MCP FunctionTool from a langchain KfinanceTool."""
17
18
 
18
- This function takes a KfinanceTool object and constructs a comprehensive
19
- documentation string that includes the tool's description and detailed
20
- information about its arguments, including default values and descriptions.
21
-
22
- :param tool: The Kfinance tool object containing metadata about the tool's functionality, description, and argument schema.
23
- :type tool: KfinanceTool
24
- :return: A formatted documentation string containing for the tool description with detailed argument information.
25
- :rtype: str
26
- """
27
-
28
- description = dedent(f"""
29
- {tool.description}
30
-
31
- Args:
32
- """).strip()
33
-
34
- for arg_name, arg_field in tool.args_schema.model_fields.items():
35
- default_value_description = (
36
- f"Default: {arg_field.default}. " if not arg_field.is_required() else ""
37
- )
38
- param_description = f"\n {arg_name}: {default_value_description}{arg_field.description}"
39
- description += param_description
40
-
41
- return description
19
+ return FunctionTool(
20
+ name=kfinance_tool.name,
21
+ description=kfinance_tool.description,
22
+ # MCP expects a JSON schema for tool params, which we
23
+ # can generate similar to how langchain generates openai json schemas.
24
+ parameters=convert_to_openai_tool(kfinance_tool)["function"]["parameters"],
25
+ # The langchain runner internally validates input arguments via the args_schema.
26
+ # When running with mcp, we need to reproduce that validation ourselves in
27
+ # run_without_langchain (which then calls _run).
28
+ # If we pass in the underlying _run method directly, mcp generates a schema from
29
+ # the _run type hints but bypasses our internal validation. This causes errors,
30
+ # for example with integer literals, which our args models allow but the
31
+ # mcp-internal validation disallows.
32
+ fn=kfinance_tool.run_without_langchain,
33
+ )
42
34
 
43
35
 
44
36
  @click.command()
@@ -85,13 +77,9 @@ def run_mcp(
85
77
  kfinance_client = Client()
86
78
 
87
79
  kfinance_mcp: FastMCP = FastMCP("Kfinance")
88
- for tool in kfinance_client.langchain_tools:
89
- logger.info("Adding %s to server", tool.name)
90
- kfinance_mcp.tool(
91
- name_or_fn=getattr(tool, "_run"),
92
- name=tool.name,
93
- description=build_doc_string(tool),
94
- )
80
+ for langchain_tool in kfinance_client.langchain_tools:
81
+ logger.info("Adding %s to server", langchain_tool.name)
82
+ kfinance_mcp.add_tool(build_mcp_tool_from_kfinance_tool(langchain_tool))
95
83
 
96
84
  logger.info("Server starting")
97
85
  kfinance_mcp.run(transport=transport)
File without changes
@@ -1,6 +1,10 @@
1
+ import contextlib
2
+ from contextlib import nullcontext as does_not_raise
1
3
  from datetime import date, datetime
2
4
 
3
5
  from langchain_core.utils.function_calling import convert_to_openai_tool
6
+ from pydantic import BaseModel, ValidationError
7
+ import pytest
4
8
  from pytest import raises
5
9
  from requests_mock import Mocker
6
10
  import time_machine
@@ -48,7 +52,7 @@ from kfinance.tool_calling.get_segments_from_identifier import (
48
52
  GetSegmentsFromIdentifierArgs,
49
53
  )
50
54
  from kfinance.tool_calling.get_transcript import GetTranscriptArgs
51
- from kfinance.tool_calling.shared_models import ToolArgsWithIdentifier
55
+ from kfinance.tool_calling.shared_models import ToolArgsWithIdentifier, ValidQuarter
52
56
 
53
57
 
54
58
  class TestGetBusinessRelationshipFromIdentifier:
@@ -638,3 +642,33 @@ class TestGetTranscript:
638
642
  tool = GetTranscript(kfinance_client=mock_client)
639
643
  response = tool.run(GetTranscriptArgs(key_dev_id=12345).model_dump(mode="json"))
640
644
  assert response == expected_response
645
+
646
+
647
+ class TestValidQuarter:
648
+ class QuarterModel(BaseModel):
649
+ quarter: ValidQuarter | None
650
+
651
+ @pytest.mark.parametrize(
652
+ "input_quarter, expectation, expected_quarter",
653
+ [
654
+ pytest.param(1, does_not_raise(), 1, id="int input works"),
655
+ pytest.param("1", does_not_raise(), 1, id="str input works"),
656
+ pytest.param(None, does_not_raise(), None, id="None input works"),
657
+ pytest.param(5, pytest.raises(ValidationError), None, id="invalid int raises"),
658
+ pytest.param("5", pytest.raises(ValidationError), None, id="invalid str raises"),
659
+ ],
660
+ )
661
+ def test_valid_quarter(
662
+ self,
663
+ input_quarter: int | str | None,
664
+ expectation: contextlib.AbstractContextManager,
665
+ expected_quarter: int | None,
666
+ ) -> None:
667
+ """
668
+ GIVEN a model that uses `ValidQuarter`
669
+ WHEN we deserialize with int, str, or None
670
+ THEN valid str get coerced to int. Invalid values raise.
671
+ """
672
+ with expectation:
673
+ res = self.QuarterModel.model_validate(dict(quarter=input_quarter))
674
+ assert res.quarter == expected_quarter
@@ -3,7 +3,7 @@ from typing import Literal, Type
3
3
  from pydantic import BaseModel, Field
4
4
 
5
5
  from kfinance.constants import LINE_ITEM_NAMES_AND_ALIASES, PeriodType, Permission
6
- from kfinance.tool_calling.shared_models import KfinanceTool, ToolArgsWithIdentifier
6
+ from kfinance.tool_calling.shared_models import KfinanceTool, ToolArgsWithIdentifier, ValidQuarter
7
7
 
8
8
 
9
9
  class GetFinancialLineItemFromIdentifierArgs(ToolArgsWithIdentifier):
@@ -16,8 +16,8 @@ class GetFinancialLineItemFromIdentifierArgs(ToolArgsWithIdentifier):
16
16
  period_type: PeriodType | None = Field(default=None, description="The period type")
17
17
  start_year: int | None = Field(default=None, description="The starting year for the data range")
18
18
  end_year: int | None = Field(default=None, description="The ending year for the data range")
19
- start_quarter: Literal[1, 2, 3, 4] | None = Field(default=None, description="Starting quarter")
20
- end_quarter: Literal[1, 2, 3, 4] | None = Field(default=None, description="Ending quarter")
19
+ start_quarter: ValidQuarter | None = Field(default=None, description="Starting quarter")
20
+ end_quarter: ValidQuarter | None = Field(default=None, description="Ending quarter")
21
21
 
22
22
 
23
23
  class GetFinancialLineItemFromIdentifier(KfinanceTool):
@@ -3,7 +3,7 @@ from typing import Literal, Type
3
3
  from pydantic import BaseModel, Field
4
4
 
5
5
  from kfinance.constants import PeriodType, Permission, StatementType
6
- from kfinance.tool_calling.shared_models import KfinanceTool, ToolArgsWithIdentifier
6
+ from kfinance.tool_calling.shared_models import KfinanceTool, ToolArgsWithIdentifier, ValidQuarter
7
7
 
8
8
 
9
9
  class GetFinancialStatementFromIdentifierArgs(ToolArgsWithIdentifier):
@@ -12,8 +12,8 @@ class GetFinancialStatementFromIdentifierArgs(ToolArgsWithIdentifier):
12
12
  period_type: PeriodType | None = Field(default=None, description="The period type")
13
13
  start_year: int | None = Field(default=None, description="The starting year for the data range")
14
14
  end_year: int | None = Field(default=None, description="The ending year for the data range")
15
- start_quarter: Literal[1, 2, 3, 4] | None = Field(default=None, description="Starting quarter")
16
- end_quarter: Literal[1, 2, 3, 4] | None = Field(default=None, description="Ending quarter")
15
+ start_quarter: ValidQuarter | None = Field(default=None, description="Starting quarter")
16
+ end_quarter: ValidQuarter | None = Field(default=None, description="Ending quarter")
17
17
 
18
18
 
19
19
  class GetFinancialStatementFromIdentifier(KfinanceTool):
@@ -3,7 +3,7 @@ from typing import Literal, Type
3
3
  from pydantic import BaseModel, Field
4
4
 
5
5
  from kfinance.constants import PeriodType, Permission, SegmentType
6
- from kfinance.tool_calling.shared_models import KfinanceTool, ToolArgsWithIdentifier
6
+ from kfinance.tool_calling.shared_models import KfinanceTool, ToolArgsWithIdentifier, ValidQuarter
7
7
 
8
8
 
9
9
  class GetSegmentsFromIdentifierArgs(ToolArgsWithIdentifier):
@@ -12,8 +12,8 @@ class GetSegmentsFromIdentifierArgs(ToolArgsWithIdentifier):
12
12
  period_type: PeriodType | None = Field(default=None, description="The period type")
13
13
  start_year: int | None = Field(default=None, description="The starting year for the data range")
14
14
  end_year: int | None = Field(default=None, description="The ending year for the data range")
15
- start_quarter: Literal[1, 2, 3, 4] | None = Field(default=None, description="Starting quarter")
16
- end_quarter: Literal[1, 2, 3, 4] | None = Field(default=None, description="Ending quarter")
15
+ start_quarter: ValidQuarter | None = Field(default=None, description="Starting quarter")
16
+ end_quarter: ValidQuarter | None = Field(default=None, description="Ending quarter")
17
17
 
18
18
 
19
19
  class GetSegmentsFromIdentifier(KfinanceTool):
@@ -1,7 +1,7 @@
1
- from typing import Any, Type
1
+ from typing import Annotated, Any, Literal, Type
2
2
 
3
3
  from langchain_core.tools import BaseTool
4
- from pydantic import BaseModel, ConfigDict, Field
4
+ from pydantic import BaseModel, BeforeValidator, ConfigDict, Field
5
5
 
6
6
  from kfinance.constants import Permission
7
7
  from kfinance.kfinance import Client
@@ -53,3 +53,18 @@ class ToolArgsWithIdentifier(BaseModel):
53
53
  identifier: str = Field(
54
54
  description="The identifier, which can be a ticker symbol, ISIN, or CUSIP"
55
55
  )
56
+
57
+
58
+ def convert_str_to_int(v: Any) -> Any:
59
+ """Convert strings to integers if possible."""
60
+ if isinstance(v, str) and v.isdigit():
61
+ return int(v)
62
+ return v
63
+
64
+
65
+ # Valid Quarter is a literal type, which converts strings to int before
66
+ # validating them.
67
+ # Claude seems to often pass strings to int literals, which raise a
68
+ # ValidationError during deserialization unless they have been converted
69
+ # to int.
70
+ ValidQuarter = Annotated[Literal[1, 2, 3, 4], BeforeValidator(convert_str_to_int)]
kfinance/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.4.1'
21
- __version_tuple__ = version_tuple = (2, 4, 1)
20
+ __version__ = version = '2.4.3'
21
+ __version_tuple__ = version_tuple = (2, 4, 3)
@@ -1,16 +0,0 @@
1
- from typing import Type
2
-
3
- import pytest
4
-
5
- from kfinance.kfinance import Client
6
- from kfinance.mcp import build_doc_string
7
- from kfinance.tool_calling import ALL_TOOLS
8
- from kfinance.tool_calling.shared_models import KfinanceTool
9
-
10
-
11
- class TestDocStringBuilding:
12
- @pytest.mark.parametrize("tool_class", ALL_TOOLS)
13
- def test_build_doc_string(self, mock_client: Client, tool_class: Type[KfinanceTool]):
14
- """This test build the docstring for each tool. A success is considered if no exception is raised"""
15
- tool = tool_class(kfinance_client=mock_client)
16
- build_doc_string(tool)