@langwatch/scenario 0.2.0 → 0.2.1
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.
- package/dist/chunk-ZMHTHRDR.mjs +618 -0
- package/dist/index.js +232 -87
- package/dist/index.mjs +44 -167
- package/dist/integrations/vitest/setup.js +280 -105
- package/dist/integrations/vitest/setup.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-ORWSJC5F.mjs +0 -309
|
@@ -30,6 +30,23 @@ var import_vitest = require("vitest");
|
|
|
30
30
|
// src/events/event-bus.ts
|
|
31
31
|
var import_rxjs = require("rxjs");
|
|
32
32
|
|
|
33
|
+
// src/config/load.ts
|
|
34
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
35
|
+
var import_node_path = __toESM(require("path"));
|
|
36
|
+
var import_node_url = require("url");
|
|
37
|
+
|
|
38
|
+
// src/domain/core/config.ts
|
|
39
|
+
var import_zod = require("zod");
|
|
40
|
+
var scenarioProjectConfigSchema = import_zod.z.object({
|
|
41
|
+
defaultModel: import_zod.z.object({
|
|
42
|
+
model: import_zod.z.custom(),
|
|
43
|
+
temperature: import_zod.z.number().min(0).max(1).optional().default(0),
|
|
44
|
+
maxTokens: import_zod.z.number().optional()
|
|
45
|
+
}).optional(),
|
|
46
|
+
langwatchEndpoint: import_zod.z.string().optional(),
|
|
47
|
+
langwatchApiKey: import_zod.z.string().optional()
|
|
48
|
+
}).strict();
|
|
49
|
+
|
|
33
50
|
// src/utils/logger.ts
|
|
34
51
|
var Logger = class _Logger {
|
|
35
52
|
constructor(context) {
|
|
@@ -41,21 +58,25 @@ var Logger = class _Logger {
|
|
|
41
58
|
static create(context) {
|
|
42
59
|
return new _Logger(context);
|
|
43
60
|
}
|
|
61
|
+
getLogLevel() {
|
|
62
|
+
return env.SCENARIO_LOG_LEVEL ?? "INFO" /* INFO */;
|
|
63
|
+
}
|
|
64
|
+
getLogLevelIndex(level) {
|
|
65
|
+
return Object.values(LogLevel).indexOf(level);
|
|
66
|
+
}
|
|
44
67
|
/**
|
|
45
68
|
* Checks if logging should occur based on LOG_LEVEL env var
|
|
46
69
|
*/
|
|
47
70
|
shouldLog(level) {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const currentLevelIndex = levels.indexOf(logLevel);
|
|
51
|
-
const requestedLevelIndex = levels.indexOf(level);
|
|
71
|
+
const currentLevelIndex = this.getLogLevelIndex(this.getLogLevel());
|
|
72
|
+
const requestedLevelIndex = this.getLogLevelIndex(level);
|
|
52
73
|
return currentLevelIndex >= 0 && requestedLevelIndex <= currentLevelIndex;
|
|
53
74
|
}
|
|
54
75
|
formatMessage(message) {
|
|
55
76
|
return this.context ? `[${this.context}] ${message}` : message;
|
|
56
77
|
}
|
|
57
78
|
error(message, data) {
|
|
58
|
-
if (this.shouldLog("
|
|
79
|
+
if (this.shouldLog("ERROR" /* ERROR */)) {
|
|
59
80
|
const formattedMessage = this.formatMessage(message);
|
|
60
81
|
if (data) {
|
|
61
82
|
console.error(formattedMessage, data);
|
|
@@ -65,7 +86,7 @@ var Logger = class _Logger {
|
|
|
65
86
|
}
|
|
66
87
|
}
|
|
67
88
|
warn(message, data) {
|
|
68
|
-
if (this.shouldLog("
|
|
89
|
+
if (this.shouldLog("WARN" /* WARN */)) {
|
|
69
90
|
const formattedMessage = this.formatMessage(message);
|
|
70
91
|
if (data) {
|
|
71
92
|
console.warn(formattedMessage, data);
|
|
@@ -75,7 +96,7 @@ var Logger = class _Logger {
|
|
|
75
96
|
}
|
|
76
97
|
}
|
|
77
98
|
info(message, data) {
|
|
78
|
-
if (this.shouldLog("
|
|
99
|
+
if (this.shouldLog("INFO" /* INFO */)) {
|
|
79
100
|
const formattedMessage = this.formatMessage(message);
|
|
80
101
|
if (data) {
|
|
81
102
|
console.info(formattedMessage, data);
|
|
@@ -85,7 +106,7 @@ var Logger = class _Logger {
|
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
108
|
debug(message, data) {
|
|
88
|
-
if (this.shouldLog("
|
|
109
|
+
if (this.shouldLog("DEBUG" /* DEBUG */)) {
|
|
89
110
|
const formattedMessage = this.formatMessage(message);
|
|
90
111
|
if (data) {
|
|
91
112
|
console.log(formattedMessage, data);
|
|
@@ -96,78 +117,149 @@ var Logger = class _Logger {
|
|
|
96
117
|
}
|
|
97
118
|
};
|
|
98
119
|
|
|
99
|
-
// src/
|
|
100
|
-
var
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
// src/config/env.ts
|
|
121
|
+
var import_zod2 = require("zod");
|
|
122
|
+
|
|
123
|
+
// src/config/log-levels.ts
|
|
124
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
125
|
+
LogLevel2["ERROR"] = "ERROR";
|
|
126
|
+
LogLevel2["WARN"] = "WARN";
|
|
127
|
+
LogLevel2["INFO"] = "INFO";
|
|
128
|
+
LogLevel2["DEBUG"] = "DEBUG";
|
|
129
|
+
return LogLevel2;
|
|
130
|
+
})(LogLevel || {});
|
|
131
|
+
|
|
132
|
+
// src/config/env.ts
|
|
133
|
+
var envSchema = import_zod2.z.object({
|
|
134
|
+
/**
|
|
135
|
+
* LangWatch API key for event reporting.
|
|
136
|
+
* If not provided, events will not be sent to LangWatch.
|
|
137
|
+
*/
|
|
138
|
+
LANGWATCH_API_KEY: import_zod2.z.string().optional(),
|
|
139
|
+
/**
|
|
140
|
+
* LangWatch endpoint URL for event reporting.
|
|
141
|
+
* Defaults to the production LangWatch endpoint.
|
|
142
|
+
*/
|
|
143
|
+
LANGWATCH_ENDPOINT: import_zod2.z.string().url().default("https://app.langwatch.ai"),
|
|
144
|
+
/**
|
|
145
|
+
* Disables simulation report info messages when set to any truthy value.
|
|
146
|
+
* Useful for CI/CD environments or when you want cleaner output.
|
|
147
|
+
*/
|
|
148
|
+
SCENARIO_DISABLE_SIMULATION_REPORT_INFO: import_zod2.z.string().optional().transform((val) => Boolean(val)),
|
|
149
|
+
/**
|
|
150
|
+
* Node environment - affects logging and behavior.
|
|
151
|
+
* Defaults to 'development' if not specified.
|
|
152
|
+
*/
|
|
153
|
+
NODE_ENV: import_zod2.z.enum(["development", "production", "test"]).default("development"),
|
|
154
|
+
/**
|
|
155
|
+
* Log level for the scenario package.
|
|
156
|
+
* Defaults to 'info' if not specified.
|
|
157
|
+
*/
|
|
158
|
+
SCENARIO_LOG_LEVEL: import_zod2.z.nativeEnum(LogLevel).optional(),
|
|
159
|
+
/**
|
|
160
|
+
* Scenario batch run ID.
|
|
161
|
+
* If not provided, a random ID will be generated.
|
|
162
|
+
*/
|
|
163
|
+
SCENARIO_BATCH_RUN_ID: import_zod2.z.string().optional()
|
|
164
|
+
});
|
|
165
|
+
var env = envSchema.parse(process.env);
|
|
166
|
+
|
|
167
|
+
// src/config/index.ts
|
|
168
|
+
var logger = new Logger("scenario.config");
|
|
169
|
+
|
|
170
|
+
// src/utils/ids.ts
|
|
171
|
+
var import_xksuid = require("xksuid");
|
|
172
|
+
function getBatchRunId() {
|
|
173
|
+
if (!env.SCENARIO_BATCH_RUN_ID) {
|
|
174
|
+
env.SCENARIO_BATCH_RUN_ID = `scenariobatchrun_${(0, import_xksuid.generate)()}`;
|
|
175
|
+
}
|
|
176
|
+
return env.SCENARIO_BATCH_RUN_ID;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/events/event-alert-message-logger.ts
|
|
180
|
+
var EventAlertMessageLogger = class _EventAlertMessageLogger {
|
|
181
|
+
static shownBatchIds = /* @__PURE__ */ new Set();
|
|
182
|
+
/**
|
|
183
|
+
* Shows a fancy greeting message about simulation reporting status.
|
|
184
|
+
* Only shows once per batch run to avoid spam.
|
|
185
|
+
*/
|
|
186
|
+
handleGreeting() {
|
|
187
|
+
if (this.isGreetingDisabled()) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const batchRunId = getBatchRunId();
|
|
191
|
+
if (_EventAlertMessageLogger.shownBatchIds.has(batchRunId)) {
|
|
192
|
+
return;
|
|
118
193
|
}
|
|
194
|
+
_EventAlertMessageLogger.shownBatchIds.add(batchRunId);
|
|
195
|
+
this.displayGreeting(batchRunId);
|
|
119
196
|
}
|
|
120
197
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
198
|
+
* Shows a fancy message about how to watch the simulation.
|
|
199
|
+
* Called when a run started event is received with a session ID.
|
|
123
200
|
*/
|
|
124
|
-
|
|
125
|
-
this.
|
|
126
|
-
event
|
|
127
|
-
});
|
|
128
|
-
if (!this.eventsEndpoint) {
|
|
129
|
-
this.logger.warn(
|
|
130
|
-
"No LANGWATCH_ENDPOINT configured, skipping event posting"
|
|
131
|
-
);
|
|
201
|
+
handleWatchMessage(params) {
|
|
202
|
+
if (this.isGreetingDisabled()) {
|
|
132
203
|
return;
|
|
133
204
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
205
|
+
this.displayWatchMessage(params);
|
|
206
|
+
}
|
|
207
|
+
isGreetingDisabled() {
|
|
208
|
+
return env.SCENARIO_DISABLE_SIMULATION_REPORT_INFO === true;
|
|
209
|
+
}
|
|
210
|
+
displayGreeting(batchRunId) {
|
|
211
|
+
const separator = "\u2500".repeat(60);
|
|
212
|
+
if (!env.LANGWATCH_API_KEY) {
|
|
213
|
+
console.log(`
|
|
214
|
+
${separator}`);
|
|
215
|
+
console.log("\u{1F680} LangWatch Simulation Reporting");
|
|
216
|
+
console.log(`${separator}`);
|
|
217
|
+
console.log("\u27A1\uFE0F API key not configured");
|
|
218
|
+
console.log(" Simulations will only output final results");
|
|
219
|
+
console.log("");
|
|
220
|
+
console.log("\u{1F4A1} To visualize conversations in real time:");
|
|
221
|
+
console.log(" \u2022 Set LANGWATCH_API_KEY environment variable");
|
|
222
|
+
console.log(" \u2022 Or configure apiKey in scenario.config.js");
|
|
223
|
+
console.log("");
|
|
224
|
+
console.log(`\u{1F4E6} Batch Run ID: ${batchRunId}`);
|
|
225
|
+
console.log(`${separator}
|
|
226
|
+
`);
|
|
227
|
+
} else {
|
|
228
|
+
console.log(`
|
|
229
|
+
${separator}`);
|
|
230
|
+
console.log("\u{1F680} LangWatch Simulation Reporting");
|
|
231
|
+
console.log(`${separator}`);
|
|
232
|
+
console.log("\u2705 Simulation reporting enabled");
|
|
233
|
+
console.log(` Endpoint: ${env.LANGWATCH_ENDPOINT}`);
|
|
234
|
+
console.log(
|
|
235
|
+
` API Key: ${env.LANGWATCH_API_KEY.length > 0 ? "Configured" : "Not configured"}`
|
|
145
236
|
);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const errorText = await response.text();
|
|
151
|
-
this.logger.error(`[${event.type}] Event POST failed:`, {
|
|
152
|
-
status: response.status,
|
|
153
|
-
statusText: response.statusText,
|
|
154
|
-
error: errorText,
|
|
155
|
-
event
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
} catch (error) {
|
|
159
|
-
this.logger.error(`[${event.type}] Event POST error:`, {
|
|
160
|
-
error,
|
|
161
|
-
event,
|
|
162
|
-
endpoint: this.eventsEndpoint
|
|
163
|
-
});
|
|
237
|
+
console.log("");
|
|
238
|
+
console.log(`\u{1F4E6} Batch Run ID: ${batchRunId}`);
|
|
239
|
+
console.log(`${separator}
|
|
240
|
+
`);
|
|
164
241
|
}
|
|
165
242
|
}
|
|
243
|
+
displayWatchMessage(params) {
|
|
244
|
+
const separator = "\u2500".repeat(60);
|
|
245
|
+
const setUrl = params.setUrl;
|
|
246
|
+
const batchUrl = `${setUrl}/${getBatchRunId()}`;
|
|
247
|
+
console.log(`
|
|
248
|
+
${separator}`);
|
|
249
|
+
console.log("\u{1F440} Watch Your Simulation Live");
|
|
250
|
+
console.log(`${separator}`);
|
|
251
|
+
console.log("\u{1F310} Open in your browser:");
|
|
252
|
+
console.log(` Scenario Set: ${setUrl}`);
|
|
253
|
+
console.log(` Batch Run: ${batchUrl}`);
|
|
254
|
+
console.log("");
|
|
255
|
+
console.log(`${separator}
|
|
256
|
+
`);
|
|
257
|
+
}
|
|
166
258
|
};
|
|
167
259
|
|
|
168
260
|
// src/events/schema.ts
|
|
169
261
|
var import_core = require("@ag-ui/core");
|
|
170
|
-
var
|
|
262
|
+
var import_zod3 = require("zod");
|
|
171
263
|
var Verdict = /* @__PURE__ */ ((Verdict2) => {
|
|
172
264
|
Verdict2["SUCCESS"] = "success";
|
|
173
265
|
Verdict2["FAILURE"] = "failure";
|
|
@@ -183,70 +275,144 @@ var ScenarioRunStatus = /* @__PURE__ */ ((ScenarioRunStatus2) => {
|
|
|
183
275
|
ScenarioRunStatus2["FAILED"] = "FAILED";
|
|
184
276
|
return ScenarioRunStatus2;
|
|
185
277
|
})(ScenarioRunStatus || {});
|
|
186
|
-
var baseEventSchema =
|
|
187
|
-
type:
|
|
188
|
-
timestamp:
|
|
189
|
-
rawEvent:
|
|
278
|
+
var baseEventSchema = import_zod3.z.object({
|
|
279
|
+
type: import_zod3.z.nativeEnum(import_core.EventType),
|
|
280
|
+
timestamp: import_zod3.z.number(),
|
|
281
|
+
rawEvent: import_zod3.z.any().optional()
|
|
190
282
|
});
|
|
191
|
-
var batchRunIdSchema =
|
|
192
|
-
var scenarioRunIdSchema =
|
|
193
|
-
var scenarioIdSchema =
|
|
283
|
+
var batchRunIdSchema = import_zod3.z.string();
|
|
284
|
+
var scenarioRunIdSchema = import_zod3.z.string();
|
|
285
|
+
var scenarioIdSchema = import_zod3.z.string();
|
|
194
286
|
var baseScenarioEventSchema = baseEventSchema.extend({
|
|
195
287
|
batchRunId: batchRunIdSchema,
|
|
196
288
|
scenarioId: scenarioIdSchema,
|
|
197
289
|
scenarioRunId: scenarioRunIdSchema,
|
|
198
|
-
scenarioSetId:
|
|
290
|
+
scenarioSetId: import_zod3.z.string().optional().default("default")
|
|
199
291
|
});
|
|
200
292
|
var scenarioRunStartedSchema = baseScenarioEventSchema.extend({
|
|
201
|
-
type:
|
|
202
|
-
metadata:
|
|
203
|
-
name:
|
|
204
|
-
description:
|
|
293
|
+
type: import_zod3.z.literal("SCENARIO_RUN_STARTED" /* RUN_STARTED */),
|
|
294
|
+
metadata: import_zod3.z.object({
|
|
295
|
+
name: import_zod3.z.string().optional(),
|
|
296
|
+
description: import_zod3.z.string().optional()
|
|
205
297
|
})
|
|
206
298
|
});
|
|
207
|
-
var scenarioResultsSchema =
|
|
208
|
-
verdict:
|
|
209
|
-
reasoning:
|
|
210
|
-
metCriteria:
|
|
211
|
-
unmetCriteria:
|
|
212
|
-
error:
|
|
299
|
+
var scenarioResultsSchema = import_zod3.z.object({
|
|
300
|
+
verdict: import_zod3.z.nativeEnum(Verdict),
|
|
301
|
+
reasoning: import_zod3.z.string().optional(),
|
|
302
|
+
metCriteria: import_zod3.z.array(import_zod3.z.string()),
|
|
303
|
+
unmetCriteria: import_zod3.z.array(import_zod3.z.string()),
|
|
304
|
+
error: import_zod3.z.string().optional()
|
|
213
305
|
});
|
|
214
306
|
var scenarioRunFinishedSchema = baseScenarioEventSchema.extend({
|
|
215
|
-
type:
|
|
216
|
-
status:
|
|
307
|
+
type: import_zod3.z.literal("SCENARIO_RUN_FINISHED" /* RUN_FINISHED */),
|
|
308
|
+
status: import_zod3.z.nativeEnum(ScenarioRunStatus),
|
|
217
309
|
results: scenarioResultsSchema.optional().nullable()
|
|
218
310
|
});
|
|
219
311
|
var scenarioMessageSnapshotSchema = import_core.MessagesSnapshotEventSchema.merge(
|
|
220
312
|
baseScenarioEventSchema.extend({
|
|
221
|
-
type:
|
|
313
|
+
type: import_zod3.z.literal("SCENARIO_MESSAGE_SNAPSHOT" /* MESSAGE_SNAPSHOT */)
|
|
222
314
|
})
|
|
223
315
|
);
|
|
224
|
-
var scenarioEventSchema =
|
|
316
|
+
var scenarioEventSchema = import_zod3.z.discriminatedUnion("type", [
|
|
225
317
|
scenarioRunStartedSchema,
|
|
226
318
|
scenarioRunFinishedSchema,
|
|
227
319
|
scenarioMessageSnapshotSchema
|
|
228
320
|
]);
|
|
229
|
-
var successSchema =
|
|
230
|
-
var errorSchema =
|
|
231
|
-
var stateSchema =
|
|
232
|
-
state:
|
|
233
|
-
messages:
|
|
234
|
-
status:
|
|
321
|
+
var successSchema = import_zod3.z.object({ success: import_zod3.z.boolean() });
|
|
322
|
+
var errorSchema = import_zod3.z.object({ error: import_zod3.z.string() });
|
|
323
|
+
var stateSchema = import_zod3.z.object({
|
|
324
|
+
state: import_zod3.z.object({
|
|
325
|
+
messages: import_zod3.z.array(import_zod3.z.any()),
|
|
326
|
+
status: import_zod3.z.string()
|
|
235
327
|
})
|
|
236
328
|
});
|
|
237
|
-
var runsSchema =
|
|
238
|
-
var eventsSchema =
|
|
329
|
+
var runsSchema = import_zod3.z.object({ runs: import_zod3.z.array(import_zod3.z.string()) });
|
|
330
|
+
var eventsSchema = import_zod3.z.object({ events: import_zod3.z.array(scenarioEventSchema) });
|
|
331
|
+
|
|
332
|
+
// src/events/event-reporter.ts
|
|
333
|
+
var EventReporter = class {
|
|
334
|
+
apiKey;
|
|
335
|
+
eventsEndpoint;
|
|
336
|
+
eventAlertMessageLogger;
|
|
337
|
+
logger = new Logger("scenario.events.EventReporter");
|
|
338
|
+
constructor(config) {
|
|
339
|
+
this.apiKey = config.apiKey ?? "";
|
|
340
|
+
this.eventsEndpoint = new URL("/api/scenario-events", config.endpoint);
|
|
341
|
+
this.eventAlertMessageLogger = new EventAlertMessageLogger();
|
|
342
|
+
this.eventAlertMessageLogger.handleGreeting();
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Posts an event to the configured endpoint.
|
|
346
|
+
* Logs success/failure but doesn't throw - event posting shouldn't break scenario execution.
|
|
347
|
+
*/
|
|
348
|
+
async postEvent(event) {
|
|
349
|
+
const result = {};
|
|
350
|
+
this.logger.debug(`[${event.type}] Posting event`, { event });
|
|
351
|
+
const processedEvent = this.processEventForApi(event);
|
|
352
|
+
try {
|
|
353
|
+
const response = await fetch(this.eventsEndpoint.href, {
|
|
354
|
+
method: "POST",
|
|
355
|
+
body: JSON.stringify(processedEvent),
|
|
356
|
+
headers: {
|
|
357
|
+
"Content-Type": "application/json",
|
|
358
|
+
"X-Auth-Token": this.apiKey
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
this.logger.debug(
|
|
362
|
+
`[${event.type}] Event POST response status: ${response.status}`
|
|
363
|
+
);
|
|
364
|
+
if (response.ok) {
|
|
365
|
+
const data = await response.json();
|
|
366
|
+
this.logger.debug(`[${event.type}] Event POST response:`, data);
|
|
367
|
+
result.setUrl = data.url;
|
|
368
|
+
} else {
|
|
369
|
+
const errorText = await response.text();
|
|
370
|
+
this.logger.error(`[${event.type}] Event POST failed:`, {
|
|
371
|
+
status: response.status,
|
|
372
|
+
statusText: response.statusText,
|
|
373
|
+
error: errorText,
|
|
374
|
+
event: JSON.stringify(processedEvent)
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
} catch (error) {
|
|
378
|
+
this.logger.error(`[${event.type}] Event POST error:`, {
|
|
379
|
+
error,
|
|
380
|
+
event: JSON.stringify(processedEvent),
|
|
381
|
+
endpoint: this.eventsEndpoint.href
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Processes event data to ensure API compatibility.
|
|
388
|
+
* Converts message content objects to strings when needed.
|
|
389
|
+
*/
|
|
390
|
+
processEventForApi(event) {
|
|
391
|
+
if (event.type === "SCENARIO_MESSAGE_SNAPSHOT" /* MESSAGE_SNAPSHOT */) {
|
|
392
|
+
return {
|
|
393
|
+
...event,
|
|
394
|
+
messages: event.messages.map((message) => ({
|
|
395
|
+
...message,
|
|
396
|
+
content: typeof message.content !== "string" ? JSON.stringify(message.content) : message.content
|
|
397
|
+
}))
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return event;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
239
403
|
|
|
240
404
|
// src/events/event-bus.ts
|
|
241
405
|
var EventBus = class _EventBus {
|
|
242
406
|
static registry = /* @__PURE__ */ new Set();
|
|
243
407
|
events$ = new import_rxjs.Subject();
|
|
244
408
|
eventReporter;
|
|
409
|
+
eventAlertMessageLogger;
|
|
245
410
|
processingPromise = null;
|
|
246
411
|
logger = new Logger("scenario.events.EventBus");
|
|
247
412
|
static globalListeners = [];
|
|
248
413
|
constructor(config) {
|
|
249
414
|
this.eventReporter = new EventReporter(config);
|
|
415
|
+
this.eventAlertMessageLogger = new EventAlertMessageLogger();
|
|
250
416
|
_EventBus.registry.add(this);
|
|
251
417
|
for (const listener of _EventBus.globalListeners) {
|
|
252
418
|
listener(this);
|
|
@@ -278,22 +444,31 @@ var EventBus = class _EventBus {
|
|
|
278
444
|
}
|
|
279
445
|
this.processingPromise = new Promise((resolve, reject) => {
|
|
280
446
|
this.events$.pipe(
|
|
447
|
+
// Post events and get results
|
|
281
448
|
(0, import_rxjs.concatMap)(async (event) => {
|
|
282
|
-
this.logger.debug(`[${event.type}] Processing event`, {
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
449
|
+
this.logger.debug(`[${event.type}] Processing event`, { event });
|
|
450
|
+
const result = await this.eventReporter.postEvent(event);
|
|
451
|
+
return { event, result };
|
|
452
|
+
}),
|
|
453
|
+
// Handle watch messages reactively
|
|
454
|
+
(0, import_rxjs.tap)(({ event, result }) => {
|
|
455
|
+
if (event.type === "SCENARIO_RUN_STARTED" /* RUN_STARTED */ && result.setUrl) {
|
|
456
|
+
this.eventAlertMessageLogger.handleWatchMessage({
|
|
457
|
+
scenarioSetId: event.scenarioSetId,
|
|
458
|
+
scenarioRunId: event.scenarioRunId,
|
|
459
|
+
setUrl: result.setUrl
|
|
460
|
+
});
|
|
461
|
+
}
|
|
287
462
|
}),
|
|
463
|
+
// Extract just the event for downstream processing
|
|
464
|
+
(0, import_rxjs.map)(({ event }) => event),
|
|
288
465
|
(0, import_rxjs.catchError)((error) => {
|
|
289
466
|
this.logger.error("Error in event stream:", error);
|
|
290
467
|
return import_rxjs.EMPTY;
|
|
291
468
|
})
|
|
292
469
|
).subscribe({
|
|
293
470
|
next: (event) => {
|
|
294
|
-
this.logger.debug(`[${event.type}] Event processed`, {
|
|
295
|
-
event
|
|
296
|
-
});
|
|
471
|
+
this.logger.debug(`[${event.type}] Event processed`, { event });
|
|
297
472
|
if (event.type === "SCENARIO_RUN_FINISHED" /* RUN_FINISHED */) {
|
|
298
473
|
resolve();
|
|
299
474
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langwatch/scenario",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A TypeScript library for testing AI agents using scenarios",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"test": "vitest",
|
|
85
85
|
"test:ci": "vitest run",
|
|
86
86
|
"lint": "eslint .",
|
|
87
|
-
"examples:vitest:run": "export SCENARIO_BATCH_ID=scenariobatch_$(uuidgen) && pnpm run buildpack && (cd examples/vitest && pnpm install) && pnpm -F vitest run test",
|
|
87
|
+
"examples:vitest:run": "export SCENARIO_BATCH_ID=scenariobatch_$(uuidgen) && pnpm run buildpack && (cd examples/vitest && pnpm install) && pnpm -F vitest-example run test",
|
|
88
88
|
"generate:api-reference": "npx typedoc src --out api-reference-docs"
|
|
89
89
|
}
|
|
90
90
|
}
|