feat(packaging)!: introduce slim agentex-sdk-client + heavy agentex-sdk split#370
Open
max-parke-scale wants to merge 1 commit into
Open
feat(packaging)!: introduce slim agentex-sdk-client + heavy agentex-sdk split#370max-parke-scale wants to merge 1 commit into
max-parke-scale wants to merge 1 commit into
Conversation
0d7db31 to
4ad75f7
Compare
max-parke-scale
added a commit
that referenced
this pull request
May 26, 2026
…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
Contributor
Author
|
Fixed in cdb3a48:
Verified locally: both wheels build with @greptile review 🤖 — posted via Claude Code |
Contributor
Author
|
@greptile review |
69316be to
820c3be
Compare
max-parke-scale
added a commit
that referenced
this pull request
May 31, 2026
…-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
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. Release/publish wiring: - release-please two-component mode (`.` + `adk/`), include-component-in-tag. - bin/publish-pypi publishes slim before heavy; both --wheel + --skip-existing. - bin/check-release-environment + publish-pypi.yml validate/pass both tokens. - 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>
7737475 to
715209b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.11.9→agentex-sdk-client-v0.11.9/agentex-sdk-v0.11.9— flagged with!in the title and commit. Any downstream tooling filtering by rawv*tags will need updating..release-please-manifest.json: seedsadk/at0.11.9so the first release produces matched versions.rye build --wheel(the--wheelflag is important — sdist-then-wheel-from-sdist can't resolve adk's cross-directoryforce-includefrom inside an unpacked sdist tarball).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.rye build's default sdist-then-wheel-from-sdist flow can't resolve the cross-directoryforce-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.11.9). The packages co-version and release-please can't rewrite a dep string, so any<Xceiling eventually excludes the co-versioned slim it pins — a<0.12ceiling would have made the first0.12.0cut unresolvable. Floor-only always resolves; an exact-pin check could be added toscripts/check-slim-depslater for tighter lockstep.Greptile Summary
This PR introduces a dual-package split of the existing
agentex-sdkmonolith into a slimagentex-sdk-client(6 REST deps) and a heavyagentex-sdk(ADK overlay, 31 deps) that contributes onlyagentex/lib/*to the sharedagentex.*namespace. Existingpip install agentex-sdkconsumers are unaffected since heavy depends on slim transitively.adk/pyproject.toml+adk/hatch_build.pycustom hook that force-includes../src/agentex/libper-file to allow test-file pruning; rootpyproject.tomlrenamed toagentex-sdk-clientwith 31 deps stripped; rye workspace joining both.release-please-config.jsonmoves to two-component mode withinclude-component-in-tag: true;bin/publish-pypipublishes slim-then-heavy with--wheeland--skip-existingflags;scripts/check-slim-depsguards against Stainless regen silently re-adding ADK deps.agentex-sdk-client>=0.11.9pin inadk/pyproject.toml, sdist support deferred, and Stainless dashboard follow-ups required before the first release.Confidence Score: 5/5
Safe to merge; the packaging split is mechanically sound, the previously flagged missing --wheel and double-publish issues are now resolved.
Both wheels build correctly with --wheel flags, publish ordering is correct (slim before heavy), and --skip-existing on both rye publish calls makes the workflow idempotent under the two-release-event pattern. The only open item is a minor guardrail gap in check-slim-deps around optional extras, which does not affect runtime correctness.
scripts/check-slim-deps — the optional-dependency gap is worth closing before the next Stainless regen cycle.
Important Files Changed
Sequence Diagram
sequenceDiagram participant RP as release-please participant GH as GitHub Releases participant WF as publish-pypi.yml participant Script as bin/publish-pypi participant PyPI RP->>GH: Create release agentex-sdk-client-vX.Y.Z GH-->>WF: trigger (release:published) WF->>Script: bash ./bin/publish-pypi Script->>Script: rye build --clean --wheel (slim, root) Script->>PyPI: rye publish --skip-existing (agentex-sdk-client) PyPI-->>Script: 200 OK Script->>Script: "cd adk && rye build --clean --wheel (heavy)" Script->>PyPI: rye publish --skip-existing (agentex-sdk) PyPI-->>Script: 200 OK RP->>GH: Create release agentex-sdk-vX.Y.Z GH-->>WF: trigger (release:published) second independent event WF->>Script: bash ./bin/publish-pypi Script->>PyPI: rye publish --skip-existing (agentex-sdk-client) already exists PyPI-->>Script: skip idempotent Script->>PyPI: rye publish --skip-existing (agentex-sdk) already exists PyPI-->>Script: skip idempotentComments 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 (9): Last reviewed commit: "feat(packaging)!: split agentex-sdk into..." | Re-trigger Greptile