Skip to content

Commit 2af7fae

Browse files
MarkDaoustcopybara-github
authored andcommitted
feat(interactions): add automaticContinuation to Interactions.create in the Java SDK
Foreground `Interactions.create()` calls are capped per request by the server (32k output tokens unary, 64k streaming) and stop with `status="requires_continuation"` plus a `continuation_token`. `Interactions.create`, `AsyncInteractions.create`, `CreateInteractionRequest`, and `CreateInteractionRequestBuilder` now support `automaticContinuation` (default `true`) and chain continuation hops client-side so a plain `create()` 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 `previous_interaction_id` to the hop that issued the token and omits `input`; with `store=false` it resends the original `input`. - Unary foreground model calls default `generation_config.max_output_tokens` 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 `CreateAgentInteraction` requests and `background=true`; `background=true` with `automaticContinuation=false` throws `IllegalArgumentException`. 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. Output convenience helpers (`outputText`, etc.) are recomputed across hop boundaries. Streaming (`stream=true`): - Events from all hops are yielded verbatim via `ContinuationEventStream` (sync and async). Consumers should treat `interaction.completed` with `status="requires_continuation"` as intermediate. - Mid-hop transport drops (`IOException` / `UncheckedIOException`) 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 `timeout(Duration)` on `CreateInteractionRequest` / builders bounds the whole `create()` call across hops via `CallDeadline`, while client-level `HttpOptions.timeout` and `RetryConfig` apply per hop. - A failure or timeout after progress throws `ContinuationInterruptedException` (`getCause()` chained) 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 logs a one-time `WARNING` if it stops at `requires_continuation` without a caller-supplied `continuation_token`. Tests: `InteractionContinuationConformanceTest` (52 sync + 28 async streaming conformance cases) and `InteractionContinuationTest` (whole-call timeout, builder immutability, and warning tests). PiperOrigin-RevId: 995657095
1 parent 6e80d42 commit 2af7fae

6 files changed

Lines changed: 4031 additions & 97 deletions

File tree

‎google/genai/_gaos/google_genai.py‎

Lines changed: 326 additions & 97 deletions
Large diffs are not rendered by default.

