Skip to content

Commit 1c0ebf8

Browse files
authoredMay 29, 2024··
feat (core): add responseMessages to generateText result (#1732)
1 parent 54d68b4 commit 1c0ebf8

File tree

5 files changed

+149
-14
lines changed

5 files changed

+149
-14
lines changed
 

‎.changeset/stupid-papayas-juggle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (core): add responseMessages to generateText result

‎content/docs/07-reference/ai-sdk-core/01-generate-text.mdx

+6
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,12 @@ const result = await generateText({
397397
description:
398398
'Warnings from the model provider (e.g. unsupported settings).',
399399
},
400+
{
401+
name: 'responseMessages',
402+
type: 'Array<CoreAssistantMessage | CoreToolMessage>',
403+
description:
404+
'The response messages that were generated during the call. It consists of an assistant message, potentially containing tool calls. When there are tool results, there is an additional tool message with the tool results that are available. If there are tools that do not have execute functions, they are not included in the tool results and need to be added separately.',
405+
},
400406
]}
401407
/>
402408

‎examples/ai-core/src/generate-text/anthropic-chatbot-with-tools.ts

+8-14
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ async function main() {
2222
messages.push({ role: 'user', content: userInput });
2323
}
2424

25-
const { text, toolCalls, toolResults } = await generateText({
26-
model: anthropic('claude-3-opus-20240229'),
27-
tools: { weatherTool },
28-
system: `You are a helpful, respectful and honest assistant.`,
29-
messages,
30-
});
25+
const { text, toolCalls, toolResults, responseMessages } =
26+
await generateText({
27+
model: anthropic('claude-3-opus-20240229'),
28+
tools: { weatherTool },
29+
system: `You are a helpful, respectful and honest assistant.`,
30+
messages,
31+
});
3132

3233
toolResponseAvailable = false;
3334

@@ -49,14 +50,7 @@ async function main() {
4950

5051
process.stdout.write('\n\n');
5152

52-
messages.push({
53-
role: 'assistant',
54-
content: [{ type: 'text', text }, ...(toolCalls ?? [])],
55-
});
56-
57-
if (toolResults.length > 0) {
58-
messages.push({ role: 'tool', content: toolResults });
59-
}
53+
messages.push(...responseMessages);
6054

6155
toolResponseAvailable = toolCalls.length > 0;
6256
}

‎packages/core/core/generate-text/generate-text.test.ts

+85
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,88 @@ describe('result.toolResults', () => {
184184
]);
185185
});
186186
});
187+
188+
describe('result.responseMessages', () => {
189+
it('should contain assistant response message when there are no tool calls', async () => {
190+
const result = await generateText({
191+
model: new MockLanguageModelV1({
192+
doGenerate: async ({ prompt, mode }) => {
193+
return {
194+
...dummyResponseValues,
195+
text: 'Hello, world!',
196+
};
197+
},
198+
}),
199+
prompt: 'test-input',
200+
});
201+
202+
assert.deepStrictEqual(result.responseMessages, [
203+
{ role: 'assistant', content: [{ type: 'text', text: 'Hello, world!' }] },
204+
]);
205+
});
206+
207+
it('should contain assistant response message and tool message when there are tool calls with results', async () => {
208+
const result = await generateText({
209+
model: new MockLanguageModelV1({
210+
doGenerate: async ({ prompt, mode }) => {
211+
return {
212+
...dummyResponseValues,
213+
text: 'Hello, world!',
214+
toolCalls: [
215+
{
216+
toolCallType: 'function',
217+
toolCallId: 'call-1',
218+
toolName: 'tool1',
219+
args: `{ "value": "value" }`,
220+
},
221+
],
222+
toolResults: [
223+
{
224+
toolCallId: 'call-1',
225+
toolName: 'tool1',
226+
args: { value: 'value' },
227+
result: 'result1',
228+
},
229+
],
230+
};
231+
},
232+
}),
233+
tools: {
234+
tool1: {
235+
parameters: z.object({ value: z.string() }),
236+
execute: async args => {
237+
assert.deepStrictEqual(args, { value: 'value' });
238+
return 'result1';
239+
},
240+
},
241+
},
242+
prompt: 'test-input',
243+
});
244+
245+
assert.deepStrictEqual(result.responseMessages, [
246+
{
247+
role: 'assistant',
248+
content: [
249+
{ type: 'text', text: 'Hello, world!' },
250+
{
251+
type: 'tool-call',
252+
toolCallId: 'call-1',
253+
toolName: 'tool1',
254+
args: { value: 'value' },
255+
},
256+
],
257+
},
258+
{
259+
role: 'tool',
260+
content: [
261+
{
262+
type: 'tool-result',
263+
toolCallId: 'call-1',
264+
toolName: 'tool1',
265+
result: 'result1',
266+
},
267+
],
268+
},
269+
]);
270+
});
271+
});

‎packages/core/core/generate-text/generate-text.ts

+45
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CoreAssistantMessage, CoreToolMessage } from '../prompt';
12
import { CallSettings } from '../prompt/call-settings';
23
import { convertToLanguageModelPrompt } from '../prompt/convert-to-language-model-prompt';
34
import { getValidatedPrompt } from '../prompt/get-validated-prompt';
@@ -184,6 +185,15 @@ Warnings from the model provider (e.g. unsupported settings)
184185
*/
185186
readonly warnings: CallWarning[] | undefined;
186187

188+
/**
189+
The response messages that were generated during the call. It consists of an assistant message,
190+
potentially containing tool calls.
191+
When there are tool results, there is an additional tool message with the tool results that are available.
192+
If there are tools that do not have execute functions, they are not included in the tool results and
193+
need to be added separately.
194+
*/
195+
readonly responseMessages: Array<CoreAssistantMessage | CoreToolMessage>;
196+
187197
/**
188198
Optional raw response data.
189199
*/
@@ -220,7 +230,42 @@ Logprobs for the completion.
220230
this.warnings = options.warnings;
221231
this.rawResponse = options.rawResponse;
222232
this.logprobs = options.logprobs;
233+
this.responseMessages = toResponseMessages(options);
234+
}
235+
}
236+
237+
/**
238+
Converts the result of a `generateText` call to a list of response messages.
239+
*/
240+
function toResponseMessages<TOOLS extends Record<string, CoreTool>>({
241+
text,
242+
toolCalls,
243+
toolResults,
244+
}: {
245+
text: string;
246+
toolCalls: ToToolCallArray<TOOLS>;
247+
toolResults: ToToolResultArray<TOOLS>;
248+
}): Array<CoreAssistantMessage | CoreToolMessage> {
249+
const responseMessages: Array<CoreAssistantMessage | CoreToolMessage> = [];
250+
251+
responseMessages.push({
252+
role: 'assistant',
253+
content: [{ type: 'text', text }, ...toolCalls],
254+
});
255+
256+
if (toolResults.length > 0) {
257+
responseMessages.push({
258+
role: 'tool',
259+
content: toolResults.map(result => ({
260+
type: 'tool-result',
261+
toolCallId: result.toolCallId,
262+
toolName: result.toolName,
263+
result: result.result,
264+
})),
265+
});
223266
}
267+
268+
return responseMessages;
224269
}
225270

226271
/**

0 commit comments

Comments
 (0)
Please sign in to comment.