Skip to content

refactor(types): promote protocol types to agentex.protocol.*#371

Open
max-parke-scale wants to merge 2 commits into
nextfrom
maxparke/agx1-292-promote-protocol-types
Open

refactor(types): promote protocol types to agentex.protocol.*#371
max-parke-scale wants to merge 2 commits into
nextfrom
maxparke/agx1-292-promote-protocol-types

Conversation

@max-parke-scale
Copy link
Copy Markdown
Contributor

@max-parke-scale max-parke-scale commented May 27, 2026

Summary

Moves the JSON-RPC envelope types (JSONRPCRequest/Response/Error) and ACP method-param types (CreateTaskParams, SendMessageParams, SendEventParams, CancelTaskParams, RPCMethod) out of agentex.lib.types.* into a new canonical location at agentex.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:

  • This PR: refactor only — set up agentex.protocol.* as the canonical location for protocol types
  • Follow-up PR: introduces the slim agentex-sdk-client package (which will ship agentex.protocol.* but not agentex.lib.*), letting REST-only consumers like packages/egp-api-backend drop hand-rolled JSON-RPC dict literals in favor of these typed shapes

Motivation

egp-api-backend today hand-rolls a ~600-line JSON-RPC gateway at egp_api_backend/server/internal/interfaces/services/evaluation_task/evaluation_task_gateways/agentex_output_evaluation_task_gateway.py. Sample:

async with session.post(
    rpc_path,
    json={
        "jsonrpc": "2.0",
        "method": "task/create",
        "params": {
            "name": task_name,
            "params": {"description": ..., "is_eval": True},
        },
    },
) as resp:

These shapes map directly onto the types defined in agentex.lib.types.acp and agentex.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

