feat: upgrade @ai-sdk/react to 2.0.107 and migrate to new API (#126)

- Upgrade @ai-sdk/react from 2.0.22 to 2.0.107
- Migrate from addToolResult to addToolOutput (new API)
- Add output-error state for proper error signaling to model
- Add sendAutomaticallyWhen for auto-retry on tool errors
- Add stop function ref for potential future use

Co-authored-by: dayuan.jiang <jiangdy@amazon.co.jp>
This commit is contained in:
Dayuan Jiang
2025-12-07 00:40:11 +09:00
committed by GitHub
parent 46567cb0b8
commit 6965a54f48
3 changed files with 153 additions and 264 deletions

View File

@@ -1,7 +1,10 @@
"use client" "use client"
import { useChat } from "@ai-sdk/react" import { useChat } from "@ai-sdk/react"
import { DefaultChatTransport } from "ai" import {
DefaultChatTransport,
lastAssistantMessageIsCompleteWithToolCalls,
} from "ai"
import { import {
CheckCircle, CheckCircle,
PanelRightClose, PanelRightClose,
@@ -105,8 +108,18 @@ export default function ChatPanel({
chartXMLRef.current = chartXML chartXMLRef.current = chartXML
}, [chartXML]) }, [chartXML])
const { messages, sendMessage, addToolResult, status, error, setMessages } = // Ref to hold stop function for use in onToolCall (avoids stale closure)
useChat({ const stopRef = useRef<(() => void) | null>(null)
const {
messages,
sendMessage,
addToolOutput,
stop,
status,
error,
setMessages,
} = useChat({
transport: new DefaultChatTransport({ transport: new DefaultChatTransport({
api: "/api/chat", api: "/api/chat",
}), }),
@@ -114,16 +127,32 @@ export default function ChatPanel({
if (toolCall.toolName === "display_diagram") { if (toolCall.toolName === "display_diagram") {
const { xml } = toolCall.input as { xml: string } const { xml } = toolCall.input as { xml: string }
// Validate the final XML result
const validationError = validateMxCellStructure(xml) const validationError = validateMxCellStructure(xml)
if (validationError) { if (validationError) {
addToolResult({ console.warn(
"[display_diagram] Validation error:",
validationError,
)
// Return error to model - sendAutomaticallyWhen will trigger retry
const errorMessage = `${validationError}
Please fix the XML issues and call display_diagram again with corrected XML.
Your failed XML:
\`\`\`xml
${xml}
\`\`\``
addToolOutput({
tool: "display_diagram", tool: "display_diagram",
toolCallId: toolCall.toolCallId, toolCallId: toolCall.toolCallId,
output: validationError, state: "output-error",
errorText: errorMessage,
}) })
} else { } else {
addToolResult({ // Success - diagram will be rendered by chat-message-display
addToolOutput({
tool: "display_diagram", tool: "display_diagram",
toolCallId: toolCall.toolCallId, toolCallId: toolCall.toolCallId,
output: "Successfully displayed the diagram.", output: "Successfully displayed the diagram.",
@@ -164,7 +193,7 @@ export default function ChatPanel({
onDisplayChart(editedXml) onDisplayChart(editedXml)
addToolResult({ addToolOutput({
tool: "edit_diagram", tool: "edit_diagram",
toolCallId: toolCall.toolCallId, toolCallId: toolCall.toolCallId,
output: `Successfully applied ${edits.length} edit(s) to the diagram.`, output: `Successfully applied ${edits.length} edit(s) to the diagram.`,
@@ -174,14 +203,14 @@ export default function ChatPanel({
console.error("[edit_diagram] Failed:", error) console.error("[edit_diagram] Failed:", error)
const errorMessage = const errorMessage =
error instanceof Error error instanceof Error ? error.message : String(error)
? error.message
: String(error)
addToolResult({ // Use addToolOutput with state: 'output-error' for proper error signaling
addToolOutput({
tool: "edit_diagram", tool: "edit_diagram",
toolCallId: toolCall.toolCallId, toolCallId: toolCall.toolCallId,
output: `Edit failed: ${errorMessage} state: "output-error",
errorText: `Edit failed: ${errorMessage}
Current diagram XML: Current diagram XML:
\`\`\`xml \`\`\`xml
@@ -216,8 +245,14 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
setShowSettingsDialog(true) setShowSettingsDialog(true)
} }
}, },
// Auto-resubmit when all tool results are available (including errors)
// This enables the model to retry when a tool returns an error
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
}) })
// Update stopRef so onToolCall can access it
stopRef.current = stop
const messagesEndRef = useRef<HTMLDivElement>(null) const messagesEndRef = useRef<HTMLDivElement>(null)
useEffect(() => { useEffect(() => {

192
package-lock.json generated
View File

@@ -15,7 +15,7 @@
"@ai-sdk/deepseek": "^1.0.30", "@ai-sdk/deepseek": "^1.0.30",
"@ai-sdk/google": "^2.0.0", "@ai-sdk/google": "^2.0.0",
"@ai-sdk/openai": "^2.0.19", "@ai-sdk/openai": "^2.0.19",
"@ai-sdk/react": "^2.0.22", "@ai-sdk/react": "^2.0.107",
"@aws-sdk/credential-providers": "^3.943.0", "@aws-sdk/credential-providers": "^3.943.0",
"@langfuse/client": "^4.4.9", "@langfuse/client": "^4.4.9",
"@langfuse/otel": "^4.4.4", "@langfuse/otel": "^4.4.4",
@@ -90,23 +90,6 @@
"zod": "^3.25.76 || ^4.1.8" "zod": "^3.25.76 || ^4.1.8"
} }
}, },
"node_modules/@ai-sdk/amazon-bedrock/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.18",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz",
"integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "2.0.0",
"@standard-schema/spec": "^1.0.0",
"eventsource-parser": "^3.0.6"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/@ai-sdk/anthropic": { "node_modules/@ai-sdk/anthropic": {
"version": "2.0.50", "version": "2.0.50",
"resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.50.tgz", "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.50.tgz",
@@ -123,23 +106,6 @@
"zod": "^3.25.76 || ^4.1.8" "zod": "^3.25.76 || ^4.1.8"
} }
}, },
"node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.18",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz",
"integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "2.0.0",
"@standard-schema/spec": "^1.0.0",
"eventsource-parser": "^3.0.6"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/@ai-sdk/azure": { "node_modules/@ai-sdk/azure": {
"version": "2.0.69", "version": "2.0.69",
"resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-2.0.69.tgz", "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-2.0.69.tgz",
@@ -191,32 +157,15 @@
"zod": "^3.25.76 || ^4.1.8" "zod": "^3.25.76 || ^4.1.8"
} }
}, },
"node_modules/@ai-sdk/deepseek/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.18",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz",
"integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "2.0.0",
"@standard-schema/spec": "^1.0.0",
"eventsource-parser": "^3.0.6"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/@ai-sdk/gateway": { "node_modules/@ai-sdk/gateway": {
"version": "2.0.7", "version": "2.0.18",
"resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.7.tgz", "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.18.tgz",
"integrity": "sha512-/AI5AKi4vOK9SEb8Z1dfXkhsJ5NAfWsoJQc96B/mzn2KIrjw5occOjIwD06scuhV9xWlghCoXJT1sQD9QH/tyg==", "integrity": "sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@ai-sdk/provider": "2.0.0", "@ai-sdk/provider": "2.0.0",
"@ai-sdk/provider-utils": "3.0.16", "@ai-sdk/provider-utils": "3.0.18",
"@vercel/oidc": "3.0.3" "@vercel/oidc": "3.0.5"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@@ -300,23 +249,6 @@
"zod": "^3.25.76 || ^4.1.8" "zod": "^3.25.76 || ^4.1.8"
} }
}, },
"node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.18",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz",
"integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "2.0.0",
"@standard-schema/spec": "^1.0.0",
"eventsource-parser": "^3.0.6"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider-utils": { "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.17", "version": "3.0.17",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.17.tgz", "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.17.tgz",
@@ -347,9 +279,9 @@
} }
}, },
"node_modules/@ai-sdk/provider-utils": { "node_modules/@ai-sdk/provider-utils": {
"version": "3.0.16", "version": "3.0.18",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.16.tgz", "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz",
"integrity": "sha512-lsWQY9aDXHitw7C1QRYIbVGmgwyT98TF3MfM8alNIXKpdJdi+W782Rzd9f1RyOfgRmZ08gJ2EYNDhWNK7RqpEA==", "integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@ai-sdk/provider": "2.0.0", "@ai-sdk/provider": "2.0.0",
@@ -364,13 +296,13 @@
} }
}, },
"node_modules/@ai-sdk/react": { "node_modules/@ai-sdk/react": {
"version": "2.0.22", "version": "2.0.107",
"resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-2.0.22.tgz", "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-2.0.107.tgz",
"integrity": "sha512-nJt2U0ZDjpdPEIHCEWlxOixUhQyA/teQ0y9gz66mYW40OhBjSsZjcEAYhbS05mvy+NMVqzlE3sVu54DqzjR68w==", "integrity": "sha512-rv0u+tAi2r2zJu2uSLXcC3TBgGrkQIWXRM+i6us6qcGmYQ2kOu2VYg+lxviOSGPhL9PVebvTlN5x8mf3rDqX+w==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@ai-sdk/provider-utils": "3.0.5", "@ai-sdk/provider-utils": "3.0.18",
"ai": "5.0.22", "ai": "5.0.107",
"swr": "^2.2.5", "swr": "^2.2.5",
"throttleit": "2.1.0" "throttleit": "2.1.0"
}, },
@@ -379,7 +311,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"react": "^18 || ^19 || ^19.0.0-rc", "react": "^18 || ^19 || ^19.0.0-rc",
"zod": "^3.25.76 || ^4" "zod": "^3.25.76 || ^4.1.8"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"zod": { "zod": {
@@ -387,67 +319,6 @@
} }
} }
}, },
"node_modules/@ai-sdk/react/node_modules/@ai-sdk/gateway": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-1.0.11.tgz",
"integrity": "sha512-ErwWS3sPOuWy42eE3AVxlKkTa1XjjKBEtNCOylVKMO5KNyz5qie8QVlLYbULOG56dtxX4zTKX3rQNJudplhcmQ==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "2.0.0",
"@ai-sdk/provider-utils": "3.0.5"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4"
}
},
"node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.5.tgz",
"integrity": "sha512-HliwB/yzufw3iwczbFVE2Fiwf1XqROB/I6ng8EKUsPM5+2wnIa8f4VbljZcDx+grhFrPV+PnRZH7zBqi8WZM7Q==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "2.0.0",
"@standard-schema/spec": "^1.0.0",
"eventsource-parser": "^3.0.3",
"zod-to-json-schema": "^3.24.1"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4"
}
},
"node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider-utils/node_modules/zod-to-json-schema": {
"version": "3.24.6",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
"integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
"license": "ISC",
"peerDependencies": {
"zod": "^3.24.1"
}
},
"node_modules/@ai-sdk/react/node_modules/ai": {
"version": "5.0.22",
"resolved": "https://registry.npmjs.org/ai/-/ai-5.0.22.tgz",
"integrity": "sha512-RZiYhj7Ux7hrLtXkHPcxzdiSZt4NOiC69O5AkNfMCsz3twwz/KRkl9ASptosoOsg833s5yRcTSdIu5z53Sl6Pw==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/gateway": "1.0.11",
"@ai-sdk/provider": "2.0.0",
"@ai-sdk/provider-utils": "3.0.5",
"@opentelemetry/api": "1.9.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4"
}
},
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
@@ -5999,9 +5870,9 @@
} }
}, },
"node_modules/@vercel/oidc": { "node_modules/@vercel/oidc": {
"version": "3.0.3", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.5.tgz",
"integrity": "sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg==", "integrity": "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==",
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">= 20" "node": ">= 20"
@@ -6049,14 +5920,14 @@
} }
}, },
"node_modules/ai": { "node_modules/ai": {
"version": "5.0.90", "version": "5.0.107",
"resolved": "https://registry.npmjs.org/ai/-/ai-5.0.90.tgz", "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.107.tgz",
"integrity": "sha512-bawNN10N2cXzFedbDdNUZo8KkcGp12VX1b+mCL5dfllh6WmLsIYYME7GVxsRJvHvPP7xRhuds5fn0jtLyxGnZw==", "integrity": "sha512-laZlS9ZC/DZfSaxPgrBqI4mM+kxRvTPBBQfa74ceBFskkunZKEsaGVFNEs4cfyGa3nCCCl1WO/fjxixp4V8Zag==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@ai-sdk/gateway": "2.0.7", "@ai-sdk/gateway": "2.0.18",
"@ai-sdk/provider": "2.0.0", "@ai-sdk/provider": "2.0.0",
"@ai-sdk/provider-utils": "3.0.16", "@ai-sdk/provider-utils": "3.0.18",
"@opentelemetry/api": "1.9.0" "@opentelemetry/api": "1.9.0"
}, },
"engines": { "engines": {
@@ -10743,23 +10614,6 @@
"zod": "^4.0.16" "zod": "^4.0.16"
} }
}, },
"node_modules/ollama-ai-provider-v2/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.17.tgz",
"integrity": "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "2.0.0",
"@standard-schema/spec": "^1.0.0",
"eventsource-parser": "^3.0.6"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/onetime": { "node_modules/onetime": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",

View File

@@ -19,7 +19,7 @@
"@ai-sdk/deepseek": "^1.0.30", "@ai-sdk/deepseek": "^1.0.30",
"@ai-sdk/google": "^2.0.0", "@ai-sdk/google": "^2.0.0",
"@ai-sdk/openai": "^2.0.19", "@ai-sdk/openai": "^2.0.19",
"@ai-sdk/react": "^2.0.22", "@ai-sdk/react": "^2.0.107",
"@aws-sdk/credential-providers": "^3.943.0", "@aws-sdk/credential-providers": "^3.943.0",
"@langfuse/client": "^4.4.9", "@langfuse/client": "^4.4.9",
"@langfuse/otel": "^4.4.4", "@langfuse/otel": "^4.4.4",