Skip to content

Commit ea7de16

Browse files
committed
chore(webapp): update dependencies and refactor AI tool integration to use the new API
- Upgraded `@ai-sdk/openai` to version `3.0.0` and `ai` to version `6.0.116` in `package.json`. - Refactored AI tool calls in `ai-generate.tsx`, `ai-generate-payload.tsx`, and `aiQueryService.server.ts` to use updated input handling. - Changed `maxSteps` to `stopWhen` in several service files for better clarity and consistency. - Updated import statements to reflect changes in the `ai` package. - Removed unused peer dependencies from `pnpm-lock.yaml` to streamline the lock file.
1 parent 9211032 commit ea7de16

10 files changed

Lines changed: 406 additions & 488 deletions

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-generate.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,19 +116,19 @@ export async function action({ request, params }: ActionFunctionArgs) {
116116
for await (const part of result.fullStream) {
117117
switch (part.type) {
118118
case "text-delta": {
119-
sendEvent({ type: "thinking", content: part.textDelta });
119+
sendEvent({ type: "thinking", content: part.text });
120120
break;
121121
}
122122
case "tool-call": {
123123
sendEvent({
124124
type: "tool_call",
125125
tool: part.toolName,
126-
args: part.args,
126+
args: part.input,
127127
});
128128

129129
// If it's a setTimeFilter call, emit the time_filter event immediately
130130
if (part.toolName === "setTimeFilter") {
131-
const args = part.args as { period?: string; from?: string; to?: string };
131+
const args = part.input as { period?: string; from?: string; to?: string };
132132
sendEvent({
133133
type: "time_filter",
134134
filter: {

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.ai-generate-payload.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { openai } from "@ai-sdk/openai";
2-
import { streamText, tool } from "ai";
2+
import { streamText, stepCountIs, tool } from "ai";
33
import { type ActionFunctionArgs } from "@remix-run/server-runtime";
44
import { z } from "zod";
55
import { env } from "~/env.server";
@@ -105,19 +105,19 @@ export async function action({ request, params }: ActionFunctionArgs) {
105105
getTaskSourceCode: tool({
106106
description:
107107
"Look up the source code of the task to understand what payload shape it expects. Use this when there is no JSON Schema available and you need to infer the payload structure from the task implementation.",
108-
parameters: z.object({}),
108+
inputSchema: z.object({}),
109109
execute: async () => {
110110
return getTaskSourceCode(environment.id, environment.type, taskIdentifier);
111111
},
112112
}),
113113
},
114-
maxSteps: 3,
114+
stopWhen: stepCountIs(3),
115115
});
116116

117117
for await (const part of result.fullStream) {
118118
switch (part.type) {
119119
case "text-delta": {
120-
sendEvent({ type: "thinking", content: part.textDelta });
120+
sendEvent({ type: "thinking", content: part.text });
121121
break;
122122
}
123123
case "tool-call": {

apps/webapp/app/v3/services/aiQueryService.server.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
type TableSchema,
66
type ValidationIssue,
77
} from "@internal/tsql";
8-
import { streamText, type LanguageModelV1, tool } from "ai";
8+
import { streamText, stepCountIs, type LanguageModel, tool } from "ai";
99
import { z } from "zod";
1010
import type { AITimeFilter } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types";
1111

@@ -55,7 +55,7 @@ export class AIQueryService {
5555

5656
constructor(
5757
private readonly tableSchema: TableSchema[],
58-
private readonly model: LanguageModelV1 = openai("gpt-4.1-mini")
58+
private readonly model: LanguageModel = openai("gpt-4.1-mini")
5959
) {}
6060

6161
/**
@@ -66,7 +66,7 @@ export class AIQueryService {
6666
return tool({
6767
description:
6868
"Set the time filter for the query page UI instead of adding time conditions to the query. ALWAYS use this tool when the user wants to filter by time (e.g., 'last 7 days', 'past hour', 'yesterday'). The UI will apply this filter automatically using the table's time column (triggered_at for runs, bucket_start for metrics). Do NOT add triggered_at or bucket_start to the WHERE clause for time filtering - use this tool instead.",
69-
parameters: z.object({
69+
inputSchema: z.object({
7070
period: z
7171
.string()
7272
.optional()
@@ -125,7 +125,7 @@ export class AIQueryService {
125125
validateTSQLQuery: tool({
126126
description:
127127
"Validate a TSQL query for syntax errors and schema compliance. Always use this tool to verify your query before returning it to the user.",
128-
parameters: z.object({
128+
inputSchema: z.object({
129129
query: z.string().describe("The TSQL query to validate"),
130130
}),
131131
execute: async ({ query }) => {
@@ -135,7 +135,7 @@ export class AIQueryService {
135135
getTableSchema: tool({
136136
description:
137137
"Get detailed schema information about available tables and columns. Use this to understand what data is available and how to query it.",
138-
parameters: z.object({
138+
inputSchema: z.object({
139139
tableName: z
140140
.string()
141141
.optional()
@@ -147,7 +147,7 @@ export class AIQueryService {
147147
}),
148148
setTimeFilter: this.buildSetTimeFilterTool(),
149149
},
150-
maxSteps: 5,
150+
stopWhen: stepCountIs(5),
151151
experimental_telemetry: {
152152
isEnabled: true,
153153
metadata: {
@@ -191,7 +191,7 @@ export class AIQueryService {
191191
validateTSQLQuery: tool({
192192
description:
193193
"Validate a TSQL query for syntax errors and schema compliance. Always use this tool to verify your query before returning it to the user.",
194-
parameters: z.object({
194+
inputSchema: z.object({
195195
query: z.string().describe("The TSQL query to validate"),
196196
}),
197197
execute: async ({ query }) => {
@@ -201,7 +201,7 @@ export class AIQueryService {
201201
getTableSchema: tool({
202202
description:
203203
"Get detailed schema information about available tables and columns. Use this to understand what data is available and how to query it.",
204-
parameters: z.object({
204+
inputSchema: z.object({
205205
tableName: z
206206
.string()
207207
.optional()
@@ -213,7 +213,7 @@ export class AIQueryService {
213213
}),
214214
setTimeFilter: this.buildSetTimeFilterTool(),
215215
},
216-
maxSteps: 5,
216+
stopWhen: stepCountIs(5),
217217
experimental_telemetry: {
218218
isEnabled: true,
219219
metadata: {

apps/webapp/app/v3/services/aiQueryTitleService.server.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { openai } from "@ai-sdk/openai";
2-
import { generateText, type LanguageModelV1 } from "ai";
2+
import { generateText, type LanguageModel } from "ai";
33
import { env } from "~/env.server";
44

55
/**
@@ -13,7 +13,7 @@ export type AIQueryTitleResult =
1313
* Service for generating concise titles for SQL queries using AI
1414
*/
1515
export class AIQueryTitleService {
16-
constructor(private readonly model: LanguageModelV1 = openai("gpt-4o-mini")) {}
16+
constructor(private readonly model: LanguageModel = openai("gpt-4o-mini")) {}
1717

1818
/**
1919
* Generate a concise title for a SQL query
@@ -45,7 +45,7 @@ Examples:
4545
- "Average execution time by task"
4646
- "Recent runs with errors"`,
4747
prompt: `Generate a concise title for this SQL query:\n\n${query}`,
48-
maxTokens: 50,
48+
maxOutputTokens: 50,
4949
experimental_telemetry: {
5050
isEnabled: true,
5151
metadata: {

apps/webapp/app/v3/services/aiRunFilterService.server.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { openai } from "@ai-sdk/openai";
22
import { type TaskTriggerSource } from "@trigger.dev/database";
3-
import { generateText, LanguageModelV1, Output, tool } from "ai";
3+
import { generateText, stepCountIs, type LanguageModel, Output, tool } from "ai";
44
import { z } from "zod";
55
import { TaskRunListSearchFilters } from "~/components/runs/v3/RunFilters";
66
import { logger } from "~/services/logger.server";
@@ -80,7 +80,7 @@ export class AIRunFilterService {
8080
queryQueues: QueryQueues;
8181
queryTasks: QueryTasks;
8282
},
83-
private readonly model: LanguageModelV1 = openai("gpt-4o-mini")
83+
private readonly model: LanguageModel = openai("gpt-4o-mini")
8484
) {}
8585

8686
async call(text: string, environmentId: string): Promise<AIFilterResult> {
@@ -91,7 +91,7 @@ export class AIRunFilterService {
9191
tools: {
9292
lookupTags: tool({
9393
description: "Look up available tags in the environment",
94-
parameters: z.object({
94+
inputSchema: z.object({
9595
query: z.string().optional().describe("Optional search query to filter tags"),
9696
}),
9797
execute: async ({ query }) => {
@@ -101,7 +101,7 @@ export class AIRunFilterService {
101101
lookupVersions: tool({
102102
description:
103103
"Look up available versions in the environment. If you specify `isCurrent` it will return a single version string if it finds one. Otherwise it will return an array of version strings.",
104-
parameters: z.object({
104+
inputSchema: z.object({
105105
isCurrent: z
106106
.boolean()
107107
.optional()
@@ -119,7 +119,7 @@ export class AIRunFilterService {
119119
}),
120120
lookupQueues: tool({
121121
description: "Look up available queues in the environment",
122-
parameters: z.object({
122+
inputSchema: z.object({
123123
query: z.string().optional().describe("Optional search query to filter queues"),
124124
type: z
125125
.enum(["task", "custom"])
@@ -135,13 +135,13 @@ export class AIRunFilterService {
135135
lookupTasks: tool({
136136
description:
137137
"Look up available tasks in the environment. It will return each one. The `slug` is used for the filtering. You also get the triggerSource which is either `STANDARD` or `SCHEDULED`",
138-
parameters: z.object({}),
138+
inputSchema: z.object({}),
139139
execute: async () => {
140140
return await this.queryFns.queryTasks.query();
141141
},
142142
}),
143143
},
144-
maxSteps: 5,
144+
stopWhen: stepCountIs(5),
145145
system: `You are an AI assistant that converts natural language descriptions into structured filter parameters for a task run filtering system.
146146
147147
Available filter options:

apps/webapp/evalite.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { defineConfig } from "evalite/config";
2+
import tsconfigPaths from "vite-tsconfig-paths";
3+
4+
// evalite 1.0 runs its own Vite instance and does not pick up `vitest.config.ts`,
5+
// so the `~/*` -> `./app/*` path alias must be wired in explicitly here (mirrors
6+
// the plugin setup in vitest.config.ts).
7+
export default defineConfig({
8+
viteConfig: {
9+
// @ts-ignore - vite-tsconfig-paths plugin type vs evalite's bundled vite version
10+
plugins: [tsconfigPaths({ projects: ["./tsconfig.json"] })],
11+
},
12+
});

apps/webapp/evals/aiQuery.eval.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Levenshtein } from "autoevals";
33
import { AIQueryService } from "~/v3/services/aiQueryService.server";
44
import { runsSchema } from "~/v3/querySchemas";
55
import dotenv from "dotenv";
6-
import { traceAISDKModel } from "evalite/ai-sdk";
6+
import { wrapAISDKModel } from "evalite/ai-sdk";
77
import { openai } from "@ai-sdk/openai";
88

99
dotenv.config({ path: "../../.env" });
@@ -365,7 +365,7 @@ LIMIT 100`,
365365
];
366366
},
367367
task: async (input) => {
368-
const service = new AIQueryService([runsSchema], traceAISDKModel(openai("gpt-4o-mini")));
368+
const service = new AIQueryService([runsSchema], wrapAISDKModel(openai("gpt-4o-mini")));
369369

370370
const result = await service.call(input);
371371
return JSON.stringify(result);

apps/webapp/evals/aiRunFilter.eval.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
type QueryVersions,
99
} from "~/v3/services/aiRunFilterService.server";
1010
import dotenv from "dotenv";
11-
import { traceAISDKModel } from "evalite/ai-sdk";
11+
import { wrapAISDKModel } from "evalite/ai-sdk";
1212
import { openai } from "@ai-sdk/openai";
1313

1414
dotenv.config({ path: "../../.env" });
@@ -272,7 +272,7 @@ evalite("AI Run Filter", {
272272
queryQueues,
273273
queryTasks,
274274
},
275-
traceAISDKModel(openai("gpt-4o-mini"))
275+
wrapAISDKModel(openai("gpt-4o-mini"))
276276
);
277277

278278
const result = await service.call(input, "123456");

apps/webapp/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"/public/build"
2828
],
2929
"dependencies": {
30-
"@ai-sdk/openai": "^1.3.23",
30+
"@ai-sdk/openai": "^3.0.0",
3131
"@ai-sdk/react": "^3.0.0",
3232
"@ariakit/react": "^0.4.6",
3333
"@ariakit/react-core": "^0.4.6",
@@ -138,7 +138,7 @@
138138
"@vercel/sdk": "^1.19.1",
139139
"@whatwg-node/fetch": "^0.9.14",
140140
"@window-splitter/react": "1.1.3",
141-
"ai": "^4.3.19",
141+
"ai": "^6.0.116",
142142
"assert-never": "^1.2.1",
143143
"aws4fetch": "^1.0.18",
144144
"class-variance-authority": "^0.5.2",
@@ -286,7 +286,7 @@
286286
"eslint-plugin-import": "^2.29.1",
287287
"eslint-plugin-react-hooks": "^4.6.2",
288288
"eslint-plugin-turbo": "^2.0.4",
289-
"evalite": "^0.11.4",
289+
"evalite": "1.0.0-beta.16",
290290
"npm-run-all": "^4.1.5",
291291
"postcss-import": "^16.0.1",
292292
"postcss-loader": "^8.1.1",

0 commit comments

Comments
 (0)