Skip to content

release: 0.12.0#382

Open
stainless-app[bot] wants to merge 10 commits into
mainfrom
release-please--branches--main--changes--next
Open

release: 0.12.0#382
stainless-app[bot] wants to merge 10 commits into
mainfrom
release-please--branches--main--changes--next

Conversation

@stainless-app
Copy link
Copy Markdown
Contributor

@stainless-app stainless-app Bot commented May 30, 2026

Automated Release PR

0.12.0 (2026-05-30)

Full Changelog: v0.11.5...v0.12.0

Features

  • api: add cleaned_at field to task response types (38ed338)
  • deps: bump openai-agents to >=0.14.3 for scale-sandbox oai_agents adapter (#375) (e1b31d9)
  • examples: OpenAI Agents SDK local-sandbox tutorials (sync + async + temporal) (#377) (a66d239)
  • lib: expose data_converter kwarg on AgentexWorker and Temporal client APIs (#372) (d04624e)

Bug Fixes

  • tutorials: restore tutorial CI deps after agentex-sdk 0.11.5 (pytest + debugpy) (#379) (0a2418c)

Performance Improvements

  • tracing: span queue linger + per-loop httpx keepalive (#362) (feec842)

Chores

  • back-merge release 0.11.5 into next (#381) (ab5a7d9)
  • deps: drop unused runtime deps and exclude tests from wheel (#367) (f4303d1)

Refactors

  • types: promote protocol types to agentex.protocol.* (#371) (6f1c14f)

This pull request is managed by Stainless's GitHub App.

The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.

For a better experience, it is recommended to use either rebase-merge or squash-merge when merging this pull request.

🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions

Greptile Summary

This release PR (0.12.0) bundles several independent improvements: per-event-loop httpx keepalive in the tracing layer (replacing the previous "disable keepalive entirely" workaround), a new data_converter kwarg threaded through the Temporal client/worker/ACP stack for OpenAI-Agents-plugin compatibility, promotion of protocol types to agentex.protocol.*, and new OpenAI Agents SDK local-sandbox tutorial examples.

  • Tracing performance (span_queue.py, processors): the drain loop now lingers up to 100 ms to coalesce spans into larger batches; httpx keepalive is re-enabled via per-event-loop WeakKeyDictionary caches in both AgentexAsyncTracingProcessor and SGPAsyncTracingProcessor, eliminating the TLS-handshake-per-span overhead while preserving cross-loop safety.
  • Temporal data_converter kwarg (worker.py, temporal_client.py, fastacp.py): callers can now pass a fully assembled DataConverter (e.g. one that composes OpenAIPayloadConverter with a custom codec); mutual exclusion with payload_codec is validated at both the config layer (TemporalACPConfig) and get_temporal_client.
  • Protocol type promotion (agentex.protocol.*): RPCMethod, CreateTaskParams, etc. are moved to a slim pydantic-only package; old import paths are kept as back-compat shims; cleaned_at is added to all task response types.

Confidence Score: 4/5

Safe to merge; the core tracing and Temporal changes are well-tested and logically sound.

The keepalive-with-id() approach in TracingModule works correctly in the primary Temporal-worker deployment (long-lived loop, id never changes), but the safety comment overstates the guarantee for sync-ACP contexts where loop identity can recycle — the processors were correctly updated to WeakKeyDictionary while this path was not. All other changes (retry logic, data_converter plumbing, protocol promotion, lazy debugpy) are straightforward and fully tested.

src/agentex/lib/adk/_modules/tracing.py — the id(loop)-based cross-loop guard is inconsistent with the WeakKeyDictionary approach adopted in the processors and may not hold if the module is ever used outside long-lived Temporal workers.

Important Files Changed

Filename Overview
src/agentex/lib/core/tracing/span_queue.py Adds linger-based batch coalescing, bounded queue with drop counting, and per-processor retry on transient 429/5xx failures. Logic is sound: task_done() tracking, retry-attempts cap, and WeakKeyDictionary patterns are all correct.
src/agentex/lib/adk/_modules/tracing.py Enables httpx keepalive (0 → 20) for the TracingModule; relies on id(loop) for cross-loop safety, unlike the processors which were updated to WeakKeyDictionary. Low practical risk in Temporal workers but not consistent with the processor changes.
src/agentex/lib/core/tracing/processors/agentex_tracing_processor.py Migrates from a single shared client to a per-event-loop WeakKeyDictionary cache with keepalive enabled; lazy construction on first property access; well-covered by new tests.
src/agentex/lib/core/tracing/processors/sgp_tracing_processor.py Same per-loop WeakKeyDictionary pattern as AgentexAsyncTracingProcessor; also moves the disabled guard into _get_client, eliminating the None client attribute.
src/agentex/lib/core/clients/temporal/temporal_client.py Adds data_converter kwarg plumbing throughout TemporalClient; correctly delegates to get_temporal_client which already validates mutual exclusion with payload_codec.
src/agentex/lib/core/temporal/workers/worker.py Exposes data_converter on AgentexWorker and get_temporal_client; validates mutual exclusion with payload_codec and special-cases the OpenAIAgentsPlugin path correctly.
src/agentex/protocol/acp.py Promotes protocol types to the canonical agentex.protocol.* namespace; old locations become thin re-export shims for back-compat.
src/agentex/lib/utils/debug.py Makes the debugpy import lazy so it is only required when AGENTEX_DEBUG_ENABLED=true; fixes module-not-found breakage after ipykernel was removed.
src/agentex/types/task.py Adds optional cleaned_at field to Task; same addition applied consistently across all four task response types.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Tracing["Tracing (per-event-loop keepalive)"]
        SP[Span enqueued] --> SQ[AsyncSpanQueue\nlinger_ms=100]
        SQ --> DL{Drain loop\nbatch coalesce}
        DL -->|START batch| PAR[asyncio.gather]
        DL -->|END batch| PAR
        PAR --> AGX[AgentexAsyncTracingProcessor\nWeakKeyDict loop→client]
        PAR --> SGP[SGPAsyncTracingProcessor\nWeakKeyDict loop→client]
        AGX -->|transient 429/5xx| RE[_reenqueue\nattempts+1]
        SGP -->|transient 429/5xx| RE
        RE --> SQ
    end

    subgraph Temporal["Temporal data_converter plumbing"]
        WC[TemporalACPConfig\ndata_converter OR payload_codec] --> FA[FastACP.create]
        FA --> TA[TemporalACP]
        TA --> TC[TemporalClient.create]
        TC --> GTC[get_temporal_client]
        GTC -->|data_converter set| CC[Client.connect\ndata_converter=dc]
        GTC -->|no openai plugin| PD[pydantic_data_converter\n+ optional codec]
        GTC -->|openai plugin + data_converter| CC
        AW[AgentexWorker\ndata_converter] --> GTC
    end
Loading

Comments Outside Diff (1)

  1. src/agentex/lib/adk/_modules/tracing.py, line 67-81 (link)

    P2 id() reuse undermines keepalive safety guarantee

    The comment here claims "Cross-loop safety is preserved by rebuilding the client whenever loop_id changes", but it uses id(loop) rather than a WeakKeyDictionary. CPython recycles freed object memory, so after a loop is GC'd, a new loop can be allocated at the same address and id(loop) == self._bound_loop_id — causing the old, dead loop's keepalive connection pool to be reused in the new loop, which is exactly the "bound to a different event loop" error the change is meant to prevent.

    Both AgentexAsyncTracingProcessor and SGPAsyncTracingProcessor were updated in this PR to use WeakKeyDictionary precisely to avoid this; TracingModule still uses the id-based approach. In practice the risk is lower here because TracingModule is used inside long-lived Temporal workers (one loop per process), but the safety guarantee stated in the comment is incomplete for sync-ACP/streaming scenarios.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/agentex/lib/adk/_modules/tracing.py
    Line: 67-81
    
    Comment:
    **id() reuse undermines keepalive safety guarantee**
    
    The comment here claims "Cross-loop safety is preserved by rebuilding the client whenever loop_id changes", but it uses `id(loop)` rather than a `WeakKeyDictionary`. CPython recycles freed object memory, so after a loop is GC'd, a new loop can be allocated at the same address and `id(loop) == self._bound_loop_id` — causing the old, dead loop's keepalive connection pool to be reused in the new loop, which is exactly the "bound to a different event loop" error the change is meant to prevent.
    
    Both `AgentexAsyncTracingProcessor` and `SGPAsyncTracingProcessor` were updated in this PR to use `WeakKeyDictionary` precisely to avoid this; `TracingModule` still uses the id-based approach. In practice the risk is lower here because `TracingModule` is used inside long-lived Temporal workers (one loop per process), but the safety guarantee stated in the comment is incomplete for sync-ACP/streaming scenarios.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Cursor Fix in Claude Code Fix in Codex

Fix All in Cursor Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/agentex/lib/adk/_modules/tracing.py:67-81
**id() reuse undermines keepalive safety guarantee**

The comment here claims "Cross-loop safety is preserved by rebuilding the client whenever loop_id changes", but it uses `id(loop)` rather than a `WeakKeyDictionary`. CPython recycles freed object memory, so after a loop is GC'd, a new loop can be allocated at the same address and `id(loop) == self._bound_loop_id` — causing the old, dead loop's keepalive connection pool to be reused in the new loop, which is exactly the "bound to a different event loop" error the change is meant to prevent.

Both `AgentexAsyncTracingProcessor` and `SGPAsyncTracingProcessor` were updated in this PR to use `WeakKeyDictionary` precisely to avoid this; `TracingModule` still uses the id-based approach. In practice the risk is lower here because `TracingModule` is used inside long-lived Temporal workers (one loop per process), but the safety guarantee stated in the comment is incomplete for sync-ACP/streaming scenarios.

Reviews (1): Last reviewed commit: "release: 0.12.0" | Re-trigger Greptile

max-parke-scale and others added 10 commits May 26, 2026 17:48
)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
Co-authored-by: Declan Brady <declan.brady@scale.com>
Co-authored-by: Michael Chou <michael.chou@scale.com>
…ts adapter (#375)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nc + temporal) (#377)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants