@ag-ui/proto 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,277 @@
1
+ import {
2
+ EventType,
3
+ RunStartedEvent,
4
+ RunFinishedEvent,
5
+ RunErrorEvent,
6
+ StepStartedEvent,
7
+ StepFinishedEvent,
8
+ RawEvent,
9
+ CustomEvent,
10
+ } from "@ag-ui/core";
11
+ import { expect, describe, it } from "@jest/globals";
12
+ import { encode, decode } from "../src/proto";
13
+ import { expectRoundTripEquality } from "./test-utils";
14
+
15
+ describe("Run Events and Misc Events", () => {
16
+ describe("Run Events", () => {
17
+ it("should round-trip encode/decode RunStartedEvent event", () => {
18
+ const event: RunStartedEvent = {
19
+ type: EventType.RUN_STARTED,
20
+ timestamp: Date.now(),
21
+ threadId: "thread-1234",
22
+ runId: "run-5678",
23
+ };
24
+
25
+ expectRoundTripEquality(event);
26
+ });
27
+
28
+ it("should round-trip encode/decode RunFinishedEvent event", () => {
29
+ const event: RunFinishedEvent = {
30
+ type: EventType.RUN_FINISHED,
31
+ timestamp: Date.now(),
32
+ threadId: "thread-1234",
33
+ runId: "run-5678",
34
+ };
35
+
36
+ expectRoundTripEquality(event);
37
+ });
38
+
39
+ it("should round-trip encode/decode RunErrorEvent event", () => {
40
+ const event: RunErrorEvent = {
41
+ type: EventType.RUN_ERROR,
42
+ timestamp: Date.now(),
43
+ message: "Failed to execute tool call",
44
+ };
45
+
46
+ expectRoundTripEquality(event);
47
+ });
48
+
49
+ it("should handle RunErrorEvent with detailed error info", () => {
50
+ const event: RunErrorEvent = {
51
+ type: EventType.RUN_ERROR,
52
+ message: "API request failed",
53
+ code: "API_ERROR",
54
+ };
55
+
56
+ expectRoundTripEquality(event);
57
+ });
58
+ });
59
+
60
+ describe("Step Events", () => {
61
+ it("should round-trip encode/decode StepStartedEvent event", () => {
62
+ const event: StepStartedEvent = {
63
+ type: EventType.STEP_STARTED,
64
+ timestamp: Date.now(),
65
+ stepName: "data_analysis",
66
+ };
67
+
68
+ expectRoundTripEquality(event);
69
+ });
70
+
71
+ it("should round-trip encode/decode StepFinishedEvent event", () => {
72
+ const event: StepFinishedEvent = {
73
+ type: EventType.STEP_FINISHED,
74
+ timestamp: Date.now(),
75
+ stepName: "data_analysis",
76
+ };
77
+
78
+ expectRoundTripEquality(event);
79
+ });
80
+
81
+ it("should handle StepStartedEvent with minimal fields", () => {
82
+ const event: StepStartedEvent = {
83
+ type: EventType.STEP_STARTED,
84
+ stepName: "process_payment",
85
+ };
86
+
87
+ expectRoundTripEquality(event);
88
+ });
89
+
90
+ it("should handle StepFinishedEvent with minimal fields", () => {
91
+ const event: StepFinishedEvent = {
92
+ type: EventType.STEP_FINISHED,
93
+ stepName: "process_payment",
94
+ };
95
+
96
+ expectRoundTripEquality(event);
97
+ });
98
+ });
99
+
100
+ describe("RawEvent", () => {
101
+ it("should round-trip encode/decode RawEvent", () => {
102
+ const event: RawEvent = {
103
+ type: EventType.RAW,
104
+ timestamp: Date.now(),
105
+ event: {
106
+ type: "user_action",
107
+ action: "button_click",
108
+ elementId: "submit-btn",
109
+ timestamp: Date.now(),
110
+ },
111
+ source: "frontend",
112
+ };
113
+
114
+ expectRoundTripEquality(event);
115
+ });
116
+
117
+ it("should handle complex nested data in RawEvent", () => {
118
+ const event: RawEvent = {
119
+ type: EventType.RAW,
120
+ event: {
121
+ type: "analytics_event",
122
+ session: {
123
+ id: "sess-12345",
124
+ user: {
125
+ id: "user-456",
126
+ attributes: {
127
+ plan: "premium",
128
+ signupDate: "2023-01-15",
129
+ preferences: ["feature1", "feature2"],
130
+ },
131
+ },
132
+ actions: [
133
+ { type: "page_view", path: "/home", timestamp: 1676480210000 },
134
+ {
135
+ type: "button_click",
136
+ elementId: "cta-1",
137
+ timestamp: 1676480215000,
138
+ },
139
+ {
140
+ type: "form_submit",
141
+ formId: "signup",
142
+ timestamp: 1676480230000,
143
+ data: { email: "user@example.com" },
144
+ },
145
+ ],
146
+ },
147
+ metadata: {
148
+ source: "web",
149
+ version: "1.2.3",
150
+ environment: "production",
151
+ },
152
+ },
153
+ };
154
+
155
+ expectRoundTripEquality(event);
156
+ });
157
+ });
158
+
159
+ describe("CustomEvent", () => {
160
+ it("should round-trip encode/decode CustomEvent", () => {
161
+ const event: CustomEvent = {
162
+ type: EventType.CUSTOM,
163
+ timestamp: Date.now(),
164
+ name: "user_preference_updated",
165
+ value: {
166
+ theme: "dark",
167
+ fontSize: "medium",
168
+ notifications: true,
169
+ },
170
+ };
171
+
172
+ expectRoundTripEquality(event);
173
+ });
174
+
175
+ it("should handle CustomEvent without a value", () => {
176
+ const event: CustomEvent = {
177
+ type: EventType.CUSTOM,
178
+ name: "heartbeat",
179
+ };
180
+
181
+ expectRoundTripEquality(event);
182
+ });
183
+
184
+ it("should handle complex values in CustomEvent", () => {
185
+ const event: CustomEvent = {
186
+ type: EventType.CUSTOM,
187
+ name: "analytics_update",
188
+ value: {
189
+ metrics: {
190
+ active_users: 12345,
191
+ conversion_rate: 0.0354,
192
+ revenue: 98765.43,
193
+ },
194
+ segments: [
195
+ { name: "new_users", count: 543, growth: 0.12 },
196
+ { name: "returning_users", count: 876, growth: -0.05 },
197
+ { name: "power_users", count: 234, growth: 0.08 },
198
+ ],
199
+ period: {
200
+ start: "2023-01-01",
201
+ end: "2023-01-31",
202
+ duration_days: 31,
203
+ },
204
+ trends: {
205
+ daily: [10, 12, 15, 14, 18, 20, 22],
206
+ weekly: [70, 85, 92, 105],
207
+ monthly: [320, 370],
208
+ },
209
+ },
210
+ };
211
+
212
+ expectRoundTripEquality(event);
213
+ });
214
+ });
215
+
216
+ describe("Edge Cases", () => {
217
+ it("should handle basic fields for each event type", () => {
218
+ const events = [
219
+ {
220
+ type: EventType.RUN_STARTED,
221
+ threadId: "thread-basic",
222
+ runId: "run-basic",
223
+ },
224
+ {
225
+ type: EventType.RUN_FINISHED,
226
+ threadId: "thread-basic",
227
+ runId: "run-basic",
228
+ },
229
+ { type: EventType.CUSTOM, name: "empty" },
230
+ ];
231
+
232
+ for (const event of events) {
233
+ const encoded = encode(event);
234
+ const decoded = decode(encoded);
235
+ expect(decoded.type).toBe(event.type);
236
+ }
237
+ });
238
+
239
+ it("should handle events with all base fields", () => {
240
+ const runEvents = [
241
+ {
242
+ type: EventType.RUN_STARTED,
243
+ timestamp: Date.now(),
244
+ threadId: "thread-full",
245
+ runId: "run-full",
246
+ rawEvent: { original: "data", from: "external_system" },
247
+ },
248
+ {
249
+ type: EventType.RUN_FINISHED,
250
+ timestamp: Date.now(),
251
+ threadId: "thread-full",
252
+ runId: "run-full",
253
+ rawEvent: { original: "data", from: "external_system" },
254
+ },
255
+ ];
256
+
257
+ const nonRunEvents = [
258
+ {
259
+ type: EventType.RUN_ERROR,
260
+ message: "Test error",
261
+ timestamp: Date.now(),
262
+ rawEvent: { original: "data", from: "external_system" },
263
+ },
264
+ {
265
+ type: EventType.CUSTOM,
266
+ name: "full_event",
267
+ timestamp: Date.now(),
268
+ rawEvent: { original: "data", from: "external_system" },
269
+ },
270
+ ];
271
+
272
+ for (const event of [...runEvents, ...nonRunEvents]) {
273
+ expectRoundTripEquality(event);
274
+ }
275
+ });
276
+ });
277
+ });
@@ -0,0 +1,237 @@
1
+ import { EventType, StateSnapshotEvent, StateDeltaEvent } from "@ag-ui/core";
2
+ import { expect, describe, it } from "@jest/globals";
3
+ import { encode, decode } from "../src/proto";
4
+ import { expectRoundTripEquality } from "./test-utils";
5
+
6
+ describe("State Events", () => {
7
+ describe("StateSnapshotEvent", () => {
8
+ it("should round-trip encode/decode correctly", () => {
9
+ const event: StateSnapshotEvent = {
10
+ type: EventType.STATE_SNAPSHOT,
11
+ timestamp: Date.now(),
12
+ snapshot: {
13
+ counter: 42,
14
+ items: ["apple", "banana", "cherry"],
15
+ config: {
16
+ enabled: true,
17
+ maxRetries: 3,
18
+ },
19
+ },
20
+ };
21
+
22
+ expectRoundTripEquality(event);
23
+ });
24
+
25
+ it("should handle empty snapshot object", () => {
26
+ const event: StateSnapshotEvent = {
27
+ type: EventType.STATE_SNAPSHOT,
28
+ snapshot: {},
29
+ };
30
+
31
+ expectRoundTripEquality(event);
32
+ });
33
+
34
+ it("should handle complex nested objects", () => {
35
+ const event: StateSnapshotEvent = {
36
+ type: EventType.STATE_SNAPSHOT,
37
+ snapshot: {
38
+ userProfile: {
39
+ name: "John Doe",
40
+ age: 30,
41
+ contact: {
42
+ email: "john@example.com",
43
+ phone: "+1234567890",
44
+ address: {
45
+ street: "123 Main St",
46
+ city: "Anytown",
47
+ country: "USA",
48
+ coordinates: {
49
+ lat: 37.7749,
50
+ lng: -122.4194,
51
+ },
52
+ },
53
+ },
54
+ preferences: {
55
+ theme: "dark",
56
+ notifications: true,
57
+ privateProfile: false,
58
+ },
59
+ },
60
+ serviceConfig: {
61
+ endpoints: [
62
+ {
63
+ name: "api1",
64
+ url: "https://api1.example.com",
65
+ methods: ["GET", "POST"],
66
+ },
67
+ {
68
+ name: "api2",
69
+ url: "https://api2.example.com",
70
+ methods: ["GET"],
71
+ },
72
+ ],
73
+ retryPolicy: {
74
+ maxRetries: 3,
75
+ backoff: "exponential",
76
+ timeouts: [1000, 2000, 4000],
77
+ },
78
+ },
79
+ stats: {
80
+ visits: 1042,
81
+ conversions: 123,
82
+ bounceRate: 0.25,
83
+ dataPoints: [
84
+ { date: "2023-01-01", value: 10 },
85
+ { date: "2023-01-02", value: 15 },
86
+ { date: "2023-01-03", value: 8 },
87
+ ],
88
+ },
89
+ },
90
+ };
91
+
92
+ expectRoundTripEquality(event);
93
+ });
94
+
95
+ it("should handle special values in snapshot", () => {
96
+ const event: StateSnapshotEvent = {
97
+ type: EventType.STATE_SNAPSHOT,
98
+ snapshot: {
99
+ nullValue: null,
100
+ emptyString: "",
101
+ zero: 0,
102
+ negativeNumber: -123,
103
+ floatNumber: 3.14159,
104
+ emptyArray: [],
105
+ emptyObject: {},
106
+ boolValues: { true: true, false: false },
107
+ infinityValue: Infinity,
108
+ nanValue: NaN,
109
+ dateString: new Date().toISOString(),
110
+ },
111
+ };
112
+
113
+ const encoded = encode(event);
114
+ const decoded = decode(encoded) as StateSnapshotEvent;
115
+
116
+ // Check specific values that might need special handling
117
+ expect(decoded.snapshot.nullValue).toBe(event.snapshot.nullValue);
118
+ expect(decoded.snapshot.emptyString).toBe(event.snapshot.emptyString);
119
+ expect(decoded.snapshot.zero).toBe(event.snapshot.zero);
120
+ expect(decoded.snapshot.negativeNumber).toBe(event.snapshot.negativeNumber);
121
+ expect(decoded.snapshot.floatNumber).toBe(event.snapshot.floatNumber);
122
+ expect(decoded.snapshot.emptyArray).toEqual(event.snapshot.emptyArray);
123
+ expect(decoded.snapshot.emptyObject).toEqual(event.snapshot.emptyObject);
124
+ expect(decoded.snapshot.boolValues).toEqual(event.snapshot.boolValues);
125
+ expect(decoded.snapshot.dateString).toBe(event.snapshot.dateString);
126
+
127
+ // Infinity/NaN don't survive JSON.stringify, so they may not be exactly equal
128
+ if (Number.isNaN(decoded.snapshot.nanValue)) {
129
+ expect(Number.isNaN(event.snapshot.nanValue)).toBe(true);
130
+ }
131
+ });
132
+ });
133
+
134
+ describe("StateDeltaEvent", () => {
135
+ it("should round-trip encode/decode correctly", () => {
136
+ const event: StateDeltaEvent = {
137
+ type: EventType.STATE_DELTA,
138
+ timestamp: Date.now(),
139
+ delta: [
140
+ { op: "add", path: "/counter", value: 42 },
141
+ { op: "add", path: "/items", value: ["apple", "banana", "cherry"] },
142
+ ],
143
+ };
144
+
145
+ expectRoundTripEquality(event);
146
+ });
147
+
148
+ it("should handle all JSON Patch operation types", () => {
149
+ const event: StateDeltaEvent = {
150
+ type: EventType.STATE_DELTA,
151
+ delta: [
152
+ { op: "add", path: "/users/123", value: { name: "John", age: 30 } },
153
+ { op: "remove", path: "/users/456" },
154
+ { op: "replace", path: "/users/789/name", value: "Jane Doe" },
155
+ { op: "move", from: "/users/old", path: "/users/new" },
156
+ {
157
+ op: "copy",
158
+ from: "/templates/default",
159
+ path: "/users/123/template",
160
+ },
161
+ { op: "test", path: "/users/123/active", value: true },
162
+ ],
163
+ };
164
+
165
+ expectRoundTripEquality(event);
166
+ });
167
+
168
+ it("should handle complex values in add operations", () => {
169
+ const event: StateDeltaEvent = {
170
+ type: EventType.STATE_DELTA,
171
+ delta: [
172
+ {
173
+ op: "add",
174
+ path: "/data",
175
+ value: {
176
+ nested: {
177
+ array: [1, 2, 3],
178
+ object: { key: "value" },
179
+ },
180
+ boolean: true,
181
+ number: 42,
182
+ },
183
+ },
184
+ ],
185
+ };
186
+
187
+ expectRoundTripEquality(event);
188
+ });
189
+
190
+ it("should handle array operations", () => {
191
+ const event: StateDeltaEvent = {
192
+ type: EventType.STATE_DELTA,
193
+ delta: [
194
+ { op: "add", path: "/items", value: [] },
195
+ { op: "add", path: "/items/0", value: "first" },
196
+ { op: "add", path: "/items/-", value: "last" },
197
+ { op: "replace", path: "/items/0", value: "updated first" },
198
+ { op: "remove", path: "/items/1" },
199
+ ],
200
+ };
201
+
202
+ expectRoundTripEquality(event);
203
+ });
204
+
205
+ it("should handle special characters in paths", () => {
206
+ const event: StateDeltaEvent = {
207
+ type: EventType.STATE_DELTA,
208
+ delta: [
209
+ { op: "add", path: "/special~0field", value: "value with tilde" },
210
+ { op: "add", path: "/special~1field", value: "value with slash" },
211
+ {
212
+ op: "add",
213
+ path: "/special/field",
214
+ value: "value with actual slash",
215
+ },
216
+ { op: "add", path: '/special"field', value: "value with quote" },
217
+ {
218
+ op: "add",
219
+ path: "/emoji\u{1F680}field",
220
+ value: "value with emoji",
221
+ },
222
+ ],
223
+ };
224
+
225
+ expectRoundTripEquality(event);
226
+ });
227
+
228
+ it("should handle empty delta array", () => {
229
+ const event: StateDeltaEvent = {
230
+ type: EventType.STATE_DELTA,
231
+ delta: [],
232
+ };
233
+
234
+ expectRoundTripEquality(event);
235
+ });
236
+ });
237
+ });
@@ -0,0 +1,33 @@
1
+ import { BaseEvent } from "@ag-ui/core";
2
+ import { encode, decode } from "../src/proto";
3
+ import { expect, describe, it } from "@jest/globals";
4
+
5
+ /**
6
+ * Performs a round-trip encode-decode on an event and returns the decoded result
7
+ */
8
+ export function roundTrip<T extends BaseEvent>(event: T): T {
9
+ const encoded = encode(event);
10
+ return decode(encoded) as T;
11
+ }
12
+
13
+ /**
14
+ * Verifies that an event is the same after round-trip encoding and decoding
15
+ */
16
+ export function expectRoundTripEquality<T extends BaseEvent>(event: T): void {
17
+ const decoded = roundTrip(event);
18
+
19
+ // Verify all properties match
20
+ for (const key in event) {
21
+ if (Object.prototype.hasOwnProperty.call(event, key)) {
22
+ expect(decoded[key]).toEqual(event[key]);
23
+ }
24
+ }
25
+ }
26
+
27
+ // Add a simple test to prevent "Your test suite must contain at least one test" error
28
+ describe("Test Utilities", () => {
29
+ it("should exist as a module for other tests to import from", () => {
30
+ expect(typeof roundTrip).toBe("function");
31
+ expect(typeof expectRoundTripEquality).toBe("function");
32
+ });
33
+ });