refactor(types): promote protocol types to agentex.protocol.*#371
refactor(types): promote protocol types to agentex.protocol.*#371max-parke-scale wants to merge 5 commits into
Conversation
Moves the JSON-RPC envelope types and ACP method-param types out of the
hand-authored ADK overlay (agentex.lib.types.*) into a new canonical
location at agentex.protocol.*. Back-compat shims at the old paths
re-export the same classes, so existing imports continue to work.
Motivation:
The existing layout puts protocol-shape types under agentex.lib.types.
That means a REST-only consumer who just wants typed JSON-RPC envelopes
(e.g. egp-api-backend, which today hand-rolls a ~600-line gateway
constructing `{"jsonrpc": "2.0", "method": "task/create", ...}` dicts by
hand) can't import the types without pulling in the full heavy ADK
runtime (~31 deps including temporalio, fastapi, claude-agent-sdk, etc.).
This is the first step toward letting that consumer drop hand-rolled
dict literals in favor of typed CreateTaskParams / SendMessageParams /
SendEventParams / JSONRPCRequest / JSONRPCResponse models, ahead of the
forthcoming slim-package split (a separate PR).
Scope:
- Move src/agentex/lib/types/acp.py → src/agentex/protocol/acp.py
- Move src/agentex/lib/types/json_rpc.py → src/agentex/protocol/json_rpc.py
- json_rpc.py: switch from `agentex.lib.utils.model_utils.BaseModel`
(which transitively imports pyyaml) to `pydantic.BaseModel` directly.
These classes don't use any model_utils extensions (from_yaml, to_json,
populate_by_name) so the swap is behavior-preserving.
- Add re-export shims at the old paths so existing
`from agentex.lib.types.{acp,json_rpc} import ...` imports continue
to work. Identity check confirms the shimmed and canonical classes
are the same objects.
- Update all internal usages within agentex.lib.* to import from the
canonical path (9 files in src/agentex/lib + 1 in tests/).
Files moved use only pydantic + agentex.types.{Task,Agent,Event,
TaskMessageContent} — all slim-safe deps. Other agentex.lib.types.*
modules with heavier deps (fastacp pulls temporal, converters pulls
openai-agents) are left in place.
Verified locally:
- ruff check . → All checks passed
- Wheel build → ships both agentex/protocol/* and shims at
agentex/lib/types/{acp,json_rpc}.py
- Import test from a fresh editable install: both canonical and shim
paths resolve to the same class objects (identity-checked)
Zero install-time impact for existing consumers — same import paths
keep working.
Tracking: AGX1-292 (the slim/heavy split that motivates this refactor
lands as a follow-up PR).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dk split Stacks on #371 (which moved protocol types to agentex.protocol.*). Together they enable REST-only consumers to install just the slim package and use typed protocol types without pulling the full ADK stack. Two-package layout: - agentex-sdk-client (slim, root pyproject.toml) * 6 deps: httpx, pydantic, typing-extensions, anyio, distro, sniffio * Ships agentex/{__init__.py, _*.py, _utils/, types/, resources/, protocol/, py.typed} * requires-python >= 3.11 * Stainless-managed * Slim wheel excludes src/agentex/lib/** - agentex-sdk (heavy, adk/pyproject.toml) * Depends on agentex-sdk-client>=0.11.4,<0.12 (lockstep, see comment) * Plus 31 ADK deps: temporalio, fastapi, redis, MCP, LLM providers, observability, CLI surface * Ships only agentex/lib/* via hatchling force-include from ../src/agentex/lib (lib/ stays at its historical location) * requires-python >= 3.12 (lib uses `from typing import override`, a 3.12+ stdlib API) * Hand-authored overlay; entire adk/ directory must be preserved across Stainless codegen via `keep_files: ["adk/**"]` Existing consumers (`pip install agentex-sdk`) see no change: heavy depends on slim, so the slim deps install transitively. Both packages contribute disjoint files to the agentex.* namespace. Release / publish wiring: - bin/publish-pypi publishes slim BEFORE heavy. Heavy depends on slim, so if the slim publish errors we abort before shipping a broken heavy that pins an unreleased slim. (Staff-engineer review fix.) - bin/check-release-environment validates BOTH PyPI tokens, with the legacy PYPI_TOKEN as a back-compat fallback. - .github/workflows/publish-pypi.yml passes AGENTEX_SDK_CLIENT_PYPI_TOKEN and AGENTEX_PYPI_TOKEN to the script. - release-please-config.json: two-package mode with components (agentex-sdk-client at root, agentex-sdk at adk/), and include-component-in-tag=true so tags become e.g. `agentex-sdk-client-v0.11.4` and `agentex-sdk-v0.11.4`. - .release-please-manifest.json: seeds adk/ at 0.11.4 so the first release produces matched versions. CI build job runs `rye build --wheel` for both packages; --wheel only (not sdist) because adk's wheel uses cross-directory force-include which can't resolve from inside an unpacked sdist tarball. Tag scheme breakage flagged with `!` in commit/PR title — downstream tooling that filters by raw `v*` tags will need updates. Required maintainer follow-ups before this can ship: - Stainless dashboard: add `adk/**` to keep_files; reduce dashboard dep-list to the 6 slim-base deps so codegen doesn't re-add the 31 ADK deps to root pyproject's `dependencies = [...]`. - PyPI: claim `agentex-sdk-client` package name; add AGENTEX_SDK_CLIENT_PYPI_TOKEN to repo secrets. - (Defer to a follow-up PR) post-codegen CI guardrail that asserts root pyproject's `dependencies = [...]` matches the 6-dep slim set. Verified locally: - Both wheels build cleanly via `rye build --wheel` - Slim ships agentex/protocol/* and excludes agentex/lib/* - Heavy ships only agentex/lib/* (incl. the back-compat shims from #371) - Dual install on Python 3.13: from agentex import Agentex AND from agentex.protocol.acp import RPCMethod AND from agentex.lib.utils.logging import make_logger AND from agentex.lib.types.acp import RPCMethod (shim) — all resolve; shim and canonical identity-check as the same class objects. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues from Greptile's review (4/5 confidence, all P1/P2):
1. Back-compat shim at `agentex.lib.types.acp` was missing two public
names from the original module: `RPC_SYNC_METHODS` and
`PARAMS_MODEL_BY_METHOD`. External consumers importing those from the
old path would have hit ImportError, breaking the PR's "zero install-
time impact" guarantee. Added both to the re-export list.
2. `agentex.protocol.json_rpc` silently dropped `from_attributes=True`
and `populate_by_name=True` config when switching from
`model_utils.BaseModel` to plain `pydantic.BaseModel`. Restored via
an explicit `model_config = ConfigDict(...)` on all three classes,
with a comment explaining why. The previous version inherited these
flags transitively; making them explicit + documented avoids drift.
3. All 10 CLI scaffolding templates (`agentex init`) generated
`from agentex.lib.types.acp import ...` — works via the shim but
immediately stale on the day this PR establishes
`agentex.protocol.acp` as canonical. Updated all templates to use
the new path so scaffolded code starts on the right foot.
Verified locally:
- ruff check . → All checks passed
- shim re-exports all 7 original symbols (5 classes + 2 module-level
constants), identity-checked vs canonical
- JSONRPCRequest/Response/Error all have
`model_config.get('from_attributes') is True` and
`populate_by_name is True`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@greptile review |
|
This would be a breaking change right? Any consumer that imports these types (any agentex agent using them) will have to update their import paths |
|
No, there's a shim at the old path. |
…,json_rpc} Codifies the invariants Greptile flagged in PR review (and that the previous commit fixed): 1. Every symbol the original modules exported must be importable from the shim path — including the two module-level constants (RPC_SYNC_METHODS, PARAMS_MODEL_BY_METHOD) that an earlier shim iteration dropped. 2. The shim re-exports must be the *same* class objects as the canonical path (identity check, not just type equality). Different objects would silently break isinstance/match-case for any consumer that mixes import styles. 3. The pydantic ConfigDict (from_attributes=True, populate_by_name=True) that JSONRPCRequest/Response/Error inherited from model_utils.BaseModel before the refactor stays preserved on the canonical agentex.protocol.json_rpc classes. Verified locally: - All 5 tests pass against the current shim. - Simulated regression (removing RPC_SYNC_METHODS from the shim re-exports): 2 tests fail with the exact ImportError + AttributeError a downstream consumer would hit. Catches the contract violation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three I001 errors from `ruff check` on the new test file — length-sort ordering inside each `from X import (a, b, c)` block. Auto-fixed via `ruff check --fix`; test still passes after sort. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
declan-scale
left a comment
There was a problem hiding this comment.
Can we update the CLAUDE.md file to reflect the migration and also update the the docs in scale-agentex/agentex/docs and sgp-docs
…olicy
Per Declan's review on this PR — document the protocol-types migration
in CLAUDE.md so future contributors know:
- `src/agentex/protocol/` is the canonical home for slim-safe wire types
(acp.py + json_rpc.py); imports allowed from a future REST-only install.
- `src/agentex/lib/types/{acp,json_rpc}.py` are back-compat shims —
re-exporting from the canonical path. Existing user imports unaffected;
new code should target agentex.protocol.*.
- Other `lib/types/*` modules (tracing, agent_card, credentials, fastacp,
llm_messages, converters) stay in place because they have heavier
transitive deps that aren't slim-safe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@declan-scale done — split across two PRs:
For 🤖 — posted via Claude Code |
Summary
Moves the JSON-RPC envelope types (
JSONRPCRequest/Response/Error) and ACP method-param types (CreateTaskParams,SendMessageParams,SendEventParams,CancelTaskParams,RPCMethod) out ofagentex.lib.types.*into a new canonical location atagentex.protocol.*. Back-compat shims at the old paths re-export the same classes — existing imports continue to work.Zero install-time impact for existing consumers. Same import paths keep working.
Tracking: AGX1-292. First of two PRs:
agentex.protocol.*as the canonical location for protocol typesagentex-sdk-clientpackage (which will shipagentex.protocol.*but notagentex.lib.*), letting REST-only consumers likepackages/egp-api-backenddrop hand-rolled JSON-RPC dict literals in favor of these typed shapesMotivation
egp-api-backendtoday hand-rolls a ~600-line JSON-RPC gateway ategp_api_backend/server/internal/interfaces/services/evaluation_task/evaluation_task_gateways/agentex_output_evaluation_task_gateway.py. Sample:These shapes map directly onto the types defined in
agentex.lib.types.acpandagentex.lib.types.json_rpc. They hand-roll because importing those types currently requires pulling in the full ADK runtime (~31 deps including temporalio, fastapi, claude-agent-sdk, etc.).Moving the protocol types out of
agentex.lib.*is the prerequisite for letting them ship in a future slim-package install.Changes
src/agentex/protocol/__init__.pysrc/agentex/protocol/acp.pysrc/agentex/lib/types/acp.py; no other changesrc/agentex/protocol/json_rpc.pysrc/agentex/lib/types/json_rpc.py; swappedfrom agentex.lib.utils.model_utils import BaseModel→from pydantic import BaseModel(behavior-preserving — these classes don't use anymodel_utilsextensions likefrom_yaml/to_json/populate_by_name)src/agentex/lib/types/acp.pysrc/agentex/lib/types/json_rpc.pysrc/agentex/lib/*tests/test_header_forwarding.pySlim-safety of moved files
Both moved modules depend only on
pydanticandagentex.types.{Task, Agent, Event, TaskMessageContent}— all already-slim-safe (Stainless-generated client surface + pydantic, which is in the bare client's 6 base deps).Other
agentex.lib.types.*modules have heavier deps (e.g.fastacp.pypulls temporal,converters.pypulls openai-agents) and are intentionally left in place. They can be promoted in follow-up PRs if/when other consumers need them slim-safe.Verification (local)
Test plan
from agentex.lib.types.acp import CreateTaskParamscontinues to work without change.from agentex.protocol.acp import CreateTaskParamsworks as the new canonical path.Greptile Summary
This PR moves the JSON-RPC envelope types (
JSONRPCRequest/Response/Error) and ACP method-param types out ofagentex.lib.types.*into a newagentex.protocol.*package, the first step toward a slim REST-only client distribution. Back-compat shims at the old paths ensure existing imports continue to work unchanged.src/agentex/protocol/containsacp.pyandjson_rpc.py;json_rpc.pyexplicitly restoresfrom_attributes=True/populate_by_name=Truevia_PROTOCOL_MODEL_CONFIGto match the behavior inherited from the previousmodel_utils.BaseModel.agentex.lib.types.acpandagentex.lib.types.json_rpcre-export all original symbols includingRPC_SYNC_METHODSandPARAMS_MODEL_BY_METHOD, closing the regression flagged in the prior review pass.tests/test_protocol_shims.pypins the back-compat contract: symbol parity, class object identity (preservingisinstancesemantics for mixed import styles), andmodel_configpreservation.Confidence Score: 5/5
Safe to merge — pure refactor with full back-compat shims, no behavioral changes, and targeted tests covering the exact regressions raised in the prior review cycle.
Both concerns from the previous review round have been resolved: RPC_SYNC_METHODS/PARAMS_MODEL_BY_METHOD are now included in the shim, and from_attributes=True/populate_by_name=True are explicitly re-applied in the canonical json_rpc.py. The new test_protocol_shims.py locks in symbol parity, class identity, and model config preservation. No logic changed — only import paths moved.
No files require special attention.
Important Files Changed
Reviews (5): Last reviewed commit: "fix(lint): sort imports in test_protocol..." | Re-trigger Greptile