Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/nine-starfishes-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@ai-sdk/openai-compatible": patch
"@ai-sdk/amazon-bedrock": patch
"@ai-sdk/open-responses": patch
"@ai-sdk/anthropic": patch
"@ai-sdk/deepseek": patch
"@ai-sdk/alibaba": patch
"@ai-sdk/mistral": patch
"@ai-sdk/cohere": patch
"@ai-sdk/google": patch
"@ai-sdk/openai": patch
"@ai-sdk/groq": patch
"@ai-sdk/xai": patch
"ai": patch
---

fix: more precise default message for tool execution denial
161 changes: 161 additions & 0 deletions packages/ai/src/ui/convert-to-model-messages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,167 @@
{
role: 'assistant',
parts: [
<<<<<<< HEAD

Check failure on line 1404 in packages/ai/src/ui/convert-to-model-messages.test.ts

View workflow job for this annotation

GitHub Actions / TypeScript

Merge conflict marker encountered.
=======

Check failure on line 1405 in packages/ai/src/ui/convert-to-model-messages.test.ts

View workflow job for this annotation

GitHub Actions / TypeScript

Merge conflict marker encountered.
{
input: {
city: 'Tokyo',
},
state: 'output-denied',
toolCallId: 'call-1',
type: 'tool-weather',
},
],
},
] as unknown as UIMessage[]);

expect(result).toMatchInlineSnapshot(`
[
{
"content": [
{
"text": "What is the weather in Tokyo?",
"type": "text",
},
],
"role": "user",
},
{
"content": [
{
"input": {
"city": "Tokyo",
},
"providerExecuted": undefined,
"toolCallId": "call-1",
"toolName": "weather",
"type": "tool-call",
},
],
"role": "assistant",
},
{
"content": [
{
"output": {
"type": "error-text",
"value": "Tool call execution denied.",
},
"toolCallId": "call-1",
"toolName": "weather",
"type": "tool-result",
},
],
"role": "tool",
},
]
`);
});

it('should convert tool output denied (dynamic tool)', async () => {
const result = await convertToModelMessages([
{
parts: [
{
text: 'What is the weather in Tokyo?',
type: 'text',
},
],
role: 'user',
},
{
parts: [
{
type: 'step-start',
},
{
approval: {
approved: false,
id: 'approval-1',
reason: "I don't want to approve this",
},
input: {
city: 'Tokyo',
},
state: 'output-denied',
toolCallId: 'call-1',
type: 'dynamic-tool',
toolName: 'weather',
},
],
role: 'assistant',
},
]);

expect(result).toMatchInlineSnapshot(`
[
{
"content": [
{
"text": "What is the weather in Tokyo?",
"type": "text",
},
],
"role": "user",
},
{
"content": [
{
"input": {
"city": "Tokyo",
},
"providerExecuted": undefined,
"toolCallId": "call-1",
"toolName": "weather",
"type": "tool-call",
},
{
"approvalId": "approval-1",
"toolCallId": "call-1",
"type": "tool-approval-request",
},
],
"role": "assistant",
},
{
"content": [
{
"approvalId": "approval-1",
"approved": false,
"providerExecuted": undefined,
"reason": "I don't want to approve this",
"type": "tool-approval-response",
},
{
"output": {
"type": "error-text",
"value": "I don't want to approve this",
},
"toolCallId": "call-1",
"toolName": "weather",
"type": "tool-result",
},
],
"role": "tool",
},
]
`);
});

