Skip to content

Commit c8fce91

Browse files
authored
feat (ai): add experimental Agent abstraction (#7092)
## Background Over 60% of users prefer an OOP Agent abstraction over the functional `generateText` / `streamText` abstractions. ## Summary Add an experimental `Agent` abstraction. ## Verification Ran new examples.
1 parent 7b069ed commit c8fce91

File tree

6 files changed

+186
-0
lines changed

6 files changed

+186
-0
lines changed

‎.changeset/fuzzy-goats-mate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (ai): add experimental Agent abstraction
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { openai } from '@ai-sdk/openai';
2+
import { Experimental_Agent as Agent } from 'ai';
3+
import 'dotenv/config';
4+
5+
async function main() {
6+
const agent = new Agent({
7+
model: openai('gpt-4o'),
8+
});
9+
10+
const { text, usage } = await agent.generate({
11+
prompt: 'Invent a new holiday and describe its traditions.',
12+
});
13+
14+
console.log(text);
15+
console.log();
16+
console.log('Usage:', usage);
17+
}
18+
19+
main().catch(console.error);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { openai } from '@ai-sdk/openai';
2+
import { Experimental_Agent as Agent } from 'ai';
3+
import 'dotenv/config';
4+
5+
async function main() {
6+
const agent = new Agent({
7+
model: openai('gpt-3.5-turbo'),
8+
maxOutputTokens: 512,
9+
temperature: 0.3,
10+
maxRetries: 5,
11+
});
12+
13+
const result = agent.stream({
14+
prompt: 'Invent a new holiday and describe its traditions.',
15+
});
16+
17+
for await (const textPart of result.textStream) {
18+
process.stdout.write(textPart);
19+
}
20+
21+
console.log();
22+
console.log('Token usage:', await result.usage);
23+
console.log('Finish reason:', await result.finishReason);
24+
}
25+
26+
main().catch(console.error);

‎packages/ai/src/agent/agent.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { IdGenerator } from '@ai-sdk/provider-utils';
2+
import {
3+
generateText,
4+
GenerateTextOnStepFinishCallback,
5+
} from '../../core/generate-text/generate-text';
6+
import { Output } from '../../core/generate-text/output';
7+
import { PrepareStepFunction } from '../../core/generate-text/prepare-step';
8+
import { StopCondition } from '../../core/generate-text/stop-condition';
9+
import { ToolCallRepairFunction } from '../../core/generate-text/tool-call-repair-function';
10+
import { ToolSet } from '../../core/generate-text/tool-set';
11+
import { CallSettings } from '../../core/prompt/call-settings';
12+
import { TelemetrySettings } from '../../core/telemetry/telemetry-settings';
13+
import { LanguageModel, ToolChoice } from '../../core/types/language-model';
14+
import { streamText } from '../../core/generate-text/stream-text';
15+
import { StreamTextResult } from '../../core/generate-text/stream-text-result';
16+
import { Prompt } from '../../core/prompt/prompt';
17+
import { ProviderMetadata } from '../../core/types/provider-metadata';
18+
import { GenerateTextResult } from '../../core/generate-text/generate-text-result';
19+
20+
export type AgentSettings<
21+
TOOLS extends ToolSet,
22+
OUTPUT = never,
23+
OUTPUT_PARTIAL = never,
24+
> = CallSettings & {
25+
/**
26+
The language model to use.
27+
*/
28+
model: LanguageModel;
29+
30+
/**
31+
The tools that the model can call. The model needs to support calling tools.
32+
*/
33+
tools?: TOOLS;
34+
35+
/**
36+
The tool choice strategy. Default: 'auto'.
37+
*/
38+
toolChoice?: ToolChoice<NoInfer<TOOLS>>;
39+
40+
/**
41+
Condition for stopping the generation when there are tool results in the last step.
42+
When the condition is an array, any of the conditions can be met to stop the generation.
43+
44+
@default stepCountIs(1)
45+
*/
46+
stopWhen?:
47+
| StopCondition<NoInfer<TOOLS>>
48+
| Array<StopCondition<NoInfer<TOOLS>>>;
49+
50+
/**
51+
Optional telemetry configuration (experimental).
52+
*/
53+
experimental_telemetry?: TelemetrySettings;
54+
55+
/**
56+
Limits the tools that are available for the model to call without
57+
changing the tool call and result types in the result.
58+
*/
59+
activeTools?: Array<keyof NoInfer<TOOLS>>;
60+
61+
/**
62+
Optional specification for parsing structured outputs from the LLM response.
63+
*/
64+
experimental_output?: Output<OUTPUT, OUTPUT_PARTIAL>;
65+
66+
/**
67+
* @deprecated Use `prepareStep` instead.
68+
*/
69+
experimental_prepareStep?: PrepareStepFunction<NoInfer<TOOLS>>;
70+
71+
/**
72+
Optional function that you can use to provide different settings for a step.
73+
*/
74+
prepareStep?: PrepareStepFunction<NoInfer<TOOLS>>;
75+
76+
/**
77+
A function that attempts to repair a tool call that failed to parse.
78+
*/
79+
experimental_repairToolCall?: ToolCallRepairFunction<NoInfer<TOOLS>>;
80+
81+
/**
82+
Callback that is called when each step (LLM call) is finished, including intermediate steps.
83+
*/
84+
onStepFinish?: GenerateTextOnStepFinishCallback<NoInfer<TOOLS>>;
85+
86+
/**
87+
* Internal. For test use only. May change without notice.
88+
*/
89+
_internal?: {
90+
generateId?: IdGenerator;
91+
currentDate?: () => Date;
92+
};
93+
};
94+
95+
export class Agent<
96+
TOOLS extends ToolSet,
97+
OUTPUT = never,
98+
OUTPUT_PARTIAL = never,
99+
> {
100+
private readonly settings: AgentSettings<TOOLS, OUTPUT, OUTPUT_PARTIAL>;
101+
102+
constructor(settings: AgentSettings<TOOLS, OUTPUT, OUTPUT_PARTIAL>) {
103+
this.settings = settings;
104+
}
105+
106+
async generate(
107+
options: Prompt & {
108+
/**
109+
Additional provider-specific metadata. They are passed through
110+
from the provider to the AI SDK and enable provider-specific
111+
results that can be fully encapsulated in the provider.
112+
*/
113+
providerMetadata?: ProviderMetadata;
114+
},
115+
): Promise<GenerateTextResult<TOOLS, OUTPUT>> {
116+
return generateText({ ...this.settings, ...options });
117+
}
118+
119+
stream(
120+
options: Prompt & {
121+
/**
122+
Additional provider-specific metadata. They are passed through
123+
from the provider to the AI SDK and enable provider-specific
124+
results that can be fully encapsulated in the provider.
125+
*/
126+
providerMetadata?: ProviderMetadata;
127+
},
128+
): StreamTextResult<TOOLS, OUTPUT_PARTIAL> {
129+
return streamText({ ...this.settings, ...options });
130+
}
131+
}

‎packages/ai/src/agent/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export {
2+
type AgentSettings as Experimental_AgentSettings,
3+
Agent as Experimental_Agent,
4+
} from './agent';

‎packages/ai/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export {
99
} from '@ai-sdk/provider-utils';
1010

1111
// directory exports
12+
export * from './agent';
1213
export * from './error';
1314
export * from './text-stream';
1415
export * from './ui';

0 commit comments

Comments
 (0)