Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2

- name: Install dependencies
run: make install
Expand All @@ -27,13 +27,13 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.14"] # Min and max supported versions
python-version: ["3.11", "3.15"] # Min and max supported versions
steps:
- name: Checkout
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
python-version: ${{ matrix.python-version }}

Expand Down
31 changes: 18 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:

steps:
- name: Setup | Checkout Repository on Release Branch
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.ref_name }}
fetch-depth: 0
Expand Down Expand Up @@ -67,23 +67,28 @@ jobs:

printf '%s\n' "Verified upstream branch has not changed, continuing with release..."

- name: Setup | Install uv
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2

- name: Action | Semantic Version Release
id: release
uses: python-semantic-release/python-semantic-release@350c48fcb3ffcdfd2e0a235206bc2ecea6b69df0 # v10.5.3
with:
github_token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }}
git_committer_name: "github-actions"
git_committer_email: "actions@users.noreply.github.com"
env:
GIT_COMMIT_AUTHOR: "github-actions <actions@users.noreply.github.com>"
GIT_COMMITTER_NAME: github-actions
GIT_COMMITTER_EMAIL: actions@users.noreply.github.com
GH_TOKEN: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }}
run: uv run --frozen --no-default-groups --group release semantic-release -v version

- name: Publish | Upload to GitHub Release Assets
uses: python-semantic-release/publish-action@ae6462adc12bd3d1738070d784b65b5189b955a9 # v10.4.1
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }}
tag: ${{ steps.release.outputs.tag }}
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_RELEASE }}
run: |
uv run --frozen --no-default-groups --group release semantic-release -v publish \
--tag "${{ steps.release.outputs.tag }}"

- name: Upload | Distribution Artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: steps.release.outputs.released == 'true'
with:
name: distribution-artifacts
Expand All @@ -105,14 +110,14 @@ jobs:

steps:
- name: Setup | Download Build Artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
id: artifact-download
with:
name: distribution-artifacts
path: dist

- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
with:
packages-dir: dist
print-hash: true
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.10
3.11
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ keywords = ["api", "client", "linkup", "sdk", "search"]
license = "MIT"
name = "linkup-sdk"
readme = "README.md"
requires-python = ">=3.10,<4.0"
requires-python = ">=3.11,<4.0"
version = "0.23.1"

