UNPKG

@tanstack/ai

Version:

Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.

91 lines (90 loc) • 3.06 kB
//#region src/utilities/block-order.ts /** * The order map for these entries in produced order, or undefined when they * are in the default order (all thinking, then text, then tool calls). * Adjacent text entries merge into one, and empty text is left out. The n-th * thinking entry gets index n. */ function buildBlockOrder(entries) { const blocks = []; let thinking = 0; let isDefault = true; for (const entry of entries) { const last = blocks.at(-1); if (entry.type === "thinking") { if (last !== void 0 && last.type !== "thinking") isDefault = false; blocks.push({ type: "thinking", index: thinking++ }); } else if (entry.type === "text") { if (entry.text === "") continue; if (last?.type === "text") { last.length += entry.text.length; continue; } if (last?.type === "tool-call") isDefault = false; blocks.push({ type: "text", length: entry.text.length }); } else blocks.push({ type: "tool-call", id: entry.id }); } return isDefault ? void 0 : blocks; } /** * The blocks of an assistant message in map order, or undefined when the * message has no map or the map does not match the message. A map matches * when it uses each `thinking` entry and each `toolCalls` entry exactly once, * and its text lengths add up to the length of the text content (`null` * content counts as no text). Readers use the default order when this * returns undefined. */ function orderedAssistantBlocks(message) { const order = message.blockOrder; if (!Array.isArray(order) || order.length === 0) return void 0; const content = message.content ?? ""; if (typeof content !== "string") return void 0; const thinking = message.thinking ?? []; const toolCalls = message.toolCalls ?? []; const usedThinking = /* @__PURE__ */ new Set(); const usedCalls = /* @__PURE__ */ new Set(); const blocks = []; let offset = 0; for (const block of order) { if (typeof block !== "object" || block === null) return void 0; if (block.type === "thinking") { const entry = Number.isInteger(block.index) ? thinking[block.index] : void 0; if (entry === void 0 || usedThinking.has(block.index)) return; usedThinking.add(block.index); blocks.push({ type: "thinking", thinking: entry }); } else if (block.type === "text") { const end = offset + block.length; if (!Number.isInteger(block.length) || block.length <= 0 || end > content.length) return; blocks.push({ type: "text", text: content.slice(offset, end) }); offset = end; } else if (block.type === "tool-call") { const toolCall = toolCalls.find((candidate) => candidate.id === block.id); if (toolCall === void 0 || usedCalls.has(block.id)) return void 0; usedCalls.add(block.id); blocks.push({ type: "tool-call", toolCall }); } else return; } if (offset !== content.length || usedThinking.size !== thinking.length || usedCalls.size !== toolCalls.length) return; return blocks; } //#endregion export { buildBlockOrder, orderedAssistantBlocks }; //# sourceMappingURL=block-order.js.map