techprotrade's picture
download
raw
3.1 kB
type Message = { role: "user" | "assistant"; content: string };
const CHAT_URL = `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/chat`;
export type ModelType = "local" | "gemini-flash" | "perplexity-sonar";
export async function streamChat({
messages,
model = "gemini-flash",
onDelta,
onDone,
onError,
}: {
messages: Message[];
model?: ModelType;
onDelta: (deltaText: string) => void;
onDone: () => void;
onError: (error: string) => void;
}) {
try {
const resp = await fetch(CHAT_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY}`,
},
body: JSON.stringify({ messages, model }),
});
if (!resp.ok) {
const errorData = await resp.json().catch(() => ({}));
const errorMessage = errorData.error || `Request failed with status ${resp.status}`;
if (resp.status === 429) { onError("Rate limit exceeded. Please wait a moment."); return; }
if (resp.status === 402) { onError("AI credits exhausted. Please add credits."); return; }
onError(errorMessage);
return;
}
if (!resp.body) { onError("No response body received"); return; }
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let textBuffer = "";
let streamDone = false;
while (!streamDone) {
const { done, value } = await reader.read();
if (done) break;
textBuffer += decoder.decode(value, { stream: true });
let newlineIndex: number;
while ((newlineIndex = textBuffer.indexOf("\n")) !== -1) {
let line = textBuffer.slice(0, newlineIndex);
textBuffer = textBuffer.slice(newlineIndex + 1);
if (line.endsWith("\r")) line = line.slice(0, -1);
if (line.startsWith(":") || line.trim() === "") continue;
if (!line.startsWith("data: ")) continue;
const jsonStr = line.slice(6).trim();
if (jsonStr === "[DONE]") { streamDone = true; break; }
try {
const parsed = JSON.parse(jsonStr);
const content = parsed.choices?.[0]?.delta?.content as string | undefined;
if (content) onDelta(content);
} catch {
textBuffer = line + "\n" + textBuffer;
break;
}
}
}
if (textBuffer.trim()) {
for (let raw of textBuffer.split("\n")) {
if (!raw) continue;
if (raw.endsWith("\r")) raw = raw.slice(0, -1);
if (raw.startsWith(":") || raw.trim() === "") continue;
if (!raw.startsWith("data: ")) continue;
const jsonStr = raw.slice(6).trim();
if (jsonStr === "[DONE]") continue;
try {
const parsed = JSON.parse(jsonStr);
const content = parsed.choices?.[0]?.delta?.content as string | undefined;
if (content) onDelta(content);
} catch { /* ignore */ }
}
}
onDone();
} catch (error) {
console.error("Stream chat error:", error);
onError(error instanceof Error ? error.message : "An unexpected error occurred");
}
}

Xet Storage Details

Size:
3.1 kB
·
Xet hash:
d696315230aeef6dc3971d47d0c791e6340219320afe9f9508db88146bdeb66f

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.