import type { Message, OpenAIMessage, OpenAIStreamChunk } from '@/types'; export async function* streamChatCompletion( messages: Message[], apiUrl: string, apiKey: string, model: string, temperature: number, maxTokens: number, systemPrompt: string, abortSignal: AbortSignal ): AsyncGenerator { const formattedMessages: OpenAIMessage[] = [ { role: 'system', content: systemPrompt }, ...messages.map((m) => ({ role: m.role, content: m.content }) as OpenAIMessage), ]; const response = await fetch('/api/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ apiUrl, apiKey, model, temperature, max_tokens: maxTokens, messages: formattedMessages, stream: true, }), signal: abortSignal, }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`); } const reader = response.body?.getReader(); if (!reader) { throw new Error('No response body'); } 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) { const trimmed = line.trim(); if (!trimmed || trimmed === 'data: [DONE]') continue; if (trimmed.startsWith('data: ')) { try { const data: OpenAIStreamChunk = JSON.parse(trimmed.slice(6)); yield data; } catch { // Ignore parse errors for incomplete chunks } } } } } finally { reader.releaseLock(); } } export async function fetchModels( apiUrl: string, apiKey: string ): Promise { const response = await fetch('/api/v1/models', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ apiUrl, apiKey }), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error?.message || `HTTP error! status: ${response.status}`); } const data = await response.json(); return data.models || []; }