@providerprotocol/ai 0.0.2 → 0.0.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/providers/openrouter/transform.completions.ts","../../src/providers/openrouter/llm.completions.ts","../../src/providers/openrouter/transform.responses.ts","../../src/providers/openrouter/llm.responses.ts","../../src/providers/openrouter/index.ts"],"sourcesContent":["import type { LLMRequest, LLMResponse } from '../../types/llm.ts';\nimport type { Message } from '../../types/messages.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { Tool, ToolCall } from '../../types/tool.ts';\nimport type { TokenUsage } from '../../types/turn.ts';\nimport type { ContentBlock, TextBlock, ImageBlock } from '../../types/content.ts';\nimport {\n AssistantMessage,\n isUserMessage,\n isAssistantMessage,\n isToolResultMessage,\n} from '../../types/messages.ts';\nimport type {\n OpenRouterLLMParams,\n OpenRouterCompletionsRequest,\n OpenRouterCompletionsMessage,\n OpenRouterUserContent,\n OpenRouterCompletionsTool,\n OpenRouterCompletionsResponse,\n OpenRouterCompletionsStreamChunk,\n OpenRouterToolCall,\n} from './types.ts';\n\n/**\n * Transform UPP request to OpenRouter Chat Completions format\n */\nexport function transformRequest<TParams extends OpenRouterLLMParams>(\n request: LLMRequest<TParams>,\n modelId: string\n): OpenRouterCompletionsRequest {\n const params: OpenRouterLLMParams = request.params ?? {};\n\n const openrouterRequest: OpenRouterCompletionsRequest = {\n model: modelId,\n messages: transformMessages(request.messages, request.system),\n };\n\n // Model parameters\n if (params.temperature !== undefined) {\n openrouterRequest.temperature = params.temperature;\n }\n if (params.top_p !== undefined) {\n openrouterRequest.top_p = params.top_p;\n }\n if (params.top_k !== undefined) {\n openrouterRequest.top_k = params.top_k;\n }\n if (params.min_p !== undefined) {\n openrouterRequest.min_p = params.min_p;\n }\n if (params.top_a !== undefined) {\n openrouterRequest.top_a = params.top_a;\n }\n if (params.max_tokens !== undefined) {\n openrouterRequest.max_tokens = params.max_tokens;\n }\n if (params.frequency_penalty !== undefined) {\n openrouterRequest.frequency_penalty = params.frequency_penalty;\n }\n if (params.presence_penalty !== undefined) {\n openrouterRequest.presence_penalty = params.presence_penalty;\n }\n if (params.repetition_penalty !== undefined) {\n openrouterRequest.repetition_penalty = params.repetition_penalty;\n }\n if (params.stop !== undefined) {\n openrouterRequest.stop = params.stop;\n }\n if (params.logprobs !== undefined) {\n openrouterRequest.logprobs = params.logprobs;\n }\n if (params.top_logprobs !== undefined) {\n openrouterRequest.top_logprobs = params.top_logprobs;\n }\n if (params.seed !== undefined) {\n openrouterRequest.seed = params.seed;\n }\n if (params.user !== undefined) {\n openrouterRequest.user = params.user;\n }\n if (params.logit_bias !== undefined) {\n openrouterRequest.logit_bias = params.logit_bias;\n }\n if (params.prediction !== undefined) {\n openrouterRequest.prediction = params.prediction;\n }\n\n // OpenRouter-specific parameters\n if (params.transforms !== undefined) {\n openrouterRequest.transforms = params.transforms;\n }\n if (params.models !== undefined) {\n openrouterRequest.models = params.models;\n }\n if (params.route !== undefined) {\n openrouterRequest.route = params.route;\n }\n if (params.provider !== undefined) {\n openrouterRequest.provider = params.provider;\n }\n if (params.debug !== undefined) {\n openrouterRequest.debug = params.debug;\n }\n\n // Tools\n if (request.tools && request.tools.length > 0) {\n openrouterRequest.tools = request.tools.map(transformTool);\n if (params.parallel_tool_calls !== undefined) {\n openrouterRequest.parallel_tool_calls = params.parallel_tool_calls;\n }\n }\n\n // Structured output via response_format\n if (request.structure) {\n const schema: Record<string, unknown> = {\n type: 'object',\n properties: request.structure.properties,\n required: request.structure.required,\n ...(request.structure.additionalProperties !== undefined\n ? { additionalProperties: request.structure.additionalProperties }\n : { additionalProperties: false }),\n };\n if (request.structure.description) {\n schema.description = request.structure.description;\n }\n\n openrouterRequest.response_format = {\n type: 'json_schema',\n json_schema: {\n name: 'json_response',\n description: request.structure.description,\n schema,\n strict: true,\n },\n };\n } else if (params.response_format !== undefined) {\n // Pass through response_format from params if no structure is defined\n openrouterRequest.response_format = params.response_format;\n }\n\n return openrouterRequest;\n}\n\n/**\n * Transform messages including system prompt\n */\nfunction transformMessages(\n messages: Message[],\n system?: string\n): OpenRouterCompletionsMessage[] {\n const result: OpenRouterCompletionsMessage[] = [];\n\n // Add system message first if present\n if (system) {\n result.push({\n role: 'system',\n content: system,\n });\n }\n\n // Transform each message\n for (const message of messages) {\n // Handle tool result messages specially - they need to produce multiple messages\n if (isToolResultMessage(message)) {\n const toolMessages = transformToolResults(message);\n result.push(...toolMessages);\n } else {\n const transformed = transformMessage(message);\n if (transformed) {\n result.push(transformed);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Filter to only valid content blocks with a type property\n */\nfunction filterValidContent<T extends { type?: string }>(content: T[]): T[] {\n return content.filter((c) => c && typeof c.type === 'string');\n}\n\n/**\n * Transform a UPP Message to OpenRouter format\n */\nfunction transformMessage(message: Message): OpenRouterCompletionsMessage | null {\n if (isUserMessage(message)) {\n const validContent = filterValidContent(message.content);\n // Check if we can use simple string content\n if (validContent.length === 1 && validContent[0]?.type === 'text') {\n return {\n role: 'user',\n content: (validContent[0] as TextBlock).text,\n };\n }\n return {\n role: 'user',\n content: validContent.map(transformContentBlock),\n };\n }\n\n if (isAssistantMessage(message)) {\n const validContent = filterValidContent(message.content);\n // Extract text content\n const textContent = validContent\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n const assistantMessage: OpenRouterCompletionsMessage = {\n role: 'assistant',\n content: textContent || null,\n };\n\n // Add tool calls if present\n if (message.toolCalls && message.toolCalls.length > 0) {\n (assistantMessage as { tool_calls?: OpenRouterToolCall[] }).tool_calls =\n message.toolCalls.map((call) => ({\n id: call.toolCallId,\n type: 'function' as const,\n function: {\n name: call.toolName,\n arguments: JSON.stringify(call.arguments),\n },\n }));\n }\n\n return assistantMessage;\n }\n\n if (isToolResultMessage(message)) {\n // Tool results are sent as individual tool messages\n // Return the first one and handle multiple in a different way\n // Actually, we need to return multiple messages for multiple tool results\n // This is handled by the caller - transform each result to a message\n const results = message.results.map((result) => ({\n role: 'tool' as const,\n tool_call_id: result.toolCallId,\n content:\n typeof result.result === 'string'\n ? result.result\n : JSON.stringify(result.result),\n }));\n\n // For now, return the first result - caller should handle multiple\n return results[0] ?? null;\n }\n\n return null;\n}\n\n/**\n * Transform multiple tool results to messages\n */\nexport function transformToolResults(\n message: Message\n): OpenRouterCompletionsMessage[] {\n if (!isToolResultMessage(message)) {\n const single = transformMessage(message);\n return single ? [single] : [];\n }\n\n return message.results.map((result) => ({\n role: 'tool' as const,\n tool_call_id: result.toolCallId,\n content:\n typeof result.result === 'string'\n ? result.result\n : JSON.stringify(result.result),\n }));\n}\n\n/**\n * Transform a content block to OpenRouter format\n */\nfunction transformContentBlock(block: ContentBlock): OpenRouterUserContent {\n switch (block.type) {\n case 'text':\n return { type: 'text', text: block.text };\n\n case 'image': {\n const imageBlock = block as ImageBlock;\n let url: string;\n\n if (imageBlock.source.type === 'base64') {\n url = `data:${imageBlock.mimeType};base64,${imageBlock.source.data}`;\n } else if (imageBlock.source.type === 'url') {\n url = imageBlock.source.url;\n } else if (imageBlock.source.type === 'bytes') {\n // Convert bytes to base64\n const base64 = btoa(\n Array.from(imageBlock.source.data)\n .map((b) => String.fromCharCode(b))\n .join('')\n );\n url = `data:${imageBlock.mimeType};base64,${base64}`;\n } else {\n throw new Error('Unknown image source type');\n }\n\n return {\n type: 'image_url',\n image_url: { url },\n };\n }\n\n default:\n throw new Error(`Unsupported content type: ${block.type}`);\n }\n}\n\n/**\n * Transform a UPP Tool to OpenRouter format\n */\nfunction transformTool(tool: Tool): OpenRouterCompletionsTool {\n return {\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: {\n type: 'object',\n properties: tool.parameters.properties,\n required: tool.parameters.required,\n ...(tool.parameters.additionalProperties !== undefined\n ? { additionalProperties: tool.parameters.additionalProperties }\n : {}),\n },\n },\n };\n}\n\n/**\n * Transform OpenRouter response to UPP LLMResponse\n */\nexport function transformResponse(data: OpenRouterCompletionsResponse): LLMResponse {\n const choice = data.choices[0];\n if (!choice) {\n throw new Error('No choices in OpenRouter response');\n }\n\n // Extract text content\n const textContent: TextBlock[] = [];\n let structuredData: unknown;\n if (choice.message.content) {\n textContent.push({ type: 'text', text: choice.message.content });\n // Try to parse as JSON for structured output (native JSON mode)\n try {\n structuredData = JSON.parse(choice.message.content);\n } catch {\n // Not valid JSON - that's fine, might not be structured output\n }\n }\n\n // Extract tool calls\n const toolCalls: ToolCall[] = [];\n if (choice.message.tool_calls) {\n for (const call of choice.message.tool_calls) {\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(call.function.arguments);\n } catch {\n // Invalid JSON - use empty object\n }\n toolCalls.push({\n toolCallId: call.id,\n toolName: call.function.name,\n arguments: args,\n });\n }\n }\n\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: data.id,\n metadata: {\n openrouter: {\n model: data.model,\n finish_reason: choice.finish_reason,\n system_fingerprint: data.system_fingerprint,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: data.usage.prompt_tokens,\n outputTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n };\n\n // Map finish reason to stop reason\n let stopReason = 'end_turn';\n switch (choice.finish_reason) {\n case 'stop':\n stopReason = 'end_turn';\n break;\n case 'length':\n stopReason = 'max_tokens';\n break;\n case 'tool_calls':\n stopReason = 'tool_use';\n break;\n case 'content_filter':\n stopReason = 'content_filter';\n break;\n }\n\n return {\n message,\n usage,\n stopReason,\n data: structuredData,\n };\n}\n\n/**\n * State for accumulating streaming response\n */\nexport interface CompletionsStreamState {\n id: string;\n model: string;\n text: string;\n toolCalls: Map<number, { id: string; name: string; arguments: string }>;\n finishReason: string | null;\n inputTokens: number;\n outputTokens: number;\n}\n\n/**\n * Create initial stream state\n */\nexport function createStreamState(): CompletionsStreamState {\n return {\n id: '',\n model: '',\n text: '',\n toolCalls: new Map(),\n finishReason: null,\n inputTokens: 0,\n outputTokens: 0,\n };\n}\n\n/**\n * Transform OpenRouter stream chunk to UPP StreamEvent\n * Returns array since one chunk may produce multiple events\n */\nexport function transformStreamEvent(\n chunk: OpenRouterCompletionsStreamChunk,\n state: CompletionsStreamState\n): StreamEvent[] {\n const events: StreamEvent[] = [];\n\n // Update state with basic info\n if (chunk.id && !state.id) {\n state.id = chunk.id;\n events.push({ type: 'message_start', index: 0, delta: {} });\n }\n if (chunk.model) {\n state.model = chunk.model;\n }\n\n // Process choices\n const choice = chunk.choices[0];\n if (choice) {\n // Text delta\n if (choice.delta.content) {\n state.text += choice.delta.content;\n events.push({\n type: 'text_delta',\n index: 0,\n delta: { text: choice.delta.content },\n });\n }\n\n // Tool call deltas\n if (choice.delta.tool_calls) {\n for (const toolCallDelta of choice.delta.tool_calls) {\n const index = toolCallDelta.index;\n let toolCall = state.toolCalls.get(index);\n\n if (!toolCall) {\n toolCall = { id: '', name: '', arguments: '' };\n state.toolCalls.set(index, toolCall);\n }\n\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id;\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name;\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments;\n events.push({\n type: 'tool_call_delta',\n index: index,\n delta: {\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n argumentsJson: toolCallDelta.function.arguments,\n },\n });\n }\n }\n }\n\n // Finish reason\n if (choice.finish_reason) {\n state.finishReason = choice.finish_reason;\n events.push({ type: 'message_stop', index: 0, delta: {} });\n }\n }\n\n // Usage info (usually comes at the end with stream_options.include_usage)\n if (chunk.usage) {\n state.inputTokens = chunk.usage.prompt_tokens;\n state.outputTokens = chunk.usage.completion_tokens;\n }\n\n return events;\n}\n\n/**\n * Build LLMResponse from accumulated stream state\n */\nexport function buildResponseFromState(state: CompletionsStreamState): LLMResponse {\n const textContent: TextBlock[] = [];\n let structuredData: unknown;\n if (state.text) {\n textContent.push({ type: 'text', text: state.text });\n // Try to parse as JSON for structured output (native JSON mode)\n try {\n structuredData = JSON.parse(state.text);\n } catch {\n // Not valid JSON - that's fine, might not be structured output\n }\n }\n\n const toolCalls: ToolCall[] = [];\n for (const [, toolCall] of state.toolCalls) {\n let args: Record<string, unknown> = {};\n if (toolCall.arguments) {\n try {\n args = JSON.parse(toolCall.arguments);\n } catch {\n // Invalid JSON - use empty object\n }\n }\n toolCalls.push({\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n arguments: args,\n });\n }\n\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: state.id,\n metadata: {\n openrouter: {\n model: state.model,\n finish_reason: state.finishReason,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.inputTokens + state.outputTokens,\n };\n\n // Map finish reason to stop reason\n let stopReason = 'end_turn';\n switch (state.finishReason) {\n case 'stop':\n stopReason = 'end_turn';\n break;\n case 'length':\n stopReason = 'max_tokens';\n break;\n case 'tool_calls':\n stopReason = 'tool_use';\n break;\n case 'content_filter':\n stopReason = 'content_filter';\n break;\n }\n\n return {\n message,\n usage,\n stopReason,\n data: structuredData,\n };\n}\n","import type { LLMHandler, BoundLLMModel, LLMRequest, LLMResponse, LLMStreamResult, LLMCapabilities } from '../../types/llm.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { LLMProvider } from '../../types/provider.ts';\nimport { UPPError } from '../../types/errors.ts';\nimport { resolveApiKey } from '../../http/keys.ts';\nimport { doFetch, doStreamFetch } from '../../http/fetch.ts';\nimport { parseSSEStream } from '../../http/sse.ts';\nimport { normalizeHttpError } from '../../http/errors.ts';\nimport type { OpenRouterLLMParams, OpenRouterCompletionsResponse, OpenRouterCompletionsStreamChunk } from './types.ts';\nimport {\n transformRequest,\n transformResponse,\n transformStreamEvent,\n createStreamState,\n buildResponseFromState,\n} from './transform.completions.ts';\n\nconst OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\n/**\n * OpenRouter API capabilities\n */\nconst OPENROUTER_CAPABILITIES: LLMCapabilities = {\n streaming: true,\n tools: true,\n structuredOutput: true,\n imageInput: true,\n videoInput: false,\n audioInput: false,\n};\n\n/**\n * Create OpenRouter Chat Completions LLM handler\n */\nexport function createCompletionsLLMHandler(): LLMHandler<OpenRouterLLMParams> {\n // Provider reference injected by createProvider() or OpenRouter's custom factory\n let providerRef: LLMProvider<OpenRouterLLMParams> | null = null;\n\n return {\n _setProvider(provider: LLMProvider<OpenRouterLLMParams>) {\n providerRef = provider;\n },\n\n bind(modelId: string): BoundLLMModel<OpenRouterLLMParams> {\n // Use the injected provider reference\n if (!providerRef) {\n throw new UPPError(\n 'Provider reference not set. Handler must be used with createProvider() or have _setProvider called.',\n 'INVALID_REQUEST',\n 'openrouter',\n 'llm'\n );\n }\n\n const model: BoundLLMModel<OpenRouterLLMParams> = {\n modelId,\n capabilities: OPENROUTER_CAPABILITIES,\n\n get provider(): LLMProvider<OpenRouterLLMParams> {\n return providerRef!;\n },\n\n async complete(request: LLMRequest<OpenRouterLLMParams>): Promise<LLMResponse> {\n const apiKey = await resolveApiKey(\n request.config,\n 'OPENROUTER_API_KEY',\n 'openrouter',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? OPENROUTER_API_URL;\n const body = transformRequest(request, modelId);\n\n const response = await doFetch(\n baseUrl,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'openrouter',\n 'llm'\n );\n\n const data = (await response.json()) as OpenRouterCompletionsResponse;\n return transformResponse(data);\n },\n\n stream(request: LLMRequest<OpenRouterLLMParams>): LLMStreamResult {\n const state = createStreamState();\n let responseResolve: (value: LLMResponse) => void;\n let responseReject: (error: Error) => void;\n\n const responsePromise = new Promise<LLMResponse>((resolve, reject) => {\n responseResolve = resolve;\n responseReject = reject;\n });\n\n async function* generateEvents(): AsyncGenerator<StreamEvent, void, unknown> {\n try {\n const apiKey = await resolveApiKey(\n request.config,\n 'OPENROUTER_API_KEY',\n 'openrouter',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? OPENROUTER_API_URL;\n const body = transformRequest(request, modelId);\n body.stream = true;\n body.stream_options = { include_usage: true };\n\n const response = await doStreamFetch(\n baseUrl,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'openrouter',\n 'llm'\n );\n\n if (!response.ok) {\n const error = await normalizeHttpError(response, 'openrouter', 'llm');\n responseReject(error);\n throw error;\n }\n\n if (!response.body) {\n const error = new UPPError(\n 'No response body for streaming request',\n 'PROVIDER_ERROR',\n 'openrouter',\n 'llm'\n );\n responseReject(error);\n throw error;\n }\n\n for await (const data of parseSSEStream(response.body)) {\n // Skip [DONE] marker\n if (data === '[DONE]') {\n continue;\n }\n\n // Check for OpenRouter error event\n if (typeof data === 'object' && data !== null) {\n const chunk = data as OpenRouterCompletionsStreamChunk;\n\n // Check for error in chunk\n if ('error' in chunk && chunk.error) {\n const errorData = chunk.error as { message?: string; type?: string };\n const error = new UPPError(\n errorData.message ?? 'Unknown error',\n 'PROVIDER_ERROR',\n 'openrouter',\n 'llm'\n );\n responseReject(error);\n throw error;\n }\n\n const uppEvents = transformStreamEvent(chunk, state);\n for (const event of uppEvents) {\n yield event;\n }\n }\n }\n\n // Build final response\n responseResolve(buildResponseFromState(state));\n } catch (error) {\n responseReject(error as Error);\n throw error;\n }\n }\n\n return {\n [Symbol.asyncIterator]() {\n return generateEvents();\n },\n response: responsePromise,\n };\n },\n };\n\n return model;\n },\n };\n}\n","import type { LLMRequest, LLMResponse } from '../../types/llm.ts';\nimport type { Message } from '../../types/messages.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { Tool, ToolCall } from '../../types/tool.ts';\nimport type { TokenUsage } from '../../types/turn.ts';\nimport type { ContentBlock, TextBlock, ImageBlock } from '../../types/content.ts';\nimport {\n AssistantMessage,\n isUserMessage,\n isAssistantMessage,\n isToolResultMessage,\n} from '../../types/messages.ts';\nimport type {\n OpenRouterLLMParams,\n OpenRouterResponsesRequest,\n OpenRouterResponsesInputItem,\n OpenRouterResponsesContentPart,\n OpenRouterResponsesTool,\n OpenRouterResponsesResponse,\n OpenRouterResponsesStreamEvent,\n OpenRouterResponsesOutputItem,\n OpenRouterResponsesMessageOutput,\n OpenRouterResponsesFunctionCallOutput,\n} from './types.ts';\n\n/**\n * Transform UPP request to OpenRouter Responses API format\n */\nexport function transformRequest<TParams extends OpenRouterLLMParams>(\n request: LLMRequest<TParams>,\n modelId: string\n): OpenRouterResponsesRequest {\n const params: OpenRouterLLMParams = request.params ?? {};\n\n const openrouterRequest: OpenRouterResponsesRequest = {\n model: modelId,\n input: transformInputItems(request.messages, request.system),\n };\n\n // Model parameters\n if (params.temperature !== undefined) {\n openrouterRequest.temperature = params.temperature;\n }\n if (params.top_p !== undefined) {\n openrouterRequest.top_p = params.top_p;\n }\n if (params.max_output_tokens !== undefined) {\n openrouterRequest.max_output_tokens = params.max_output_tokens;\n } else if (params.max_tokens !== undefined) {\n openrouterRequest.max_output_tokens = params.max_tokens;\n }\n if (params.reasoning !== undefined) {\n openrouterRequest.reasoning = { ...params.reasoning };\n }\n\n // Tools\n if (request.tools && request.tools.length > 0) {\n openrouterRequest.tools = request.tools.map(transformTool);\n if (params.parallel_tool_calls !== undefined) {\n openrouterRequest.parallel_tool_calls = params.parallel_tool_calls;\n }\n }\n\n // Structured output via text.format\n if (request.structure) {\n const schema: Record<string, unknown> = {\n type: 'object',\n properties: request.structure.properties,\n required: request.structure.required,\n ...(request.structure.additionalProperties !== undefined\n ? { additionalProperties: request.structure.additionalProperties }\n : { additionalProperties: false }),\n };\n if (request.structure.description) {\n schema.description = request.structure.description;\n }\n\n openrouterRequest.text = {\n format: {\n type: 'json_schema',\n name: 'json_response',\n description: request.structure.description,\n schema,\n strict: true,\n },\n };\n }\n\n return openrouterRequest;\n}\n\n/**\n * Transform messages to Responses API input items\n */\nfunction transformInputItems(\n messages: Message[],\n system?: string\n): OpenRouterResponsesInputItem[] | string {\n const result: OpenRouterResponsesInputItem[] = [];\n\n if (system) {\n result.push({\n type: 'message',\n role: 'system',\n content: system,\n });\n }\n\n for (const message of messages) {\n const items = transformMessage(message);\n result.push(...items);\n }\n\n // If there's only one user message with simple text, return as string\n if (result.length === 1 && result[0]?.type === 'message') {\n const item = result[0] as { role?: string; content?: string | unknown[] };\n if (item.role === 'user' && typeof item.content === 'string') {\n return item.content;\n }\n }\n\n return result;\n}\n\n/**\n * Filter to only valid content blocks with a type property\n */\nfunction filterValidContent<T extends { type?: string }>(content: T[]): T[] {\n return content.filter((c) => c && typeof c.type === 'string');\n}\n\n/**\n * Transform a UPP Message to OpenRouter Responses API input items\n */\nfunction transformMessage(message: Message): OpenRouterResponsesInputItem[] {\n if (isUserMessage(message)) {\n const validContent = filterValidContent(message.content);\n // Check if we can use simple string content\n if (validContent.length === 1 && validContent[0]?.type === 'text') {\n return [\n {\n type: 'message',\n role: 'user',\n content: (validContent[0] as TextBlock).text,\n },\n ];\n }\n return [\n {\n type: 'message',\n role: 'user',\n content: validContent.map(transformContentPart),\n },\n ];\n }\n\n if (isAssistantMessage(message)) {\n const validContent = filterValidContent(message.content);\n const items: OpenRouterResponsesInputItem[] = [];\n\n // Add message content - only text parts for assistant messages\n const contentParts: OpenRouterResponsesContentPart[] = validContent\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c): OpenRouterResponsesContentPart => ({\n type: 'output_text',\n text: c.text,\n annotations: [],\n }));\n\n // Get message id from metadata or generate one\n const messageId = message.id ?? `msg_${Date.now()}`;\n\n // Add assistant message if we have text content\n if (contentParts.length > 0) {\n items.push({\n type: 'message',\n role: 'assistant',\n id: messageId,\n status: 'completed',\n content: contentParts,\n });\n }\n\n // Add function_call items for each tool call (must precede function_call_output)\n const openrouterMeta = message.metadata?.openrouter as\n | { functionCallItems?: Array<{ id: string; call_id: string; name: string; arguments: string }> }\n | undefined;\n const functionCallItems = openrouterMeta?.functionCallItems;\n\n if (functionCallItems && functionCallItems.length > 0) {\n for (const fc of functionCallItems) {\n items.push({\n type: 'function_call',\n id: fc.id,\n call_id: fc.call_id,\n name: fc.name,\n arguments: fc.arguments,\n });\n }\n } else if (message.toolCalls && message.toolCalls.length > 0) {\n for (const call of message.toolCalls) {\n items.push({\n type: 'function_call',\n id: `fc_${call.toolCallId}`,\n call_id: call.toolCallId,\n name: call.toolName,\n arguments: JSON.stringify(call.arguments),\n });\n }\n }\n\n return items;\n }\n\n if (isToolResultMessage(message)) {\n // Tool results are function_call_output items\n return message.results.map((result, index) => ({\n type: 'function_call_output' as const,\n id: `fco_${result.toolCallId}_${index}`,\n call_id: result.toolCallId,\n output:\n typeof result.result === 'string'\n ? result.result\n : JSON.stringify(result.result),\n }));\n }\n\n return [];\n}\n\n/**\n * Transform a content block to Responses API format\n */\nfunction transformContentPart(block: ContentBlock): OpenRouterResponsesContentPart {\n switch (block.type) {\n case 'text':\n return { type: 'input_text', text: block.text };\n\n case 'image': {\n const imageBlock = block as ImageBlock;\n if (imageBlock.source.type === 'base64') {\n return {\n type: 'input_image',\n image_url: `data:${imageBlock.mimeType};base64,${imageBlock.source.data}`,\n detail: 'auto',\n };\n }\n\n if (imageBlock.source.type === 'url') {\n return {\n type: 'input_image',\n image_url: imageBlock.source.url,\n detail: 'auto',\n };\n }\n\n if (imageBlock.source.type === 'bytes') {\n // Convert bytes to base64\n const base64 = btoa(\n Array.from(imageBlock.source.data)\n .map((b) => String.fromCharCode(b))\n .join('')\n );\n return {\n type: 'input_image',\n image_url: `data:${imageBlock.mimeType};base64,${base64}`,\n detail: 'auto',\n };\n }\n\n throw new Error('Unknown image source type');\n }\n\n default:\n throw new Error(`Unsupported content type: ${block.type}`);\n }\n}\n\n/**\n * Transform a UPP Tool to Responses API format\n */\nfunction transformTool(tool: Tool): OpenRouterResponsesTool {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: {\n type: 'object',\n properties: tool.parameters.properties,\n required: tool.parameters.required,\n ...(tool.parameters.additionalProperties !== undefined\n ? { additionalProperties: tool.parameters.additionalProperties }\n : {}),\n },\n };\n}\n\n/**\n * Transform OpenRouter Responses API response to UPP LLMResponse\n */\nexport function transformResponse(data: OpenRouterResponsesResponse): LLMResponse {\n // Extract text content and tool calls from output items\n const textContent: TextBlock[] = [];\n const toolCalls: ToolCall[] = [];\n const functionCallItems: Array<{\n id: string;\n call_id: string;\n name: string;\n arguments: string;\n }> = [];\n let hadRefusal = false;\n let structuredData: unknown;\n\n for (const item of data.output) {\n if (item.type === 'message') {\n const messageItem = item as OpenRouterResponsesMessageOutput;\n for (const content of messageItem.content) {\n if (content.type === 'output_text') {\n textContent.push({ type: 'text', text: content.text });\n // Try to parse as JSON for structured output (native JSON mode)\n // Only set data if text is valid JSON\n if (structuredData === undefined) {\n try {\n structuredData = JSON.parse(content.text);\n } catch {\n // Not valid JSON - that's fine, might not be structured output\n }\n }\n } else if (content.type === 'refusal') {\n textContent.push({ type: 'text', text: content.refusal });\n hadRefusal = true;\n }\n }\n } else if (item.type === 'function_call') {\n const functionCall = item as OpenRouterResponsesFunctionCallOutput;\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(functionCall.arguments);\n } catch {\n // Invalid JSON - use empty object\n }\n toolCalls.push({\n toolCallId: functionCall.call_id,\n toolName: functionCall.name,\n arguments: args,\n });\n functionCallItems.push({\n id: functionCall.id,\n call_id: functionCall.call_id,\n name: functionCall.name,\n arguments: functionCall.arguments,\n });\n }\n }\n\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: data.id,\n metadata: {\n openrouter: {\n model: data.model,\n status: data.status,\n // Store response_id for multi-turn tool calling\n response_id: data.id,\n functionCallItems:\n functionCallItems.length > 0 ? functionCallItems : undefined,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: data.usage.input_tokens,\n outputTokens: data.usage.output_tokens,\n totalTokens: data.usage.total_tokens,\n };\n\n // Map status to stop reason\n let stopReason = 'end_turn';\n if (data.status === 'completed') {\n stopReason = toolCalls.length > 0 ? 'tool_use' : 'end_turn';\n } else if (data.status === 'incomplete') {\n stopReason = data.incomplete_details?.reason === 'max_output_tokens'\n ? 'max_tokens'\n : 'end_turn';\n } else if (data.status === 'failed') {\n stopReason = 'error';\n }\n if (hadRefusal && stopReason !== 'error') {\n stopReason = 'content_filter';\n }\n\n return {\n message,\n usage,\n stopReason,\n data: structuredData,\n };\n}\n\n/**\n * State for accumulating streaming response\n */\nexport interface ResponsesStreamState {\n id: string;\n model: string;\n textByIndex: Map<number, string>;\n toolCalls: Map<\n number,\n { itemId?: string; callId?: string; name?: string; arguments: string }\n >;\n status: string;\n inputTokens: number;\n outputTokens: number;\n hadRefusal: boolean;\n}\n\n/**\n * Create initial stream state\n */\nexport function createStreamState(): ResponsesStreamState {\n return {\n id: '',\n model: '',\n textByIndex: new Map(),\n toolCalls: new Map(),\n status: 'in_progress',\n inputTokens: 0,\n outputTokens: 0,\n hadRefusal: false,\n };\n}\n\n/**\n * Transform OpenRouter Responses API stream event to UPP StreamEvent\n * Returns array since one event may produce multiple UPP events\n */\nexport function transformStreamEvent(\n event: OpenRouterResponsesStreamEvent,\n state: ResponsesStreamState\n): StreamEvent[] {\n const events: StreamEvent[] = [];\n\n switch (event.type) {\n case 'response.created':\n state.id = event.response.id;\n state.model = event.response.model;\n events.push({ type: 'message_start', index: 0, delta: {} });\n break;\n\n case 'response.in_progress':\n state.status = 'in_progress';\n break;\n\n case 'response.completed':\n case 'response.done':\n // Handle both event type names (OpenRouter docs show response.done)\n state.status = 'completed';\n if (event.response?.usage) {\n state.inputTokens = event.response.usage.input_tokens;\n state.outputTokens = event.response.usage.output_tokens;\n }\n // CRITICAL: OpenRouter's streaming doesn't send function call arguments incrementally.\n // They only appear in the final response.completed event's output array.\n // We must extract them here to populate state.toolCalls with actual arguments.\n if (event.response?.output) {\n for (let i = 0; i < event.response.output.length; i++) {\n const item = event.response.output[i];\n if (item && item.type === 'function_call') {\n const functionCall = item as OpenRouterResponsesFunctionCallOutput;\n const existing = state.toolCalls.get(i) ?? { arguments: '' };\n existing.itemId = functionCall.id ?? existing.itemId;\n existing.callId = functionCall.call_id ?? existing.callId;\n existing.name = functionCall.name ?? existing.name;\n // This is the key fix: get arguments from the completed response\n if (functionCall.arguments) {\n existing.arguments = functionCall.arguments;\n }\n state.toolCalls.set(i, existing);\n }\n }\n }\n events.push({ type: 'message_stop', index: 0, delta: {} });\n break;\n\n case 'response.failed':\n state.status = 'failed';\n events.push({ type: 'message_stop', index: 0, delta: {} });\n break;\n\n case 'response.output_item.added':\n if (event.item.type === 'function_call') {\n const functionCall = event.item as OpenRouterResponsesFunctionCallOutput;\n const existing = state.toolCalls.get(event.output_index) ?? {\n arguments: '',\n };\n existing.itemId = functionCall.id;\n existing.callId = functionCall.call_id;\n existing.name = functionCall.name;\n if (functionCall.arguments) {\n existing.arguments = functionCall.arguments;\n }\n state.toolCalls.set(event.output_index, existing);\n }\n events.push({\n type: 'content_block_start',\n index: event.output_index,\n delta: {},\n });\n break;\n\n case 'response.output_item.done':\n if (event.item.type === 'function_call') {\n const functionCall = event.item as OpenRouterResponsesFunctionCallOutput;\n const existing = state.toolCalls.get(event.output_index) ?? {\n arguments: '',\n };\n existing.itemId = functionCall.id;\n existing.callId = functionCall.call_id;\n existing.name = functionCall.name;\n if (functionCall.arguments) {\n existing.arguments = functionCall.arguments;\n }\n state.toolCalls.set(event.output_index, existing);\n } else if (event.item.type === 'message') {\n // Extract text from completed message item (may not have incremental deltas)\n const messageItem = event.item as OpenRouterResponsesMessageOutput;\n for (const content of messageItem.content || []) {\n if (content.type === 'output_text') {\n const existingText = state.textByIndex.get(event.output_index) ?? '';\n if (!existingText && content.text) {\n // Only use done text if we didn't get incremental deltas\n state.textByIndex.set(event.output_index, content.text);\n events.push({\n type: 'text_delta',\n index: event.output_index,\n delta: { text: content.text },\n });\n }\n }\n }\n }\n events.push({\n type: 'content_block_stop',\n index: event.output_index,\n delta: {},\n });\n break;\n\n case 'response.content_part.delta':\n case 'response.output_text.delta': {\n // Handle both event type names (OpenRouter docs vs OpenAI-style)\n // Accumulate text\n const textDelta = (event as { delta: string }).delta;\n const currentText = state.textByIndex.get(event.output_index) ?? '';\n state.textByIndex.set(event.output_index, currentText + textDelta);\n events.push({\n type: 'text_delta',\n index: event.output_index,\n delta: { text: textDelta },\n });\n break;\n }\n\n case 'response.output_text.done':\n case 'response.content_part.done':\n // Handle both event type names\n if ('text' in event) {\n state.textByIndex.set(event.output_index, (event as { text: string }).text);\n }\n break;\n\n case 'response.refusal.delta': {\n state.hadRefusal = true;\n const currentRefusal = state.textByIndex.get(event.output_index) ?? '';\n state.textByIndex.set(event.output_index, currentRefusal + event.delta);\n events.push({\n type: 'text_delta',\n index: event.output_index,\n delta: { text: event.delta },\n });\n break;\n }\n\n case 'response.refusal.done':\n state.hadRefusal = true;\n state.textByIndex.set(event.output_index, event.refusal);\n break;\n\n case 'response.function_call_arguments.delta': {\n // Accumulate function call arguments\n let toolCall = state.toolCalls.get(event.output_index);\n if (!toolCall) {\n toolCall = { arguments: '' };\n state.toolCalls.set(event.output_index, toolCall);\n }\n if (event.item_id && !toolCall.itemId) {\n toolCall.itemId = event.item_id;\n }\n if (event.call_id && !toolCall.callId) {\n toolCall.callId = event.call_id;\n }\n toolCall.arguments += event.delta;\n events.push({\n type: 'tool_call_delta',\n index: event.output_index,\n delta: {\n toolCallId: toolCall.callId ?? toolCall.itemId ?? '',\n toolName: toolCall.name,\n argumentsJson: event.delta,\n },\n });\n break;\n }\n\n case 'response.function_call_arguments.done': {\n // Finalize function call\n let toolCall = state.toolCalls.get(event.output_index);\n if (!toolCall) {\n toolCall = { arguments: '' };\n state.toolCalls.set(event.output_index, toolCall);\n }\n if (event.item_id) {\n toolCall.itemId = event.item_id;\n }\n if (event.call_id) {\n toolCall.callId = event.call_id;\n }\n toolCall.name = event.name;\n toolCall.arguments = event.arguments;\n break;\n }\n\n case 'response.reasoning.delta':\n // Emit reasoning as a reasoning_delta event\n events.push({\n type: 'reasoning_delta',\n index: 0,\n delta: { text: event.delta },\n });\n break;\n\n case 'error':\n // Error events are handled at the handler level\n break;\n\n default:\n // Ignore other events\n break;\n }\n\n return events;\n}\n\n/**\n * Build LLMResponse from accumulated stream state\n */\nexport function buildResponseFromState(state: ResponsesStreamState): LLMResponse {\n const textContent: TextBlock[] = [];\n let structuredData: unknown;\n\n // Combine all text content\n for (const [, text] of state.textByIndex) {\n if (text) {\n textContent.push({ type: 'text', text });\n // Try to parse as JSON for structured output (native JSON mode)\n if (structuredData === undefined) {\n try {\n structuredData = JSON.parse(text);\n } catch {\n // Not valid JSON - that's fine, might not be structured output\n }\n }\n }\n }\n\n const toolCalls: ToolCall[] = [];\n const functionCallItems: Array<{\n id: string;\n call_id: string;\n name: string;\n arguments: string;\n }> = [];\n for (const [, toolCall] of state.toolCalls) {\n let args: Record<string, unknown> = {};\n if (toolCall.arguments) {\n try {\n args = JSON.parse(toolCall.arguments);\n } catch {\n // Invalid JSON - use empty object\n }\n }\n const itemId = toolCall.itemId ?? '';\n const callId = toolCall.callId ?? toolCall.itemId ?? '';\n const name = toolCall.name ?? '';\n toolCalls.push({\n toolCallId: callId,\n toolName: name,\n arguments: args,\n });\n\n if (itemId && callId && name) {\n functionCallItems.push({\n id: itemId,\n call_id: callId,\n name,\n arguments: toolCall.arguments,\n });\n }\n }\n\n const message = new AssistantMessage(\n textContent,\n toolCalls.length > 0 ? toolCalls : undefined,\n {\n id: state.id,\n metadata: {\n openrouter: {\n model: state.model,\n status: state.status,\n // Store response_id for multi-turn tool calling\n response_id: state.id,\n functionCallItems:\n functionCallItems.length > 0 ? functionCallItems : undefined,\n },\n },\n }\n );\n\n const usage: TokenUsage = {\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.inputTokens + state.outputTokens,\n };\n\n // Map status to stop reason\n let stopReason = 'end_turn';\n if (state.status === 'completed') {\n stopReason = toolCalls.length > 0 ? 'tool_use' : 'end_turn';\n } else if (state.status === 'failed') {\n stopReason = 'error';\n }\n if (state.hadRefusal && stopReason !== 'error') {\n stopReason = 'content_filter';\n }\n\n return {\n message,\n usage,\n stopReason,\n data: structuredData,\n };\n}\n","import type { LLMHandler, BoundLLMModel, LLMRequest, LLMResponse, LLMStreamResult, LLMCapabilities } from '../../types/llm.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { LLMProvider } from '../../types/provider.ts';\nimport { UPPError } from '../../types/errors.ts';\nimport { resolveApiKey } from '../../http/keys.ts';\nimport { doFetch, doStreamFetch } from '../../http/fetch.ts';\nimport { parseSSEStream } from '../../http/sse.ts';\nimport { normalizeHttpError } from '../../http/errors.ts';\nimport type { OpenRouterLLMParams, OpenRouterResponsesResponse, OpenRouterResponsesStreamEvent, OpenRouterResponseErrorEvent } from './types.ts';\nimport {\n transformRequest,\n transformResponse,\n transformStreamEvent,\n createStreamState,\n buildResponseFromState,\n} from './transform.responses.ts';\n\nconst OPENROUTER_RESPONSES_API_URL = 'https://openrouter.ai/api/v1/responses';\n\n/**\n * OpenRouter API capabilities\n */\nconst OPENROUTER_CAPABILITIES: LLMCapabilities = {\n streaming: true,\n tools: true,\n structuredOutput: true,\n imageInput: true,\n videoInput: false,\n audioInput: false,\n};\n\n/**\n * Create OpenRouter Responses API LLM handler\n */\nexport function createResponsesLLMHandler(): LLMHandler<OpenRouterLLMParams> {\n // Provider reference injected by createProvider() or OpenRouter's custom factory\n let providerRef: LLMProvider<OpenRouterLLMParams> | null = null;\n\n return {\n _setProvider(provider: LLMProvider<OpenRouterLLMParams>) {\n providerRef = provider;\n },\n\n bind(modelId: string): BoundLLMModel<OpenRouterLLMParams> {\n // Use the injected provider reference\n if (!providerRef) {\n throw new UPPError(\n 'Provider reference not set. Handler must be used with createProvider() or have _setProvider called.',\n 'INVALID_REQUEST',\n 'openrouter',\n 'llm'\n );\n }\n\n const model: BoundLLMModel<OpenRouterLLMParams> = {\n modelId,\n capabilities: OPENROUTER_CAPABILITIES,\n\n get provider(): LLMProvider<OpenRouterLLMParams> {\n return providerRef!;\n },\n\n async complete(request: LLMRequest<OpenRouterLLMParams>): Promise<LLMResponse> {\n const apiKey = await resolveApiKey(\n request.config,\n 'OPENROUTER_API_KEY',\n 'openrouter',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? OPENROUTER_RESPONSES_API_URL;\n const body = transformRequest(request, modelId);\n\n const response = await doFetch(\n baseUrl,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'openrouter',\n 'llm'\n );\n\n const data = (await response.json()) as OpenRouterResponsesResponse;\n\n // Check for error in response\n if (data.status === 'failed' && data.error) {\n throw new UPPError(\n data.error.message,\n 'PROVIDER_ERROR',\n 'openrouter',\n 'llm'\n );\n }\n\n return transformResponse(data);\n },\n\n stream(request: LLMRequest<OpenRouterLLMParams>): LLMStreamResult {\n const state = createStreamState();\n let responseResolve: (value: LLMResponse) => void;\n let responseReject: (error: Error) => void;\n\n const responsePromise = new Promise<LLMResponse>((resolve, reject) => {\n responseResolve = resolve;\n responseReject = reject;\n });\n\n async function* generateEvents(): AsyncGenerator<StreamEvent, void, unknown> {\n try {\n const apiKey = await resolveApiKey(\n request.config,\n 'OPENROUTER_API_KEY',\n 'openrouter',\n 'llm'\n );\n\n const baseUrl = request.config.baseUrl ?? OPENROUTER_RESPONSES_API_URL;\n const body = transformRequest(request, modelId);\n body.stream = true;\n\n const response = await doStreamFetch(\n baseUrl,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'openrouter',\n 'llm'\n );\n\n if (!response.ok) {\n const error = await normalizeHttpError(response, 'openrouter', 'llm');\n responseReject(error);\n throw error;\n }\n\n if (!response.body) {\n const error = new UPPError(\n 'No response body for streaming request',\n 'PROVIDER_ERROR',\n 'openrouter',\n 'llm'\n );\n responseReject(error);\n throw error;\n }\n\n for await (const data of parseSSEStream(response.body)) {\n // Skip [DONE] marker\n if (data === '[DONE]') {\n continue;\n }\n\n // Check for OpenRouter error event\n if (typeof data === 'object' && data !== null) {\n const event = data as OpenRouterResponsesStreamEvent;\n\n // Check for error event\n if (event.type === 'error') {\n const errorEvent = event as OpenRouterResponseErrorEvent;\n const error = new UPPError(\n errorEvent.error.message,\n 'PROVIDER_ERROR',\n 'openrouter',\n 'llm'\n );\n responseReject(error);\n throw error;\n }\n\n const uppEvents = transformStreamEvent(event, state);\n for (const uppEvent of uppEvents) {\n yield uppEvent;\n }\n }\n }\n\n // Build final response\n responseResolve(buildResponseFromState(state));\n } catch (error) {\n responseReject(error as Error);\n throw error;\n }\n }\n\n return {\n [Symbol.asyncIterator]() {\n return generateEvents();\n },\n response: responsePromise,\n };\n },\n };\n\n return model;\n },\n };\n}\n","import type {\n Provider,\n ModelReference,\n LLMHandler,\n LLMProvider,\n} from '../../types/provider.ts';\nimport { createCompletionsLLMHandler } from './llm.completions.ts';\nimport { createResponsesLLMHandler } from './llm.responses.ts';\nimport type { OpenRouterLLMParams, OpenRouterConfig } from './types.ts';\n\n/**\n * OpenRouter provider options\n */\nexport interface OpenRouterProviderOptions {\n /**\n * Which API to use:\n * - 'completions': Chat Completions API (default, recommended)\n * - 'responses': Responses API (beta)\n */\n api?: 'completions' | 'responses';\n}\n\n/**\n * OpenRouter provider with configurable API mode\n *\n * @example\n * // Using the Chat Completions API (default)\n * const model = openrouter('openai/gpt-4o');\n *\n * @example\n * // Using the Responses API (beta)\n * const model = openrouter('openai/gpt-4o', { api: 'responses' });\n *\n * @example\n * // Explicit Completions API\n * const model = openrouter('anthropic/claude-3.5-sonnet', { api: 'completions' });\n */\nexport interface OpenRouterProvider extends Provider<OpenRouterProviderOptions> {\n /**\n * Create a model reference\n * @param modelId - The model identifier (e.g., 'openai/gpt-4o', 'anthropic/claude-3.5-sonnet', 'meta-llama/llama-3.1-70b-instruct')\n * @param options - Provider options including API selection\n */\n (modelId: string, options?: OpenRouterProviderOptions): ModelReference<OpenRouterProviderOptions>;\n\n /** Provider name */\n readonly name: 'openrouter';\n\n /** Provider version */\n readonly version: string;\n\n /** Supported modalities */\n readonly modalities: {\n llm: LLMHandler<OpenRouterLLMParams>;\n };\n}\n\n/**\n * Create the OpenRouter provider\n */\nfunction createOpenRouterProvider(): OpenRouterProvider {\n // Track which API mode is currently active for the modalities\n // Default to 'completions' (unlike OpenAI which defaults to 'responses')\n let currentApiMode: 'completions' | 'responses' = 'completions';\n\n // Create handlers eagerly so we can inject provider reference\n const completionsHandler = createCompletionsLLMHandler();\n const responsesHandler = createResponsesLLMHandler();\n\n const fn = function (\n modelId: string,\n options?: OpenRouterProviderOptions\n ): ModelReference<OpenRouterProviderOptions> {\n const apiMode = options?.api ?? 'completions';\n currentApiMode = apiMode;\n return { modelId, provider };\n };\n\n // Create a dynamic modalities object that returns the correct handler\n const modalities = {\n get llm(): LLMHandler<OpenRouterLLMParams> {\n return currentApiMode === 'responses'\n ? responsesHandler\n : completionsHandler;\n },\n };\n\n // Define properties\n Object.defineProperties(fn, {\n name: {\n value: 'openrouter',\n writable: false,\n configurable: true,\n },\n version: {\n value: '1.0.0',\n writable: false,\n configurable: true,\n },\n modalities: {\n value: modalities,\n writable: false,\n configurable: true,\n },\n });\n\n const provider = fn as OpenRouterProvider;\n\n // Inject provider reference into both handlers (spec compliance)\n completionsHandler._setProvider?.(provider as unknown as LLMProvider<OpenRouterLLMParams>);\n responsesHandler._setProvider?.(provider as unknown as LLMProvider<OpenRouterLLMParams>);\n\n return provider;\n}\n\n/**\n * OpenRouter provider\n *\n * Supports both the Chat Completions API (default) and Responses API (beta).\n *\n * OpenRouter is a unified API that provides access to hundreds of AI models\n * through a single endpoint, including models from OpenAI, Anthropic, Google,\n * Meta, Mistral, and many others.\n *\n * @example\n * ```ts\n * import { openrouter } from './providers/openrouter';\n * import { llm } from './core/llm';\n *\n * // Using Chat Completions API (default, recommended)\n * const model = llm({\n * model: openrouter('openai/gpt-4o'),\n * params: { max_tokens: 1000 }\n * });\n *\n * // Using Responses API (beta)\n * const betaModel = llm({\n * model: openrouter('openai/gpt-4o', { api: 'responses' }),\n * params: { max_output_tokens: 1000 }\n * });\n *\n * // Using OpenRouter-specific features\n * const routedModel = llm({\n * model: openrouter('openai/gpt-4o'),\n * params: {\n * max_tokens: 1000,\n * // Fallback routing\n * models: ['openai/gpt-4o', 'anthropic/claude-3.5-sonnet'],\n * route: 'fallback',\n * // Provider preferences\n * provider: {\n * allow_fallbacks: true,\n * require_parameters: true,\n * },\n * }\n * });\n *\n * // Generate\n * const turn = await model.generate('Hello!');\n * console.log(turn.response.text);\n * ```\n */\nexport const openrouter = createOpenRouterProvider();\n\n// Re-export types\nexport type {\n OpenRouterLLMParams,\n OpenRouterConfig,\n OpenRouterAPIMode,\n OpenRouterModelOptions,\n OpenRouterModelReference,\n OpenRouterProviderPreferences,\n} from './types.ts';\n"],"mappings":"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","names":["transformRequest","transformTool","transformMessage","filterValidContent","transformResponse","createStreamState","transformStreamEvent","buildResponseFromState","OPENROUTER_CAPABILITIES","transformRequest","transformResponse","createStreamState","transformStreamEvent","buildResponseFromState"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@providerprotocol/ai",
3
- "version": "0.0.2",
4
- "description": "UPP-1.1: Unified Provider Protocol for AI inference",
3
+ "version": "0.0.4",
4
+ "description": "UPP: Unified Provider Protocol for AI inference",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Provider Protocol"
@@ -41,6 +41,12 @@
41
41
  "import": "./dist/ollama/index.js",
