@standardagents/openrouter 0.10.0-dev.ffffff → 0.11.0-next.24f9ff1

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/index.js CHANGED
@@ -146,6 +146,11 @@ function transformMessages(messages) {
146
146
  instructions = instructions ? `${instructions}
147
147
 
148
148
  ${msg.content}` : msg.content;
149
+ input.push({
150
+ type: "message",
151
+ role: "system",
152
+ content: msg.content
153
+ });
149
154
  break;
150
155
  case "user":
151
156
  input.push(transformUserMessage(msg));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/OpenRouterProvider.ts","../src/transformers.ts","../src/icons.ts","../src/providerOptions.ts","../src/index.ts"],"sourcesContent":["import type {\n LLMProviderInterface as Provider,\n ProviderFactoryConfig,\n ProviderRequest,\n ProviderResponse,\n ProviderStreamChunk,\n ModelCapabilities,\n ProviderModelInfo,\n InspectedRequest,\n ResponseSummary,\n} from '@standardagents/spec';\nimport { ProviderError } from '@standardagents/spec';\nimport {\n buildCreateParams,\n transformResponse,\n createStreamState,\n parseSSEStream,\n createErrorChunk,\n truncateBase64,\n fetchGenerationMetadata,\n} from './transformers';\nimport {\n getModelIconDataUri,\n svgToDataUri,\n OPENROUTER_ICON,\n} from './icons';\n\nimport type { OpenRouter as OpenRouterClient } from '@openrouter/sdk';\n\n// Re-export ProviderError for consumers\nexport { ProviderError };\n\n/**\n * OpenRouter model info from their API\n */\ninterface OpenRouterModelInfo {\n id: string;\n name: string;\n context_length: number;\n top_provider?: {\n max_completion_tokens?: number;\n };\n architecture?: {\n modality?: string;\n tokenizer?: string;\n instruct_type?: string;\n };\n pricing?: {\n prompt: string;\n completion: string;\n image?: string;\n request?: string;\n };\n}\n\n/**\n * Extended provider config for OpenRouter\n */\nexport interface OpenRouterConfig extends ProviderFactoryConfig {\n /** List of providers to route to (e.g., ['openai', 'anthropic']) */\n providers?: string[];\n}\n\n/**\n * OpenRouter provider implementation for Standard Agents\n *\n * Uses the official @openrouter/sdk Responses API for API calls.\n * OpenRouter is a multi-provider gateway that routes requests to various LLM providers.\n */\nexport class OpenRouterProvider implements Provider {\n readonly name = 'openrouter';\n readonly specificationVersion = '1' as const;\n\n private client: OpenRouterClient | null = null;\n private config: OpenRouterConfig;\n\n constructor(config: OpenRouterConfig) {\n this.config = config;\n }\n\n private async getClient(): Promise<OpenRouterClient> {\n if (!this.client) {\n const { OpenRouter } = await import('@openrouter/sdk');\n this.client = new OpenRouter({\n apiKey: this.config.apiKey,\n serverURL: this.config.baseUrl,\n timeoutMs: this.config.timeout,\n });\n }\n return this.client;\n }\n\n supportsModel(_modelId: string): boolean {\n // OpenRouter supports a wide variety of models\n // Model support is determined at runtime\n return true;\n }\n\n /**\n * Get the icon for a model as a data URI.\n * Extracts the AI lab/organization from the model ID prefix and returns\n * the corresponding SVG icon as a data URI.\n * For example, 'anthropic/claude-3-opus' returns the Anthropic icon.\n * Falls back to the OpenRouter icon for unknown labs.\n */\n getIcon(modelId?: string): string {\n if (!modelId) {\n return svgToDataUri(OPENROUTER_ICON);\n }\n\n // Try to get model-specific icon (extracts lab from model ID)\n const modelIcon = getModelIconDataUri(modelId);\n if (modelIcon) {\n return modelIcon;\n }\n\n // Fall back to OpenRouter icon\n return svgToDataUri(OPENROUTER_ICON);\n }\n\n // ============================================================================\n // Model Capabilities\n // ============================================================================\n\n /** Cache for model capabilities to avoid repeated API calls */\n private static modelsCache: OpenRouterModelInfo[] | null = null;\n private static modelsCacheTime = 0;\n private static readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\n /**\n * Get capabilities for a specific model.\n * Fetches from OpenRouter's models API which provides rich metadata.\n */\n async getModelCapabilities(modelId: string): Promise<ModelCapabilities | null> {\n try {\n const models = await this.fetchModelsWithCache();\n const model = models.find((m) => m.id === modelId);\n\n if (!model) {\n return null;\n }\n\n return this.mapOpenRouterCapabilities(model);\n } catch (error) {\n console.error(`Failed to fetch capabilities for ${modelId}:`, error);\n return null;\n }\n }\n\n private async fetchModelsWithCache(): Promise<OpenRouterModelInfo[]> {\n const now = Date.now();\n\n // Return cached data if still valid\n if (\n OpenRouterProvider.modelsCache &&\n now - OpenRouterProvider.modelsCacheTime < OpenRouterProvider.CACHE_TTL\n ) {\n return OpenRouterProvider.modelsCache;\n }\n\n // Fetch from OpenRouter API\n const response = await fetch('https://openrouter.ai/api/v1/models');\n\n if (!response.ok) {\n throw new Error(`OpenRouter API error: ${response.status}`);\n }\n\n const data = (await response.json()) as { data: OpenRouterModelInfo[] };\n OpenRouterProvider.modelsCache = data.data || [];\n OpenRouterProvider.modelsCacheTime = now;\n\n return OpenRouterProvider.modelsCache;\n }\n\n private mapOpenRouterCapabilities(model: OpenRouterModelInfo): ModelCapabilities {\n const capabilities: ModelCapabilities = {\n maxContextTokens: model.context_length,\n supportsStreaming: true, // OpenRouter always supports streaming\n supportsToolCalls: true, // Most models support it, exceptions below\n supportsJsonMode: true, // Most modern models support it\n };\n\n // Determine image support from modality or architecture\n if (\n model.architecture?.modality?.includes('image') ||\n model.architecture?.modality?.includes('multimodal') ||\n model.architecture?.modality === 'text+image->text'\n ) {\n capabilities.supportsImages = true;\n } else {\n capabilities.supportsImages = false;\n }\n\n // Max output tokens from top provider info\n if (model.top_provider?.max_completion_tokens) {\n capabilities.maxOutputTokens = model.top_provider.max_completion_tokens;\n }\n\n // Reasoning levels for specific model families\n if (model.id.includes('o1') || model.id.includes('o3') || model.id.includes('o4')) {\n capabilities.reasoningLevels = { 0: null, 33: 'low', 66: 'medium', 100: 'high' };\n // o1-preview and o1-mini don't support tools\n if (model.id.includes('o1-preview') || model.id.includes('o1-mini')) {\n capabilities.supportsToolCalls = false;\n capabilities.supportsJsonMode = false;\n }\n } else if (model.id.includes('claude') && model.id.includes('thinking')) {\n capabilities.reasoningLevels = { 0: null, 100: 'enabled' };\n }\n\n return capabilities;\n }\n\n /**\n * Get list of available models from OpenRouter.\n * Fetches from OpenRouter's public models API with caching.\n *\n * @param filter - Optional search string to filter models by name/id\n */\n async getModels(filter?: string): Promise<ProviderModelInfo[]> {\n try {\n const rawModels = await this.fetchModelsWithCache();\n\n let models = rawModels.map((m) => this.mapToProviderModelInfo(m));\n\n if (filter) {\n const lowerFilter = filter.toLowerCase();\n models = models.filter(\n (m) =>\n m.id.toLowerCase().includes(lowerFilter) ||\n m.name.toLowerCase().includes(lowerFilter) ||\n (m.description && m.description.toLowerCase().includes(lowerFilter))\n );\n }\n\n return models;\n } catch (error) {\n console.error('Failed to fetch models from OpenRouter:', error);\n return [];\n }\n }\n\n private mapToProviderModelInfo(model: OpenRouterModelInfo): ProviderModelInfo {\n return {\n id: model.id,\n name: model.name,\n description: model.architecture?.modality || undefined,\n contextLength: model.context_length,\n iconId: this.getIcon(model.id),\n slug: model.id.replace(/[/:]/g, '-'), // URL-safe slug\n };\n }\n\n // ============================================================================\n // Generation Methods\n // ============================================================================\n\n async generate(request: ProviderRequest): Promise<ProviderResponse> {\n const client = await this.getClient();\n\n try {\n const params = buildCreateParams(request);\n\n // Add provider routing if configured\n if (this.config.providers && this.config.providers.length > 0) {\n (params as any).provider = { only: this.config.providers };\n }\n\n const response = await client.beta.responses.send({\n ...params,\n stream: false,\n });\n\n return transformResponse(response);\n } catch (error) {\n throw this.toProviderError(error);\n }\n }\n\n async stream(request: ProviderRequest): Promise<AsyncIterable<ProviderStreamChunk>> {\n const self = this;\n const apiKey = this.config.apiKey;\n const baseUrl = this.config.baseUrl || 'https://openrouter.ai/api/v1';\n\n try {\n const params = buildCreateParams(request);\n\n // Add provider routing if configured\n if (this.config.providers && this.config.providers.length > 0) {\n (params as any).provider = { only: this.config.providers };\n }\n\n // Use raw fetch instead of SDK to bypass Zod validation issues\n // SDK validation fails when OpenRouter returns null for usage in stream events\n const response = await fetch(`${baseUrl}/responses`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n ...params,\n stream: true,\n }),\n signal: request.signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage = `OpenRouter API error: ${response.status}`;\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.error?.message || errorJson.message || errorMessage;\n } catch {\n errorMessage = errorText || errorMessage;\n }\n throw new ProviderError(errorMessage, 'invalid_request', response.status);\n }\n\n return {\n async *[Symbol.asyncIterator]() {\n const state = createStreamState();\n let finishChunk: ProviderStreamChunk | null = null;\n let responseId: string | null = null;\n\n try {\n // Process stream and buffer the finish chunk\n for await (const chunk of parseSSEStream(response, state)) {\n if (chunk.type === 'finish') {\n // Buffer the finish chunk and extract responseId\n finishChunk = chunk;\n responseId = (chunk as any).responseId || null;\n } else {\n // Yield all other chunks immediately\n yield chunk;\n }\n }\n\n // Yield finish chunk immediately - don't block on metadata fetch\n // Include responseId so caller can fetch metadata asynchronously if needed\n if (finishChunk) {\n // Add metadata fetch info to the chunk for async fetching\n const finishWithMeta = {\n ...finishChunk,\n // Include what's needed to fetch metadata later\n _asyncMetadata: responseId ? {\n generationId: responseId,\n apiKey,\n baseUrl,\n } : undefined,\n };\n yield finishWithMeta;\n }\n } catch (error) {\n const providerError = self.toProviderError(error);\n yield createErrorChunk(providerError.message, providerError.code);\n }\n },\n };\n } catch (error) {\n throw this.toProviderError(error);\n }\n }\n\n // ============================================================================\n // Response Metadata (Async)\n // ============================================================================\n\n /**\n * Fetch additional metadata about a completed response.\n * Called asynchronously after the response is received.\n *\n * Uses OpenRouter's /generation endpoint to fetch accurate provider info,\n * token counts, and cost data.\n */\n async getResponseMetadata(\n summary: ResponseSummary,\n signal?: AbortSignal\n ): Promise<Record<string, unknown> | null> {\n if (!summary.responseId) {\n return null;\n }\n\n const metadata = await fetchGenerationMetadata(\n this.config.apiKey,\n summary.responseId,\n this.config.baseUrl || 'https://openrouter.ai/api/v1',\n signal\n );\n\n if (!metadata) {\n return null;\n }\n\n return {\n actual_provider: metadata.providerName,\n native_tokens_prompt: metadata.nativePromptTokens,\n native_tokens_completion: metadata.nativeCompletionTokens,\n generation_cost: metadata.totalCost,\n generation_latency: metadata.latency,\n };\n }\n\n // ============================================================================\n // Error Handling\n // ============================================================================\n\n private toProviderError(error: unknown): ProviderError {\n if (error instanceof ProviderError) {\n return error;\n }\n\n if (error instanceof Error) {\n const anyError = error as any;\n const status = anyError.status || anyError.statusCode;\n const retryAfter = anyError.headers?.['retry-after']\n ? parseInt(anyError.headers['retry-after'], 10)\n : undefined;\n\n if (status === 429) {\n return new ProviderError(error.message, 'rate_limit', status, retryAfter);\n }\n if (status === 401 || status === 403) {\n return new ProviderError(error.message, 'auth_error', status);\n }\n if (status === 400) {\n return new ProviderError(error.message, 'invalid_request', status);\n }\n if (status >= 500) {\n return new ProviderError(error.message, 'server_error', status);\n }\n if (error.name === 'AbortError' || anyError.code === 'ETIMEDOUT') {\n return new ProviderError(error.message, 'timeout');\n }\n\n return new ProviderError(error.message, 'unknown', status);\n }\n\n return new ProviderError(String(error), 'unknown');\n }\n\n // ============================================================================\n // Inspection\n // ============================================================================\n\n /**\n * Transform a ProviderRequest to OpenRouter Responses API format for inspection.\n * Returns the exact request body that would be sent to OpenRouter, with base64 data truncated.\n */\n async inspectRequest(request: ProviderRequest): Promise<InspectedRequest> {\n const params = buildCreateParams(request);\n\n // Add provider routing if configured\n if (this.config.providers && this.config.providers.length > 0) {\n (params as any).provider = { only: this.config.providers };\n }\n\n return {\n body: truncateBase64(params as unknown as Record<string, unknown>),\n messagesPath: 'input',\n metadata: {\n endpoint: 'https://openrouter.ai/api/v1/responses',\n },\n };\n }\n}\n","/**\n * Discrete transformer functions for OpenRouter Responses API provider.\n * These functions convert between Standard Agent types and OpenRouter Responses API types.\n * Kept as pure functions for easy testing.\n */\n\nimport type {\n ProviderRequest,\n ProviderResponse,\n ProviderMessage,\n ProviderTool,\n ProviderToolCallPart,\n ProviderFinishReason,\n ProviderUsage,\n ProviderStreamChunk,\n ContentPart,\n ProviderGeneratedImage,\n} from '@standardagents/spec';\n\nimport type {\n OpenResponsesRequest,\n OpenResponsesEasyInputMessage,\n OpenResponsesFunctionCallOutput,\n OpenResponsesNonStreamingResponse,\n OpenResponsesStreamEvent,\n ResponseInputText,\n ResponseInputImage,\n ResponsesOutputItem,\n ResponsesOutputMessage,\n ResponsesOutputItemFunctionCall,\n OpenResponsesFunctionToolCall,\n OpenResponsesRequestToolFunction,\n} from '@openrouter/sdk/models';\n\n// Type for input items (non-string variant of OpenResponsesInput)\ntype InputItem = OpenResponsesFunctionToolCall | ResponsesOutputMessage | ResponsesOutputItemFunctionCall | OpenResponsesFunctionCallOutput | OpenResponsesEasyInputMessage;\n\n/**\n * Result type for transformToolMessage.\n * Contains the tool output and an optional synthetic user message for attachments.\n * OpenRouter doesn't support images in function_call_output, so we inject them\n * as synthetic user messages after the tool output.\n */\nexport type ToolMessageTransformResult = {\n toolOutput: OpenResponsesFunctionCallOutput;\n syntheticUserMessage?: OpenResponsesEasyInputMessage;\n};\n\n// ============================================================================\n// Type aliases for clarity\n// ============================================================================\n\n/**\n * Content type for function call output with multimodal content.\n * Note: The API accepts arrays here, but SDK types only allow strings.\n * We serialize this to JSON for type safety.\n */\ntype FunctionCallOutputContent =\n | { type: 'input_text'; text: string }\n | { type: 'input_image'; image_url: string; detail?: 'auto' | 'low' | 'high' };\n\n// ============================================================================\n// Input Content Transformers\n// ============================================================================\n\n/**\n * Transform a single content part to Responses API input format.\n *\n * IMPORTANT: We use snake_case field names (image_url) because we bypass the SDK\n * and send raw JSON to the API. The SDK would normally convert camelCase to snake_case.\n */\nexport function transformContentPart(\n part: ContentPart\n): ResponseInputText | ResponseInputImage {\n if (part.type === 'text') {\n return { type: 'input_text', text: part.text };\n }\n if (part.type === 'image') {\n const data = part.data || '';\n const imageUrl = data.startsWith('data:')\n ? data\n : `data:${part.mediaType || 'image/png'};base64,${data}`;\n // Use snake_case image_url for raw API (SDK types use camelCase internally)\n return {\n type: 'input_image',\n image_url: imageUrl,\n detail: part.detail || 'auto',\n } as unknown as ResponseInputImage;\n }\n // Handle image_url format (stored format uses this instead of 'image')\n if (part.type === 'image_url') {\n const url = part.image_url?.url || '';\n const detail = part.image_url?.detail || 'auto';\n // Use snake_case image_url for raw API (SDK types use camelCase internally)\n return {\n type: 'input_image',\n image_url: url,\n detail,\n } as unknown as ResponseInputImage;\n }\n // File parts - convert to text placeholder (OpenRouter doesn't support files directly)\n return {\n type: 'input_text',\n text: `[File: ${part.filename || 'file'}]`,\n };\n}\n\n/**\n * Transform message content (string or parts array) to Responses API format.\n */\nexport function transformMessageContent(\n content: string | ContentPart[]\n): string | Array<ResponseInputText | ResponseInputImage> {\n if (typeof content === 'string') {\n return content;\n }\n return content.map(transformContentPart);\n}\n\n// ============================================================================\n// Message Transformers\n// ============================================================================\n\n/**\n * Transform a user message to Responses API input item.\n */\nexport function transformUserMessage(\n msg: ProviderMessage & { role: 'user' }\n): OpenResponsesEasyInputMessage {\n const content = transformMessageContent(msg.content);\n return {\n role: 'user',\n content: typeof content === 'string' ? content : content,\n };\n}\n\n/**\n * Transform an assistant message to Responses API input items.\n * Assistant messages from previous turns need special handling.\n *\n * Handles:\n * - Text content → output message\n * - Tool calls → function_call items\n * - Reasoning details → reasoning item (for multi-turn conversations with reasoning models)\n */\nexport function transformAssistantMessage(\n msg: ProviderMessage & { role: 'assistant' }\n): InputItem[] {\n const items: InputItem[] = [];\n\n // Add reasoning item if present (from previous reasoning model responses)\n // This preserves reasoning context across multi-turn conversations\n // Note: API expects summary as array of { type: 'summary_text', text: '...' } objects\n const reasoningDetails = (msg as any).reasoningDetails as Array<{ type: string; text: string }> | undefined;\n if (reasoningDetails && reasoningDetails.length > 0) {\n // Extract summary texts (as objects) and encrypted content\n const summaryTexts: Array<{ type: 'summary_text'; text: string }> = [];\n let encryptedContent: string | undefined;\n\n for (const detail of reasoningDetails) {\n if (detail.type === 'reasoning.text') {\n summaryTexts.push({ type: 'summary_text', text: detail.text });\n } else if (detail.type === 'reasoning.encrypted') {\n encryptedContent = detail.text;\n }\n }\n\n // Only add reasoning item if we have summary texts\n if (summaryTexts.length > 0) {\n // API expects summary as objects, encrypted_content in snake_case\n const reasoningItem = {\n type: 'reasoning' as const,\n id: '',\n summary: summaryTexts,\n ...(encryptedContent ? { encrypted_content: encryptedContent } : {}),\n };\n items.push(reasoningItem as unknown as InputItem);\n }\n }\n\n // Add assistant message if there's content OR if there are tool calls\n const hasToolCalls = msg.toolCalls && msg.toolCalls.length > 0;\n if (msg.content || hasToolCalls) {\n // Use type assertion since we're constructing a valid ResponsesOutputMessage\n const outputMessage = {\n type: 'message' as const,\n role: 'assistant' as const,\n id: '',\n status: 'completed' as const,\n content: msg.content\n ? [{ type: 'output_text' as const, text: msg.content, annotations: [] }]\n : [],\n } satisfies ResponsesOutputMessage;\n items.push(outputMessage);\n }\n\n // Add tool calls if present\n if (msg.toolCalls && msg.toolCalls.length > 0) {\n for (const tc of msg.toolCalls) {\n // Use snake_case call_id for raw API (SDK types use camelCase internally)\n // We bypass SDK validation so must match raw API format\n const functionCall = {\n type: 'function_call' as const,\n call_id: tc.id,\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n } as unknown as ResponsesOutputItemFunctionCall;\n items.push(functionCall);\n }\n }\n\n return items;\n}\n\n/**\n * Transform a tool result message to Responses API input items.\n *\n * OpenRouter's API does not support image_url parts in function_call_output (tool results).\n * When a tool returns image attachments, we:\n * 1. Keep only text in the tool output\n * 2. Create a synthetic user message with the images to inject after the tool output\n *\n * This transformation is interstitial - it only affects data sent to the LLM,\n * NOT the stored message history.\n */\nexport function transformToolMessage(\n msg: ProviderMessage & { role: 'tool' }\n): ToolMessageTransformResult {\n let output: string;\n\n if (typeof msg.content === 'string') {\n output = msg.content;\n } else if ('type' in msg.content) {\n if (msg.content.type === 'text') {\n output = msg.content.text;\n } else if (msg.content.type === 'error') {\n output = `Error: ${msg.content.error}`;\n } else {\n output = JSON.stringify(msg.content);\n }\n } else {\n output = JSON.stringify(msg.content);\n }\n\n // Check for image attachments\n const imageAttachments = msg.attachments?.filter(a => a.type === 'image' && a.data) || [];\n\n // Base tool output (text only, or \"Success\" if no text but has images)\n // Use snake_case call_id for raw API (SDK types use camelCase internally)\n const toolOutput = {\n type: 'function_call_output',\n call_id: msg.toolCallId,\n output: output || (imageAttachments.length > 0 ? 'Success' : ''),\n } as unknown as OpenResponsesFunctionCallOutput;\n\n // If no images, return just the tool output\n if (imageAttachments.length === 0) {\n return { toolOutput };\n }\n\n // Create synthetic user message with images\n // OpenRouter doesn't support images in function_call_output, so we inject\n // a user message with the images after the tool output\n // IMPORTANT: Use snake_case (image_url) for raw API - SDK would convert camelCase\n const imageContent: Array<{ type: string; text?: string; image_url?: string; detail?: string }> = [];\n\n // Descriptive text\n const toolName = msg.toolName || 'the tool';\n const isSingle = imageAttachments.length === 1;\n const descriptor = isSingle ? 'the file' : `${imageAttachments.length} files`;\n const verb = isSingle ? 'is' : 'are';\n imageContent.push({\n type: 'input_text',\n text: `Here ${verb} ${descriptor} from ${toolName}:`,\n });\n\n // Add each image\n for (const attachment of imageAttachments) {\n const attachmentData = attachment.data || '';\n const imageData = attachmentData.startsWith('data:')\n ? attachmentData\n : `data:${attachment.mediaType || 'image/png'};base64,${attachmentData}`;\n\n imageContent.push({\n type: 'input_image',\n image_url: imageData,\n detail: 'auto',\n });\n }\n\n // Construct as a simple user message (same format as transformUserMessage)\n // The API accepts array content for multimodal messages even though SDK types\n // may not reflect this\n const syntheticUserMessage = {\n role: 'user' as const,\n content: imageContent,\n } as OpenResponsesEasyInputMessage;\n\n return { toolOutput, syntheticUserMessage };\n}\n\n/**\n * Transform an array of ProviderMessages to Responses API input format.\n * Returns both the input items and extracted system instructions.\n *\n * For tool messages with image attachments, this injects synthetic user messages\n * after the tool output to work around OpenRouter's lack of image support in\n * function_call_output.\n */\nexport function transformMessages(messages: ProviderMessage[]): {\n input: InputItem[];\n instructions: string | undefined;\n} {\n let instructions: string | undefined;\n const input: InputItem[] = [];\n\n for (const msg of messages) {\n switch (msg.role) {\n case 'system':\n // System messages become instructions\n instructions = instructions\n ? `${instructions}\\n\\n${msg.content}`\n : msg.content;\n break;\n\n case 'user':\n input.push(transformUserMessage(msg));\n break;\n\n case 'assistant':\n input.push(...transformAssistantMessage(msg));\n break;\n\n case 'tool': {\n // Handle synthetic user messages for image attachments\n const result = transformToolMessage(msg);\n input.push(result.toolOutput);\n if (result.syntheticUserMessage) {\n input.push(result.syntheticUserMessage);\n }\n break;\n }\n }\n }\n\n return { input, instructions };\n}\n\n// ============================================================================\n// Tool Transformers\n// ============================================================================\n\n/**\n * Transform a single ProviderTool to Responses API function tool.\n */\nexport function transformTool(tool: ProviderTool): OpenResponsesRequestToolFunction {\n const inputParams = tool.function.parameters as Record<string, unknown> | null | undefined;\n\n let parameters: Record<string, unknown> | null;\n if (inputParams && typeof inputParams === 'object') {\n parameters = {\n ...inputParams,\n additionalProperties: false,\n };\n } else {\n // Tools with no parameters need a valid empty schema\n parameters = {\n type: 'object',\n properties: {},\n required: [],\n additionalProperties: false,\n };\n }\n\n return {\n type: 'function',\n name: tool.function.name,\n description: tool.function.description || undefined,\n parameters,\n strict: true,\n };\n}\n\n/**\n * Transform an array of ProviderTools to Responses API format.\n */\nexport function transformTools(tools: ProviderTool[]): OpenResponsesRequestToolFunction[] {\n return tools.map(transformTool);\n}\n\n/**\n * Transform tool choice option to Responses API format.\n */\nexport function transformToolChoice(\n choice: ProviderRequest['toolChoice']\n): OpenResponsesRequest['toolChoice'] {\n if (choice === 'auto') {\n return 'auto';\n }\n if (choice === 'none') {\n return 'none';\n }\n if (choice === 'required') {\n return 'required';\n }\n if (typeof choice === 'object' && 'name' in choice) {\n return { type: 'function', name: choice.name };\n }\n return 'auto';\n}\n\n// ============================================================================\n// Response Transformers\n// ============================================================================\n\n/**\n * Map Responses API status/incomplete_details to Provider finish reason.\n */\nexport function mapFinishReason(response: OpenResponsesNonStreamingResponse): ProviderFinishReason {\n if (response.status === 'failed') {\n return 'error';\n }\n if (response.status === 'incomplete') {\n if (response.incompleteDetails?.reason === 'max_output_tokens') {\n return 'length';\n }\n if (response.incompleteDetails?.reason === 'content_filter') {\n return 'content_filter';\n }\n }\n\n // Check if there are function calls in the output\n const hasToolCalls = response.output.some(\n (item) => item.type === 'function_call'\n );\n if (hasToolCalls) {\n return 'tool_calls';\n }\n\n return 'stop';\n}\n\n/**\n * Extract text content from response output items.\n */\nexport function extractTextContent(output: ResponsesOutputItem[]): string | null {\n const textParts: string[] = [];\n\n for (const item of output) {\n if (item.type === 'message' && item.role === 'assistant') {\n for (const content of item.content) {\n if (content.type === 'output_text') {\n textParts.push(content.text);\n }\n }\n }\n }\n\n return textParts.length > 0 ? textParts.join('') : null;\n}\n\n/**\n * Extract tool calls from response output items.\n */\nexport function extractToolCalls(\n output: ResponsesOutputItem[]\n): ProviderToolCallPart[] | undefined {\n const toolCalls: ProviderToolCallPart[] = [];\n\n for (const item of output) {\n if (item.type === 'function_call') {\n let parsedArgs: Record<string, unknown> = {};\n try {\n parsedArgs = item.arguments ? JSON.parse(item.arguments) : {};\n } catch {\n // Keep empty object on parse failure\n }\n\n toolCalls.push({\n id: item.callId,\n name: item.name,\n arguments: parsedArgs,\n });\n }\n }\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n}\n\n/**\n * Extract generated images from response output items.\n * Handles image_generation_call output items from OpenRouter.\n */\nexport function extractImages(\n output: ResponsesOutputItem[]\n): ProviderGeneratedImage[] | undefined {\n const images: ProviderGeneratedImage[] = [];\n\n for (const item of output) {\n const itemAny = item as any;\n\n // Handle image_generation_call output items\n if (itemAny.type === 'image_generation_call' && itemAny.result) {\n const imageData = itemAny.result as string;\n\n // Determine if it's a URL or base64 data\n const isDataUrl = imageData.startsWith('data:');\n const isHttpUrl = imageData.startsWith('http://') || imageData.startsWith('https://');\n\n // Try to determine media type from data URL or default to png\n let mediaType = 'image/png';\n if (isDataUrl) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) {\n mediaType = match[1];\n }\n }\n\n images.push({\n id: itemAny.id || undefined,\n data: imageData,\n mediaType,\n // Include revised prompt if available\n revisedPrompt: itemAny.revised_prompt || itemAny.revisedPrompt || undefined,\n });\n }\n\n // Also check for images in message content (some models return images this way)\n if (itemAny.type === 'message' && itemAny.content && Array.isArray(itemAny.content)) {\n for (const content of itemAny.content) {\n // Check for output_image type content\n if (content.type === 'output_image' || content.type === 'image') {\n const imageData = content.image || content.data || content.url || content.image_url;\n if (imageData) {\n let mediaType = content.media_type || content.mediaType || 'image/png';\n if (typeof imageData === 'string' && imageData.startsWith('data:')) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) {\n mediaType = match[1];\n }\n }\n images.push({\n id: content.id || undefined,\n data: imageData,\n mediaType,\n });\n }\n }\n }\n }\n }\n\n return images.length > 0 ? images : undefined;\n}\n\n/**\n * Extended usage type that includes OpenRouter-specific fields.\n * These fields are returned when `usage: { include: true }` is in the request.\n *\n * Note: Raw API returns snake_case (input_tokens), SDK may normalize to camelCase (inputTokens).\n * We handle both formats for robustness.\n */\ninterface OpenRouterExtendedUsage {\n // camelCase (SDK normalized)\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n inputTokensDetails?: { cachedTokens?: number };\n outputTokensDetails?: { reasoningTokens?: number };\n // snake_case (raw API)\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n input_tokens_details?: { cached_tokens?: number };\n output_tokens_details?: { reasoning_tokens?: number };\n // OpenRouter-specific fields (when usage.include is true)\n cost?: number;\n native_tokens_prompt?: number;\n native_tokens_completion?: number;\n nativeTokensPrompt?: number;\n nativeTokensCompletion?: number;\n}\n\n/**\n * Transform Responses API usage to Provider usage format.\n * Includes cost when available (requires usage: { include: true } in request).\n * Handles both snake_case (raw API) and camelCase (SDK normalized) field names.\n */\nexport function transformUsage(\n usage: OpenRouterExtendedUsage | undefined | null,\n actualProvider?: string\n): ProviderUsage {\n if (!usage) {\n return {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n };\n }\n\n // Handle both snake_case and camelCase field names\n const inputTokens = usage.native_tokens_prompt || usage.nativeTokensPrompt ||\n usage.input_tokens || usage.inputTokens || 0;\n const outputTokens = usage.native_tokens_completion || usage.nativeTokensCompletion ||\n usage.output_tokens || usage.outputTokens || 0;\n const totalTokens = usage.total_tokens || usage.totalTokens || 0;\n\n const reasoningTokens = usage.output_tokens_details?.reasoning_tokens ||\n usage.outputTokensDetails?.reasoningTokens;\n const cachedTokens = usage.input_tokens_details?.cached_tokens ||\n usage.inputTokensDetails?.cachedTokens;\n\n return {\n promptTokens: inputTokens,\n completionTokens: outputTokens,\n totalTokens: totalTokens || (inputTokens + outputTokens),\n reasoningTokens,\n cachedTokens,\n cost: usage.cost,\n provider: actualProvider,\n };\n}\n\n/**\n * Transform a complete Responses API response to ProviderResponse.\n */\nexport function transformResponse(response: OpenResponsesNonStreamingResponse): ProviderResponse {\n const content = extractTextContent(response.output);\n const toolCalls = extractToolCalls(response.output);\n const images = extractImages(response.output);\n\n // Extract actual provider from model ID (e.g., \"google/gemini-2.5-flash\" -> \"google\")\n const actualProvider = response.model?.split('/')[0] || undefined;\n\n return {\n content,\n toolCalls,\n images,\n finishReason: mapFinishReason(response),\n usage: transformUsage(response.usage as OpenRouterExtendedUsage, actualProvider),\n metadata: {\n model: response.model,\n provider: 'openrouter',\n actualProvider,\n requestId: response.id,\n },\n };\n}\n\n// ============================================================================\n// Reasoning Details Extraction\n// ============================================================================\n\n/**\n * Extract reasoning details from response output items or streamed content.\n *\n * OpenRouter reasoning format:\n * - Output item with type: \"reasoning\"\n * - Contains: encrypted_content (string) and summary (string[])\n * - Streaming: response.reasoning.delta events\n */\nfunction extractReasoningDetails(\n output: ResponsesOutputItem[] | undefined,\n streamedReasoningContent: string\n): Array<{ type: string; text: string }> | undefined {\n const details: Array<{ type: string; text: string }> = [];\n\n // Extract from response output items (for non-streaming or completed response)\n if (output && Array.isArray(output)) {\n for (const item of output) {\n const itemAny = item as any;\n\n // OpenRouter format: type \"reasoning\" with summary array and/or encrypted_content\n if (itemAny.type === 'reasoning') {\n // Summary is an array of reasoning steps (preferred for display)\n if (itemAny.summary && Array.isArray(itemAny.summary)) {\n for (const step of itemAny.summary) {\n if (typeof step === 'string') {\n details.push({ type: 'reasoning.text', text: step });\n }\n }\n }\n // Fallback to encrypted_content if no summary\n // (encrypted_content may not be human-readable)\n if (details.length === 0 && itemAny.encrypted_content) {\n details.push({ type: 'reasoning.encrypted', text: itemAny.encrypted_content });\n }\n // Also check for plain content field\n if (itemAny.content) {\n if (typeof itemAny.content === 'string') {\n details.push({ type: 'reasoning.text', text: itemAny.content });\n } else if (Array.isArray(itemAny.content)) {\n for (const part of itemAny.content) {\n if (part.text) {\n details.push({ type: 'reasoning.text', text: part.text });\n }\n }\n }\n }\n }\n\n // Also check for message items with reasoning content\n if (itemAny.type === 'message' && itemAny.content && Array.isArray(itemAny.content)) {\n for (const part of itemAny.content) {\n if (part.type === 'reasoning' || part.type === 'reasoning_text') {\n details.push({ type: 'reasoning.text', text: part.text || '' });\n }\n }\n }\n }\n }\n\n // If no reasoning found in output items but we have streamed reasoning, use that\n if (details.length === 0 && streamedReasoningContent) {\n details.push({ type: 'reasoning.text', text: streamedReasoningContent });\n }\n\n return details.length > 0 ? details : undefined;\n}\n\n// ============================================================================\n// Request Builder\n// ============================================================================\n\n/**\n * Build a Responses API create request from a ProviderRequest.\n */\nexport function buildCreateParams(request: ProviderRequest): OpenResponsesRequest {\n const { input, instructions } = transformMessages(request.messages);\n\n const params: OpenResponsesRequest = {\n model: request.model,\n input,\n store: false, // Always stateless\n };\n\n // Note: We don't include `usage: { include: true }` in the request\n // as it slows down responses. Instead, we fetch generation metadata\n // after the stream completes using fetchGenerationMetadata().\n\n if (instructions) {\n params.instructions = instructions;\n }\n\n // Tools\n if (request.tools && request.tools.length > 0) {\n params.tools = transformTools(request.tools);\n\n if (request.toolChoice !== undefined) {\n params.toolChoice = transformToolChoice(request.toolChoice);\n }\n\n if (request.parallelToolCalls !== undefined) {\n params.parallelToolCalls = request.parallelToolCalls;\n }\n }\n\n // Generation parameters\n if (request.maxOutputTokens !== undefined) {\n params.maxOutputTokens = request.maxOutputTokens;\n }\n\n if (request.temperature !== undefined) {\n params.temperature = request.temperature;\n }\n\n if (request.topP !== undefined) {\n params.topP = request.topP;\n }\n\n // Reasoning (for models that support it)\n // OpenRouter supports: minimal, low, medium, high\n if (request.reasoning?.level !== undefined) {\n const effortMap: Record<number, 'minimal' | 'low' | 'medium' | 'high'> = {\n 10: 'minimal', // Basic reasoning\n 33: 'low', // Light reasoning\n 66: 'medium', // Balanced reasoning\n 100: 'high', // Deep reasoning\n };\n const effort = effortMap[request.reasoning.level];\n if (effort) {\n params.reasoning = {\n effort,\n };\n }\n }\n\n // Response format\n if (request.responseFormat) {\n if (request.responseFormat.type === 'json') {\n if (request.responseFormat.schema) {\n params.text = {\n format: {\n type: 'json_schema',\n name: 'response',\n schema: request.responseFormat.schema,\n strict: true,\n },\n };\n } else {\n params.text = {\n format: { type: 'json_object' },\n };\n }\n }\n }\n\n // Merge provider-specific options\n if (request.providerOptions) {\n Object.assign(params, request.providerOptions);\n }\n\n return params;\n}\n\n// ============================================================================\n// Stream State and Chunk Transformers\n// ============================================================================\n\nexport interface StreamState {\n toolCalls: Map<string, { id: string; name: string; arguments: string }>;\n reasoningContent: string;\n hasContent: boolean;\n hasReasoning: boolean;\n currentItemId: string | null;\n // Image generation tracking\n imageGenerations: Map<string, { id: string; data: string; status: string }>;\n}\n\nexport function createStreamState(): StreamState {\n return {\n toolCalls: new Map(),\n reasoningContent: '',\n hasContent: false,\n hasReasoning: false,\n currentItemId: null,\n imageGenerations: new Map(),\n };\n}\n\n/**\n * Process a stream event and return provider stream chunks.\n */\nexport function processStreamEvent(\n event: OpenResponsesStreamEvent,\n state: StreamState\n): ProviderStreamChunk[] {\n const chunks: ProviderStreamChunk[] = [];\n\n switch (event.type) {\n // Text content streaming\n case 'response.output_text.delta':\n state.hasContent = true;\n chunks.push({ type: 'content-delta', delta: event.delta });\n break;\n\n case 'response.output_text.done':\n // Content done is emitted at the end\n break;\n\n // Reasoning streaming - OpenRouter uses 'response.reasoning.delta'\n // Note: SDK types may not include reasoning events, so we check dynamically\n case 'response.reasoning_text.delta': // Fallback variant\n state.hasReasoning = true;\n state.reasoningContent += event.delta;\n chunks.push({ type: 'reasoning-delta', delta: event.delta });\n break;\n\n case 'response.reasoning_text.done':\n // Reasoning done is emitted at the end\n break;\n\n // Function call and image generation streaming\n case 'response.output_item.added':\n if (event.item.type === 'function_call') {\n // Handle both camelCase (callId) and snake_case (call_id) from API\n const callId = event.item.callId || (event.item as any).call_id || '';\n const name = event.item.name || '';\n state.toolCalls.set(callId, {\n id: callId,\n name,\n arguments: '',\n });\n chunks.push({\n type: 'tool-call-start',\n id: callId,\n name,\n });\n }\n // Track image generation items\n if ((event.item as any).type === 'image_generation_call') {\n const itemAny = event.item as any;\n const imageId = itemAny.id || itemAny.call_id || itemAny.callId || `img-${state.imageGenerations.size}`;\n state.imageGenerations.set(imageId, {\n id: imageId,\n data: '',\n status: 'in_progress',\n });\n }\n break;\n\n case 'response.output_item.done': {\n // Handle completed image generation items\n const itemAny = event.item as any;\n if (itemAny.type === 'image_generation_call' && itemAny.result) {\n const imageId = itemAny.id || itemAny.call_id || itemAny.callId || '';\n const imageData = itemAny.result as string;\n\n // Determine media type\n let mediaType = 'image/png';\n if (imageData.startsWith('data:')) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) mediaType = match[1];\n }\n\n // Update state\n const existing = state.imageGenerations.get(imageId);\n if (existing) {\n existing.data = imageData;\n existing.status = 'completed';\n }\n\n // Emit image-done chunk\n const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);\n chunks.push({\n type: 'image-done',\n index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,\n image: {\n id: imageId || undefined,\n data: imageData,\n mediaType,\n revisedPrompt: itemAny.revised_prompt || itemAny.revisedPrompt || undefined,\n },\n });\n }\n break;\n }\n\n case 'response.function_call_arguments.delta': {\n // Handle both camelCase (itemId) and snake_case (item_id) from API\n const itemId = event.itemId || (event as any).item_id || '';\n const deltaToolCall = Array.from(state.toolCalls.values()).find(\n (tc) => tc.id === itemId\n );\n if (deltaToolCall) {\n deltaToolCall.arguments += event.delta;\n chunks.push({\n type: 'tool-call-delta',\n id: deltaToolCall.id,\n argumentsDelta: event.delta,\n });\n }\n break;\n }\n\n case 'response.function_call_arguments.done': {\n // Handle both camelCase (itemId) and snake_case (item_id) from API\n const itemId = event.itemId || (event as any).item_id || '';\n const doneToolCall = Array.from(state.toolCalls.values()).find(\n (tc) => tc.id === itemId\n );\n if (doneToolCall) {\n let parsedArgs: Record<string, unknown> = {};\n try {\n parsedArgs = doneToolCall.arguments\n ? JSON.parse(doneToolCall.arguments)\n : {};\n } catch {\n // Keep empty object on parse failure\n }\n chunks.push({\n type: 'tool-call-done',\n id: doneToolCall.id,\n arguments: parsedArgs,\n });\n }\n break;\n }\n\n // Response completion\n case 'response.completed': {\n if (state.hasContent) {\n chunks.push({ type: 'content-done' });\n }\n if (state.hasReasoning) {\n chunks.push({ type: 'reasoning-done' });\n }\n\n // Extract actual provider from model ID (e.g., \"google/gemini-2.5-flash\" -> \"google\")\n const completedProvider = event.response.model?.split('/')[0] || undefined;\n\n // Extract reasoning details from response output items\n // Reasoning can appear as output items with type 'reasoning'\n const reasoningDetails = extractReasoningDetails(event.response.output, state.reasoningContent);\n\n // Extract generated images from response output and emit image-done chunks\n // Skip images that were already emitted via output_item.done events\n const generatedImages = extractImages(event.response.output);\n if (generatedImages && generatedImages.length > 0) {\n for (let i = 0; i < generatedImages.length; i++) {\n const img = generatedImages[i];\n // Check if this image was already emitted via output_item.done\n const alreadyEmitted = img.id && state.imageGenerations.get(img.id)?.status === 'completed';\n if (!alreadyEmitted) {\n chunks.push({\n type: 'image-done',\n index: i,\n image: img,\n });\n }\n }\n }\n\n // Include responseId for post-stream generation metadata fetch\n chunks.push({\n type: 'finish',\n finishReason: mapFinishReason(event.response),\n usage: transformUsage(event.response.usage as OpenRouterExtendedUsage, completedProvider),\n responseId: event.response.id, // Used to fetch generation metadata after stream\n reasoningDetails,\n } as ProviderStreamChunk);\n break;\n }\n\n case 'response.failed':\n chunks.push({\n type: 'error',\n error: event.response.error?.message || 'Response generation failed',\n code: event.response.error?.code,\n });\n break;\n\n case 'response.incomplete': {\n if (state.hasContent) {\n chunks.push({ type: 'content-done' });\n }\n if (state.hasReasoning) {\n chunks.push({ type: 'reasoning-done' });\n }\n\n // Extract actual provider from model ID\n const incompleteProvider = event.response.model?.split('/')[0] || undefined;\n\n // Extract reasoning details from response output items\n const incompleteReasoningDetails = extractReasoningDetails(event.response.output, state.reasoningContent);\n\n // Extract any generated images (may be partial)\n // Skip images that were already emitted via output_item.done events\n const incompleteImages = extractImages(event.response.output);\n if (incompleteImages && incompleteImages.length > 0) {\n for (let i = 0; i < incompleteImages.length; i++) {\n const img = incompleteImages[i];\n // Check if this image was already emitted via output_item.done\n const alreadyEmitted = img.id && state.imageGenerations.get(img.id)?.status === 'completed';\n if (!alreadyEmitted) {\n chunks.push({\n type: 'image-done',\n index: i,\n image: img,\n });\n }\n }\n }\n\n // Include responseId for post-stream generation metadata fetch\n chunks.push({\n type: 'finish',\n finishReason: mapFinishReason(event.response),\n usage: transformUsage(event.response.usage as OpenRouterExtendedUsage, incompleteProvider),\n responseId: event.response.id, // Used to fetch generation metadata after stream\n reasoningDetails: incompleteReasoningDetails,\n } as ProviderStreamChunk);\n break;\n }\n\n // Handle reasoning and image events dynamically (SDK types may not include them)\n default: {\n const eventType = (event as any).type as string;\n const eventAny = event as any;\n\n // OpenRouter reasoning streaming: response.reasoning.delta and response.reasoning.done\n if (eventType === 'response.reasoning.delta') {\n state.hasReasoning = true;\n state.reasoningContent += eventAny.delta || '';\n chunks.push({ type: 'reasoning-delta', delta: eventAny.delta || '' });\n }\n\n // Image generation partial streaming\n // Some providers may stream partial image data\n if (eventType === 'response.image_generation_call.partial_image' ||\n eventType === 'response.image_generation.partial') {\n const imageId = eventAny.item_id || eventAny.itemId || eventAny.id || '';\n const partialData = eventAny.partial_image || eventAny.delta || eventAny.data || '';\n\n // Accumulate partial data in state\n const existing = state.imageGenerations.get(imageId);\n if (existing) {\n existing.data += partialData;\n } else {\n state.imageGenerations.set(imageId, {\n id: imageId,\n data: partialData,\n status: 'generating',\n });\n }\n\n // Emit image-delta chunk\n const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);\n chunks.push({\n type: 'image-delta',\n index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,\n data: partialData,\n });\n }\n\n // Image generation completed (when not through output_item.done)\n if (eventType === 'response.image_generation_call.completed' ||\n eventType === 'response.image_generation.done') {\n const imageId = eventAny.item_id || eventAny.itemId || eventAny.id || '';\n const imageData = eventAny.result || eventAny.image || eventAny.data || '';\n\n if (imageData) {\n // Determine media type\n let mediaType = 'image/png';\n if (imageData.startsWith('data:')) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) mediaType = match[1];\n }\n\n // Update state\n const existing = state.imageGenerations.get(imageId);\n if (existing) {\n existing.data = imageData;\n existing.status = 'completed';\n }\n\n // Emit image-done chunk\n const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);\n chunks.push({\n type: 'image-done',\n index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,\n image: {\n id: imageId || undefined,\n data: imageData,\n mediaType,\n revisedPrompt: eventAny.revised_prompt || eventAny.revisedPrompt || undefined,\n },\n });\n }\n }\n\n break;\n }\n }\n\n return chunks;\n}\n\n/**\n * Create error chunk.\n */\nexport function createErrorChunk(error: string, code?: string): ProviderStreamChunk {\n return { type: 'error', error, code };\n}\n\n// ============================================================================\n// Inspection Utilities\n// ============================================================================\n\n/**\n * Check if a string looks like base64 data that should be truncated.\n */\nfunction isBase64Like(str: string): boolean {\n if (str.startsWith('data:')) return true;\n if (str.length > 200) {\n const base64Pattern = /^[A-Za-z0-9+/]+=*$/;\n return base64Pattern.test(str.substring(0, 200));\n }\n return false;\n}\n\n/**\n * Truncate a base64 string for display, preserving the start and showing length.\n */\nfunction truncateBase64String(str: string, maxLength: number = 50): string {\n if (str.length <= maxLength) return str;\n const preview = str.substring(0, maxLength);\n return `${preview}...[truncated, ${str.length.toLocaleString()} chars]`;\n}\n\n/**\n * Recursively truncate base64 data in an object for inspection.\n */\nexport function truncateBase64<T>(obj: T, maxLength: number = 50): T {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (typeof obj === 'string') {\n if (isBase64Like(obj)) {\n return truncateBase64String(obj, maxLength) as T;\n }\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => truncateBase64(item, maxLength)) as T;\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = truncateBase64(value, maxLength);\n }\n return result as T;\n }\n\n return obj;\n}\n\n// ============================================================================\n// Legacy exports for backward compatibility during migration\n// ============================================================================\n\n/**\n * @deprecated Use transformMessages instead\n */\nexport function transformMessagesToChat(messages: ProviderMessage[]) {\n // This is a compatibility shim - the new code uses transformMessages\n const result: Array<{ role: string; content?: string | unknown[] | null; tool_calls?: unknown[]; tool_call_id?: string }> = [];\n\n for (const msg of messages) {\n switch (msg.role) {\n case 'system':\n result.push({ role: 'system', content: msg.content });\n break;\n case 'user':\n result.push({ role: 'user', content: msg.content });\n break;\n case 'assistant':\n result.push({\n role: 'assistant',\n content: msg.content || null,\n tool_calls: msg.toolCalls?.map(tc => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },\n })),\n });\n break;\n case 'tool':\n result.push({\n role: 'tool',\n tool_call_id: msg.toolCallId,\n content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),\n });\n break;\n }\n }\n return result;\n}\n\n/**\n * @deprecated Use transformTools instead\n */\nexport function transformToolsToChat(tools: ProviderTool[]) {\n return tools.map(tool => ({\n type: 'function',\n function: {\n name: tool.function.name,\n description: tool.function.description,\n parameters: tool.function.parameters,\n },\n }));\n}\n\n/**\n * @deprecated Use transformToolChoice instead\n */\nexport function transformToolChoiceToChat(choice: ProviderRequest['toolChoice']) {\n return transformToolChoice(choice);\n}\n\n/**\n * Create content-done chunk.\n */\nexport function createContentDoneChunk(): ProviderStreamChunk {\n return { type: 'content-done' };\n}\n\n/**\n * Create reasoning-done chunk.\n */\nexport function createReasoningDoneChunk(): ProviderStreamChunk {\n return { type: 'reasoning-done' };\n}\n\n/**\n * Create finish chunk with final state.\n */\nexport function createFinishChunk(\n finishReason: ProviderFinishReason,\n usage: ProviderUsage\n): ProviderStreamChunk {\n return {\n type: 'finish',\n finishReason,\n usage,\n };\n}\n\n// ============================================================================\n// Raw SSE Stream Processing (bypasses SDK validation)\n// ============================================================================\n\n/**\n * Parse a raw JSON stream event from OpenRouter.\n * Handles null values that SDK validation rejects.\n */\nexport function parseRawStreamEvent(jsonStr: string): OpenResponsesStreamEvent | null {\n try {\n const event = JSON.parse(jsonStr);\n // The event is already in the correct format, just return it\n // Our processStreamEvent handles the types correctly\n return event as OpenResponsesStreamEvent;\n } catch {\n return null;\n }\n}\n\n/**\n * Create an async iterable from a raw SSE response stream.\n * This bypasses SDK Zod validation which fails on null usage fields.\n */\nexport async function* parseSSEStream(\n response: Response,\n state: StreamState\n): AsyncIterable<ProviderStreamChunk> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('No response body');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events from buffer\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep incomplete line in buffer\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(':')) continue;\n\n // Parse data lines\n if (trimmed.startsWith('data: ')) {\n const data = trimmed.slice(6);\n\n // Handle [DONE] marker\n if (data === '[DONE]') continue;\n\n const event = parseRawStreamEvent(data);\n if (event) {\n const chunks = processStreamEvent(event, state);\n for (const chunk of chunks) {\n yield chunk;\n }\n }\n }\n }\n }\n\n // Process any remaining buffer\n if (buffer.trim()) {\n const trimmed = buffer.trim();\n if (trimmed.startsWith('data: ')) {\n const data = trimmed.slice(6);\n if (data !== '[DONE]') {\n const event = parseRawStreamEvent(data);\n if (event) {\n const chunks = processStreamEvent(event, state);\n for (const chunk of chunks) {\n yield chunk;\n }\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Process a tool call from the stream (legacy helper).\n */\nexport function processToolCall(\n toolCall: { id: string; name: string; arguments: unknown },\n _state: StreamState\n): ProviderStreamChunk[] {\n const chunks: ProviderStreamChunk[] = [];\n\n chunks.push({\n type: 'tool-call-start',\n id: toolCall.id,\n name: toolCall.name,\n });\n\n const args = typeof toolCall.arguments === 'string'\n ? JSON.parse(toolCall.arguments)\n : toolCall.arguments as Record<string, unknown>;\n\n chunks.push({\n type: 'tool-call-done',\n id: toolCall.id,\n arguments: args,\n });\n\n return chunks;\n}\n\n// ============================================================================\n// Generation Metadata Fetch\n// ============================================================================\n\n/**\n * Generation metadata returned by OpenRouter's /api/v1/generation endpoint.\n * Contains accurate usage, cost, and provider information.\n */\nexport interface GenerationMetadata {\n /** Unique generation ID */\n id: string;\n /** Total cost in USD */\n totalCost: number;\n /** Input/prompt tokens */\n promptTokens: number;\n /** Output/completion tokens */\n completionTokens: number;\n /** Native input tokens (provider-specific) */\n nativePromptTokens?: number;\n /** Native output tokens (provider-specific) */\n nativeCompletionTokens?: number;\n /** Actual provider that served the request */\n providerName: string;\n /** Request latency in milliseconds */\n latency?: number;\n /** Model used */\n model?: string;\n /** Finish reason */\n finishReason?: string;\n /** Created timestamp */\n createdAt?: string;\n}\n\n/**\n * Raw response from OpenRouter's generation endpoint.\n */\ninterface OpenRouterGenerationResponse {\n data?: {\n id?: string;\n total_cost?: number;\n tokens_prompt?: number;\n tokens_completion?: number;\n native_tokens_prompt?: number;\n native_tokens_completion?: number;\n provider_name?: string;\n latency?: number;\n model?: string;\n finish_reason?: string;\n created_at?: string;\n generation_time?: number;\n };\n error?: {\n message?: string;\n code?: number;\n };\n}\n\n/**\n * Fetch generation metadata from OpenRouter after a stream completes.\n *\n * This is the recommended way to get accurate token usage, cost, and provider\n * information. Should be called with the responseId from the finish chunk.\n *\n * @param apiKey - OpenRouter API key\n * @param generationId - The response/generation ID from the stream finish chunk\n * @param baseUrl - Optional base URL (defaults to https://openrouter.ai/api/v1)\n * @param signal - Optional abort signal for cancellation\n * @returns Generation metadata or null if not available\n */\nexport async function fetchGenerationMetadata(\n apiKey: string,\n generationId: string,\n baseUrl: string = 'https://openrouter.ai/api/v1',\n signal?: AbortSignal\n): Promise<GenerationMetadata | null> {\n const url = `${baseUrl}/generation?id=${encodeURIComponent(generationId)}`;\n\n // Retry with backoff - generation data may not be immediately available\n const maxRetries = 3;\n const delays = [500, 1000, 2000]; // ms\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n if (attempt > 0) {\n await new Promise(resolve => setTimeout(resolve, delays[attempt - 1]));\n }\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n },\n signal,\n });\n\n if (response.status === 404 && attempt < maxRetries - 1) {\n // Generation not yet indexed, will retry\n continue;\n }\n\n if (!response.ok) {\n console.error(`Failed to fetch generation metadata: ${response.status}`);\n return null;\n }\n\n const result = await response.json() as OpenRouterGenerationResponse;\n\n if (result.error) {\n console.error('Generation metadata error:', result.error.message);\n return null;\n }\n\n const data = result.data;\n if (!data) {\n return null;\n }\n\n return {\n id: data.id || generationId,\n totalCost: data.total_cost || 0,\n promptTokens: data.tokens_prompt || 0,\n completionTokens: data.tokens_completion || 0,\n nativePromptTokens: data.native_tokens_prompt,\n nativeCompletionTokens: data.native_tokens_completion,\n providerName: data.provider_name || 'unknown',\n latency: data.latency || data.generation_time,\n model: data.model,\n finishReason: data.finish_reason,\n createdAt: data.created_at,\n };\n } catch (error) {\n if (attempt < maxRetries - 1) {\n continue;\n }\n console.error('Error fetching generation metadata:', error);\n return null;\n }\n }\n\n return null; // All retries exhausted\n}\n","/**\n * Lab/provider icons as SVG strings.\n * Embedded directly to avoid external dependencies.\n * These icons are used by OpenRouter to show the AI lab for each model.\n */\n\n// Anthropic icon (Claude)\nexport const ANTHROPIC_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#F0EFEA\"/>\n<path d=\"M32.84 10H26.72L37.88 38H44L32.84 10Z\" fill=\"black\"/>\n<path d=\"M15.16 10L4 38H10.24L12.5224 32.12H24.1976L26.48 38H32.72L21.56 10H15.16ZM14.5408 26.92L18.36 17.08L22.1793 26.92H14.5408Z\" fill=\"black\"/>\n</svg>`;\n\n// Google icon\nexport const GOOGLE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"none\" width=\"48\" height=\"48\"><rect width=\"16\" height=\"16\" fill=\"white\"/><path fill=\"#4285F4\" d=\"M14.9 8.161c0-.476-.039-.954-.121-1.422h-6.64v2.695h3.802a3.24 3.24 0 01-1.407 2.127v1.75h2.269c1.332-1.22 2.097-3.02 2.097-5.15z\"/><path fill=\"#34A853\" d=\"M8.14 15c1.898 0 3.499-.62 4.665-1.69l-2.268-1.749c-.631.427-1.446.669-2.395.669-1.836 0-3.393-1.232-3.952-2.888H1.85v1.803A7.044 7.044 0 008.14 15z\"/><path fill=\"#FBBC04\" d=\"M4.187 9.342a4.17 4.17 0 010-2.68V4.859H1.849a6.97 6.97 0 000 6.286l2.338-1.803z\"/><path fill=\"#EA4335\" d=\"M8.14 3.77a3.837 3.837 0 012.7 1.05l2.01-1.999a6.786 6.786 0 00-4.71-1.82 7.042 7.042 0 00-6.29 3.858L4.186 6.66c.556-1.658 2.116-2.89 3.952-2.89z\"/></svg>`;\n\n// Meta icon (simplified)\nexport const META_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#0668E1\"/>\n<path d=\"M35.5 16c-2.8 0-5.2 2.2-7.5 6.5-2.3-4.3-4.7-6.5-7.5-6.5-4.4 0-8 5.6-8 12s3.6 12 8 12c2.8 0 5.2-2.2 7.5-6.5 2.3 4.3 4.7 6.5 7.5 6.5 4.4 0 8-5.6 8-12s-3.6-12-8-12zm-15 20c-2.8 0-5-4-5-8s2.2-8 5-8c1.4 0 2.8 1 4.2 3-1.8 3.2-2.8 5.8-2.8 8 0 2.2 1 4.8 2.8 8-1.4 2-2.8 3-4.2 3zm15 0c-1.4 0-2.8-1-4.2-3 1.8-3.2 2.8-5.8 2.8-8 0-2.2-1-4.8-2.8-8 1.4-2 2.8-3 4.2-3 2.8 0 5 4 5 8s-2.2 8-5 8z\" fill=\"white\"/>\n</svg>`;\n\n// NVIDIA icon\nexport const NVIDIA_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"white\"/>\n<path d=\"M17.8962 17.7465V14.9519C18.1789 14.9346 18.4615 14.9174 18.7442 14.9174C26.5881 14.6758 31.7291 21.5072 31.7291 21.5072C31.7291 21.5072 26.1818 29.0287 20.2282 29.0287C19.4332 29.0287 18.6559 28.9079 17.9139 28.6664V20.1789C20.9702 20.5412 21.5885 21.8523 23.4082 24.8367L27.4891 21.49C27.4891 21.49 24.5035 17.6775 19.4862 17.6775C18.9562 17.6603 18.4262 17.6948 17.8962 17.7465ZM17.8962 8.5V12.6747L18.7442 12.623C29.6445 12.2607 36.7641 21.352 36.7641 21.352C36.7641 21.352 28.6021 31.047 20.1045 31.047C19.3625 31.047 18.6382 30.978 17.9139 30.8573V33.4449C18.5145 33.5139 19.1329 33.5657 19.7335 33.5657C27.6481 33.5657 33.3721 29.6152 38.9194 24.9574C39.8381 25.682 43.601 27.4243 44.3784 28.1834C39.1137 32.4961 26.8355 35.9636 19.8749 35.9636C19.2035 35.9636 18.5675 35.9291 17.9315 35.86V39.5H48V8.5L17.8962 8.5ZM17.8962 28.6664V30.8745C10.5823 29.5979 8.55061 22.1628 8.55061 22.1628C8.55061 22.1628 12.0662 18.3676 17.8962 17.7465V20.1617H17.8785C14.8222 19.7994 12.4196 22.594 12.4196 22.594C12.4196 22.594 13.7799 27.3036 17.8962 28.6664ZM4.9113 21.8523C4.9113 21.8523 9.2396 15.6074 17.9139 14.9519V12.6747C8.30327 13.4338 0 21.3692 0 21.3692C0 21.3692 4.6993 34.6525 17.8962 35.86V33.4449C8.21494 32.2718 4.9113 21.8523 4.9113 21.8523Z\" fill=\"#76B900\"/>\n</svg>`;\n\n// Alibaba icon\nexport const ALIBABA_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF5A00\"/>\n<path d=\"M31.5 12c3 .2 6.2.7 8.6 2.7 1.9 1.6 2.7 4.2 2.3 6.6-.4 1.8-1.2 3.4-2.1 5-1.8 3.1-4 6-6.1 8.9-.4.5-.7 1.1-.6 1.7 0 .5.4.9.9 1.1.9.4 1.8.3 2.7.2 2.9-.3 5.6-1.2 8.4-2.1.2-.1.3.1.4.2-2.9 1.9-6 3.6-9.3 4.8-1.8.6-3.7 1.2-5.7 1-1-.1-2-.7-2.4-1.7-.4-1.3-.2-2.8.3-4 .9-1.9 2.2-3.6 3.5-5.3 2.1-2.8 4.3-5.5 6-8.6.6-1.1 1.1-2.4.6-3.6-.5-1.1-1.6-1.8-2.7-2.3-1.9-.8-3.9-1.4-5.9-1.9l.9.6c.7.5 1.4 1.1 2.1convergence 1.6-2.6.5-5.3 1-7.9 1.7-3.9 1-7.8 2.3-11.6 3.7.4.7.7 1.5 1.1 2.2-.9 1-1.7 1.9-2.6 2.9 2.1.6 4.3.7 6.4.2 1.8-.4 3.5-1.3 4.9-2.5-.4-.5-.9-.8-1.4-1.2 1.7 0 3.2 1.5 3.2 3.2l-1 .1c-.1-.4-.2-.7-.3-1.1-1.9 1.7-4.4 2.8-6.9 3.1-2.2.3-4.5-.1-6.7-.8.1 1.3.3 2.6.4 3.9-1.2.5-2.3 1.1-3.3 2-.8.7-1.6 1.5-1.9 2.5-.3.9.2 1.9 1 2.4 1 .6 2.1.9 3.2 1 1.5.2 3 .2 4.4.1 2.9-.2 5.8-.8 8.6-1.5.3-.1.4.4.1.4-2.3 1.2-4.6 2.1-7.1 2.8-2.3.6-4.7.9-7.1 1-2.4-.1-5-.5-7-1.9-1.4-1.2-2.2-3-2.2-4.8v-.1c0-1.9.7-3.8 1.6-5.5 1.2-2.4 2.8-4.6 4.6-6.5 3.1-3.4 7-6.1 11.2-8 4.4-2 9-3.4 13.8-3.7z\" fill=\"white\"/>\n</svg>`;\n\n// DeepSeek icon\nexport const DEEPSEEK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"white\"/>\n<path d=\"M47.4964 9.43757C46.988 9.19072 46.769 9.66115 46.4717 9.90013C46.37 9.97722 46.284 10.0774 46.1979 10.1699C45.4548 10.9562 44.5866 11.4729 43.4523 11.4112C41.794 11.3186 40.3781 11.8353 39.1265 13.092C38.8605 11.5423 37.9767 10.617 36.6312 10.0234C35.9273 9.71497 35.2154 9.40651 34.7226 8.7357C34.3784 8.25783 34.2845 7.7258 34.1124 7.20147C34.0029 6.8854 33.8934 6.56146 33.5257 6.50747C33.1268 6.44587 32.9703 6.77725 32.8138 7.05499C32.1881 8.18844 31.9456 9.43749 31.969 10.7019C32.0237 13.5471 33.2363 15.8137 35.6455 17.4252C35.9193 17.6103 35.9896 17.7952 35.9036 18.0652C35.7393 18.6204 35.5437 19.16 35.3717 19.7151C35.2622 20.0699 35.0978 20.147 34.7146 19.9926C33.3926 19.4453 32.2505 18.6357 31.2414 17.6566C29.5284 16.0143 27.9795 14.2024 26.0475 12.7837C25.5938 12.4521 25.1401 12.1438 24.6707 11.8507C22.6994 9.95412 24.9289 8.39653 25.4451 8.21163C25.9848 8.01885 25.6329 7.35566 23.8884 7.36353C22.144 7.37114 20.5483 7.94946 18.5146 8.72047C18.2173 8.83615 17.9043 8.92086 17.5836 8.99025C15.7376 8.64338 13.8211 8.56612 11.8186 8.78986C8.04819 9.20613 5.03671 10.9718 2.823 13.9865C0.16347 17.6103 -0.462297 21.7274 0.304303 26.0221C1.11002 30.5478 3.44099 34.295 7.02367 37.225C10.7393 40.2626 15.0181 41.7507 19.8992 41.4655C22.8639 41.2959 26.1649 40.9027 29.8884 37.7801C30.827 38.2427 31.8126 38.4276 33.4475 38.5664C34.7069 38.6821 35.9194 38.5048 36.858 38.3121C38.3286 38.0037 38.227 36.6544 37.6951 36.4077C33.385 34.4185 34.3314 35.228 33.471 34.5726C35.6613 32.0052 38.9624 29.3374 40.253 20.6942C40.3546 20.0081 40.2686 19.5763 40.253 19.0211C40.2451 18.682 40.3234 18.551 40.7145 18.5123C41.794 18.3889 42.8422 18.096 43.8043 17.5717C46.5969 16.0606 47.7233 13.5779 47.9892 10.6017C48.0284 10.1468 47.9814 9.67638 47.4964 9.43757ZM23.1611 36.2225C18.984 32.969 16.9581 31.8971 16.1211 31.9434C15.3388 31.9897 15.4797 32.8764 15.6517 33.4547C15.8316 34.0252 16.0663 34.4185 16.3948 34.9196C16.6216 35.2511 16.7782 35.7445 16.168 36.1147C14.8226 36.9397 12.4836 35.8371 12.3741 35.7832C9.65202 34.1948 7.37571 32.0975 5.77205 29.2295C4.22322 26.4691 3.32373 23.5086 3.17504 20.3474C3.13592 19.5841 3.36276 19.3141 4.12936 19.1755C5.13843 18.9904 6.17884 18.9517 7.1879 19.0982C11.4512 19.7151 15.0807 21.6041 18.1235 24.5956C19.86 26.2995 21.1742 28.3352 22.5275 30.3243C23.9668 32.4368 25.5156 34.4493 27.4868 36.0992C28.183 36.6775 28.7384 37.1169 29.2703 37.4408C27.6668 37.6181 24.9915 37.6568 23.1611 36.2225ZM25.1636 23.4623C25.1636 23.123 25.4374 22.8532 25.7817 22.8532C25.8599 22.8532 25.9303 22.8685 25.9929 22.8916C26.079 22.9225 26.1572 22.9688 26.2198 23.038C26.3293 23.1461 26.3919 23.3002 26.3919 23.4622C26.3919 23.8014 26.1181 24.0712 25.774 24.0712C25.4299 24.0712 25.1636 23.8015 25.1636 23.4623ZM31.3824 26.6235C30.9835 26.7854 30.5846 26.9241 30.2013 26.9395C29.6068 26.9703 28.9575 26.7313 28.6056 26.4383C28.058 25.9834 27.6669 25.7291 27.5026 24.9349C27.4322 24.5956 27.4713 24.0713 27.534 23.7707C27.6747 23.123 27.5183 22.7067 27.0568 22.3288C26.6813 22.0204 26.2041 21.9357 25.68 21.9357C25.4844 21.9357 25.3046 21.8508 25.1716 21.7814C24.9525 21.6735 24.7727 21.4036 24.9447 21.0721C24.9995 20.9643 25.2655 20.702 25.3281 20.6558C26.04 20.2548 26.8613 20.3859 27.6201 20.6866C28.3242 20.9719 28.8561 21.4962 29.6226 22.2363C30.4048 23.1306 30.5456 23.3776 30.9915 24.0482C31.3436 24.5725 31.6643 25.1122 31.8833 25.7291C32.0162 26.1146 31.844 26.4307 31.3824 26.6235Z\" fill=\"#4D6BFE\"/>\n</svg>`;\n\n// Qwen icon (same as Alibaba)\nexport const QWEN_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF5A00\"/>\n<path d=\"M31.5 12c3 .2 6.2.7 8.6 2.7 1.9 1.6 2.7 4.2 2.3 6.6-.4 1.8-1.2 3.4-2.1 5-1.8 3.1-4 6-6.1 8.9-.4.5-.7 1.1-.6 1.7 0 .5.4.9.9 1.1.9.4 1.8.3 2.7.2 2.9-.3 5.6-1.2 8.4-2.1.2-.1.3.1.4.2-2.9 1.9-6 3.6-9.3 4.8-1.8.6-3.7 1.2-5.7 1-1-.1-2-.7-2.4-1.7-.4-1.3-.2-2.8.3-4 .9-1.9 2.2-3.6 3.5-5.3 2.1-2.8 4.3-5.5 6-8.6.6-1.1 1.1-2.4.6-3.6-.5-1.1-1.6-1.8-2.7-2.3-1.9-.8-3.9-1.4-5.9-1.9l.9.6c.7.5 1.4 1.1 2.1 1.6-2.6.5-5.3 1-7.9 1.7-3.9 1-7.8 2.3-11.6 3.7.4.7.7 1.5 1.1 2.2-.9 1-1.7 1.9-2.6 2.9 2.1.6 4.3.7 6.4.2 1.8-.4 3.5-1.3 4.9-2.5-.4-.5-.9-.8-1.4-1.2 1.7 0 3.2 1.5 3.2 3.2l-1 .1c-.1-.4-.2-.7-.3-1.1-1.9 1.7-4.4 2.8-6.9 3.1-2.2.3-4.5-.1-6.7-.8.1 1.3.3 2.6.4 3.9-1.2.5-2.3 1.1-3.3 2-.8.7-1.6 1.5-1.9 2.5-.3.9.2 1.9 1 2.4 1 .6 2.1.9 3.2 1 1.5.2 3 .2 4.4.1 2.9-.2 5.8-.8 8.6-1.5.3-.1.4.4.1.4-2.3 1.2-4.6 2.1-7.1 2.8-2.3.6-4.7.9-7.1 1-2.4-.1-5-.5-7-1.9-1.4-1.2-2.2-3-2.2-4.8v-.1c0-1.9.7-3.8 1.6-5.5 1.2-2.4 2.8-4.6 4.6-6.5 3.1-3.4 7-6.1 11.2-8 4.4-2 9-3.4 13.8-3.7z\" fill=\"white\"/>\n</svg>`;\n\n// MiniMax icon\nexport const MINIMAX_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF6B6B\"/>\n<text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" fill=\"white\" font-family=\"Arial, sans-serif\" font-size=\"16\" font-weight=\"bold\">MM</text>\n</svg>`;\n\n// Moonshot AI icon\nexport const MOONSHOTAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" rx=\"24\" fill=\"#16191E\"/>\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M7.57811 31.3729L21.8866 35.2005C21.8673 36.2177 21.8973 37.2353 21.9766 38.2497L30.9105 40.6389C28.2608 41.7325 25.388 42.1773 22.5316 41.9363L22.2616 41.9123L22.1956 41.9063L22.0696 41.8928L21.9286 41.8778C21.85 41.8683 21.7715 41.8583 21.6931 41.8478L21.5326 41.8268L21.3676 41.8028C21.2072 41.7795 21.0472 41.754 20.8876 41.7263L20.8246 41.7143L20.7121 41.6948L20.5516 41.6648L20.4466 41.6423L20.3071 41.6138L20.1946 41.5898L20.0521 41.5598L19.9066 41.5253L19.7656 41.4923L19.6636 41.4668L19.5316 41.4338L19.3966 41.3978L19.2541 41.3603L19.1311 41.3258L18.9676 41.2808L18.8746 41.2508L18.7486 41.2133L18.6091 41.1713L18.4516 41.1204L18.3646 41.0919L18.2446 41.0529L18.1096 41.0064L18.0106 40.9704C17.9885 40.963 17.9665 40.9555 17.9446 40.9479L17.8426 40.9104L17.6911 40.8549L17.6056 40.8219L17.4856 40.7769L17.3551 40.7244L17.2231 40.6719L17.1046 40.6239L16.9621 40.5639L16.8676 40.5219L16.7731 40.4814C16.7525 40.4725 16.732 40.4635 16.7116 40.4544L16.6126 40.4094L16.4581 40.3389L16.3801 40.3029L16.2361 40.2339L16.1431 40.189L16.0171 40.129L15.8881 40.063L15.7486 39.9925L15.6706 39.952L15.5161 39.8695L15.4306 39.8245L15.3436 39.7765C15.3205 39.7636 15.2975 39.7506 15.2746 39.7375L15.1336 39.658L15.0436 39.607L14.9671 39.562L14.8591 39.5005L14.7361 39.4255L14.5966 39.3415L14.5186 39.2935L14.3926 39.2141L14.3011 39.1556L14.1826 39.0806L14.0776 39.0101L13.9981 38.9576C13.971 38.9397 13.944 38.9217 13.9171 38.9036L13.8511 38.8586L13.7851 38.8136C13.765 38.7997 13.745 38.7857 13.7251 38.7716L13.6396 38.7116L13.5256 38.6306L13.4221 38.5556L13.3111 38.4746L13.2271 38.4116L13.1131 38.3261L12.9991 38.2377L12.8701 38.1372L12.8026 38.0847L12.7066 38.0067L12.5956 37.9167L12.4621 37.8072L12.3931 37.7487L12.3241 37.6902C12.3025 37.6718 12.281 37.6533 12.2596 37.6347L12.1921 37.5747L12.1006 37.4952L11.9956 37.4022L11.8936 37.3122L11.8006 37.2253L11.7001 37.1323L11.6206 37.0573L11.4886 36.9313C11.4388 36.8831 11.3893 36.8346 11.3401 36.7858L11.2966 36.7438L11.2351 36.6808L11.1316 36.5758L11.0566 36.4993L10.9816 36.4198C10.8959 36.332 10.8119 36.2424 10.7296 36.1514L10.6096 36.0194L10.5166 35.9144L10.4101 35.7944L10.3471 35.7209L10.2676 35.6279L10.1806 35.5259L10.1116 35.4419C10.098 35.4255 10.0845 35.409 10.0711 35.3924L10.0036 35.31L9.9046 35.187L9.8431 35.109L9.7681 35.013L9.7381 34.9755C8.87983 33.8631 8.15489 32.654 7.57811 31.3729ZM6.04811 22.6828L23.0776 27.2378C22.7925 28.2307 22.5577 29.2374 22.3741 30.254L38.5995 34.5945C37.7934 35.6981 36.865 36.7069 35.832 37.6017L6.98561 29.8835L6.96161 29.8145L6.90911 29.6585C6.88359 29.5822 6.85859 29.5057 6.83411 29.4291L6.82361 29.3946C6.70815 29.0276 6.6046 28.6569 6.51311 28.2832L6.46811 28.0942L6.44111 27.9742L6.40961 27.8287L6.38261 27.7072L6.35561 27.5723L6.33011 27.4463L6.30311 27.3053C6.26411 27.0938 6.22811 26.8808 6.19661 26.6664L6.17111 26.4894L6.15461 26.3649L6.13511 26.2119C6.12507 26.1315 6.11557 26.051 6.10661 25.9704L6.09911 25.8999C5.98704 24.8312 5.96997 23.7546 6.04811 22.6828ZM8.4376 14.9586L26.3595 19.7521C25.8076 20.6595 25.3021 21.5984 24.8446 22.5643L41.787 27.0968C41.574 28.3267 41.235 29.5146 40.785 30.6439L23.4601 26.0094L6.18611 21.3899L6.20861 21.2399L6.22061 21.1664L6.23561 21.0659L6.25811 20.9354L6.28511 20.7885C6.32411 20.5665 6.36911 20.346 6.41711 20.1255L6.45911 19.9396L6.48911 19.8121L6.52511 19.6666C6.55811 19.5316 6.59261 19.3966 6.63011 19.2646L6.67211 19.1116L6.70661 18.9872L6.75161 18.8372L6.78911 18.7142L6.83411 18.5702L6.87311 18.4472L6.91961 18.3047C7.30804 17.1397 7.81573 16.0178 8.4346 14.9571L8.4376 14.9586ZM15.1006 8.35926L32.028 12.8858C31.1348 13.6965 30.2893 14.5583 29.496 15.467L41.2305 18.6062C41.631 19.8841 41.8935 21.2219 42 22.6033L9.1591 13.8202L9.2266 13.7227L9.2671 13.6627L9.3271 13.5802L9.3961 13.4827L9.4786 13.3687L9.5596 13.2607L9.6556 13.1318L9.7306 13.0343L9.8161 12.9248L9.8986 12.8198L9.9886 12.7088L10.0711 12.6053L10.1686 12.4898L10.2496 12.3908L10.3486 12.2753L10.4281 12.1854L10.5361 12.0624L10.6156 11.9724L10.7161 11.8614L10.7971 11.7744L10.9066 11.6574L10.9936 11.5674L11.0881 11.4669L11.3401 11.212L11.4901 11.065L11.5786 10.981L11.6926 10.8745C12.7264 9.90579 13.8702 9.06161 15.1006 8.35926ZM24.0256 6.0015H24.1711L24.2941 6.003L24.3976 6.0045L24.4786 6.0075L24.5806 6.0105L24.6496 6.012L24.7636 6.0165L24.8341 6.0195L24.9241 6.024L25.0051 6.027L25.1356 6.0345L25.2931 6.045L25.5091 6.06149L25.6411 6.07199L25.707 6.07799L25.8226 6.08999L25.9455 6.10199L26.016 6.10949L26.169 6.12749L26.244 6.13649L26.406 6.15748L26.5275 6.17248L26.5905 6.18148L26.688 6.19648L26.9985 6.24447L27.1035 6.26247L27.201 6.27897L27.411 6.31797L27.549 6.34496L27.714 6.37796L27.783 6.39296L27.8955 6.41696L27.957 6.43196L28.05 6.45145L28.113 6.46645L28.2105 6.48895L28.284 6.50695L28.3905 6.53244L28.5345 6.56844L28.7025 6.61344L28.872 6.65843L29.0415 6.70643L29.1165 6.72892L29.2215 6.75892L29.3385 6.79492L29.448 6.82941L29.523 6.85341L29.598 6.87741L29.712 6.91491L29.8605 6.9644L30.0135 7.01839L30.0855 7.04389L30.1815 7.07839L30.321 7.12938L30.486 7.19088L30.66 7.25837L30.81 7.31836L30.8805 7.34836L30.9705 7.38436L31.032 7.41135L31.1265 7.45035L31.1865 7.47735L31.272 7.51484L31.437 7.58684L31.587 7.65583L31.698 7.70832L31.8105 7.76232L31.9005 7.80431L32.0385 7.87331L32.175 7.9408L32.328 8.01879L32.4075 8.06079L32.481 8.09978L32.55 8.13578L32.64 8.18527L32.7015 8.21827L32.7795 8.26177L32.9115 8.33676L33.0705 8.42675L33.201 8.50324L33.2865 8.55423L33.366 8.60223L33.51 8.69072L33.642 8.77321L33.789 8.8662L33.843 8.9022L33.939 8.96369L34.065 9.04768L34.125 9.08818L34.218 9.15117L34.311 9.21567L34.3455 9.24116C34.4265 9.29666 34.5075 9.35365 34.587 9.41215L34.7115 9.50214L34.809 9.57413L34.893 9.63862L35.022 9.73611L35.145 9.8321L35.205 9.8771L35.28 9.93859L35.409 10.0421L35.5275 10.1396L35.655 10.2461C36.723 11.146 37.6845 12.1704 38.5185 13.2922L16.8331 7.49235L16.9261 7.45185L17.0236 7.40985L17.1451 7.35886L17.2741 7.30636C17.4436 7.23887 17.6146 7.17138 17.7856 7.10988L17.9296 7.05739L18.0691 7.0079L18.1951 6.9629L18.3391 6.9164C18.4696 6.87141 18.6031 6.82941 18.7351 6.78892L18.8716 6.74842L19.0006 6.71093L19.1536 6.66593L19.2811 6.63143L19.4311 6.59244L19.5601 6.55644L19.6951 6.52195L19.8316 6.48895L19.9741 6.45595L20.1091 6.42596L20.2561 6.39446L20.3926 6.36446L20.5351 6.33746L20.6731 6.31047L20.8231 6.28347L20.9596 6.25947L21.1066 6.23398L21.2446 6.21298L21.3901 6.19048L21.5281 6.17098L21.6811 6.15148L21.8176 6.13349L21.9751 6.11549L22.1101 6.10049L22.2676 6.08549C22.4071 6.07049 22.5466 6.05849 22.6876 6.04949L22.8466 6.0375L22.9816 6.03L23.1466 6.021L23.2861 6.015L23.4361 6.009L23.5816 6.006L23.7301 6.003L24.0256 6V6.0015Z\" fill=\"white\"/>\n</svg>`;\n\n// X.AI (Grok) icon\nexport const XAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"black\"/>\n<path d=\"M10.008 19.311L24.0672 40H30.3165L16.2557 19.311H10.008ZM16.2509 30.8017L10 40H16.2541L19.3771 35.4017L16.2509 30.8017ZM31.7459 8L20.941 23.8993L24.0672 28.5009L38 8H31.7459ZM32.8777 17.8382V40H38V10.3008L32.8777 17.8382Z\" fill=\"white\"/>\n</svg>`;\n\n// Z.AI (Zhipu AI) icon - simplified\nexport const ZAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" rx=\"4\" fill=\"#2D2D2D\"/>\n<path d=\"M35 10H13v4h14L13 34v4h22v-4H21l14-20V10z\" fill=\"white\"/>\n</svg>`;\n\n// EXAONE (LG AI) icon\nexport const EXAONE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#A8E6A3\"/>\n<text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" fill=\"#2C3E50\" font-family=\"Arial, sans-serif\" font-size=\"14\" font-weight=\"bold\">EXA</text>\n</svg>`;\n\n// OpenRouter icon\nexport const OPENROUTER_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\">\n<rect width=\"24\" height=\"24\" fill=\"#6366F1\"/>\n<path fill=\"white\" d=\"M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z\" transform=\"scale(0.75) translate(4 4)\"/>\n</svg>`;\n\n// OpenAI icon (for models going through OpenRouter)\nexport const OPENAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" rx=\"24\" fill=\"white\"/>\n<path d=\"M19.3418 18.5599V14.7599C19.3418 14.4399 19.4608 14.1998 19.7382 14.04L27.3102 9.63997C28.3409 9.03999 29.5699 8.76014 30.8383 8.76014C35.5954 8.76014 38.6085 12.4802 38.6085 16.4401C38.6085 16.72 38.6085 17.04 38.5687 17.3601L30.7194 12.72C30.2437 12.4401 29.7678 12.4401 29.2922 12.72L19.3418 18.5599ZM37.0226 33.36V24.2799C37.0226 23.7197 36.7846 23.3197 36.309 23.0398L26.3586 17.1998L29.6093 15.3197C29.8868 15.1599 30.1247 15.1599 30.4022 15.3197L37.9741 19.7197C40.1547 20.9999 41.6213 23.7197 41.6213 26.3596C41.6213 29.3995 39.8375 32.1999 37.0226 33.36ZM17.0029 25.3601L13.7522 23.4402C13.4748 23.2804 13.3557 23.0402 13.3557 22.7202V13.9203C13.3557 9.64039 16.6065 6.40016 21.0069 6.40016C22.6722 6.40016 24.2179 6.96029 25.5265 7.96025L17.7168 12.5204C17.2412 12.8003 17.0033 13.2002 17.0033 13.7605L17.0029 25.3601ZM24 29.44L19.3418 26.8001V21.2003L24 18.5604L28.6578 21.2003V26.8001L24 29.44ZM26.993 41.6002C25.3278 41.6002 23.7821 41.04 22.4735 40.0402L30.2831 35.4799C30.7588 35.2001 30.9967 34.8001 30.9967 34.2399V22.6399L34.2873 24.5598C34.5646 24.7196 34.6837 24.9597 34.6837 25.2798V34.0797C34.6837 38.3596 31.3931 41.6002 26.993 41.6002ZM17.5975 32.6802L10.0255 28.2803C7.84493 27.0001 6.37833 24.2803 6.37833 21.6404C6.37833 18.5604 8.20193 15.8004 11.0164 14.6403V23.7602C11.0164 24.3204 11.2544 24.7204 11.73 25.0003L21.641 30.8001L18.3902 32.6802C18.1129 32.84 17.8749 32.84 17.5975 32.6802ZM17.1617 39.2402C12.682 39.2402 9.39151 35.8402 9.39151 31.6401C9.39151 31.3201 9.43125 31.0001 9.47066 30.68L17.2803 35.2402C17.7559 35.5201 18.2319 35.5201 18.7074 35.2402L28.6578 29.4404V33.2404C28.6578 33.5605 28.5388 33.8005 28.2614 33.9604L20.6894 38.3604C19.6586 38.9603 18.4301 39.2402 17.1617 39.2402ZM26.993 44C31.7899 44 35.7936 40.5601 36.7057 36C41.1457 34.8399 44 30.6399 44 26.36C44 23.5598 42.8108 20.8401 40.6701 18.88C40.8683 18.0399 40.9872 17.1998 40.9872 16.3602C40.9872 10.6403 36.3885 6.35998 31.0763 6.35998C30.0062 6.35998 28.9754 6.51979 27.9446 6.88001C26.1604 5.11992 23.7025 4 21.0069 4C16.2101 4 12.2064 7.4398 11.2943 12C6.8543 13.1601 4 17.3601 4 21.6399C4 24.4401 5.18916 27.1599 7.32995 29.1199C7.13174 29.96 7.01277 30.8001 7.01277 31.6398C7.01277 37.3597 11.6114 41.6399 16.9236 41.6399C17.9938 41.6399 19.0246 41.4801 20.0554 41.1199C21.8392 42.88 24.2971 44 26.993 44Z\" fill=\"black\"/>\n</svg>`;\n\n// Mistral AI icon\nexport const MISTRAL_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF7000\"/>\n<path d=\"M10 10h8v8h-8zM18 10h4v8h-4zM26 10h4v8h-4zM30 10h8v8h-8zM10 18h8v4h-8zM30 18h8v4h-8zM10 22h8v4h-8zM18 22h12v4H18zM30 22h8v4h-8zM10 26h8v4h-8zM30 26h8v4h-8zM10 30h8v8h-8zM18 30h4v8h-4zM26 30h4v8h-4zM30 30h8v8h-8z\" fill=\"white\"/>\n</svg>`;\n\n// Cohere icon\nexport const COHERE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#39594D\"/>\n<circle cx=\"24\" cy=\"24\" r=\"12\" fill=\"#D18EE2\"/>\n<circle cx=\"24\" cy=\"24\" r=\"6\" fill=\"#39594D\"/>\n</svg>`;\n\n// Perplexity icon\nexport const PERPLEXITY_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#20808D\"/>\n<path d=\"M24 8L36 20v16L24 40 12 28V16L24 8z\" stroke=\"white\" stroke-width=\"2\" fill=\"none\"/>\n<path d=\"M24 8v32M12 20h24M12 28h24\" stroke=\"white\" stroke-width=\"2\"/>\n</svg>`;\n\n/**\n * Map of lab/provider IDs to their SVG icons.\n * Keys are normalized to lowercase.\n */\nexport const LAB_ICONS: Record<string, string> = {\n anthropic: ANTHROPIC_ICON,\n google: GOOGLE_ICON,\n 'google-ai': GOOGLE_ICON,\n meta: META_ICON,\n 'meta-llama': META_ICON,\n nvidia: NVIDIA_ICON,\n alibaba: ALIBABA_ICON,\n deepseek: DEEPSEEK_ICON,\n qwen: QWEN_ICON,\n minimax: MINIMAX_ICON,\n moonshotai: MOONSHOTAI_ICON,\n moonshot: MOONSHOTAI_ICON,\n 'x-ai': XAI_ICON,\n xai: XAI_ICON,\n 'z-ai': ZAI_ICON,\n zai: ZAI_ICON,\n zhipu: ZAI_ICON,\n exaone: EXAONE_ICON,\n lgai: EXAONE_ICON,\n openrouter: OPENROUTER_ICON,\n openai: OPENAI_ICON,\n 'openai-sdk': OPENAI_ICON,\n mistral: MISTRAL_ICON,\n mistralai: MISTRAL_ICON,\n cohere: COHERE_ICON,\n perplexity: PERPLEXITY_ICON,\n};\n\n/**\n * Convert SVG string to data URI for use in img src.\n */\nexport function svgToDataUri(svg: string): string {\n const encoded = encodeURIComponent(svg)\n .replace(/'/g, '%27')\n .replace(/\"/g, '%22');\n return `data:image/svg+xml,${encoded}`;\n}\n\n/**\n * Get the icon for a lab/provider as a data URI.\n * @param labId - The lab/provider ID (e.g., 'anthropic', 'google')\n * @returns Data URI string or undefined if not found\n */\nexport function getLabIconDataUri(labId: string): string | undefined {\n const icon = LAB_ICONS[labId.toLowerCase()];\n if (icon) {\n return svgToDataUri(icon);\n }\n return undefined;\n}\n\n/**\n * Extract the lab from a model ID.\n * OpenRouter model IDs are typically in format 'lab/model-name'.\n * @param modelId - The model ID (e.g., 'anthropic/claude-3-opus')\n * @returns The lab name or undefined\n */\nexport function extractLabFromModelId(modelId: string): string | undefined {\n if (!modelId || !modelId.includes('/')) {\n return undefined;\n }\n return modelId.split('/')[0].toLowerCase();\n}\n\n/**\n * Get the icon for a model as a data URI.\n * Extracts the lab from the model ID and returns the corresponding icon.\n * @param modelId - The model ID (e.g., 'anthropic/claude-3-opus')\n * @returns Data URI string or undefined if not found\n */\nexport function getModelIconDataUri(modelId: string): string | undefined {\n const lab = extractLabFromModelId(modelId);\n if (lab) {\n return getLabIconDataUri(lab);\n }\n return undefined;\n}\n","/**\n * OpenRouter provider options schema.\n *\n * Defines typed providerOptions for OpenRouter models, providing\n * TypeScript autocompletion and runtime validation.\n *\n * @see https://openrouter.ai/docs/guides/routing/provider-selection\n * @module\n */\n\nimport { z } from 'zod';\n\n/**\n * Percentile-based performance threshold.\n * Can be a simple number (applies to p50) or object with specific percentiles.\n */\nconst percentileSchema = z.union([\n z.number(),\n z.object({\n p50: z.number().optional(),\n p75: z.number().optional(),\n p90: z.number().optional(),\n p99: z.number().optional(),\n }),\n]);\n\n/**\n * OpenRouter provider routing options.\n *\n * These options control how OpenRouter routes requests to underlying providers.\n * They are nested under the `provider` key in providerOptions.\n *\n * @see https://openrouter.ai/docs/guides/routing/provider-selection\n */\nconst providerRoutingSchema = z.object({\n /** Provider slugs to try in order (e.g., ['anthropic', 'openai']) */\n order: z.array(z.string()).optional(),\n\n /** Allow fallback to other providers if preferred unavailable (default: true) */\n allow_fallbacks: z.boolean().optional(),\n\n /** Only use providers that support all parameters in the request */\n require_parameters: z.boolean().optional(),\n\n /** Control data storage policies: 'allow' or 'deny' */\n data_collection: z.enum(['allow', 'deny']).optional(),\n\n /** Restrict to Zero Data Retention endpoints only */\n zdr: z.boolean().optional(),\n\n /** Restrict to these providers only (exclusive list) */\n only: z.array(z.string()).optional(),\n\n /** Skip these providers */\n ignore: z.array(z.string()).optional(),\n\n /** Sort providers by price, throughput, or latency */\n sort: z.union([\n z.enum(['price', 'throughput', 'latency']),\n z.object({\n by: z.enum(['price', 'throughput', 'latency']),\n partition: z.enum(['model', 'none']).optional(),\n }),\n ]).optional(),\n\n /** Maximum price constraints (hard limit - request fails if no provider meets threshold) */\n max_price: z.object({\n /** Max price per million prompt tokens */\n prompt: z.number().optional(),\n /** Max price per million completion tokens */\n completion: z.number().optional(),\n /** Max price per request */\n request: z.number().optional(),\n /** Max price per image */\n image: z.number().optional(),\n }).optional(),\n\n /** Allowed quantization levels */\n quantizations: z.array(z.enum([\n 'int4', 'int8', 'fp4', 'fp6', 'fp8', 'fp16', 'bf16', 'fp32', 'unknown'\n ])).optional(),\n\n /** Minimum throughput in tokens/sec (soft preference) */\n preferred_min_throughput: percentileSchema.optional(),\n\n /** Maximum latency in seconds (soft preference) */\n preferred_max_latency: percentileSchema.optional(),\n\n /** Restrict to models allowing text distillation */\n enforce_distillable_text: z.boolean().optional(),\n}).passthrough(); // Allow unknown keys for forward compatibility\n\n/**\n * OpenRouter provider options schema.\n *\n * Routing options are nested under the `provider` key to match\n * OpenRouter's API structure.\n *\n * @example\n * ```typescript\n * providerOptions: {\n * provider: {\n * zdr: true,\n * max_price: { prompt: 1, completion: 5 },\n * order: ['anthropic', 'openai'],\n * },\n * }\n * ```\n */\nexport const openrouterProviderOptions = z.object({\n /** Provider routing configuration */\n provider: providerRoutingSchema.optional(),\n}).passthrough(); // Allow other top-level options for forward compatibility\n\n/**\n * TypeScript type for OpenRouter provider options.\n * Inferred from the Zod schema for type-safe usage.\n */\nexport type OpenRouterProviderOptions = z.infer<typeof openrouterProviderOptions>;\n","import type { ProviderFactoryWithOptions, ProviderFactoryConfig } from '@standardagents/spec';\nimport { OpenRouterProvider, type OpenRouterConfig } from './OpenRouterProvider';\nimport { openrouterProviderOptions } from './providerOptions';\n\n/**\n * OpenRouter provider factory for Standard Agents\n *\n * OpenRouter is a multi-provider gateway that routes requests to various LLM providers.\n * Includes typed providerOptions for routing configuration.\n *\n * @example\n * ```typescript\n * import { defineModel } from '@standardagents/builder';\n * import { openrouter } from '@standardagents/openrouter';\n *\n * export default defineModel({\n * name: 'claude-3-opus',\n * provider: openrouter,\n * model: 'anthropic/claude-3-opus',\n * inputPrice: 15,\n * outputPrice: 75,\n * capabilities: {\n * supportsImages: true,\n * supportsToolCalls: true,\n * maxContextTokens: 200000,\n * },\n * providerOptions: {\n * provider: {\n * zdr: true,\n * only: ['anthropic'],\n * },\n * },\n * });\n * ```\n */\nexport const openrouter: ProviderFactoryWithOptions<typeof openrouterProviderOptions> =\n Object.assign(\n (config: ProviderFactoryConfig) => new OpenRouterProvider(config as OpenRouterConfig),\n { providerOptions: openrouterProviderOptions }\n );\n\n// Re-export the provider options schema and types\nexport { openrouterProviderOptions, type OpenRouterProviderOptions } from './providerOptions';\n\n// Re-export the provider class for advanced usage\nexport { OpenRouterProvider, type OpenRouterConfig } from './OpenRouterProvider';\n\n// Re-export generation metadata utilities for post-stream usage fetching\nexport { fetchGenerationMetadata, type GenerationMetadata } from './transformers';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/OpenRouterProvider.ts","../src/transformers.ts","../src/icons.ts","../src/providerOptions.ts","../src/index.ts"],"sourcesContent":["import type {\n LLMProviderInterface as Provider,\n ProviderFactoryConfig,\n ProviderRequest,\n ProviderResponse,\n ProviderStreamChunk,\n ModelCapabilities,\n ProviderModelInfo,\n InspectedRequest,\n ResponseSummary,\n} from '@standardagents/spec';\nimport { ProviderError } from '@standardagents/spec';\nimport {\n buildCreateParams,\n transformResponse,\n createStreamState,\n parseSSEStream,\n createErrorChunk,\n truncateBase64,\n fetchGenerationMetadata,\n} from './transformers';\nimport {\n getModelIconDataUri,\n svgToDataUri,\n OPENROUTER_ICON,\n} from './icons';\n\nimport type { OpenRouter as OpenRouterClient } from '@openrouter/sdk';\n\n// Re-export ProviderError for consumers\nexport { ProviderError };\n\n/**\n * OpenRouter model info from their API\n */\ninterface OpenRouterModelInfo {\n id: string;\n name: string;\n context_length: number;\n top_provider?: {\n max_completion_tokens?: number;\n };\n architecture?: {\n modality?: string;\n tokenizer?: string;\n instruct_type?: string;\n };\n pricing?: {\n prompt: string;\n completion: string;\n image?: string;\n request?: string;\n };\n}\n\n/**\n * Extended provider config for OpenRouter\n */\nexport interface OpenRouterConfig extends ProviderFactoryConfig {\n /** List of providers to route to (e.g., ['openai', 'anthropic']) */\n providers?: string[];\n}\n\n/**\n * OpenRouter provider implementation for Standard Agents\n *\n * Uses the official @openrouter/sdk Responses API for API calls.\n * OpenRouter is a multi-provider gateway that routes requests to various LLM providers.\n */\nexport class OpenRouterProvider implements Provider {\n readonly name = 'openrouter';\n readonly specificationVersion = '1' as const;\n\n private client: OpenRouterClient | null = null;\n private config: OpenRouterConfig;\n\n constructor(config: OpenRouterConfig) {\n this.config = config;\n }\n\n private async getClient(): Promise<OpenRouterClient> {\n if (!this.client) {\n const { OpenRouter } = await import('@openrouter/sdk');\n this.client = new OpenRouter({\n apiKey: this.config.apiKey,\n serverURL: this.config.baseUrl,\n timeoutMs: this.config.timeout,\n });\n }\n return this.client;\n }\n\n supportsModel(_modelId: string): boolean {\n // OpenRouter supports a wide variety of models\n // Model support is determined at runtime\n return true;\n }\n\n /**\n * Get the icon for a model as a data URI.\n * Extracts the AI lab/organization from the model ID prefix and returns\n * the corresponding SVG icon as a data URI.\n * For example, 'anthropic/claude-3-opus' returns the Anthropic icon.\n * Falls back to the OpenRouter icon for unknown labs.\n */\n getIcon(modelId?: string): string {\n if (!modelId) {\n return svgToDataUri(OPENROUTER_ICON);\n }\n\n // Try to get model-specific icon (extracts lab from model ID)\n const modelIcon = getModelIconDataUri(modelId);\n if (modelIcon) {\n return modelIcon;\n }\n\n // Fall back to OpenRouter icon\n return svgToDataUri(OPENROUTER_ICON);\n }\n\n // ============================================================================\n // Model Capabilities\n // ============================================================================\n\n /** Cache for model capabilities to avoid repeated API calls */\n private static modelsCache: OpenRouterModelInfo[] | null = null;\n private static modelsCacheTime = 0;\n private static readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\n /**\n * Get capabilities for a specific model.\n * Fetches from OpenRouter's models API which provides rich metadata.\n */\n async getModelCapabilities(modelId: string): Promise<ModelCapabilities | null> {\n try {\n const models = await this.fetchModelsWithCache();\n const model = models.find((m) => m.id === modelId);\n\n if (!model) {\n return null;\n }\n\n return this.mapOpenRouterCapabilities(model);\n } catch (error) {\n console.error(`Failed to fetch capabilities for ${modelId}:`, error);\n return null;\n }\n }\n\n private async fetchModelsWithCache(): Promise<OpenRouterModelInfo[]> {\n const now = Date.now();\n\n // Return cached data if still valid\n if (\n OpenRouterProvider.modelsCache &&\n now - OpenRouterProvider.modelsCacheTime < OpenRouterProvider.CACHE_TTL\n ) {\n return OpenRouterProvider.modelsCache;\n }\n\n // Fetch from OpenRouter API\n const response = await fetch('https://openrouter.ai/api/v1/models');\n\n if (!response.ok) {\n throw new Error(`OpenRouter API error: ${response.status}`);\n }\n\n const data = (await response.json()) as { data: OpenRouterModelInfo[] };\n OpenRouterProvider.modelsCache = data.data || [];\n OpenRouterProvider.modelsCacheTime = now;\n\n return OpenRouterProvider.modelsCache;\n }\n\n private mapOpenRouterCapabilities(model: OpenRouterModelInfo): ModelCapabilities {\n const capabilities: ModelCapabilities = {\n maxContextTokens: model.context_length,\n supportsStreaming: true, // OpenRouter always supports streaming\n supportsToolCalls: true, // Most models support it, exceptions below\n supportsJsonMode: true, // Most modern models support it\n };\n\n // Determine image support from modality or architecture\n if (\n model.architecture?.modality?.includes('image') ||\n model.architecture?.modality?.includes('multimodal') ||\n model.architecture?.modality === 'text+image->text'\n ) {\n capabilities.supportsImages = true;\n } else {\n capabilities.supportsImages = false;\n }\n\n // Max output tokens from top provider info\n if (model.top_provider?.max_completion_tokens) {\n capabilities.maxOutputTokens = model.top_provider.max_completion_tokens;\n }\n\n // Reasoning levels for specific model families\n if (model.id.includes('o1') || model.id.includes('o3') || model.id.includes('o4')) {\n capabilities.reasoningLevels = { 0: null, 33: 'low', 66: 'medium', 100: 'high' };\n // o1-preview and o1-mini don't support tools\n if (model.id.includes('o1-preview') || model.id.includes('o1-mini')) {\n capabilities.supportsToolCalls = false;\n capabilities.supportsJsonMode = false;\n }\n } else if (model.id.includes('claude') && model.id.includes('thinking')) {\n capabilities.reasoningLevels = { 0: null, 100: 'enabled' };\n }\n\n return capabilities;\n }\n\n /**\n * Get list of available models from OpenRouter.\n * Fetches from OpenRouter's public models API with caching.\n *\n * @param filter - Optional search string to filter models by name/id\n */\n async getModels(filter?: string): Promise<ProviderModelInfo[]> {\n try {\n const rawModels = await this.fetchModelsWithCache();\n\n let models = rawModels.map((m) => this.mapToProviderModelInfo(m));\n\n if (filter) {\n const lowerFilter = filter.toLowerCase();\n models = models.filter(\n (m) =>\n m.id.toLowerCase().includes(lowerFilter) ||\n m.name.toLowerCase().includes(lowerFilter) ||\n (m.description && m.description.toLowerCase().includes(lowerFilter))\n );\n }\n\n return models;\n } catch (error) {\n console.error('Failed to fetch models from OpenRouter:', error);\n return [];\n }\n }\n\n private mapToProviderModelInfo(model: OpenRouterModelInfo): ProviderModelInfo {\n return {\n id: model.id,\n name: model.name,\n description: model.architecture?.modality || undefined,\n contextLength: model.context_length,\n iconId: this.getIcon(model.id),\n slug: model.id.replace(/[/:]/g, '-'), // URL-safe slug\n };\n }\n\n // ============================================================================\n // Generation Methods\n // ============================================================================\n\n async generate(request: ProviderRequest): Promise<ProviderResponse> {\n const client = await this.getClient();\n\n try {\n const params = buildCreateParams(request);\n\n // Add provider routing if configured\n if (this.config.providers && this.config.providers.length > 0) {\n (params as any).provider = { only: this.config.providers };\n }\n\n const response = await client.beta.responses.send({\n ...params,\n stream: false,\n });\n\n return transformResponse(response);\n } catch (error) {\n throw this.toProviderError(error);\n }\n }\n\n async stream(request: ProviderRequest): Promise<AsyncIterable<ProviderStreamChunk>> {\n const self = this;\n const apiKey = this.config.apiKey;\n const baseUrl = this.config.baseUrl || 'https://openrouter.ai/api/v1';\n\n try {\n const params = buildCreateParams(request);\n\n // Add provider routing if configured\n if (this.config.providers && this.config.providers.length > 0) {\n (params as any).provider = { only: this.config.providers };\n }\n\n // Use raw fetch instead of SDK to bypass Zod validation issues\n // SDK validation fails when OpenRouter returns null for usage in stream events\n const response = await fetch(`${baseUrl}/responses`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n ...params,\n stream: true,\n }),\n signal: request.signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage = `OpenRouter API error: ${response.status}`;\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.error?.message || errorJson.message || errorMessage;\n } catch {\n errorMessage = errorText || errorMessage;\n }\n throw new ProviderError(errorMessage, 'invalid_request', response.status);\n }\n\n return {\n async *[Symbol.asyncIterator]() {\n const state = createStreamState();\n let finishChunk: ProviderStreamChunk | null = null;\n let responseId: string | null = null;\n\n try {\n // Process stream and buffer the finish chunk\n for await (const chunk of parseSSEStream(response, state)) {\n if (chunk.type === 'finish') {\n // Buffer the finish chunk and extract responseId\n finishChunk = chunk;\n responseId = (chunk as any).responseId || null;\n } else {\n // Yield all other chunks immediately\n yield chunk;\n }\n }\n\n // Yield finish chunk immediately - don't block on metadata fetch\n // Include responseId so caller can fetch metadata asynchronously if needed\n if (finishChunk) {\n // Add metadata fetch info to the chunk for async fetching\n const finishWithMeta = {\n ...finishChunk,\n // Include what's needed to fetch metadata later\n _asyncMetadata: responseId ? {\n generationId: responseId,\n apiKey,\n baseUrl,\n } : undefined,\n };\n yield finishWithMeta;\n }\n } catch (error) {\n const providerError = self.toProviderError(error);\n yield createErrorChunk(providerError.message, providerError.code);\n }\n },\n };\n } catch (error) {\n throw this.toProviderError(error);\n }\n }\n\n // ============================================================================\n // Response Metadata (Async)\n // ============================================================================\n\n /**\n * Fetch additional metadata about a completed response.\n * Called asynchronously after the response is received.\n *\n * Uses OpenRouter's /generation endpoint to fetch accurate provider info,\n * token counts, and cost data.\n */\n async getResponseMetadata(\n summary: ResponseSummary,\n signal?: AbortSignal\n ): Promise<Record<string, unknown> | null> {\n if (!summary.responseId) {\n return null;\n }\n\n const metadata = await fetchGenerationMetadata(\n this.config.apiKey,\n summary.responseId,\n this.config.baseUrl || 'https://openrouter.ai/api/v1',\n signal\n );\n\n if (!metadata) {\n return null;\n }\n\n return {\n actual_provider: metadata.providerName,\n native_tokens_prompt: metadata.nativePromptTokens,\n native_tokens_completion: metadata.nativeCompletionTokens,\n generation_cost: metadata.totalCost,\n generation_latency: metadata.latency,\n };\n }\n\n // ============================================================================\n // Error Handling\n // ============================================================================\n\n private toProviderError(error: unknown): ProviderError {\n if (error instanceof ProviderError) {\n return error;\n }\n\n if (error instanceof Error) {\n const anyError = error as any;\n const status = anyError.status || anyError.statusCode;\n const retryAfter = anyError.headers?.['retry-after']\n ? parseInt(anyError.headers['retry-after'], 10)\n : undefined;\n\n if (status === 429) {\n return new ProviderError(error.message, 'rate_limit', status, retryAfter);\n }\n if (status === 401 || status === 403) {\n return new ProviderError(error.message, 'auth_error', status);\n }\n if (status === 400) {\n return new ProviderError(error.message, 'invalid_request', status);\n }\n if (status >= 500) {\n return new ProviderError(error.message, 'server_error', status);\n }\n if (error.name === 'AbortError' || anyError.code === 'ETIMEDOUT') {\n return new ProviderError(error.message, 'timeout');\n }\n\n return new ProviderError(error.message, 'unknown', status);\n }\n\n return new ProviderError(String(error), 'unknown');\n }\n\n // ============================================================================\n // Inspection\n // ============================================================================\n\n /**\n * Transform a ProviderRequest to OpenRouter Responses API format for inspection.\n * Returns the exact request body that would be sent to OpenRouter, with base64 data truncated.\n */\n async inspectRequest(request: ProviderRequest): Promise<InspectedRequest> {\n const params = buildCreateParams(request);\n\n // Add provider routing if configured\n if (this.config.providers && this.config.providers.length > 0) {\n (params as any).provider = { only: this.config.providers };\n }\n\n return {\n body: truncateBase64(params as unknown as Record<string, unknown>),\n messagesPath: 'input',\n metadata: {\n endpoint: 'https://openrouter.ai/api/v1/responses',\n },\n };\n }\n}\n","/**\n * Discrete transformer functions for OpenRouter Responses API provider.\n * These functions convert between Standard Agent types and OpenRouter Responses API types.\n * Kept as pure functions for easy testing.\n */\n\nimport type {\n ProviderRequest,\n ProviderResponse,\n ProviderMessage,\n ProviderTool,\n ProviderToolCallPart,\n ProviderFinishReason,\n ProviderUsage,\n ProviderStreamChunk,\n ContentPart,\n ProviderGeneratedImage,\n} from '@standardagents/spec';\n\nimport type {\n OpenResponsesRequest,\n OpenResponsesEasyInputMessage,\n OpenResponsesFunctionCallOutput,\n OpenResponsesNonStreamingResponse,\n OpenResponsesStreamEvent,\n ResponseInputText,\n ResponseInputImage,\n ResponsesOutputItem,\n ResponsesOutputMessage,\n ResponsesOutputItemFunctionCall,\n OpenResponsesFunctionToolCall,\n OpenResponsesRequestToolFunction,\n} from '@openrouter/sdk/models';\n\n// Type for input items (non-string variant of OpenResponsesInput)\ntype InputItem = OpenResponsesFunctionToolCall | ResponsesOutputMessage | ResponsesOutputItemFunctionCall | OpenResponsesFunctionCallOutput | OpenResponsesEasyInputMessage;\n\n/**\n * Result type for transformToolMessage.\n * Contains the tool output and an optional synthetic user message for attachments.\n * OpenRouter doesn't support images in function_call_output, so we inject them\n * as synthetic user messages after the tool output.\n */\nexport type ToolMessageTransformResult = {\n toolOutput: OpenResponsesFunctionCallOutput;\n syntheticUserMessage?: OpenResponsesEasyInputMessage;\n};\n\n// ============================================================================\n// Type aliases for clarity\n// ============================================================================\n\n/**\n * Content type for function call output with multimodal content.\n * Note: The API accepts arrays here, but SDK types only allow strings.\n * We serialize this to JSON for type safety.\n */\ntype FunctionCallOutputContent =\n | { type: 'input_text'; text: string }\n | { type: 'input_image'; image_url: string; detail?: 'auto' | 'low' | 'high' };\n\n// ============================================================================\n// Input Content Transformers\n// ============================================================================\n\n/**\n * Transform a single content part to Responses API input format.\n *\n * IMPORTANT: We use snake_case field names (image_url) because we bypass the SDK\n * and send raw JSON to the API. The SDK would normally convert camelCase to snake_case.\n */\nexport function transformContentPart(\n part: ContentPart\n): ResponseInputText | ResponseInputImage {\n if (part.type === 'text') {\n return { type: 'input_text', text: part.text };\n }\n if (part.type === 'image') {\n const data = part.data || '';\n const imageUrl = data.startsWith('data:')\n ? data\n : `data:${part.mediaType || 'image/png'};base64,${data}`;\n // Use snake_case image_url for raw API (SDK types use camelCase internally)\n return {\n type: 'input_image',\n image_url: imageUrl,\n detail: part.detail || 'auto',\n } as unknown as ResponseInputImage;\n }\n // Handle image_url format (stored format uses this instead of 'image')\n if (part.type === 'image_url') {\n const url = part.image_url?.url || '';\n const detail = part.image_url?.detail || 'auto';\n // Use snake_case image_url for raw API (SDK types use camelCase internally)\n return {\n type: 'input_image',\n image_url: url,\n detail,\n } as unknown as ResponseInputImage;\n }\n // File parts - convert to text placeholder (OpenRouter doesn't support files directly)\n return {\n type: 'input_text',\n text: `[File: ${part.filename || 'file'}]`,\n };\n}\n\n/**\n * Transform message content (string or parts array) to Responses API format.\n */\nexport function transformMessageContent(\n content: string | ContentPart[]\n): string | Array<ResponseInputText | ResponseInputImage> {\n if (typeof content === 'string') {\n return content;\n }\n return content.map(transformContentPart);\n}\n\n// ============================================================================\n// Message Transformers\n// ============================================================================\n\n/**\n * Transform a user message to Responses API input item.\n */\nexport function transformUserMessage(\n msg: ProviderMessage & { role: 'user' }\n): OpenResponsesEasyInputMessage {\n const content = transformMessageContent(msg.content);\n return {\n role: 'user',\n content: typeof content === 'string' ? content : content,\n };\n}\n\n/**\n * Transform an assistant message to Responses API input items.\n * Assistant messages from previous turns need special handling.\n *\n * Handles:\n * - Text content → output message\n * - Tool calls → function_call items\n * - Reasoning details → reasoning item (for multi-turn conversations with reasoning models)\n */\nexport function transformAssistantMessage(\n msg: ProviderMessage & { role: 'assistant' }\n): InputItem[] {\n const items: InputItem[] = [];\n\n // Add reasoning item if present (from previous reasoning model responses)\n // This preserves reasoning context across multi-turn conversations\n // Note: API expects summary as array of { type: 'summary_text', text: '...' } objects\n const reasoningDetails = (msg as any).reasoningDetails as Array<{ type: string; text: string }> | undefined;\n if (reasoningDetails && reasoningDetails.length > 0) {\n // Extract summary texts (as objects) and encrypted content\n const summaryTexts: Array<{ type: 'summary_text'; text: string }> = [];\n let encryptedContent: string | undefined;\n\n for (const detail of reasoningDetails) {\n if (detail.type === 'reasoning.text') {\n summaryTexts.push({ type: 'summary_text', text: detail.text });\n } else if (detail.type === 'reasoning.encrypted') {\n encryptedContent = detail.text;\n }\n }\n\n // Only add reasoning item if we have summary texts\n if (summaryTexts.length > 0) {\n // API expects summary as objects, encrypted_content in snake_case\n const reasoningItem = {\n type: 'reasoning' as const,\n id: '',\n summary: summaryTexts,\n ...(encryptedContent ? { encrypted_content: encryptedContent } : {}),\n };\n items.push(reasoningItem as unknown as InputItem);\n }\n }\n\n // Add assistant message if there's content OR if there are tool calls\n const hasToolCalls = msg.toolCalls && msg.toolCalls.length > 0;\n if (msg.content || hasToolCalls) {\n // Use type assertion since we're constructing a valid ResponsesOutputMessage\n const outputMessage = {\n type: 'message' as const,\n role: 'assistant' as const,\n id: '',\n status: 'completed' as const,\n content: msg.content\n ? [{ type: 'output_text' as const, text: msg.content, annotations: [] }]\n : [],\n } satisfies ResponsesOutputMessage;\n items.push(outputMessage);\n }\n\n // Add tool calls if present\n if (msg.toolCalls && msg.toolCalls.length > 0) {\n for (const tc of msg.toolCalls) {\n // Use snake_case call_id for raw API (SDK types use camelCase internally)\n // We bypass SDK validation so must match raw API format\n const functionCall = {\n type: 'function_call' as const,\n call_id: tc.id,\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n } as unknown as ResponsesOutputItemFunctionCall;\n items.push(functionCall);\n }\n }\n\n return items;\n}\n\n/**\n * Transform a tool result message to Responses API input items.\n *\n * OpenRouter's API does not support image_url parts in function_call_output (tool results).\n * When a tool returns image attachments, we:\n * 1. Keep only text in the tool output\n * 2. Create a synthetic user message with the images to inject after the tool output\n *\n * This transformation is interstitial - it only affects data sent to the LLM,\n * NOT the stored message history.\n */\nexport function transformToolMessage(\n msg: ProviderMessage & { role: 'tool' }\n): ToolMessageTransformResult {\n let output: string;\n\n if (typeof msg.content === 'string') {\n output = msg.content;\n } else if ('type' in msg.content) {\n if (msg.content.type === 'text') {\n output = msg.content.text;\n } else if (msg.content.type === 'error') {\n output = `Error: ${msg.content.error}`;\n } else {\n output = JSON.stringify(msg.content);\n }\n } else {\n output = JSON.stringify(msg.content);\n }\n\n // Check for image attachments\n const imageAttachments = msg.attachments?.filter(a => a.type === 'image' && a.data) || [];\n\n // Base tool output (text only, or \"Success\" if no text but has images)\n // Use snake_case call_id for raw API (SDK types use camelCase internally)\n const toolOutput = {\n type: 'function_call_output',\n call_id: msg.toolCallId,\n output: output || (imageAttachments.length > 0 ? 'Success' : ''),\n } as unknown as OpenResponsesFunctionCallOutput;\n\n // If no images, return just the tool output\n if (imageAttachments.length === 0) {\n return { toolOutput };\n }\n\n // Create synthetic user message with images\n // OpenRouter doesn't support images in function_call_output, so we inject\n // a user message with the images after the tool output\n // IMPORTANT: Use snake_case (image_url) for raw API - SDK would convert camelCase\n const imageContent: Array<{ type: string; text?: string; image_url?: string; detail?: string }> = [];\n\n // Descriptive text\n const toolName = msg.toolName || 'the tool';\n const isSingle = imageAttachments.length === 1;\n const descriptor = isSingle ? 'the file' : `${imageAttachments.length} files`;\n const verb = isSingle ? 'is' : 'are';\n imageContent.push({\n type: 'input_text',\n text: `Here ${verb} ${descriptor} from ${toolName}:`,\n });\n\n // Add each image\n for (const attachment of imageAttachments) {\n const attachmentData = attachment.data || '';\n const imageData = attachmentData.startsWith('data:')\n ? attachmentData\n : `data:${attachment.mediaType || 'image/png'};base64,${attachmentData}`;\n\n imageContent.push({\n type: 'input_image',\n image_url: imageData,\n detail: 'auto',\n });\n }\n\n // Construct as a simple user message (same format as transformUserMessage)\n // The API accepts array content for multimodal messages even though SDK types\n // may not reflect this\n const syntheticUserMessage = {\n role: 'user' as const,\n content: imageContent,\n } as OpenResponsesEasyInputMessage;\n\n return { toolOutput, syntheticUserMessage };\n}\n\n/**\n * Transform an array of ProviderMessages to Responses API input format.\n * Returns both the input items and extracted system instructions.\n *\n * For tool messages with image attachments, this injects synthetic user messages\n * after the tool output to work around OpenRouter's lack of image support in\n * function_call_output.\n *\n * NOTE: OpenRouter's Responses API may not fully support the `instructions` field\n * like OpenAI does. We include system messages BOTH as the first input item AND\n * as instructions for maximum compatibility.\n */\nexport function transformMessages(messages: ProviderMessage[]): {\n input: InputItem[];\n instructions: string | undefined;\n} {\n let instructions: string | undefined;\n const input: InputItem[] = [];\n\n for (const msg of messages) {\n switch (msg.role) {\n case 'system':\n // Accumulate system messages for instructions field\n instructions = instructions\n ? `${instructions}\\n\\n${msg.content}`\n : msg.content;\n // ALSO add as input item for OpenRouter compatibility\n // OpenRouter docs show system messages as: { type: 'message', role: 'system', content: '...' }\n input.push({\n type: 'message',\n role: 'system',\n content: msg.content,\n } as unknown as InputItem);\n break;\n\n case 'user':\n input.push(transformUserMessage(msg));\n break;\n\n case 'assistant':\n input.push(...transformAssistantMessage(msg));\n break;\n\n case 'tool': {\n // Handle synthetic user messages for image attachments\n const result = transformToolMessage(msg);\n input.push(result.toolOutput);\n if (result.syntheticUserMessage) {\n input.push(result.syntheticUserMessage);\n }\n break;\n }\n }\n }\n\n return { input, instructions };\n}\n\n// ============================================================================\n// Tool Transformers\n// ============================================================================\n\n/**\n * Transform a single ProviderTool to Responses API function tool.\n */\nexport function transformTool(tool: ProviderTool): OpenResponsesRequestToolFunction {\n const inputParams = tool.function.parameters as Record<string, unknown> | null | undefined;\n\n let parameters: Record<string, unknown> | null;\n if (inputParams && typeof inputParams === 'object') {\n parameters = {\n ...inputParams,\n additionalProperties: false,\n };\n } else {\n // Tools with no parameters need a valid empty schema\n parameters = {\n type: 'object',\n properties: {},\n required: [],\n additionalProperties: false,\n };\n }\n\n return {\n type: 'function',\n name: tool.function.name,\n description: tool.function.description || undefined,\n parameters,\n strict: true,\n };\n}\n\n/**\n * Transform an array of ProviderTools to Responses API format.\n */\nexport function transformTools(tools: ProviderTool[]): OpenResponsesRequestToolFunction[] {\n return tools.map(transformTool);\n}\n\n/**\n * Transform tool choice option to Responses API format.\n */\nexport function transformToolChoice(\n choice: ProviderRequest['toolChoice']\n): OpenResponsesRequest['toolChoice'] {\n if (choice === 'auto') {\n return 'auto';\n }\n if (choice === 'none') {\n return 'none';\n }\n if (choice === 'required') {\n return 'required';\n }\n if (typeof choice === 'object' && 'name' in choice) {\n return { type: 'function', name: choice.name };\n }\n return 'auto';\n}\n\n// ============================================================================\n// Response Transformers\n// ============================================================================\n\n/**\n * Map Responses API status/incomplete_details to Provider finish reason.\n */\nexport function mapFinishReason(response: OpenResponsesNonStreamingResponse): ProviderFinishReason {\n if (response.status === 'failed') {\n return 'error';\n }\n if (response.status === 'incomplete') {\n if (response.incompleteDetails?.reason === 'max_output_tokens') {\n return 'length';\n }\n if (response.incompleteDetails?.reason === 'content_filter') {\n return 'content_filter';\n }\n }\n\n // Check if there are function calls in the output\n const hasToolCalls = response.output.some(\n (item) => item.type === 'function_call'\n );\n if (hasToolCalls) {\n return 'tool_calls';\n }\n\n return 'stop';\n}\n\n/**\n * Extract text content from response output items.\n */\nexport function extractTextContent(output: ResponsesOutputItem[]): string | null {\n const textParts: string[] = [];\n\n for (const item of output) {\n if (item.type === 'message' && item.role === 'assistant') {\n for (const content of item.content) {\n if (content.type === 'output_text') {\n textParts.push(content.text);\n }\n }\n }\n }\n\n return textParts.length > 0 ? textParts.join('') : null;\n}\n\n/**\n * Extract tool calls from response output items.\n */\nexport function extractToolCalls(\n output: ResponsesOutputItem[]\n): ProviderToolCallPart[] | undefined {\n const toolCalls: ProviderToolCallPart[] = [];\n\n for (const item of output) {\n if (item.type === 'function_call') {\n let parsedArgs: Record<string, unknown> = {};\n try {\n parsedArgs = item.arguments ? JSON.parse(item.arguments) : {};\n } catch {\n // Keep empty object on parse failure\n }\n\n toolCalls.push({\n id: item.callId,\n name: item.name,\n arguments: parsedArgs,\n });\n }\n }\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n}\n\n/**\n * Extract generated images from response output items.\n * Handles image_generation_call output items from OpenRouter.\n */\nexport function extractImages(\n output: ResponsesOutputItem[]\n): ProviderGeneratedImage[] | undefined {\n const images: ProviderGeneratedImage[] = [];\n\n for (const item of output) {\n const itemAny = item as any;\n\n // Handle image_generation_call output items\n if (itemAny.type === 'image_generation_call' && itemAny.result) {\n const imageData = itemAny.result as string;\n\n // Determine if it's a URL or base64 data\n const isDataUrl = imageData.startsWith('data:');\n const isHttpUrl = imageData.startsWith('http://') || imageData.startsWith('https://');\n\n // Try to determine media type from data URL or default to png\n let mediaType = 'image/png';\n if (isDataUrl) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) {\n mediaType = match[1];\n }\n }\n\n images.push({\n id: itemAny.id || undefined,\n data: imageData,\n mediaType,\n // Include revised prompt if available\n revisedPrompt: itemAny.revised_prompt || itemAny.revisedPrompt || undefined,\n });\n }\n\n // Also check for images in message content (some models return images this way)\n if (itemAny.type === 'message' && itemAny.content && Array.isArray(itemAny.content)) {\n for (const content of itemAny.content) {\n // Check for output_image type content\n if (content.type === 'output_image' || content.type === 'image') {\n const imageData = content.image || content.data || content.url || content.image_url;\n if (imageData) {\n let mediaType = content.media_type || content.mediaType || 'image/png';\n if (typeof imageData === 'string' && imageData.startsWith('data:')) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) {\n mediaType = match[1];\n }\n }\n images.push({\n id: content.id || undefined,\n data: imageData,\n mediaType,\n });\n }\n }\n }\n }\n }\n\n return images.length > 0 ? images : undefined;\n}\n\n/**\n * Extended usage type that includes OpenRouter-specific fields.\n * These fields are returned when `usage: { include: true }` is in the request.\n *\n * Note: Raw API returns snake_case (input_tokens), SDK may normalize to camelCase (inputTokens).\n * We handle both formats for robustness.\n */\ninterface OpenRouterExtendedUsage {\n // camelCase (SDK normalized)\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n inputTokensDetails?: { cachedTokens?: number };\n outputTokensDetails?: { reasoningTokens?: number };\n // snake_case (raw API)\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n input_tokens_details?: { cached_tokens?: number };\n output_tokens_details?: { reasoning_tokens?: number };\n // OpenRouter-specific fields (when usage.include is true)\n cost?: number;\n native_tokens_prompt?: number;\n native_tokens_completion?: number;\n nativeTokensPrompt?: number;\n nativeTokensCompletion?: number;\n}\n\n/**\n * Transform Responses API usage to Provider usage format.\n * Includes cost when available (requires usage: { include: true } in request).\n * Handles both snake_case (raw API) and camelCase (SDK normalized) field names.\n */\nexport function transformUsage(\n usage: OpenRouterExtendedUsage | undefined | null,\n actualProvider?: string\n): ProviderUsage {\n if (!usage) {\n return {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n };\n }\n\n // Handle both snake_case and camelCase field names\n const inputTokens = usage.native_tokens_prompt || usage.nativeTokensPrompt ||\n usage.input_tokens || usage.inputTokens || 0;\n const outputTokens = usage.native_tokens_completion || usage.nativeTokensCompletion ||\n usage.output_tokens || usage.outputTokens || 0;\n const totalTokens = usage.total_tokens || usage.totalTokens || 0;\n\n const reasoningTokens = usage.output_tokens_details?.reasoning_tokens ||\n usage.outputTokensDetails?.reasoningTokens;\n const cachedTokens = usage.input_tokens_details?.cached_tokens ||\n usage.inputTokensDetails?.cachedTokens;\n\n return {\n promptTokens: inputTokens,\n completionTokens: outputTokens,\n totalTokens: totalTokens || (inputTokens + outputTokens),\n reasoningTokens,\n cachedTokens,\n cost: usage.cost,\n provider: actualProvider,\n };\n}\n\n/**\n * Transform a complete Responses API response to ProviderResponse.\n */\nexport function transformResponse(response: OpenResponsesNonStreamingResponse): ProviderResponse {\n const content = extractTextContent(response.output);\n const toolCalls = extractToolCalls(response.output);\n const images = extractImages(response.output);\n\n // Extract actual provider from model ID (e.g., \"google/gemini-2.5-flash\" -> \"google\")\n const actualProvider = response.model?.split('/')[0] || undefined;\n\n return {\n content,\n toolCalls,\n images,\n finishReason: mapFinishReason(response),\n usage: transformUsage(response.usage as OpenRouterExtendedUsage, actualProvider),\n metadata: {\n model: response.model,\n provider: 'openrouter',\n actualProvider,\n requestId: response.id,\n },\n };\n}\n\n// ============================================================================\n// Reasoning Details Extraction\n// ============================================================================\n\n/**\n * Extract reasoning details from response output items or streamed content.\n *\n * OpenRouter reasoning format:\n * - Output item with type: \"reasoning\"\n * - Contains: encrypted_content (string) and summary (string[])\n * - Streaming: response.reasoning.delta events\n */\nfunction extractReasoningDetails(\n output: ResponsesOutputItem[] | undefined,\n streamedReasoningContent: string\n): Array<{ type: string; text: string }> | undefined {\n const details: Array<{ type: string; text: string }> = [];\n\n // Extract from response output items (for non-streaming or completed response)\n if (output && Array.isArray(output)) {\n for (const item of output) {\n const itemAny = item as any;\n\n // OpenRouter format: type \"reasoning\" with summary array and/or encrypted_content\n if (itemAny.type === 'reasoning') {\n // Summary is an array of reasoning steps (preferred for display)\n if (itemAny.summary && Array.isArray(itemAny.summary)) {\n for (const step of itemAny.summary) {\n if (typeof step === 'string') {\n details.push({ type: 'reasoning.text', text: step });\n }\n }\n }\n // Fallback to encrypted_content if no summary\n // (encrypted_content may not be human-readable)\n if (details.length === 0 && itemAny.encrypted_content) {\n details.push({ type: 'reasoning.encrypted', text: itemAny.encrypted_content });\n }\n // Also check for plain content field\n if (itemAny.content) {\n if (typeof itemAny.content === 'string') {\n details.push({ type: 'reasoning.text', text: itemAny.content });\n } else if (Array.isArray(itemAny.content)) {\n for (const part of itemAny.content) {\n if (part.text) {\n details.push({ type: 'reasoning.text', text: part.text });\n }\n }\n }\n }\n }\n\n // Also check for message items with reasoning content\n if (itemAny.type === 'message' && itemAny.content && Array.isArray(itemAny.content)) {\n for (const part of itemAny.content) {\n if (part.type === 'reasoning' || part.type === 'reasoning_text') {\n details.push({ type: 'reasoning.text', text: part.text || '' });\n }\n }\n }\n }\n }\n\n // If no reasoning found in output items but we have streamed reasoning, use that\n if (details.length === 0 && streamedReasoningContent) {\n details.push({ type: 'reasoning.text', text: streamedReasoningContent });\n }\n\n return details.length > 0 ? details : undefined;\n}\n\n// ============================================================================\n// Request Builder\n// ============================================================================\n\n/**\n * Build a Responses API create request from a ProviderRequest.\n */\nexport function buildCreateParams(request: ProviderRequest): OpenResponsesRequest {\n const { input, instructions } = transformMessages(request.messages);\n\n const params: OpenResponsesRequest = {\n model: request.model,\n input,\n store: false, // Always stateless\n };\n\n // Note: We don't include `usage: { include: true }` in the request\n // as it slows down responses. Instead, we fetch generation metadata\n // after the stream completes using fetchGenerationMetadata().\n\n if (instructions) {\n params.instructions = instructions;\n }\n\n // Tools\n if (request.tools && request.tools.length > 0) {\n params.tools = transformTools(request.tools);\n\n if (request.toolChoice !== undefined) {\n params.toolChoice = transformToolChoice(request.toolChoice);\n }\n\n if (request.parallelToolCalls !== undefined) {\n params.parallelToolCalls = request.parallelToolCalls;\n }\n }\n\n // Generation parameters\n if (request.maxOutputTokens !== undefined) {\n params.maxOutputTokens = request.maxOutputTokens;\n }\n\n if (request.temperature !== undefined) {\n params.temperature = request.temperature;\n }\n\n if (request.topP !== undefined) {\n params.topP = request.topP;\n }\n\n // Reasoning (for models that support it)\n // OpenRouter supports: minimal, low, medium, high\n if (request.reasoning?.level !== undefined) {\n const effortMap: Record<number, 'minimal' | 'low' | 'medium' | 'high'> = {\n 10: 'minimal', // Basic reasoning\n 33: 'low', // Light reasoning\n 66: 'medium', // Balanced reasoning\n 100: 'high', // Deep reasoning\n };\n const effort = effortMap[request.reasoning.level];\n if (effort) {\n params.reasoning = {\n effort,\n };\n }\n }\n\n // Response format\n if (request.responseFormat) {\n if (request.responseFormat.type === 'json') {\n if (request.responseFormat.schema) {\n params.text = {\n format: {\n type: 'json_schema',\n name: 'response',\n schema: request.responseFormat.schema,\n strict: true,\n },\n };\n } else {\n params.text = {\n format: { type: 'json_object' },\n };\n }\n }\n }\n\n // Merge provider-specific options\n if (request.providerOptions) {\n Object.assign(params, request.providerOptions);\n }\n\n return params;\n}\n\n// ============================================================================\n// Stream State and Chunk Transformers\n// ============================================================================\n\nexport interface StreamState {\n toolCalls: Map<string, { id: string; name: string; arguments: string }>;\n reasoningContent: string;\n hasContent: boolean;\n hasReasoning: boolean;\n currentItemId: string | null;\n // Image generation tracking\n imageGenerations: Map<string, { id: string; data: string; status: string }>;\n}\n\nexport function createStreamState(): StreamState {\n return {\n toolCalls: new Map(),\n reasoningContent: '',\n hasContent: false,\n hasReasoning: false,\n currentItemId: null,\n imageGenerations: new Map(),\n };\n}\n\n/**\n * Process a stream event and return provider stream chunks.\n */\nexport function processStreamEvent(\n event: OpenResponsesStreamEvent,\n state: StreamState\n): ProviderStreamChunk[] {\n const chunks: ProviderStreamChunk[] = [];\n\n switch (event.type) {\n // Text content streaming\n case 'response.output_text.delta':\n state.hasContent = true;\n chunks.push({ type: 'content-delta', delta: event.delta });\n break;\n\n case 'response.output_text.done':\n // Content done is emitted at the end\n break;\n\n // Reasoning streaming - OpenRouter uses 'response.reasoning.delta'\n // Note: SDK types may not include reasoning events, so we check dynamically\n case 'response.reasoning_text.delta': // Fallback variant\n state.hasReasoning = true;\n state.reasoningContent += event.delta;\n chunks.push({ type: 'reasoning-delta', delta: event.delta });\n break;\n\n case 'response.reasoning_text.done':\n // Reasoning done is emitted at the end\n break;\n\n // Function call and image generation streaming\n case 'response.output_item.added':\n if (event.item.type === 'function_call') {\n // Handle both camelCase (callId) and snake_case (call_id) from API\n const callId = event.item.callId || (event.item as any).call_id || '';\n const name = event.item.name || '';\n state.toolCalls.set(callId, {\n id: callId,\n name,\n arguments: '',\n });\n chunks.push({\n type: 'tool-call-start',\n id: callId,\n name,\n });\n }\n // Track image generation items\n if ((event.item as any).type === 'image_generation_call') {\n const itemAny = event.item as any;\n const imageId = itemAny.id || itemAny.call_id || itemAny.callId || `img-${state.imageGenerations.size}`;\n state.imageGenerations.set(imageId, {\n id: imageId,\n data: '',\n status: 'in_progress',\n });\n }\n break;\n\n case 'response.output_item.done': {\n // Handle completed image generation items\n const itemAny = event.item as any;\n if (itemAny.type === 'image_generation_call' && itemAny.result) {\n const imageId = itemAny.id || itemAny.call_id || itemAny.callId || '';\n const imageData = itemAny.result as string;\n\n // Determine media type\n let mediaType = 'image/png';\n if (imageData.startsWith('data:')) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) mediaType = match[1];\n }\n\n // Update state\n const existing = state.imageGenerations.get(imageId);\n if (existing) {\n existing.data = imageData;\n existing.status = 'completed';\n }\n\n // Emit image-done chunk\n const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);\n chunks.push({\n type: 'image-done',\n index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,\n image: {\n id: imageId || undefined,\n data: imageData,\n mediaType,\n revisedPrompt: itemAny.revised_prompt || itemAny.revisedPrompt || undefined,\n },\n });\n }\n break;\n }\n\n case 'response.function_call_arguments.delta': {\n // Handle both camelCase (itemId) and snake_case (item_id) from API\n const itemId = event.itemId || (event as any).item_id || '';\n const deltaToolCall = Array.from(state.toolCalls.values()).find(\n (tc) => tc.id === itemId\n );\n if (deltaToolCall) {\n deltaToolCall.arguments += event.delta;\n chunks.push({\n type: 'tool-call-delta',\n id: deltaToolCall.id,\n argumentsDelta: event.delta,\n });\n }\n break;\n }\n\n case 'response.function_call_arguments.done': {\n // Handle both camelCase (itemId) and snake_case (item_id) from API\n const itemId = event.itemId || (event as any).item_id || '';\n const doneToolCall = Array.from(state.toolCalls.values()).find(\n (tc) => tc.id === itemId\n );\n if (doneToolCall) {\n let parsedArgs: Record<string, unknown> = {};\n try {\n parsedArgs = doneToolCall.arguments\n ? JSON.parse(doneToolCall.arguments)\n : {};\n } catch {\n // Keep empty object on parse failure\n }\n chunks.push({\n type: 'tool-call-done',\n id: doneToolCall.id,\n arguments: parsedArgs,\n });\n }\n break;\n }\n\n // Response completion\n case 'response.completed': {\n if (state.hasContent) {\n chunks.push({ type: 'content-done' });\n }\n if (state.hasReasoning) {\n chunks.push({ type: 'reasoning-done' });\n }\n\n // Extract actual provider from model ID (e.g., \"google/gemini-2.5-flash\" -> \"google\")\n const completedProvider = event.response.model?.split('/')[0] || undefined;\n\n // Extract reasoning details from response output items\n // Reasoning can appear as output items with type 'reasoning'\n const reasoningDetails = extractReasoningDetails(event.response.output, state.reasoningContent);\n\n // Extract generated images from response output and emit image-done chunks\n // Skip images that were already emitted via output_item.done events\n const generatedImages = extractImages(event.response.output);\n if (generatedImages && generatedImages.length > 0) {\n for (let i = 0; i < generatedImages.length; i++) {\n const img = generatedImages[i];\n // Check if this image was already emitted via output_item.done\n const alreadyEmitted = img.id && state.imageGenerations.get(img.id)?.status === 'completed';\n if (!alreadyEmitted) {\n chunks.push({\n type: 'image-done',\n index: i,\n image: img,\n });\n }\n }\n }\n\n // Include responseId for post-stream generation metadata fetch\n chunks.push({\n type: 'finish',\n finishReason: mapFinishReason(event.response),\n usage: transformUsage(event.response.usage as OpenRouterExtendedUsage, completedProvider),\n responseId: event.response.id, // Used to fetch generation metadata after stream\n reasoningDetails,\n } as ProviderStreamChunk);\n break;\n }\n\n case 'response.failed':\n chunks.push({\n type: 'error',\n error: event.response.error?.message || 'Response generation failed',\n code: event.response.error?.code,\n });\n break;\n\n case 'response.incomplete': {\n if (state.hasContent) {\n chunks.push({ type: 'content-done' });\n }\n if (state.hasReasoning) {\n chunks.push({ type: 'reasoning-done' });\n }\n\n // Extract actual provider from model ID\n const incompleteProvider = event.response.model?.split('/')[0] || undefined;\n\n // Extract reasoning details from response output items\n const incompleteReasoningDetails = extractReasoningDetails(event.response.output, state.reasoningContent);\n\n // Extract any generated images (may be partial)\n // Skip images that were already emitted via output_item.done events\n const incompleteImages = extractImages(event.response.output);\n if (incompleteImages && incompleteImages.length > 0) {\n for (let i = 0; i < incompleteImages.length; i++) {\n const img = incompleteImages[i];\n // Check if this image was already emitted via output_item.done\n const alreadyEmitted = img.id && state.imageGenerations.get(img.id)?.status === 'completed';\n if (!alreadyEmitted) {\n chunks.push({\n type: 'image-done',\n index: i,\n image: img,\n });\n }\n }\n }\n\n // Include responseId for post-stream generation metadata fetch\n chunks.push({\n type: 'finish',\n finishReason: mapFinishReason(event.response),\n usage: transformUsage(event.response.usage as OpenRouterExtendedUsage, incompleteProvider),\n responseId: event.response.id, // Used to fetch generation metadata after stream\n reasoningDetails: incompleteReasoningDetails,\n } as ProviderStreamChunk);\n break;\n }\n\n // Handle reasoning and image events dynamically (SDK types may not include them)\n default: {\n const eventType = (event as any).type as string;\n const eventAny = event as any;\n\n // OpenRouter reasoning streaming: response.reasoning.delta and response.reasoning.done\n if (eventType === 'response.reasoning.delta') {\n state.hasReasoning = true;\n state.reasoningContent += eventAny.delta || '';\n chunks.push({ type: 'reasoning-delta', delta: eventAny.delta || '' });\n }\n\n // Image generation partial streaming\n // Some providers may stream partial image data\n if (eventType === 'response.image_generation_call.partial_image' ||\n eventType === 'response.image_generation.partial') {\n const imageId = eventAny.item_id || eventAny.itemId || eventAny.id || '';\n const partialData = eventAny.partial_image || eventAny.delta || eventAny.data || '';\n\n // Accumulate partial data in state\n const existing = state.imageGenerations.get(imageId);\n if (existing) {\n existing.data += partialData;\n } else {\n state.imageGenerations.set(imageId, {\n id: imageId,\n data: partialData,\n status: 'generating',\n });\n }\n\n // Emit image-delta chunk\n const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);\n chunks.push({\n type: 'image-delta',\n index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,\n data: partialData,\n });\n }\n\n // Image generation completed (when not through output_item.done)\n if (eventType === 'response.image_generation_call.completed' ||\n eventType === 'response.image_generation.done') {\n const imageId = eventAny.item_id || eventAny.itemId || eventAny.id || '';\n const imageData = eventAny.result || eventAny.image || eventAny.data || '';\n\n if (imageData) {\n // Determine media type\n let mediaType = 'image/png';\n if (imageData.startsWith('data:')) {\n const match = imageData.match(/^data:([^;,]+)/);\n if (match) mediaType = match[1];\n }\n\n // Update state\n const existing = state.imageGenerations.get(imageId);\n if (existing) {\n existing.data = imageData;\n existing.status = 'completed';\n }\n\n // Emit image-done chunk\n const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);\n chunks.push({\n type: 'image-done',\n index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,\n image: {\n id: imageId || undefined,\n data: imageData,\n mediaType,\n revisedPrompt: eventAny.revised_prompt || eventAny.revisedPrompt || undefined,\n },\n });\n }\n }\n\n break;\n }\n }\n\n return chunks;\n}\n\n/**\n * Create error chunk.\n */\nexport function createErrorChunk(error: string, code?: string): ProviderStreamChunk {\n return { type: 'error', error, code };\n}\n\n// ============================================================================\n// Inspection Utilities\n// ============================================================================\n\n/**\n * Check if a string looks like base64 data that should be truncated.\n */\nfunction isBase64Like(str: string): boolean {\n if (str.startsWith('data:')) return true;\n if (str.length > 200) {\n const base64Pattern = /^[A-Za-z0-9+/]+=*$/;\n return base64Pattern.test(str.substring(0, 200));\n }\n return false;\n}\n\n/**\n * Truncate a base64 string for display, preserving the start and showing length.\n */\nfunction truncateBase64String(str: string, maxLength: number = 50): string {\n if (str.length <= maxLength) return str;\n const preview = str.substring(0, maxLength);\n return `${preview}...[truncated, ${str.length.toLocaleString()} chars]`;\n}\n\n/**\n * Recursively truncate base64 data in an object for inspection.\n */\nexport function truncateBase64<T>(obj: T, maxLength: number = 50): T {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (typeof obj === 'string') {\n if (isBase64Like(obj)) {\n return truncateBase64String(obj, maxLength) as T;\n }\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => truncateBase64(item, maxLength)) as T;\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = truncateBase64(value, maxLength);\n }\n return result as T;\n }\n\n return obj;\n}\n\n// ============================================================================\n// Legacy exports for backward compatibility during migration\n// ============================================================================\n\n/**\n * @deprecated Use transformMessages instead\n */\nexport function transformMessagesToChat(messages: ProviderMessage[]) {\n // This is a compatibility shim - the new code uses transformMessages\n const result: Array<{ role: string; content?: string | unknown[] | null; tool_calls?: unknown[]; tool_call_id?: string }> = [];\n\n for (const msg of messages) {\n switch (msg.role) {\n case 'system':\n result.push({ role: 'system', content: msg.content });\n break;\n case 'user':\n result.push({ role: 'user', content: msg.content });\n break;\n case 'assistant':\n result.push({\n role: 'assistant',\n content: msg.content || null,\n tool_calls: msg.toolCalls?.map(tc => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },\n })),\n });\n break;\n case 'tool':\n result.push({\n role: 'tool',\n tool_call_id: msg.toolCallId,\n content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),\n });\n break;\n }\n }\n return result;\n}\n\n/**\n * @deprecated Use transformTools instead\n */\nexport function transformToolsToChat(tools: ProviderTool[]) {\n return tools.map(tool => ({\n type: 'function',\n function: {\n name: tool.function.name,\n description: tool.function.description,\n parameters: tool.function.parameters,\n },\n }));\n}\n\n/**\n * @deprecated Use transformToolChoice instead\n */\nexport function transformToolChoiceToChat(choice: ProviderRequest['toolChoice']) {\n return transformToolChoice(choice);\n}\n\n/**\n * Create content-done chunk.\n */\nexport function createContentDoneChunk(): ProviderStreamChunk {\n return { type: 'content-done' };\n}\n\n/**\n * Create reasoning-done chunk.\n */\nexport function createReasoningDoneChunk(): ProviderStreamChunk {\n return { type: 'reasoning-done' };\n}\n\n/**\n * Create finish chunk with final state.\n */\nexport function createFinishChunk(\n finishReason: ProviderFinishReason,\n usage: ProviderUsage\n): ProviderStreamChunk {\n return {\n type: 'finish',\n finishReason,\n usage,\n };\n}\n\n// ============================================================================\n// Raw SSE Stream Processing (bypasses SDK validation)\n// ============================================================================\n\n/**\n * Parse a raw JSON stream event from OpenRouter.\n * Handles null values that SDK validation rejects.\n */\nexport function parseRawStreamEvent(jsonStr: string): OpenResponsesStreamEvent | null {\n try {\n const event = JSON.parse(jsonStr);\n // The event is already in the correct format, just return it\n // Our processStreamEvent handles the types correctly\n return event as OpenResponsesStreamEvent;\n } catch {\n return null;\n }\n}\n\n/**\n * Create an async iterable from a raw SSE response stream.\n * This bypasses SDK Zod validation which fails on null usage fields.\n */\nexport async function* parseSSEStream(\n response: Response,\n state: StreamState\n): AsyncIterable<ProviderStreamChunk> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('No response body');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events from buffer\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep incomplete line in buffer\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(':')) continue;\n\n // Parse data lines\n if (trimmed.startsWith('data: ')) {\n const data = trimmed.slice(6);\n\n // Handle [DONE] marker\n if (data === '[DONE]') continue;\n\n const event = parseRawStreamEvent(data);\n if (event) {\n const chunks = processStreamEvent(event, state);\n for (const chunk of chunks) {\n yield chunk;\n }\n }\n }\n }\n }\n\n // Process any remaining buffer\n if (buffer.trim()) {\n const trimmed = buffer.trim();\n if (trimmed.startsWith('data: ')) {\n const data = trimmed.slice(6);\n if (data !== '[DONE]') {\n const event = parseRawStreamEvent(data);\n if (event) {\n const chunks = processStreamEvent(event, state);\n for (const chunk of chunks) {\n yield chunk;\n }\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\n/**\n * Process a tool call from the stream (legacy helper).\n */\nexport function processToolCall(\n toolCall: { id: string; name: string; arguments: unknown },\n _state: StreamState\n): ProviderStreamChunk[] {\n const chunks: ProviderStreamChunk[] = [];\n\n chunks.push({\n type: 'tool-call-start',\n id: toolCall.id,\n name: toolCall.name,\n });\n\n const args = typeof toolCall.arguments === 'string'\n ? JSON.parse(toolCall.arguments)\n : toolCall.arguments as Record<string, unknown>;\n\n chunks.push({\n type: 'tool-call-done',\n id: toolCall.id,\n arguments: args,\n });\n\n return chunks;\n}\n\n// ============================================================================\n// Generation Metadata Fetch\n// ============================================================================\n\n/**\n * Generation metadata returned by OpenRouter's /api/v1/generation endpoint.\n * Contains accurate usage, cost, and provider information.\n */\nexport interface GenerationMetadata {\n /** Unique generation ID */\n id: string;\n /** Total cost in USD */\n totalCost: number;\n /** Input/prompt tokens */\n promptTokens: number;\n /** Output/completion tokens */\n completionTokens: number;\n /** Native input tokens (provider-specific) */\n nativePromptTokens?: number;\n /** Native output tokens (provider-specific) */\n nativeCompletionTokens?: number;\n /** Actual provider that served the request */\n providerName: string;\n /** Request latency in milliseconds */\n latency?: number;\n /** Model used */\n model?: string;\n /** Finish reason */\n finishReason?: string;\n /** Created timestamp */\n createdAt?: string;\n}\n\n/**\n * Raw response from OpenRouter's generation endpoint.\n */\ninterface OpenRouterGenerationResponse {\n data?: {\n id?: string;\n total_cost?: number;\n tokens_prompt?: number;\n tokens_completion?: number;\n native_tokens_prompt?: number;\n native_tokens_completion?: number;\n provider_name?: string;\n latency?: number;\n model?: string;\n finish_reason?: string;\n created_at?: string;\n generation_time?: number;\n };\n error?: {\n message?: string;\n code?: number;\n };\n}\n\n/**\n * Fetch generation metadata from OpenRouter after a stream completes.\n *\n * This is the recommended way to get accurate token usage, cost, and provider\n * information. Should be called with the responseId from the finish chunk.\n *\n * @param apiKey - OpenRouter API key\n * @param generationId - The response/generation ID from the stream finish chunk\n * @param baseUrl - Optional base URL (defaults to https://openrouter.ai/api/v1)\n * @param signal - Optional abort signal for cancellation\n * @returns Generation metadata or null if not available\n */\nexport async function fetchGenerationMetadata(\n apiKey: string,\n generationId: string,\n baseUrl: string = 'https://openrouter.ai/api/v1',\n signal?: AbortSignal\n): Promise<GenerationMetadata | null> {\n const url = `${baseUrl}/generation?id=${encodeURIComponent(generationId)}`;\n\n // Retry with backoff - generation data may not be immediately available\n const maxRetries = 3;\n const delays = [500, 1000, 2000]; // ms\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n if (attempt > 0) {\n await new Promise(resolve => setTimeout(resolve, delays[attempt - 1]));\n }\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n },\n signal,\n });\n\n if (response.status === 404 && attempt < maxRetries - 1) {\n // Generation not yet indexed, will retry\n continue;\n }\n\n if (!response.ok) {\n console.error(`Failed to fetch generation metadata: ${response.status}`);\n return null;\n }\n\n const result = await response.json() as OpenRouterGenerationResponse;\n\n if (result.error) {\n console.error('Generation metadata error:', result.error.message);\n return null;\n }\n\n const data = result.data;\n if (!data) {\n return null;\n }\n\n return {\n id: data.id || generationId,\n totalCost: data.total_cost || 0,\n promptTokens: data.tokens_prompt || 0,\n completionTokens: data.tokens_completion || 0,\n nativePromptTokens: data.native_tokens_prompt,\n nativeCompletionTokens: data.native_tokens_completion,\n providerName: data.provider_name || 'unknown',\n latency: data.latency || data.generation_time,\n model: data.model,\n finishReason: data.finish_reason,\n createdAt: data.created_at,\n };\n } catch (error) {\n if (attempt < maxRetries - 1) {\n continue;\n }\n console.error('Error fetching generation metadata:', error);\n return null;\n }\n }\n\n return null; // All retries exhausted\n}\n","/**\n * Lab/provider icons as SVG strings.\n * Embedded directly to avoid external dependencies.\n * These icons are used by OpenRouter to show the AI lab for each model.\n */\n\n// Anthropic icon (Claude)\nexport const ANTHROPIC_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#F0EFEA\"/>\n<path d=\"M32.84 10H26.72L37.88 38H44L32.84 10Z\" fill=\"black\"/>\n<path d=\"M15.16 10L4 38H10.24L12.5224 32.12H24.1976L26.48 38H32.72L21.56 10H15.16ZM14.5408 26.92L18.36 17.08L22.1793 26.92H14.5408Z\" fill=\"black\"/>\n</svg>`;\n\n// Google icon\nexport const GOOGLE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"none\" width=\"48\" height=\"48\"><rect width=\"16\" height=\"16\" fill=\"white\"/><path fill=\"#4285F4\" d=\"M14.9 8.161c0-.476-.039-.954-.121-1.422h-6.64v2.695h3.802a3.24 3.24 0 01-1.407 2.127v1.75h2.269c1.332-1.22 2.097-3.02 2.097-5.15z\"/><path fill=\"#34A853\" d=\"M8.14 15c1.898 0 3.499-.62 4.665-1.69l-2.268-1.749c-.631.427-1.446.669-2.395.669-1.836 0-3.393-1.232-3.952-2.888H1.85v1.803A7.044 7.044 0 008.14 15z\"/><path fill=\"#FBBC04\" d=\"M4.187 9.342a4.17 4.17 0 010-2.68V4.859H1.849a6.97 6.97 0 000 6.286l2.338-1.803z\"/><path fill=\"#EA4335\" d=\"M8.14 3.77a3.837 3.837 0 012.7 1.05l2.01-1.999a6.786 6.786 0 00-4.71-1.82 7.042 7.042 0 00-6.29 3.858L4.186 6.66c.556-1.658 2.116-2.89 3.952-2.89z\"/></svg>`;\n\n// Meta icon (simplified)\nexport const META_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#0668E1\"/>\n<path d=\"M35.5 16c-2.8 0-5.2 2.2-7.5 6.5-2.3-4.3-4.7-6.5-7.5-6.5-4.4 0-8 5.6-8 12s3.6 12 8 12c2.8 0 5.2-2.2 7.5-6.5 2.3 4.3 4.7 6.5 7.5 6.5 4.4 0 8-5.6 8-12s-3.6-12-8-12zm-15 20c-2.8 0-5-4-5-8s2.2-8 5-8c1.4 0 2.8 1 4.2 3-1.8 3.2-2.8 5.8-2.8 8 0 2.2 1 4.8 2.8 8-1.4 2-2.8 3-4.2 3zm15 0c-1.4 0-2.8-1-4.2-3 1.8-3.2 2.8-5.8 2.8-8 0-2.2-1-4.8-2.8-8 1.4-2 2.8-3 4.2-3 2.8 0 5 4 5 8s-2.2 8-5 8z\" fill=\"white\"/>\n</svg>`;\n\n// NVIDIA icon\nexport const NVIDIA_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"white\"/>\n<path d=\"M17.8962 17.7465V14.9519C18.1789 14.9346 18.4615 14.9174 18.7442 14.9174C26.5881 14.6758 31.7291 21.5072 31.7291 21.5072C31.7291 21.5072 26.1818 29.0287 20.2282 29.0287C19.4332 29.0287 18.6559 28.9079 17.9139 28.6664V20.1789C20.9702 20.5412 21.5885 21.8523 23.4082 24.8367L27.4891 21.49C27.4891 21.49 24.5035 17.6775 19.4862 17.6775C18.9562 17.6603 18.4262 17.6948 17.8962 17.7465ZM17.8962 8.5V12.6747L18.7442 12.623C29.6445 12.2607 36.7641 21.352 36.7641 21.352C36.7641 21.352 28.6021 31.047 20.1045 31.047C19.3625 31.047 18.6382 30.978 17.9139 30.8573V33.4449C18.5145 33.5139 19.1329 33.5657 19.7335 33.5657C27.6481 33.5657 33.3721 29.6152 38.9194 24.9574C39.8381 25.682 43.601 27.4243 44.3784 28.1834C39.1137 32.4961 26.8355 35.9636 19.8749 35.9636C19.2035 35.9636 18.5675 35.9291 17.9315 35.86V39.5H48V8.5L17.8962 8.5ZM17.8962 28.6664V30.8745C10.5823 29.5979 8.55061 22.1628 8.55061 22.1628C8.55061 22.1628 12.0662 18.3676 17.8962 17.7465V20.1617H17.8785C14.8222 19.7994 12.4196 22.594 12.4196 22.594C12.4196 22.594 13.7799 27.3036 17.8962 28.6664ZM4.9113 21.8523C4.9113 21.8523 9.2396 15.6074 17.9139 14.9519V12.6747C8.30327 13.4338 0 21.3692 0 21.3692C0 21.3692 4.6993 34.6525 17.8962 35.86V33.4449C8.21494 32.2718 4.9113 21.8523 4.9113 21.8523Z\" fill=\"#76B900\"/>\n</svg>`;\n\n// Alibaba icon\nexport const ALIBABA_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF5A00\"/>\n<path d=\"M31.5 12c3 .2 6.2.7 8.6 2.7 1.9 1.6 2.7 4.2 2.3 6.6-.4 1.8-1.2 3.4-2.1 5-1.8 3.1-4 6-6.1 8.9-.4.5-.7 1.1-.6 1.7 0 .5.4.9.9 1.1.9.4 1.8.3 2.7.2 2.9-.3 5.6-1.2 8.4-2.1.2-.1.3.1.4.2-2.9 1.9-6 3.6-9.3 4.8-1.8.6-3.7 1.2-5.7 1-1-.1-2-.7-2.4-1.7-.4-1.3-.2-2.8.3-4 .9-1.9 2.2-3.6 3.5-5.3 2.1-2.8 4.3-5.5 6-8.6.6-1.1 1.1-2.4.6-3.6-.5-1.1-1.6-1.8-2.7-2.3-1.9-.8-3.9-1.4-5.9-1.9l.9.6c.7.5 1.4 1.1 2.1convergence 1.6-2.6.5-5.3 1-7.9 1.7-3.9 1-7.8 2.3-11.6 3.7.4.7.7 1.5 1.1 2.2-.9 1-1.7 1.9-2.6 2.9 2.1.6 4.3.7 6.4.2 1.8-.4 3.5-1.3 4.9-2.5-.4-.5-.9-.8-1.4-1.2 1.7 0 3.2 1.5 3.2 3.2l-1 .1c-.1-.4-.2-.7-.3-1.1-1.9 1.7-4.4 2.8-6.9 3.1-2.2.3-4.5-.1-6.7-.8.1 1.3.3 2.6.4 3.9-1.2.5-2.3 1.1-3.3 2-.8.7-1.6 1.5-1.9 2.5-.3.9.2 1.9 1 2.4 1 .6 2.1.9 3.2 1 1.5.2 3 .2 4.4.1 2.9-.2 5.8-.8 8.6-1.5.3-.1.4.4.1.4-2.3 1.2-4.6 2.1-7.1 2.8-2.3.6-4.7.9-7.1 1-2.4-.1-5-.5-7-1.9-1.4-1.2-2.2-3-2.2-4.8v-.1c0-1.9.7-3.8 1.6-5.5 1.2-2.4 2.8-4.6 4.6-6.5 3.1-3.4 7-6.1 11.2-8 4.4-2 9-3.4 13.8-3.7z\" fill=\"white\"/>\n</svg>`;\n\n// DeepSeek icon\nexport const DEEPSEEK_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"white\"/>\n<path d=\"M47.4964 9.43757C46.988 9.19072 46.769 9.66115 46.4717 9.90013C46.37 9.97722 46.284 10.0774 46.1979 10.1699C45.4548 10.9562 44.5866 11.4729 43.4523 11.4112C41.794 11.3186 40.3781 11.8353 39.1265 13.092C38.8605 11.5423 37.9767 10.617 36.6312 10.0234C35.9273 9.71497 35.2154 9.40651 34.7226 8.7357C34.3784 8.25783 34.2845 7.7258 34.1124 7.20147C34.0029 6.8854 33.8934 6.56146 33.5257 6.50747C33.1268 6.44587 32.9703 6.77725 32.8138 7.05499C32.1881 8.18844 31.9456 9.43749 31.969 10.7019C32.0237 13.5471 33.2363 15.8137 35.6455 17.4252C35.9193 17.6103 35.9896 17.7952 35.9036 18.0652C35.7393 18.6204 35.5437 19.16 35.3717 19.7151C35.2622 20.0699 35.0978 20.147 34.7146 19.9926C33.3926 19.4453 32.2505 18.6357 31.2414 17.6566C29.5284 16.0143 27.9795 14.2024 26.0475 12.7837C25.5938 12.4521 25.1401 12.1438 24.6707 11.8507C22.6994 9.95412 24.9289 8.39653 25.4451 8.21163C25.9848 8.01885 25.6329 7.35566 23.8884 7.36353C22.144 7.37114 20.5483 7.94946 18.5146 8.72047C18.2173 8.83615 17.9043 8.92086 17.5836 8.99025C15.7376 8.64338 13.8211 8.56612 11.8186 8.78986C8.04819 9.20613 5.03671 10.9718 2.823 13.9865C0.16347 17.6103 -0.462297 21.7274 0.304303 26.0221C1.11002 30.5478 3.44099 34.295 7.02367 37.225C10.7393 40.2626 15.0181 41.7507 19.8992 41.4655C22.8639 41.2959 26.1649 40.9027 29.8884 37.7801C30.827 38.2427 31.8126 38.4276 33.4475 38.5664C34.7069 38.6821 35.9194 38.5048 36.858 38.3121C38.3286 38.0037 38.227 36.6544 37.6951 36.4077C33.385 34.4185 34.3314 35.228 33.471 34.5726C35.6613 32.0052 38.9624 29.3374 40.253 20.6942C40.3546 20.0081 40.2686 19.5763 40.253 19.0211C40.2451 18.682 40.3234 18.551 40.7145 18.5123C41.794 18.3889 42.8422 18.096 43.8043 17.5717C46.5969 16.0606 47.7233 13.5779 47.9892 10.6017C48.0284 10.1468 47.9814 9.67638 47.4964 9.43757ZM23.1611 36.2225C18.984 32.969 16.9581 31.8971 16.1211 31.9434C15.3388 31.9897 15.4797 32.8764 15.6517 33.4547C15.8316 34.0252 16.0663 34.4185 16.3948 34.9196C16.6216 35.2511 16.7782 35.7445 16.168 36.1147C14.8226 36.9397 12.4836 35.8371 12.3741 35.7832C9.65202 34.1948 7.37571 32.0975 5.77205 29.2295C4.22322 26.4691 3.32373 23.5086 3.17504 20.3474C3.13592 19.5841 3.36276 19.3141 4.12936 19.1755C5.13843 18.9904 6.17884 18.9517 7.1879 19.0982C11.4512 19.7151 15.0807 21.6041 18.1235 24.5956C19.86 26.2995 21.1742 28.3352 22.5275 30.3243C23.9668 32.4368 25.5156 34.4493 27.4868 36.0992C28.183 36.6775 28.7384 37.1169 29.2703 37.4408C27.6668 37.6181 24.9915 37.6568 23.1611 36.2225ZM25.1636 23.4623C25.1636 23.123 25.4374 22.8532 25.7817 22.8532C25.8599 22.8532 25.9303 22.8685 25.9929 22.8916C26.079 22.9225 26.1572 22.9688 26.2198 23.038C26.3293 23.1461 26.3919 23.3002 26.3919 23.4622C26.3919 23.8014 26.1181 24.0712 25.774 24.0712C25.4299 24.0712 25.1636 23.8015 25.1636 23.4623ZM31.3824 26.6235C30.9835 26.7854 30.5846 26.9241 30.2013 26.9395C29.6068 26.9703 28.9575 26.7313 28.6056 26.4383C28.058 25.9834 27.6669 25.7291 27.5026 24.9349C27.4322 24.5956 27.4713 24.0713 27.534 23.7707C27.6747 23.123 27.5183 22.7067 27.0568 22.3288C26.6813 22.0204 26.2041 21.9357 25.68 21.9357C25.4844 21.9357 25.3046 21.8508 25.1716 21.7814C24.9525 21.6735 24.7727 21.4036 24.9447 21.0721C24.9995 20.9643 25.2655 20.702 25.3281 20.6558C26.04 20.2548 26.8613 20.3859 27.6201 20.6866C28.3242 20.9719 28.8561 21.4962 29.6226 22.2363C30.4048 23.1306 30.5456 23.3776 30.9915 24.0482C31.3436 24.5725 31.6643 25.1122 31.8833 25.7291C32.0162 26.1146 31.844 26.4307 31.3824 26.6235Z\" fill=\"#4D6BFE\"/>\n</svg>`;\n\n// Qwen icon (same as Alibaba)\nexport const QWEN_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF5A00\"/>\n<path d=\"M31.5 12c3 .2 6.2.7 8.6 2.7 1.9 1.6 2.7 4.2 2.3 6.6-.4 1.8-1.2 3.4-2.1 5-1.8 3.1-4 6-6.1 8.9-.4.5-.7 1.1-.6 1.7 0 .5.4.9.9 1.1.9.4 1.8.3 2.7.2 2.9-.3 5.6-1.2 8.4-2.1.2-.1.3.1.4.2-2.9 1.9-6 3.6-9.3 4.8-1.8.6-3.7 1.2-5.7 1-1-.1-2-.7-2.4-1.7-.4-1.3-.2-2.8.3-4 .9-1.9 2.2-3.6 3.5-5.3 2.1-2.8 4.3-5.5 6-8.6.6-1.1 1.1-2.4.6-3.6-.5-1.1-1.6-1.8-2.7-2.3-1.9-.8-3.9-1.4-5.9-1.9l.9.6c.7.5 1.4 1.1 2.1 1.6-2.6.5-5.3 1-7.9 1.7-3.9 1-7.8 2.3-11.6 3.7.4.7.7 1.5 1.1 2.2-.9 1-1.7 1.9-2.6 2.9 2.1.6 4.3.7 6.4.2 1.8-.4 3.5-1.3 4.9-2.5-.4-.5-.9-.8-1.4-1.2 1.7 0 3.2 1.5 3.2 3.2l-1 .1c-.1-.4-.2-.7-.3-1.1-1.9 1.7-4.4 2.8-6.9 3.1-2.2.3-4.5-.1-6.7-.8.1 1.3.3 2.6.4 3.9-1.2.5-2.3 1.1-3.3 2-.8.7-1.6 1.5-1.9 2.5-.3.9.2 1.9 1 2.4 1 .6 2.1.9 3.2 1 1.5.2 3 .2 4.4.1 2.9-.2 5.8-.8 8.6-1.5.3-.1.4.4.1.4-2.3 1.2-4.6 2.1-7.1 2.8-2.3.6-4.7.9-7.1 1-2.4-.1-5-.5-7-1.9-1.4-1.2-2.2-3-2.2-4.8v-.1c0-1.9.7-3.8 1.6-5.5 1.2-2.4 2.8-4.6 4.6-6.5 3.1-3.4 7-6.1 11.2-8 4.4-2 9-3.4 13.8-3.7z\" fill=\"white\"/>\n</svg>`;\n\n// MiniMax icon\nexport const MINIMAX_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF6B6B\"/>\n<text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" fill=\"white\" font-family=\"Arial, sans-serif\" font-size=\"16\" font-weight=\"bold\">MM</text>\n</svg>`;\n\n// Moonshot AI icon\nexport const MOONSHOTAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" rx=\"24\" fill=\"#16191E\"/>\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M7.57811 31.3729L21.8866 35.2005C21.8673 36.2177 21.8973 37.2353 21.9766 38.2497L30.9105 40.6389C28.2608 41.7325 25.388 42.1773 22.5316 41.9363L22.2616 41.9123L22.1956 41.9063L22.0696 41.8928L21.9286 41.8778C21.85 41.8683 21.7715 41.8583 21.6931 41.8478L21.5326 41.8268L21.3676 41.8028C21.2072 41.7795 21.0472 41.754 20.8876 41.7263L20.8246 41.7143L20.7121 41.6948L20.5516 41.6648L20.4466 41.6423L20.3071 41.6138L20.1946 41.5898L20.0521 41.5598L19.9066 41.5253L19.7656 41.4923L19.6636 41.4668L19.5316 41.4338L19.3966 41.3978L19.2541 41.3603L19.1311 41.3258L18.9676 41.2808L18.8746 41.2508L18.7486 41.2133L18.6091 41.1713L18.4516 41.1204L18.3646 41.0919L18.2446 41.0529L18.1096 41.0064L18.0106 40.9704C17.9885 40.963 17.9665 40.9555 17.9446 40.9479L17.8426 40.9104L17.6911 40.8549L17.6056 40.8219L17.4856 40.7769L17.3551 40.7244L17.2231 40.6719L17.1046 40.6239L16.9621 40.5639L16.8676 40.5219L16.7731 40.4814C16.7525 40.4725 16.732 40.4635 16.7116 40.4544L16.6126 40.4094L16.4581 40.3389L16.3801 40.3029L16.2361 40.2339L16.1431 40.189L16.0171 40.129L15.8881 40.063L15.7486 39.9925L15.6706 39.952L15.5161 39.8695L15.4306 39.8245L15.3436 39.7765C15.3205 39.7636 15.2975 39.7506 15.2746 39.7375L15.1336 39.658L15.0436 39.607L14.9671 39.562L14.8591 39.5005L14.7361 39.4255L14.5966 39.3415L14.5186 39.2935L14.3926 39.2141L14.3011 39.1556L14.1826 39.0806L14.0776 39.0101L13.9981 38.9576C13.971 38.9397 13.944 38.9217 13.9171 38.9036L13.8511 38.8586L13.7851 38.8136C13.765 38.7997 13.745 38.7857 13.7251 38.7716L13.6396 38.7116L13.5256 38.6306L13.4221 38.5556L13.3111 38.4746L13.2271 38.4116L13.1131 38.3261L12.9991 38.2377L12.8701 38.1372L12.8026 38.0847L12.7066 38.0067L12.5956 37.9167L12.4621 37.8072L12.3931 37.7487L12.3241 37.6902C12.3025 37.6718 12.281 37.6533 12.2596 37.6347L12.1921 37.5747L12.1006 37.4952L11.9956 37.4022L11.8936 37.3122L11.8006 37.2253L11.7001 37.1323L11.6206 37.0573L11.4886 36.9313C11.4388 36.8831 11.3893 36.8346 11.3401 36.7858L11.2966 36.7438L11.2351 36.6808L11.1316 36.5758L11.0566 36.4993L10.9816 36.4198C10.8959 36.332 10.8119 36.2424 10.7296 36.1514L10.6096 36.0194L10.5166 35.9144L10.4101 35.7944L10.3471 35.7209L10.2676 35.6279L10.1806 35.5259L10.1116 35.4419C10.098 35.4255 10.0845 35.409 10.0711 35.3924L10.0036 35.31L9.9046 35.187L9.8431 35.109L9.7681 35.013L9.7381 34.9755C8.87983 33.8631 8.15489 32.654 7.57811 31.3729ZM6.04811 22.6828L23.0776 27.2378C22.7925 28.2307 22.5577 29.2374 22.3741 30.254L38.5995 34.5945C37.7934 35.6981 36.865 36.7069 35.832 37.6017L6.98561 29.8835L6.96161 29.8145L6.90911 29.6585C6.88359 29.5822 6.85859 29.5057 6.83411 29.4291L6.82361 29.3946C6.70815 29.0276 6.6046 28.6569 6.51311 28.2832L6.46811 28.0942L6.44111 27.9742L6.40961 27.8287L6.38261 27.7072L6.35561 27.5723L6.33011 27.4463L6.30311 27.3053C6.26411 27.0938 6.22811 26.8808 6.19661 26.6664L6.17111 26.4894L6.15461 26.3649L6.13511 26.2119C6.12507 26.1315 6.11557 26.051 6.10661 25.9704L6.09911 25.8999C5.98704 24.8312 5.96997 23.7546 6.04811 22.6828ZM8.4376 14.9586L26.3595 19.7521C25.8076 20.6595 25.3021 21.5984 24.8446 22.5643L41.787 27.0968C41.574 28.3267 41.235 29.5146 40.785 30.6439L23.4601 26.0094L6.18611 21.3899L6.20861 21.2399L6.22061 21.1664L6.23561 21.0659L6.25811 20.9354L6.28511 20.7885C6.32411 20.5665 6.36911 20.346 6.41711 20.1255L6.45911 19.9396L6.48911 19.8121L6.52511 19.6666C6.55811 19.5316 6.59261 19.3966 6.63011 19.2646L6.67211 19.1116L6.70661 18.9872L6.75161 18.8372L6.78911 18.7142L6.83411 18.5702L6.87311 18.4472L6.91961 18.3047C7.30804 17.1397 7.81573 16.0178 8.4346 14.9571L8.4376 14.9586ZM15.1006 8.35926L32.028 12.8858C31.1348 13.6965 30.2893 14.5583 29.496 15.467L41.2305 18.6062C41.631 19.8841 41.8935 21.2219 42 22.6033L9.1591 13.8202L9.2266 13.7227L9.2671 13.6627L9.3271 13.5802L9.3961 13.4827L9.4786 13.3687L9.5596 13.2607L9.6556 13.1318L9.7306 13.0343L9.8161 12.9248L9.8986 12.8198L9.9886 12.7088L10.0711 12.6053L10.1686 12.4898L10.2496 12.3908L10.3486 12.2753L10.4281 12.1854L10.5361 12.0624L10.6156 11.9724L10.7161 11.8614L10.7971 11.7744L10.9066 11.6574L10.9936 11.5674L11.0881 11.4669L11.3401 11.212L11.4901 11.065L11.5786 10.981L11.6926 10.8745C12.7264 9.90579 13.8702 9.06161 15.1006 8.35926ZM24.0256 6.0015H24.1711L24.2941 6.003L24.3976 6.0045L24.4786 6.0075L24.5806 6.0105L24.6496 6.012L24.7636 6.0165L24.8341 6.0195L24.9241 6.024L25.0051 6.027L25.1356 6.0345L25.2931 6.045L25.5091 6.06149L25.6411 6.07199L25.707 6.07799L25.8226 6.08999L25.9455 6.10199L26.016 6.10949L26.169 6.12749L26.244 6.13649L26.406 6.15748L26.5275 6.17248L26.5905 6.18148L26.688 6.19648L26.9985 6.24447L27.1035 6.26247L27.201 6.27897L27.411 6.31797L27.549 6.34496L27.714 6.37796L27.783 6.39296L27.8955 6.41696L27.957 6.43196L28.05 6.45145L28.113 6.46645L28.2105 6.48895L28.284 6.50695L28.3905 6.53244L28.5345 6.56844L28.7025 6.61344L28.872 6.65843L29.0415 6.70643L29.1165 6.72892L29.2215 6.75892L29.3385 6.79492L29.448 6.82941L29.523 6.85341L29.598 6.87741L29.712 6.91491L29.8605 6.9644L30.0135 7.01839L30.0855 7.04389L30.1815 7.07839L30.321 7.12938L30.486 7.19088L30.66 7.25837L30.81 7.31836L30.8805 7.34836L30.9705 7.38436L31.032 7.41135L31.1265 7.45035L31.1865 7.47735L31.272 7.51484L31.437 7.58684L31.587 7.65583L31.698 7.70832L31.8105 7.76232L31.9005 7.80431L32.0385 7.87331L32.175 7.9408L32.328 8.01879L32.4075 8.06079L32.481 8.09978L32.55 8.13578L32.64 8.18527L32.7015 8.21827L32.7795 8.26177L32.9115 8.33676L33.0705 8.42675L33.201 8.50324L33.2865 8.55423L33.366 8.60223L33.51 8.69072L33.642 8.77321L33.789 8.8662L33.843 8.9022L33.939 8.96369L34.065 9.04768L34.125 9.08818L34.218 9.15117L34.311 9.21567L34.3455 9.24116C34.4265 9.29666 34.5075 9.35365 34.587 9.41215L34.7115 9.50214L34.809 9.57413L34.893 9.63862L35.022 9.73611L35.145 9.8321L35.205 9.8771L35.28 9.93859L35.409 10.0421L35.5275 10.1396L35.655 10.2461C36.723 11.146 37.6845 12.1704 38.5185 13.2922L16.8331 7.49235L16.9261 7.45185L17.0236 7.40985L17.1451 7.35886L17.2741 7.30636C17.4436 7.23887 17.6146 7.17138 17.7856 7.10988L17.9296 7.05739L18.0691 7.0079L18.1951 6.9629L18.3391 6.9164C18.4696 6.87141 18.6031 6.82941 18.7351 6.78892L18.8716 6.74842L19.0006 6.71093L19.1536 6.66593L19.2811 6.63143L19.4311 6.59244L19.5601 6.55644L19.6951 6.52195L19.8316 6.48895L19.9741 6.45595L20.1091 6.42596L20.2561 6.39446L20.3926 6.36446L20.5351 6.33746L20.6731 6.31047L20.8231 6.28347L20.9596 6.25947L21.1066 6.23398L21.2446 6.21298L21.3901 6.19048L21.5281 6.17098L21.6811 6.15148L21.8176 6.13349L21.9751 6.11549L22.1101 6.10049L22.2676 6.08549C22.4071 6.07049 22.5466 6.05849 22.6876 6.04949L22.8466 6.0375L22.9816 6.03L23.1466 6.021L23.2861 6.015L23.4361 6.009L23.5816 6.006L23.7301 6.003L24.0256 6V6.0015Z\" fill=\"white\"/>\n</svg>`;\n\n// X.AI (Grok) icon\nexport const XAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"black\"/>\n<path d=\"M10.008 19.311L24.0672 40H30.3165L16.2557 19.311H10.008ZM16.2509 30.8017L10 40H16.2541L19.3771 35.4017L16.2509 30.8017ZM31.7459 8L20.941 23.8993L24.0672 28.5009L38 8H31.7459ZM32.8777 17.8382V40H38V10.3008L32.8777 17.8382Z\" fill=\"white\"/>\n</svg>`;\n\n// Z.AI (Zhipu AI) icon - simplified\nexport const ZAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" rx=\"4\" fill=\"#2D2D2D\"/>\n<path d=\"M35 10H13v4h14L13 34v4h22v-4H21l14-20V10z\" fill=\"white\"/>\n</svg>`;\n\n// EXAONE (LG AI) icon\nexport const EXAONE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#A8E6A3\"/>\n<text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" fill=\"#2C3E50\" font-family=\"Arial, sans-serif\" font-size=\"14\" font-weight=\"bold\">EXA</text>\n</svg>`;\n\n// OpenRouter icon\nexport const OPENROUTER_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\">\n<rect width=\"24\" height=\"24\" fill=\"#6366F1\"/>\n<path fill=\"white\" d=\"M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z\" transform=\"scale(0.75) translate(4 4)\"/>\n</svg>`;\n\n// OpenAI icon (for models going through OpenRouter)\nexport const OPENAI_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" rx=\"24\" fill=\"white\"/>\n<path d=\"M19.3418 18.5599V14.7599C19.3418 14.4399 19.4608 14.1998 19.7382 14.04L27.3102 9.63997C28.3409 9.03999 29.5699 8.76014 30.8383 8.76014C35.5954 8.76014 38.6085 12.4802 38.6085 16.4401C38.6085 16.72 38.6085 17.04 38.5687 17.3601L30.7194 12.72C30.2437 12.4401 29.7678 12.4401 29.2922 12.72L19.3418 18.5599ZM37.0226 33.36V24.2799C37.0226 23.7197 36.7846 23.3197 36.309 23.0398L26.3586 17.1998L29.6093 15.3197C29.8868 15.1599 30.1247 15.1599 30.4022 15.3197L37.9741 19.7197C40.1547 20.9999 41.6213 23.7197 41.6213 26.3596C41.6213 29.3995 39.8375 32.1999 37.0226 33.36ZM17.0029 25.3601L13.7522 23.4402C13.4748 23.2804 13.3557 23.0402 13.3557 22.7202V13.9203C13.3557 9.64039 16.6065 6.40016 21.0069 6.40016C22.6722 6.40016 24.2179 6.96029 25.5265 7.96025L17.7168 12.5204C17.2412 12.8003 17.0033 13.2002 17.0033 13.7605L17.0029 25.3601ZM24 29.44L19.3418 26.8001V21.2003L24 18.5604L28.6578 21.2003V26.8001L24 29.44ZM26.993 41.6002C25.3278 41.6002 23.7821 41.04 22.4735 40.0402L30.2831 35.4799C30.7588 35.2001 30.9967 34.8001 30.9967 34.2399V22.6399L34.2873 24.5598C34.5646 24.7196 34.6837 24.9597 34.6837 25.2798V34.0797C34.6837 38.3596 31.3931 41.6002 26.993 41.6002ZM17.5975 32.6802L10.0255 28.2803C7.84493 27.0001 6.37833 24.2803 6.37833 21.6404C6.37833 18.5604 8.20193 15.8004 11.0164 14.6403V23.7602C11.0164 24.3204 11.2544 24.7204 11.73 25.0003L21.641 30.8001L18.3902 32.6802C18.1129 32.84 17.8749 32.84 17.5975 32.6802ZM17.1617 39.2402C12.682 39.2402 9.39151 35.8402 9.39151 31.6401C9.39151 31.3201 9.43125 31.0001 9.47066 30.68L17.2803 35.2402C17.7559 35.5201 18.2319 35.5201 18.7074 35.2402L28.6578 29.4404V33.2404C28.6578 33.5605 28.5388 33.8005 28.2614 33.9604L20.6894 38.3604C19.6586 38.9603 18.4301 39.2402 17.1617 39.2402ZM26.993 44C31.7899 44 35.7936 40.5601 36.7057 36C41.1457 34.8399 44 30.6399 44 26.36C44 23.5598 42.8108 20.8401 40.6701 18.88C40.8683 18.0399 40.9872 17.1998 40.9872 16.3602C40.9872 10.6403 36.3885 6.35998 31.0763 6.35998C30.0062 6.35998 28.9754 6.51979 27.9446 6.88001C26.1604 5.11992 23.7025 4 21.0069 4C16.2101 4 12.2064 7.4398 11.2943 12C6.8543 13.1601 4 17.3601 4 21.6399C4 24.4401 5.18916 27.1599 7.32995 29.1199C7.13174 29.96 7.01277 30.8001 7.01277 31.6398C7.01277 37.3597 11.6114 41.6399 16.9236 41.6399C17.9938 41.6399 19.0246 41.4801 20.0554 41.1199C21.8392 42.88 24.2971 44 26.993 44Z\" fill=\"black\"/>\n</svg>`;\n\n// Mistral AI icon\nexport const MISTRAL_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#FF7000\"/>\n<path d=\"M10 10h8v8h-8zM18 10h4v8h-4zM26 10h4v8h-4zM30 10h8v8h-8zM10 18h8v4h-8zM30 18h8v4h-8zM10 22h8v4h-8zM18 22h12v4H18zM30 22h8v4h-8zM10 26h8v4h-8zM30 26h8v4h-8zM10 30h8v8h-8zM18 30h4v8h-4zM26 30h4v8h-4zM30 30h8v8h-8z\" fill=\"white\"/>\n</svg>`;\n\n// Cohere icon\nexport const COHERE_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#39594D\"/>\n<circle cx=\"24\" cy=\"24\" r=\"12\" fill=\"#D18EE2\"/>\n<circle cx=\"24\" cy=\"24\" r=\"6\" fill=\"#39594D\"/>\n</svg>`;\n\n// Perplexity icon\nexport const PERPLEXITY_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\">\n<rect width=\"48\" height=\"48\" fill=\"#20808D\"/>\n<path d=\"M24 8L36 20v16L24 40 12 28V16L24 8z\" stroke=\"white\" stroke-width=\"2\" fill=\"none\"/>\n<path d=\"M24 8v32M12 20h24M12 28h24\" stroke=\"white\" stroke-width=\"2\"/>\n</svg>`;\n\n/**\n * Map of lab/provider IDs to their SVG icons.\n * Keys are normalized to lowercase.\n */\nexport const LAB_ICONS: Record<string, string> = {\n anthropic: ANTHROPIC_ICON,\n google: GOOGLE_ICON,\n 'google-ai': GOOGLE_ICON,\n meta: META_ICON,\n 'meta-llama': META_ICON,\n nvidia: NVIDIA_ICON,\n alibaba: ALIBABA_ICON,\n deepseek: DEEPSEEK_ICON,\n qwen: QWEN_ICON,\n minimax: MINIMAX_ICON,\n moonshotai: MOONSHOTAI_ICON,\n moonshot: MOONSHOTAI_ICON,\n 'x-ai': XAI_ICON,\n xai: XAI_ICON,\n 'z-ai': ZAI_ICON,\n zai: ZAI_ICON,\n zhipu: ZAI_ICON,\n exaone: EXAONE_ICON,\n lgai: EXAONE_ICON,\n openrouter: OPENROUTER_ICON,\n openai: OPENAI_ICON,\n 'openai-sdk': OPENAI_ICON,\n mistral: MISTRAL_ICON,\n mistralai: MISTRAL_ICON,\n cohere: COHERE_ICON,\n perplexity: PERPLEXITY_ICON,\n};\n\n/**\n * Convert SVG string to data URI for use in img src.\n */\nexport function svgToDataUri(svg: string): string {\n const encoded = encodeURIComponent(svg)\n .replace(/'/g, '%27')\n .replace(/\"/g, '%22');\n return `data:image/svg+xml,${encoded}`;\n}\n\n/**\n * Get the icon for a lab/provider as a data URI.\n * @param labId - The lab/provider ID (e.g., 'anthropic', 'google')\n * @returns Data URI string or undefined if not found\n */\nexport function getLabIconDataUri(labId: string): string | undefined {\n const icon = LAB_ICONS[labId.toLowerCase()];\n if (icon) {\n return svgToDataUri(icon);\n }\n return undefined;\n}\n\n/**\n * Extract the lab from a model ID.\n * OpenRouter model IDs are typically in format 'lab/model-name'.\n * @param modelId - The model ID (e.g., 'anthropic/claude-3-opus')\n * @returns The lab name or undefined\n */\nexport function extractLabFromModelId(modelId: string): string | undefined {\n if (!modelId || !modelId.includes('/')) {\n return undefined;\n }\n return modelId.split('/')[0].toLowerCase();\n}\n\n/**\n * Get the icon for a model as a data URI.\n * Extracts the lab from the model ID and returns the corresponding icon.\n * @param modelId - The model ID (e.g., 'anthropic/claude-3-opus')\n * @returns Data URI string or undefined if not found\n */\nexport function getModelIconDataUri(modelId: string): string | undefined {\n const lab = extractLabFromModelId(modelId);\n if (lab) {\n return getLabIconDataUri(lab);\n }\n return undefined;\n}\n","/**\n * OpenRouter provider options schema.\n *\n * Defines typed providerOptions for OpenRouter models, providing\n * TypeScript autocompletion and runtime validation.\n *\n * @see https://openrouter.ai/docs/guides/routing/provider-selection\n * @module\n */\n\nimport { z } from 'zod';\n\n/**\n * Percentile-based performance threshold.\n * Can be a simple number (applies to p50) or object with specific percentiles.\n */\nconst percentileSchema = z.union([\n z.number(),\n z.object({\n p50: z.number().optional(),\n p75: z.number().optional(),\n p90: z.number().optional(),\n p99: z.number().optional(),\n }),\n]);\n\n/**\n * OpenRouter provider routing options.\n *\n * These options control how OpenRouter routes requests to underlying providers.\n * They are nested under the `provider` key in providerOptions.\n *\n * @see https://openrouter.ai/docs/guides/routing/provider-selection\n */\nconst providerRoutingSchema = z.object({\n /** Provider slugs to try in order (e.g., ['anthropic', 'openai']) */\n order: z.array(z.string()).optional(),\n\n /** Allow fallback to other providers if preferred unavailable (default: true) */\n allow_fallbacks: z.boolean().optional(),\n\n /** Only use providers that support all parameters in the request */\n require_parameters: z.boolean().optional(),\n\n /** Control data storage policies: 'allow' or 'deny' */\n data_collection: z.enum(['allow', 'deny']).optional(),\n\n /** Restrict to Zero Data Retention endpoints only */\n zdr: z.boolean().optional(),\n\n /** Restrict to these providers only (exclusive list) */\n only: z.array(z.string()).optional(),\n\n /** Skip these providers */\n ignore: z.array(z.string()).optional(),\n\n /** Sort providers by price, throughput, or latency */\n sort: z.union([\n z.enum(['price', 'throughput', 'latency']),\n z.object({\n by: z.enum(['price', 'throughput', 'latency']),\n partition: z.enum(['model', 'none']).optional(),\n }),\n ]).optional(),\n\n /** Maximum price constraints (hard limit - request fails if no provider meets threshold) */\n max_price: z.object({\n /** Max price per million prompt tokens */\n prompt: z.number().optional(),\n /** Max price per million completion tokens */\n completion: z.number().optional(),\n /** Max price per request */\n request: z.number().optional(),\n /** Max price per image */\n image: z.number().optional(),\n }).optional(),\n\n /** Allowed quantization levels */\n quantizations: z.array(z.enum([\n 'int4', 'int8', 'fp4', 'fp6', 'fp8', 'fp16', 'bf16', 'fp32', 'unknown'\n ])).optional(),\n\n /** Minimum throughput in tokens/sec (soft preference) */\n preferred_min_throughput: percentileSchema.optional(),\n\n /** Maximum latency in seconds (soft preference) */\n preferred_max_latency: percentileSchema.optional(),\n\n /** Restrict to models allowing text distillation */\n enforce_distillable_text: z.boolean().optional(),\n}).passthrough(); // Allow unknown keys for forward compatibility\n\n/**\n * OpenRouter provider options schema.\n *\n * Routing options are nested under the `provider` key to match\n * OpenRouter's API structure.\n *\n * @example\n * ```typescript\n * providerOptions: {\n * provider: {\n * zdr: true,\n * max_price: { prompt: 1, completion: 5 },\n * order: ['anthropic', 'openai'],\n * },\n * }\n * ```\n */\nexport const openrouterProviderOptions = z.object({\n /** Provider routing configuration */\n provider: providerRoutingSchema.optional(),\n}).passthrough(); // Allow other top-level options for forward compatibility\n\n/**\n * TypeScript type for OpenRouter provider options.\n * Inferred from the Zod schema for type-safe usage.\n */\nexport type OpenRouterProviderOptions = z.infer<typeof openrouterProviderOptions>;\n","import type { ProviderFactoryWithOptions, ProviderFactoryConfig } from '@standardagents/spec';\nimport { OpenRouterProvider, type OpenRouterConfig } from './OpenRouterProvider';\nimport { openrouterProviderOptions } from './providerOptions';\n\n/**\n * OpenRouter provider factory for Standard Agents\n *\n * OpenRouter is a multi-provider gateway that routes requests to various LLM providers.\n * Includes typed providerOptions for routing configuration.\n *\n * @example\n * ```typescript\n * import { defineModel } from '@standardagents/builder';\n * import { openrouter } from '@standardagents/openrouter';\n *\n * export default defineModel({\n * name: 'claude-3-opus',\n * provider: openrouter,\n * model: 'anthropic/claude-3-opus',\n * inputPrice: 15,\n * outputPrice: 75,\n * capabilities: {\n * supportsImages: true,\n * supportsToolCalls: true,\n * maxContextTokens: 200000,\n * },\n * providerOptions: {\n * provider: {\n * zdr: true,\n * only: ['anthropic'],\n * },\n * },\n * });\n * ```\n */\nexport const openrouter: ProviderFactoryWithOptions<typeof openrouterProviderOptions> =\n Object.assign(\n (config: ProviderFactoryConfig) => new OpenRouterProvider(config as OpenRouterConfig),\n { providerOptions: openrouterProviderOptions }\n );\n\n// Re-export the provider options schema and types\nexport { openrouterProviderOptions, type OpenRouterProviderOptions } from './providerOptions';\n\n// Re-export the provider class for advanced usage\nexport { OpenRouterProvider, type OpenRouterConfig } from './OpenRouterProvider';\n\n// Re-export generation metadata utilities for post-stream usage fetching\nexport { fetchGenerationMetadata, type GenerationMetadata } from './transformers';\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standardagents/openrouter",
3
- "version": "0.10.0-dev.ffffff",
3
+ "version": "0.11.0-next.24f9ff1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "restricted",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.9.0",
33
- "@standardagents/spec": "0.10.0"
33
+ "@standardagents/spec": "0.11.0-next.24f9ff1"
34
34
  },
35
35
  "keywords": [
36
36
  "standardagents",