classifiers = [
Expand All @@ -18,7 +18,6 @@ classifiers = [
dependencies = ["httpx>=0.23.0", "pydantic>=2.0.0"]
Comment thread
marchelbling marked this conversation as resolved.

[project.optional-dependencies]
build = ["uv>=0.11.6,<0.12.0"] # For python-semantic-release build command, used in GitHub actions
x402 = ["x402[httpx,evm]>=2.0.0"]

[project.urls]
Expand All @@ -38,6 +37,7 @@ dev = [
"python-dotenv>=1.2.2",
"x402[httpx,evm]>=2.0.0",
]
release = ["python-semantic-release>=10.7.0,<11.0.0"]

[tool.pyright]
include = ["./src/", "./tests/"]
Expand All @@ -58,7 +58,7 @@ skip_covered = true

[tool.ruff]
line-length = 100
target-version = "py310"
target-version = "py311"
unsafe-fixes = true

[tool.ruff.lint]
Expand Down Expand Up @@ -123,7 +123,6 @@ packages = ["src/linkup"] # Because project and source code directory names diff
[tool.semantic_release]
allow_zero_version = true
build_command = """
python -m pip install -e '.[build]'
uv lock --upgrade-package "$PACKAGE_NAME"
git add uv.lock
uv build
Expand All @@ -137,7 +136,8 @@ parse_squash_commits = false

[tool.uv]
exclude-newer = "2 weeks" # Reduce risks of supply chain attacks
required-version = ">=0.11.15,<0.12.0"
exclude-newer-package = { certifi = "2026-10-21T00:00:00Z" } # HACK: this is a temporary workaround and can be removed 2 weeks after this date
required-version = ">=0.13.5,<0.14.0"

[build-system]
build-backend = "hatchling.build"
Expand Down
38 changes: 23 additions & 15 deletions src/linkup/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ async def async_search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: bool | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupSearchResults: ...

@overload
Expand All @@ -341,7 +341,7 @@ async def async_search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: bool | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupSourcedAnswer: ...

@overload
Expand All @@ -360,7 +360,7 @@ async def async_search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: Literal[False] | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> JSONObject: ...

@overload
Expand All @@ -379,7 +379,7 @@ async def async_search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: Literal[True],
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupSearchStructuredResponse: ...

@overload
Expand All @@ -398,7 +398,7 @@ async def async_search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: bool | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> (
LinkupSearchResults | LinkupSourcedAnswer | JSONObject | LinkupSearchStructuredResponse
): ...
Expand All @@ -418,7 +418,7 @@ async def async_search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: bool | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupSearchResults | LinkupSourcedAnswer | JSONObject | LinkupSearchStructuredResponse:
"""Asynchronously perform a web search using the Linkup API /search endpoint.

Expand Down Expand Up @@ -597,7 +597,7 @@ async def async_research(
to_date: datetime.date | str | None = None,
exclude_domains: list[str] | None = None,
include_domains: list[str] | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupResearchTask:
"""Asynchronously create a research task using the Linkup API /research endpoint.

Expand Down Expand Up @@ -712,7 +712,7 @@ async def async_list_research(
page_size: int | None = None,
sort_by: Literal["createdAt", "updatedAt"] | None = None,
sort_direction: Literal["asc", "desc"] | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupResearchTasksPage:
"""Asynchronously list research tasks for the authenticated organization.

Expand Down Expand Up @@ -776,7 +776,9 @@ def get_research(self, research_id: str, timeout: float | None = None) -> Linkup
return self._parse_research_task(response.json())

async def async_get_research(
self, research_id: str, timeout: float | None = None
self,
research_id: str,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupResearchTask:
"""Asynchronously retrieve a single research task by identifier.

Expand Down Expand Up @@ -840,7 +842,9 @@ def create_tasks(
return [self._parse_task(task) for task in response_data]

async def async_create_tasks(
self, tasks: list[LinkupTaskInput], timeout: float | None = None
self,
tasks: list[LinkupTaskInput],
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> list[LinkupTask]:
"""Asynchronously create a mixed batch of search, fetch, and research tasks.

Expand Down Expand Up @@ -941,7 +945,7 @@ async def async_list_tasks(
task_type: Literal["search", "fetch", "research"]
| list[Literal["search", "fetch", "research"]]
| None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupTasksPage:
"""Asynchronously list tasks for the authenticated organization.

Expand Down Expand Up @@ -1009,7 +1013,11 @@ def get_task(self, task_id: str, timeout: float | None = None) -> LinkupTask:

return self._parse_task(cast("dict[str, Any]", response.json()))

async def async_get_task(self, task_id: str, timeout: float | None = None) -> LinkupTask:
async def async_get_task(
self,
task_id: str,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
) -> LinkupTask:
"""Asynchronously retrieve a single task by identifier.

Args:
Expand Down Expand Up @@ -1117,7 +1125,7 @@ async def async_fetch(
include_raw_html: bool | None = None,
render_js: bool | None = None,
extract_images: bool | None = None,
timeout: float | None = None,
timeout: float | None = None, # ruff: ignore[async-function-with-timeout]
include_raw_content: bool | None = None,
mode: Literal["standard", "pro"] | None = None,
schema: type[pydantic.BaseModel] | dict[str, Any] | str | None = None,
Expand Down Expand Up @@ -1247,7 +1255,7 @@ async def _async_request(
*,
json: dict[str, Any] | list[dict[str, Any]] | None = None,
params: dict[str, Any] | None = None,
timeout: float | None,
timeout: float | None, # ruff: ignore[async-function-with-timeout]
) -> httpx.Response:
try:
async with httpx.AsyncClient(
Expand Down Expand Up @@ -1334,7 +1342,7 @@ async def _async_handle_x402_payment(
url: str,
json: dict[str, Any] | list[dict[str, Any]] | None,
params: dict[str, Any] | None,
timeout: float | None,
timeout: float | None, # ruff: ignore[async-function-with-timeout]
) -> httpx.Response:
if self._x402_signer is None:
raise RuntimeError("x402 signer is not configured")
Expand Down
Loading
Loading