Skip to content

feat(interactions): add automaticContinuation to Interactions.CreateAsync in the .NET SDK - #139

Open
copybara-service[bot] wants to merge 1 commit into
mainfrom
copybara/995657094
Open

copybara-service[bot] wants to merge 1 commit into
mainfrom
copybara/995657094

Conversation

@copybara-service

Copy link
Copy Markdown

feat(interactions): add automaticContinuation to Interactions.CreateAsync in the .NET SDK

Foreground Interactions.CreateAsync() calls are capped per request by the server (32k output tokens unary, 64k streaming) and stop with status="requires_continuation" plus a continuation_token. IInteractions.CreateAsync and Interactions.CreateAsync now accept bool? automaticContinuation = null (default true) and TimeSpan? timeout = null and chain continuation hops client-side so a plain CreateAsync() returns the full decode.

Request chaining:

  • Each hop after the first sends the continuation_token from the previous hop. With Store enabled (default) it sets PreviousInteractionId to the hop that issued the token and omits Input; with Store = false it resends the original Input.
  • Unary foreground model calls default GenerationConfig.MaxOutputTokens to 65536 when unset without mutating caller request objects (streaming uses the server's 64k default). Explicit values are never overridden.
  • Stops on completed, incomplete (token preserved for manual resumption), requires_action, failed, cancelled, a missing token, or a repeated token. Skipped for agent requests and Background = true; Background = true with automaticContinuation: false throws ArgumentException.

Unary (Stream = false) merging:

  • Hops are merged into one Interaction: Steps concatenated in hop order (leading user_input steps dropped on later hops), Usage counts and per-modality breakdowns summed across hops with Usage.InitialInputTokens taken from hop 1, Id / Status / ContinuationToken / Updated from the final hop, and PreviousInteractionId / Created / Input from hop 1.
  • ContinuationIds lists every hop id in order, de-duplicated with any server-provided ids. Convenience output properties (OutputText, etc.) are recomputed across hop boundaries.

Streaming (Stream = true):

  • Events from all hops are yielded verbatim on EventStream<InteractionSseEvent> (await foreach and Next()). Consumers should treat interaction.completed with status="requires_continuation" as intermediate.
  • Mid-hop transport drops or premature EOF resume from the latest interaction.status_update checkpoint token when no content was yielded since that checkpoint (up to 3 no-progress reconnects). SSE error events are terminal; HTTP status retries stay with RetryConfig.

Timeouts and errors:

  • Per-call TimeSpan? timeout bounds the whole CreateAsync() call across hops via CallDeadline, while HttpClient.Timeout, RetryConfig, and CancellationToken apply per hop / immediate cancellation.
  • A failure or timeout after progress throws ContinuationInterruptedException (InnerException set to the underlying error, or ContinuationTimeoutException on deadline expiry) carrying PartialInteraction, ContinuationToken, InteractionId, and ContinuationIds so billed work is preserved for manual resumption. First-hop failures propagate unchanged.

Opt-out:

  • automaticContinuation: false returns the single server response untouched (no budget injection, no merging) and emits a one-time Trace.TraceWarning if it stops at requires_continuation without a caller-supplied continuation_token.

Tests: Google.GenAI.Tests/InteractionsContinuationConformanceTest.cs (52 conformance cases) and Google.GenAI.Tests/InteractionsContinuationTest.cs (whole-call timeout with fake clock, non-mutation, warning, and EventStream.Next() tests).

@copybara-service
copybara-service Bot requested a review from a team October 28, 2026 04:29
…sync in the .NET SDK

Foreground `Interactions.CreateAsync()` calls are capped per request by the server (32k output tokens unary, 64k streaming) and stop with `status="requires_continuation"` plus a `continuation_token`. `IInteractions.CreateAsync` and `Interactions.CreateAsync` now accept `bool? automaticContinuation = null` (default `true`) and `TimeSpan? timeout = null` and chain continuation hops client-side so a plain `CreateAsync()` returns the full decode.

Request chaining:
- Each hop after the first sends the `continuation_token` from the previous hop. With `Store` enabled (default) it sets `PreviousInteractionId` to the hop that issued the token and omits `Input`; with `Store = false` it resends the original `Input`.
- Unary foreground model calls default `GenerationConfig.MaxOutputTokens` to 65536 when unset without mutating caller request objects (streaming uses the server's 64k default). Explicit values are never overridden.
- Stops on `completed`, `incomplete` (token preserved for manual resumption), `requires_action`, `failed`, `cancelled`, a missing token, or a repeated token. Skipped for agent requests and `Background = true`; `Background = true` with `automaticContinuation: false` throws `ArgumentException`.

Unary (`Stream = false`) merging:
- Hops are merged into one `Interaction`: `Steps` concatenated in hop order (leading `user_input` steps dropped on later hops), `Usage` counts and per-modality breakdowns summed across hops with `Usage.InitialInputTokens` taken from hop 1, `Id` / `Status` / `ContinuationToken` / `Updated` from the final hop, and `PreviousInteractionId` / `Created` / `Input` from hop 1.
- `ContinuationIds` lists every hop id in order, de-duplicated with any server-provided ids. Convenience output properties (`OutputText`, etc.) are recomputed across hop boundaries.

Streaming (`Stream = true`):
- Events from all hops are yielded verbatim on `EventStream<InteractionSseEvent>` (`await foreach` and `Next()`). Consumers should treat `interaction.completed` with `status="requires_continuation"` as intermediate.
- Mid-hop transport drops or premature EOF resume from the latest `interaction.status_update` checkpoint token when no content was yielded since that checkpoint (up to 3 no-progress reconnects). SSE `error` events are terminal; HTTP status retries stay with `RetryConfig`.

Timeouts and errors:
- Per-call `TimeSpan? timeout` bounds the whole `CreateAsync()` call across hops via `CallDeadline`, while `HttpClient.Timeout`, `RetryConfig`, and `CancellationToken` apply per hop / immediate cancellation.
- A failure or timeout after progress throws `ContinuationInterruptedException` (`InnerException` set to the underlying error, or `ContinuationTimeoutException` on deadline expiry) carrying `PartialInteraction`, `ContinuationToken`, `InteractionId`, and `ContinuationIds` so billed work is preserved for manual resumption. First-hop failures propagate unchanged.

Opt-out:
- `automaticContinuation: false` returns the single server response untouched (no budget injection, no merging) and emits a one-time `Trace.TraceWarning` if it stops at `requires_continuation` without a caller-supplied `continuation_token`.

Tests: `Google.GenAI.Tests/InteractionsContinuationConformanceTest.cs` (52 conformance cases) and `Google.GenAI.Tests/InteractionsContinuationTest.cs` (whole-call timeout with fake clock, non-mutation, warning, and `EventStream.Next()` tests).
PiperOrigin-RevId: 995657094
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant