/** * OpenAI-compatible AI client for multiple providers. * Supports OpenAI, Ollama, OpenRouter, and any OpenAI-compatible API. */ export interface AIConfig { apiKey: string; baseUrl: string; model: string; systemPrompt: string; groupPrompt?: string; } export interface AIMessage { role: 'user' | 'assistant' | 'system'; content: string; } /** * Mask an API key for safe display * sk-proj-abc123xyz... → sk-***...***xyz */ export function maskApiKey(key: string): string { if (!key || key.length < 8) return '***'; const start = key.substring(0, 3); const end = key.substring(key.length - 3); return `${start}***...***${end}`; } /** * Generate AI response using OpenAI-compatible API */ export async function generateAIResponse( config: AIConfig, context: AIMessage[], userMessage: string ): Promise { const messages: AIMessage[] = []; // Build system prompt (global + group) let systemPrompt = config.systemPrompt; if (config.groupPrompt) { systemPrompt += '\n\n' + config.groupPrompt; } messages.push({ role: 'system', content: systemPrompt }); // Add context messages (last 50) messages.push(...context.slice(-50)); // Add user message messages.push({ role: 'user', content: userMessage }); const response = await fetch(`${config.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}`, }, body: JSON.stringify({ model: config.model, messages, max_tokens: 500, temperature: 0.8, presence_penalty: 0.6, frequency_penalty: 0.6, }), }); if (!response.ok) { const error = await response.text(); throw new Error(`AI API error: ${response.status} - ${error}`); } const data = await response.json() as { choices: Array<{ message?: { content?: string } }> }; return data.choices[0]?.message?.content || 'Keine Antwort erhalten.'; } /** * Get default base URL for a provider */ export function getDefaultBaseUrl(provider: string): string { switch (provider) { case 'openai': return 'https://api.openai.com/v1'; case 'ollama': return 'http://localhost:11434/v1'; case 'openrouter': return 'https://openrouter.ai/api/v1'; default: return 'https://api.openai.com/v1'; } } /** * Get available models for a provider */ export function getProviderModels(provider: string): string[] { switch (provider) { case 'openai': return ['gpt-4o-mini', 'gpt-4o', 'gpt-3.5-turbo']; case 'ollama': return ['llama3', 'llama3.1', 'mistral', 'codellama']; case 'openrouter': return ['openai/gpt-4o-mini', 'anthropic/claude-3.5-sonnet', 'meta-llama/llama-3.1-8b-instruct']; default: return ['gpt-4o-mini']; } }