Skip to content

Commit d763cb8

Browse files
fix(core): define RunEvent schema and update ApiClient to use it
1 parent 440c16c commit d763cb8

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

packages/core/src/v3/apiClient/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
RetrieveQueueParam,
4343
RetrieveRunResponse,
4444
RetrieveRunTraceResponseBody,
45+
RunEvent,
4546
ScheduleObject,
4647
SendInputStreamResponseBody,
4748
StreamBatchItemsResponse,
@@ -700,7 +701,7 @@ export class ApiClient {
700701

701702
listRunEvents(runId: string, requestOptions?: ZodFetchOptions) {
702703
return zodfetch(
703-
z.any(), // TODO: define a proper schema for this
704+
RunEvent.array(),
704705
`${this.baseUrl}/api/v1/runs/${runId}/events`,
705706
{
706707
method: "GET",

packages/core/src/v3/schemas/api-type.test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from "vitest";
2-
import { InitializeDeploymentRequestBody } from "./api.js";
2+
import { InitializeDeploymentRequestBody, RunEvent } from "./api.js";
33
import type { InitializeDeploymentRequestBody as InitializeDeploymentRequestBodyType } from "./api.js";
44

55
describe("InitializeDeploymentRequestBody", () => {
@@ -139,3 +139,60 @@ describe("InitializeDeploymentRequestBody", () => {
139139
});
140140
});
141141
});
142+
143+
describe("RunEvent Schema", () => {
144+
const validEvent = {
145+
spanId: "span_123",
146+
parentId: "span_root",
147+
runId: "run_abc",
148+
message: "Test event",
149+
style: {
150+
icon: "task",
151+
variant: "primary",
152+
},
153+
startTime: "2024-03-14T00:00:00Z",
154+
duration: 1234,
155+
isError: false,
156+
isPartial: false,
157+
isCancelled: false,
158+
level: "INFO",
159+
kind: "TASK",
160+
attemptNumber: 1,
161+
};
162+
163+
it("parses a valid event correctly", () => {
164+
const result = RunEvent.safeParse(validEvent);
165+
expect(result.success).toBe(true);
166+
if (result.success) {
167+
expect(result.data.spanId).toBe("span_123");
168+
expect(result.data.startTime).toBeInstanceOf(Date);
169+
expect(result.data.level).toBe("INFO");
170+
}
171+
});
172+
173+
it("fails on missing required fields", () => {
174+
const invalidEvent = { ...validEvent };
175+
delete (invalidEvent as any).spanId;
176+
const result = RunEvent.safeParse(invalidEvent);
177+
expect(result.success).toBe(false);
178+
});
179+
180+
it("fails on invalid level", () => {
181+
const invalidEvent = { ...validEvent, level: "INVALID_LEVEL" };
182+
const result = RunEvent.safeParse(invalidEvent);
183+
expect(result.success).toBe(false);
184+
});
185+
186+
it("coerces startTime to Date", () => {
187+
const result = RunEvent.parse(validEvent);
188+
expect(result.startTime).toBeInstanceOf(Date);
189+
expect(result.startTime.toISOString()).toBe("2024-03-14T00:00:00.000Z");
190+
});
191+
192+
it("allows optional parentId", () => {
193+
const eventWithoutParent = { ...validEvent };
194+
delete (eventWithoutParent as any).parentId;
195+
const result = RunEvent.safeParse(eventWithoutParent);
196+
expect(result.success).toBe(true);
197+
});
198+
});

packages/core/src/v3/schemas/api.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
} from "./common.js";
1010
import { BackgroundWorkerMetadata } from "./resources.js";
1111
import { DequeuedMessage, MachineResources } from "./runEngine.js";
12+
import { TaskEventStyle } from "./style.js";
13+
import { SpanEvents } from "./openTelemetry.js";
1214

1315
export const RunEngineVersion = z.union([z.literal("V1"), z.literal("V2")]);
1416

@@ -1639,3 +1641,24 @@ export const SendInputStreamResponseBody = z.object({
16391641
ok: z.boolean(),
16401642
});
16411643
export type SendInputStreamResponseBody = z.infer<typeof SendInputStreamResponseBody>;
1644+
export const TaskEventLevel = z.enum(["TRACE", "DEBUG", "INFO", "LOG", "WARN", "ERROR"]);
1645+
export type TaskEventLevel = z.infer<typeof TaskEventLevel>;
1646+
1647+
export const RunEvent = z.object({
1648+
spanId: z.string(),
1649+
parentId: z.string().optional(),
1650+
runId: z.string(),
1651+
message: z.string(),
1652+
style: TaskEventStyle,
1653+
startTime: z.coerce.date(),
1654+
duration: z.number(),
1655+
isError: z.boolean(),
1656+
isPartial: z.boolean(),
1657+
isCancelled: z.boolean(),
1658+
level: TaskEventLevel,
1659+
events: SpanEvents.optional(),
1660+
kind: z.string(),
1661+
attemptNumber: z.number().optional(),
1662+
});
1663+
1664+
export type RunEvent = z.infer<typeof RunEvent>;

0 commit comments

Comments
 (0)