lesson 23 LLM sentence explainer
This commit is contained in:
1 parent
075fe19382
commit
e734d09039
10 files changed
+744
-25
No files matched your search
@@ -0,0 +1,115 @@
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
|
||||
//@ts-ignore
|
||||
const { VITE_LLM_API_KEY } = import.meta.env;
|
||||
|
||||
// Minimal type definitions
|
||||
type DeepSeekChunk = {
|
||||
choices: Array<{
|
||||
delta: {
|
||||
content?: string;
|
||||
reasoning_content?: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
|
||||
// Type guard - simple and effective
|
||||
function isValidChunk(data: unknown): data is DeepSeekChunk {
|
||||
return (
|
||||
typeof data === "object" &&
|
||||
data !== null &&
|
||||
"choices" in data &&
|
||||
Array.isArray((data as any).choices) &&
|
||||
(data as any).choices.length > 0 &&
|
||||
"delta" in (data as any).choices[0]
|
||||
);
|
||||
}
|
||||
|
||||
// Helper - one-liner safe parse
|
||||
function safeParse<T>(text: string): T | null {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function* LLMstream(body: {
|
||||
model: string;
|
||||
messages: {
|
||||
role: "system" | "user";
|
||||
content: string;
|
||||
}[];
|
||||
}) {
|
||||
if (!VITE_LLM_API_KEY) return;
|
||||
|
||||
let endpoint = "https://api.deepseek.com/chat/completions";
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${VITE_LLM_API_KEY}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...body,
|
||||
stream: true,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok || !response.body) {
|
||||
throw response;
|
||||
}
|
||||
|
||||
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
||||
|
||||
let buffer = "";
|
||||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += value;
|
||||
|
||||
const events = buffer.split("\n\n");
|
||||
buffer = events.pop() || "";
|
||||
|
||||
for (const event of events) {
|
||||
for (const line of event.split("\n")) {
|
||||
const DATA_START = "data: ";
|
||||
|
||||
if (!line.startsWith(DATA_START)) continue;
|
||||
const payload = line.slice(DATA_START.length);
|
||||
|
||||
if (payload === "[DONE]") {
|
||||
yield { content: "", done: true };
|
||||
return;
|
||||
}
|
||||
|
||||
const chunk = safeParse<DeepSeekChunk>(payload);
|
||||
|
||||
if (!chunk?.choices?.[0]?.delta) continue;
|
||||
|
||||
const delta = chunk.choices[0].delta;
|
||||
|
||||
// Yield content if present
|
||||
if (delta.content) {
|
||||
yield {
|
||||
content: delta.content,
|
||||
reasoning: delta.reasoning_content,
|
||||
done: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Yield reasoning separately if present and no content
|
||||
if (delta.reasoning_content && !delta.content) {
|
||||
yield {
|
||||
content: "",
|
||||
reasoning: delta.reasoning_content,
|
||||
done: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user