UNPKG

agents

Version:

A home for your AI agents

225 lines (224 loc) 7.47 kB
import { t as applyChunkToParts } from "./message-builder-BymO4N_D.js"; //#region src/chat/stream-accumulator.ts function asMetadata(value) { if (value != null && typeof value === "object" && !Array.isArray(value)) return value; } var StreamAccumulator = class { constructor(options) { this.messageId = options.messageId; this._isContinuation = options.continuation ?? false; this.parts = options.existingParts ? [...options.existingParts] : []; this.metadata = options.existingMetadata ? { ...options.existingMetadata } : void 0; this._pendingContinuationChunks = this._isContinuation && options.existingParts === void 0 && options.existingMetadata === void 0 ? [] : null; } applyChunk(chunk) { this._pendingContinuationChunks?.push(chunk); const handled = applyChunkToParts(this.parts, chunk); if (chunk.type === "tool-approval-request" && chunk.toolCallId) return { handled, action: { type: "tool-approval-request", toolCallId: chunk.toolCallId } }; if ((chunk.type === "tool-output-available" || chunk.type === "tool-output-error") && chunk.toolCallId) { if (!this.parts.some((p) => "toolCallId" in p && p.toolCallId === chunk.toolCallId)) return { handled, action: { type: "cross-message-tool-update", updateType: chunk.type === "tool-output-available" ? "output-available" : "output-error", toolCallId: chunk.toolCallId, output: chunk.output, errorText: chunk.errorText, preliminary: chunk.preliminary } }; } if (!handled) switch (chunk.type) { case "start": { if (chunk.messageId != null && !this._isContinuation) this.messageId = chunk.messageId; const startMeta = asMetadata(chunk.messageMetadata); if (startMeta) this.metadata = this.metadata ? { ...this.metadata, ...startMeta } : { ...startMeta }; return { handled: true, action: { type: "start", messageId: chunk.messageId, metadata: startMeta } }; } case "finish": { const finishMeta = asMetadata(chunk.messageMetadata); if (finishMeta) this.metadata = this.metadata ? { ...this.metadata, ...finishMeta } : { ...finishMeta }; return { handled: true, action: { type: "finish", finishReason: "finishReason" in chunk ? chunk.finishReason : void 0, metadata: finishMeta } }; } case "message-metadata": { const msgMeta = asMetadata(chunk.messageMetadata); if (msgMeta) this.metadata = this.metadata ? { ...this.metadata, ...msgMeta } : { ...msgMeta }; return { handled: true, action: { type: "message-metadata", metadata: msgMeta ?? {} } }; } case "finish-step": return { handled: true }; case "error": return { handled: true, action: { type: "error", error: chunk.errorText ?? JSON.stringify(chunk) } }; } return { handled }; } /** Snapshot the current state as a UIMessage. */ toMessage() { return { id: this.messageId, role: "assistant", parts: [...this.parts], ...this.metadata != null && { metadata: this.metadata } }; } /** * Merge this accumulator's message into an existing message array. * Handles continuation (walk backward for last assistant), replacement * (update existing by messageId), or append (new message). An unseeded * continuation adopts current parts here before applying its queued chunks. */ mergeInto(messages) { let existingIdx = messages.findIndex((m) => m.id === this.messageId); if (existingIdx < 0 && this._isContinuation) { for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "assistant") { existingIdx = i; break; } } if (this._pendingContinuationChunks !== null) { const pendingChunks = this._pendingContinuationChunks; this._pendingContinuationChunks = null; this.parts.splice(0, this.parts.length, ...existingIdx >= 0 ? messages[existingIdx].parts : []); const currentMetadata = existingIdx >= 0 ? asMetadata(messages[existingIdx].metadata) : void 0; this.metadata = currentMetadata ? { ...currentMetadata } : void 0; if (existingIdx >= 0) this.messageId = messages[existingIdx].id; for (const chunk of pendingChunks) this.applyChunk(chunk); } const partialMessage = { id: existingIdx >= 0 ? messages[existingIdx].id : this.messageId, role: "assistant", parts: [...this.parts], ...this.metadata != null && { metadata: this.metadata } }; if (existingIdx >= 0) { const updated = [...messages]; updated[existingIdx] = partialMessage; return updated; } return [...messages, partialMessage]; } }; //#endregion //#region src/chat/broadcast-state.ts function transition(state, event) { switch (event.type) { case "clear": return { state: { status: "idle" }, isStreaming: false }; case "resume-fallback": { const accumulator = new StreamAccumulator({ messageId: event.messageId }); return { state: { status: "observing", streamId: event.streamId, accumulator }, isStreaming: true }; } case "response": { let accumulator; const isReplayedStart = event.replay === true && event.chunkData?.type === "start"; if (state.status === "idle" || state.streamId !== event.streamId || isReplayedStart) accumulator = new StreamAccumulator({ messageId: event.messageId, continuation: event.continuation }); else accumulator = state.accumulator; if (event.chunkData) accumulator.applyChunk(event.chunkData); let messagesUpdate; if (event.done) { messagesUpdate = (prev) => accumulator.mergeInto(prev); return { state: { status: "idle" }, messagesUpdate, isStreaming: false }; } if (event.chunkData && !event.replay) messagesUpdate = (prev) => accumulator.mergeInto(prev); else if (event.replayComplete) messagesUpdate = (prev) => accumulator.mergeInto(prev); return { state: { status: "observing", streamId: event.streamId, accumulator }, messagesUpdate, isStreaming: true }; } } } //#endregion //#region src/chat/protocol.ts /** * Wire protocol message type constants for the cf_agent_chat_* protocol. * * These are the string values used on the wire between agent servers and * clients. Both @cloudflare/ai-chat (via its MessageType enum) and * @cloudflare/think use these values. */ const STREAM_RESUME_NONE_REASONS = { /** No active, pending, or terminal stream exists for this agent. */ IDLE: "idle", /** An active tool continuation is owned by another live connection. */ CONTINUATION_OWNED: "continuation-owned" }; const CHAT_MESSAGE_TYPES = { CHAT_MESSAGES: "cf_agent_chat_messages", USE_CHAT_REQUEST: "cf_agent_use_chat_request", USE_CHAT_RESPONSE: "cf_agent_use_chat_response", CHAT_CLEAR: "cf_agent_chat_clear", CHAT_REQUEST_CANCEL: "cf_agent_chat_request_cancel", STREAM_RESUMING: "cf_agent_stream_resuming", STREAM_RESUME_ACK: "cf_agent_stream_resume_ack", STREAM_RESUME_REQUEST: "cf_agent_stream_resume_request", STREAM_RESUME_NONE: "cf_agent_stream_resume_none", STREAM_PENDING: "cf_agent_stream_pending", TOOL_RESULT: "cf_agent_tool_result", TOOL_APPROVAL: "cf_agent_tool_approval", MESSAGE_UPDATED: "cf_agent_message_updated", CHAT_RECOVERING: "cf_agent_chat_recovering" }; //#endregion export { StreamAccumulator as i, STREAM_RESUME_NONE_REASONS as n, transition as r, CHAT_MESSAGE_TYPES as t }; //# sourceMappingURL=protocol-B0nh6KNf.js.map