@langwatch/scenario 0.2.13 → 0.4.0
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/README.md +36 -9
- package/dist/index.d.mts +433 -256
- package/dist/index.d.ts +433 -256
- package/dist/index.js +2221 -516
- package/dist/index.mjs +2611 -303
- package/dist/integrations/vitest/config.mjs +0 -2
- package/dist/integrations/vitest/reporter.js +36 -11
- package/dist/integrations/vitest/reporter.mjs +159 -8
- package/dist/integrations/vitest/setup-global.mjs +0 -2
- package/dist/integrations/vitest/setup.js +85 -53
- package/dist/integrations/vitest/setup.mjs +619 -18
- package/package.json +46 -30
- package/dist/chunk-6SKQWXT7.mjs +0 -528
- package/dist/chunk-7P6ASYW6.mjs +0 -9
- package/dist/chunk-OL4RFXV4.mjs +0 -133
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langwatch/scenario",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A TypeScript library for testing AI agents using scenarios",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -29,33 +29,36 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@ag-ui/core": "^0.0.28",
|
|
32
|
-
"@ai-sdk/openai": "^
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
32
|
+
"@ai-sdk/openai": "^2.0.74",
|
|
33
|
+
"@openai/agents": "^0.3.3",
|
|
34
|
+
"ai": "5.0.104",
|
|
35
|
+
"chalk": "^5.6.2",
|
|
36
|
+
"langwatch": "0.9.0",
|
|
37
|
+
"open": "11.0.0",
|
|
36
38
|
"rxjs": "^7.8.2",
|
|
37
39
|
"xksuid": "^0.0.4",
|
|
38
|
-
"zod": "^3.
|
|
40
|
+
"zod": "^3.25.76"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
|
-
"@eslint/js": "^9.
|
|
42
|
-
"@types/jest": "^
|
|
43
|
-
"@types/node": "^
|
|
44
|
-
"@typescript-eslint/parser": "^8.
|
|
45
|
-
"@typescript/native-preview": "
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"eslint
|
|
49
|
-
"eslint-
|
|
50
|
-
"eslint-plugin-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"typescript
|
|
58
|
-
"
|
|
43
|
+
"@eslint/js": "^9.39.1",
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
45
|
+
"@types/node": "^24.10.1",
|
|
46
|
+
"@typescript-eslint/parser": "^8.48.0",
|
|
47
|
+
"@typescript/native-preview": "7.0.0-dev.20251128.1",
|
|
48
|
+
"@vitest/coverage-v8": "4.0.14",
|
|
49
|
+
"dotenv": "^17.2.3",
|
|
50
|
+
"eslint": "^9.39.1",
|
|
51
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
52
|
+
"eslint-plugin-import": "^2.32.0",
|
|
53
|
+
"eslint-plugin-unused-imports": "^4.3.0",
|
|
54
|
+
"globals": "^16.5.0",
|
|
55
|
+
"jest": "^30.2.0",
|
|
56
|
+
"ts-jest": "^29.4.5",
|
|
57
|
+
"tsup": "^8.5.1",
|
|
58
|
+
"tsx": "^4.20.6",
|
|
59
|
+
"typescript": "^5.9.3",
|
|
60
|
+
"typescript-eslint": "^8.48.0",
|
|
61
|
+
"vitest": "^4.0.14"
|
|
59
62
|
},
|
|
60
63
|
"exports": {
|
|
61
64
|
".": {
|
|
@@ -85,16 +88,29 @@
|
|
|
85
88
|
}
|
|
86
89
|
},
|
|
87
90
|
"peerDependencies": {
|
|
91
|
+
"ai": ">=5.0.0",
|
|
88
92
|
"vitest": ">=3.2.4"
|
|
89
93
|
},
|
|
90
94
|
"scripts": {
|
|
91
|
-
"
|
|
95
|
+
"clean": "rm -rf dist *.tgz",
|
|
96
|
+
"build": "tsup",
|
|
92
97
|
"buildpack": "pnpm run build && pnpm pack",
|
|
93
|
-
"watch": "pnpm run build -- --watch
|
|
94
|
-
"
|
|
95
|
-
"test:ci": "vitest run",
|
|
98
|
+
"watch": "pnpm run build -- --watch",
|
|
99
|
+
"typecheck": "tsc --noEmit",
|
|
96
100
|
"lint": "eslint .",
|
|
97
|
-
"
|
|
98
|
-
"
|
|
101
|
+
"format": "eslint . --fix",
|
|
102
|
+
"test": "vitest run",
|
|
103
|
+
"test:watch": "vitest",
|
|
104
|
+
"test:ci": "vitest run --coverage",
|
|
105
|
+
"generate:api-reference": "npx typedoc src --out api-reference-docs && rm -rf ../docs/docs/public/reference/javascript/scenario && mv api-reference-docs ../docs/docs/public/reference/javascript/scenario",
|
|
106
|
+
"clean:all": "pnpm -r --parallel exec rm -rf dist *.tgz .cache",
|
|
107
|
+
"build:all": "pnpm run build",
|
|
108
|
+
"typecheck:all": "pnpm -r --parallel run typecheck",
|
|
109
|
+
"lint:all": "pnpm -r --parallel run lint",
|
|
110
|
+
"format:all": "pnpm -r --parallel run format",
|
|
111
|
+
"test:all": "pnpm -r --parallel run test",
|
|
112
|
+
"vitest-examples": "pnpm -F vitest-examples",
|
|
113
|
+
"openai-realtime-demo": "pnpm -F openai-realtime-demo",
|
|
114
|
+
"realtime-client": "pnpm -F realtime-client"
|
|
99
115
|
}
|
|
100
116
|
}
|
package/dist/chunk-6SKQWXT7.mjs
DELETED
|
@@ -1,528 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Logger,
|
|
3
|
-
getEnv
|
|
4
|
-
} from "./chunk-OL4RFXV4.mjs";
|
|
5
|
-
import {
|
|
6
|
-
__export
|
|
7
|
-
} from "./chunk-7P6ASYW6.mjs";
|
|
8
|
-
|
|
9
|
-
// src/domain/core/config.ts
|
|
10
|
-
import { z } from "zod";
|
|
11
|
-
var DEFAULT_TEMPERATURE = 0;
|
|
12
|
-
var scenarioProjectConfigSchema = z.object({
|
|
13
|
-
defaultModel: z.object({
|
|
14
|
-
model: z.custom(),
|
|
15
|
-
temperature: z.number().min(0).max(1).optional().default(DEFAULT_TEMPERATURE),
|
|
16
|
-
maxTokens: z.number().optional()
|
|
17
|
-
}).optional(),
|
|
18
|
-
headless: z.boolean().optional().default(
|
|
19
|
-
typeof process !== "undefined" ? !["false", "0"].includes(process.env.SCENARIO_HEADLESS || "false") : false
|
|
20
|
-
)
|
|
21
|
-
}).strict();
|
|
22
|
-
function defineConfig(config2) {
|
|
23
|
-
return config2;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// src/domain/agents/index.ts
|
|
27
|
-
var AgentRole = /* @__PURE__ */ ((AgentRole2) => {
|
|
28
|
-
AgentRole2["USER"] = "User";
|
|
29
|
-
AgentRole2["AGENT"] = "Agent";
|
|
30
|
-
AgentRole2["JUDGE"] = "Judge";
|
|
31
|
-
return AgentRole2;
|
|
32
|
-
})(AgentRole || {});
|
|
33
|
-
var allAgentRoles = [
|
|
34
|
-
"User" /* USER */,
|
|
35
|
-
"Agent" /* AGENT */,
|
|
36
|
-
"Judge" /* JUDGE */
|
|
37
|
-
];
|
|
38
|
-
var AgentAdapter = class {
|
|
39
|
-
role = "Agent" /* AGENT */;
|
|
40
|
-
};
|
|
41
|
-
var UserSimulatorAgentAdapter = class {
|
|
42
|
-
role = "User" /* USER */;
|
|
43
|
-
};
|
|
44
|
-
var JudgeAgentAdapter = class {
|
|
45
|
-
role = "Judge" /* JUDGE */;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// src/domain/scenarios/index.ts
|
|
49
|
-
var DEFAULT_MAX_TURNS = 10;
|
|
50
|
-
var DEFAULT_VERBOSE = false;
|
|
51
|
-
|
|
52
|
-
// src/domain/index.ts
|
|
53
|
-
var domain_exports = {};
|
|
54
|
-
__export(domain_exports, {
|
|
55
|
-
AgentAdapter: () => AgentAdapter,
|
|
56
|
-
AgentRole: () => AgentRole,
|
|
57
|
-
DEFAULT_MAX_TURNS: () => DEFAULT_MAX_TURNS,
|
|
58
|
-
DEFAULT_TEMPERATURE: () => DEFAULT_TEMPERATURE,
|
|
59
|
-
DEFAULT_VERBOSE: () => DEFAULT_VERBOSE,
|
|
60
|
-
JudgeAgentAdapter: () => JudgeAgentAdapter,
|
|
61
|
-
UserSimulatorAgentAdapter: () => UserSimulatorAgentAdapter,
|
|
62
|
-
allAgentRoles: () => allAgentRoles,
|
|
63
|
-
defineConfig: () => defineConfig,
|
|
64
|
-
scenarioProjectConfigSchema: () => scenarioProjectConfigSchema
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// src/events/event-bus.ts
|
|
68
|
-
import {
|
|
69
|
-
concatMap,
|
|
70
|
-
EMPTY,
|
|
71
|
-
catchError,
|
|
72
|
-
Subject,
|
|
73
|
-
tap,
|
|
74
|
-
map
|
|
75
|
-
} from "rxjs";
|
|
76
|
-
|
|
77
|
-
// src/events/event-alert-message-logger.ts
|
|
78
|
-
import open from "open";
|
|
79
|
-
|
|
80
|
-
// src/config/load.ts
|
|
81
|
-
import fs from "node:fs/promises";
|
|
82
|
-
import path from "node:path";
|
|
83
|
-
import { pathToFileURL } from "node:url";
|
|
84
|
-
async function loadScenarioProjectConfig() {
|
|
85
|
-
const cwd = process.cwd();
|
|
86
|
-
const configNames = [
|
|
87
|
-
"scenario.config.js",
|
|
88
|
-
"scenario.config.mjs"
|
|
89
|
-
];
|
|
90
|
-
for (const name of configNames) {
|
|
91
|
-
const fullPath = path.join(cwd, name);
|
|
92
|
-
try {
|
|
93
|
-
await fs.access(fullPath);
|
|
94
|
-
const configModule = await import(pathToFileURL(fullPath).href);
|
|
95
|
-
const config2 = configModule.default || configModule;
|
|
96
|
-
const parsed = scenarioProjectConfigSchema.safeParse(config2);
|
|
97
|
-
if (!parsed.success) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`Invalid config file ${name}: ${JSON.stringify(parsed.error.format(), null, 2)}`
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
return parsed.data;
|
|
103
|
-
} catch (error) {
|
|
104
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
throw error;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return await scenarioProjectConfigSchema.parseAsync({});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// src/config/get-project-config.ts
|
|
114
|
-
var logger = new Logger("scenario.config");
|
|
115
|
-
var configLoaded = false;
|
|
116
|
-
var config = null;
|
|
117
|
-
var configLoadPromise = null;
|
|
118
|
-
async function loadProjectConfig() {
|
|
119
|
-
if (configLoaded) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
if (configLoadPromise) {
|
|
123
|
-
return configLoadPromise;
|
|
124
|
-
}
|
|
125
|
-
configLoadPromise = (async () => {
|
|
126
|
-
try {
|
|
127
|
-
config = await loadScenarioProjectConfig();
|
|
128
|
-
logger.debug("loaded scenario project config", { config });
|
|
129
|
-
} catch (error) {
|
|
130
|
-
logger.error("error loading scenario project config", { error });
|
|
131
|
-
} finally {
|
|
132
|
-
configLoaded = true;
|
|
133
|
-
}
|
|
134
|
-
})();
|
|
135
|
-
return configLoadPromise;
|
|
136
|
-
}
|
|
137
|
-
async function getProjectConfig() {
|
|
138
|
-
await loadProjectConfig();
|
|
139
|
-
return config;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// src/utils/ids.ts
|
|
143
|
-
import crypto from "node:crypto";
|
|
144
|
-
import process2 from "node:process";
|
|
145
|
-
import { generate, parse } from "xksuid";
|
|
146
|
-
var batchRunId;
|
|
147
|
-
function generateThreadId() {
|
|
148
|
-
return `scenariothread_${generate()}`;
|
|
149
|
-
}
|
|
150
|
-
function generateScenarioRunId() {
|
|
151
|
-
return `scenariorun_${generate()}`;
|
|
152
|
-
}
|
|
153
|
-
function generateScenarioId() {
|
|
154
|
-
return `scenario_${generate()}`;
|
|
155
|
-
}
|
|
156
|
-
function getBatchRunId() {
|
|
157
|
-
if (batchRunId) {
|
|
158
|
-
return batchRunId;
|
|
159
|
-
}
|
|
160
|
-
if (process2.env.SCENARIO_BATCH_RUN_ID) {
|
|
161
|
-
return batchRunId = process2.env.SCENARIO_BATCH_RUN_ID;
|
|
162
|
-
}
|
|
163
|
-
if (process2.env.VITEST_WORKER_ID || process2.env.JEST_WORKER_ID) {
|
|
164
|
-
const parentProcessId = process2.ppid;
|
|
165
|
-
const now = /* @__PURE__ */ new Date();
|
|
166
|
-
const year = now.getUTCFullYear();
|
|
167
|
-
const week = String(getISOWeekNumber(now)).padStart(2, "0");
|
|
168
|
-
const raw = `${parentProcessId}_${year}_w${week}`;
|
|
169
|
-
const hash = crypto.createHash("sha256").update(raw).digest("hex").slice(0, 12);
|
|
170
|
-
return batchRunId = `scenariobatch_${hash}`;
|
|
171
|
-
}
|
|
172
|
-
return batchRunId = `scenariobatch_${generate()}`;
|
|
173
|
-
}
|
|
174
|
-
function getISOWeekNumber(date) {
|
|
175
|
-
const tmp = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
176
|
-
const dayNum = tmp.getUTCDay() || 7;
|
|
177
|
-
tmp.setUTCDate(tmp.getUTCDate() + 4 - dayNum);
|
|
178
|
-
const yearStart = new Date(Date.UTC(tmp.getUTCFullYear(), 0, 1));
|
|
179
|
-
const weekNo = Math.ceil(((tmp.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
180
|
-
return weekNo;
|
|
181
|
-
}
|
|
182
|
-
function generateMessageId() {
|
|
183
|
-
return `scenariomsg_${generate()}`;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// src/events/event-alert-message-logger.ts
|
|
187
|
-
var EventAlertMessageLogger = class _EventAlertMessageLogger {
|
|
188
|
-
static shownBatchIds = /* @__PURE__ */ new Set();
|
|
189
|
-
/**
|
|
190
|
-
* Shows a fancy greeting message about simulation reporting status.
|
|
191
|
-
* Only shows once per batch run to avoid spam.
|
|
192
|
-
*/
|
|
193
|
-
handleGreeting() {
|
|
194
|
-
if (this.isGreetingDisabled()) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (_EventAlertMessageLogger.shownBatchIds.has(getBatchRunId())) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
_EventAlertMessageLogger.shownBatchIds.add(getBatchRunId());
|
|
201
|
-
this.displayGreeting();
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Shows a fancy message about how to watch the simulation.
|
|
205
|
-
* Called when a run started event is received with a session ID.
|
|
206
|
-
*/
|
|
207
|
-
async handleWatchMessage(params) {
|
|
208
|
-
if (this.isGreetingDisabled()) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
await this.displayWatchMessage(params);
|
|
212
|
-
}
|
|
213
|
-
isGreetingDisabled() {
|
|
214
|
-
return getEnv().SCENARIO_DISABLE_SIMULATION_REPORT_INFO === true;
|
|
215
|
-
}
|
|
216
|
-
displayGreeting() {
|
|
217
|
-
const separator = "\u2500".repeat(60);
|
|
218
|
-
const env = getEnv();
|
|
219
|
-
if (!env.LANGWATCH_API_KEY) {
|
|
220
|
-
console.log(`
|
|
221
|
-
${separator}`);
|
|
222
|
-
console.log("\u{1F3AD} Running Scenario Tests");
|
|
223
|
-
console.log(`${separator}`);
|
|
224
|
-
console.log("\u27A1\uFE0F LangWatch API key not configured");
|
|
225
|
-
console.log(" Simulations will only output final results");
|
|
226
|
-
console.log("");
|
|
227
|
-
console.log("\u{1F4A1} To visualize conversations in real time:");
|
|
228
|
-
console.log(" \u2022 Set LANGWATCH_API_KEY environment variable");
|
|
229
|
-
console.log(" \u2022 Or configure apiKey in scenario.config.js");
|
|
230
|
-
console.log("");
|
|
231
|
-
console.log(`${separator}
|
|
232
|
-
`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async displayWatchMessage(params) {
|
|
236
|
-
const separator = "\u2500".repeat(60);
|
|
237
|
-
const setUrl = params.setUrl;
|
|
238
|
-
const batchUrl = `${setUrl}/${getBatchRunId()}`;
|
|
239
|
-
console.log(`
|
|
240
|
-
${separator}`);
|
|
241
|
-
console.log("\u{1F3AD} Running Scenario Tests");
|
|
242
|
-
console.log(`${separator}`);
|
|
243
|
-
console.log(`Follow it live: ${batchUrl}`);
|
|
244
|
-
console.log(`${separator}
|
|
245
|
-
`);
|
|
246
|
-
const projectConfig = await getProjectConfig();
|
|
247
|
-
if (!(projectConfig == null ? void 0 : projectConfig.headless)) {
|
|
248
|
-
try {
|
|
249
|
-
open(batchUrl);
|
|
250
|
-
} catch (_) {
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
// src/events/schema.ts
|
|
257
|
-
import { EventType, MessagesSnapshotEventSchema } from "@ag-ui/core";
|
|
258
|
-
import { z as z2 } from "zod";
|
|
259
|
-
var Verdict = /* @__PURE__ */ ((Verdict2) => {
|
|
260
|
-
Verdict2["SUCCESS"] = "success";
|
|
261
|
-
Verdict2["FAILURE"] = "failure";
|
|
262
|
-
Verdict2["INCONCLUSIVE"] = "inconclusive";
|
|
263
|
-
return Verdict2;
|
|
264
|
-
})(Verdict || {});
|
|
265
|
-
var ScenarioRunStatus = /* @__PURE__ */ ((ScenarioRunStatus2) => {
|
|
266
|
-
ScenarioRunStatus2["SUCCESS"] = "SUCCESS";
|
|
267
|
-
ScenarioRunStatus2["ERROR"] = "ERROR";
|
|
268
|
-
ScenarioRunStatus2["CANCELLED"] = "CANCELLED";
|
|
269
|
-
ScenarioRunStatus2["IN_PROGRESS"] = "IN_PROGRESS";
|
|
270
|
-
ScenarioRunStatus2["PENDING"] = "PENDING";
|
|
271
|
-
ScenarioRunStatus2["FAILED"] = "FAILED";
|
|
272
|
-
return ScenarioRunStatus2;
|
|
273
|
-
})(ScenarioRunStatus || {});
|
|
274
|
-
var baseEventSchema = z2.object({
|
|
275
|
-
type: z2.nativeEnum(EventType),
|
|
276
|
-
timestamp: z2.number(),
|
|
277
|
-
rawEvent: z2.any().optional()
|
|
278
|
-
});
|
|
279
|
-
var batchRunIdSchema = z2.string();
|
|
280
|
-
var scenarioRunIdSchema = z2.string();
|
|
281
|
-
var scenarioIdSchema = z2.string();
|
|
282
|
-
var baseScenarioEventSchema = baseEventSchema.extend({
|
|
283
|
-
batchRunId: batchRunIdSchema,
|
|
284
|
-
scenarioId: scenarioIdSchema,
|
|
285
|
-
scenarioRunId: scenarioRunIdSchema,
|
|
286
|
-
scenarioSetId: z2.string().optional().default("default")
|
|
287
|
-
});
|
|
288
|
-
var scenarioRunStartedSchema = baseScenarioEventSchema.extend({
|
|
289
|
-
type: z2.literal("SCENARIO_RUN_STARTED" /* RUN_STARTED */),
|
|
290
|
-
metadata: z2.object({
|
|
291
|
-
name: z2.string().optional(),
|
|
292
|
-
description: z2.string().optional()
|
|
293
|
-
})
|
|
294
|
-
});
|
|
295
|
-
var scenarioResultsSchema = z2.object({
|
|
296
|
-
verdict: z2.nativeEnum(Verdict),
|
|
297
|
-
reasoning: z2.string().optional(),
|
|
298
|
-
metCriteria: z2.array(z2.string()),
|
|
299
|
-
unmetCriteria: z2.array(z2.string()),
|
|
300
|
-
error: z2.string().optional()
|
|
301
|
-
});
|
|
302
|
-
var scenarioRunFinishedSchema = baseScenarioEventSchema.extend({
|
|
303
|
-
type: z2.literal("SCENARIO_RUN_FINISHED" /* RUN_FINISHED */),
|
|
304
|
-
status: z2.nativeEnum(ScenarioRunStatus),
|
|
305
|
-
results: scenarioResultsSchema.optional().nullable()
|
|
306
|
-
});
|
|
307
|
-
var scenarioMessageSnapshotSchema = MessagesSnapshotEventSchema.merge(
|
|
308
|
-
baseScenarioEventSchema.extend({
|
|
309
|
-
type: z2.literal("SCENARIO_MESSAGE_SNAPSHOT" /* MESSAGE_SNAPSHOT */)
|
|
310
|
-
})
|
|
311
|
-
);
|
|
312
|
-
var scenarioEventSchema = z2.discriminatedUnion("type", [
|
|
313
|
-
scenarioRunStartedSchema,
|
|
314
|
-
scenarioRunFinishedSchema,
|
|
315
|
-
scenarioMessageSnapshotSchema
|
|
316
|
-
]);
|
|
317
|
-
var successSchema = z2.object({ success: z2.boolean() });
|
|
318
|
-
var errorSchema = z2.object({ error: z2.string() });
|
|
319
|
-
var stateSchema = z2.object({
|
|
320
|
-
state: z2.object({
|
|
321
|
-
messages: z2.array(z2.any()),
|
|
322
|
-
status: z2.string()
|
|
323
|
-
})
|
|
324
|
-
});
|
|
325
|
-
var runsSchema = z2.object({ runs: z2.array(z2.string()) });
|
|
326
|
-
var eventsSchema = z2.object({ events: z2.array(scenarioEventSchema) });
|
|
327
|
-
|
|
328
|
-
// src/events/event-reporter.ts
|
|
329
|
-
var EventReporter = class {
|
|
330
|
-
apiKey;
|
|
331
|
-
eventsEndpoint;
|
|
332
|
-
eventAlertMessageLogger;
|
|
333
|
-
logger = new Logger("scenario.events.EventReporter");
|
|
334
|
-
isEnabled;
|
|
335
|
-
constructor(config2) {
|
|
336
|
-
this.apiKey = config2.apiKey ?? "";
|
|
337
|
-
this.eventsEndpoint = new URL("/api/scenario-events", config2.endpoint);
|
|
338
|
-
this.eventAlertMessageLogger = new EventAlertMessageLogger();
|
|
339
|
-
this.eventAlertMessageLogger.handleGreeting();
|
|
340
|
-
this.isEnabled = this.apiKey.length > 0 && this.eventsEndpoint.href.length > 0;
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Posts an event to the configured endpoint.
|
|
344
|
-
* Logs success/failure but doesn't throw - event posting shouldn't break scenario execution.
|
|
345
|
-
*/
|
|
346
|
-
async postEvent(event) {
|
|
347
|
-
if (!this.isEnabled) return {};
|
|
348
|
-
const result = {};
|
|
349
|
-
this.logger.debug(`[${event.type}] Posting event`, { event });
|
|
350
|
-
const processedEvent = this.processEventForApi(event);
|
|
351
|
-
try {
|
|
352
|
-
const response = await fetch(this.eventsEndpoint.href, {
|
|
353
|
-
method: "POST",
|
|
354
|
-
body: JSON.stringify(processedEvent),
|
|
355
|
-
headers: {
|
|
356
|
-
"Content-Type": "application/json",
|
|
357
|
-
"X-Auth-Token": this.apiKey
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
this.logger.debug(
|
|
361
|
-
`[${event.type}] Event POST response status: ${response.status}`
|
|
362
|
-
);
|
|
363
|
-
if (response.ok) {
|
|
364
|
-
const data = await response.json();
|
|
365
|
-
this.logger.debug(`[${event.type}] Event POST response:`, data);
|
|
366
|
-
result.setUrl = data.url;
|
|
367
|
-
} else {
|
|
368
|
-
const errorText = await response.text();
|
|
369
|
-
this.logger.error(`[${event.type}] Event POST failed:`, {
|
|
370
|
-
status: response.status,
|
|
371
|
-
statusText: response.statusText,
|
|
372
|
-
error: errorText,
|
|
373
|
-
event: JSON.stringify(processedEvent)
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
} catch (error) {
|
|
377
|
-
this.logger.error(`[${event.type}] Event POST error:`, {
|
|
378
|
-
error,
|
|
379
|
-
event: JSON.stringify(processedEvent),
|
|
380
|
-
endpoint: this.eventsEndpoint.href
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
return result;
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Processes event data to ensure API compatibility.
|
|
387
|
-
* Converts message content objects to strings when needed.
|
|
388
|
-
*/
|
|
389
|
-
processEventForApi(event) {
|
|
390
|
-
if (event.type === "SCENARIO_MESSAGE_SNAPSHOT" /* MESSAGE_SNAPSHOT */) {
|
|
391
|
-
return {
|
|
392
|
-
...event,
|
|
393
|
-
messages: event.messages.map((message) => ({
|
|
394
|
-
...message,
|
|
395
|
-
content: typeof message.content !== "string" ? JSON.stringify(message.content) : message.content
|
|
396
|
-
}))
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
return event;
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
// src/events/event-bus.ts
|
|
404
|
-
var EventBus = class _EventBus {
|
|
405
|
-
static registry = /* @__PURE__ */ new Set();
|
|
406
|
-
events$ = new Subject();
|
|
407
|
-
eventReporter;
|
|
408
|
-
eventAlertMessageLogger;
|
|
409
|
-
processingPromise = null;
|
|
410
|
-
logger = new Logger("scenario.events.EventBus");
|
|
411
|
-
static globalListeners = [];
|
|
412
|
-
constructor(config2) {
|
|
413
|
-
this.eventReporter = new EventReporter(config2);
|
|
414
|
-
this.eventAlertMessageLogger = new EventAlertMessageLogger();
|
|
415
|
-
_EventBus.registry.add(this);
|
|
416
|
-
for (const listener of _EventBus.globalListeners) {
|
|
417
|
-
listener(this);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
static getAllBuses() {
|
|
421
|
-
return _EventBus.registry;
|
|
422
|
-
}
|
|
423
|
-
static addGlobalListener(listener) {
|
|
424
|
-
_EventBus.globalListeners.push(listener);
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Publishes an event into the processing pipeline.
|
|
428
|
-
*/
|
|
429
|
-
publish(event) {
|
|
430
|
-
this.logger.debug(`[${event.type}] Publishing event`, {
|
|
431
|
-
event
|
|
432
|
-
});
|
|
433
|
-
this.events$.next(event);
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* Begins listening for and processing events.
|
|
437
|
-
* Returns a promise that resolves when a RUN_FINISHED event is fully processed.
|
|
438
|
-
*/
|
|
439
|
-
listen() {
|
|
440
|
-
this.logger.debug("Listening for events");
|
|
441
|
-
if (this.processingPromise) {
|
|
442
|
-
return this.processingPromise;
|
|
443
|
-
}
|
|
444
|
-
this.processingPromise = new Promise((resolve, reject) => {
|
|
445
|
-
this.events$.pipe(
|
|
446
|
-
// Post events and get results
|
|
447
|
-
concatMap(async (event) => {
|
|
448
|
-
this.logger.debug(`[${event.type}] Processing event`, { event });
|
|
449
|
-
const result = await this.eventReporter.postEvent(event);
|
|
450
|
-
return { event, result };
|
|
451
|
-
}),
|
|
452
|
-
// Handle watch messages reactively
|
|
453
|
-
tap(async ({ event, result }) => {
|
|
454
|
-
if (event.type === "SCENARIO_RUN_STARTED" /* RUN_STARTED */ && result.setUrl) {
|
|
455
|
-
await this.eventAlertMessageLogger.handleWatchMessage({
|
|
456
|
-
scenarioSetId: event.scenarioSetId,
|
|
457
|
-
scenarioRunId: event.scenarioRunId,
|
|
458
|
-
setUrl: result.setUrl
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
}),
|
|
462
|
-
// Extract just the event for downstream processing
|
|
463
|
-
map(({ event }) => event),
|
|
464
|
-
catchError((error) => {
|
|
465
|
-
this.logger.error("Error in event stream:", error);
|
|
466
|
-
return EMPTY;
|
|
467
|
-
})
|
|
468
|
-
).subscribe({
|
|
469
|
-
next: (event) => {
|
|
470
|
-
this.logger.debug(`[${event.type}] Event processed`, { event });
|
|
471
|
-
if (event.type === "SCENARIO_RUN_FINISHED" /* RUN_FINISHED */) {
|
|
472
|
-
resolve();
|
|
473
|
-
}
|
|
474
|
-
},
|
|
475
|
-
error: (error) => {
|
|
476
|
-
this.logger.error("Error in event stream:", error);
|
|
477
|
-
reject(error);
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
});
|
|
481
|
-
return this.processingPromise;
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Stops accepting new events and drains the processing queue.
|
|
485
|
-
*/
|
|
486
|
-
async drain() {
|
|
487
|
-
this.logger.debug("Draining event stream");
|
|
488
|
-
this.events$.complete();
|
|
489
|
-
if (this.processingPromise) {
|
|
490
|
-
await this.processingPromise;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Subscribes to an event stream.
|
|
495
|
-
* @param source$ - The event stream to subscribe to.
|
|
496
|
-
*/
|
|
497
|
-
subscribeTo(source$) {
|
|
498
|
-
this.logger.debug("Subscribing to event stream");
|
|
499
|
-
return source$.subscribe(this.events$);
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Expose the events$ observable for external subscription (read-only).
|
|
503
|
-
*/
|
|
504
|
-
get eventsObservable() {
|
|
505
|
-
return this.events$.asObservable();
|
|
506
|
-
}
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
export {
|
|
510
|
-
DEFAULT_TEMPERATURE,
|
|
511
|
-
scenarioProjectConfigSchema,
|
|
512
|
-
defineConfig,
|
|
513
|
-
AgentRole,
|
|
514
|
-
allAgentRoles,
|
|
515
|
-
AgentAdapter,
|
|
516
|
-
UserSimulatorAgentAdapter,
|
|
517
|
-
JudgeAgentAdapter,
|
|
518
|
-
DEFAULT_MAX_TURNS,
|
|
519
|
-
DEFAULT_VERBOSE,
|
|
520
|
-
domain_exports,
|
|
521
|
-
getProjectConfig,
|
|
522
|
-
generateThreadId,
|
|
523
|
-
generateScenarioRunId,
|
|
524
|
-
generateScenarioId,
|
|
525
|
-
getBatchRunId,
|
|
526
|
-
generateMessageId,
|
|
527
|
-
EventBus
|
|
528
|
-
};
|