UNPKG

@maximai/maxim-js

Version:

Maxim AI JS SDK. Visit https://getmaxim.ai for more info.

387 lines 18.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parsePromptMessagesV3 = parsePromptMessagesV3; exports.processToolResultsFromPromptV3 = processToolResultsFromPromptV3; exports.convertDoGenerateResultToChatCompletionResultV3 = convertDoGenerateResultToChatCompletionResultV3; exports.processStreamV3 = processStreamV3; const utils_1 = require("../utils"); const uuid_1 = require("uuid"); /** * Converts a LanguageModelV3Prompt into an array of CompletionRequest or ChatCompletionMessage objects. * * This function transforms the structured prompt format used by the Vercel AI SDK v3 into the message format expected by downstream consumers, handling system, user, assistant, and tool roles. * It also extracts file attachments from file messages. * * @param prompt - The prompt to be parsed, consisting of structured message parts. * @returns An object containing parsed messages and extracted file attachments. * @throws If an unsupported user message type is encountered. */ function parsePromptMessagesV3(prompt) { const attachments = []; const promptMessages = prompt .map((promptMsg) => { var _a, _b, _c, _d, _e; switch (promptMsg.role) { case "system": { return [ { role: "system", content: promptMsg.content, }, ]; } case "user": { const contentItems = []; for (const msg of promptMsg.content) { switch (msg.type) { case "text": contentItems.push({ type: "text", text: msg.text, }); break; case "file": { // Handle different data types: Uint8Array, string (base64), or URL let attachment; // Check if data is a URL object if (msg.data instanceof URL || (typeof msg.data === "object" && msg.data !== null && "href" in msg.data)) { // If it's a URL, create a URL attachment const urlString = msg.data instanceof URL ? msg.data.toString() : msg.data.href; attachment = { id: (0, uuid_1.v4)(), type: "url", url: urlString, mimeType: msg.mediaType, tags: { attachedTo: "input" }, }; } else if (typeof msg.data === "string") { // Convert base64 string to Buffer let fileData; // If it's a base64 string, decode it if (msg.data.startsWith("data:")) { const match = msg.data.match(/^data:([^;]+);base64,(.+)$/); if (match) { const base64Data = match[2]; fileData = Buffer.from(base64Data, "base64"); } else { // Assume it's already base64 without the data URI prefix fileData = Buffer.from(msg.data, "base64"); } } else { // Assume it's base64 encoded fileData = Buffer.from(msg.data, "base64"); } // Extract file extension from mediaType if possible const mediaTypeParts = ((_a = msg.mediaType) === null || _a === void 0 ? void 0 : _a.split("/")) || []; const extension = ((_b = mediaTypeParts[1]) === null || _b === void 0 ? void 0 : _b.split(";")[0]) || "bin"; // Create fileData attachment attachment = { id: (0, uuid_1.v4)(), type: "fileData", data: fileData, mimeType: msg.mediaType, name: `file.${extension}`, tags: { attachedTo: "input" }, }; } else { // It's a Uint8Array, convert to Buffer const fileData = Buffer.from(msg.data); // Extract file extension from mediaType if possible const mediaTypeParts = ((_c = msg.mediaType) === null || _c === void 0 ? void 0 : _c.split("/")) || []; const extension = ((_d = mediaTypeParts[1]) === null || _d === void 0 ? void 0 : _d.split(";")[0]) || "bin"; // Create fileData attachment attachment = { id: (0, uuid_1.v4)(), type: "fileData", data: fileData, mimeType: msg.mediaType, name: `file.${extension}`, tags: { attachedTo: "input" }, }; } attachments.push(attachment); // Don't include file content in the message - it's now an attachment // The parseAttachmentsFromMessages function will handle image_url types, // but we've already extracted the file data, so we skip adding it to content break; } default: throw new Error(`Unsupported user message type: ${msg}`); } } // Only create user message if there's content (text or other non-file items) if (contentItems.length > 0) { return [ { role: "user", content: contentItems, }, ]; } else { // If all content was files, return empty string content return [ { role: "user", content: "", }, ]; } } case "assistant": { const assistantText = promptMsg.content.find((msg) => msg.type === "text"); const assistantToolCalls = promptMsg.content.filter((msg) => msg.type === "tool-call"); return [ { role: "assistant", content: (_e = assistantText === null || assistantText === void 0 ? void 0 : assistantText.text) !== null && _e !== void 0 ? _e : null, tool_calls: assistantToolCalls.map((tool) => ({ id: tool.toolCallId, type: "function", function: { name: tool.toolName, arguments: JSON.stringify(tool.input), }, })), }, ]; } case "tool": { const toolCalls = promptMsg.content.filter((part) => part.type === "tool-result"); return [ ...toolCalls.map((tool) => ({ role: "tool", tool_call_id: tool.toolCallId, content: (0, utils_1.parseToolResultOutput)(tool.output), })), ]; } } }) .flat(); return { messages: promptMessages, attachments }; } /** * Processes tool results from the raw prompt and logs them to Maxim. * Calls toolCallError for error-type results (error-text, error-json) and toolCallResult for successes. * * @param prompt - The raw LanguageModelV3 prompt containing tool results * @param logger - The MaximLogger instance for logging tool results/errors */ function processToolResultsFromPromptV3(prompt, logger) { for (const promptMsg of prompt) { if (promptMsg.role !== "tool") continue; for (const part of promptMsg.content) { if (part.type !== "tool-result") continue; const toolCallId = part.toolCallId; const output = part.output; const isError = output.type === "error-text" || output.type === "error-json"; if (isError) { const errorInfo = (0, utils_1.extractErrorInfo)(output.value); logger.toolCallError(toolCallId, errorInfo); } else { const content = (0, utils_1.parseToolResultOutput)(output); logger.toolCallResult(toolCallId, content); } } } } /** * Converts a doGenerate result object into a ChatCompletionResult format. * * This function adapts the result of a language model generation v3 (including token usage, model info, and choices) into the standardized ChatCompletionResult structure expected by downstream consumers. * * @param result - The result object from a generation call, including usage, response, and content fields. * @returns The formatted chat completion result, including id, model, choices, and token usage. */ function convertDoGenerateResultToChatCompletionResultV3(result) { var _a, _b, _c, _d, _e, _f; return { id: (0, uuid_1.v4)(), object: "chat_completion", created: Math.floor(Date.now() / 1000), model: (_b = (_a = result.response) === null || _a === void 0 ? void 0 : _a.modelId) !== null && _b !== void 0 ? _b : "unknown", choices: result.content.map((content, index) => { switch (content.type) { case "text": return { index, message: { content: content.text, role: "assistant", }, finish_reason: result.finishReason.unified, logprobs: null, }; case "file": return { index, message: { content: content.data, role: "assistant", }, finish_reason: result.finishReason.unified, logprobs: null, }; case "tool-call": return { index, logprobs: null, message: { content: null, role: "assistant", tool_calls: [ { id: content.toolCallId, type: "function", function: { name: content.toolName, arguments: content.input, }, }, ], }, finish_reason: result.finishReason.unified, }; case "tool-result": return { index, logprobs: null, message: { content: typeof content.result === "string" ? content.result : JSON.stringify(content.result), role: "assistant", }, finish_reason: result.finishReason.unified, }; case "source": return { index, logprobs: null, message: { content: content.sourceType === "url" ? content.url : content.title, role: "assistant", }, finish_reason: result.finishReason.unified, }; default: return { index, logprobs: null, message: { content: JSON.stringify(content), role: "assistant", }, finish_reason: result.finishReason.unified, }; } }), usage: { prompt_tokens: (_c = result.usage.inputTokens.total) !== null && _c !== void 0 ? _c : 0, completion_tokens: (_d = result.usage.outputTokens.total) !== null && _d !== void 0 ? _d : 0, total_tokens: ((_e = result.usage.inputTokens.total) !== null && _e !== void 0 ? _e : 0) + ((_f = result.usage.outputTokens.total) !== null && _f !== void 0 ? _f : 0), }, }; } /** * Processes a stream of language model output chunks and logs the result to Maxim tracing. * * This function aggregates streamed output parts, constructs a chat completion result, and finalizes the generation, span, and trace as appropriate. It also handles errors and ensures proper cleanup of tracing resources. * * @param chunks - The array of streamed output parts from the language model. * @param span - The Maxim tracing span associated with this generation. * @param trace - The Maxim tracing trace associated with this generation. * @param generation - The Maxim generation object to log the result to. * @param model - The model identifier used for this generation. * @param maximMetadata - Optional Maxim metadata for advanced tracing. */ function processStreamV3(chunks, span, trace, generation, model, maximMetadata) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; try { const result = processChunksV3(chunks); generation.result({ id: (0, uuid_1.v4)(), object: "chat_completion", created: Math.floor(Date.now() / 1000), model: model, choices: [ { index: 0, message: { tool_calls: result.toolCalls.map((toolCall) => ({ id: toolCall.toolCallId, type: toolCall.type, function: { name: toolCall.toolName, arguments: toolCall.input, }, })), content: result.text, role: "assistant", }, finish_reason: (_a = result.finishReason) !== null && _a !== void 0 ? _a : "stop", logprobs: null, }, ], usage: { prompt_tokens: (_c = (_b = result.usage) === null || _b === void 0 ? void 0 : _b.promptTokens) !== null && _c !== void 0 ? _c : 0, completion_tokens: (_e = (_d = result.usage) === null || _d === void 0 ? void 0 : _d.completionTokens) !== null && _e !== void 0 ? _e : 0, total_tokens: ((_g = (_f = result.usage) === null || _f === void 0 ? void 0 : _f.promptTokens) !== null && _g !== void 0 ? _g : 0) + ((_j = (_h = result.usage) === null || _h === void 0 ? void 0 : _h.completionTokens) !== null && _j !== void 0 ? _j : 0), }, }); generation.end(); } catch (error) { generation.error({ message: error.message, }); console.error("[Maxim SDK] Logging failed:", error); } finally { span.end(); // Note: Trace ending is now handled by the wrapper to support tool-call sequences } } /** * Processes an array of streamed language model output chunks into a structured result. * * This function aggregates text, tool calls, token usage, and finish reason from the provided stream parts, returning a single object summarizing the output of the language model stream. * * @param chunks - The array of streamed output parts from the language model. * @returns An object containing the aggregated text, tool calls, token usage, and finish reason. */ function processChunksV3(chunks) { var _a, _b; let text = ""; const toolCalls = {}; let usage = undefined; let finishReason = undefined; for (const chunk of chunks) { switch (chunk.type) { case "text-delta": text += chunk.delta; break; case "tool-call": toolCalls[chunk.toolCallId] = chunk; break; case "tool-result": text += typeof chunk.result === "string" ? chunk.result : JSON.stringify(chunk.result); break; case "finish": usage = { promptTokens: (_a = chunk.usage.inputTokens.total) !== null && _a !== void 0 ? _a : 0, completionTokens: (_b = chunk.usage.outputTokens.total) !== null && _b !== void 0 ? _b : 0, }; finishReason = chunk.finishReason.unified; break; } } return { text, toolCalls: Object.values(toolCalls), usage, finishReason }; } //# sourceMappingURL=utils.js.map