|
1 | 1 | import { chat } from "@trigger.dev/sdk/ai"; |
2 | 2 | import { locals } from "@trigger.dev/core/v3"; |
3 | | -import { streamText, type LanguageModel, type UIMessage } from "ai"; |
| 3 | +import { stepCountIs, streamText, tool, type LanguageModel, type UIMessage } from "ai"; |
4 | 4 | import { z } from "zod"; |
5 | 5 |
|
6 | 6 | /** |
@@ -61,3 +61,73 @@ export const testChatAgent = chat |
61 | 61 | return streamText({ model, messages, abortSignal: signal }); |
62 | 62 | }, |
63 | 63 | }); |
| 64 | + |
| 65 | +/** |
| 66 | + * A minimal agent with no lifecycle hooks. With `hydrateMessages` absent, the |
| 67 | + * default snapshot + `.out`/`.in` replay boot path is what restores prior |
| 68 | + * history on a continuation run. |
| 69 | + */ |
| 70 | +export const testPlainChatAgent = chat.agent({ |
| 71 | + id: "e2e-test-chat-plain", |
| 72 | + run: async ({ messages, signal }) => { |
| 73 | + const model = locals.get(testChatModelLocal); |
| 74 | + if (!model) { |
| 75 | + throw new Error("test model not injected via locals"); |
| 76 | + } |
| 77 | + return streamText({ model, messages, abortSignal: signal }); |
| 78 | + }, |
| 79 | +}); |
| 80 | + |
| 81 | +/** |
| 82 | + * A tool with a server-side `execute`: the agent runs it automatically and |
| 83 | + * feeds the result back to the model, so a single turn covers the whole |
| 84 | + * tool loop. |
| 85 | + */ |
| 86 | +const weatherTool = tool({ |
| 87 | + description: "Get the current weather for a city.", |
| 88 | + inputSchema: z.object({ city: z.string() }), |
| 89 | + execute: async ({ city }) => ({ city, tempC: 21, summary: "clear" }), |
| 90 | +}); |
| 91 | + |
| 92 | +export const testToolChatAgent = chat.agent({ |
| 93 | + id: "e2e-test-chat-tool", |
| 94 | + run: async ({ messages, signal }) => { |
| 95 | + const model = locals.get(testChatModelLocal); |
| 96 | + if (!model) { |
| 97 | + throw new Error("test model not injected via locals"); |
| 98 | + } |
| 99 | + return streamText({ |
| 100 | + model, |
| 101 | + messages, |
| 102 | + tools: { getWeather: weatherTool }, |
| 103 | + stopWhen: stepCountIs(5), |
| 104 | + abortSignal: signal, |
| 105 | + }); |
| 106 | + }, |
| 107 | +}); |
| 108 | + |
| 109 | +/** |
| 110 | + * A tool with no `execute`: the model's call parks the turn on a |
| 111 | + * human-in-the-loop round-trip. The client supplies the tool output on the |
| 112 | + * next message and the agent continues from there. |
| 113 | + */ |
| 114 | +const askUserTool = tool({ |
| 115 | + description: "Ask the user a question and wait for their answer.", |
| 116 | + inputSchema: z.object({ question: z.string() }), |
| 117 | +}); |
| 118 | + |
| 119 | +export const testHitlChatAgent = chat.agent({ |
| 120 | + id: "e2e-test-chat-hitl", |
| 121 | + run: async ({ messages, signal }) => { |
| 122 | + const model = locals.get(testChatModelLocal); |
| 123 | + if (!model) { |
| 124 | + throw new Error("test model not injected via locals"); |
| 125 | + } |
| 126 | + return streamText({ |
| 127 | + model, |
| 128 | + messages, |
| 129 | + tools: { askUser: askUserTool }, |
| 130 | + abortSignal: signal, |
| 131 | + }); |
| 132 | + }, |
| 133 | +}); |
0 commit comments