@ai-sdk/ui-utils
Advanced tools
Comparing version 1.1.6 to 1.1.7
# @ai-sdk/ui-utils | ||
## 1.1.7 | ||
### Patch Changes | ||
- 0d2d9bf: fix (ui): single assistant message with multiple tool steps | ||
## 1.1.6 | ||
@@ -4,0 +10,0 @@ |
@@ -67,9 +67,16 @@ import { LanguageModelV1FinishReason, JSONValue as JSONValue$1 } from '@ai-sdk/provider'; | ||
Once the call is complete, the invocation is a tool result. | ||
The step is used to track how to map an assistant UI message with many tool invocations | ||
back to a sequence of LLM assistant/tool result message pairs. | ||
It is optional for backwards compatibility. | ||
*/ | ||
type ToolInvocation = ({ | ||
state: 'partial-call'; | ||
step?: number; | ||
} & ToolCall<string, any>) | ({ | ||
state: 'call'; | ||
step?: number; | ||
} & ToolCall<string, any>) | ({ | ||
state: 'result'; | ||
step?: number; | ||
} & ToolResult<string, any, any>); | ||
@@ -403,3 +410,3 @@ /** | ||
declare const getOriginalFetch$1: () => typeof fetch; | ||
declare function callChatApi({ api, body, streamProtocol, credentials, headers, abortController, restoreMessagesOnFailure, onResponse, onUpdate, onFinish, onToolCall, generateId, fetch, }: { | ||
declare function callChatApi({ api, body, streamProtocol, credentials, headers, abortController, restoreMessagesOnFailure, onResponse, onUpdate, onFinish, onToolCall, generateId, fetch, lastMessage, }: { | ||
api: string; | ||
@@ -413,3 +420,7 @@ body: Record<string, any>; | ||
onResponse: ((response: Response) => void | Promise<void>) | undefined; | ||
onUpdate: (newMessages: Message[], data: JSONValue[] | undefined) => void; | ||
onUpdate: (options: { | ||
message: Message; | ||
data: JSONValue[] | undefined; | ||
replaceLastMessage: boolean; | ||
}) => void; | ||
onFinish: UseChatOptions['onFinish']; | ||
@@ -419,2 +430,3 @@ onToolCall: UseChatOptions['onToolCall']; | ||
fetch: ReturnType<typeof getOriginalFetch$1> | undefined; | ||
lastMessage: Message | undefined; | ||
}): Promise<void>; | ||
@@ -565,2 +577,4 @@ | ||
declare function extractMaxToolInvocationStep(toolInvocations: ToolInvocation[] | undefined): number | undefined; | ||
/** | ||
@@ -683,2 +697,2 @@ * Performs a deep-equal comparison of two parsed JSON objects. | ||
export { type AssistantMessage, type AssistantStatus, type AssistantStreamPart, type AssistantStreamString, type Attachment, type ChatRequest, type ChatRequestOptions, type CreateMessage, type DataMessage, type DataStreamPart, type DataStreamString, type DeepPartial, type IdGenerator, type JSONValue, type Message, type RequestOptions, type Schema, type ToolInvocation, type UseAssistantOptions, type UseChatOptions, type UseCompletionOptions, asSchema, callChatApi, callCompletionApi, formatAssistantStreamPart, formatDataStreamPart, getTextFromDataUrl, isDeepEqualData, jsonSchema, parseAssistantStreamPart, parseDataStreamPart, parsePartialJson, prepareAttachmentsForRequest, processAssistantStream, processDataStream, processTextStream, zodSchema }; | ||
export { type AssistantMessage, type AssistantStatus, type AssistantStreamPart, type AssistantStreamString, type Attachment, type ChatRequest, type ChatRequestOptions, type CreateMessage, type DataMessage, type DataStreamPart, type DataStreamString, type DeepPartial, type IdGenerator, type JSONValue, type Message, type RequestOptions, type Schema, type ToolInvocation, type UseAssistantOptions, type UseChatOptions, type UseCompletionOptions, asSchema, callChatApi, callCompletionApi, extractMaxToolInvocationStep, formatAssistantStreamPart, formatDataStreamPart, getTextFromDataUrl, isDeepEqualData, jsonSchema, parseAssistantStreamPart, parseDataStreamPart, parsePartialJson, prepareAttachmentsForRequest, processAssistantStream, processDataStream, processTextStream, zodSchema }; |
@@ -36,2 +36,3 @@ "use strict"; | ||
callCompletionApi: () => callCompletionApi, | ||
extractMaxToolInvocationStep: () => extractMaxToolInvocationStep, | ||
formatAssistantStreamPart: () => formatAssistantStreamPart, | ||
@@ -869,11 +870,20 @@ formatDataStreamPart: () => formatDataStreamPart, | ||
generateId: generateId2 = import_provider_utils2.generateId, | ||
getCurrentDate = () => /* @__PURE__ */ new Date() | ||
getCurrentDate = () => /* @__PURE__ */ new Date(), | ||
lastMessage | ||
}) { | ||
const createdAt = getCurrentDate(); | ||
let currentMessage = void 0; | ||
let createNewMessage = true; | ||
let newMessageId = void 0; | ||
const previousMessages = []; | ||
var _a, _b; | ||
const replaceLastMessage = (lastMessage == null ? void 0 : lastMessage.role) === "assistant"; | ||
let step = replaceLastMessage ? 1 + // find max step in existing tool invocations: | ||
((_b = (_a = lastMessage.toolInvocations) == null ? void 0 : _a.reduce((max, toolInvocation) => { | ||
var _a2; | ||
return Math.max(max, (_a2 = toolInvocation.step) != null ? _a2 : 0); | ||
}, 0)) != null ? _b : 0) : 0; | ||
const message = replaceLastMessage ? structuredClone(lastMessage) : { | ||
id: generateId2(), | ||
createdAt: getCurrentDate(), | ||
role: "assistant", | ||
content: "" | ||
}; | ||
const data = []; | ||
let messageAnnotations = void 0; | ||
let messageAnnotations = replaceLastMessage ? lastMessage == null ? void 0 : lastMessage.annotations : void 0; | ||
const partialToolCalls = {}; | ||
@@ -888,8 +898,4 @@ let usage = { | ||
const copiedData = [...data]; | ||
if (currentMessage == null) { | ||
update(previousMessages, copiedData); | ||
return; | ||
} | ||
if (messageAnnotations == null ? void 0 : messageAnnotations.length) { | ||
currentMessage.annotations = messageAnnotations; | ||
message.annotations = messageAnnotations; | ||
} | ||
@@ -899,3 +905,3 @@ const copiedMessage = { | ||
// with SolidJS. SolidJS uses referential integration of sub-objects to detect changes. | ||
...JSON.parse(JSON.stringify(currentMessage)), | ||
...structuredClone(message), | ||
// add a revision id to ensure that the message is updated with SWR. SWR uses a | ||
@@ -906,55 +912,34 @@ // hashing approach by default to detect changes, but it only works for shallow | ||
// forwarded to rendering): | ||
revisionId: generateId2(), | ||
// Fill in createdAt to retain Date object (lost in JSON.parse): | ||
createdAt: currentMessage.createdAt | ||
revisionId: generateId2() | ||
}; | ||
update([...previousMessages, copiedMessage], copiedData); | ||
update({ | ||
message: copiedMessage, | ||
data: copiedData, | ||
replaceLastMessage | ||
}); | ||
} | ||
function getMessage() { | ||
if (createNewMessage || currentMessage == null) { | ||
if (currentMessage != null) { | ||
previousMessages.push(currentMessage); | ||
} | ||
currentMessage = { | ||
id: newMessageId != null ? newMessageId : generateId2(), | ||
role: "assistant", | ||
content: "", | ||
createdAt | ||
}; | ||
createNewMessage = false; | ||
newMessageId = void 0; | ||
} | ||
return currentMessage; | ||
} | ||
await processDataStream({ | ||
stream, | ||
onTextPart(value) { | ||
const activeMessage = getMessage(); | ||
currentMessage = { | ||
...activeMessage, | ||
content: activeMessage.content + value | ||
}; | ||
message.content += value; | ||
execUpdate(); | ||
}, | ||
onReasoningPart(value) { | ||
var _a; | ||
const activeMessage = getMessage(); | ||
currentMessage = { | ||
...activeMessage, | ||
reasoning: ((_a = activeMessage.reasoning) != null ? _a : "") + value | ||
}; | ||
var _a2; | ||
message.reasoning = ((_a2 = message.reasoning) != null ? _a2 : "") + value; | ||
execUpdate(); | ||
}, | ||
onToolCallStreamingStartPart(value) { | ||
const activeMessage = getMessage(); | ||
if (activeMessage.toolInvocations == null) { | ||
activeMessage.toolInvocations = []; | ||
if (message.toolInvocations == null) { | ||
message.toolInvocations = []; | ||
} | ||
partialToolCalls[value.toolCallId] = { | ||
text: "", | ||
step, | ||
toolName: value.toolName, | ||
index: activeMessage.toolInvocations.length | ||
index: message.toolInvocations.length | ||
}; | ||
activeMessage.toolInvocations.push({ | ||
message.toolInvocations.push({ | ||
state: "partial-call", | ||
step, | ||
toolCallId: value.toolCallId, | ||
@@ -967,8 +952,8 @@ toolName: value.toolName, | ||
onToolCallDeltaPart(value) { | ||
const activeMessage = getMessage(); | ||
const partialToolCall = partialToolCalls[value.toolCallId]; | ||
partialToolCall.text += value.argsTextDelta; | ||
const { value: partialArgs } = parsePartialJson(partialToolCall.text); | ||
activeMessage.toolInvocations[partialToolCall.index] = { | ||
message.toolInvocations[partialToolCall.index] = { | ||
state: "partial-call", | ||
step: partialToolCall.step, | ||
toolCallId: value.toolCallId, | ||
@@ -981,11 +966,15 @@ toolName: partialToolCall.toolName, | ||
async onToolCallPart(value) { | ||
const activeMessage = getMessage(); | ||
if (partialToolCalls[value.toolCallId] != null) { | ||
activeMessage.toolInvocations[partialToolCalls[value.toolCallId].index] = { state: "call", ...value }; | ||
message.toolInvocations[partialToolCalls[value.toolCallId].index] = { | ||
state: "call", | ||
step, | ||
...value | ||
}; | ||
} else { | ||
if (activeMessage.toolInvocations == null) { | ||
activeMessage.toolInvocations = []; | ||
if (message.toolInvocations == null) { | ||
message.toolInvocations = []; | ||
} | ||
activeMessage.toolInvocations.push({ | ||
message.toolInvocations.push({ | ||
state: "call", | ||
step, | ||
...value | ||
@@ -997,3 +986,8 @@ }); | ||
if (result != null) { | ||
activeMessage.toolInvocations[activeMessage.toolInvocations.length - 1] = { state: "result", ...value, result }; | ||
message.toolInvocations[message.toolInvocations.length - 1] = { | ||
state: "result", | ||
step, | ||
...value, | ||
result | ||
}; | ||
} | ||
@@ -1004,4 +998,3 @@ } | ||
onToolResultPart(value) { | ||
const activeMessage = getMessage(); | ||
const toolInvocations = activeMessage.toolInvocations; | ||
const toolInvocations = message.toolInvocations; | ||
if (toolInvocations == null) { | ||
@@ -1038,6 +1031,8 @@ throw new Error("tool_result must be preceded by a tool_call"); | ||
onFinishStepPart(value) { | ||
createNewMessage = !value.isContinued; | ||
step += 1; | ||
}, | ||
onStartStepPart(value) { | ||
newMessageId = value.messageId; | ||
if (!replaceLastMessage) { | ||
message.id = value.messageId; | ||
} | ||
}, | ||
@@ -1054,3 +1049,3 @@ onFinishMessagePart(value) { | ||
}); | ||
onFinish == null ? void 0 : onFinish({ message: currentMessage, finishReason, usage }); | ||
onFinish == null ? void 0 : onFinish({ message, finishReason, usage }); | ||
} | ||
@@ -1088,3 +1083,4 @@ | ||
generateId: generateId2, | ||
fetch: fetch2 = getOriginalFetch() | ||
fetch: fetch2 = getOriginalFetch(), | ||
lastMessage | ||
}) { | ||
@@ -1133,3 +1129,7 @@ var _a, _b; | ||
resultMessage.content += chunk; | ||
onUpdate([{ ...resultMessage }], []); | ||
onUpdate({ | ||
message: { ...resultMessage }, | ||
data: [], | ||
replaceLastMessage: false | ||
}); | ||
} | ||
@@ -1147,2 +1147,3 @@ }); | ||
update: onUpdate, | ||
lastMessage, | ||
onToolCall, | ||
@@ -1289,2 +1290,10 @@ onFinish({ message, finishReason, usage }) { | ||
// src/extract-max-tool-invocation-step.ts | ||
function extractMaxToolInvocationStep(toolInvocations) { | ||
return toolInvocations == null ? void 0 : toolInvocations.reduce((max, toolInvocation) => { | ||
var _a; | ||
return Math.max(max, (_a = toolInvocation.step) != null ? _a : 0); | ||
}, 0); | ||
} | ||
// src/is-deep-equal-data.ts | ||
@@ -1469,2 +1478,3 @@ function isDeepEqualData(obj1, obj2) { | ||
callCompletionApi, | ||
extractMaxToolInvocationStep, | ||
formatAssistantStreamPart, | ||
@@ -1471,0 +1481,0 @@ formatDataStreamPart, |
{ | ||
"name": "@ai-sdk/ui-utils", | ||
"version": "1.1.6", | ||
"version": "1.1.7", | ||
"license": "Apache-2.0", | ||
@@ -5,0 +5,0 @@ "sideEffects": false, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
347548
3827