@opencode-ai/ai
Advanced tools
@@ -606,2 +606,3 @@ import { Schema } from "effect"; | ||
| }, { | ||
| providerMetadataKey: string; | ||
| tools: Partial<Record<number, ToolStream.PendingTool>>; | ||
@@ -608,0 +609,0 @@ reasoningSignatures: {}; |
@@ -316,14 +316,14 @@ import { Buffer } from "node:buffer"; | ||
| }; | ||
| const anthropicMetadata = (metadata) => ({ anthropic: metadata }); | ||
| const signatureFromMetadata = (metadata) => { | ||
| const anthropic = metadata?.anthropic; | ||
| if (!ProviderShared.isRecord(anthropic)) | ||
| const providerMetadata = (key, metadata) => ({ [key]: metadata }); | ||
| const signatureFromMetadata = (metadata, key) => { | ||
| const provider = metadata?.[key]; | ||
| if (!ProviderShared.isRecord(provider)) | ||
| return undefined; | ||
| return typeof anthropic.signature === "string" ? anthropic.signature : undefined; | ||
| return typeof provider.signature === "string" ? provider.signature : undefined; | ||
| }; | ||
| const redactedDataFromMetadata = (metadata) => { | ||
| const anthropic = metadata?.anthropic; | ||
| if (!ProviderShared.isRecord(anthropic)) | ||
| const redactedDataFromMetadata = (metadata, key) => { | ||
| const provider = metadata?.[key]; | ||
| if (!ProviderShared.isRecord(provider)) | ||
| return undefined; | ||
| return typeof anthropic.redactedData === "string" ? anthropic.redactedData : undefined; | ||
| return typeof provider.redactedData === "string" ? provider.redactedData : undefined; | ||
| }; | ||
@@ -383,3 +383,3 @@ const lowerTool = (breakpoints, tool, inputSchema) => ({ | ||
| }; | ||
| const lowerServerToolResult = Effect.fn("AnthropicMessages.lowerServerToolResult")(function* (part) { | ||
| const lowerServerToolResult = Effect.fn("AnthropicMessages.lowerServerToolResult")(function* (part, providerMetadataKey) { | ||
| const wireType = serverToolResultType(part.name); | ||
@@ -390,3 +390,3 @@ if (!wireType) | ||
| // histories constructed directly from provider events. | ||
| const payload = part.providerMetadata?.anthropic?.["result"] ?? part.result.value; | ||
| const payload = part.providerMetadata?.[providerMetadataKey]?.["result"] ?? part.result.value; | ||
| return { | ||
@@ -664,2 +664,3 @@ type: wireType, | ||
| const messages = []; | ||
| const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider); | ||
| for (const [index, message] of request.messages.entries()) { | ||
@@ -708,4 +709,4 @@ if (message.role === "system") { | ||
| // redactedData round-trip as opaque redacted_thinking blocks. | ||
| const signature = part.encrypted ?? signatureFromMetadata(part.providerMetadata); | ||
| const redactedData = redactedDataFromMetadata(part.providerMetadata); | ||
| const signature = part.encrypted ?? signatureFromMetadata(part.providerMetadata, providerMetadataKey); | ||
| const redactedData = redactedDataFromMetadata(part.providerMetadata, providerMetadataKey); | ||
| if (signature === undefined && redactedData !== undefined) { | ||
@@ -740,3 +741,3 @@ content.push({ type: "redacted_thinking", data: redactedData }); | ||
| if (part.type === "tool-result" && part.providerExecuted) { | ||
| content.push(yield* lowerServerToolResult(part)); | ||
| content.push(yield* lowerServerToolResult(part, providerMetadataKey)); | ||
| continue; | ||
@@ -906,3 +907,3 @@ } | ||
| // expose that subset through `output_tokens_details.thinking_tokens`. | ||
| const mapUsage = (usage) => { | ||
| const mapUsage = (usage, providerMetadataKey) => { | ||
| if (!usage) | ||
@@ -922,3 +923,3 @@ return undefined; | ||
| totalTokens: ProviderShared.totalTokens(inputTokens, usage.output_tokens, undefined), | ||
| providerMetadata: { anthropic: usage }, | ||
| providerMetadata: { [providerMetadataKey]: usage }, | ||
| }); | ||
@@ -931,3 +932,3 @@ }; | ||
| // consistent with `nonCached + cacheRead + cacheWrite`. | ||
| const mergeUsage = (left, right) => { | ||
| const mergeUsage = (left, right, providerMetadataKey) => { | ||
| if (!left) | ||
@@ -952,3 +953,4 @@ return right; | ||
| providerMetadata: { | ||
| anthropic: mergeJsonRecords(left.providerMetadata?.["anthropic"], right.providerMetadata?.["anthropic"]) ?? {}, | ||
| [providerMetadataKey]: mergeJsonRecords(left.providerMetadata?.[providerMetadataKey], right.providerMetadata?.[providerMetadataKey]) ?? | ||
| {}, | ||
| }, | ||
@@ -968,3 +970,3 @@ }); | ||
| const isServerToolResultType = (type) => type in SERVER_TOOL_RESULT_NAMES; | ||
| const serverToolResultEvent = (block) => { | ||
| const serverToolResultEvent = (block, providerMetadataKey) => { | ||
| if (!block.type || !isServerToolResultType(block.type)) | ||
@@ -983,3 +985,3 @@ return undefined; | ||
| // stateless requests must round-trip the typed result block verbatim. | ||
| providerMetadata: anthropicMetadata({ blockType: block.type, result: block.content }), | ||
| providerMetadata: providerMetadata(providerMetadataKey, { blockType: block.type, result: block.content }), | ||
| }); | ||
@@ -989,4 +991,4 @@ }; | ||
| const onMessageStart = (state, event) => { | ||
| const usage = mapUsage(event.message?.usage); | ||
| return [usage ? { ...state, usage: mergeUsage(state.usage, usage) } : state, NO_EVENTS]; | ||
| const usage = mapUsage(event.message?.usage, state.providerMetadataKey); | ||
| return [usage ? { ...state, usage: mergeUsage(state.usage, usage, state.providerMetadataKey) } : state, NO_EVENTS]; | ||
| }; | ||
@@ -1037,4 +1039,6 @@ const onContentBlockStart = (state, event) => { | ||
| const id = `reasoning-${event.index ?? 0}`; | ||
| const providerMetadata = block.signature === undefined ? undefined : anthropicMetadata({ signature: block.signature }); | ||
| const lifecycle = Lifecycle.reasoningStart(state.lifecycle, events, id, providerMetadata); | ||
| const metadata = block.signature === undefined | ||
| ? undefined | ||
| : providerMetadata(state.providerMetadataKey, { signature: block.signature }); | ||
| const lifecycle = Lifecycle.reasoningStart(state.lifecycle, events, id, metadata); | ||
| return [ | ||
@@ -1044,3 +1048,3 @@ { | ||
| lifecycle: block.thinking | ||
| ? Lifecycle.reasoningDelta(lifecycle, events, id, block.thinking, providerMetadata) | ||
| ? Lifecycle.reasoningDelta(lifecycle, events, id, block.thinking, metadata) | ||
| : lifecycle, | ||
@@ -1062,3 +1066,3 @@ reasoningSignatures: event.index === undefined || block.signature === undefined | ||
| ...state, | ||
| lifecycle: Lifecycle.reasoningStart(state.lifecycle, events, `reasoning-${event.index ?? 0}`, anthropicMetadata({ redactedData: block.data })), | ||
| lifecycle: Lifecycle.reasoningStart(state.lifecycle, events, `reasoning-${event.index ?? 0}`, providerMetadata(state.providerMetadataKey, { redactedData: block.data })), | ||
| }, | ||
@@ -1068,3 +1072,3 @@ events, | ||
| } | ||
| const result = serverToolResultEvent(block); | ||
| const result = serverToolResultEvent(block, state.providerMetadataKey); | ||
| if (!result) | ||
@@ -1134,3 +1138,3 @@ return [state, NO_EVENTS]; | ||
| ? Lifecycle.stepStart(state.lifecycle, events) | ||
| : Lifecycle.reasoningEnd(Lifecycle.textEnd(state.lifecycle, events, `text-${event.index}`), events, `reasoning-${event.index}`, signature === undefined ? undefined : anthropicMetadata({ signature })); | ||
| : Lifecycle.reasoningEnd(Lifecycle.textEnd(state.lifecycle, events, `text-${event.index}`), events, `reasoning-${event.index}`, signature === undefined ? undefined : providerMetadata(state.providerMetadataKey, { signature })); | ||
| events.push(...resultEvents); | ||
@@ -1142,3 +1146,3 @@ const reasoningSignatures = { ...state.reasoningSignatures }; | ||
| const onMessageDelta = (state, event) => { | ||
| const usage = mergeUsage(state.usage, mapUsage(event.usage)); | ||
| const usage = mergeUsage(state.usage, mapUsage(event.usage, state.providerMetadataKey), state.providerMetadataKey); | ||
| return [ | ||
@@ -1155,3 +1159,3 @@ { | ||
| ? undefined | ||
| : anthropicMetadata({ stopSequence: event.delta.stop_sequence }), | ||
| : providerMetadata(state.providerMetadataKey, { stopSequence: event.delta.stop_sequence }), | ||
| }, | ||
@@ -1269,3 +1273,4 @@ }, | ||
| event: Protocol.jsonEvent(AnthropicEvent), | ||
| initial: () => ({ | ||
| initial: (request) => ({ | ||
| providerMetadataKey: request.model.route.providerMetadataKey ?? String(request.model.provider), | ||
| tools: ToolStream.empty(), | ||
@@ -1272,0 +1277,0 @@ reasoningSignatures: {}, |
@@ -130,2 +130,3 @@ import { Schema } from "effect"; | ||
| interface ParserState { | ||
| readonly providerMetadataKey: string; | ||
| readonly tools: ToolStream.State<number>; | ||
@@ -132,0 +133,0 @@ readonly pendingFinish: { |
@@ -184,11 +184,13 @@ import { Effect, Schema } from "effect"; | ||
| }); | ||
| const bedrockMetadata = (metadata) => ({ bedrock: metadata }); | ||
| const reasoningSignature = (part) => { | ||
| const bedrock = part.providerMetadata?.bedrock; | ||
| const providerMetadata = (key, metadata) => ({ [key]: metadata }); | ||
| const reasoningSignature = (part, providerMetadataKey) => { | ||
| const metadata = part.providerMetadata?.[providerMetadataKey]; | ||
| return (part.encrypted ?? | ||
| (ProviderShared.isRecord(bedrock) && typeof bedrock.signature === "string" ? bedrock.signature : undefined)); | ||
| (ProviderShared.isRecord(metadata) && typeof metadata.signature === "string" ? metadata.signature : undefined)); | ||
| }; | ||
| const reasoningRedactedData = (part) => { | ||
| const bedrock = part.providerMetadata?.bedrock; | ||
| return ProviderShared.isRecord(bedrock) && typeof bedrock.redactedData === "string" ? bedrock.redactedData : undefined; | ||
| const reasoningRedactedData = (part, providerMetadataKey) => { | ||
| const metadata = part.providerMetadata?.[providerMetadataKey]; | ||
| return ProviderShared.isRecord(metadata) && typeof metadata.redactedData === "string" | ||
| ? metadata.redactedData | ||
| : undefined; | ||
| }; | ||
@@ -234,2 +236,3 @@ const lowerToolCall = (part) => ({ | ||
| const messages = []; | ||
| const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider); | ||
| for (const message of request.messages) { | ||
@@ -281,4 +284,4 @@ if (message.role === "system") { | ||
| if (part.type === "reasoning") { | ||
| const signature = reasoningSignature(part); | ||
| const redactedData = reasoningRedactedData(part); | ||
| const signature = reasoningSignature(part, providerMetadataKey); | ||
| const redactedData = reasoningRedactedData(part, providerMetadataKey); | ||
| if (signature === undefined && redactedData !== undefined) { | ||
@@ -377,3 +380,3 @@ content.push({ reasoningContent: { redactedContent: redactedData } }); | ||
| // Bedrock does not break reasoning out of outputTokens for current models. | ||
| const mapUsage = (usage) => { | ||
| const mapUsage = (usage, providerMetadataKey) => { | ||
| if (!usage) | ||
@@ -389,3 +392,3 @@ return undefined; | ||
| totalTokens: ProviderShared.totalTokens(inputTokens, usage.outputTokens, usage.totalTokens), | ||
| providerMetadata: { bedrock: usage }, | ||
| providerMetadata: { [providerMetadataKey]: usage }, | ||
| }); | ||
@@ -431,9 +434,9 @@ }; | ||
| const redactedData = reasoning.redactedContent ?? reasoning.data; | ||
| const providerMetadata = reasoning.signature | ||
| ? bedrockMetadata({ signature: reasoning.signature }) | ||
| const metadata = reasoning.signature | ||
| ? providerMetadata(state.providerMetadataKey, { signature: reasoning.signature }) | ||
| : redactedData !== undefined | ||
| ? bedrockMetadata({ redactedData }) | ||
| ? providerMetadata(state.providerMetadataKey, { redactedData }) | ||
| : undefined; | ||
| const lifecycle = reasoning.text !== undefined || providerMetadata !== undefined | ||
| ? Lifecycle.reasoningDelta(state.lifecycle, events, `reasoning-${index}`, reasoning.text ?? "", providerMetadata) | ||
| const lifecycle = reasoning.text !== undefined || metadata !== undefined | ||
| ? Lifecycle.reasoningDelta(state.lifecycle, events, `reasoning-${index}`, reasoning.text ?? "", metadata) | ||
| : state.lifecycle; | ||
@@ -469,3 +472,3 @@ return [ | ||
| : Lifecycle.reasoningEnd(Lifecycle.textEnd(state.lifecycle, events, `text-${index}`), events, `reasoning-${index}`, state.reasoningSignatures[index] | ||
| ? bedrockMetadata({ signature: state.reasoningSignatures[index] }) | ||
| ? providerMetadata(state.providerMetadataKey, { signature: state.reasoningSignatures[index] }) | ||
| : undefined); | ||
@@ -501,3 +504,3 @@ events.push(...resultEvents); | ||
| if (event.metadata) { | ||
| const usage = mapUsage(event.metadata.usage) ?? state.pendingFinish?.usage; | ||
| const usage = mapUsage(event.metadata.usage, state.providerMetadataKey) ?? state.pendingFinish?.usage; | ||
| return [ | ||
@@ -559,3 +562,4 @@ { | ||
| event: BedrockEvent, | ||
| initial: () => ({ | ||
| initial: (request) => ({ | ||
| providerMetadataKey: request.model.route.providerMetadataKey ?? String(request.model.provider), | ||
| tools: ToolStream.empty(), | ||
@@ -562,0 +566,0 @@ pendingFinish: undefined, |
@@ -200,2 +200,3 @@ import { Schema } from "effect"; | ||
| route: string; | ||
| providerMetadataKey: string; | ||
| hasToolCalls: boolean; | ||
@@ -202,0 +203,0 @@ lifecycle: Lifecycle.State; |
+35
-21
@@ -200,15 +200,16 @@ import { Effect, Option, Schema } from "effect"; | ||
| }); | ||
| const googleMetadata = (metadata) => ({ google: metadata }); | ||
| const thoughtSignature = (providerMetadata) => { | ||
| const google = providerMetadata?.google; | ||
| return ProviderShared.isRecord(google) && typeof google.thoughtSignature === "string" | ||
| ? google.thoughtSignature | ||
| const providerMetadata = (key, metadata) => ({ [key]: metadata }); | ||
| const thoughtSignature = (metadata, key) => { | ||
| const value = metadata?.[key]; | ||
| return ProviderShared.isRecord(value) && typeof value.thoughtSignature === "string" | ||
| ? value.thoughtSignature | ||
| : undefined; | ||
| }; | ||
| const lowerToolCall = (part, omitIds) => ({ | ||
| const lowerToolCall = (part, omitIds, metadataKey) => ({ | ||
| functionCall: { ...(omitIds ? {} : { id: part.id }), name: part.name, args: part.input }, | ||
| thoughtSignature: thoughtSignature(part.providerMetadata), | ||
| thoughtSignature: thoughtSignature(part.providerMetadata, metadataKey), | ||
| }); | ||
| const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) { | ||
| const contents = []; | ||
| const metadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider); | ||
| const omitCallIds = omitsFunctionCallIds(request.model.id); | ||
@@ -255,11 +256,15 @@ const legacyToolMedia = routesLegacyToolMedia(request.model.id); | ||
| if (part.type === "text") { | ||
| parts.push({ text: part.text, thoughtSignature: thoughtSignature(part.providerMetadata) }); | ||
| parts.push({ text: part.text, thoughtSignature: thoughtSignature(part.providerMetadata, metadataKey) }); | ||
| continue; | ||
| } | ||
| if (part.type === "reasoning") { | ||
| parts.push({ text: part.text, thought: true, thoughtSignature: thoughtSignature(part.providerMetadata) }); | ||
| parts.push({ | ||
| text: part.text, | ||
| thought: true, | ||
| thoughtSignature: thoughtSignature(part.providerMetadata, metadataKey), | ||
| }); | ||
| continue; | ||
| } | ||
| if (part.type === "tool-call") { | ||
| const lowered = lowerToolCall(part, omitCallIds); | ||
| const lowered = lowerToolCall(part, omitCallIds, metadataKey); | ||
| const signature = lowered.thoughtSignature; | ||
@@ -401,3 +406,3 @@ parts.push({ | ||
| // to produce the inclusive `outputTokens` the rest of the contract expects. | ||
| const mapUsage = (usage) => { | ||
| const mapUsage = (usage, metadataKey) => { | ||
| if (!usage) | ||
@@ -424,3 +429,3 @@ return undefined; | ||
| totalTokens: ProviderShared.totalTokens(promptTokens, outputTokens, usage.totalTokenCount ?? undefined), | ||
| providerMetadata: { google: usage }, | ||
| providerMetadata: providerMetadata(metadataKey, usage), | ||
| }); | ||
@@ -465,5 +470,5 @@ }; | ||
| if (state.reasoningSignature !== undefined) | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", googleMetadata({ thoughtSignature: state.reasoningSignature })); | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", providerMetadata(state.providerMetadataKey, { thoughtSignature: state.reasoningSignature })); | ||
| if (state.textSignature !== undefined) | ||
| lifecycle = Lifecycle.textEnd(lifecycle, events, "text-0", googleMetadata({ thoughtSignature: state.textSignature })); | ||
| lifecycle = Lifecycle.textEnd(lifecycle, events, "text-0", providerMetadata(state.providerMetadataKey, { thoughtSignature: state.textSignature })); | ||
| Lifecycle.finish(lifecycle, events, { | ||
@@ -475,3 +480,5 @@ reason: { | ||
| usage: state.usage, | ||
| providerMetadata: state.promptFeedback === undefined ? undefined : googleMetadata({ promptFeedback: state.promptFeedback }), | ||
| providerMetadata: state.promptFeedback === undefined | ||
| ? undefined | ||
| : providerMetadata(state.providerMetadataKey, { promptFeedback: state.promptFeedback }), | ||
| }); | ||
@@ -484,3 +491,5 @@ return events; | ||
| promptFeedback: event.promptFeedback ?? state.promptFeedback, | ||
| usage: event.usageMetadata ? (mapUsage(event.usageMetadata) ?? state.usage) : state.usage, | ||
| usage: event.usageMetadata | ||
| ? (mapUsage(event.usageMetadata, state.providerMetadataKey) ?? state.usage) | ||
| : state.usage, | ||
| }; | ||
@@ -520,7 +529,9 @@ const candidate = event.candidates?.[0]; | ||
| if (part.thought) { | ||
| lifecycle = Lifecycle.reasoningDelta(lifecycle, events, "reasoning-0", part.text, signature ? googleMetadata({ thoughtSignature: signature }) : undefined); | ||
| lifecycle = Lifecycle.reasoningDelta(lifecycle, events, "reasoning-0", part.text, signature ? providerMetadata(state.providerMetadataKey, { thoughtSignature: signature }) : undefined); | ||
| continue; | ||
| } | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningSignature ? googleMetadata({ thoughtSignature: reasoningSignature }) : undefined); | ||
| lifecycle = Lifecycle.textDelta(lifecycle, events, "text-0", part.text, textSignature ? googleMetadata({ thoughtSignature: textSignature }) : undefined); | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningSignature | ||
| ? providerMetadata(state.providerMetadataKey, { thoughtSignature: reasoningSignature }) | ||
| : undefined); | ||
| lifecycle = Lifecycle.textDelta(lifecycle, events, "text-0", part.text, textSignature ? providerMetadata(state.providerMetadataKey, { thoughtSignature: textSignature }) : undefined); | ||
| textSignature = undefined; | ||
@@ -540,3 +551,5 @@ continue; | ||
| const id = supplied !== undefined && !duplicate ? supplied : `tool_${crypto.randomUUID().replaceAll("-", "")}`; | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningSignature ? googleMetadata({ thoughtSignature: reasoningSignature }) : undefined); | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningSignature | ||
| ? providerMetadata(state.providerMetadataKey, { thoughtSignature: reasoningSignature }) | ||
| : undefined); | ||
| lifecycle = Lifecycle.stepStart(lifecycle, events); | ||
@@ -548,3 +561,3 @@ events.push(LLMEvent.toolCall({ | ||
| providerMetadata: part.thoughtSignature | ||
| ? googleMetadata({ thoughtSignature: part.thoughtSignature }) | ||
| ? providerMetadata(state.providerMetadataKey, { thoughtSignature: part.thoughtSignature }) | ||
| : undefined, | ||
@@ -585,2 +598,3 @@ })); | ||
| route: `${request.model.provider}/${request.model.route.id}`, | ||
| providerMetadataKey: request.model.route.providerMetadataKey ?? String(request.model.provider), | ||
| hasToolCalls: false, | ||
@@ -587,0 +601,0 @@ lifecycle: Lifecycle.initial(), |
@@ -272,2 +272,3 @@ import { Effect, Schema } from "effect"; | ||
| export interface ParserState { | ||
| readonly providerMetadataKey: string; | ||
| readonly tools: ToolStream.State<number>; | ||
@@ -274,0 +275,0 @@ readonly pendingTools: Partial<Record<number, PendingToolDelta>>; |
@@ -210,12 +210,12 @@ import { Effect, Schema } from "effect"; | ||
| const openAICompatibleReasoningContent = (native) => isRecord(native) && typeof native.reasoning_content === "string" ? native.reasoning_content : undefined; | ||
| const reasoningField = (part) => { | ||
| const field = part.providerMetadata?.openai?.reasoningField; | ||
| const reasoningField = (part, providerMetadataKey) => { | ||
| const field = part.providerMetadata?.[providerMetadataKey]?.reasoningField; | ||
| return typeof field === "string" ? field : undefined; | ||
| }; | ||
| const reasoningDetails = (parts, native) => { | ||
| const reasoningDetails = (parts, native, providerMetadataKey) => { | ||
| const observed = parts.flatMap((part) => { | ||
| const details = part.providerMetadata?.openai?.reasoningDetails; | ||
| const details = part.providerMetadata?.[providerMetadataKey]?.reasoningDetails; | ||
| return Array.isArray(details) ? details : []; | ||
| }); | ||
| if (parts.some((part) => Array.isArray(part.providerMetadata?.openai?.reasoningDetails))) | ||
| if (parts.some((part) => Array.isArray(part.providerMetadata?.[providerMetadataKey]?.reasoningDetails))) | ||
| return observed; | ||
@@ -266,6 +266,8 @@ if (isRecord(native) && Array.isArray(native.reasoning_details)) | ||
| const text = reasoning.map((part) => part.text).join(""); | ||
| const details = reasoningDetails(reasoning, message.native?.openaiCompatible); | ||
| const observedField = reasoning.map(reasoningField).find((value) => value !== undefined); | ||
| const details = reasoningDetails(reasoning, message.native?.openaiCompatible, options.providerMetadataKey); | ||
| const observedField = reasoning | ||
| .map((part) => reasoningField(part, options.providerMetadataKey)) | ||
| .find((value) => value !== undefined); | ||
| const nativeReasoning = openAICompatibleReasoningContent(message.native?.openaiCompatible); | ||
| const fullyStructured = reasoning.every((part) => Array.isArray(part.providerMetadata?.openai?.reasoningDetails)); | ||
| const fullyStructured = reasoning.every((part) => Array.isArray(part.providerMetadata?.[options.providerMetadataKey]?.reasoningDetails)); | ||
| const field = (() => { | ||
@@ -364,2 +366,3 @@ if (configuredField !== undefined && (requireReasoning || reasoning.length > 0 || nativeReasoning !== undefined)) | ||
| ...options, | ||
| providerMetadataKey: request.model.route.providerMetadataKey ?? String(request.model.provider), | ||
| toolCallID: (id) => { | ||
@@ -650,3 +653,3 @@ if (mistral) | ||
| // `prompt_cache_hit_tokens`, and Zai uses top-level `cached_tokens`. | ||
| const mapUsage = (usage) => { | ||
| const mapUsage = (usage, providerMetadataKey) => { | ||
| if (!usage) | ||
@@ -671,3 +674,3 @@ return undefined; | ||
| totalTokens: ProviderShared.totalTokens(input, output, usage.total_tokens ?? undefined), | ||
| providerMetadata: { openai: usage }, | ||
| providerMetadata: { [providerMetadataKey]: usage }, | ||
| }); | ||
@@ -733,4 +736,4 @@ }; | ||
| const conflictingDetailValue = (previous, current) => previous !== undefined && previous !== null && current !== undefined && current !== null && previous !== current; | ||
| const reasoningMetadata = (field, details) => ({ | ||
| openai: { | ||
| const reasoningMetadata = (providerMetadataKey, field, details) => ({ | ||
| [providerMetadataKey]: { | ||
| ...(field ? { reasoningField: field } : {}), | ||
@@ -760,3 +763,5 @@ ...(details ? { reasoningDetails: details } : {}), | ||
| const choiceUsage = choice?.usage; | ||
| const usage = mapUsage(event.usage) ?? (choiceUsage ? mapUsage(choiceUsage) : undefined) ?? state.usage; | ||
| const usage = mapUsage(event.usage, state.providerMetadataKey) ?? | ||
| (choiceUsage ? mapUsage(choiceUsage, state.providerMetadataKey) : undefined) ?? | ||
| state.usage; | ||
| const rawFinishReason = choice?.finish_reason; | ||
@@ -792,3 +797,3 @@ const finishReason = rawFinishReason | ||
| const reasoningDetailsObserved = state.reasoningDetailsObserved || detailDelta !== undefined; | ||
| const deltaMetadata = reasoningMetadata(reasoningField); | ||
| const deltaMetadata = reasoningMetadata(state.providerMetadataKey, reasoningField); | ||
| const text = detailDelta?.length ? (detailText(detailDelta) ?? reasoning?.text) : reasoning?.text; | ||
@@ -803,7 +808,7 @@ if (text !== undefined) | ||
| if (delta?.content) { | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningMetadata(reasoningField, reasoningDetailsObserved ? state.reasoningDetails : undefined)); | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningMetadata(state.providerMetadataKey, reasoningField, reasoningDetailsObserved ? state.reasoningDetails : undefined)); | ||
| lifecycle = Lifecycle.textDelta(lifecycle, events, "text-0", delta.content); | ||
| } | ||
| if (delta?.refusal) { | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningMetadata(reasoningField, reasoningDetailsObserved ? state.reasoningDetails : undefined)); | ||
| lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningMetadata(state.providerMetadataKey, reasoningField, reasoningDetailsObserved ? state.reasoningDetails : undefined)); | ||
| lifecycle = Lifecycle.textDelta(lifecycle, events, "text-0", delta.refusal); | ||
@@ -853,2 +858,3 @@ } | ||
| { | ||
| providerMetadataKey: state.providerMetadataKey, | ||
| tools: finished?.tools ?? tools, | ||
@@ -893,5 +899,5 @@ pendingTools, | ||
| : { normalized: hasToolCalls ? "tool-calls" : "stop" }; | ||
| const metadata = reasoningMetadata(state.reasoningField, state.reasoningDetailsObserved ? state.reasoningDetails : undefined); | ||
| const metadata = reasoningMetadata(state.providerMetadataKey, state.reasoningField, state.reasoningDetailsObserved ? state.reasoningDetails : undefined); | ||
| const started = state.reasoningDetailsObserved && !state.reasoningEmitted | ||
| ? Lifecycle.reasoningStart(state.lifecycle, events, "reasoning-0", reasoningMetadata(state.reasoningField)) | ||
| ? Lifecycle.reasoningStart(state.lifecycle, events, "reasoning-0", reasoningMetadata(state.providerMetadataKey, state.reasoningField)) | ||
| : state.lifecycle; | ||
@@ -922,2 +928,3 @@ const ended = Lifecycle.reasoningEnd(started, events, "reasoning-0", metadata); | ||
| initial: (request) => ({ | ||
| providerMetadataKey: request.model.route.providerMetadataKey ?? String(request.model.provider), | ||
| tools: ToolStream.empty(), | ||
@@ -924,0 +931,0 @@ pendingTools: {}, |
@@ -20,2 +20,3 @@ import { Route, type RouteDefaultsInput } from "../route/client.js"; | ||
| readonly region?: string; | ||
| readonly topP?: number; | ||
| readonly providerOptions?: OpenAIProviderOptionsInput; | ||
@@ -22,0 +23,0 @@ } |
@@ -12,3 +12,3 @@ import { Auth } from "../route/auth.js"; | ||
| provider: id, | ||
| providerMetadataKey: OpenAIResponses.route.providerMetadataKey, | ||
| providerMetadataKey: "mantle", | ||
| protocol: OpenAIResponses.protocol, | ||
@@ -23,2 +23,3 @@ endpoint: OpenAIResponses.route.endpoint, | ||
| provider: id, | ||
| providerMetadataKey: "mantle", | ||
| }); | ||
@@ -68,2 +69,3 @@ export const routes = [responsesRoute, chatRoute]; | ||
| credentials: settings.credentials, | ||
| generation: settings.topP === undefined ? undefined : { topP: settings.topP }, | ||
| headers: settings.headers === undefined ? undefined : { ...settings.headers }, | ||
@@ -70,0 +72,0 @@ http: settings.body === undefined ? undefined : { body: { ...settings.body } }, |
@@ -13,2 +13,3 @@ import { Auth } from "../route/auth.js"; | ||
| provider: id, | ||
| providerMetadataKey: "bedrock", | ||
| endpoint: { baseURL: baseURL ?? bedrockBaseURL(resolvedRegion) }, | ||
@@ -15,0 +16,0 @@ auth: apiKey === undefined ? BedrockConverse.sigV4Auth(credentials) : Auth.bearer(apiKey), |
@@ -8,2 +8,3 @@ import { OpenAICompatibleChat } from "../protocols/openai-compatible-chat.js"; | ||
| provider: id, | ||
| providerMetadataKey: "vertex", | ||
| }); | ||
@@ -10,0 +11,0 @@ export const routes = [route]; |
@@ -8,2 +8,3 @@ import { OpenAICompatibleResponses } from "../protocols/openai-compatible-responses.js"; | ||
| provider: id, | ||
| providerMetadataKey: "vertex", | ||
| providerOptions: { store: false }, | ||
@@ -10,0 +11,0 @@ }); |
@@ -41,3 +41,3 @@ import { Effect } from "effect"; | ||
| provider: id, | ||
| providerMetadataKey: "google", | ||
| providerMetadataKey: "vertex", | ||
| protocol, | ||
@@ -44,0 +44,0 @@ endpoint: Endpoint.path(({ request }) => { |
@@ -87,2 +87,3 @@ import { Effect, Schema } from "effect"; | ||
| provider: profile.provider, | ||
| providerMetadataKey: "openrouter", | ||
| protocol, | ||
@@ -89,0 +90,0 @@ endpoint: Endpoint.path("/chat/completions", { baseURL: profile.baseURL }), |
@@ -52,2 +52,3 @@ import { Context, Effect, Layer, Schema, Stream } from "effect"; | ||
| readonly provider?: string | ProviderID; | ||
| readonly providerMetadataKey?: string; | ||
| readonly auth?: Auth.Definition; | ||
@@ -54,0 +55,0 @@ readonly transport?: Transport<Body, Prepared, unknown>; |
@@ -101,3 +101,3 @@ import { Cause, Context, Effect, Layer, Schema, Stream } from "effect"; | ||
| with: (patch) => { | ||
| const { id, provider, auth, transport, endpoint, ...defaults } = patch; | ||
| const { id, provider, providerMetadataKey, auth, transport, endpoint, ...defaults } = patch; | ||
| return build({ | ||
@@ -107,2 +107,6 @@ ...routeInput, | ||
| provider: provider ?? routeInput.provider, | ||
| providerMetadataKey: providerMetadataKey ?? | ||
| (provider !== undefined && String(provider) !== String(routeInput.provider) | ||
| ? String(provider) | ||
| : routeInput.providerMetadataKey), | ||
| auth: auth ?? routeInput.auth, | ||
@@ -109,0 +113,0 @@ endpoint: endpoint ? Endpoint.merge(routeInput.endpoint, endpoint) : routeInput.endpoint, |
+3
-3
| { | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "version": "0.0.0-dev-18329", | ||
| "version": "0.0.0-dev-18332", | ||
| "name": "@opencode-ai/ai", | ||
@@ -33,3 +33,3 @@ "type": "module", | ||
| "@effect/platform-node": "4.0.0-rc.111", | ||
| "@opencode-ai/http-recorder": "0.0.0-dev-18329", | ||
| "@opencode-ai/http-recorder": "0.0.0-dev-18332", | ||
| "@tsconfig/bun": "1.0.9", | ||
@@ -43,3 +43,3 @@ "@types/bun": "1.3.13", | ||
| "@smithy/util-utf8": "4.2.2", | ||
| "@opencode-ai/schema": "0.0.0-dev-18329", | ||
| "@opencode-ai/schema": "0.0.0-dev-18332", | ||
| "aws4fetch": "1.0.20", | ||
@@ -46,0 +46,0 @@ "effect": "4.0.0-rc.111", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1366268
0.25%31427
0.15%+ Added
- Removed