feat(packaging)!: introduce slim agentex-client + heavy agentex-sdk split#370
feat(packaging)!: introduce slim agentex-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.
|
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.
416d8a1 to
d4a6984
Compare
Publishes the existing wheel as two namespace-sharing packages so REST-only
consumers install just the Stainless client without the ADK runtime.
- agentex-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-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-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>
d4a6984 to
afaa0ca
Compare
Currently Blocked on access to PyPi to create the new package
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-clientagentex/{__init__.py, _*.py, _utils/, types/, resources/, protocol/, py.typed}pip install agentex-sdkagentex/lib/*; depends onagentex-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-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-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_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-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-clientpackage name; addAGENTEX_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-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-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 existing
agentex-sdkwheel into two namespace-sharing packages: a slim REST client (agentex-client, 6 deps, Python ≥ 3.11) and the heavy ADK overlay (agentex-sdk, 31 deps, Python ≥ 3.12). Existing consumers see no change because the heavy wheel depends on the slim transitively.pyproject.toml/adk/pyproject.toml: root package renamed toagentex-clientand stripped to 6 deps; newadk/workspace member carries all ADK deps plus a hatchling build hook that force-includes../src/agentex/libper-file (pruning tests), with a 320-file floor guard to catch a broken walk.bin/publish-pypibuilds both wheels with--wheel(required for the cross-directoryforce-include) and publishes slim-first;--check-urlmakes the workflow idempotent under release-please's per-component double-trigger;scripts/check-slim-depsadds a CI guardrail that fails if Stainless regenerates the root dep list with ADK deps.agentex-sdk-clientandAGENTEX_SDK_CLIENT_PYPI_TOKEN, but the actual code usesagentex-clientandAGENTEX_CLIENT_PYPI_TOKENthroughout. Following the checklist as written would claim the wrong PyPI name and add a secret the publish pipeline never reads — the slim publish would silently fall back toPYPI_TOKEN(the heavy's token), which fails if that token is project-scoped. The checklist should be updated toagentex-client/AGENTEX_CLIENT_PYPI_TOKENbefore maintainers act on it.Confidence Score: 5/5
Safe to merge once the PR description checklist is corrected; all code paths are internally consistent and prior review concerns have been addressed.
The packaging split is well-structured: wheel disjointness is enforced by both the root exclude and the ADK build hook's force_include, the file-count floor in hatch_build.py provides a safety net, and the publish script correctly sequences slim-before-heavy with --check-url idempotency. The PR description's checklist uses different names than the code, but that discrepancy lives only in the PR description text, not in any committed file.
The PR description's Required maintainer follow-ups checklist should be reviewed and corrected before any PyPI or secrets work begins.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[uv build --all-packages --wheel] --> B[agentex_client-*.whl\nname: agentex-client\n6 deps, Python >= 3.11] A --> C[agentex_sdk-*.whl\nname: agentex-sdk\n31 deps + agentex-client, Python >= 3.12] C -->|adk/hatch_build.py force_include| D[src/agentex/lib/**] B -->|pyproject.toml exclude| E[src/agentex lib excluded] B --> F[site-packages/agentex/\n__init__.py, _*.py\ntypes/, resources/, protocol/] C --> G[site-packages/agentex/lib/**] F & G --> H[agentex.* namespace\ndisjoint files, no conflicts] subgraph publish [bin/publish-pypi] direction LR P1[uv publish --check-url\nAGENTEX_CLIENT_PYPI_TOKEN\nagentex_client-*.whl] --> P2[uv publish --check-url\nAGENTEX_PYPI_TOKEN\nagentex_sdk-*.whl] end B --> P1 C --> P2Comments 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
Prompt To Fix All With AI
Reviews (12): Last reviewed commit: "feat(packaging)!: split agentex-sdk into..." | Re-trigger Greptile