it('should convert tool output result with approval and follow up text (static tool)', async () => {
const result = await convertToModelMessages([
{
parts: [
{
text: 'What is the weather in Tokyo?',
type: 'text',
},
],
role: 'user',
},
{
parts: [
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))

Check failure on line 1564 in packages/ai/src/ui/convert-to-model-messages.test.ts

View workflow job for this annotation

GitHub Actions / TypeScript

Merge conflict marker encountered.
{ type: 'step-start' },
{
type: 'dynamic-tool',
Expand Down
37 changes: 37 additions & 0 deletions packages/ai/src/ui/convert-to-model-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,45 @@
case 'output-available': {
const toolName = getToolOrDynamicToolName(toolPart);

<<<<<<< HEAD

Check failure on line 269 in packages/ai/src/ui/convert-to-model-messages.ts

View workflow job for this annotation

GitHub Actions / TypeScript

Merge conflict marker encountered.
return {
type: 'tool-result',
=======

Check failure on line 272 in packages/ai/src/ui/convert-to-model-messages.ts

View workflow job for this annotation

GitHub Actions / TypeScript

Merge conflict marker encountered.
// For provider-executed tools, the tool result is already in the
// assistant content. Skip adding to tool message to avoid duplicates
// (which would create orphaned function_call_output entries).
if (toolPart.providerExecuted === true) {
continue;
}

switch (toolPart.state) {
case 'output-denied': {
content.push({
type: 'tool-result',
toolCallId: toolPart.toolCallId,
toolName: getToolName(toolPart),
output: {
type: 'error-text' as const,
value:
toolPart.approval?.reason ??
'Tool call execution denied.',
},
...(toolPart.callProviderMetadata != null
? { providerOptions: toolPart.callProviderMetadata }
: {}),
});
break;
}

case 'output-error':
case 'output-available': {
const toolName = getToolName(toolPart);
content.push({
type: 'tool-result',
toolCallId: toolPart.toolCallId,
toolName,
output: await createToolModelOutput({
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))

Check failure on line 307 in packages/ai/src/ui/convert-to-model-messages.ts

View workflow job for this annotation

GitHub Actions / TypeScript

Merge conflict marker encountered.
toolCallId: toolPart.toolCallId,
toolName,
output: createToolModelOutput({
Expand Down
6 changes: 6 additions & 0 deletions packages/alibaba/src/convert-to-alibaba-chat-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@
case 'error-text':
contentValue = output.value;
break;
<<<<<<< HEAD

Check failure on line 166 in packages/alibaba/src/convert-to-alibaba-chat-messages.ts

View workflow job for this annotation

GitHub Actions / TypeScript

Merge conflict marker encountered.
=======
case 'execution-denied':
contentValue = output.reason ?? 'Tool call execution denied.';
break;
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))
case 'content':
case 'json':
case 'error-json':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ export async function convertToBedrockChatMessages(
case 'error-text':
toolResultContent = [{ text: output.value }];
break;
<<<<<<< HEAD
=======
case 'execution-denied':
toolResultContent = [
{ text: output.reason ?? 'Tool call execution denied.' },
];
break;
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))
case 'json':
case 'error-json':
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,13 @@ export async function convertToAnthropicMessagesPrompt({
case 'error-text':
contentValue = output.value;
break;
<<<<<<< HEAD
=======
case 'execution-denied':
contentValue =
output.reason ?? 'Tool call execution denied.';
break;
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))
case 'json':
case 'error-json':
default:
Expand Down
18 changes: 18 additions & 0 deletions packages/cohere/src/convert-to-cohere-chat-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export async function convertToCohereChatPrompt(
...content.map(toolResult => {
const output = toolResult.output;

<<<<<<< HEAD
let contentValue: string;
switch (output.type) {
case 'text':
Expand All @@ -166,6 +167,23 @@ export async function convertToCohereChatPrompt(
contentValue = JSON.stringify(output.value);
break;
}
=======
let contentValue: string;
switch (output.type) {
case 'text':
case 'error-text':
contentValue = output.value;
break;
case 'execution-denied':
contentValue = output.reason ?? 'Tool call execution denied.';
break;
case 'content':
case 'json':
case 'error-json':
contentValue = JSON.stringify(output.value);
break;
}
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))

return {
role: 'tool' as const,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ export function convertToDeepSeekChatMessages({
case 'error-text':
contentValue = output.value;
break;
<<<<<<< HEAD
=======
case 'execution-denied':
contentValue = output.reason ?? 'Tool call execution denied.';
break;
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))
case 'content':
case 'json':
case 'error-json':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,14 @@ export function convertToGoogleGenerativeAIMessages(
name: part.toolName,
response: {
name: part.toolName,
<<<<<<< HEAD
content: output.value,
=======
content:
output.type === 'execution-denied'
? (output.reason ?? 'Tool call execution denied.')
: output.value,
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))
},
},
});
Expand Down
6 changes: 6 additions & 0 deletions packages/groq/src/convert-to-groq-chat-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ export function convertToGroqChatMessages(
case 'error-text':
contentValue = output.value;
break;
<<<<<<< HEAD
=======
case 'execution-denied':
contentValue = output.reason ?? 'Tool call execution denied.';
break;
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))
case 'content':
case 'json':
case 'error-json':
Expand Down
6 changes: 6 additions & 0 deletions packages/mistral/src/convert-to-mistral-chat-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ export function convertToMistralChatMessages(
case 'error-text':
contentValue = output.value;
break;
<<<<<<< HEAD
=======
case 'execution-denied':
contentValue = output.reason ?? 'Tool call execution denied.';
break;
>>>>>>> 327642b278 ([v6.0] fix: more precise default message for tool execution denial (#16804))
case 'content':
case 'json':
case 'error-json':
Expand Down
Loading
Loading