Skip to content

Keep a cascaded reply's tool calls and results in the conversation - #276

Merged
MikeAlhayek merged 3 commits into
CrestApps:mainfrom
giannik:fix/cascade-keeps-tool-calls
Oct 19, 2026
Merged

MikeAlhayek merged 3 commits into
CrestApps:mainfrom
giannik:fix/cascade-keeps-tool-calls

Conversation

@giannik

@giannik giannik commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Fixes #274

A cascaded realtime session now keeps a reply's tool calls and their results in its history, with the reply's text,
so the next turn knows what was looked up.

Changes

  • CascadedRealtimeSession.RunTurnAsync keeps every update the chat leg streams for a reply, and adds the reply's
    messages to the history in place of its text alone: the tool calls, their results, then the text.
    • This happens on completion and on interruption alike: what was said before an interruption stays, as before, and
      so does what was looked up for it.
    • A reply marked ExcludeFromConversation still stays out of the history.
  • A call an interruption left unanswered is left out. A reply cancelled between asking for a tool and getting its
    answer leaves a FunctionCallContent with no FunctionResultContent. Providers that pair each call with its result
    (Anthropic among them) refuse a conversation holding one.
  • Docs:
    • core/realtime-voice.md, the chat leg: what the history keeps;
    • a changelog entry under cascaded realtime deployments.

Tests

In CascadedRealtimeSessionTests:

  • Session_KeepsTheToolCallsAndTheirResultsInHistoryForTheNextTurn: the second turn's request holds the call and
    its result, in order, before the answer built on them.
  • Session_LeavesOutAToolCallTheInterruptionLeftUnanswered: a reply interrupted after its call leaves no
    FunctionCallContent in the next request.

The fake chat client can now emit any update, and can hold only its first reply. The existing tests use it as before.

On our fork, the realtime tests pass (189), and so does the full test project, except the 2 known failures under a
Greek locale (EmbeddedResourceAIProfileTemplateProviderTests, OpenXmlIngestionDocumentReaderTests), which fail
the same way without this change.

Verified on a live call

The call ran through CrestApps.OrchardCore's voice agent on Telnyx, on a cascade of Deepgram nova-3,
claude-haiku-5-5 and ElevenLabs eleven_flash_v2_5, with a booking profile.

  • Before: the model retracted a correct availability answer on the next turn ("I didn't actually check our
    booking calendar").
  • After:
    • "Next Tuesday afternoon" led to listCalendars and getAvailableSlots, then a choice of dates.
    • "Give me the first available slot" was answered from the slots already found, without asking the calendar again.
    • The name and email were confirmed, then bookAppointment ran, and the booking was confirmed.

🤖 Generated with Claude Code

A cascaded realtime session kept only the text of each reply in its history.
The tool calls the chat leg made for the reply, and their results, were
dropped, so the next turn was written without them:

- a booking lost the slots it had found between turns;
- a model that saw an answer it had no record of checking took it back. On a
  CrestApps.OrchardCore browser session with claude-haiku-5-5 as the chat leg,
  the reply after a correct availability answer (backed by two tool calls)
  was "I didn't actually check our booking calendar, so the afternoon times I
  mentioned for Tuesday weren't confirmed."

The session now keeps everything the chat leg returned for a reply (the tool
calls, their results, then the text) as the reply's messages, in order. A
reply interrupted between asking for a tool and getting its answer leaves a
call with no result; providers that pair each call with its result
(Anthropic among them) refuse such a conversation, so that call is left out.
An out-of-band reply still stays out of the history.

Tests: Session_KeepsTheToolCallsAndTheirResultsInHistoryForTheNextTurn and
Session_LeavesOutAToolCallTheInterruptionLeftUnanswered (the fake chat client
can now emit any update, and hold only its first reply). Realtime tests
189/189.
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts. Please resolve those before requesting a review.

Keeps both cascade changelog entries after CrestApps#277.
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts. Please resolve those before requesting a review.

@MikeAlhayek
MikeAlhayek merged commit 4d1f34b into CrestApps:main Oct 19, 2026
9 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.

A cascaded realtime session forgets its tool calls between turns

2 participants