95 lines
2.4 KiB
TypeScript
95 lines
2.4 KiB
TypeScript
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<OpenAIStreamChunk, void, unknown> {
|
|
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<string[]> {
|
|
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 || [];
|
|
}
|