From 08096575ff6cd76982b7887549982a9a39a6a319 Mon Sep 17 00:00:00 2001 From: cjumel Date: Thu, 21 May 2026 11:33:28 +0200 Subject: [PATCH 1/2] chore: upgrade semantic release action --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4c62860..c57f9fc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: - name: Action | Semantic Version Release id: release - uses: python-semantic-release/python-semantic-release@v10.4.1 + uses: python-semantic-release/python-semantic-release@v10.5.3 with: github_token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }} git_committer_name: "github-actions" From 7b7841f90fb32c20bb5bda568bd58fcae1033685 Mon Sep 17 00:00:00 2001 From: cjumel Date: Thu, 21 May 2026 11:37:45 +0200 Subject: [PATCH 2/2] fix: allow structured output schema to be a dict --- pyproject.toml | 2 -- src/linkup/_client.py | 44 +++++++++++++++++++-------------------- tests/unit/client_test.py | 28 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6fd4cb6..421409c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,6 @@ Homepage = "https://github.com/LinkupPlatform/linkup-python-sdk" Source = "https://github.com/LinkupPlatform/linkup-python-sdk" Tracker = "https://github.com/LinkupPlatform/linkup-python-sdk/issues" - [dependency-groups] dev = [ "prek>=0.3.5", @@ -140,7 +139,6 @@ parse_squash_commits = false exclude-newer = "2 weeks" # Reduce risks of supply chain attacks required-version = ">=0.10.0,<0.11.0" - [build-system] build-backend = "hatchling.build" requires = ["hatchling"] diff --git a/src/linkup/_client.py b/src/linkup/_client.py index 0f65539..3159ec2 100644 --- a/src/linkup/_client.py +++ b/src/linkup/_client.py @@ -92,7 +92,7 @@ def search( query: str, depth: Literal["standard", "deep"], output_type: Literal["searchResults", "sourcedAnswer", "structured"], - structured_output_schema: type[BaseModel] | str | None = None, + structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None, include_images: bool | None = None, from_date: date | None = None, to_date: date | None = None, @@ -118,8 +118,8 @@ def search( supporting it, and "structured" will base the output on the format provided in structured_output_schema. structured_output_schema: If output_type is "structured", specify the schema of the - output. Supported formats are a pydantic.BaseModel or a string representing a - valid object JSON schema. + output. Supported formats are a pydantic.BaseModel, a Python dictionary containing a + valid object JSON schema, or a string representing a valid object JSON schema. include_images: Indicate whether images should be included during the search. from_date: The date from which the search results should be considered. If None, the search results will not be filtered by date. @@ -146,8 +146,8 @@ def search( True Raises: - TypeError: If structured_output_schema is not provided or is not a string or a - pydantic.BaseModel when output_type is "structured". + TypeError: If structured_output_schema is not provided or is not a string, dictionary, + or pydantic.BaseModel when output_type is "structured". LinkupInvalidRequestError: If structured_output_schema doesn't represent a valid object JSON schema when output_type is "structured". LinkupAuthenticationError: If the Linkup API key is invalid. @@ -189,7 +189,7 @@ async def async_search( query: str, depth: Literal["standard", "deep"], output_type: Literal["searchResults", "sourcedAnswer", "structured"], - structured_output_schema: type[BaseModel] | str | None = None, + structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None, include_images: bool | None = None, from_date: date | None = None, to_date: date | None = None, @@ -215,8 +215,8 @@ async def async_search( supporting it, and "structured" will base the output on the format provided in structured_output_schema. structured_output_schema: If output_type is "structured", specify the schema of the - output. Supported formats are a pydantic.BaseModel or a string representing a - valid object JSON schema. + output. Supported formats are a pydantic.BaseModel, a Python dictionary containing a + valid object JSON schema, or a string representing a valid object JSON schema. include_images: Indicate whether images should be included during the search. from_date: The date from which the search results should be considered. If None, the search results will not be filtered by date. @@ -243,8 +243,8 @@ async def async_search( True Raises: - TypeError: If structured_output_schema is not provided or is not a string or a - pydantic.BaseModel when output_type is "structured". + TypeError: If structured_output_schema is not provided or is not a string, dictionary, + or pydantic.BaseModel when output_type is "structured". LinkupInvalidRequestError: If structured_output_schema doesn't represent a valid object JSON schema when output_type is "structured". LinkupAuthenticationError: If the Linkup API key is invalid. @@ -287,7 +287,7 @@ def research( output_type: Literal["sourcedAnswer", "structured"], reasoning_depth: Literal["S", "M", "L", "XL"] | None = None, mode: Literal["answer", "auto", "investigate", "research"] | None = None, - structured_output_schema: type[BaseModel] | str | None = None, + structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None, from_date: date | None = None, to_date: date | None = None, exclude_domains: list[str] | None = None, @@ -307,8 +307,8 @@ def research( is used. mode: The research mode to use. If None, the Linkup API default is used. structured_output_schema: If output_type is "structured", specify the output schema. - Supported formats are a pydantic.BaseModel or a string representing a valid object - JSON schema. + Supported formats are a pydantic.BaseModel, a Python dictionary containing a valid + object JSON schema, or a string representing a valid object JSON schema. from_date: The date from which the research sources should be considered. If None, sources will not be filtered by a start date. to_date: The date until which the research sources should be considered. If None, @@ -322,8 +322,8 @@ def research( The created research task. Raises: - TypeError: If structured_output_schema is not a string or pydantic.BaseModel when - provided. + TypeError: If structured_output_schema is not a string, dictionary, or + pydantic.BaseModel when provided. LinkupInvalidRequestError: If the request parameters are invalid. LinkupAuthenticationError: If the Linkup API key is invalid. LinkupInsufficientCreditError: If you have run out of credit. @@ -356,7 +356,7 @@ async def async_research( output_type: Literal["sourcedAnswer", "structured"], reasoning_depth: Literal["S", "M", "L", "XL"] | None = None, mode: Literal["answer", "auto", "investigate", "research"] | None = None, - structured_output_schema: type[BaseModel] | str | None = None, + structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None, from_date: date | None = None, to_date: date | None = None, exclude_domains: list[str] | None = None, @@ -376,8 +376,8 @@ async def async_research( is used. mode: The research mode to use. If None, the Linkup API default is used. structured_output_schema: If output_type is "structured", specify the output schema. - Supported formats are a pydantic.BaseModel or a string representing a valid object - JSON schema. + Supported formats are a pydantic.BaseModel, a Python dictionary containing a valid + object JSON schema, or a string representing a valid object JSON schema. from_date: The date from which the research sources should be considered. If None, sources will not be filtered by a start date. to_date: The date until which the research sources should be considered. If None, @@ -391,8 +391,8 @@ async def async_research( The created research task. Raises: - TypeError: If structured_output_schema is not a string or pydantic.BaseModel when - provided. + TypeError: If structured_output_schema is not a string, dictionary, or + pydantic.BaseModel when provided. LinkupInvalidRequestError: If the request parameters are invalid. LinkupAuthenticationError: If the Linkup API key is invalid. LinkupInsufficientCreditError: If you have run out of credit. @@ -1352,7 +1352,7 @@ def _parse_search_response( self, response: httpx.Response, output_type: Literal["searchResults", "sourcedAnswer", "structured"], - structured_output_schema: type[BaseModel] | str | None, + structured_output_schema: type[BaseModel] | dict[str, Any] | str | None, include_sources: bool | None, ) -> Any: # noqa: ANN401 return self._parse_search_response_data( @@ -1366,7 +1366,7 @@ def _parse_search_response_data( self, response_data: Any, # noqa: ANN401 output_type: Literal["searchResults", "sourcedAnswer", "structured"], - structured_output_schema: type[BaseModel] | str | dict[str, Any] | None, + structured_output_schema: type[BaseModel] | dict[str, Any] | str | None, include_sources: bool | None, ) -> Any: # noqa: ANN401 if output_type == "searchResults": diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index 925edc2..273c048 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -185,6 +185,34 @@ class Company(BaseModel): "website_url": "https://www.linkup.so/", }, ), + ( + { + "query": "query", + "depth": "standard", + "output_type": "structured", + "structured_output_schema": Company.model_json_schema(), + }, + { + "q": "query", + "depth": "standard", + "outputType": "structured", + "structuredOutputSchema": json.dumps(Company.model_json_schema()), + }, + b""" + { + "name": "Linkup", + "founders_names": ["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], + "creation_date": "2024", + "website_url": "https://www.linkup.so/" + } + """, + { + "name": "Linkup", + "founders_names": ["Philippe Mizrahi", "Denis Charrier", "Boris Toledano"], + "creation_date": "2024", + "website_url": "https://www.linkup.so/", + }, + ), ( { "query": "query",