UNPKG

@assistant-ui/react

Version:

React components for AI chat.

204 lines 6.28 kB
// src/runtimes/local/LocalThreadRuntimeCore.tsx import { generateId } from "../../internal.mjs"; import { fromCoreMessage } from "../edge/index.mjs"; import { shouldContinue } from "./shouldContinue.mjs"; import { BaseThreadRuntimeCore } from "../core/BaseThreadRuntimeCore.mjs"; var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore { constructor(configProvider, threadId, options) { super(configProvider); this.threadId = threadId; this.setOptions(options); } capabilities = { switchToBranch: true, edit: true, reload: true, cancel: true, unstable_copy: true, speech: false, attachments: false, feedback: false }; abortController = null; isDisabled = false; suggestions = []; get adapters() { return this._options.adapters; } _options; get extras() { return void 0; } setOptions(options) { if (this._options === options) return; this._options = options; let hasUpdates = false; const canSpeak = options.adapters?.speech !== void 0; if (this.capabilities.speech !== canSpeak) { this.capabilities.speech = canSpeak; hasUpdates = true; } const canAttach = options.adapters?.attachments !== void 0; if (this.capabilities.attachments !== canAttach) { this.capabilities.attachments = canAttach; hasUpdates = true; } const canFeedback = options.adapters?.feedback !== void 0; if (this.capabilities.feedback !== canFeedback) { this.capabilities.feedback = canFeedback; hasUpdates = true; } if (hasUpdates) this._notifySubscribers(); } async append(message) { const newMessage = fromCoreMessage(message, { attachments: message.attachments }); this.repository.addOrUpdateMessage(message.parentId, newMessage); const startRun = message.startRun ?? message.role === "user"; if (startRun) { await this.startRun(newMessage.id); } else { this.repository.resetHead(newMessage.id); this._notifySubscribers(); } } async startRun(parentId) { this.repository.resetHead(parentId); const id = generateId(); let message = { id, role: "assistant", status: { type: "running" }, content: [], createdAt: /* @__PURE__ */ new Date() }; this._notifyEventSubscribers("run-start"); do { message = await this.performRoundtrip(parentId, message); } while (shouldContinue(message)); } async performRoundtrip(parentId, message) { const messages = this.repository.getMessages(); this.abortController?.abort(); this.abortController = new AbortController(); const initialContent = message.content; const initialSteps = message.metadata?.steps; const initalCustom = message.metadata?.custom; const updateMessage = (m) => { const newSteps = m.metadata?.steps || m.metadata?.roundtrips; const steps2 = newSteps ? [...initialSteps ?? [], ...newSteps] : void 0; message = { ...message, ...m.content ? { content: [...initialContent, ...m.content ?? []] } : void 0, status: m.status ?? message.status, // TODO deprecated, remove in v0.6 ...steps2 ? { roundtrips: steps2 } : void 0, ...m.metadata ? { metadata: { ...message.metadata, ...steps2 ? { roundtrips: steps2, steps: steps2 } : void 0, ...m.metadata?.custom ? { custom: { ...initalCustom ?? {}, ...m.metadata.custom } } : void 0 } } : void 0 }; this.repository.addOrUpdateMessage(parentId, message); this._notifySubscribers(); }; const maxSteps = this._options.maxSteps ? this._options.maxSteps : (this._options.maxToolRoundtrips ?? 1) + 1; const steps = message.metadata?.steps?.length ?? 0; if (steps >= maxSteps) { updateMessage({ status: { type: "incomplete", reason: "tool-calls" } }); return message; } else { updateMessage({ status: { type: "running" } }); } try { const promiseOrGenerator = this.adapters.chatModel.run({ messages, abortSignal: this.abortController.signal, config: this.getModelConfig(), onUpdate: updateMessage, unstable_assistantMessageId: message.id }); if (Symbol.asyncIterator in promiseOrGenerator) { for await (const r of promiseOrGenerator) { updateMessage(r); } } else { updateMessage(await promiseOrGenerator); } this.abortController = null; if (message.status.type === "running") { updateMessage({ status: { type: "complete", reason: "unknown" } }); } } catch (e) { this.abortController = null; if (e instanceof Error && e.name === "AbortError") { updateMessage({ status: { type: "incomplete", reason: "cancelled" } }); } else { updateMessage({ status: { type: "incomplete", reason: "error", error: e } }); throw e; } } return message; } cancelRun() { this.abortController?.abort(); this.abortController = null; } addToolResult({ messageId, toolCallId, result }) { const messageData = this.repository.getMessage(messageId); const { parentId } = messageData; let { message } = messageData; if (message.role !== "assistant") throw new Error("Tried to add tool result to non-assistant message"); let added = false; let found = false; const newContent = message.content.map((c) => { if (c.type !== "tool-call") return c; if (c.toolCallId !== toolCallId) return c; found = true; if (!c.result) added = true; return { ...c, result }; }); if (!found) throw new Error("Tried to add tool result to non-existing tool call"); message = { ...message, content: newContent }; this.repository.addOrUpdateMessage(parentId, message); if (added && shouldContinue(message)) { this.performRoundtrip(parentId, message); } } }; export { LocalThreadRuntimeCore }; //# sourceMappingURL=LocalThreadRuntimeCore.mjs.map