File Change
src/agentex/protocol/__init__.py New — package docstring
src/agentex/protocol/acp.py Moved from src/agentex/lib/types/acp.py; no other change
src/agentex/protocol/json_rpc.py Moved from src/agentex/lib/types/json_rpc.py; swapped from agentex.lib.utils.model_utils import BaseModelfrom pydantic import BaseModel (behavior-preserving — these classes don't use any model_utils extensions like from_yaml/to_json/populate_by_name)
src/agentex/lib/types/acp.py Replaced with back-compat shim re-exporting from canonical path
src/agentex/lib/types/json_rpc.py Replaced with back-compat shim re-exporting from canonical path
9 files in src/agentex/lib/* Internal imports updated to canonical path
tests/test_header_forwarding.py Internal import updated to canonical path

Slim-safety of moved files

Both moved modules depend only on pydantic and agentex.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.py pulls temporal, converters.py pulls 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)

ruff check .
# All checks passed!

python -m build --wheel
unzip -l dist/agentex_sdk-0.11.4-py3-none-any.whl | grep -E "(protocol/|lib/types/(acp|json_rpc))"
# Both paths shipped: agentex/protocol/acp.py + agentex/protocol/json_rpc.py
#                     agentex/lib/types/acp.py (shim) + agentex/lib/types/json_rpc.py (shim)

# Identity check from a fresh install:
python -c "
from agentex.protocol.acp import RPCMethod as A
from agentex.lib.types.acp import RPCMethod as B
assert A is B  # same class object — shim re-exports, not duplicates
print('identity OK')
"
# identity OK

Test plan

  • CI passes (ruff, pyright, mypy, pytest).
  • Any consumer doing from agentex.lib.types.acp import CreateTaskParams continues to work without change.
  • from agentex.protocol.acp import CreateTaskParams works as the new canonical path.

Greptile Summary

This PR promotes the JSON-RPC envelope types (JSONRPCRequest/Response/Error) and ACP method-param types (CreateTaskParams, SendMessageParams, etc.) from agentex.lib.types.* into a new canonical agentex.protocol.* package, with back-compat shims at the old paths. The motivation is to enable a future slim agentex-sdk-client package that ships only pydantic + stainless-generated types without the full ADK runtime.

  • New canonical package agentex.protocol.{acp,json_rpc} created; both previous concern points from earlier review (missing RPC_SYNC_METHODS/PARAMS_MODEL_BY_METHOD in the shim, and the dropped model_utils.BaseModel config flags) are fully addressed in this version.
  • Back-compat shims at agentex.lib.types.{acp,json_rpc} re-export all symbols including module-level constants; identity equality verified (A is B).
  • Internal imports updated in 9 source files and 3 test files to use the new canonical path; CLI scaffold templates also updated.

Confidence Score: 5/5

This is a pure refactor — no logic changes, only type relocations with re-export shims at the old paths. Safe to merge.

The two issues flagged in a prior review pass (missing module-level constants in the shim, and the dropped model_utils.BaseModel config) are both fully resolved in this version. All symbols are re-exported at the old paths, the model_config is explicitly preserved in json_rpc.py with a clear comment, and every internal consumer has been updated to the new canonical path. No logic is changed anywhere.

No files require special attention. The shim files and canonical modules are consistent with each other.

Important Files Changed

Filename Overview
src/agentex/protocol/acp.py New canonical location for ACP param types; content is verbatim from the original lib/types/acp.py, using plain pydantic BaseModel (matching the original, which never used model_utils.BaseModel).
src/agentex/protocol/json_rpc.py New canonical location for JSON-RPC envelope types; explicitly preserves the model_config (from_attributes=True, populate_by_name=True) that the original model_utils.BaseModel applied, with a clear explanatory comment.
src/agentex/lib/types/acp.py Replaced with a back-compat shim; correctly re-exports all symbols including RPC_SYNC_METHODS and PARAMS_MODEL_BY_METHOD that were flagged missing in a prior review pass.
src/agentex/lib/types/json_rpc.py Replaced with a back-compat shim re-exporting all three JSON-RPC types from the canonical location; all existing callers continue to work.
src/agentex/protocol/init.py Package init with a clear docstring explaining the slim-safety contract and back-compat story; intentionally exports nothing at the package level.
src/agentex/lib/sdk/fastacp/base/base_acp_server.py Import-only change; all logic is untouched. Now pulls RPC types from the canonical agentex.protocol paths.
src/agentex/lib/cli/templates/default/project/acp.py.j2 CLI scaffold template updated to import from the new canonical path; since templates and SDK ship together, no version mismatch risk.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Canonical["agentex.protocol (new canonical)"]
        PA["agentex.protocol.acp\nRPCMethod, CreateTaskParams\nSendMessageParams, SendEventParams\nCancelTaskParams, RPC_SYNC_METHODS\nPARAMS_MODEL_BY_METHOD"]
        PJ["agentex.protocol.json_rpc\nJSONRPCRequest\nJSONRPCResponse\nJSONRPCError"]
    end

    subgraph Shims["agentex.lib.types (back-compat shims)"]
        LA["agentex.lib.types.acp\n(re-exports from protocol.acp)"]
        LJ["agentex.lib.types.json_rpc\n(re-exports from protocol.json_rpc)"]
    end

    subgraph Consumers["Internal consumers (updated)"]
        BS["base_acp_server.py"]
        TS["temporal_task_service.py"]
        WF["workflow.py"]
        IMPL["fastacp/impl/*.py"]
        TESTS["tests/"]
        TMPL["cli templates (*.j2)"]
    end

    PA -->|re-export| LA
    PJ -->|re-export| LJ

    BS --> PA
    BS --> PJ
    TS --> PA
    WF --> PA
    IMPL --> PA
    TESTS --> PA
    TESTS --> PJ
    TMPL --> PA
Loading

Reviews (3): Last reviewed commit: "fix(types): address Greptile review feed..." | Re-trigger Greptile

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>
Comment thread src/agentex/lib/types/acp.py
Comment thread src/agentex/protocol/json_rpc.py Outdated
max-parke-scale added a commit that referenced this pull request May 27, 2026
…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>
@max-parke-scale
Copy link
Copy Markdown
Contributor Author

@greptile review

@declan-scale
Copy link
Copy Markdown
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants