Repository navigation
feat(openai_agents): expose real usage, response_id, plumb previous_response_id, opt-in prompt_cache_key for stateful responses and prompt caching - #335
Merged
Conversation
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
Four changes to the streaming Responses API path in
temporal_streaming_model.py.My goal is to:
usage)prompt_cache_key)response_id,previous_response_id)Capture real
usagefromResponseCompletedEvent.response.usageand surface in the span'soutputdict.TemporalStreamingModel.get_responsewas constructing a zero-filledUsageand discardingevent.response.usage. It now reads usage off the streaming protocol and falls back to zeros only on the error path (stream ends without a completed event).The
len(''.join(reasoning_contents)) // 4reasoning-tokens estimator is dropped — the real value arrives in the API response.The
streaming_model_get_responsespan now carries{input_tokens, output_tokens, total_tokens, cached_input_tokens, reasoning_tokens}so traces show cache-hit rate.Return real
response_idcaptured fromResponseCompletedEvent.response.idinstead of the previously fabricatedf"resp_{uuid.uuid4().hex[:8]}".OpenAI Agents SDK reads this field for
previous_response_idchaining (run.py:145), exposes it asRunResult.last_response_id(result.py:108), and writes it into traces (tracing/span_data.py:164). A client-side UUID has never been issued by any server, so any caller that picks it up and tries to chain (the documented use case) gets a 400 "response not found".Latent since this file was added (commit
2f2a6ed7) because nothing in the codebase chainsprevious_response_idyet.Forward
previous_response_id,conversation_id, andpromptfrom the SDK kwarg.The SDK's abstract
Model.get_responsehasprevious_response_id,conversation_id,promptas required keyword-only params; the SDK threadsprevious_response_iddown through_ServerConversationTrackerwhen callers set it onRunner.run/RunConfig.Our implementation declared
**kwargs # noqa: ARG002and silently swallowed all three. Callers who used the official chaining API got no stateful behavior and no error. Replaced with explicit named params.This change is not necessarily required we need to pass
previous_response_idbut we could be doing that viaextra_args. That said, the silent behavior is sketch. I could be convinced that the right thing to here is to raise an exception when we see them inkwargsand expect them inextra_argsbut this felt less dangerous and more intuitive to the caller.Plumb
prompt_cache_keytoresponses.createas an opt-in parameter.Callers set it via
model_settings.extra_args["prompt_cache_key"]. We do not auto-inject a default. I don't trustprompt_cache_keyto be standard across all OpenAI-compatible endpoints.When unset, the parameter resolves to
NOT_GIVENand is omitted from the request body entirely.Test plan
All 38 tests in
test_streaming_model.pypass locally:test_usage_captured_from_completed_eventtest_usage_falls_back_when_no_completed_eventtest_usage_emitted_in_span_outputtest_response_id_captured_from_completed_eventtest_response_id_is_none_when_no_completed_event— guards against the fake-UUID footguntest_prompt_cache_key_not_sent_by_default— verifiesNOT_GIVENfallback for non-OpenAI compattest_prompt_cache_key_forwarded_when_opted_intest_previous_response_id_not_sent_by_default— verifiesNOT_GIVENfallbacktest_previous_response_id_forwarded_via_sdk_kwarg— verifies the SDK's official chaining API now workstest_conversation_id_and_prompt_accepted_but_not_forwarded— verifies SDK contract compliance without surface-area expansiontask_idkwargs)Why these changes together
The motivating workstream (downstream of this PR) is a stateful-Responses-API migration for ST&S agents to chain via
previous_response_idfor 40–80% better cache utilization on reasoning models. That migration needs:Usageto measure cache hit rate before/after.response_idto actually pass back asprevious_response_id.previous_response_idactually reachingresponses.createinstead of being silently swallowed.prompt_cache_keyfor callers who want it without forcing it on everyone.Once a release containing these changes lands and
aimi-scalebumps itsagentex-sdkpin, the runtime monkey-patch inst_s/*/agentex_usage_patch.pyand the corresponding_apply_agentex_usage_patch()calls in eachrun_worker.pycan be deleted.Greptile Summary
Four correctness fixes to the Responses API streaming path: (1) real
Usageis now read fromResponseCompletedEvent.response.usageand surfaced in the span; (2)response_idis the server-issued value instead of a fabricated UUID that would 400 on any chained turn; (3)previous_response_id,conversation_id, andpromptare explicitly accepted and forwarded rather than silently swallowed by**kwargs; (4)prompt_cache_keyis opt-in viaextra_argsand resolves toNOT_GIVENby default.Confidence Score: 5/5
Safe to merge — all changes are correctness improvements with safe fallback paths and comprehensive test coverage.
No P0 or P1 findings. All four changes fix real latent bugs (fake UUID chaining, zero usage, silently dropped SDK params). Fallbacks are explicit (zeros, None, NOT_GIVEN). The removal of **kwargs is intentional and acknowledged. Tests cover every new code path.
No files require special attention.
Important Files Changed
usageandresponse_idfromResponseCompletedEvent, forwardsprevious_response_id/conversation_id/promptas explicit kwargs (replacing**kwargsswallowing), and plumbs opt-inprompt_cache_key— all correctness improvements with safe fallbacks.task_idkwarg from 28 existing test calls.Sequence Diagram
sequenceDiagram participant SDK as OpenAI Agents SDK participant TSM as TemporalStreamingModel participant OAI as OpenAI responses.create participant Redis as Redis (streaming) SDK->>TSM: get_response(..., previous_response_id, conversation_id, prompt) Note over TSM: pops prompt_cache_key from extra_args (NOT_GIVEN default) TSM->>OAI: responses.create(stream=True, previous_response_id, conversation, prompt, prompt_cache_key, ...) loop stream events OAI-->>TSM: ResponseTextDeltaEvent / etc. TSM->>Redis: stream partial content end OAI-->>TSM: ResponseCompletedEvent(response.id, response.usage) Note over TSM: captured_response_id = response.id<br/>captured_usage = response.usage TSM-->>SDK: ModelResponse(response_id=captured_response_id, usage=real_usage) Note over SDK: RunResult.last_response_id = captured_response_id<br/>(used for next turn's previous_response_id)Reviews (7): Last reviewed commit: "Merge branch 'main' into dpeticolas/prom..." | Re-trigger Greptile