@langwatch/scenario 0.2.12 → 0.3.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 +35 -8
- package/dist/{chunk-7HLDX5EL.mjs → chunk-3Z7E24UI.mjs} +52 -43
- package/dist/{chunk-OL4RFXV4.mjs → chunk-RHTLQKEJ.mjs} +1 -1
- package/dist/index.d.mts +110 -128
- package/dist/index.d.ts +110 -128
- package/dist/index.js +118 -111
- package/dist/index.mjs +23 -23
- package/dist/integrations/vitest/reporter.js +14 -10
- package/dist/integrations/vitest/reporter.mjs +7 -3
- package/dist/integrations/vitest/setup-global.js +1 -1
- package/dist/integrations/vitest/setup-global.mjs +1 -1
- package/dist/integrations/vitest/setup.js +155 -90
- package/dist/integrations/vitest/setup.mjs +2 -2
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ import { describe, it, expect } from "vitest";
|
|
|
78
78
|
import { openai } from "@ai-sdk/openai";
|
|
79
79
|
import scenario, { type AgentAdapter, AgentRole } from "@langwatch/scenario";
|
|
80
80
|
import { generateText, tool } from "ai";
|
|
81
|
-
import { z } from "zod";
|
|
81
|
+
import { z } from "zod/v4";
|
|
82
82
|
|
|
83
83
|
describe("Weather Agent", () => {
|
|
84
84
|
it("should get the weather for a city", async () => {
|
|
@@ -103,13 +103,40 @@ describe("Weather Agent", () => {
|
|
|
103
103
|
tools: { get_current_weather: getCurrentWeather },
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
if (response.toolCalls
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
107
|
+
const toolCall = response.toolCalls[0];
|
|
108
|
+
// Agent executes the tool directly and returns both messages
|
|
109
|
+
const toolResult = await getCurrentWeather.execute(
|
|
110
|
+
toolCall.input as { city: string },
|
|
111
|
+
{
|
|
112
|
+
toolCallId: toolCall.toolCallId,
|
|
113
|
+
messages: input.messages,
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
return [
|
|
117
|
+
{
|
|
118
|
+
role: "assistant",
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: "tool-call",
|
|
122
|
+
toolName: toolCall.toolName,
|
|
123
|
+
toolCallId: toolCall.toolCallId,
|
|
124
|
+
input: toolCall.input,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
role: "tool",
|
|
130
|
+
content: [
|
|
131
|
+
{
|
|
132
|
+
type: "tool-result",
|
|
133
|
+
toolName: toolCall.toolName,
|
|
134
|
+
toolCallId: toolCall.toolCallId,
|
|
135
|
+
output: { type: "text", value: toolResult as string },
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
];
|
|
113
140
|
}
|
|
114
141
|
|
|
115
142
|
return response.text;
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Logger,
|
|
3
3
|
getEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RHTLQKEJ.mjs";
|
|
5
5
|
import {
|
|
6
6
|
__export
|
|
7
7
|
} from "./chunk-7P6ASYW6.mjs";
|
|
8
8
|
|
|
9
9
|
// src/domain/core/config.ts
|
|
10
|
-
import { z } from "zod";
|
|
10
|
+
import { z } from "zod/v4";
|
|
11
11
|
var DEFAULT_TEMPERATURE = 0;
|
|
12
12
|
var scenarioProjectConfigSchema = z.object({
|
|
13
13
|
defaultModel: z.object({
|
|
14
14
|
model: z.custom(),
|
|
15
15
|
temperature: z.number().min(0).max(1).optional().default(DEFAULT_TEMPERATURE),
|
|
16
16
|
maxTokens: z.number().optional()
|
|
17
|
-
}).optional()
|
|
17
|
+
}).optional(),
|
|
18
|
+
headless: z.boolean().optional().default(
|
|
19
|
+
typeof process !== "undefined" ? !["false", "0"].includes(process.env.SCENARIO_HEADLESS || "false") : false
|
|
20
|
+
)
|
|
18
21
|
}).strict();
|
|
19
22
|
function defineConfig(config2) {
|
|
20
23
|
return config2;
|
|
@@ -71,6 +74,12 @@ import {
|
|
|
71
74
|
map
|
|
72
75
|
} from "rxjs";
|
|
73
76
|
|
|
77
|
+
// src/events/event-alert-message-logger.ts
|
|
78
|
+
import * as fs2 from "fs";
|
|
79
|
+
import * as os from "os";
|
|
80
|
+
import * as path2 from "path";
|
|
81
|
+
import open from "open";
|
|
82
|
+
|
|
74
83
|
// src/config/load.ts
|
|
75
84
|
import fs from "node:fs/promises";
|
|
76
85
|
import path from "node:path";
|
|
@@ -139,7 +148,7 @@ import process2 from "node:process";
|
|
|
139
148
|
import { generate, parse } from "xksuid";
|
|
140
149
|
var batchRunId;
|
|
141
150
|
function generateThreadId() {
|
|
142
|
-
return `
|
|
151
|
+
return `scenariothread_${generate()}`;
|
|
143
152
|
}
|
|
144
153
|
function generateScenarioRunId() {
|
|
145
154
|
return `scenariorun_${generate()}`;
|
|
@@ -161,9 +170,9 @@ function getBatchRunId() {
|
|
|
161
170
|
const week = String(getISOWeekNumber(now)).padStart(2, "0");
|
|
162
171
|
const raw = `${parentProcessId}_${year}_w${week}`;
|
|
163
172
|
const hash = crypto.createHash("sha256").update(raw).digest("hex").slice(0, 12);
|
|
164
|
-
return batchRunId = `
|
|
173
|
+
return batchRunId = `scenariobatch_${hash}`;
|
|
165
174
|
}
|
|
166
|
-
return batchRunId = `
|
|
175
|
+
return batchRunId = `scenariobatch_${generate()}`;
|
|
167
176
|
}
|
|
168
177
|
function getISOWeekNumber(date) {
|
|
169
178
|
const tmp = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
@@ -178,8 +187,23 @@ function generateMessageId() {
|
|
|
178
187
|
}
|
|
179
188
|
|
|
180
189
|
// src/events/event-alert-message-logger.ts
|
|
181
|
-
var EventAlertMessageLogger = class
|
|
182
|
-
|
|
190
|
+
var EventAlertMessageLogger = class {
|
|
191
|
+
/**
|
|
192
|
+
* Creates a coordination file to prevent duplicate messages across processes.
|
|
193
|
+
* Returns true if this process should show the message (first one to create the file).
|
|
194
|
+
*/
|
|
195
|
+
createCoordinationFile(type) {
|
|
196
|
+
try {
|
|
197
|
+
const batchId = getBatchRunId();
|
|
198
|
+
const tmpDir = os.tmpdir();
|
|
199
|
+
const fileName = `scenario-${type}-${batchId}`;
|
|
200
|
+
const filePath = path2.join(tmpDir, fileName);
|
|
201
|
+
fs2.writeFileSync(filePath, process.pid.toString(), { flag: "wx" });
|
|
202
|
+
return true;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
183
207
|
/**
|
|
184
208
|
* Shows a fancy greeting message about simulation reporting status.
|
|
185
209
|
* Only shows once per batch run to avoid spam.
|
|
@@ -188,21 +212,23 @@ var EventAlertMessageLogger = class _EventAlertMessageLogger {
|
|
|
188
212
|
if (this.isGreetingDisabled()) {
|
|
189
213
|
return;
|
|
190
214
|
}
|
|
191
|
-
if (
|
|
215
|
+
if (!this.createCoordinationFile("greeting")) {
|
|
192
216
|
return;
|
|
193
217
|
}
|
|
194
|
-
_EventAlertMessageLogger.shownBatchIds.add(getBatchRunId());
|
|
195
218
|
this.displayGreeting();
|
|
196
219
|
}
|
|
197
220
|
/**
|
|
198
221
|
* Shows a fancy message about how to watch the simulation.
|
|
199
222
|
* Called when a run started event is received with a session ID.
|
|
200
223
|
*/
|
|
201
|
-
handleWatchMessage(params) {
|
|
224
|
+
async handleWatchMessage(params) {
|
|
202
225
|
if (this.isGreetingDisabled()) {
|
|
203
226
|
return;
|
|
204
227
|
}
|
|
205
|
-
this.
|
|
228
|
+
if (!this.createCoordinationFile(`watch-${params.scenarioSetId}`)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
await this.displayWatchMessage(params);
|
|
206
232
|
}
|
|
207
233
|
isGreetingDisabled() {
|
|
208
234
|
return getEnv().SCENARIO_DISABLE_SIMULATION_REPORT_INFO === true;
|
|
@@ -213,54 +239,37 @@ var EventAlertMessageLogger = class _EventAlertMessageLogger {
|
|
|
213
239
|
if (!env.LANGWATCH_API_KEY) {
|
|
214
240
|
console.log(`
|
|
215
241
|
${separator}`);
|
|
216
|
-
console.log("\u{
|
|
242
|
+
console.log("\u{1F3AD} Running Scenario Tests");
|
|
217
243
|
console.log(`${separator}`);
|
|
218
|
-
console.log("\u27A1\uFE0F API key not configured");
|
|
244
|
+
console.log("\u27A1\uFE0F LangWatch API key not configured");
|
|
219
245
|
console.log(" Simulations will only output final results");
|
|
220
246
|
console.log("");
|
|
221
247
|
console.log("\u{1F4A1} To visualize conversations in real time:");
|
|
222
248
|
console.log(" \u2022 Set LANGWATCH_API_KEY environment variable");
|
|
223
249
|
console.log(" \u2022 Or configure apiKey in scenario.config.js");
|
|
224
250
|
console.log("");
|
|
225
|
-
console.log(`\u{1F4E6} Batch Run ID: ${getBatchRunId()}`);
|
|
226
|
-
console.log("");
|
|
227
|
-
console.log("\u{1F507} To disable these messages:");
|
|
228
|
-
console.log(" \u2022 Set SCENARIO_DISABLE_SIMULATION_REPORT_INFO=true");
|
|
229
|
-
console.log(`${separator}
|
|
230
|
-
`);
|
|
231
|
-
} else {
|
|
232
|
-
console.log(`
|
|
233
|
-
${separator}`);
|
|
234
|
-
console.log("\u{1F680} LangWatch Simulation Reporting");
|
|
235
|
-
console.log(`${separator}`);
|
|
236
|
-
console.log("\u2705 Simulation reporting enabled");
|
|
237
|
-
console.log(` Endpoint: ${env.LANGWATCH_ENDPOINT}`);
|
|
238
|
-
console.log(
|
|
239
|
-
` API Key: ${env.LANGWATCH_API_KEY.length > 0 ? "Configured" : "Not configured"}`
|
|
240
|
-
);
|
|
241
|
-
console.log("");
|
|
242
|
-
console.log(`\u{1F4E6} Batch Run ID: ${getBatchRunId()}`);
|
|
243
|
-
console.log("");
|
|
244
|
-
console.log("\u{1F507} To disable these messages:");
|
|
245
|
-
console.log(" \u2022 Set SCENARIO_DISABLE_SIMULATION_REPORT_INFO=true");
|
|
246
251
|
console.log(`${separator}
|
|
247
252
|
`);
|
|
248
253
|
}
|
|
249
254
|
}
|
|
250
|
-
displayWatchMessage(params) {
|
|
255
|
+
async displayWatchMessage(params) {
|
|
251
256
|
const separator = "\u2500".repeat(60);
|
|
252
257
|
const setUrl = params.setUrl;
|
|
253
258
|
const batchUrl = `${setUrl}/${getBatchRunId()}`;
|
|
254
259
|
console.log(`
|
|
255
260
|
${separator}`);
|
|
256
|
-
console.log("\u{
|
|
261
|
+
console.log("\u{1F3AD} Running Scenario Tests");
|
|
257
262
|
console.log(`${separator}`);
|
|
258
|
-
console.log(
|
|
259
|
-
console.log(` Scenario Set: ${setUrl}`);
|
|
260
|
-
console.log(` Batch Run: ${batchUrl}`);
|
|
261
|
-
console.log("");
|
|
263
|
+
console.log(`Follow it live: ${batchUrl}`);
|
|
262
264
|
console.log(`${separator}
|
|
263
265
|
`);
|
|
266
|
+
const projectConfig = await getProjectConfig();
|
|
267
|
+
if (!(projectConfig == null ? void 0 : projectConfig.headless)) {
|
|
268
|
+
try {
|
|
269
|
+
open(batchUrl);
|
|
270
|
+
} catch (_) {
|
|
271
|
+
}
|
|
272
|
+
}
|
|
264
273
|
}
|
|
265
274
|
};
|
|
266
275
|
|
|
@@ -461,9 +470,9 @@ var EventBus = class _EventBus {
|
|
|
461
470
|
return { event, result };
|
|
462
471
|
}),
|
|
463
472
|
// Handle watch messages reactively
|
|
464
|
-
tap(({ event, result }) => {
|
|
473
|
+
tap(async ({ event, result }) => {
|
|
465
474
|
if (event.type === "SCENARIO_RUN_STARTED" /* RUN_STARTED */ && result.setUrl) {
|
|
466
|
-
this.eventAlertMessageLogger.handleWatchMessage({
|
|
475
|
+
await this.eventAlertMessageLogger.handleWatchMessage({
|
|
467
476
|
scenarioSetId: event.scenarioSetId,
|
|
468
477
|
scenarioRunId: event.scenarioRunId,
|
|
469
478
|
setUrl: result.setUrl
|