Skip to content

fix(agentic): cap-recovery de-noise, Gemini serial-tool workaround, translation-400 retry#16

Merged
ethancjackson merged 3 commits into
mainfrom
agent-run-hygiene
Jul 16, 2026
Merged

fix(agentic): cap-recovery de-noise, Gemini serial-tool workaround, translation-400 retry#16
ethancjackson merged 3 commits into
mainfrom
agent-run-hygiene

Conversation

@ethancjackson

Copy link
Copy Markdown
Collaborator

Summary — trace-review-driven hygiene (3 of 4 concerns; sandbox persistence held)

  • De-noise cap recovery: recovered cap hits now log one WARNING naming the cap; the residual ERROR spans come from ADK/OTEL internals stamping the closed span (documented in the runner docstring — not ours to override).
  • Gemini serial-tool-call workaround: build_adk_agent appends a call-tools-one-at-a-time instruction for Gemini-family models with tools, mitigating the proxy's parallel-batch function_response.name translation bug (reported to the proxy team). Claude untouched. The LiteLLM parallel_tool_calls=False knob was investigated but the proxy silently drops unrecognized params, so the deterministic prompt lever was chosen.
  • Narrow 400 retry: AgentPredictor.predict retries exactly once on a BadRequest carrying function_response.name (fresh session), so a translation-bug hit costs a retry, not a prediction.
  • Sandbox persistence: investigation only, no code — Ethan is consulting prior art. Key findings in the PR discussion below.

Verification (orchestrator-run)

pre-commit --all-files clean; 684 passed, 7 skipped. Live re-verify items (serial-instruction efficacy, real-400 signature match, residual span noise) queued for the next smoke.

Sandbox investigation (for the persistence decision — no changes shipped)

aieng-agents' CodeInterpreter.run_code creates a fresh E2B sandbox per call and kills it in finally — documented behavior, exactly matching the observed NameErrors. Per-run persistence would mean re-architecting that external tool or a run-scoped wrapper threaded through ADK's tool plumbing. arch is confirmed absent from the E2B template. Separately: DomainConfig.code_exec_preinstalled is only rendered into the adaptive agent's instruction — the stateless code agent is never told what's installed; that's the natural fix site once the hold lifts.

🤖 Generated with Claude Code

ethancjackson and others added 3 commits July 16, 2026 10:31
The graceful tool-iteration cap recovery worked in 6/6 completed runs of
the TSX smoke, yet each healthy recovered run surfaced ERROR-level spans,
making the runs look failed in trace review.

Investigation: the runner itself only logged the recovery at INFO — it
never emitted an ERROR. The ERROR level comes from ADK internals: ADK
raises LlmCallsLimitExceededError inside run_async, and ADK's own span
(plus the OpenInference ADK instrumentation) records that exception and
marks the span ERROR before it ever reaches our except clause. That status
is stamped inside the already-closed ADK span, so we cannot cleanly
override it from our code without reaching into ADK/OTEL internals.

Change what is ours: upgrade the recovery log from INFO to a single
WARNING that names the cap and states the recovery is expected, not a
failure — so operators have one authoritative, easy-to-grep signal. Document
the residual, unsuppressable ADK/OTEL ERROR span noise in the runner's class
docstring so it is not mistaken for a real failure.

Tests: assert a recovered capped run emits exactly one runner WARNING
carrying the cap value and no runner ERROR record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…around)

The Vector proxy's OpenAI<->Gemini translation drops
`function_response.name` on Gemini *parallel* tool-call batches, producing a
blocking 400 that costs the whole prediction. The proxy team has been
notified (see planning-docs/workshop-paper/proxy-bug-report-function-response-name.md
on the content branch); this is an interim mitigation.

build_adk_agent now appends a short instruction line telling Gemini-family
agents to call tools one at a time, applied only when the agent (a) is
Gemini-family and (b) actually has tools — a tool-free agent can never emit a
parallel batch. Claude parallel batches translate fine and are left untouched.

Investigated the LiteLLM `parallel_tool_calls=False` knob as an alternative:
ADK's LiteLlm forwards it to litellm.acompletion, but whether the Vector
proxy honours that OpenAI param is unverified — it silently drops others
(e.g. reasoning_effort; see planning-docs/vector-llm-proxy.md). The prompt
line is the deterministic, offline-testable lever, so we use that and keep it
clearly marked as a workaround to remove once the proxy is fixed.

Tests: the serial-call line is appended for a Gemini agent with tools (both
the bare-string and proxy-wrapped LiteLlm paths) and absent for a tool-free
Gemini agent and for a Claude agent with tools.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Defense in depth for the Vector-proxy OpenAI<->Gemini translation bug that
drops function_response.name on Gemini parallel tool-call batches, surfacing
as a blocking 400 that otherwise loses the whole prediction. The agent-factory
serial-tool-call workaround reduces how often the model emits a parallel batch,
but if one still slips through, AgentPredictor.predict now retries the whole
run exactly once. The runner's fresh_session_per_message default makes the
rerun start from a clean session.

The retry is kept strictly to that one error signature: the exception string
must contain "function_response.name" AND carry a bad-request signal (HTTP 400
status code or a BadRequestError-shaped class name). Any other failure — and a
second consecutive translation 400 — propagates unchanged.

Tests: one translation 400 retries once and succeeds; a second consecutive
translation 400 re-raises after the single retry; an unrelated 400 and a
marker-bearing non-400 error both propagate without a retry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ethancjackson ethancjackson merged commit 6c00832 into main Jul 16, 2026
2 checks passed
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.

1 participant