@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
209 lines • 8.43 kB
JavaScript
export async function* wrapOpenAIStream(stream, options = {}) {
const { includeUsage = true, includeToolCalls = true, emitFunctionCallsAsTokens = false, choiceIndex = 0, } = options;
let usage;
const choiceState = new Map();
const getChoiceState = (index) => {
if (!choiceState.has(index)) {
choiceState.set(index, {
functionCallAccumulator: null,
toolCallsAccumulator: new Map(),
finished: false,
});
}
return choiceState.get(index);
};
try {
for await (const chunk of stream) {
const choices = chunk.choices;
if (!choices || choices.length === 0) {
continue;
}
if (chunk.usage) {
usage = chunk.usage;
}
for (const choice of choices) {
if (!choice)
continue;
const idx = choice.index;
if (choiceIndex !== "all" && idx !== choiceIndex) {
continue;
}
const state = getChoiceState(idx);
const delta = choice.delta;
if (!delta)
continue;
const choicePrefix = choiceIndex === "all" ? `[choice:${idx}]` : "";
if (delta.content) {
yield {
type: "token",
value: choicePrefix
? `${choicePrefix}${delta.content}`
: delta.content,
timestamp: Date.now(),
};
}
if (delta.function_call) {
if (delta.function_call.name) {
state.functionCallAccumulator = {
name: delta.function_call.name,
arguments: delta.function_call.arguments || "",
};
}
else if (delta.function_call.arguments &&
state.functionCallAccumulator) {
state.functionCallAccumulator.arguments +=
delta.function_call.arguments;
}
if (emitFunctionCallsAsTokens && delta.function_call.arguments) {
yield {
type: "token",
value: delta.function_call.arguments,
timestamp: Date.now(),
};
}
}
if (delta.tool_calls) {
for (const toolCall of delta.tool_calls) {
const existing = state.toolCallsAccumulator.get(toolCall.index);
if (toolCall.id || toolCall.function?.name) {
state.toolCallsAccumulator.set(toolCall.index, {
id: toolCall.id || existing?.id || "",
name: toolCall.function?.name || existing?.name || "",
arguments: toolCall.function?.arguments || "",
});
}
else if (toolCall.function?.arguments && existing) {
existing.arguments += toolCall.function.arguments;
}
if (emitFunctionCallsAsTokens && toolCall.function?.arguments) {
yield {
type: "token",
value: toolCall.function.arguments,
timestamp: Date.now(),
};
}
}
}
if (choice.finish_reason && !state.finished) {
state.finished = true;
if (state.functionCallAccumulator && includeToolCalls) {
yield {
type: "message",
value: JSON.stringify({
type: "function_call",
function_call: state.functionCallAccumulator,
...(choiceIndex === "all" ? { choiceIndex: idx } : {}),
}),
role: "assistant",
timestamp: Date.now(),
};
}
if (state.toolCallsAccumulator.size > 0 && includeToolCalls) {
const toolCalls = Array.from(state.toolCallsAccumulator.values());
yield {
type: "message",
value: JSON.stringify({
type: "tool_calls",
tool_calls: toolCalls,
...(choiceIndex === "all" ? { choiceIndex: idx } : {}),
}),
role: "assistant",
timestamp: Date.now(),
};
}
}
}
}
yield {
type: "complete",
timestamp: Date.now(),
...(includeUsage && usage ? { usage } : {}),
};
}
catch (error) {
yield {
type: "error",
error: error instanceof Error ? error : new Error(String(error)),
timestamp: Date.now(),
};
}
}
export function openaiStream(client, params, options) {
return async () => {
const stream = await client.chat.completions.create({
...params,
stream: true,
});
return wrapOpenAIStream(stream, options);
};
}
export function openaiText(client, model, prompt, options) {
const messages = typeof prompt === "string" ? [{ role: "user", content: prompt }] : prompt;
const { includeUsage, includeToolCalls, emitFunctionCallsAsTokens, ...chatParams } = options || {};
return openaiStream(client, { model, messages, ...chatParams }, { includeUsage, includeToolCalls, emitFunctionCallsAsTokens });
}
export function openaiJSON(client, model, prompt, options) {
const messages = typeof prompt === "string" ? [{ role: "user", content: prompt }] : prompt;
const { includeUsage, includeToolCalls, emitFunctionCallsAsTokens, ...chatParams } = options || {};
return openaiStream(client, {
model,
messages,
response_format: { type: "json_object" },
...chatParams,
}, { includeUsage, includeToolCalls, emitFunctionCallsAsTokens });
}
export function openaiWithTools(client, model, messages, tools, options) {
const { includeUsage, includeToolCalls, emitFunctionCallsAsTokens, ...chatParams } = options || {};
return openaiStream(client, { model, messages, tools, ...chatParams }, {
includeUsage,
includeToolCalls: includeToolCalls ?? true,
emitFunctionCallsAsTokens,
});
}
export function isOpenAIChunk(obj) {
if (!obj || typeof obj !== "object" || !("choices" in obj)) {
return false;
}
const chunk = obj;
if (!Array.isArray(chunk.choices) || chunk.choices.length === 0) {
return false;
}
const firstChoice = chunk.choices[0];
return firstChoice !== undefined && "delta" in firstChoice;
}
export async function extractOpenAIText(stream) {
let text = "";
for await (const chunk of stream) {
const content = chunk.choices?.[0]?.delta?.content;
if (content) {
text += content;
}
}
return text;
}
export function isOpenAIStream(input) {
if (!input || typeof input !== "object")
return false;
if (!(Symbol.asyncIterator in input))
return false;
const stream = input;
if (typeof stream.toReadableStream === "function" && "controller" in stream) {
return true;
}
if ("response" in stream && typeof stream.toReadableStream === "function") {
return true;
}
return false;
}
export const openaiAdapter = {
name: "openai",
detect: isOpenAIStream,
wrap: wrapOpenAIStream,
};
import { registerAdapter } from "./registry";
try {
registerAdapter(openaiAdapter, { silent: true });
}
catch {
}
//# sourceMappingURL=openai.js.map