UNPKG

@ai-sdk/google

Version:

The **[Google Generative AI provider](https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai)** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for the [Google Generative AI](https://ai.google/discover/generativeai/)

186 lines (172 loc) 6.01 kB
import type { LanguageModelV3FinishReason, LanguageModelV3StreamPart, SharedV3ProviderMetadata, SharedV3Warning, } from '@ai-sdk/provider'; import { convertGoogleInteractionsUsage } from './convert-google-interactions-usage'; import { type GoogleInteractionsResponse } from './google-interactions-api'; import { mapGoogleInteractionsFinishReason } from './map-google-interactions-finish-reason'; import { parseGoogleInteractionsOutputs } from './parse-google-interactions-outputs'; /** * Synthesizes a `LanguageModelV3StreamPart` stream from a fully-resolved * Interaction response (i.e. the `response` returned after polling a * `background: true` agent call to a terminal status). * * Agent calls cannot use SSE (`stream: true` is incompatible with * `background: true`), so we deterministically replay the polled outputs as a * stream sequence in the same order/shape `buildGoogleInteractionsStreamTransform` * would produce. Each text/reasoning block is emitted as a single delta — the * server has already produced the whole block by the time we synthesize. */ export function synthesizeGoogleInteractionsAgentStream({ response, warnings, generateId, includeRawChunks, headerServiceTier, }: { response: GoogleInteractionsResponse; warnings: Array<SharedV3Warning>; generateId: () => string; includeRawChunks?: boolean; headerServiceTier?: string; }): ReadableStream<LanguageModelV3StreamPart> { return new ReadableStream<LanguageModelV3StreamPart>({ start(controller) { controller.enqueue({ type: 'stream-start', warnings }); const interactionId = typeof response.id === 'string' && response.id.length > 0 ? response.id : undefined; let timestamp: Date | undefined; const created = response.created; if (typeof created === 'string') { const parsed = new Date(created); if (!Number.isNaN(parsed.getTime())) { timestamp = parsed; } } controller.enqueue({ type: 'response-metadata', ...(interactionId != null ? { id: interactionId } : {}), modelId: response.model ?? undefined, ...(timestamp ? { timestamp } : {}), }); if (includeRawChunks) { controller.enqueue({ type: 'raw', rawValue: response }); } const { content, hasFunctionCall } = parseGoogleInteractionsOutputs({ steps: response.steps ?? null, generateId, interactionId, }); let blockCounter = 0; const nextBlockId = () => `${interactionId ?? 'agent'}:${blockCounter++}`; for (const part of content) { switch (part.type) { case 'text': { const id = nextBlockId(); const providerMetadata = part.providerMetadata; controller.enqueue({ type: 'text-start', id }); if (part.text.length > 0) { controller.enqueue({ type: 'text-delta', id, delta: part.text }); } controller.enqueue({ type: 'text-end', id, ...(providerMetadata ? { providerMetadata } : {}), }); break; } case 'reasoning': { const id = nextBlockId(); const providerMetadata = part.providerMetadata; controller.enqueue({ type: 'reasoning-start', id }); if (part.text.length > 0) { controller.enqueue({ type: 'reasoning-delta', id, delta: part.text, }); } controller.enqueue({ type: 'reasoning-end', id, ...(providerMetadata ? { providerMetadata } : {}), }); break; } case 'tool-call': { const providerMetadata = part.providerMetadata; controller.enqueue({ type: 'tool-input-start', id: part.toolCallId, toolName: part.toolName, ...(part.providerExecuted ? { providerExecuted: part.providerExecuted } : {}), }); controller.enqueue({ type: 'tool-input-delta', id: part.toolCallId, delta: part.input, }); controller.enqueue({ type: 'tool-input-end', id: part.toolCallId, }); controller.enqueue({ type: 'tool-call', toolCallId: part.toolCallId, toolName: part.toolName, input: part.input, ...(part.providerExecuted ? { providerExecuted: part.providerExecuted } : {}), ...(providerMetadata ? { providerMetadata } : {}), }); break; } case 'tool-result': { controller.enqueue({ type: 'tool-result', toolCallId: part.toolCallId, toolName: part.toolName, result: part.result, }); break; } case 'source': case 'file': { controller.enqueue(part); break; } default: break; } } const serviceTier = response.service_tier ?? headerServiceTier; const finishReason: LanguageModelV3FinishReason = { unified: mapGoogleInteractionsFinishReason({ status: response.status, hasFunctionCall, }), raw: response.status, }; const providerMetadata: SharedV3ProviderMetadata = { google: { ...(interactionId != null ? { interactionId } : {}), ...(serviceTier != null ? { serviceTier } : {}), }, }; controller.enqueue({ type: 'finish', finishReason, usage: convertGoogleInteractionsUsage(response.usage), providerMetadata, }); controller.close(); }, }); }