Skip to content
Open
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
48 changes: 48 additions & 0 deletions .github/workflows/test-sdk-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,54 @@ jobs:
- name: make verify
run: make verify

lowest-vendor-sdks:
name: Lowest vendor SDKs
runs-on: ubuntu-latest
defaults:
run:
working-directory: sdks/python
steps:
- name: Checkout
uses: actions/checkout@v7

# The oldest Python the package supports, from `requires-python`, so the oldest SDKs
# run on the oldest interpreter a caller may have.
- name: Read the oldest supported Python
id: python
run: |
python3 - <<'PY' >> "$GITHUB_OUTPUT"
import re, tomllib
spec = tomllib.load(open("pyproject.toml", "rb"))["project"]["requires-python"]
print(f"version={re.fullmatch(r'>=\s*(\d+\.\d+)(\.\d+)?', spec)[1]}")
PY

- name: Set up Python ${{ steps.python.outputs.version }}
uses: actions/setup-python@v7
with:
python-version: ${{ steps.python.outputs.version }}

# Each vendor SDK at the lower bound pyproject.toml declares, read from there so the
# job tests whatever the bounds say. Tests only: mypy at these versions trips on test
# code that builds the SDKs' errors on httpx 2 types, which they predate.
- name: Install the package with each vendor SDK at its lower bound
run: |
python -m pip install --upgrade pip
pins=$(python - <<'PY'
import re, tomllib
deps = tomllib.load(open("pyproject.toml", "rb"))["project"]["dependencies"]
for dep in deps:
match = re.fullmatch(r"(openai|anthropic|google-genai)>=([^,]+),.*", dep)
if match:
print(f"{match[1]}=={match[2]}")
PY
)
echo "Pinning: $pins"
test "$(echo "$pins" | wc -l)" -eq 3
pip install -e ".[dev]" $pins

- name: Tests
run: python -m pytest -p no:cacheprovider -q

coverage:
name: Coverage
runs-on: ubuntu-latest
Expand Down
31 changes: 23 additions & 8 deletions sdks/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,33 @@ classifiers = [
# `messages.parse` and `extra_body`, google-genai `response_json_schema`, the SDKs'
# exception attributes), which a major release may change without warning; a new major
# is admitted by a deliberate bump after the suite has run against it, not by resolution.
# Dependabot (.github/dependabot.yml, pip ecosystem) proposes those bumps.
# Dependabot (.github/dependabot.yml, pip ecosystem) proposes those bumps. The `Verify`
# CI jobs install the newest versions each range admits, so the upper end is always the
# version under test.
#
# Verified against: pydantic 2.13, textstat 0.7.13, openai 3.14, anthropic 1.6,
# google-genai 2.23, httpx 0.28, attrs 26.1 (September 2026); pillow 12.3 (October 2026).
# The vendor SDK lower bounds (openai, anthropic, google-genai) are the oldest versions
# the suite passes on, so a caller pinned to an older release can still install the
# package; the `Lowest vendor SDKs` CI job installs exactly those and runs it. The other
# lower bounds are not tested that way.
dependencies = [
"pydantic>=2.0.0,<3",
"textstat>=0.7.0,<1",
# Native provider SDKs (D10): one adapter per SDK under providers/. httpx is what
# google-genai transports over and what the error taxonomy reads transport failures
# from. openai (since 2.47.0) and anthropic (since 1.0.0) transport over httpx 2,
# published as the separate httpx2 package, which they bring themselves; the lower
# bounds are the first versions to do so, so httpx2 is always present for the error
# classifier and the tests that build the SDKs' exceptions.
"openai>=2.47.0,<4",
"anthropic>=1.0.0,<2",
# published as the separate httpx2 package, and earlier releases over httpx; the
# classifier reads either, looking httpx2 up only once something has imported it.
#
# 1.108.1 is the first openai to export `omit`, which the adapter sends for an absent
# parameter. 0.77.0 is the first anthropic whose `messages.create` takes
# `output_config`, which carries the structured-output format.
#
# anthropic stays below 2: 2.0 moved the default `anthropic-version` header from
# 2023-06-01 to 2026-10-21, and that API version refuses `temperature`, which the
# Math Standards Alignment contract pins. Admitting 2.x means deciding what the
# adapter sends there, with the live suite run against it.
"openai>=1.108.1,<4",
"anthropic>=0.77.0,<2",
# 1.39.0 is the first to let a client be closed (Client.close, AsyncClient.aclose).
"google-genai>=1.39.0,<3",
"httpx>=0.27.0,<1",
Expand Down Expand Up @@ -71,6 +83,9 @@ dev = [
"pytest-cov>=5.0.0",
"ruff>=0.9.0",
"mypy>=1.14.0",
# The tests build openai and anthropic errors on httpx 2 responses. Their newer
# releases bring it themselves; at the lower bounds above nothing else does.
"httpx2>=2.0.0,<3",
# `make generate-kg-client` / `make check-kg-client`. Pinned exactly: the generator's
# output is committed, so a version bump is a large reviewable diff rather than a
# surprise stale-client failure on someone else's PR.
Expand Down
Loading
Loading