Skip to content

AgentCore 0.14.2

Choose a tag to compare

@github-actions github-actions released this 07 Oct 01:41
· 51 commits to main since this release
ac94faa

Fixed

  • A streamed call that completes with nothing in it — no visible text, no tool call, no reasoning and no usage — now raises LLMEmptyCompletion instead of being assembled into a valid empty LLMResponse. is_empty_completion has always named "an empty stream" among the cases it recovers, routing it through is_retriable_with_fallback to a same-key resample and then a chain advance; the raise is what was missing, so the blank response reached the loop as an answer. Because a reply with no tool call is shaped exactly like "the model chose to stop talking", under no_tool_behavior="stop" it ENDED THE RUN and recorded stopped_by="no_tool", which reads as a clean finish while discarding every turn of work already done. Measured on ApodexHarness's 2026-10-06 GDPval batch over llm-hub, 10 of 19 streamed trials died this way — six within five minutes, one on turn 1 after 37s — with usage absent and backfilled as estimated zeros. The new error's wording matches the existing _EMPTY_COMPLETION_PATTERNS, so classification and retry need no change, and LLMEmptyCompletion is exported from agent_core.errors.

    The predicate is deliberately "nothing whatsoever", not "no visible text": a real 0-token reply still reports usage and a reasoning-only stream is the runaway guard's business, so both keep flowing to the handlers that already reason about them. One behaviour change follows for consumers: a stream whose ONLY content was a tool-call delta that never received a function name used to return an empty response after dropping the unusable call (test_llm_runtime_nameless_tool_calls), and now raises. That module's own docstring described the remaining half of the failure — the run "ends looking like an ordinary empty submission rather than the infrastructure fault it is" — which this completes.

    As a second line, run_agent_loop no longer lets a blank reply reach the no-tool exit at all: a response with no tool call, no text and no usage is resampled up to LoopConfig.empty_completion_max_retries (new, default 2) without spending a logical turn, and if it stays blank the run stops under its own stopped_by="empty_completion" rather than no_tool. This covers what the stream guard cannot see — a non-streamed reply, or one a product wrapper rebuilt. Consumers reading stopped_by should expect the new value; those that treat any non-empty stopped_by generically need no change.

    Empty-response resampling now checks the original response with the same predicate as stream assembly, before history insertion and response observers. Reasoning, opaque signed blocks, usage (including reported zeros), and structured refusal details are preserved. Blank and whitespace-only replies never enter retry history, and a non-empty response resets the consecutive-empty retry budget so separate failures each get their own recovery allowance.