‎google/genai/_gaos/interactions.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def create(
4848
extra_query: Optional[Mapping[str, Any]] = None,
4949
extra_body: Optional[Mapping[str, Any]] = None,
5050
timeout: Optional[Union[float, httpx.Timeout]] = None,
51+
automatic_continuation: Optional[bool] = None,
5152
) -> Union[interactions.Interaction, Stream[interactions.InteractionSSEEvent]]:
5253
r"""Creates a new interaction.
5354
@@ -87,6 +88,7 @@ def create(
8788
extra_query: Optional[Mapping[str, Any]] = None,
8889
extra_body: Optional[Mapping[str, Any]] = None,
8990
timeout: Optional[Union[float, httpx.Timeout]] = None,
91+
automatic_continuation: Optional[bool] = None,
9092
) -> interactions.Interaction:
9193
r"""Creates a new interaction.
9294
@@ -154,6 +156,7 @@ def create(
154156
extra_query: Optional[Mapping[str, Any]] = None,
155157
extra_body: Optional[Mapping[str, Any]] = None,
156158
timeout: Optional[Union[float, httpx.Timeout]] = None,
159+
automatic_continuation: Optional[bool] = None,
157160
) -> Stream[interactions.InteractionSSEEvent]:
158161
r"""Creates a new interaction.
159162
@@ -220,6 +223,7 @@ def create(
220223
extra_query: Optional[Mapping[str, Any]] = None,
221224
extra_body: Optional[Mapping[str, Any]] = None,
222225
timeout: Optional[Union[float, httpx.Timeout]] = None,
226+
automatic_continuation: Optional[bool] = None,
223227
) -> interactions.Interaction:
224228
r"""Creates a new interaction.
225229
@@ -285,6 +289,7 @@ def create(
285289
extra_query: Optional[Mapping[str, Any]] = None,
286290
extra_body: Optional[Mapping[str, Any]] = None,
287291
timeout: Optional[Union[float, httpx.Timeout]] = None,
292+
automatic_continuation: Optional[bool] = None,
288293
) -> Stream[interactions.InteractionSSEEvent]:
289294
r"""Creates a new interaction.
290295
@@ -335,6 +340,7 @@ def create(
335340
extra_query: Optional[Mapping[str, Any]] = None,
336341
extra_body: Optional[Mapping[str, Any]] = None,
337342
timeout: Optional[Union[float, httpx.Timeout]] = None,
343+
automatic_continuation: Optional[bool] = None,
338344
**body_kwargs: Any,
339345
) -> Union[interactions.Interaction, Stream[interactions.InteractionSSEEvent]]:
340346
r"""Creates a new interaction.
@@ -1317,6 +1323,7 @@ async def create(
13171323
extra_query: Optional[Mapping[str, Any]] = None,
13181324
extra_body: Optional[Mapping[str, Any]] = None,
13191325
timeout: Optional[Union[float, httpx.Timeout]] = None,
1326+
automatic_continuation: Optional[bool] = None,
13201327
) -> Union[interactions.Interaction, AsyncStream[interactions.InteractionSSEEvent]]:
13211328
r"""Creates a new interaction.
13221329
@@ -1356,6 +1363,7 @@ async def create(
13561363
extra_query: Optional[Mapping[str, Any]] = None,
13571364
extra_body: Optional[Mapping[str, Any]] = None,
13581365
timeout: Optional[Union[float, httpx.Timeout]] = None,
1366+
automatic_continuation: Optional[bool] = None,
13591367
) -> interactions.Interaction:
13601368
r"""Creates a new interaction.
13611369
@@ -1423,6 +1431,7 @@ async def create(
14231431
extra_query: Optional[Mapping[str, Any]] = None,
14241432
extra_body: Optional[Mapping[str, Any]] = None,
14251433
timeout: Optional[Union[float, httpx.Timeout]] = None,
1434+
automatic_continuation: Optional[bool] = None,
14261435
) -> AsyncStream[interactions.InteractionSSEEvent]:
14271436
r"""Creates a new interaction.
14281437
@@ -1489,6 +1498,7 @@ async def create(
14891498
extra_query: Optional[Mapping[str, Any]] = None,
14901499
extra_body: Optional[Mapping[str, Any]] = None,
14911500
timeout: Optional[Union[float, httpx.Timeout]] = None,
1501+
automatic_continuation: Optional[bool] = None,
14921502
) -> interactions.Interaction:
14931503
r"""Creates a new interaction.
14941504
@@ -1554,6 +1564,7 @@ async def create(
15541564
extra_query: Optional[Mapping[str, Any]] = None,
15551565
extra_body: Optional[Mapping[str, Any]] = None,
15561566
timeout: Optional[Union[float, httpx.Timeout]] = None,
1567+
automatic_continuation: Optional[bool] = None,
15571568
) -> AsyncStream[interactions.InteractionSSEEvent]:
15581569
r"""Creates a new interaction.
15591570
@@ -1604,6 +1615,7 @@ async def create(
16041615
extra_query: Optional[Mapping[str, Any]] = None,
16051616
extra_body: Optional[Mapping[str, Any]] = None,
16061617
timeout: Optional[Union[float, httpx.Timeout]] = None,
1618+
automatic_continuation: Optional[bool] = None,
16071619
**body_kwargs: Any,
16081620
) -> Union[interactions.Interaction, AsyncStream[interactions.InteractionSSEEvent]]:
16091621
r"""Creates a new interaction.

‎google/genai/_gaos/types/interactions/interaction.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ class InteractionTypedDict(TypedDict):
167167
r"""Configuration parameters for the agent interaction."""
168168
cached_content: NotRequired[str]
169169
r"""The name of the cached content used as context to serve the prediction. Note: only used in explicit caching, where users can have control over caching (e.g. what content to cache) and enjoy guaranteed cost savings. Format: cachedContents/{cachedContent}"""
170+
continuation_ids: NotRequired[List[str]]
171+
r"""The IDs of all server interactions merged into this response when automatic continuation is enabled, in order (ending with the final hop's ID).
172+
173+
Note: this is added by the SDK.
174+
"""
170175
continuation_token: NotRequired[str]
171176
r"""Opaque token to resume a long decode. Output: set when status is
172177
REQUIRES_CONTINUATION and decoding can be resumed. Input: pass the latest
@@ -263,6 +268,12 @@ class Interaction(BaseModel):
263268
] = None
264269
r"""The name of the cached content used as context to serve the prediction. Note: only used in explicit caching, where users can have control over caching (e.g. what content to cache) and enjoy guaranteed cost savings. Format: cachedContents/{cachedContent}"""
265270

271+
continuation_ids: Optional[List[str]] = None
272+
r"""The IDs of all server interactions merged into this response when automatic continuation is enabled, in order (ending with the final hop's ID).
273+
274+
Note: this is added by the SDK.
275+
"""
276+
266277
continuation_token: Optional[str] = None
267278
r"""Opaque token to resume a long decode. Output: set when status is
268279
REQUIRES_CONTINUATION and decoding can be resumed. Input: pass the latest
@@ -380,6 +391,7 @@ def _serialize_model(self, handler):
380391
"agent",
381392
"agent_config",
382393
"cached_content",
394+
"continuation_ids",
383395
"continuation_token",
384396
"created",
385397
"environment",

0 commit comments

Comments
 (0)