diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..10db3ad --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +LINKUP_API_KEY= diff --git a/examples/1_direct_search_results.py b/examples/1_direct_search_results.py index 34769ba..a7f9041 100644 --- a/examples/1_direct_search_results.py +++ b/examples/1_direct_search_results.py @@ -3,10 +3,12 @@ for instance in a RAG system, with the output_type parameter set to "searchResults". """ +from dotenv import load_dotenv from rich import print from linkup import LinkupClient +load_dotenv() client = LinkupClient() response = client.search( diff --git a/examples/2_sourced_answer_search.py b/examples/2_sourced_answer_search.py index 0f565f4..5957d78 100644 --- a/examples/2_sourced_answer_search.py +++ b/examples/2_sourced_answer_search.py @@ -4,10 +4,12 @@ along with the sources supporting it. """ +from dotenv import load_dotenv from rich import print from linkup import LinkupClient +load_dotenv() client = LinkupClient() response = client.search( diff --git a/examples/3_structured_search.py b/examples/3_structured_search.py index 00e7a1b..6d03ffc 100644 --- a/examples/3_structured_search.py +++ b/examples/3_structured_search.py @@ -4,6 +4,7 @@ documented schema to steer the Linkup search in any direction. """ +from dotenv import load_dotenv from pydantic import BaseModel, Field from rich import print @@ -19,6 +20,7 @@ class Events(BaseModel): events: list[Event] = Field(description="The list of events") +load_dotenv() client = LinkupClient() response = client.search( diff --git a/examples/4_asynchronous_search.py b/examples/4_asynchronous_search.py index 6ea3d48..529b905 100644 --- a/examples/4_asynchronous_search.py +++ b/examples/4_asynchronous_search.py @@ -7,10 +7,12 @@ import asyncio import time +from dotenv import load_dotenv from rich import print from linkup import LinkupClient +load_dotenv() client = LinkupClient() queries: list[str] = [ diff --git a/pyproject.toml b/pyproject.toml index 9ebb970..fc565c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dev = [ "pytest-cov>=6.2.1", "pytest-mock>=3.14.1", "pytest>=8.4.1", + "python-dotenv>=1.1.1", "rich>=14.1.0", ] diff --git a/src/linkup/client.py b/src/linkup/client.py index c2b3d2f..a5e6fe5 100644 --- a/src/linkup/client.py +++ b/src/linkup/client.py @@ -96,7 +96,7 @@ def search( LinkupInsufficientCreditError: If you have run out of credit. LinkupNoResultError: If the search query did not yield any result. """ - params: dict[str, Union[str, bool, list[str]]] = self._get_search_params( + params: dict[str, Union[str, bool, list[str], None]] = self._get_search_params( query=query, depth=depth, output_type=output_type, @@ -172,7 +172,7 @@ async def async_search( LinkupAuthenticationError: If the Linkup API key is invalid, or there is no more credit available. """ - params: dict[str, Union[str, bool, list[str]]] = self._get_search_params( + params: dict[str, Union[str, bool, list[str], None]] = self._get_search_params( query=query, depth=depth, output_type=output_type, @@ -316,7 +316,7 @@ def _get_search_params( include_domains: Union[list[str], None], from_date: Union[date, None], to_date: Union[date, None], - ) -> dict[str, Union[str, bool, list[str]]]: + ) -> dict[str, Union[str, bool, list[str], None]]: structured_output_schema_param: str = "" if structured_output_schema is not None: if isinstance(structured_output_schema, str): @@ -337,8 +337,8 @@ def _get_search_params( includeImages=include_images, excludeDomains=exclude_domains or [], includeDomains=include_domains or [], - fromDate=from_date.isoformat() if from_date is not None else "", - toDate=to_date.isoformat() if to_date is not None else "", + fromDate=from_date.isoformat() if from_date is not None else None, + toDate=to_date.isoformat() if to_date is not None else date.today().isoformat(), ) def _validate_search_response( diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index 6b54796..886781f 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -42,8 +42,8 @@ class Company(BaseModel): "includeImages": False, "excludeDomains": [], "includeDomains": [], - "fromDate": "", - "toDate": "", + "fromDate": None, + "toDate": "2000-01-01", }, b""" { @@ -109,8 +109,8 @@ class Company(BaseModel): "includeImages": False, "excludeDomains": [], "includeDomains": [], - "fromDate": "", - "toDate": "", + "fromDate": None, + "toDate": "2000-01-01", }, b""" { @@ -158,8 +158,8 @@ class Company(BaseModel): "includeImages": False, "excludeDomains": [], "includeDomains": [], - "fromDate": "", - "toDate": "", + "fromDate": None, + "toDate": "2000-01-01", }, b""" { @@ -191,8 +191,8 @@ class Company(BaseModel): "includeImages": False, "excludeDomains": [], "includeDomains": [], - "fromDate": "", - "toDate": "", + "fromDate": None, + "toDate": "2000-01-01", }, b""" { @@ -225,6 +225,7 @@ def test_search( mock_request_response_content: bytes, expected_search_response: Any, ) -> None: + mocker.patch("linkup.client.date").today.return_value = date(2000, 1, 1) request_mock = mocker.patch( "linkup.client.LinkupClient._request", return_value=Response( @@ -257,6 +258,7 @@ async def test_async_search( mock_request_response_content: bytes, expected_search_response: Any, ) -> None: + mocker.patch("linkup.client.date").today.return_value = date(2000, 1, 1) request_mock = mocker.patch( "linkup.client.LinkupClient._async_request", return_value=Response( diff --git a/uv.lock b/uv.lock index 363aa9d..9c4858a 100644 --- a/uv.lock +++ b/uv.lock @@ -261,6 +261,7 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-mock" }, + { name = "python-dotenv" }, { name = "rich" }, ] @@ -280,6 +281,7 @@ dev = [ { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "pytest-cov", specifier = ">=6.2.1" }, { name = "pytest-mock", specifier = ">=3.14.1" }, + { name = "python-dotenv", specifier = ">=1.1.1" }, { name = "rich", specifier = ">=14.1.0" }, ] @@ -628,6 +630,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, +] + [[package]] name = "pyyaml" version = "6.0.2"