@fgv/ts-extras 5.1.0-36 → 5.1.0-39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packlets/ai-assist/apiClient.js +66 -111
- package/dist/packlets/ai-assist/apiClient.js.map +1 -1
- package/dist/packlets/ai-assist/converters.js +16 -3
- package/dist/packlets/ai-assist/converters.js.map +1 -1
- package/dist/packlets/ai-assist/embeddingClient.js +5 -6
- package/dist/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/dist/packlets/ai-assist/imageOptionsResolver.js +0 -19
- package/dist/packlets/ai-assist/imageOptionsResolver.js.map +1 -1
- package/dist/packlets/ai-assist/index.js +2 -2
- package/dist/packlets/ai-assist/index.js.map +1 -1
- package/dist/packlets/ai-assist/model.js +139 -7
- package/dist/packlets/ai-assist/model.js.map +1 -1
- package/dist/packlets/ai-assist/registry.js +58 -51
- package/dist/packlets/ai-assist/registry.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +38 -11
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +187 -36
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/common.js +13 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +12 -4
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +6 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +10 -3
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +5 -2
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/dist/packlets/ai-assist/streamingClient.js +16 -16
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
- package/dist/packlets/crypto-utils/converters.js +18 -0
- package/dist/packlets/crypto-utils/converters.js.map +1 -1
- package/dist/packlets/crypto-utils/index.browser.js +2 -2
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
- package/dist/packlets/crypto-utils/index.js +2 -2
- package/dist/packlets/crypto-utils/index.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/keyStore.js +19 -4
- package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/model.js.map +1 -1
- package/dist/packlets/crypto-utils/model.js.map +1 -1
- package/dist/packlets/crypto-utils/spkiHelpers.js +104 -25
- package/dist/packlets/crypto-utils/spkiHelpers.js.map +1 -1
- package/dist/ts-extras.d.ts +377 -132
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/packlets/ai-assist/apiClient.d.ts +12 -2
- package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/apiClient.js +65 -110
- package/lib/packlets/ai-assist/apiClient.js.map +1 -1
- package/lib/packlets/ai-assist/converters.d.ts +8 -2
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -1
- package/lib/packlets/ai-assist/converters.js +17 -4
- package/lib/packlets/ai-assist/converters.js.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.js +4 -5
- package/lib/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/lib/packlets/ai-assist/imageOptionsResolver.d.ts +0 -8
- package/lib/packlets/ai-assist/imageOptionsResolver.d.ts.map +1 -1
- package/lib/packlets/ai-assist/imageOptionsResolver.js +0 -19
- package/lib/packlets/ai-assist/imageOptionsResolver.js.map +1 -1
- package/lib/packlets/ai-assist/index.d.ts +3 -3
- package/lib/packlets/ai-assist/index.d.ts.map +1 -1
- package/lib/packlets/ai-assist/index.js +6 -1
- package/lib/packlets/ai-assist/index.js.map +1 -1
- package/lib/packlets/ai-assist/model.d.ts +181 -98
- package/lib/packlets/ai-assist/model.d.ts.map +1 -1
- package/lib/packlets/ai-assist/model.js +143 -8
- package/lib/packlets/ai-assist/model.js.map +1 -1
- package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
- package/lib/packlets/ai-assist/registry.js +58 -51
- package/lib/packlets/ai-assist/registry.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +37 -10
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +82 -3
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +187 -35
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +24 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js +14 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +10 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +12 -4
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +6 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +10 -3
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js +5 -2
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.js +15 -15
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
- package/lib/packlets/crypto-utils/converters.d.ts +9 -1
- package/lib/packlets/crypto-utils/converters.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/converters.js +19 -1
- package/lib/packlets/crypto-utils/converters.js.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.js +6 -2
- package/lib/packlets/crypto-utils/index.browser.js.map +1 -1
- package/lib/packlets/crypto-utils/index.d.ts +1 -1
- package/lib/packlets/crypto-utils/index.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.js +6 -2
- package/lib/packlets/crypto-utils/index.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +9 -2
- package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/keyStore.js +19 -4
- package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/model.d.ts +7 -0
- package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/model.js.map +1 -1
- package/lib/packlets/crypto-utils/model.d.ts +11 -1
- package/lib/packlets/crypto-utils/model.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/model.js.map +1 -1
- package/lib/packlets/crypto-utils/spkiHelpers.d.ts +59 -2
- package/lib/packlets/crypto-utils/spkiHelpers.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/spkiHelpers.js +108 -25
- package/lib/packlets/crypto-utils/spkiHelpers.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientToolContinuationBuilder.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Per-provider continuation message builders and the `executeClientToolTurn`\n * helper for orchestrating a single client-tool round-trip.\n *\n * Each provider requires a different wire format for the follow-up request:\n * - **Anthropic**: assistant turn reconstructed from the ordered accumulation\n * buffer (thinking / redacted_thinking / text / tool_use in original stream\n * order) + user turn with `tool_result` blocks. When thinking is active, the\n * follow-up request must NOT set `tool_choice: { type: 'any' }` or\n * `tool_choice: { type: 'tool', ... }` (E3 / §5.4 of b4-spike-findings).\n * - **OpenAI / xAI Responses API**: `function_call` input items +\n * `function_call_output` input items.\n * - **Gemini**: model turn with `functionCall` parts + user turn with\n * `functionResponse` parts (correlation by tool name).\n *\n * @packageDocumentation\n */\n\nimport { captureAsyncResult, fail, type Logging, Result, succeed } from '@fgv/ts-utils';\nimport { type JsonArray, type JsonObject } from '@fgv/ts-json-base';\n\nimport {\n type AiServerToolConfig,\n type AiToolConfig,\n type IAiClientTool,\n type IAiClientToolContinuation,\n type IAiClientToolTurnResult,\n type IAiStreamEvent,\n type IChatRequest,\n type IAiProviderDescriptor,\n resolveModel\n} from '../model';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport { splitChatRequest } from '../chatRequestBuilders';\nimport { resolveEffectiveBaseUrl } from '../endpoint';\nimport { type IAccumulatedBlock } from './anthropic';\nimport { type IAccumulatedFunctionCall } from './openaiResponses';\nimport { type IAccumulatedGeminiFunctionCall } from './gemini';\nimport { callAnthropicStream } from './anthropic';\nimport { callOpenAiResponsesStream } from './openaiResponses';\nimport { callGeminiStream } from './gemini';\nimport { type IStreamApiConfig } from './common';\n\n// ============================================================================\n// Tool-result accumulation (internal)\n// ============================================================================\n\n/**\n * Accumulated result for a single tool call, collected during stream iteration.\n * @internal\n */\ninterface IToolCallResult {\n readonly toolName: string;\n readonly callId?: string;\n readonly args: JsonObject;\n readonly result: string;\n readonly isError: boolean;\n}\n\n// ============================================================================\n// Anthropic continuation builder\n// ============================================================================\n\n/**\n * Builds the Anthropic follow-up messages for a client-tool round-trip.\n *\n * Reconstructs the assistant turn from the ordered accumulation buffer\n * (all block types in original stream order) and appends a user turn\n * with `tool_result` blocks for each executed tool call.\n *\n * **Constraint (E3):** The returned continuation does NOT include a forced\n * `tool_choice` field. When thinking is active, Anthropic rejects\n * `tool_choice: { type: 'any' }` and `tool_choice: { type: 'tool', ... }`\n * with an HTTP 400 error. Only `tool_choice: { type: 'auto' }` (the default,\n * i.e. omitted) is compatible with extended thinking.\n *\n * @internal\n */\nexport function buildAnthropicContinuation(\n accBuffer: Map<number, IAccumulatedBlock>,\n toolResults: IToolCallResult[]\n): IAiClientToolContinuation {\n // Reconstruct the assistant turn from the ordered accumulation buffer.\n // Sort by buffer key (SSE index) to restore original stream order.\n const sortedKeys = Array.from(accBuffer.keys()).sort((a, b) => a - b);\n const assistantContent: JsonArray = [];\n\n for (const key of sortedKeys) {\n const block = accBuffer.get(key);\n /* c8 ignore next 1 - defensive: key always exists in map since we iterate its keys */\n if (!block) continue;\n if (block.type === 'thinking') {\n assistantContent.push({\n type: 'thinking',\n thinking: block.thinking,\n signature: block.signature\n });\n } else if (block.type === 'redacted_thinking') {\n assistantContent.push({\n type: 'redacted_thinking',\n data: block.data\n });\n } else if (block.type === 'text') {\n if (block.text.length > 0) {\n assistantContent.push({ type: 'text', text: block.text });\n }\n } else if (block.type === 'tool_use') {\n let parsedInput: JsonObject;\n try {\n /* c8 ignore next 1 - defensive: argsBuffer is JSON-parsed in the adapter before emitting client-tool-call-done */\n parsedInput = JSON.parse(block.argsBuffer || '{}') as JsonObject;\n /* c8 ignore start - defensive: malformed argsBuffer defaults to empty object */\n } catch {\n parsedInput = {};\n }\n /* c8 ignore stop */\n assistantContent.push({\n type: 'tool_use',\n id: block.id,\n name: block.name,\n input: parsedInput\n });\n }\n }\n\n // Build user turn with tool_result blocks for each tool call.\n const userContent: JsonArray = toolResults.map((r): JsonObject => {\n const block: JsonObject = {\n type: 'tool_result',\n // eslint-disable-next-line @typescript-eslint/naming-convention\n tool_use_id: r.callId ?? r.toolName,\n content: r.result\n };\n if (r.isError) {\n return { ...block, is_error: true };\n }\n return block;\n });\n\n const assistantMessage: JsonObject = {\n role: 'assistant',\n content: assistantContent\n };\n const userMessage: JsonObject = {\n role: 'user',\n content: userContent\n };\n\n return {\n messages: [assistantMessage, userMessage],\n toolCallsSummary: toolResults.map((r) => ({\n toolName: r.toolName,\n callId: r.callId,\n args: r.args,\n result: r.result,\n isError: r.isError\n }))\n };\n}\n\n// ============================================================================\n// OpenAI / xAI Responses API continuation builder\n// ============================================================================\n\n/**\n * Builds the OpenAI / xAI Responses API follow-up input items for a\n * client-tool round-trip.\n *\n * Emits `function_call` items (the model's call) followed by\n * `function_call_output` items (the harness execution result), one pair\n * per executed tool call.\n *\n * @internal\n */\nexport function buildOpenAiContinuation(\n calls: Map<string, IAccumulatedFunctionCall>,\n toolResults: IToolCallResult[]\n): IAiClientToolContinuation {\n const items: JsonObject[] = [];\n\n // Emit function_call items for each call (model's side). Per the Responses API spec\n // (ResponseFunctionToolCall), `call_id` is the required correlation field — it must\n // match the matching function_call_output's `call_id` below. The optional `id` field\n // is the output-item id (`fc_*`) used to reference the streamed item; we omit it\n // because it is not load-bearing for input items.\n for (const [callId, call] of calls) {\n items.push({\n type: 'function_call',\n call_id: callId,\n name: call.name,\n arguments: call.argsBuffer\n });\n }\n\n // Emit function_call_output items (harness execution results).\n for (const r of toolResults) {\n items.push({\n type: 'function_call_output',\n call_id: r.callId ?? r.toolName,\n output: r.result\n });\n }\n\n return {\n messages: items,\n toolCallsSummary: toolResults.map((r) => ({\n toolName: r.toolName,\n callId: r.callId,\n args: r.args,\n result: r.result,\n isError: r.isError\n }))\n };\n}\n\n// ============================================================================\n// Gemini continuation builder\n// ============================================================================\n\n/**\n * Builds the Gemini follow-up contents for a client-tool round-trip.\n *\n * Emits a model turn with `functionCall` parts (one per tool call) and a\n * user turn with `functionResponse` parts (correlation by tool name, since\n * Gemini does not assign call IDs).\n *\n * @internal\n */\nexport function buildGeminiContinuation(\n calls: IAccumulatedGeminiFunctionCall[],\n toolResults: IToolCallResult[]\n): IAiClientToolContinuation {\n // Model turn: functionCall parts for each call.\n const modelParts: JsonArray = calls.map(\n (call): JsonObject => ({\n functionCall: {\n name: call.name,\n args: call.args\n }\n })\n );\n\n // User turn: functionResponse parts for each executed result.\n // Correlation is by name since Gemini has no call IDs.\n const userParts: JsonArray = toolResults.map(\n (r): JsonObject => ({\n functionResponse: {\n name: r.toolName,\n response: {\n content: r.result,\n ...(r.isError ? { error: true } : {})\n }\n }\n })\n );\n\n const modelMessage: JsonObject = {\n role: 'model',\n parts: modelParts\n };\n const userMessage: JsonObject = {\n role: 'user',\n parts: userParts\n };\n\n return {\n messages: [modelMessage, userMessage],\n toolCallsSummary: toolResults.map((r) => ({\n toolName: r.toolName,\n callId: r.callId,\n args: r.args,\n result: r.result,\n isError: r.isError\n }))\n };\n}\n\n// ============================================================================\n// executeClientToolTurn parameters\n// ============================================================================\n\n/**\n * Parameters for {@link AiAssist.executeClientToolTurn}.\n *\n * @remarks\n * Carries the unified {@link AiAssist.IChatRequest} shape (`system?` + ordered\n * `messages`): the last message is the current user turn and the preceding\n * messages are history, linearized before the current turn — identically to the\n * completion and streaming paths. {@link IExecuteClientToolTurnParams.continuationMessages}\n * remains a distinct post-current-turn axis (see below).\n *\n * @public\n */\nexport interface IExecuteClientToolTurnParams extends IChatRequest {\n /** The provider descriptor for routing (Anthropic / OpenAI / Gemini). */\n readonly descriptor: IAiProviderDescriptor;\n /** API key for authentication. */\n readonly apiKey: string;\n /**\n * The cumulative provider-native wire tail from the previous turn's\n * {@link AiAssist.IAiClientToolContinuation.messages}. Supply this as-is\n * each round — `messages` is already cumulative, so **replace** rather\n * than manually concatenate:\n *\n * ```ts\n * tail = outcome.continuation.messages; // replace — already cumulative\n * ```\n *\n * On the first turn this should be `undefined` (or omitted).\n *\n * Each provider applies its own shape guard to the supplied wire objects:\n * - Anthropic: projects each entry to `{ role, content }` (sufficient for\n * thinking blocks and `tool_result` arrays).\n * - OpenAI / xAI Responses: passes each item verbatim (`function_call` /\n * `function_call_output` items carry distinct fields per `type`); only\n * guards that each entry is a JSON object.\n * - Gemini: projects each entry to `{ role, parts }`.\n *\n * Entries that fail their provider's shape check are silently skipped.\n * Do NOT place these objects in the `messages` parameter — the\n * normalized-message path strips provider-native fields.\n */\n readonly continuationMessages?: ReadonlyArray<JsonObject>;\n /** Temperature (default: 0.7). */\n readonly temperature?: number;\n /** Server-side tools to include. */\n readonly tools?: ReadonlyArray<AiServerToolConfig>;\n /** Client-defined tools available for the model to call. */\n readonly clientTools: ReadonlyArray<IAiClientTool>;\n /** Optional abort signal. */\n readonly signal?: AbortSignal;\n /**\n * Optional override of the descriptor's default base URL. Same semantics as\n * the non-streaming completion path and `callProviderCompletionStream`: a\n * well-formed `http`/`https` URL is substituted for `descriptor.baseUrl`\n * when composing the per-format request, with the per-format suffix appended\n * unchanged. Validated at the dispatcher; auth shape is unaffected. Use this\n * to point a client-tool turn at a local / LAN OpenAI-compatible server\n * (Ollama, LM Studio, llama.cpp).\n */\n readonly endpoint?: string;\n /** Optional logger for diagnostics. */\n readonly logger?: Logging.ILogger;\n /** Optional resolved thinking config (pre-resolved by the caller). */\n readonly resolvedThinking?: IResolvedThinkingConfig;\n /** Resolved model string (pre-resolved by the caller). When omitted, uses the descriptor's default model. */\n readonly model?: string;\n}\n\n/**\n * Return value of {@link AiAssist.executeClientToolTurn}.\n * @public\n */\nexport interface IExecuteClientToolTurnResult {\n /**\n * The unified-event iterable. Callers iterate this to drive the streaming UI.\n * The iterable forwards `text-delta`, `tool-event`, `client-tool-call-start`,\n * `client-tool-call-done`, and `client-tool-result` events through.\n */\n readonly events: AsyncIterable<IAiStreamEvent>;\n /**\n * Resolves when the stream terminates. On success, carries the\n * {@link AiAssist.IAiClientToolTurnResult} with the optional continuation for the\n * next round. On failure, carries the error message.\n */\n readonly nextTurn: Promise<Result<IAiClientToolTurnResult>>;\n}\n\n// ============================================================================\n// executeClientToolTurn\n// ============================================================================\n\n/**\n * Orchestrates a single client-tool streaming turn for any supported provider.\n *\n * Starts a streaming request, iterates the underlying provider stream, and:\n * - Forwards `text-delta`, `tool-event`, `client-tool-call-start`, and\n * `client-tool-call-done` events through to the consumer.\n * - For each `client-tool-call-done` event: validates the raw args against the\n * tool's `parametersSchema`, invokes `execute(typedArgs)`, and emits a\n * `client-tool-result` event.\n * - After stream completion: builds the per-provider continuation (or\n * `{ continuation: undefined }` when no tool calls occurred) and resolves\n * `nextTurn`.\n *\n * **Anthropic constraint (E3):** The continuation for Anthropic does not set\n * a forced `tool_choice`. Only `tool_choice: 'auto'` (the default, i.e.\n * omitted) is compatible with extended thinking.\n *\n * @param params - Turn parameters\n * @returns `{ events, nextTurn }` — stream iterable + completion promise\n * @public\n */\nexport function executeClientToolTurn(\n params: IExecuteClientToolTurnParams\n): Result<IExecuteClientToolTurnResult> {\n const {\n descriptor,\n apiKey,\n system,\n messages,\n continuationMessages,\n temperature,\n tools,\n clientTools,\n signal,\n logger,\n resolvedThinking,\n model,\n endpoint\n } = params;\n\n const splitResult = splitChatRequest(system, messages);\n if (splitResult.isFailure()) {\n return fail(splitResult.message);\n }\n const { prompt, head } = splitResult.value;\n if (prompt.attachments.length > 0 && !descriptor.acceptsImageInput) {\n return fail(`provider \"${descriptor.id}\" does not accept image input`);\n }\n\n // Build a lookup map of client tools by name for fast access.\n // Fail fast on duplicate names — silently overwriting would cause one tool\n // to shadow another with no observable signal.\n const toolsByName = new Map<string, IAiClientTool>();\n for (const tool of clientTools) {\n if (toolsByName.has(tool.config.name)) {\n return fail(`executeClientToolTurn: duplicate client tool name '${tool.config.name}'`);\n }\n toolsByName.set(tool.config.name, tool);\n }\n\n // Merge server tools and client tool configs into a single array for the provider.\n // This is the fix for P1-1: client tools were never sent to the provider because\n // the adapters only received `tools` (server tools). Both must coexist per design §2.5.\n const effectiveTools: ReadonlyArray<AiToolConfig> | undefined =\n clientTools.length > 0 ? [...(tools ?? []), ...clientTools.map((t) => t.config)] : tools;\n\n const effectiveTemperature = temperature ?? 0.7;\n const resolvedModel = model ?? resolveModel(descriptor.defaultModel);\n if (resolvedModel.length === 0) {\n return fail(`provider \"${descriptor.id}\": no model resolved; pass model or set descriptor.defaultModel`);\n }\n const baseUrlResult = resolveEffectiveBaseUrl(descriptor, endpoint);\n if (baseUrlResult.isFailure()) {\n return fail(baseUrlResult.message);\n }\n const config: IStreamApiConfig = {\n baseUrl: baseUrlResult.value,\n apiKey,\n model: resolvedModel\n };\n\n // Accumulation buffers — populated by the adapter, read by the builder.\n const anthropicBuffer = new Map<number, IAccumulatedBlock>();\n const openAiCallMap = new Map<string, IAccumulatedFunctionCall>();\n const geminiCalls: IAccumulatedGeminiFunctionCall[] = [];\n\n // Collected tool results, populated as each client-tool-call-done is processed.\n const toolResults: IToolCallResult[] = [];\n\n // Stream start: open the underlying adapter stream.\n const streamPromise: Promise<Result<AsyncIterable<IAiStreamEvent>>> = (() => {\n switch (descriptor.apiFormat) {\n case 'anthropic':\n return callAnthropicStream(\n config,\n prompt,\n head,\n effectiveTemperature,\n effectiveTools,\n logger,\n signal,\n resolvedThinking,\n anthropicBuffer,\n continuationMessages\n );\n case 'openai':\n return callOpenAiResponsesStream(\n config,\n prompt,\n /* c8 ignore next 1 - defensive: openai path requires tools; empty array fallback unreachable in practice */\n effectiveTools ?? [],\n head,\n effectiveTemperature,\n logger,\n signal,\n resolvedThinking,\n openAiCallMap,\n continuationMessages\n );\n case 'gemini':\n return callGeminiStream(\n config,\n prompt,\n head,\n effectiveTemperature,\n effectiveTools,\n logger,\n signal,\n resolvedThinking,\n geminiCalls,\n continuationMessages\n );\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = descriptor.apiFormat;\n return Promise.resolve(fail(`unsupported API format: ${String(_exhaustive)}`));\n }\n }\n })();\n\n // Resolve controls for `nextTurn`.\n let resolveNextTurn!: (result: Result<IAiClientToolTurnResult>) => void;\n const nextTurn = new Promise<Result<IAiClientToolTurnResult>>((resolve) => {\n resolveNextTurn = resolve;\n });\n\n // The unified-event generator: opens the stream, proxies events, executes tools.\n async function* eventGenerator(): AsyncGenerator<IAiStreamEvent> {\n const streamResult = await streamPromise;\n if (streamResult.isFailure()) {\n resolveNextTurn(fail(streamResult.message));\n yield { type: 'error', message: streamResult.message };\n return;\n }\n\n let truncated = false;\n let fullText = '';\n let streamError: string | undefined;\n\n for await (const event of streamResult.value) {\n if (event.type === 'done') {\n truncated = event.truncated;\n fullText = event.fullText;\n yield event;\n continue;\n }\n\n if (event.type === 'error') {\n streamError = event.message;\n yield event;\n continue;\n }\n\n if (event.type === 'text-delta') {\n yield event;\n continue;\n }\n\n if (event.type === 'tool-event') {\n yield event;\n continue;\n }\n\n if (event.type === 'client-tool-call-start') {\n yield event;\n continue;\n }\n\n if (event.type === 'client-tool-call-done') {\n yield event;\n\n const { toolName, callId, args } = event;\n const tool = toolsByName.get(toolName);\n\n if (!tool) {\n const errMsg = `model called unknown tool: ${toolName}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n resolveNextTurn(fail(errMsg));\n return;\n }\n\n const validationResult = tool.config.parametersSchema.validate(args);\n if (validationResult.isFailure()) {\n const errMsg = `${toolName} (callId=${callId}): ${validationResult.message}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n\n const executeResult = await captureAsyncResult(async () => tool.execute(validationResult.value));\n const executionResult: Result<unknown> = executeResult.isSuccess()\n ? executeResult.value\n : executeResult;\n\n if (executionResult.isFailure()) {\n const errMsg = `${toolName} (callId=${callId}): ${executionResult.message}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n\n // JSON.stringify can throw (circular references) or return undefined\n // (e.g. for a bare `undefined` value, or a value of type `function`).\n // Either outcome violates the client-tool-result event contract, so\n // emit an isError result with diagnostic context instead.\n let resultStr: string;\n try {\n const stringified = JSON.stringify(executionResult.value);\n if (stringified === undefined) {\n const errMsg = `${toolName} (callId=${callId}): tool returned a non-serializable value (JSON.stringify produced undefined)`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n resultStr = stringified;\n } catch (e) {\n const errMsg = `${toolName} (callId=${callId}): failed to serialize tool result: ${\n (e as Error).message\n }`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: resultStr,\n isError: false\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: resultStr, isError: false });\n continue;\n }\n\n // client-tool-result events are emitted by this layer, not the adapters — no passthrough needed.\n }\n\n // Stream has ended. Build the continuation.\n if (streamError !== undefined) {\n resolveNextTurn(fail(streamError));\n return;\n }\n\n if (toolResults.length === 0) {\n resolveNextTurn(succeed({ continuation: undefined, truncated, fullText }));\n return;\n }\n\n let continuation: IAiClientToolContinuation;\n switch (descriptor.apiFormat) {\n case 'anthropic':\n continuation = buildAnthropicContinuation(anthropicBuffer, toolResults);\n break;\n case 'openai':\n continuation = buildOpenAiContinuation(openAiCallMap, toolResults);\n break;\n case 'gemini':\n continuation = buildGeminiContinuation(geminiCalls, toolResults);\n break;\n /* c8 ignore next 5 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = descriptor.apiFormat;\n resolveNextTurn(fail(`unsupported API format: ${String(_exhaustive)}`));\n return;\n }\n }\n\n // Prepend inbound continuationMessages so the returned continuation is cumulative.\n // A consumer that does `tail = outcome.continuation.messages` (replace) is then\n // correct for all N rounds — messages always contains the full wire tail from\n // round 1 through the current round. toolCallsSummary stays per-round (this\n // round's calls only); messages is the accumulated tail to re-send.\n if (continuationMessages && continuationMessages.length > 0) {\n continuation = { ...continuation, messages: [...continuationMessages, ...continuation.messages] };\n }\n\n resolveNextTurn(succeed({ continuation, truncated, fullText }));\n }\n\n return succeed({\n events: { [Symbol.asyncIterator]: () => eventGenerator() },\n nextTurn\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"clientToolContinuationBuilder.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Per-provider continuation message builders and the `executeClientToolTurn`\n * helper for orchestrating a single client-tool round-trip.\n *\n * Each provider requires a different wire format for the follow-up request:\n * - **Anthropic**: assistant turn reconstructed from the ordered accumulation\n * buffer (thinking / redacted_thinking / text / tool_use in original stream\n * order) + user turn with `tool_result` blocks. When thinking is active, the\n * follow-up request must NOT set `tool_choice: { type: 'any' }` or\n * `tool_choice: { type: 'tool', ... }` (E3 / §5.4 of b4-spike-findings).\n * - **OpenAI / xAI Responses API**: `function_call` input items +\n * `function_call_output` input items.\n * - **Gemini**: model turn with `functionCall` parts + user turn with\n * `functionResponse` parts (correlation by tool name).\n *\n * @packageDocumentation\n */\n\nimport { captureAsyncResult, fail, type Logging, Result, succeed } from '@fgv/ts-utils';\nimport { type JsonArray, type JsonObject } from '@fgv/ts-json-base';\n\nimport {\n type AiServerToolConfig,\n type AiToolConfig,\n type IAiClientTool,\n type IAiClientToolContinuation,\n type IAiClientToolTurnResult,\n type IAiStreamEvent,\n type IChatRequest,\n type IAiProviderDescriptor,\n resolveProviderModel\n} from '../model';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport { splitChatRequest } from '../chatRequestBuilders';\nimport { resolveEffectiveBaseUrl } from '../endpoint';\nimport { type IAccumulatedBlock } from './anthropic';\nimport { type IAccumulatedFunctionCall } from './openaiResponses';\nimport { type IAccumulatedGeminiFunctionCall } from './gemini';\nimport { callAnthropicStream } from './anthropic';\nimport { callOpenAiResponsesStream } from './openaiResponses';\nimport { callGeminiStream } from './gemini';\nimport { type IStreamApiConfig } from './common';\n\n// ============================================================================\n// Tool-result accumulation (internal)\n// ============================================================================\n\n/**\n * Accumulated result for a single tool call, collected during stream iteration.\n * @internal\n */\ninterface IToolCallResult {\n readonly toolName: string;\n readonly callId?: string;\n readonly args: JsonObject;\n readonly result: string;\n readonly isError: boolean;\n}\n\n// ============================================================================\n// Anthropic continuation builder\n// ============================================================================\n\n/**\n * Returns `true` when `id` is a usable correlation id — present and non-empty.\n *\n * `??` is NOT sufficient for this check: an empty string is a *bad* id (a\n * `tool_use_id: ''` is rejected by Anthropic as a \"malformed identifier\") but\n * `'' ?? fallback` yields `''`, silently passing the empty id through. Every id\n * correlation in this module must use this predicate, never `??`.\n *\n * @internal\n */\nfunction isUsableId(id: string | undefined): id is string {\n return id !== undefined && id.length > 0;\n}\n\n/**\n * Builds the Anthropic follow-up messages for a client-tool round-trip.\n *\n * Reconstructs the assistant turn from the ordered accumulation buffer\n * (all block types in original stream order) and appends a user turn\n * with `tool_result` blocks for each executed tool call.\n *\n * **Single source of truth for the id (id-correlation fix).** Each\n * `tool_result.tool_use_id` is drawn from — and validated against — the set of\n * assistant `tool_use.id`s actually present in the accumulation buffer. The id\n * is NEVER derived from the tool name: a tool name can never match a `toolu_*`\n * id, so a name fallback produces exactly the \"malformed identifier\" the\n * provider rejects. If any tool result cannot be correlated to a buffered\n * `tool_use` block with a non-empty id, the build fails loud (naming the tool)\n * rather than emitting a malformed continuation.\n *\n * **Constraint (E3):** The returned continuation does NOT include a forced\n * `tool_choice` field. When thinking is active, Anthropic rejects\n * `tool_choice: { type: 'any' }` and `tool_choice: { type: 'tool', ... }`\n * with an HTTP 400 error. Only `tool_choice: { type: 'auto' }` (the default,\n * i.e. omitted) is compatible with extended thinking.\n *\n * @internal\n */\nexport function buildAnthropicContinuation(\n accBuffer: Map<number, IAccumulatedBlock>,\n toolResults: IToolCallResult[],\n logger?: Logging.ILogger\n): Result<IAiClientToolContinuation> {\n // Reconstruct the assistant turn from the ordered accumulation buffer.\n // Sort by buffer key (SSE index) to restore original stream order.\n const sortedKeys = Array.from(accBuffer.keys()).sort((a, b) => a - b);\n const assistantContent: JsonArray = [];\n // The set of assistant tool_use ids — the single source of truth every\n // tool_result.tool_use_id must be drawn from.\n const bufferedToolUseIds = new Set<string>();\n\n for (const key of sortedKeys) {\n const block = accBuffer.get(key);\n /* c8 ignore next 1 - defensive: key always exists in map since we iterate its keys */\n if (!block) continue;\n if (block.type === 'thinking') {\n assistantContent.push({\n type: 'thinking',\n thinking: block.thinking,\n signature: block.signature\n });\n } else if (block.type === 'redacted_thinking') {\n assistantContent.push({\n type: 'redacted_thinking',\n data: block.data\n });\n } else if (block.type === 'text') {\n if (block.text.length > 0) {\n assistantContent.push({ type: 'text', text: block.text });\n }\n } else if (block.type === 'tool_use') {\n // A buffered tool_use with an empty id can never be referenced by a valid\n // tool_result; emitting it would corrupt the assistant turn. Fail loud.\n if (!isUsableId(block.id)) {\n return fail(\n `Anthropic continuation: buffered tool_use block for tool '${block.name}' has an empty id; ` +\n `cannot build a valid continuation`\n );\n }\n let parsedInput: JsonObject;\n try {\n /* c8 ignore next 1 - defensive: argsBuffer is JSON-parsed in the adapter before emitting client-tool-call-done */\n parsedInput = JSON.parse(block.argsBuffer || '{}') as JsonObject;\n /* c8 ignore start - defensive: malformed argsBuffer defaults to empty object */\n } catch {\n parsedInput = {};\n }\n /* c8 ignore stop */\n bufferedToolUseIds.add(block.id);\n assistantContent.push({\n type: 'tool_use',\n id: block.id,\n name: block.name,\n input: parsedInput\n });\n }\n }\n\n // Build user turn with tool_result blocks for each tool call. Correlate each\n // result to a buffered tool_use id — the SAME id that keys the assistant\n // tool_use block — and fail loud on a missing / empty / mismatched id. A\n // missing callId is a bug to surface, not paper over with a name fallback.\n const userContent: JsonArray = [];\n for (const r of toolResults) {\n if (!isUsableId(r.callId)) {\n return fail(\n `Anthropic continuation: tool '${r.toolName}' result has no call id (missing or empty); ` +\n `cannot correlate it to an assistant tool_use block`\n );\n }\n if (!bufferedToolUseIds.has(r.callId)) {\n return fail(\n `Anthropic continuation: tool '${r.toolName}' call id '${r.callId}' does not match any ` +\n `buffered assistant tool_use block id`\n );\n }\n const block: JsonObject = {\n type: 'tool_result',\n // eslint-disable-next-line @typescript-eslint/naming-convention\n tool_use_id: r.callId,\n content: r.result\n };\n userContent.push(r.isError ? { ...block, is_error: true } : block);\n }\n\n // Decisive diagnostic: the tool_use.id ↔ tool_result.tool_use_id pairing for\n // the outgoing continuation. A failing turn is field-confirmable by capturing\n // this line (the pairing is by construction matched here — divergence fails\n // loud above, before reaching this point).\n if (logger) {\n const pairing = toolResults.map((r) => `${r.toolName}:${r.callId}`).join(', ');\n logger.detail(`ai-assist:anthropic-continuation tool_use.id↔tool_result.tool_use_id [${pairing}]`);\n }\n\n const assistantMessage: JsonObject = {\n role: 'assistant',\n content: assistantContent\n };\n const userMessage: JsonObject = {\n role: 'user',\n content: userContent\n };\n\n return succeed({\n messages: [assistantMessage, userMessage],\n toolCallsSummary: toolResults.map((r) => ({\n toolName: r.toolName,\n callId: r.callId,\n args: r.args,\n result: r.result,\n isError: r.isError\n }))\n });\n}\n\n// ============================================================================\n// OpenAI / xAI Responses API continuation builder\n// ============================================================================\n\n/**\n * Builds the OpenAI / xAI Responses API follow-up input items for a\n * client-tool round-trip.\n *\n * Emits `function_call` items (the model's call) followed by\n * `function_call_output` items (the harness execution result), one pair\n * per executed tool call.\n *\n * **Single source of truth for the id (id-correlation fix).** Each\n * `function_call_output.call_id` is drawn from — and validated against — the\n * set of `function_call.call_id`s in the accumulation map. It is NEVER derived\n * from the tool name (the OpenAI parity of the Anthropic `tool_use_id` fix). A\n * missing / empty / unmatched call id fails the build loud rather than emitting\n * a continuation the provider would reject.\n *\n * @internal\n */\nexport function buildOpenAiContinuation(\n calls: Map<string, IAccumulatedFunctionCall>,\n toolResults: IToolCallResult[],\n logger?: Logging.ILogger\n): Result<IAiClientToolContinuation> {\n const items: JsonObject[] = [];\n\n // Emit function_call items for each call (model's side). Per the Responses API spec\n // (ResponseFunctionToolCall), `call_id` is the required correlation field — it must\n // match the matching function_call_output's `call_id` below. The optional `id` field\n // is the output-item id (`fc_*`) used to reference the streamed item; we omit it\n // because it is not load-bearing for input items.\n const bufferedCallIds = new Set<string>();\n for (const [callId, call] of calls) {\n bufferedCallIds.add(callId);\n items.push({\n type: 'function_call',\n call_id: callId,\n name: call.name,\n arguments: call.argsBuffer\n });\n }\n\n // Emit function_call_output items (harness execution results). Correlate each\n // to a buffered function_call call_id — never the tool name — and fail loud on\n // a missing / empty / mismatched id.\n for (const r of toolResults) {\n if (!isUsableId(r.callId)) {\n return fail(\n `OpenAI continuation: tool '${r.toolName}' result has no call id (missing or empty); ` +\n `cannot correlate it to a function_call item`\n );\n }\n if (!bufferedCallIds.has(r.callId)) {\n return fail(\n `OpenAI continuation: tool '${r.toolName}' call id '${r.callId}' does not match any ` +\n `accumulated function_call call_id`\n );\n }\n items.push({\n type: 'function_call_output',\n call_id: r.callId,\n output: r.result\n });\n }\n\n // Decisive diagnostic: the function_call.call_id ↔ function_call_output.call_id\n // pairing for the outgoing continuation (matched by construction here).\n if (logger) {\n const pairing = toolResults.map((r) => `${r.toolName}:${r.callId}`).join(', ');\n logger.detail(`ai-assist:openai-continuation function_call.call_id↔output.call_id [${pairing}]`);\n }\n\n return succeed({\n messages: items,\n toolCallsSummary: toolResults.map((r) => ({\n toolName: r.toolName,\n callId: r.callId,\n args: r.args,\n result: r.result,\n isError: r.isError\n }))\n });\n}\n\n// ============================================================================\n// Gemini continuation builder\n// ============================================================================\n\n/**\n * Builds the Gemini follow-up contents for a client-tool round-trip.\n *\n * Emits a model turn with `functionCall` parts (one per tool call) and a\n * user turn with `functionResponse` parts (correlation by tool name, since\n * Gemini does not assign call IDs).\n *\n * When thinking is enabled, Gemini stamps an opaque `thoughtSignature` on each\n * `functionCall` part and requires it echoed back, verbatim, as a sibling of\n * `functionCall` on the continuation's model turn — otherwise the follow-up is\n * rejected with \"Function call is missing a thought_signature in functionCall\n * parts\". The captured signature (see {@link IAccumulatedGeminiFunctionCall})\n * is replayed here only when present; the key is omitted entirely when thinking\n * was disabled. See https://ai.google.dev/gemini-api/docs/thought-signatures.\n *\n * @internal\n */\nexport function buildGeminiContinuation(\n calls: IAccumulatedGeminiFunctionCall[],\n toolResults: IToolCallResult[]\n): IAiClientToolContinuation {\n // Model turn: functionCall parts for each call. Replay the part-level\n // thoughtSignature as a sibling of functionCall only when present.\n const modelParts: JsonArray = calls.map(\n (call): JsonObject => ({\n functionCall: {\n name: call.name,\n args: call.args\n },\n ...(call.thoughtSignature !== undefined ? { thoughtSignature: call.thoughtSignature } : {})\n })\n );\n\n // User turn: functionResponse parts for each executed result.\n // Correlation is by name since Gemini has no call IDs.\n const userParts: JsonArray = toolResults.map(\n (r): JsonObject => ({\n functionResponse: {\n name: r.toolName,\n response: {\n content: r.result,\n ...(r.isError ? { error: true } : {})\n }\n }\n })\n );\n\n const modelMessage: JsonObject = {\n role: 'model',\n parts: modelParts\n };\n const userMessage: JsonObject = {\n role: 'user',\n parts: userParts\n };\n\n return {\n messages: [modelMessage, userMessage],\n toolCallsSummary: toolResults.map((r) => ({\n toolName: r.toolName,\n callId: r.callId,\n args: r.args,\n result: r.result,\n isError: r.isError\n }))\n };\n}\n\n// ============================================================================\n// executeClientToolTurn parameters\n// ============================================================================\n\n/**\n * Decision returned by an {@link IExecuteClientToolTurnParams.onBeforeToolExecute}\n * gate for a single client-tool call.\n *\n * @remarks\n * - `{ action: 'proceed' }` — run the tool's `execute` normally.\n * - `{ action: 'deny', reason }` — do NOT run `execute`; a synthesized tool-result\n * carrying `reason` is fed into the continuation exactly like a normal result so\n * the model can react, and a `client-tool-result` event is emitted for the call.\n * The turn continues.\n *\n * A gate that wants to signal a hard error (as opposed to a deliberate deny) should\n * return `Result.fail` (or reject) from the callback. Note this is NOT the same as a\n * `tool.execute` failure: an `execute` failure yields an `isError` tool-result and the\n * **turn continues**, whereas a gate `fail`/reject **terminates the turn** — it fails\n * `nextTurn` (and emits an inline `error` event) rather than synthesizing a deny.\n *\n * @public\n */\nexport type IToolExecutionDecision =\n | { readonly action: 'proceed' }\n | { readonly action: 'deny'; readonly reason: string };\n\n/**\n * Parameters for {@link AiAssist.executeClientToolTurn}.\n *\n * @remarks\n * Carries the unified {@link AiAssist.IChatRequest} shape (`system?` + ordered\n * `messages`): the last message is the current user turn and the preceding\n * messages are history, linearized before the current turn — identically to the\n * completion and streaming paths. {@link IExecuteClientToolTurnParams.continuationMessages}\n * remains a distinct post-current-turn axis (see below).\n *\n * @public\n */\nexport interface IExecuteClientToolTurnParams extends IChatRequest {\n /** The provider descriptor for routing (Anthropic / OpenAI / Gemini). */\n readonly descriptor: IAiProviderDescriptor;\n /** API key for authentication. */\n readonly apiKey: string;\n /**\n * The cumulative provider-native wire tail from the previous turn's\n * {@link AiAssist.IAiClientToolContinuation.messages}. Supply this as-is\n * each round — `messages` is already cumulative, so **replace** rather\n * than manually concatenate:\n *\n * ```ts\n * tail = outcome.continuation.messages; // replace — already cumulative\n * ```\n *\n * On the first turn this should be `undefined` (or omitted).\n *\n * Each provider applies its own shape guard to the supplied wire objects:\n * - Anthropic: projects each entry to `{ role, content }` (sufficient for\n * thinking blocks and `tool_result` arrays).\n * - OpenAI / xAI Responses: passes each item verbatim (`function_call` /\n * `function_call_output` items carry distinct fields per `type`); only\n * guards that each entry is a JSON object.\n * - Gemini: projects each entry to `{ role, parts }`.\n *\n * Entries that fail their provider's shape check are silently skipped.\n * Do NOT place these objects in the `messages` parameter — the\n * normalized-message path strips provider-native fields.\n */\n readonly continuationMessages?: ReadonlyArray<JsonObject>;\n /**\n * Sampling temperature. Sent to the provider only when explicitly provided; omitted otherwise\n * so the provider's own default applies (current-gen models reject a caller-supplied default).\n */\n readonly temperature?: number;\n /** Server-side tools to include. */\n readonly tools?: ReadonlyArray<AiServerToolConfig>;\n /** Client-defined tools available for the model to call. */\n readonly clientTools: ReadonlyArray<IAiClientTool>;\n /** Optional abort signal. */\n readonly signal?: AbortSignal;\n /**\n * Optional override of the descriptor's default base URL. Same semantics as\n * the non-streaming completion path and `callProviderCompletionStream`: a\n * well-formed `http`/`https` URL is substituted for `descriptor.baseUrl`\n * when composing the per-format request, with the per-format suffix appended\n * unchanged. Validated at the dispatcher; auth shape is unaffected. Use this\n * to point a client-tool turn at a local / LAN OpenAI-compatible server\n * (Ollama, LM Studio, llama.cpp).\n */\n readonly endpoint?: string;\n /** Optional logger for diagnostics. */\n readonly logger?: Logging.ILogger;\n /** Optional resolved thinking config (pre-resolved by the caller). */\n readonly resolvedThinking?: IResolvedThinkingConfig;\n /** Resolved model string (pre-resolved by the caller). When omitted, uses the descriptor's default model. */\n readonly model?: string;\n /**\n * Optional host gate invoked for each client-tool call **after** the model's raw\n * args have been validated against the tool's `parametersSchema` and **before**\n * `tool.execute(...)` runs. The full `tool` (including `tool.config.annotations`)\n * and the validated `args` are passed so gate logic can key off a tool's behavior\n * annotations (e.g. deny a `destructiveHint` call pending confirmation).\n *\n * Returning `{ action: 'proceed' }` (or omitting the callback) runs `execute`\n * normally. Returning `{ action: 'deny', reason }` skips `execute` and synthesizes\n * a denial tool-result carrying `reason` into the continuation (the model sees the\n * denial and can react); **the turn continues**. A callback that returns `Result.fail`\n * or rejects is a hard error that **terminates the turn** (fails `nextTurn` + emits an\n * inline `error` event) — distinct from a `tool.execute` failure, which continues the\n * turn with an `isError` result. A deny must therefore be explicit.\n */\n readonly onBeforeToolExecute?: (\n tool: IAiClientTool,\n args: unknown\n ) => Promise<Result<IToolExecutionDecision>>;\n}\n\n/**\n * Return value of {@link AiAssist.executeClientToolTurn}.\n * @public\n */\nexport interface IExecuteClientToolTurnResult {\n /**\n * The unified-event iterable. Callers iterate this to drive the streaming UI.\n * The iterable forwards `text-delta`, `tool-event`, `client-tool-call-start`,\n * `client-tool-call-done`, and `client-tool-result` events through.\n */\n readonly events: AsyncIterable<IAiStreamEvent>;\n /**\n * Resolves when the stream terminates. On success, carries the\n * {@link AiAssist.IAiClientToolTurnResult} with the optional continuation for the\n * next round. On failure, carries the error message.\n */\n readonly nextTurn: Promise<Result<IAiClientToolTurnResult>>;\n}\n\n// ============================================================================\n// executeClientToolTurn\n// ============================================================================\n\n/**\n * True when a request would combine Gemini built-in grounding (a `web_search`\n * server tool) with client (function) tools — a combination Gemini's\n * `generateContent` API rejects with HTTP 400 (`INVALID_ARGUMENT`). Callers gate\n * on `descriptor.apiFormat === 'gemini'` before consulting this; other providers\n * accept the mix. Kept as a pure predicate so the conflict rule is unit-testable\n * without a live stream.\n *\n * @internal\n */\nexport function hasGeminiToolConflict(\n tools: ReadonlyArray<AiServerToolConfig> | undefined,\n clientTools: ReadonlyArray<IAiClientTool>\n): boolean {\n return clientTools.length > 0 && (tools?.some((t) => t.type === 'web_search') ?? false);\n}\n\n/**\n * Orchestrates a single client-tool streaming turn for any supported provider.\n *\n * Starts a streaming request, iterates the underlying provider stream, and:\n * - Forwards `text-delta`, `tool-event`, `client-tool-call-start`, and\n * `client-tool-call-done` events through to the consumer.\n * - For each `client-tool-call-done` event: validates the raw args against the\n * tool's `parametersSchema`, invokes `execute(typedArgs)`, and emits a\n * `client-tool-result` event.\n * - After stream completion: builds the per-provider continuation (or\n * `{ continuation: undefined }` when no tool calls occurred) and resolves\n * `nextTurn`.\n *\n * **Anthropic constraint (E3):** The continuation for Anthropic does not set\n * a forced `tool_choice`. Only `tool_choice: 'auto'` (the default, i.e.\n * omitted) is compatible with extended thinking.\n *\n * @param params - Turn parameters\n * @returns `{ events, nextTurn }` — stream iterable + completion promise\n * @public\n */\nexport function executeClientToolTurn(\n params: IExecuteClientToolTurnParams\n): Result<IExecuteClientToolTurnResult> {\n const {\n descriptor,\n apiKey,\n system,\n messages,\n continuationMessages,\n temperature,\n tools,\n clientTools,\n signal,\n logger,\n resolvedThinking,\n model,\n endpoint,\n onBeforeToolExecute\n } = params;\n\n const splitResult = splitChatRequest(system, messages);\n if (splitResult.isFailure()) {\n return fail(splitResult.message);\n }\n const { prompt, head } = splitResult.value;\n if (prompt.attachments.length > 0 && !descriptor.acceptsImageInput) {\n return fail(`provider \"${descriptor.id}\" does not accept image input`);\n }\n\n // Build a lookup map of client tools by name for fast access.\n // Fail fast on duplicate names — silently overwriting would cause one tool\n // to shadow another with no observable signal.\n const toolsByName = new Map<string, IAiClientTool>();\n for (const tool of clientTools) {\n if (toolsByName.has(tool.config.name)) {\n return fail(`executeClientToolTurn: duplicate client tool name '${tool.config.name}'`);\n }\n toolsByName.set(tool.config.name, tool);\n }\n\n // Merge server tools and client tool configs into a single array for the provider.\n // This is the fix for P1-1: client tools were never sent to the provider because\n // the adapters only received `tools` (server tools). Both must coexist per design §2.5.\n const effectiveTools: ReadonlyArray<AiToolConfig> | undefined =\n clientTools.length > 0 ? [...(tools ?? []), ...clientTools.map((t) => t.config)] : tools;\n\n // Gemini pre-flight: its generateContent API HTTP-400s (INVALID_ARGUMENT) when\n // built-in grounding (`web_search`) and function calling (client tools) are\n // combined in one request. Fail fast with a clear, actionable message rather\n // than letting the opaque wire 400 surface. Other providers accept the mix.\n if (descriptor.apiFormat === 'gemini' && hasGeminiToolConflict(tools, clientTools)) {\n return fail(\n 'executeClientToolTurn: Gemini cannot combine web_search grounding with client (function) tools in the same request; send one or the other'\n );\n }\n\n const modelResult = resolveProviderModel(descriptor, model);\n if (modelResult.isFailure()) {\n return fail(modelResult.message);\n }\n const resolvedModel = modelResult.value;\n const baseUrlResult = resolveEffectiveBaseUrl(descriptor, endpoint);\n if (baseUrlResult.isFailure()) {\n return fail(baseUrlResult.message);\n }\n const config: IStreamApiConfig = {\n baseUrl: baseUrlResult.value,\n apiKey,\n model: resolvedModel\n };\n\n // Accumulation buffers — populated by the adapter, read by the builder.\n const anthropicBuffer = new Map<number, IAccumulatedBlock>();\n const openAiCallMap = new Map<string, IAccumulatedFunctionCall>();\n const geminiCalls: IAccumulatedGeminiFunctionCall[] = [];\n\n // Collected tool results, populated as each client-tool-call-done is processed.\n const toolResults: IToolCallResult[] = [];\n\n // Stream start: open the underlying adapter stream.\n const streamPromise: Promise<Result<AsyncIterable<IAiStreamEvent>>> = (() => {\n switch (descriptor.apiFormat) {\n case 'anthropic':\n return callAnthropicStream(\n config,\n prompt,\n head,\n temperature,\n effectiveTools,\n logger,\n signal,\n resolvedThinking,\n anthropicBuffer,\n continuationMessages\n );\n case 'openai':\n return callOpenAiResponsesStream(\n config,\n prompt,\n /* c8 ignore next 1 - defensive: openai path requires tools; empty array fallback unreachable in practice */\n effectiveTools ?? [],\n head,\n temperature,\n logger,\n signal,\n resolvedThinking,\n openAiCallMap,\n continuationMessages\n );\n case 'gemini':\n return callGeminiStream(\n config,\n prompt,\n head,\n temperature,\n effectiveTools,\n logger,\n signal,\n resolvedThinking,\n geminiCalls,\n continuationMessages\n );\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = descriptor.apiFormat;\n return Promise.resolve(fail(`unsupported API format: ${String(_exhaustive)}`));\n }\n }\n })();\n\n // Resolve controls for `nextTurn`.\n let resolveNextTurn!: (result: Result<IAiClientToolTurnResult>) => void;\n const nextTurn = new Promise<Result<IAiClientToolTurnResult>>((resolve) => {\n resolveNextTurn = resolve;\n });\n\n // The unified-event generator: opens the stream, proxies events, executes tools.\n async function* eventGenerator(): AsyncGenerator<IAiStreamEvent> {\n const streamResult = await streamPromise;\n if (streamResult.isFailure()) {\n resolveNextTurn(fail(streamResult.message));\n yield { type: 'error', message: streamResult.message };\n return;\n }\n\n let truncated = false;\n let fullText = '';\n let streamError: string | undefined;\n\n for await (const event of streamResult.value) {\n if (event.type === 'done') {\n truncated = event.truncated;\n fullText = event.fullText;\n yield event;\n continue;\n }\n\n if (event.type === 'error') {\n streamError = event.message;\n yield event;\n continue;\n }\n\n if (event.type === 'text-delta') {\n yield event;\n continue;\n }\n\n if (event.type === 'tool-event') {\n yield event;\n continue;\n }\n\n if (event.type === 'client-tool-call-start') {\n yield event;\n continue;\n }\n\n if (event.type === 'client-tool-call-done') {\n yield event;\n\n const { toolName, callId, args } = event;\n const tool = toolsByName.get(toolName);\n\n if (!tool) {\n const errMsg = `model called unknown tool: ${toolName}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n resolveNextTurn(fail(errMsg));\n return;\n }\n\n const validationResult = tool.config.parametersSchema.validate(args);\n if (validationResult.isFailure()) {\n const errMsg = `${toolName} (callId=${callId}): ${validationResult.message}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n\n // Host gate: run after arg-validation, before execute. A `deny` decision\n // synthesizes a denial tool-result and continues the turn; a `fail`/reject\n // from the gate itself is a hard error (mirrors an execute failure), never\n // a silent deny.\n if (onBeforeToolExecute !== undefined) {\n const decisionResult: Result<IToolExecutionDecision> = (\n await captureAsyncResult(async () => onBeforeToolExecute(tool, validationResult.value))\n ).onSuccess((decision) => decision);\n\n if (decisionResult.isFailure()) {\n const errMsg = `${toolName} (callId=${callId}): ${decisionResult.message}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n // Gate-fail is a hard, turn-terminating error (unlike a non-fatal `deny`, whose\n // generator continues). Emit an explicit `error` event so a consumer watching only\n // `events` sees the fatal failure inline and can distinguish it from a deny — matching\n // the stream-open / continuation hard-error paths.\n yield { type: 'error', message: errMsg };\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n resolveNextTurn(fail(errMsg));\n return;\n }\n\n if (decisionResult.value.action === 'deny') {\n const denialMsg = `${toolName} (callId=${callId}): tool execution denied: ${decisionResult.value.reason}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: denialMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: denialMsg, isError: true });\n continue;\n }\n }\n\n const executeResult = await captureAsyncResult(async () => tool.execute(validationResult.value));\n const executionResult: Result<unknown> = executeResult.isSuccess()\n ? executeResult.value\n : executeResult;\n\n if (executionResult.isFailure()) {\n const errMsg = `${toolName} (callId=${callId}): ${executionResult.message}`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n\n // JSON.stringify can throw (circular references) or return undefined\n // (e.g. for a bare `undefined` value, or a value of type `function`).\n // Either outcome violates the client-tool-result event contract, so\n // emit an isError result with diagnostic context instead.\n let resultStr: string;\n try {\n const stringified = JSON.stringify(executionResult.value);\n if (stringified === undefined) {\n const errMsg = `${toolName} (callId=${callId}): tool returned a non-serializable value (JSON.stringify produced undefined)`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n resultStr = stringified;\n } catch (e) {\n const errMsg = `${toolName} (callId=${callId}): failed to serialize tool result: ${\n (e as Error).message\n }`;\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: errMsg,\n isError: true\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: errMsg, isError: true });\n continue;\n }\n const resultEvent: IAiStreamEvent = {\n type: 'client-tool-result',\n toolName,\n callId,\n result: resultStr,\n isError: false\n };\n yield resultEvent;\n toolResults.push({ toolName, callId, args, result: resultStr, isError: false });\n continue;\n }\n\n // client-tool-result events are emitted by this layer, not the adapters — no passthrough needed.\n }\n\n // Stream has ended. Build the continuation.\n if (streamError !== undefined) {\n resolveNextTurn(fail(streamError));\n return;\n }\n\n if (toolResults.length === 0) {\n resolveNextTurn(succeed({ continuation: undefined, truncated, fullText }));\n return;\n }\n\n let continuationResult: Result<IAiClientToolContinuation>;\n switch (descriptor.apiFormat) {\n case 'anthropic':\n continuationResult = buildAnthropicContinuation(anthropicBuffer, toolResults, logger);\n break;\n case 'openai':\n continuationResult = buildOpenAiContinuation(openAiCallMap, toolResults, logger);\n break;\n case 'gemini':\n // Gemini correlates by tool name by design (no call ids) — its builder\n // cannot mis-key and stays non-fallible.\n continuationResult = succeed(buildGeminiContinuation(geminiCalls, toolResults));\n break;\n /* c8 ignore next 5 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = descriptor.apiFormat;\n resolveNextTurn(fail(`unsupported API format: ${String(_exhaustive)}`));\n return;\n }\n }\n\n // A bad id-correlation fails loud here rather than emitting a malformed\n // continuation the provider would reject as a \"malformed identifier\". Mirror\n // the stream-open-failure path above: surface an `error` event so a consumer\n // iterating `events` sees the failure inline, not only via `nextTurn`.\n if (continuationResult.isFailure()) {\n resolveNextTurn(fail(continuationResult.message));\n yield { type: 'error', message: continuationResult.message };\n return;\n }\n let continuation = continuationResult.value;\n\n // Prepend inbound continuationMessages so the returned continuation is cumulative.\n // A consumer that does `tail = outcome.continuation.messages` (replace) is then\n // correct for all N rounds — messages always contains the full wire tail from\n // round 1 through the current round. toolCallsSummary stays per-round (this\n // round's calls only); messages is the accumulated tail to re-send.\n if (continuationMessages && continuationMessages.length > 0) {\n continuation = { ...continuation, messages: [...continuationMessages, ...continuation.messages] };\n }\n\n resolveNextTurn(succeed({ continuation, truncated, fullText }));\n }\n\n return succeed({\n events: { [Symbol.asyncIterator]: () => eventGenerator() },\n nextTurn\n });\n}\n"]}
|
|
@@ -25,10 +25,20 @@ export interface IProviderCompletionStreamParams extends IChatRequest {
|
|
|
25
25
|
readonly descriptor: IAiProviderDescriptor;
|
|
26
26
|
/** API key for authentication */
|
|
27
27
|
readonly apiKey: string;
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Sampling temperature. Sent to the provider only when explicitly provided; omitted otherwise
|
|
30
|
+
* so the provider's own default applies (current-gen models reject a caller-supplied default).
|
|
31
|
+
*/
|
|
29
32
|
readonly temperature?: number;
|
|
30
33
|
/** Optional model override — string or context-aware map. */
|
|
31
34
|
readonly modelOverride?: ModelSpec;
|
|
35
|
+
/**
|
|
36
|
+
* Optional quality tier selecting which completion model to use. `undefined`
|
|
37
|
+
* selects the `base` tier; `'frontier'` cascades to `advanced` then `base`
|
|
38
|
+
* when a tier is unset for a provider. Orthogonal to `thinking` and `tools`,
|
|
39
|
+
* which never select a model.
|
|
40
|
+
*/
|
|
41
|
+
readonly tier?: 'advanced' | 'frontier';
|
|
32
42
|
/** Optional logger for request/response observability. */
|
|
33
43
|
readonly logger?: Logging.ILogger;
|
|
34
44
|
/** Server-side tools to include in the request. */
|
|
@@ -70,6 +80,19 @@ export interface IStreamApiConfig {
|
|
|
70
80
|
* @internal
|
|
71
81
|
*/
|
|
72
82
|
export declare const UNRECOGNIZED_EVENT_WARN_TAG: string;
|
|
83
|
+
/**
|
|
84
|
+
* Stable prefix every "malformed tool_use block" warning starts with. Emitted
|
|
85
|
+
* when a streaming adapter sees a client tool_use `content_block_start` that is
|
|
86
|
+
* missing a usable correlation id and/or name — a block that, if silently
|
|
87
|
+
* dropped, would orphan its argument deltas and corrupt the follow-up tool
|
|
88
|
+
* continuation (the id-correlation defect class). Production deployments can
|
|
89
|
+
* filter / alert on this exact substring. Distinct from
|
|
90
|
+
* {@link UNRECOGNIZED_EVENT_WARN_TAG} (which is for unrecognized SSE *event
|
|
91
|
+
* names*); this is a malformed *payload* of a recognized event.
|
|
92
|
+
*
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
export declare const MALFORMED_TOOL_USE_WARN_TAG: string;
|
|
73
96
|
/**
|
|
74
97
|
* Environment variable that opts in to **raw payload preview** in the
|
|
75
98
|
* {@link UNRECOGNIZED_EVENT_WARN_TAG} warning. Any non-empty, non-`'0'` value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/common.ts"],"names":[],"mappings":"AAoBA;;;;;;GAMG;AAEH,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,EAAW,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,+BAAgC,SAAQ,YAAY;IACnE,8BAA8B;IAC9B,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/common.ts"],"names":[],"mappings":"AAoBA;;;;;;GAMG;AAEH,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,EAAW,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,+BAAgC,SAAQ,YAAY;IACnE,8BAA8B;IAC9B,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACxC,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAClC,mDAAmD;IACnD,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACnD,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,EAAE,MAAuC,CAAC;AAElF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,EAAE,MAAuC,CAAC;AAclF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,uCAAuC,EAAE,MAAoD,CAAC;AAE3G;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgC1E;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,EACxB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAmC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAG7F"}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
20
|
// SOFTWARE.
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR = exports.UNRECOGNIZED_EVENT_WARN_TAG = void 0;
|
|
22
|
+
exports.UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR = exports.MALFORMED_TOOL_USE_WARN_TAG = exports.UNRECOGNIZED_EVENT_WARN_TAG = void 0;
|
|
23
23
|
exports.formatUnrecognizedEventPayloadPreview = formatUnrecognizedEventPayloadPreview;
|
|
24
24
|
exports.openSseConnection = openSseConnection;
|
|
25
25
|
exports.validateEventPayload = validateEventPayload;
|
|
@@ -42,6 +42,19 @@ const ts_utils_1 = require("@fgv/ts-utils");
|
|
|
42
42
|
* @internal
|
|
43
43
|
*/
|
|
44
44
|
exports.UNRECOGNIZED_EVENT_WARN_TAG = 'ai-assist:unrecognized-event';
|
|
45
|
+
/**
|
|
46
|
+
* Stable prefix every "malformed tool_use block" warning starts with. Emitted
|
|
47
|
+
* when a streaming adapter sees a client tool_use `content_block_start` that is
|
|
48
|
+
* missing a usable correlation id and/or name — a block that, if silently
|
|
49
|
+
* dropped, would orphan its argument deltas and corrupt the follow-up tool
|
|
50
|
+
* continuation (the id-correlation defect class). Production deployments can
|
|
51
|
+
* filter / alert on this exact substring. Distinct from
|
|
52
|
+
* {@link UNRECOGNIZED_EVENT_WARN_TAG} (which is for unrecognized SSE *event
|
|
53
|
+
* names*); this is a malformed *payload* of a recognized event.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
exports.MALFORMED_TOOL_USE_WARN_TAG = 'ai-assist:malformed-tool-use';
|
|
45
58
|
/**
|
|
46
59
|
* Maximum characters of raw SSE payload to include in the
|
|
47
60
|
* {@link UNRECOGNIZED_EVENT_WARN_TAG} warning when raw-preview mode is opted in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/common.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Shared infrastructure for the per-format streaming adapters: HTTP connection\n * helper, common config and request-params types, and a typed-validation\n * helper for SSE event payloads.\n *\n * @packageDocumentation\n */\n\nimport { fail, type Logging, Result, succeed, type Validator } from '@fgv/ts-utils';\n\nimport {\n type AiServerToolConfig,\n type IAiProviderDescriptor,\n type IChatRequest,\n type IThinkingConfig,\n type ModelSpec\n} from '../model';\n\n/**\n * Parameters for a streaming completion request. Structurally identical to\n * the non-streaming `IProviderCompletionParams`; kept as its own interface\n * so callers can be explicit about which path they're invoking.\n *\n * @remarks\n * Carries the unified {@link AiAssist.IChatRequest} shape (`system?` + ordered\n * `messages`): the last message is the current user turn and the preceding\n * messages are history, linearized before the current turn (identical to the\n * completion and client-tool turn paths).\n *\n * @public\n */\nexport interface IProviderCompletionStreamParams extends IChatRequest {\n /** The provider descriptor */\n readonly descriptor: IAiProviderDescriptor;\n /** API key for authentication */\n readonly apiKey: string;\n /** Sampling temperature (default: 0.7) */\n readonly temperature?: number;\n /** Optional model override — string or context-aware map. */\n readonly modelOverride?: ModelSpec;\n /** Optional logger for request/response observability. */\n readonly logger?: Logging.ILogger;\n /** Server-side tools to include in the request. */\n readonly tools?: ReadonlyArray<AiServerToolConfig>;\n /** Optional abort signal for cancelling the in-flight stream. */\n readonly signal?: AbortSignal;\n /**\n * Optional override of the descriptor's default base URL. Same semantics as\n * the non-streaming completion path: a well-formed `http`/`https` URL is\n * substituted for `descriptor.baseUrl` when composing the streaming\n * request, with the per-format suffix appended unchanged. Validated at the\n * dispatcher; auth shape is unaffected.\n */\n readonly endpoint?: string;\n /**\n * Optional thinking/reasoning mode configuration.\n */\n readonly thinking?: IThinkingConfig;\n}\n\n/**\n * Configuration for a single per-format streaming call: where to POST, what\n * key to authenticate with, and which model to request.\n *\n * @internal\n */\nexport interface IStreamApiConfig {\n readonly baseUrl: string;\n readonly apiKey: string;\n readonly model: string;\n}\n\n/**\n * Stable log-line prefix that every streaming adapter's \"unrecognized SSE event\"\n * warning starts with. Production deployments can filter / alert on this exact\n * substring without coupling to the per-adapter detail message. Surfacing this\n * as a shared constant ensures all adapters emit the same prefix; loosening or\n * renaming the prefix is a coordinated, intentional change rather than a per-file\n * accident.\n *\n * @internal\n */\nexport const UNRECOGNIZED_EVENT_WARN_TAG: string = 'ai-assist:unrecognized-event';\n\n/**\n * Maximum characters of raw SSE payload to include in the\n * {@link UNRECOGNIZED_EVENT_WARN_TAG} warning when raw-preview mode is opted in\n * via {@link UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR}. Long enough to identify\n * the JSON shape that arrived (\"the new event carries field X\"), short enough\n * that a hot stream of unknown events with a verbose payload doesn't blow up\n * log volume.\n *\n * @internal\n */\nconst UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX: number = 200;\n\n/**\n * Environment variable that opts in to **raw payload preview** in the\n * {@link UNRECOGNIZED_EVENT_WARN_TAG} warning. Any non-empty, non-`'0'` value\n * activates the raw preview. **Default behavior** (env var absent / empty /\n * `'0'`) is **structural-only** preview — top-level JSON keys + payload byte\n * length, never the values.\n *\n * The default-safe posture exists because unrecognized SSE event payloads can\n * carry tool arguments, tool results, user-conversation text, or other\n * potentially sensitive content. Emitting them verbatim at `warn` level — which\n * is the level explicitly designed to surface in production logs / alerting —\n * is a PII leak waiting to happen.\n *\n * Ops triaging an active drift signal (`ai-assist:unrecognized-event` warnings\n * appearing in production logs after a provider API evolution) can set this\n * env var to widen the preview and see the actual payload shape during\n * investigation, then unset it once the new event family is handled.\n *\n * @internal\n */\nexport const UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR: string = 'AI_ASSIST_UNRECOGNIZED_EVENT_FULL_PAYLOAD';\n\n/**\n * Renders an SSE `data:` payload for inclusion in an unrecognized-event\n * warning.\n *\n * - Empty payload: returns `<no payload>`.\n * - Default (env var unset): returns structural-only preview (e.g.\n * `{ keys: [type, data], length: 1234 }` for a JSON object payload;\n * `<array payload, length=N>` / `<{type} payload, length=N>` /\n * `<non-JSON payload, length=N>` for other shapes). Never includes\n * field values.\n * - With {@link UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR} set to a truthy\n * value: returns the raw payload, newlines collapsed to spaces, capped\n * at {@link UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX} chars with a trailing\n * ellipsis on truncation. **Use this mode only for active drift triage**\n * — it leaks payload content into warn logs.\n *\n * @internal\n */\nexport function formatUnrecognizedEventPayloadPreview(data: string): string {\n if (data.length === 0) return '<no payload>';\n\n // Opt-in raw preview for ops triage. Accessed via `globalThis` (always\n // defined) rather than a bare `process` reference, so webpack/rollup never\n // try to bundle or polyfill `process` for browser consumers. A\n // `typeof process !== 'undefined'` guard is runtime-safe but still triggers\n // webpack's static module resolution on the `process` identifier.\n const proc = (globalThis as unknown as { process?: { env?: Record<string, string | undefined> } }).process;\n const envValue = proc?.env?.[UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR];\n const rawPreviewOptIn = envValue !== undefined && envValue !== '' && envValue !== '0';\n if (rawPreviewOptIn) {\n const collapsed = data.replace(/\\s+/g, ' ');\n return collapsed.length > UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX\n ? `${collapsed.slice(0, UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX)}…`\n : collapsed;\n }\n\n // Default-safe: structural information only. Never emits payload values.\n try {\n const parsed: unknown = JSON.parse(data);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n const keys = Object.keys(parsed);\n return `{ keys: [${keys.join(', ')}], length: ${data.length} }`;\n }\n if (Array.isArray(parsed)) {\n return `<array payload, length=${data.length}>`;\n }\n return `<${typeof parsed} payload, length=${data.length}>`;\n } catch {\n return `<non-JSON payload, length=${data.length}>`;\n }\n}\n\n/**\n * Opens an SSE-style POST connection. Returns the underlying Response on a\n * 2xx; failures (network, non-2xx, missing body) surface as Result.fail\n * carrying the body text.\n *\n * @internal\n */\nexport async function openSseConnection(\n url: string,\n headers: Record<string, string>,\n body: unknown,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<Response>> {\n /* c8 ignore next 1 - optional logger */\n logger?.detail(`AI streaming request: POST ${url}`);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers\n },\n body: JSON.stringify(body),\n signal\n });\n } catch (err: unknown) {\n /* c8 ignore next 1 - defensive: fetch errors are always Error instances in practice */\n const detail = err instanceof Error ? err.message : String(err);\n /* c8 ignore next 1 - optional logger */\n logger?.error(`AI streaming request failed: ${detail}`);\n return fail(`AI streaming request failed: ${detail}`);\n }\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'unknown error');\n /* c8 ignore next 1 - optional logger */\n logger?.error(`AI streaming API returned ${response.status}: ${errorText}`);\n return fail(`AI streaming API returned ${response.status}: ${errorText}`);\n }\n /* c8 ignore next 3 - defensive coding: response.body is always defined for successful fetch responses */\n if (!response.body) {\n return fail('AI streaming API returned an empty body');\n }\n return succeed(response);\n}\n\n/**\n * Validates a parsed SSE event payload against a typed shape, returning the\n * validated value or `undefined` if validation fails. Adapters use the\n * `undefined` return to skip unrecognized event shapes — the same lenient\n * behavior the inline casts had, but with a real type-checked path on the\n * happy case.\n *\n * @internal\n */\nexport function validateEventPayload<T>(json: unknown, validator: Validator<T>): T | undefined {\n const result = validator.validate(json);\n return result.isSuccess() ? result.value : undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/common.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Shared infrastructure for the per-format streaming adapters: HTTP connection\n * helper, common config and request-params types, and a typed-validation\n * helper for SSE event payloads.\n *\n * @packageDocumentation\n */\n\nimport { fail, type Logging, Result, succeed, type Validator } from '@fgv/ts-utils';\n\nimport {\n type AiServerToolConfig,\n type IAiProviderDescriptor,\n type IChatRequest,\n type IThinkingConfig,\n type ModelSpec\n} from '../model';\n\n/**\n * Parameters for a streaming completion request. Structurally identical to\n * the non-streaming `IProviderCompletionParams`; kept as its own interface\n * so callers can be explicit about which path they're invoking.\n *\n * @remarks\n * Carries the unified {@link AiAssist.IChatRequest} shape (`system?` + ordered\n * `messages`): the last message is the current user turn and the preceding\n * messages are history, linearized before the current turn (identical to the\n * completion and client-tool turn paths).\n *\n * @public\n */\nexport interface IProviderCompletionStreamParams extends IChatRequest {\n /** The provider descriptor */\n readonly descriptor: IAiProviderDescriptor;\n /** API key for authentication */\n readonly apiKey: string;\n /**\n * Sampling temperature. Sent to the provider only when explicitly provided; omitted otherwise\n * so the provider's own default applies (current-gen models reject a caller-supplied default).\n */\n readonly temperature?: number;\n /** Optional model override — string or context-aware map. */\n readonly modelOverride?: ModelSpec;\n /**\n * Optional quality tier selecting which completion model to use. `undefined`\n * selects the `base` tier; `'frontier'` cascades to `advanced` then `base`\n * when a tier is unset for a provider. Orthogonal to `thinking` and `tools`,\n * which never select a model.\n */\n readonly tier?: 'advanced' | 'frontier';\n /** Optional logger for request/response observability. */\n readonly logger?: Logging.ILogger;\n /** Server-side tools to include in the request. */\n readonly tools?: ReadonlyArray<AiServerToolConfig>;\n /** Optional abort signal for cancelling the in-flight stream. */\n readonly signal?: AbortSignal;\n /**\n * Optional override of the descriptor's default base URL. Same semantics as\n * the non-streaming completion path: a well-formed `http`/`https` URL is\n * substituted for `descriptor.baseUrl` when composing the streaming\n * request, with the per-format suffix appended unchanged. Validated at the\n * dispatcher; auth shape is unaffected.\n */\n readonly endpoint?: string;\n /**\n * Optional thinking/reasoning mode configuration.\n */\n readonly thinking?: IThinkingConfig;\n}\n\n/**\n * Configuration for a single per-format streaming call: where to POST, what\n * key to authenticate with, and which model to request.\n *\n * @internal\n */\nexport interface IStreamApiConfig {\n readonly baseUrl: string;\n readonly apiKey: string;\n readonly model: string;\n}\n\n/**\n * Stable log-line prefix that every streaming adapter's \"unrecognized SSE event\"\n * warning starts with. Production deployments can filter / alert on this exact\n * substring without coupling to the per-adapter detail message. Surfacing this\n * as a shared constant ensures all adapters emit the same prefix; loosening or\n * renaming the prefix is a coordinated, intentional change rather than a per-file\n * accident.\n *\n * @internal\n */\nexport const UNRECOGNIZED_EVENT_WARN_TAG: string = 'ai-assist:unrecognized-event';\n\n/**\n * Stable prefix every \"malformed tool_use block\" warning starts with. Emitted\n * when a streaming adapter sees a client tool_use `content_block_start` that is\n * missing a usable correlation id and/or name — a block that, if silently\n * dropped, would orphan its argument deltas and corrupt the follow-up tool\n * continuation (the id-correlation defect class). Production deployments can\n * filter / alert on this exact substring. Distinct from\n * {@link UNRECOGNIZED_EVENT_WARN_TAG} (which is for unrecognized SSE *event\n * names*); this is a malformed *payload* of a recognized event.\n *\n * @internal\n */\nexport const MALFORMED_TOOL_USE_WARN_TAG: string = 'ai-assist:malformed-tool-use';\n\n/**\n * Maximum characters of raw SSE payload to include in the\n * {@link UNRECOGNIZED_EVENT_WARN_TAG} warning when raw-preview mode is opted in\n * via {@link UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR}. Long enough to identify\n * the JSON shape that arrived (\"the new event carries field X\"), short enough\n * that a hot stream of unknown events with a verbose payload doesn't blow up\n * log volume.\n *\n * @internal\n */\nconst UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX: number = 200;\n\n/**\n * Environment variable that opts in to **raw payload preview** in the\n * {@link UNRECOGNIZED_EVENT_WARN_TAG} warning. Any non-empty, non-`'0'` value\n * activates the raw preview. **Default behavior** (env var absent / empty /\n * `'0'`) is **structural-only** preview — top-level JSON keys + payload byte\n * length, never the values.\n *\n * The default-safe posture exists because unrecognized SSE event payloads can\n * carry tool arguments, tool results, user-conversation text, or other\n * potentially sensitive content. Emitting them verbatim at `warn` level — which\n * is the level explicitly designed to surface in production logs / alerting —\n * is a PII leak waiting to happen.\n *\n * Ops triaging an active drift signal (`ai-assist:unrecognized-event` warnings\n * appearing in production logs after a provider API evolution) can set this\n * env var to widen the preview and see the actual payload shape during\n * investigation, then unset it once the new event family is handled.\n *\n * @internal\n */\nexport const UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR: string = 'AI_ASSIST_UNRECOGNIZED_EVENT_FULL_PAYLOAD';\n\n/**\n * Renders an SSE `data:` payload for inclusion in an unrecognized-event\n * warning.\n *\n * - Empty payload: returns `<no payload>`.\n * - Default (env var unset): returns structural-only preview (e.g.\n * `{ keys: [type, data], length: 1234 }` for a JSON object payload;\n * `<array payload, length=N>` / `<{type} payload, length=N>` /\n * `<non-JSON payload, length=N>` for other shapes). Never includes\n * field values.\n * - With {@link UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR} set to a truthy\n * value: returns the raw payload, newlines collapsed to spaces, capped\n * at {@link UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX} chars with a trailing\n * ellipsis on truncation. **Use this mode only for active drift triage**\n * — it leaks payload content into warn logs.\n *\n * @internal\n */\nexport function formatUnrecognizedEventPayloadPreview(data: string): string {\n if (data.length === 0) return '<no payload>';\n\n // Opt-in raw preview for ops triage. Accessed via `globalThis` (always\n // defined) rather than a bare `process` reference, so webpack/rollup never\n // try to bundle or polyfill `process` for browser consumers. A\n // `typeof process !== 'undefined'` guard is runtime-safe but still triggers\n // webpack's static module resolution on the `process` identifier.\n const proc = (globalThis as unknown as { process?: { env?: Record<string, string | undefined> } }).process;\n const envValue = proc?.env?.[UNRECOGNIZED_EVENT_FULL_PAYLOAD_ENV_VAR];\n const rawPreviewOptIn = envValue !== undefined && envValue !== '' && envValue !== '0';\n if (rawPreviewOptIn) {\n const collapsed = data.replace(/\\s+/g, ' ');\n return collapsed.length > UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX\n ? `${collapsed.slice(0, UNRECOGNIZED_EVENT_PAYLOAD_PREVIEW_MAX)}…`\n : collapsed;\n }\n\n // Default-safe: structural information only. Never emits payload values.\n try {\n const parsed: unknown = JSON.parse(data);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n const keys = Object.keys(parsed);\n return `{ keys: [${keys.join(', ')}], length: ${data.length} }`;\n }\n if (Array.isArray(parsed)) {\n return `<array payload, length=${data.length}>`;\n }\n return `<${typeof parsed} payload, length=${data.length}>`;\n } catch {\n return `<non-JSON payload, length=${data.length}>`;\n }\n}\n\n/**\n * Opens an SSE-style POST connection. Returns the underlying Response on a\n * 2xx; failures (network, non-2xx, missing body) surface as Result.fail\n * carrying the body text.\n *\n * @internal\n */\nexport async function openSseConnection(\n url: string,\n headers: Record<string, string>,\n body: unknown,\n logger?: Logging.ILogger,\n signal?: AbortSignal\n): Promise<Result<Response>> {\n /* c8 ignore next 1 - optional logger */\n logger?.detail(`AI streaming request: POST ${url}`);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers\n },\n body: JSON.stringify(body),\n signal\n });\n } catch (err: unknown) {\n /* c8 ignore next 1 - defensive: fetch errors are always Error instances in practice */\n const detail = err instanceof Error ? err.message : String(err);\n /* c8 ignore next 1 - optional logger */\n logger?.error(`AI streaming request failed: ${detail}`);\n return fail(`AI streaming request failed: ${detail}`);\n }\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'unknown error');\n /* c8 ignore next 1 - optional logger */\n logger?.error(`AI streaming API returned ${response.status}: ${errorText}`);\n return fail(`AI streaming API returned ${response.status}: ${errorText}`);\n }\n /* c8 ignore next 3 - defensive coding: response.body is always defined for successful fetch responses */\n if (!response.body) {\n return fail('AI streaming API returned an empty body');\n }\n return succeed(response);\n}\n\n/**\n * Validates a parsed SSE event payload against a typed shape, returning the\n * validated value or `undefined` if validation fails. Adapters use the\n * `undefined` return to skip unrecognized event shapes — the same lenient\n * behavior the inline casts had, but with a real type-checked path on the\n * happy case.\n *\n * @internal\n */\nexport function validateEventPayload<T>(json: unknown, validator: Validator<T>): T | undefined {\n const result = validator.validate(json);\n return result.isSuccess() ? result.value : undefined;\n}\n"]}
|
|
@@ -23,6 +23,15 @@ import { IStreamApiConfig } from './common';
|
|
|
23
23
|
export interface IAccumulatedGeminiFunctionCall {
|
|
24
24
|
readonly name: string;
|
|
25
25
|
readonly args: JsonObject;
|
|
26
|
+
/**
|
|
27
|
+
* The opaque thought signature Gemini stamps on a `functionCall` part when
|
|
28
|
+
* thinking is enabled. It must be echoed back, verbatim, on the continuation's
|
|
29
|
+
* model turn alongside the `functionCall`, or Gemini rejects the follow-up with
|
|
30
|
+
* "Function call is missing a thought_signature in functionCall parts".
|
|
31
|
+
* Absent when thinking is disabled. See
|
|
32
|
+
* https://ai.google.dev/gemini-api/docs/thought-signatures.
|
|
33
|
+
*/
|
|
34
|
+
readonly thoughtSignature?: string;
|
|
26
35
|
}
|
|
27
36
|
/**
|
|
28
37
|
* Issues a streaming Gemini request and returns the unified-event iterable
|
|
@@ -30,5 +39,5 @@ export interface IAccumulatedGeminiFunctionCall {
|
|
|
30
39
|
*
|
|
31
40
|
* @internal
|
|
32
41
|
*/
|
|
33
|
-
export declare function callGeminiStream(config: IStreamApiConfig, prompt: AiPrompt, messagesBefore: ReadonlyArray<IChatMessage> | undefined, temperature: number, tools: ReadonlyArray<AiToolConfig> | undefined, logger?: Logging.ILogger, signal?: AbortSignal, resolvedThinking?: IResolvedThinkingConfig, functionCalls?: IAccumulatedGeminiFunctionCall[], continuationMessages?: ReadonlyArray<JsonObject>): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
|
|
42
|
+
export declare function callGeminiStream(config: IStreamApiConfig, prompt: AiPrompt, messagesBefore: ReadonlyArray<IChatMessage> | undefined, temperature: number | undefined, tools: ReadonlyArray<AiToolConfig> | undefined, logger?: Logging.ILogger, signal?: AbortSignal, resolvedThinking?: IResolvedThinkingConfig, functionCalls?: IAccumulatedGeminiFunctionCall[], continuationMessages?: ReadonlyArray<JsonObject>): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
|
|
34
43
|
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/gemini.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAuC,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGlE,OAAO,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAG/F,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAA2C,MAAM,UAAU,CAAC;AAMrF;;;;;GAKG;AACH,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/gemini.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAuC,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGlE,OAAO,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAG/F,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAA2C,MAAM,UAAU,CAAC;AAMrF;;;;;GAKG;AACH,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AA2JD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,QAAQ,EAChB,cAAc,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,SAAS,EACvD,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,SAAS,EAC9C,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,EACxB,MAAM,CAAC,EAAE,WAAW,EACpB,gBAAgB,CAAC,EAAE,uBAAuB,EAC1C,aAAa,CAAC,EAAE,8BAA8B,EAAE,EAChD,oBAAoB,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,GAC/C,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,CAmChD"}
|
|
@@ -64,8 +64,9 @@ const geminiFunctionCallInner = ts_utils_1.Validators.object({
|
|
|
64
64
|
}, { options: { optionalFields: ['name', 'args'] } });
|
|
65
65
|
const geminiStreamPart = ts_utils_1.Validators.object({
|
|
66
66
|
text: ts_utils_1.Validators.string.optional(),
|
|
67
|
-
functionCall: geminiFunctionCallInner.optional()
|
|
68
|
-
|
|
67
|
+
functionCall: geminiFunctionCallInner.optional(),
|
|
68
|
+
thoughtSignature: ts_utils_1.Validators.string.optional()
|
|
69
|
+
}, { options: { optionalFields: ['text', 'functionCall', 'thoughtSignature'] } });
|
|
69
70
|
const geminiStreamContent = ts_utils_1.Validators.object({ parts: ts_utils_1.Validators.arrayOf(geminiStreamPart).optional() }, { options: { optionalFields: ['parts'] } });
|
|
70
71
|
const geminiStreamCandidate = ts_utils_1.Validators.object({
|
|
71
72
|
content: geminiStreamContent.optional(),
|
|
@@ -123,7 +124,10 @@ function translateGeminiStream(response, functionCalls) {
|
|
|
123
124
|
const { name, args } = part.functionCall;
|
|
124
125
|
/* c8 ignore next 1 - defensive: Gemini always sends args; {} fallback unreachable in practice */
|
|
125
126
|
const callArgs = args !== null && args !== void 0 ? args : {};
|
|
126
|
-
|
|
127
|
+
// Capture the part-level thoughtSignature (sibling of functionCall) so the
|
|
128
|
+
// continuation builder can replay it on the model turn. Reads as undefined
|
|
129
|
+
// when thinking is disabled; the builder omits the key in that case.
|
|
130
|
+
functionCalls.push({ name, args: callArgs, thoughtSignature: part.thoughtSignature });
|
|
127
131
|
yield yield __await({ type: 'client-tool-call-done', toolName: name, args: callArgs });
|
|
128
132
|
}
|
|
129
133
|
}
|
|
@@ -170,7 +174,11 @@ async function callGeminiStream(config, prompt, messagesBefore, temperature, too
|
|
|
170
174
|
head: messagesBefore,
|
|
171
175
|
rawTail: continuationMessages
|
|
172
176
|
});
|
|
173
|
-
|
|
177
|
+
// Temperature is sent only when explicitly provided; otherwise Gemini's default applies.
|
|
178
|
+
const generationConfig = {};
|
|
179
|
+
if (temperature !== undefined) {
|
|
180
|
+
generationConfig.temperature = temperature;
|
|
181
|
+
}
|
|
174
182
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.geminiThinkingBudget) !== undefined) {
|
|
175
183
|
generationConfig.thinkingConfig = { thinkingBudget: resolvedThinking.geminiThinkingBudget };
|
|
176
184
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/gemini.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming adapter for Gemini's `streamGenerateContent` endpoint. Gemini\n * emits no explicit tool-progress events even when `google_search` is\n * enabled — grounding metadata arrives attached to text chunks — so this\n * adapter never yields `tool-event`s.\n *\n * Client-defined tools (`functionCall` parts) are emitted as\n * `client-tool-call-done` immediately (no delta accumulation needed for Gemini).\n *\n * @packageDocumentation\n */\n\nimport { type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\nimport { isJsonObject, type JsonObject } from '@fgv/ts-json-base';\n\nimport { buildGeminiContents } from '../chatRequestBuilders';\nimport { AiPrompt, type AiToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { toGeminiTools } from '../toolFormats';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport { IStreamApiConfig, openSseConnection, validateEventPayload } from './common';\n\n// ============================================================================\n// Accumulated call state (internal — used by C3 continuation builder)\n// ============================================================================\n\n/**\n * An accumulated function call from a Gemini stream. Gemini does not assign\n * call IDs; correlation is by tool name. Arguments arrive complete in the\n * `functionCall` part (no delta accumulation).\n * @internal\n */\nexport interface IAccumulatedGeminiFunctionCall {\n readonly name: string;\n readonly args: JsonObject;\n}\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * One `parts[]` element in a Gemini streaming chunk. Text parts and\n * functionCall parts are both surfaced.\n * @internal\n */\ninterface IGeminiStreamPart {\n readonly text?: string;\n readonly functionCall?: { readonly name?: string; readonly args?: JsonObject };\n}\n\n/**\n * One `candidates[]` element in a Gemini streaming chunk.\n * @internal\n */\ninterface IGeminiStreamCandidate {\n readonly content?: { readonly parts?: ReadonlyArray<IGeminiStreamPart> };\n readonly finishReason?: string;\n}\n\n/**\n * One streaming chunk from `streamGenerateContent?alt=sse`.\n * @internal\n */\ninterface IGeminiStreamChunk {\n readonly candidates: ReadonlyArray<IGeminiStreamCandidate>;\n}\n\nconst jsonObjectValidator: Validator<JsonObject> = Validators.isA<JsonObject>(\n 'JsonObject',\n (v): v is JsonObject => isJsonObject(v)\n);\n\nconst geminiFunctionCallInner: Validator<{ name?: string; args?: JsonObject }> = Validators.object<{\n name?: string;\n args?: JsonObject;\n}>(\n {\n name: Validators.string.optional(),\n args: jsonObjectValidator.optional()\n },\n { options: { optionalFields: ['name', 'args'] } }\n);\n\nconst geminiStreamPart: Validator<IGeminiStreamPart> = Validators.object<IGeminiStreamPart>(\n {\n text: Validators.string.optional(),\n functionCall: geminiFunctionCallInner.optional()\n },\n { options: { optionalFields: ['text', 'functionCall'] } }\n);\n\nconst geminiStreamContent: Validator<{ parts?: ReadonlyArray<IGeminiStreamPart> }> = Validators.object<{\n parts?: ReadonlyArray<IGeminiStreamPart>;\n}>({ parts: Validators.arrayOf(geminiStreamPart).optional() }, { options: { optionalFields: ['parts'] } });\n\nconst geminiStreamCandidate: Validator<IGeminiStreamCandidate> = Validators.object<IGeminiStreamCandidate>(\n {\n content: geminiStreamContent.optional(),\n finishReason: Validators.string.optional()\n },\n { options: { optionalFields: ['content', 'finishReason'] } }\n);\n\nconst geminiStreamChunk: Validator<IGeminiStreamChunk> = Validators.object<IGeminiStreamChunk>({\n candidates: Validators.arrayOf(geminiStreamCandidate)\n});\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates a Gemini streamGenerateContent SSE stream into unified events.\n *\n * @internal\n */\nasync function* translateGeminiStream(\n response: Response,\n functionCalls: IAccumulatedGeminiFunctionCall[]\n): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let receivedFinishReason = false;\n\n try {\n /* c8 ignore next - body is non-null at this point per openSseConnection */\n if (!response.body) return;\n for await (const message of readSseEvents(response.body)) {\n const json = parseSseEventJson(message.data);\n /* c8 ignore next 3 - defensive: malformed SSE events skipped */\n if (json === undefined) {\n continue;\n }\n const chunk = validateEventPayload(json, geminiStreamChunk);\n /* c8 ignore next 1 - defensive: chunk?.candidates optional chain unreachable after validation */\n const candidate = chunk?.candidates[0];\n /* c8 ignore next 3 - defensive: SSE events without candidates skipped */\n if (!candidate) {\n continue;\n }\n /* c8 ignore next 1 - defensive: candidate.content?.parts null branch unreachable after validation */\n const parts = candidate.content?.parts;\n if (parts) {\n for (const part of parts) {\n if (typeof part.text === 'string' && part.text.length > 0) {\n fullText += part.text;\n yield { type: 'text-delta', delta: part.text };\n /* c8 ignore next 1 - defensive: functionCall parts without a `name` are silently dropped (cannot construct a continuation builder entry without one) */\n } else if (part.functionCall?.name) {\n const { name, args } = part.functionCall;\n /* c8 ignore next 1 - defensive: Gemini always sends args; {} fallback unreachable in practice */\n const callArgs = args ?? {};\n functionCalls.push({ name, args: callArgs });\n yield { type: 'client-tool-call-done', toolName: name, args: callArgs };\n }\n }\n }\n const finishReason = candidate.finishReason;\n if (typeof finishReason === 'string' && finishReason.length > 0) {\n truncated = finishReason === 'MAX_TOKENS';\n receivedFinishReason = true;\n }\n }\n } catch (err: unknown) /* c8 ignore start - defensive: stream errors are always Error instances */ {\n yield { type: 'error', message: err instanceof Error ? err.message : String(err) };\n return;\n } /* c8 ignore stop */\n\n if (receivedFinishReason) {\n yield { type: 'done', truncated, fullText };\n } else {\n yield { type: 'error', message: 'Gemini stream ended without a finishReason' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Gemini request and returns the unified-event iterable\n * on success.\n *\n * @internal\n */\nexport async function callGeminiStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number,\n tools: ReadonlyArray<AiToolConfig> | undefined,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig,\n functionCalls?: IAccumulatedGeminiFunctionCall[],\n continuationMessages?: ReadonlyArray<JsonObject>\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/models/${config.model}:streamGenerateContent?alt=sse`;\n const contents = buildGeminiContents(prompt, {\n head: messagesBefore,\n rawTail: continuationMessages\n });\n const generationConfig: Record<string, unknown> = { temperature };\n if (resolvedThinking?.geminiThinkingBudget !== undefined) {\n generationConfig.thinkingConfig = { thinkingBudget: resolvedThinking.geminiThinkingBudget };\n }\n if (resolvedThinking?.otherParams !== undefined) {\n Object.assign(generationConfig, resolvedThinking.otherParams);\n }\n const body: Record<string, unknown> = {\n systemInstruction: { parts: [{ text: prompt.system }] },\n contents,\n generationConfig\n };\n /* c8 ignore next 3 - tools branch not exercised in streaming tests */\n if (tools && tools.length > 0) {\n body.tools = toGeminiTools(tools);\n }\n const headers: Record<string, string> = { 'x-goog-api-key': config.apiKey };\n /* c8 ignore next 4 - optional logger diagnostic output */\n if (logger) {\n const toolTypes = tools && tools.length > 0 ? tools.map((t) => t.type).join(',') : 'none';\n logger.info(`Gemini streaming: model=${config.model}, tools=${toolTypes}`);\n }\n const calls = functionCalls ?? [];\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateGeminiStream(response, calls)));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/gemini.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming adapter for Gemini's `streamGenerateContent` endpoint. Gemini\n * emits no explicit tool-progress events even when `google_search` is\n * enabled — grounding metadata arrives attached to text chunks — so this\n * adapter never yields `tool-event`s.\n *\n * Client-defined tools (`functionCall` parts) are emitted as\n * `client-tool-call-done` immediately (no delta accumulation needed for Gemini).\n *\n * @packageDocumentation\n */\n\nimport { type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\nimport { isJsonObject, type JsonObject } from '@fgv/ts-json-base';\n\nimport { buildGeminiContents } from '../chatRequestBuilders';\nimport { AiPrompt, type AiToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { toGeminiTools } from '../toolFormats';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport { IStreamApiConfig, openSseConnection, validateEventPayload } from './common';\n\n// ============================================================================\n// Accumulated call state (internal — used by C3 continuation builder)\n// ============================================================================\n\n/**\n * An accumulated function call from a Gemini stream. Gemini does not assign\n * call IDs; correlation is by tool name. Arguments arrive complete in the\n * `functionCall` part (no delta accumulation).\n * @internal\n */\nexport interface IAccumulatedGeminiFunctionCall {\n readonly name: string;\n readonly args: JsonObject;\n /**\n * The opaque thought signature Gemini stamps on a `functionCall` part when\n * thinking is enabled. It must be echoed back, verbatim, on the continuation's\n * model turn alongside the `functionCall`, or Gemini rejects the follow-up with\n * \"Function call is missing a thought_signature in functionCall parts\".\n * Absent when thinking is disabled. See\n * https://ai.google.dev/gemini-api/docs/thought-signatures.\n */\n readonly thoughtSignature?: string;\n}\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * One `parts[]` element in a Gemini streaming chunk. Text parts and\n * functionCall parts are both surfaced.\n * @internal\n */\ninterface IGeminiStreamPart {\n readonly text?: string;\n readonly functionCall?: { readonly name?: string; readonly args?: JsonObject };\n /**\n * The opaque thought signature attached to a `functionCall` part when thinking\n * is enabled. A sibling of `functionCall`/`text` at the part level (base64\n * string). Captured for replay on the continuation's model turn.\n */\n readonly thoughtSignature?: string;\n}\n\n/**\n * One `candidates[]` element in a Gemini streaming chunk.\n * @internal\n */\ninterface IGeminiStreamCandidate {\n readonly content?: { readonly parts?: ReadonlyArray<IGeminiStreamPart> };\n readonly finishReason?: string;\n}\n\n/**\n * One streaming chunk from `streamGenerateContent?alt=sse`.\n * @internal\n */\ninterface IGeminiStreamChunk {\n readonly candidates: ReadonlyArray<IGeminiStreamCandidate>;\n}\n\nconst jsonObjectValidator: Validator<JsonObject> = Validators.isA<JsonObject>(\n 'JsonObject',\n (v): v is JsonObject => isJsonObject(v)\n);\n\nconst geminiFunctionCallInner: Validator<{ name?: string; args?: JsonObject }> = Validators.object<{\n name?: string;\n args?: JsonObject;\n}>(\n {\n name: Validators.string.optional(),\n args: jsonObjectValidator.optional()\n },\n { options: { optionalFields: ['name', 'args'] } }\n);\n\nconst geminiStreamPart: Validator<IGeminiStreamPart> = Validators.object<IGeminiStreamPart>(\n {\n text: Validators.string.optional(),\n functionCall: geminiFunctionCallInner.optional(),\n thoughtSignature: Validators.string.optional()\n },\n { options: { optionalFields: ['text', 'functionCall', 'thoughtSignature'] } }\n);\n\nconst geminiStreamContent: Validator<{ parts?: ReadonlyArray<IGeminiStreamPart> }> = Validators.object<{\n parts?: ReadonlyArray<IGeminiStreamPart>;\n}>({ parts: Validators.arrayOf(geminiStreamPart).optional() }, { options: { optionalFields: ['parts'] } });\n\nconst geminiStreamCandidate: Validator<IGeminiStreamCandidate> = Validators.object<IGeminiStreamCandidate>(\n {\n content: geminiStreamContent.optional(),\n finishReason: Validators.string.optional()\n },\n { options: { optionalFields: ['content', 'finishReason'] } }\n);\n\nconst geminiStreamChunk: Validator<IGeminiStreamChunk> = Validators.object<IGeminiStreamChunk>({\n candidates: Validators.arrayOf(geminiStreamCandidate)\n});\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates a Gemini streamGenerateContent SSE stream into unified events.\n *\n * @internal\n */\nasync function* translateGeminiStream(\n response: Response,\n functionCalls: IAccumulatedGeminiFunctionCall[]\n): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let receivedFinishReason = false;\n\n try {\n /* c8 ignore next - body is non-null at this point per openSseConnection */\n if (!response.body) return;\n for await (const message of readSseEvents(response.body)) {\n const json = parseSseEventJson(message.data);\n /* c8 ignore next 3 - defensive: malformed SSE events skipped */\n if (json === undefined) {\n continue;\n }\n const chunk = validateEventPayload(json, geminiStreamChunk);\n /* c8 ignore next 1 - defensive: chunk?.candidates optional chain unreachable after validation */\n const candidate = chunk?.candidates[0];\n /* c8 ignore next 3 - defensive: SSE events without candidates skipped */\n if (!candidate) {\n continue;\n }\n /* c8 ignore next 1 - defensive: candidate.content?.parts null branch unreachable after validation */\n const parts = candidate.content?.parts;\n if (parts) {\n for (const part of parts) {\n if (typeof part.text === 'string' && part.text.length > 0) {\n fullText += part.text;\n yield { type: 'text-delta', delta: part.text };\n /* c8 ignore next 1 - defensive: functionCall parts without a `name` are silently dropped (cannot construct a continuation builder entry without one) */\n } else if (part.functionCall?.name) {\n const { name, args } = part.functionCall;\n /* c8 ignore next 1 - defensive: Gemini always sends args; {} fallback unreachable in practice */\n const callArgs = args ?? {};\n // Capture the part-level thoughtSignature (sibling of functionCall) so the\n // continuation builder can replay it on the model turn. Reads as undefined\n // when thinking is disabled; the builder omits the key in that case.\n functionCalls.push({ name, args: callArgs, thoughtSignature: part.thoughtSignature });\n yield { type: 'client-tool-call-done', toolName: name, args: callArgs };\n }\n }\n }\n const finishReason = candidate.finishReason;\n if (typeof finishReason === 'string' && finishReason.length > 0) {\n truncated = finishReason === 'MAX_TOKENS';\n receivedFinishReason = true;\n }\n }\n } catch (err: unknown) /* c8 ignore start - defensive: stream errors are always Error instances */ {\n yield { type: 'error', message: err instanceof Error ? err.message : String(err) };\n return;\n } /* c8 ignore stop */\n\n if (receivedFinishReason) {\n yield { type: 'done', truncated, fullText };\n } else {\n yield { type: 'error', message: 'Gemini stream ended without a finishReason' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Gemini request and returns the unified-event iterable\n * on success.\n *\n * @internal\n */\nexport async function callGeminiStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number | undefined,\n tools: ReadonlyArray<AiToolConfig> | undefined,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig,\n functionCalls?: IAccumulatedGeminiFunctionCall[],\n continuationMessages?: ReadonlyArray<JsonObject>\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/models/${config.model}:streamGenerateContent?alt=sse`;\n const contents = buildGeminiContents(prompt, {\n head: messagesBefore,\n rawTail: continuationMessages\n });\n // Temperature is sent only when explicitly provided; otherwise Gemini's default applies.\n const generationConfig: Record<string, unknown> = {};\n if (temperature !== undefined) {\n generationConfig.temperature = temperature;\n }\n if (resolvedThinking?.geminiThinkingBudget !== undefined) {\n generationConfig.thinkingConfig = { thinkingBudget: resolvedThinking.geminiThinkingBudget };\n }\n if (resolvedThinking?.otherParams !== undefined) {\n Object.assign(generationConfig, resolvedThinking.otherParams);\n }\n const body: Record<string, unknown> = {\n systemInstruction: { parts: [{ text: prompt.system }] },\n contents,\n generationConfig\n };\n /* c8 ignore next 3 - tools branch not exercised in streaming tests */\n if (tools && tools.length > 0) {\n body.tools = toGeminiTools(tools);\n }\n const headers: Record<string, string> = { 'x-goog-api-key': config.apiKey };\n /* c8 ignore next 4 - optional logger diagnostic output */\n if (logger) {\n const toolTypes = tools && tools.length > 0 ? tools.map((t) => t.type).join(',') : 'none';\n logger.info(`Gemini streaming: model=${config.model}, tools=${toolTypes}`);\n }\n const calls = functionCalls ?? [];\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateGeminiStream(response, calls)));\n}\n"]}
|
|
@@ -15,5 +15,5 @@ import { IStreamApiConfig } from './common';
|
|
|
15
15
|
*
|
|
16
16
|
* @internal
|
|
17
17
|
*/
|
|
18
|
-
export declare function callOpenAiChatStream(config: IStreamApiConfig, prompt: AiPrompt, messagesBefore: ReadonlyArray<IChatMessage> | undefined, temperature: number, logger?: Logging.ILogger, signal?: AbortSignal, resolvedThinking?: IResolvedThinkingConfig): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
|
|
18
|
+
export declare function callOpenAiChatStream(config: IStreamApiConfig, prompt: AiPrompt, messagesBefore: ReadonlyArray<IChatMessage> | undefined, temperature: number | undefined, logger?: Logging.ILogger, signal?: AbortSignal, resolvedThinking?: IResolvedThinkingConfig): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
|
|
19
19
|
//# sourceMappingURL=openaiChat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openaiChat.d.ts","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/openaiChat.ts"],"names":[],"mappings":"AAoBA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAuC,MAAM,eAAe,CAAC;AAI1F,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAA2C,MAAM,UAAU,CAAC;AA+GrF;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,QAAQ,EAChB,cAAc,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,SAAS,EACvD,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"openaiChat.d.ts","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/openaiChat.ts"],"names":[],"mappings":"AAoBA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAuC,MAAM,eAAe,CAAC;AAI1F,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAA2C,MAAM,UAAU,CAAC;AA+GrF;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,QAAQ,EAChB,cAAc,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,SAAS,EACvD,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,EACxB,MAAM,CAAC,EAAE,WAAW,EACpB,gBAAgB,CAAC,EAAE,uBAAuB,GACzC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,CA2BhD"}
|
|
@@ -153,7 +153,12 @@ async function callOpenAiChatStream(config, prompt, messagesBefore, temperature,
|
|
|
153
153
|
if (effort !== undefined && supportsReasoning) {
|
|
154
154
|
body.reasoning_effort = effort;
|
|
155
155
|
}
|
|
156
|
-
|
|
156
|
+
// Temperature is sent only when the caller explicitly provided one; omitting it lets the
|
|
157
|
+
// provider apply its own default (current-gen models reject a caller-supplied default). This
|
|
158
|
+
// adapter is reached only via the conflict-guarded streaming entry point — the client-tool path
|
|
159
|
+
// routes OpenAI through the Responses adapter, not chat — so, like callOpenAiCompletion, no
|
|
160
|
+
// effort gate is needed: temperature is present here only when the effort is undefined/'none'.
|
|
161
|
+
if (temperature !== undefined) {
|
|
157
162
|
body.temperature = temperature;
|
|
158
163
|
}
|
|
159
164
|
if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
|