42
42
  "default": "./dist/ollama/index.js"
43
43
  },
44
+ "./openrouter": {
45
+ "types": "./dist/openrouter/index.d.ts",
46
+ "bun": "./src/openrouter/index.ts",
47
+ "import": "./dist/openrouter/index.js",
48
+ "default": "./dist/openrouter/index.js"
49
+ },
44
50
  "./http": {
45
51
  "types": "./dist/http/index.d.ts",
46
52
  "bun": "./src/http/index.ts",
@@ -82,6 +88,7 @@
82
88
  "gpt",
83
89
  "ollama",
84
90
  "llama",
91
+ "openrouter",
85
92
  "provider",
86
93
  "protocol",
87
94
  "unified"
@@ -0,0 +1,10 @@
1
+ // Re-export from providers/openrouter
2
+ export { openrouter } from '../providers/openrouter/index.ts';
3
+ export type {
4
+ OpenRouterLLMParams,
5
+ OpenRouterConfig,
6
+ OpenRouterAPIMode,
7
+ OpenRouterModelOptions,
8
+ OpenRouterModelReference,
9
+ OpenRouterProviderPreferences,
10
+ } from '../providers/openrouter/index.ts';
@@ -30,14 +30,18 @@ export function transformRequest<TParams extends AnthropicLLMParams>(
30
30
  request: LLMRequest<TParams>,
31
31
  modelId: string
32
32
  ): AnthropicRequest {
33
- const params = (request.params ?? { max_tokens: 4096 }) as AnthropicLLMParams;
33
+ const params = (request.params ?? {}) as AnthropicLLMParams;
34
34
 
35
35
  const anthropicRequest: AnthropicRequest = {
36
36
  model: modelId,
37
- max_tokens: params.max_tokens ?? 4096,
38
37
  messages: request.messages.map(transformMessage),
39
38
  };
40
39
 
40
+ // Only include max_tokens if provided - let Anthropic API enforce its requirement
41
+ if (params.max_tokens !== undefined) {
42
+ anthropicRequest.max_tokens = params.max_tokens;
43
+ }
44
+
41
45
  // System prompt (top-level in Anthropic)
42
46
  if (request.system) {
43
47
  anthropicRequest.system = request.system;
@@ -3,8 +3,8 @@
3
3
  * These are passed through to the Anthropic Messages API
4
4
  */
5
5
  export interface AnthropicLLMParams {
6
- /** Maximum number of tokens to generate (required by Anthropic) */
7
- max_tokens: number;
6
+ /** Maximum number of tokens to generate (required by Anthropic API) */
7
+ max_tokens?: number;
8
8
 
9
9
  /** Temperature for randomness (0.0 - 1.0) */
10
10
  temperature?: number;
@@ -42,7 +42,7 @@ export interface AnthropicLLMParams {
42
42
  */
43
43
  export interface AnthropicRequest {
44
44
  model: string;
45
- max_tokens: number;
45
+ max_tokens?: number;
46
46
  messages: AnthropicMessage[];
47
47
  system?: string;
48
48
  temperature?: number;
@@ -0,0 +1,173 @@
1
+ import type {
2
+ Provider,
3
+ ModelReference,
4
+ LLMHandler,
5
+ LLMProvider,
6
+ } from '../../types/provider.ts';
7
+ import { createCompletionsLLMHandler } from './llm.completions.ts';
8
+ import { createResponsesLLMHandler } from './llm.responses.ts';
9
+ import type { OpenRouterLLMParams, OpenRouterConfig } from './types.ts';
10
+
11
+ /**
12
+ * OpenRouter provider options
13
+ */
14
+ export interface OpenRouterProviderOptions {
15
+ /**
16
+ * Which API to use:
17
+ * - 'completions': Chat Completions API (default, recommended)
18
+ * - 'responses': Responses API (beta)
19
+ */
20
+ api?: 'completions' | 'responses';
21
+ }
22
+
23
+ /**
24
+ * OpenRouter provider with configurable API mode
25
+ *
26
+ * @example
27
+ * // Using the Chat Completions API (default)
28
+ * const model = openrouter('openai/gpt-4o');
29
+ *
30
+ * @example
31
+ * // Using the Responses API (beta)
32
+ * const model = openrouter('openai/gpt-4o', { api: 'responses' });
33
+ *
34
+ * @example
35
+ * // Explicit Completions API
36
+ * const model = openrouter('anthropic/claude-3.5-sonnet', { api: 'completions' });
37
+ */
38
+ export interface OpenRouterProvider extends Provider<OpenRouterProviderOptions> {
39
+ /**
40
+ * Create a model reference
41
+ * @param modelId - The model identifier (e.g., 'openai/gpt-4o', 'anthropic/claude-3.5-sonnet', 'meta-llama/llama-3.1-70b-instruct')
42
+ * @param options - Provider options including API selection
43
+ */
44
+ (modelId: string, options?: OpenRouterProviderOptions): ModelReference<OpenRouterProviderOptions>;
45
+
46
+ /** Provider name */
47
+ readonly name: 'openrouter';
48
+
49
+ /** Provider version */
50
+ readonly version: string;
51
+
52
+ /** Supported modalities */
53
+ readonly modalities: {
54
+ llm: LLMHandler<OpenRouterLLMParams>;
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Create the OpenRouter provider
60
+ */
61
+ function createOpenRouterProvider(): OpenRouterProvider {
62
+ // Track which API mode is currently active for the modalities
63
+ // Default to 'completions' (unlike OpenAI which defaults to 'responses')
64
+ let currentApiMode: 'completions' | 'responses' = 'completions';
65
+
66
+ // Create handlers eagerly so we can inject provider reference
67
+ const completionsHandler = createCompletionsLLMHandler();
68
+ const responsesHandler = createResponsesLLMHandler();
69
+
70
+ const fn = function (
71
+ modelId: string,
72
+ options?: OpenRouterProviderOptions
73
+ ): ModelReference<OpenRouterProviderOptions> {
74
+ const apiMode = options?.api ?? 'completions';
75
+ currentApiMode = apiMode;
76
+ return { modelId, provider };
77
+ };
78
+
79
+ // Create a dynamic modalities object that returns the correct handler
80
+ const modalities = {
81
+ get llm(): LLMHandler<OpenRouterLLMParams> {
82
+ return currentApiMode === 'responses'
83
+ ? responsesHandler
84
+ : completionsHandler;
85
+ },
86
+ };
87
+
88
+ // Define properties
89
+ Object.defineProperties(fn, {
90
+ name: {
91
+ value: 'openrouter',
92
+ writable: false,
93
+ configurable: true,
94
+ },
95
+ version: {
96
+ value: '1.0.0',
97
+ writable: false,
98
+ configurable: true,
99
+ },
100
+ modalities: {
101
+ value: modalities,
102
+ writable: false,
103
+ configurable: true,
104
+ },
105
+ });
106
+
107
+ const provider = fn as OpenRouterProvider;
108
+
109
+ // Inject provider reference into both handlers (spec compliance)
110
+ completionsHandler._setProvider?.(provider as unknown as LLMProvider<OpenRouterLLMParams>);
111
+ responsesHandler._setProvider?.(provider as unknown as LLMProvider<OpenRouterLLMParams>);
112
+
113
+ return provider;
114
+ }
115
+
116
+ /**
117
+ * OpenRouter provider
118
+ *
119
+ * Supports both the Chat Completions API (default) and Responses API (beta).
120
+ *
121
+ * OpenRouter is a unified API that provides access to hundreds of AI models
122
+ * through a single endpoint, including models from OpenAI, Anthropic, Google,
123
+ * Meta, Mistral, and many others.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { openrouter } from './providers/openrouter';
128
+ * import { llm } from './core/llm';
129
+ *
130
+ * // Using Chat Completions API (default, recommended)
131
+ * const model = llm({
132
+ * model: openrouter('openai/gpt-4o'),
133
+ * params: { max_tokens: 1000 }
134
+ * });
135
+ *
136
+ * // Using Responses API (beta)
137
+ * const betaModel = llm({
138
+ * model: openrouter('openai/gpt-4o', { api: 'responses' }),
139
+ * params: { max_output_tokens: 1000 }
140
+ * });
141
+ *
142
+ * // Using OpenRouter-specific features
143
+ * const routedModel = llm({
144
+ * model: openrouter('openai/gpt-4o'),
145
+ * params: {
146
+ * max_tokens: 1000,
147
+ * // Fallback routing
148
+ * models: ['openai/gpt-4o', 'anthropic/claude-3.5-sonnet'],
149
+ * route: 'fallback',
150
+ * // Provider preferences
151
+ * provider: {
152
+ * allow_fallbacks: true,
153
+ * require_parameters: true,
154
+ * },
155
+ * }
156
+ * });
157
+ *
158
+ * // Generate
159
+ * const turn = await model.generate('Hello!');
160
+ * console.log(turn.response.text);
161
+ * ```
162
+ */
163
+ export const openrouter = createOpenRouterProvider();
164
+
165
+ // Re-export types
166
+ export type {
167
+ OpenRouterLLMParams,
168
+ OpenRouterConfig,
169
+ OpenRouterAPIMode,
170
+ OpenRouterModelOptions,
171
+ OpenRouterModelReference,
172
+ OpenRouterProviderPreferences,
173
+ } from './types.ts';