Commit graph

17 commits

Author SHA1 Message Date
Yiorgis Gozadinos
fa0c4a4f50
Treat an unfinished history tail as a continuation too
deferred_tool_results may arrive with a non-empty prompt, so the absence of a
prompt cannot be the only test for a resumption. Reproduced: resuming a live
question that way replaced its search result with the earlier-question notice
while the deferred result arrived alongside it.

_is_resumption accepts either signal — no prompt, or a history ending with a
request the model has not answered or a response whose tool calls have no
returns. A settled history ends with the previous answer, so a genuinely new
question is unaffected.

A new prompt on top of an unanswered tail is ambiguous and now counts as a
continuation: compacting costs the answer if it is one, while not compacting
only costs a larger request.
2026-08-13 13:00:01 +03:00
Yiorgis Gozadinos
8eef0b3734
Leave a resumed run's evidence alone
A run carrying no prompt is continuing a question rather than asking one:
pydantic-ai resumes that way for deferred tool results, interruptions and
suspended responses. len(ctx.messages) then counts the live question's own
messages, so its search result was replaced by the earlier-question notice and
the model was asked to answer with the evidence removed. Reproduced: resuming
with an in-flight history left a notice where the only evidence was.

Switch compaction off for the whole run when ctx.prompt is None. The absence of
a prompt is the signal rather than the message layout — the resume shapes differ
from each other, and deriving the boundary from layout is what broke this to
begin with.
2026-08-13 13:00:01 +03:00
Yiorgis Gozadinos
d9bd3a701f
Take the compaction turn boundary from the run, not the message shape 2026-08-13 12:59:22 +03:00
Yiorgis Gozadinos
5d76461b9f
Trim earlier-question evidence off the wire, keeping the images
_compact_old_tool_returns ran in before_model_request, whose result core
assigns back onto ctx.state.message_history, so the trim reached
all_messages() and every host that persists a thread. It now runs in
wrap_model_request, which operates on a detached list: the model sees the
trimmed history, the host keeps what it retrieved.

Content attached to a ToolReturn arrives as its own UserPromptPart in the
same ModelRequest as the ToolReturnPart, so the turn-boundary scan read an
image-bearing search result as a new user turn and discarded evidence
retrieved earlier in the same turn. _is_user_turn() now requires a request
with no tool returns.

Page images on a replaced return stay. Dropping them with their text bounds
context growth, but a follow-up about a figure already shown ("what colour
is that box?") carries no terms that could retrieve it again: measured on
gemma4-26b against two ORB figures, removing the image turned both answers
into "I cannot find enough information", and keeping it answers correctly.
Bounding that growth needs to preserve cited figures, which is a separate
change.

The replacement notice no longer claims citations remain in state;
_clear_invocation_state has cleared them by then.
2026-08-13 12:59:22 +03:00
Yiorgis Gozadinos
c137305468
Scope the limit notice and spend the cite window on own turns only
The request-limit notice said only the cite tool remained available, but
chat registers rag and analysis in one agent, so exhausting analysis
claimed rag_search was gone too. Scoped to the capability's own tools.

The cite window was counted over every model request once loaded, so
turns spent on another capability expired it before the model was ever
placed where citing was the obvious move. Count only requests whose
preceding response called one of this capability's tools; engagement is
also the only thing that can loop, which is all the bound guards against.

Also: _count_tool_traffic returns a named tuple rather than four bare
ints, and counts failures only for this capability's tools, so host-tool
retries and output-validation retries no longer read as its failures.
2026-07-30 19:14:14 +03:00
Yiorgis Gozadinos
27ed0b3bb3
Send analysis to the sandbox when only its search budget is spent
The notice told the model to answer from what it had the moment
qa.max_searches ran out, while up to 15 code executions remained and
in-code search() does not count against that budget. It now names the
spent tool and points at whichever evidence tool still has budget,
falling back to answer-and-cite only when none do.

Also count RetryPromptPart in n_failed_tools: _cite rejects with
ModelRetry, so a run whose every cite attempt was refused reported zero
failures. And note that n_requests is the run's request count, which
tracks a capability's own budget only while it stays loaded.
2026-07-30 18:17:26 +03:00
Yiorgis Gozadinos
721acbcf38
Address review on PR #524
- _budget_notice no longer names the cite tool after prepare_tools has
  withdrawn it; the post-grace state gets the plain no-tools text back.
- Split search-budget rejections from any failed tool call: the code tool
  raises ToolFailed for every error in model-written Python, so
  budget_spent was true for a ZeroDivisionError.
- docs/capabilities/rag.md described the old single-turn removal.
- Drop the rationale clause from the CHANGELOG entry.
2026-07-30 15:50:06 +03:00
Yiorgis Gozadinos
68238f0b6a
Name the readlines workaround when sandbox code iterates a file
50 executions across 40 cases in an 822-case run died on
'_io.TextIOWrapper' object is not iterable, and those cases scored 37.5%
judged against 60.1% and cited 12.5% against 55.2%.
2026-07-30 14:56:17 +03:00
Yiorgis Gozadinos
5752f61f2c
Recover chunk ids mistyped from search results
Resolve a cited id that misses exactly to the nearest id the run
retrieved, above a 0.75 similarity cutoff.
2026-07-30 14:55:43 +03:00
Yiorgis Gozadinos
685a7c393d
Keep the cite tool past a capability's request limit 2026-07-30 14:55:08 +03:00
Yiorgis Gozadinos
f13a3fb677
Report tool failures with ToolFailed 2026-07-27 18:26:42 +03:00
Yiorgis Gozadinos
b8dcb066dc
Port cite partial-success feedback to capabilities 2026-07-24 15:27:19 +03:00
Yiorgis Gozadinos
175929f23a
Improve coverage 2026-07-24 15:26:18 +03:00
Yiorgis Gozadinos
77585ef26a
fix per-question capability limits and tool isolation 2026-07-24 15:26:18 +03:00
Yiorgis Gozadinos
43c17a6777
fix capability execution limits and chat loading 2026-07-24 15:26:17 +03:00
Yiorgis Gozadinos
22ffd5e92e
Add max retries=3 back 2026-07-24 15:26:17 +03:00
Yiorgis Gozadinos
9deb1f2bd4
replace haiku.skills with native Pydantic AI capabilities 2026-07-24 15:26:17 +03:00