import {Post, Topic} from "./topics"; import {Settings} from "./settings"; import {generateUUID} from "./uuids"; import {tokenizeTopic, tokensToPosts, tokensToTopic} from "./model"; // import {replaceSmileysInText} from "./smileys"; // // try { // console.log(replaceSmileysInText("lol")) // }catch(e) {} // @see https://github.com/openai/openai-node/blob/14784f95797d4d525dafecfd4ec9c7a133540da0/src/resources/chat/completions.ts type OobaboogaStreamChunk = { id: string; // Unique identifier for the chunk object: string; // The type of the chunk, e.g., "text_completion.chunk" created: number; // Unix timestamp of when the chunk was created model: string; // Name or identifier of the model generating the completion choices: { index: number; // The index of the choice in the completion finish_reason: string | null; // Reason why the completion stopped, or null if still in progress text: string; // The generated text for this chunk logprobs: { top_logprobs: Record[]; // Log probabilities for the top tokens, as an array of key-value pairs }; }[]; usage?: { prompt_tokens: number; // Number of tokens in the prompt completion_tokens: number; // Number of tokens generated in the completion total_tokens: number; // Total tokens used }; }; export async function generateTopic(settings: Settings, nPosts: number): Promise { console.log(settings); const rawOutput = await fetApiWithStream(settings, "<|start_header_id|>", nPosts); // const rawOutput = await fetApi(settings); // console.log(rawOutput); // let rawOutput = "rawOutput"; return tokensToTopic(rawOutput); } export async function generatePosts(settings: Settings, nPosts: number, topic: Topic): Promise { // console.log(settings); const rawOutput = await fetApiWithStream(settings, tokenizeTopic(topic), nPosts); // const rawOutput = await fetApi(settings); // console.log(rawOutput); // let rawOutput = "rawOutput"; console.log("rawOutput"); console.log(rawOutput); return tokensToPosts(rawOutput); } async function fetApi(settings: Settings): Promise { const response = await fetch(new URL("/v1/completions", settings.apiURL), { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ prompt: "<|start_header_id|>", temperature: settings.temperature, max_tokens: 1000, stream: false, skip_special_tokens: false, stop: "<|end_of_post|>" // top_p: 1, // frequency_penalty: 0, // presence_penalty: 0, }), }); if (response.status !== 200) { throw new Error(`Failed to fetch API (${response.status}): ${response.statusText}`); } const json = await response.json(); console.log(json) return json.choices[0].text; } const postEndToken = "<|end_of_post|>"; // @see https://github.com/openai/openai-node/issues/18 // nPosts: number of post before stop async function fetApiWithStream(settings: Settings, prompt: string, nPosts: number): Promise { const controller = new AbortController() const response = await fetch(new URL("/v1/completions", settings.apiURL), { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ prompt, temperature: settings.temperature, max_tokens: 2000, stream: true, skip_special_tokens: false, // stop: "<|end_of_post|>" // top_p: 1, // frequency_penalty: 0, // presence_penalty: 0, }), signal: controller.signal, }); if (!response.ok) { throw new Error(`Failed to fetch API (${response.status} ${response.statusText}): ${await response.text()}`); } // console.log("Streaming !!!!"); // // const decoderStream = new TextDecoderStream("utf-8"); // const writer = new WritableStream({ // write(rawChunk: string) { // // output.innerHTML += chunk; // const chunk = JSON.parse(rawChunk.trimStart().slice(6)) as OobaboogaStreamChunk; // remove "data: " and parse // console.log(chunk) // } // }); console.log(`Fetching topic with ${nPosts} posts...`); let endTokenCount = 0; let tokens = ""; // Dont know why but the first token is skipped let finishReason: string | null = null; try { await response.body.pipeThrough(new TextDecoderStream("utf-8")).pipeTo(new WritableStream({ write(rawChunk: string) { // chunk can contains multiple lines, one chunk data per line for (const rawChunkLine of rawChunk.split("\n")) { if (!rawChunkLine.startsWith("data:")) continue; const chunk = JSON.parse(rawChunkLine.slice(6)) as OobaboogaStreamChunk; // remove "data: " and parse const text = chunk.choices[0].text; console.log(text) tokens += chunk.choices[0].text; if (text.includes(postEndToken)) { endTokenCount++; if(endTokenCount >= nPosts) { finishReason = "custom_stop"; controller.abort(); break; } } else { finishReason = chunk.choices[0].finish_reason; } } // output.innerHTML += chunk; // console.log("----") // console.log(rawChunk) // console.log(rawChunk.slice(6).trimEnd()) // console.log(chunk.choices[0].text) // tokens += chunk.choices[0].text; } })); } catch (e) { if (e.name !== 'AbortError') { throw e; } } console.log("Done fetching data") console.log(`Finish reason: ${finishReason}`) console.log(`Tokens: ${tokens}`) return tokens; }