feat(packaging)!: introduce slim agentex-sdk-client + heavy agentex-sdk split#370
feat(packaging)!: introduce slim agentex-sdk-client + heavy agentex-sdk split#370max-parke-scale wants to merge 1 commit into
Conversation
0d7db31 to
4ad75f7
Compare
…end_path Follows up the slim/heavy split with the test relocations + dev-install plumbing that should have been part of the original commit: - Move tests/lib/* and tests at tests/ root that exercise lib code (test_function_tool.py, test_model_utils.py, test_header_forwarding.py) into adk/tests/. Tests now live with the code they test. The Path-based source references in test_claude_agents_*.py (`_SRC = parents[2] / "src"`) resolve correctly to adk/src/ via the new location. - Fix test_function_tool.py's broken `src.agentex.lib.*` import — switch to the installed-package path `agentex.lib.*` so it works against the editable install. - Add `from pkgutil import extend_path; __path__ = extend_path(...)` to src/agentex/__init__.py. This is the load-bearing fix for dev workflow: without it, two editable installs (slim at root, heavy at adk/) each contributing files to `agentex/` get only the first source dir in `agentex.__path__`, so `import agentex.lib.*` fails. With it, Python discovers both source trees and the namespace merges. Wheel installs (production) already worked because both wheels' files land in the same site-packages/agentex/ directory. - scripts/bootstrap: after `rye sync`, also `pip install -e ./adk` so agentex-sdk's deps land in the dev venv. agentex-sdk-client is already installed via the root sync, so adk's dep on it resolves to the local editable install (no PyPI lookup needed). - pyproject.toml [tool.pytest.ini_options].testpaths includes "adk/tests". - pyproject.toml [tool.ruff.lint.per-file-ignores] extends test-friendly ignores to adk/tests/. - Drop the rye workspace config — pkgutil.extend_path + explicit pip install -e ./adk in bootstrap gives the same dev experience without rye-workspace-version-mismatch quirks. - .github/workflows/ci.yml: lint + test jobs call ./scripts/bootstrap instead of `rye sync` directly; build job builds both packages. Self-review took: I shipped the file move without running the test suite locally — that's why CI broke on PR #370. Mea culpa. The functional design is correct; the rollout was sloppy. Verified locally: - `ruff check .` → All checks passed - `pytest --collect-only adk/tests/` → 100+ tests collect cleanly - `pytest adk/tests/test_function_tool.py` → 10 passed - Dev install (`pip install -e .` + `pip install -e ./adk`): `from agentex import Agentex` and `from agentex.lib.* import …` both work Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2a08eeb to
3a95482
Compare
1198bf9 to
da5b8f0
Compare
|
Fixed in cdb3a48:
Verified locally: both wheels build with @greptile review 🤖 — posted via Claude Code |
|
@greptile review |
69316be to
820c3be
Compare
…-wheel tests Addresses review of the dual-wheel split (#370): - adk: pin agentex-sdk-client floor-only (>=0.11.5). The two packages co-version and release-please can't rewrite a dep string, so a <0.12 ceiling would make the first 0.12.0 cut unresolvable (slim is a new PyPI name with no 0.11.x to fall back to). - adk: prune agentex/lib/** test files from the heavy wheel via a custom hatch build hook — force-include ignores `exclude` (hatchling #1395). Drops 14 test files; keeps the 138 .j2 templates and py.typed. The hook imports build-only hatchling, so it's excluded from pyright. - release-please-config: scope extra-files (_version.py) to the slim package so a heavy-only release can't overwrite the slim's __version__. - run_agent_test.sh: fail loud when a wheel is missing instead of silently testing the pre-installed SDK; fix the dead repo-root fallback glob (quoted inside ls). - ci: add scripts/check-slim-deps guardrail asserting root pyproject keeps exactly the 6 slim deps — catches Stainless re-adding the ADK deps. - requirements{,-dev}.lock: regenerate for the two-package workspace via rye sync — the locks still named the pre-split agentex-sdk and pinned openai-agents below the new >=0.14.3 floor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
820c3be to
7737475
Compare
715209b to
622431a
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Publishes the existing wheel as two namespace-sharing packages so REST-only
consumers install just the Stainless client without the ADK runtime.
- agentex-sdk-client (slim, root pyproject): Stainless client + types +
protocol; 6 deps; requires-python >=3.11; wheel excludes src/agentex/lib/**.
- agentex-sdk (heavy, adk/): the ADK overlay (agentex/lib/*) via a hatchling
build hook that force-includes ../src/agentex/lib and prunes test files
(force-include ignores `exclude`, hatchling #1395); pins agentex-sdk-client
floor-only; requires-python >=3.12.
Heavy depends on slim, so existing `pip install agentex-sdk` consumers are
unchanged. Both contribute disjoint files to the agentex.* namespace.
uv workspace wiring (this repo is uv-based post rye→uv migration):
- [tool.uv.workspace] members = ["adk"] + [tool.uv.sources]
agentex-sdk-client = { workspace = true } so dev resolves the ADK's client
dep to the local root; the published heavy wheel still pins the PyPI version.
- CI + scripts/{bootstrap,test} sync `--all-packages` so the ADK member's deps
install for lint/test; both wheels build via `uv build --all-packages --wheel`
(--wheel load-bearing — the heavy's cross-dir force-include can't go via sdist).
Release/publish wiring:
- release-please two-component mode (`.` + `adk/`), include-component-in-tag.
- bin/publish-pypi publishes slim before heavy via uv; `--check-url` makes the
per-component-tag double-trigger idempotent. Dual tokens, PYPI_TOKEN fallback.
- scripts/check-slim-deps CI guardrail fails if the slim dep set drifts from
the 6-dep base (catches Stainless re-adding ADK deps).
BREAKING CHANGE: release tag scheme changes from v* to <component>-v*.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
622431a to
416d8a1
Compare
declan-scale
left a comment
There was a problem hiding this comment.
We should monitor once this is merged into next, and see what parts of the codegen may create regressions and then create ci/cd steps to address
| { | ||
| ".": "0.12.0" | ||
| ".": "0.12.0", | ||
| "adk": "0.12.0" |
There was a problem hiding this comment.
This will likely lead to the autogen creating merge conflicts, we will see when it merges into next
There was a problem hiding this comment.
Agreed, will monitor - and at some point followup with splitting the repos.
Summary
Publishes the existing
agentex-sdkwheel as two namespace-sharing packages so REST-only consumers can install just the Stainless REST surface without dragging in the full ADK runtime.pip install agentex-sdk-clientagentex/{__init__.py, _*.py, _utils/, types/, resources/, protocol/, py.typed}pip install agentex-sdkagentex/lib/*; depends onagentex-sdk-clientThe two packages contribute disjoint files to the
agentex.*namespace. Existingpip install agentex-sdkconsumers see no change: heavy depends on slim, so the slim deps install transitively.Motivating consumer:
packages/egp-api-backendhand-rolls a ~600-line JSON-RPC gateway today because it can't import the typed wire shapes without pulling in temporalio, fastapi, claude-agent-sdk, and 28 other deps. With this split + #371 (which moves protocol types toagentex.protocol.*), that gateway can pinagentex-sdk-clientand usefrom agentex.protocol.acp import RPCMethod, CreateTaskParams, ....Tracking: AGX1-292.
Note on the PR stack
This change was originally drafted as a single large PR (history visible in the force-push). On review it was clearly two distinct concerns, so it's been split:
agentex.lib.types.*toagentex.protocol.*with back-compat shims. Zero install-time impact.Reviewing them separately should be much more tractable.
Repo layout after merge
src/agentex/lib/stays where it is — Stainless already preserves it perCONTRIBUTING.md. The slim wheel's[tool.hatch.build.targets.wheel].excludekeeps lib/ out of the slim. The heavy wheel pulls lib/ in via a hatchling build hook (adk/hatch_build.py) that force-includes../src/agentex/liband prunes the test files (force-include ignoresexclude— hatchling #1395). Same source file, two disjoint wheels.Python-version pins
agentex-sdk-client:requires-python = ">= 3.11,<4". Zero 3.12-only imports in the Stainless surface.agentex-sdk:requires-python = ">= 3.12,<4".agentex/lib/*usesfrom typing import override(3.12+ stdlib) in 19 files. The combined package's prior>= 3.11pin was de-facto broken on 3.11; this PR aligns the pin with what actually works.Release / publish wiring
bin/publish-pypi: publishes slim before heavy. Heavy depends on slim, so flipping the order means a slim-side failure (token, transient PyPI 5xx, name collision) aborts before we ship a heavy that pins an unreleased slim.bin/check-release-environment: validates bothAGENTEX_SDK_CLIENT_PYPI_TOKENandAGENTEX_PYPI_TOKEN, with legacyPYPI_TOKENas fallback..github/workflows/publish-pypi.yml: passes both token secrets to the script.release-please-config.json: two-package mode (.andadk/) withinclude-component-in-tag = true. Tag scheme changes fromv0.12.0→agentex-sdk-client-v0.12.0/agentex-sdk-v0.12.0— flagged with!in the title and commit. Any downstream tooling filtering by rawv*tags will need updating..release-please-manifest.json: seedsadk/at0.12.0so the first release produces matched versions.uv build --all-packages --wheel(the--wheelflag is important — uv's sdist-then-wheel default can't resolve adk's cross-directoryforce-include).Required maintainer follow-ups before this can ship
adk/**tokeep_filesso the ADK overlay persists across codegen.pyproject.tomlto the 6 slim-base deps. (See "Risks" below — if this isn't done, every Stainless regen will silently re-add the 31 ADK deps to slim'sdependencies = [...].)agentex-sdk-clientpackage name; addAGENTEX_SDK_CLIENT_PYPI_TOKENto repo secrets.agentex-sdkpublishing continues usingAGENTEX_PYPI_TOKENfromadk/.adk/survives and root pyproject's slim shape isn't clobbered.Planned follow-up PRs
The post-codegen dep-list guardrail (
scripts/check-slim-deps) and therequirements{,-dev}.lockregeneration — originally planned here — are now included in this PR.scaleapi/scaleapi): migratepackages/egp-api-backendfrom hand-rolled JSON-RPC to typedagentex.protocol.acpshapes; pinagentex-sdk-client. ~600 lines of dict-literal construction become typed model usage.README.mddescribes capabilities the slim doesn't ship; deferring to its own PR so this one stays focused on packaging.agentex.__version__policy:release-please-config.json'sextra-filesupdates only root_version.py, so the runtime__version__reflects the slim only. Either lockstep-version both (recommended, since they're co-released) or add a separateagentex.lib.__version__.Verification (local)
Risks
exclude. The slim's[tool.hatch.build.targets.wheel].exclude = ["src/agentex/lib/**"]is a hand-edit to Stainless's emitted file. Manual edits todependencies = [...]survive Stainless 3-way merge historically (~7 confirmed examples fromgit log). We're betting the wheel-targetexcludesurvives the same way. If it gets clobbered, the slim wheel would start re-including lib/ and conflict with the heavy on install. Detection: the slim-deps guardrail (scripts/check-slim-deps, run in CI) catches it. Mitigation if it happens: re-add manually or move the exclude to the Stainless dashboard if configurable.v*tags needs to update — flagged with!in title/commit per Conventional Commits.force-include. PyPI tolerates wheel-only releases; if reviewers want sdist support, options are (a) configure hatchling to copy../src/agentex/libinto the sdist, or (b) explicitly disable sdist for adk/.agentex-sdk-clientpin inadk/pyproject.tomlis floor-only (>=0.12.0). The packages co-version and release-please can't rewrite the pin string, so any<Xceiling eventually excludes the co-versioned slim it pins. Floor-only always resolves; an exact-pin check could be added toscripts/check-slim-depslater for tighter lockstep.Greptile Summary
This PR splits the single
agentex-sdkwheel into two namespace-sharing packages: a slimagentex-sdk-client(6 deps, REST surface only) and a heavyagentex-sdk(31 ADK deps,agentex/lib/*only) that depends on the slim. Existingpip install agentex-sdkconsumers see no change — the heavy pulls the slim transitively.pyproject.tomlis renamed toagentex-sdk-client, drops all 31 ADK deps, and gains a[tool.uv.workspace]+exclude = [\"src/agentex/lib/**\"]to keep the slim wheel disjoint from the heavy.adk/directory ships the heavy wheel via a custom hatchling build hook (hatch_build.py) that force-includes../src/agentex/libper-file, with a_MIN_FILES=320floor guard to catch a broken walk at build time rather than silently shipping an empty wheel.--check-urlidempotency, and ascripts/check-slim-depsCI guardrail that fails if Stainless codegen ever accidentally re-adds ADK deps to the slim's dependency list.Confidence Score: 5/5
Safe to merge after required maintainer follow-ups (Stainless dashboard rename, PyPI name claim, secret registration) are completed out-of-band.
The two previously flagged concerns — missing --wheel flag in bin/publish-pypi and double-trigger re-upload failures — are both resolved: the script now uses uv build --all-packages --wheel and uv publish --check-url for idempotency. The hatchling build hook has a runtime floor guard, the slim-dep guardrail runs in CI, and the workspace uv.lock / import paths are correctly updated. No new defects found in the changed paths.
No files require special attention; the required follow-ups (Stainless dashboard, PyPI name claim, AGENTEX_SDK_CLIENT_PYPI_TOKEN secret) are explicitly tracked in the PR description checklist.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD subgraph Source["Source Tree (shared)"] SRC["src/agentex/\n__init__.py, _*.py\ntypes/, resources/\nprotocol/"] LIB["src/agentex/lib/\n(ADK overlay)"] end subgraph SlimWheel["agentex-sdk-client wheel"] SLIM["agentex/* REST surface\n6 deps, requires-python ge 3.11"] end subgraph HeavyWheel["agentex-sdk wheel"] HOOK["hatch_build.py\n(force-include lib/, prune tests)"] HEAVY["agentex/lib/*\n31 ADK deps, requires-python ge 3.12"] end subgraph Release["Release Pipeline"] RP["release-please (two-component tags)"] PUB["bin/publish-pypi\nuv build --all-packages --wheel\nuv publish --check-url"] CHECK["scripts/check-slim-deps (CI guardrail)"] end SRC --> SlimWheel LIB --> HOOK --> HEAVY SlimWheel -->|Requires-Dist| HeavyWheel RP --> PUB CHECK --> PUBComments Outside Diff (1)
.github/workflows/publish-pypi.yml, line 8-9 (link)With
include-component-in-tag: true, release-please creates two separate GitHub releases:agentex-sdk-client-v*andagentex-sdk-v*. Each triggers this workflow independently. Every invocation ofbin/publish-pypipublishes both packages unconditionally, so the second triggered run will attempt to re-upload artifacts already present on PyPI. Sincerye publish(via twine) exits non-zero on a 409 Conflict and the script runs withset -eux, the second workflow run will fail. Adding--skip-existingto bothrye publishcalls inbin/publish-pypi(or switching totwine upload --skip-existing dist/*) would make the publish script idempotent and tolerate this re-trigger.Prompt To Fix With AI
Reviews (10): Last reviewed commit: "feat(packaging)!: split agentex-sdk into..." | Re-trigger Greptile