export interface OpenRouterMessage { role: 'system' | 'user' | 'assistant'; content: string; } export interface OpenRouterModelConfig { model: string; maxTokens?: number; temperature?: number; topP?: number; topK?: number; stopSequences?: string[]; } export interface OpenRouterResponse { text: string; finishReason?: string; usage?: { promptTokens: number; completionTokens: number; totalTokens: number; }; } export interface OpenRouterStreamChunk { text: string; done: boolean; } export class OpenRouterClient { private apiKey: string; private baseUrl: string; private models: Map = new Map(); constructor(apiKey?: string) { this.apiKey = apiKey || process.env.OPENROUTER_API_KEY || ''; this.baseUrl = 'https://openrouter.ai/api/v1'; if (!this.apiKey) { console.warn('OpenRouter API key not provided. Some features may not work.'); } } /** * Get available OpenRouter models */ getAvailableModels(): string[] { return [ 'google/gemini-2.5-flash-image-preview:free', 'deepseek/deepseek-chat-v3.1:free', 'openai/gpt-oss-120b:free', 'openai/gpt-oss-20b:free', 'z-ai/glm-4.5-air:free', 'qwen/qwen3-coder:free', 'moonshotai/kimi-k2:free', 'tngtech/deepseek-r1t2-chimera:free' ]; } /** * Get model information */ getModelInfo(modelName: string) { const modelConfigs = { 'google/gemini-2.5-flash-image-preview:free': { maxTokens: 1_048_576, features: ['text', 'code', 'multimodal', 'image', 'fast'], category: 'openrouter-gemini' }, 'deepseek/deepseek-chat-v3.1:free': { maxTokens: 131_072, features: ['text', 'code', 'reasoning', 'chat'], category: 'openrouter-deepseek' }, 'openai/gpt-oss-120b:free': { maxTokens: 32_768, features: ['text', 'code', 'reasoning', 'large'], category: 'openrouter-openai' }, 'openai/gpt-oss-20b:free': { maxTokens: 16_384, features: ['text', 'code', 'reasoning'], category: 'openrouter-openai' }, 'z-ai/glm-4.5-air:free': { maxTokens: 8_192, features: ['text', 'code', 'lightweight', 'fast'], category: 'openrouter-glm' }, 'qwen/qwen3-coder:free': { maxTokens: 32_768, features: ['code', 'programming', 'specialized'], category: 'openrouter-qwen' }, 'moonshotai/kimi-k2:free': { maxTokens: 128_000, features: ['text', 'code', 'long-context'], category: 'openrouter-moonshot' }, 'tngtech/deepseek-r1t2-chimera:free': { maxTokens: 65_536, features: ['text', 'code', 'reasoning', 'advanced'], category: 'openrouter-deepseek' } }; return modelConfigs[modelName as keyof typeof modelConfigs] || { maxTokens: 16_384, features: ['text', 'code'], category: 'openrouter-general' }; } /** * Generate content using OpenRouter API */ async generateContent( modelName: string, prompt: string, config?: Partial ): Promise { try { const response = await fetch(`${this.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'HTTP-Referer': process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000', 'X-Title': 'DeepSite AI Platform' }, body: JSON.stringify({ model: modelName, messages: [ { role: 'user', content: prompt } ], max_tokens: config?.maxTokens || 4096, temperature: config?.temperature || 0.7, top_p: config?.topP || 0.9, stop: config?.stopSequences || undefined }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`); } const data = await response.json(); const choice = data.choices?.[0]; if (!choice) { throw new Error('No response from OpenRouter API'); } return { text: choice.message?.content || '', finishReason: choice.finish_reason, usage: { promptTokens: data.usage?.prompt_tokens || 0, completionTokens: data.usage?.completion_tokens || 0, totalTokens: data.usage?.total_tokens || 0, } }; } catch (error) { console.error('Error generating content with OpenRouter:', error); throw new Error(`OpenRouter generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Generate content with system instruction */ async generateWithSystemInstruction( modelName: string, systemInstruction: string, userPrompt: string, config?: Partial ): Promise { try { const response = await fetch(`${this.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'HTTP-Referer': process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000', 'X-Title': 'DeepSite AI Platform' }, body: JSON.stringify({ model: modelName, messages: [ { role: 'system', content: systemInstruction }, { role: 'user', content: userPrompt } ], max_tokens: config?.maxTokens || 4096, temperature: config?.temperature || 0.7, top_p: config?.topP || 0.9, stop: config?.stopSequences || undefined }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`); } const data = await response.json(); const choice = data.choices?.[0]; if (!choice) { throw new Error('No response from OpenRouter API'); } return { text: choice.message?.content || '', finishReason: choice.finish_reason, usage: { promptTokens: data.usage?.prompt_tokens || 0, completionTokens: data.usage?.completion_tokens || 0, totalTokens: data.usage?.total_tokens || 0, } }; } catch (error) { console.error('Error generating content with OpenRouter:', error); throw new Error(`OpenRouter generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Generate content with conversation history */ async generateWithHistory( modelName: string, messages: OpenRouterMessage[], config?: Partial ): Promise { try { const response = await fetch(`${this.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'HTTP-Referer': process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000', 'X-Title': 'DeepSite AI Platform' }, body: JSON.stringify({ model: modelName, messages: messages.map(msg => ({ role: msg.role, content: msg.content })), max_tokens: config?.maxTokens || 4096, temperature: config?.temperature || 0.7, top_p: config?.topP || 0.9, stop: config?.stopSequences || undefined }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`); } const data = await response.json(); const choice = data.choices?.[0]; if (!choice) { throw new Error('No response from OpenRouter API'); } return { text: choice.message?.content || '', finishReason: choice.finish_reason, usage: { promptTokens: data.usage?.prompt_tokens || 0, completionTokens: data.usage?.completion_tokens || 0, totalTokens: data.usage?.total_tokens || 0, } }; } catch (error) { console.error('Error generating content with OpenRouter:', error); throw new Error(`OpenRouter generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Generate content stream using OpenRouter API */ async* generateContentStream( modelName: string, prompt: string, config?: Partial ): AsyncGenerator { try { const response = await fetch(`${this.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'HTTP-Referer': process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000', 'X-Title': 'DeepSite AI Platform' }, body: JSON.stringify({ model: modelName, messages: [ { role: 'user', content: prompt } ], max_tokens: config?.maxTokens || 4096, temperature: config?.temperature || 0.7, top_p: config?.topP || 0.9, stop: config?.stopSequences || undefined, stream: true }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`); } const reader = response.body?.getReader(); if (!reader) { throw new Error('Failed to get response reader'); } const decoder = new TextDecoder(); let buffer = ''; try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6); if (data === '[DONE]') { yield { text: '', done: true }; return; } try { const parsed = JSON.parse(data); const content = parsed.choices?.[0]?.delta?.content; if (content) { yield { text: content, done: false }; } } catch { // Skip invalid JSON continue; } } } } } finally { reader.releaseLock(); } yield { text: '', done: true }; } catch (error) { console.error('Error streaming content with OpenRouter:', error); throw new Error(`OpenRouter streaming failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } } // Export singleton instance export const openRouterClient = new OpenRouterClient();