@agentlensai/server 0.6.0 → 0.8.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/dist/db/benchmark-store.d.ts +74 -0
- package/dist/db/benchmark-store.d.ts.map +1 -0
- package/dist/db/benchmark-store.js +268 -0
- package/dist/db/benchmark-store.js.map +1 -0
- package/dist/db/guardrail-store.d.ts +34 -0
- package/dist/db/guardrail-store.d.ts.map +1 -0
- package/dist/db/guardrail-store.js +221 -0
- package/dist/db/guardrail-store.js.map +1 -0
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +112 -0
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/schema.sqlite.d.ts +58 -1
- package/dist/db/schema.sqlite.d.ts.map +1 -1
- package/dist/db/schema.sqlite.js +6 -0
- package/dist/db/schema.sqlite.js.map +1 -1
- package/dist/db/sqlite-store.d.ts +12 -0
- package/dist/db/sqlite-store.d.ts.map +1 -1
- package/dist/db/sqlite-store.js +47 -0
- package/dist/db/sqlite-store.js.map +1 -1
- package/dist/db/tenant-scoped-store.d.ts +1 -0
- package/dist/db/tenant-scoped-store.d.ts.map +1 -1
- package/dist/db/tenant-scoped-store.js +3 -0
- package/dist/db/tenant-scoped-store.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/benchmark/engine.d.ts +24 -0
- package/dist/lib/benchmark/engine.d.ts.map +1 -0
- package/dist/lib/benchmark/engine.js +159 -0
- package/dist/lib/benchmark/engine.js.map +1 -0
- package/dist/lib/benchmark/metric-aggregator.d.ts +38 -0
- package/dist/lib/benchmark/metric-aggregator.d.ts.map +1 -0
- package/dist/lib/benchmark/metric-aggregator.js +159 -0
- package/dist/lib/benchmark/metric-aggregator.js.map +1 -0
- package/dist/lib/benchmark/statistical.d.ts +51 -0
- package/dist/lib/benchmark/statistical.d.ts.map +1 -0
- package/dist/lib/benchmark/statistical.js +381 -0
- package/dist/lib/benchmark/statistical.js.map +1 -0
- package/dist/lib/guardrails/actions.d.ts +28 -0
- package/dist/lib/guardrails/actions.d.ts.map +1 -0
- package/dist/lib/guardrails/actions.js +126 -0
- package/dist/lib/guardrails/actions.js.map +1 -0
- package/dist/lib/guardrails/conditions.d.ts +13 -0
- package/dist/lib/guardrails/conditions.d.ts.map +1 -0
- package/dist/lib/guardrails/conditions.js +188 -0
- package/dist/lib/guardrails/conditions.js.map +1 -0
- package/dist/lib/guardrails/engine.d.ts +24 -0
- package/dist/lib/guardrails/engine.d.ts.map +1 -0
- package/dist/lib/guardrails/engine.js +122 -0
- package/dist/lib/guardrails/engine.js.map +1 -0
- package/dist/lib/replay/builder.d.ts +28 -0
- package/dist/lib/replay/builder.d.ts.map +1 -0
- package/dist/lib/replay/builder.js +482 -0
- package/dist/lib/replay/builder.js.map +1 -0
- package/dist/routes/agents.d.ts +4 -3
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +37 -12
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/benchmarks.d.ts +18 -0
- package/dist/routes/benchmarks.d.ts.map +1 -0
- package/dist/routes/benchmarks.js +312 -0
- package/dist/routes/benchmarks.js.map +1 -0
- package/dist/routes/guardrails.d.ts +18 -0
- package/dist/routes/guardrails.d.ts.map +1 -0
- package/dist/routes/guardrails.js +184 -0
- package/dist/routes/guardrails.js.map +1 -0
- package/dist/routes/replay.d.ts +28 -0
- package/dist/routes/replay.d.ts.map +1 -0
- package/dist/routes/replay.js +140 -0
- package/dist/routes/replay.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Benchmark REST Endpoints (Story 3.5)
|
|
3
|
+
*
|
|
4
|
+
* POST /api/benchmarks — Create benchmark
|
|
5
|
+
* GET /api/benchmarks — List benchmarks
|
|
6
|
+
* GET /api/benchmarks/:id — Get benchmark detail
|
|
7
|
+
* PUT /api/benchmarks/:id/status — Transition status
|
|
8
|
+
* GET /api/benchmarks/:id/results — Get comparison results
|
|
9
|
+
* DELETE /api/benchmarks/:id — Delete draft/cancelled
|
|
10
|
+
*/
|
|
11
|
+
import { Hono } from 'hono';
|
|
12
|
+
import { BENCHMARK_METRICS } from '@agentlensai/core';
|
|
13
|
+
import { getTenantStore } from './tenant-helper.js';
|
|
14
|
+
import { BenchmarkStore, } from '../db/benchmark-store.js';
|
|
15
|
+
import { BenchmarkEngine } from '../lib/benchmark/engine.js';
|
|
16
|
+
// ─── Validation Helpers ────────────────────────────────────
|
|
17
|
+
const VALID_METRICS = new Set(BENCHMARK_METRICS);
|
|
18
|
+
const VALID_STATUSES = new Set(['draft', 'running', 'completed', 'cancelled']);
|
|
19
|
+
function validateCreateInput(body) {
|
|
20
|
+
if (!body || typeof body !== 'object') {
|
|
21
|
+
return { error: 'Request body is required' };
|
|
22
|
+
}
|
|
23
|
+
// Name required
|
|
24
|
+
if (!body.name || typeof body.name !== 'string' || body.name.trim() === '') {
|
|
25
|
+
return { error: 'name is required and must be a non-empty string' };
|
|
26
|
+
}
|
|
27
|
+
// Variants: 2-10 with name+tag
|
|
28
|
+
if (!Array.isArray(body.variants)) {
|
|
29
|
+
return { error: 'variants must be an array' };
|
|
30
|
+
}
|
|
31
|
+
if (body.variants.length < 2 || body.variants.length > 10) {
|
|
32
|
+
return { error: 'Must have between 2 and 10 variants' };
|
|
33
|
+
}
|
|
34
|
+
for (let i = 0; i < body.variants.length; i++) {
|
|
35
|
+
const v = body.variants[i];
|
|
36
|
+
if (!v || typeof v !== 'object') {
|
|
37
|
+
return { error: `variants[${i}] must be an object` };
|
|
38
|
+
}
|
|
39
|
+
if (!v.name || typeof v.name !== 'string' || v.name.trim() === '') {
|
|
40
|
+
return { error: `variants[${i}].name is required` };
|
|
41
|
+
}
|
|
42
|
+
if (!v.tag || typeof v.tag !== 'string' || v.tag.trim() === '') {
|
|
43
|
+
return { error: `variants[${i}].tag is required` };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Metrics: optional array, but if provided must be valid
|
|
47
|
+
let metrics = BENCHMARK_METRICS.filter(m => m !== 'health_score'); // default: all supported
|
|
48
|
+
if (body.metrics !== undefined) {
|
|
49
|
+
if (!Array.isArray(body.metrics) || body.metrics.length === 0) {
|
|
50
|
+
return { error: 'metrics must be a non-empty array' };
|
|
51
|
+
}
|
|
52
|
+
for (const m of body.metrics) {
|
|
53
|
+
if (!VALID_METRICS.has(m)) {
|
|
54
|
+
return { error: `Invalid metric: ${m}` };
|
|
55
|
+
}
|
|
56
|
+
if (m === 'health_score') {
|
|
57
|
+
return { error: `Metric "health_score" is not yet supported for benchmarks. It requires pre-computed health snapshots.` };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
metrics = body.metrics;
|
|
61
|
+
}
|
|
62
|
+
// minSessionsPerVariant: optional, must be ≥ 1
|
|
63
|
+
let minSessions;
|
|
64
|
+
if (body.minSessionsPerVariant !== undefined) {
|
|
65
|
+
const val = Number(body.minSessionsPerVariant);
|
|
66
|
+
if (!Number.isInteger(val) || val < 1) {
|
|
67
|
+
return { error: 'minSessionsPerVariant must be an integer ≥ 1' };
|
|
68
|
+
}
|
|
69
|
+
minSessions = val;
|
|
70
|
+
}
|
|
71
|
+
const input = {
|
|
72
|
+
name: body.name.trim(),
|
|
73
|
+
description: body.description ?? undefined,
|
|
74
|
+
agentId: body.agentId ?? undefined,
|
|
75
|
+
metrics,
|
|
76
|
+
minSessionsPerVariant: minSessions,
|
|
77
|
+
timeRange: body.timeRange,
|
|
78
|
+
variants: body.variants.map((v) => ({
|
|
79
|
+
name: v.name.trim(),
|
|
80
|
+
description: v.description ?? undefined,
|
|
81
|
+
tag: v.tag.trim(),
|
|
82
|
+
agentId: v.agentId ?? undefined,
|
|
83
|
+
})),
|
|
84
|
+
};
|
|
85
|
+
return { input };
|
|
86
|
+
}
|
|
87
|
+
// ─── Route Factory ─────────────────────────────────────────
|
|
88
|
+
export function benchmarkRoutes(store, db) {
|
|
89
|
+
const app = new Hono();
|
|
90
|
+
const engine = new BenchmarkEngine();
|
|
91
|
+
function getBenchmarkStore(c) {
|
|
92
|
+
if (!db)
|
|
93
|
+
return null;
|
|
94
|
+
return new BenchmarkStore(db);
|
|
95
|
+
}
|
|
96
|
+
function getTenantId(c) {
|
|
97
|
+
const apiKey = c.get('apiKey');
|
|
98
|
+
return apiKey?.tenantId ?? 'default';
|
|
99
|
+
}
|
|
100
|
+
// ─── POST / — Create benchmark ─────────────────────────
|
|
101
|
+
app.post('/', async (c) => {
|
|
102
|
+
const benchmarkStore = getBenchmarkStore(c);
|
|
103
|
+
if (!benchmarkStore) {
|
|
104
|
+
return c.json({ error: 'Database not available', status: 500 }, 500);
|
|
105
|
+
}
|
|
106
|
+
const tenantId = getTenantId(c);
|
|
107
|
+
const body = await c.req.json().catch(() => null);
|
|
108
|
+
const { error, input } = validateCreateInput(body);
|
|
109
|
+
if (error || !input) {
|
|
110
|
+
return c.json({ error, status: 400 }, 400);
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const benchmark = benchmarkStore.create(tenantId, input);
|
|
114
|
+
return c.json(benchmark, 201);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
const message = err instanceof Error ? err.message : 'Failed to create benchmark';
|
|
118
|
+
return c.json({ error: message, status: 400 }, 400);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
// ─── GET / — List benchmarks ────────────────────────────
|
|
122
|
+
app.get('/', async (c) => {
|
|
123
|
+
const benchmarkStore = getBenchmarkStore(c);
|
|
124
|
+
if (!benchmarkStore) {
|
|
125
|
+
return c.json({ error: 'Database not available', status: 500 }, 500);
|
|
126
|
+
}
|
|
127
|
+
const tenantId = getTenantId(c);
|
|
128
|
+
// Parse query params
|
|
129
|
+
const statusRaw = c.req.query('status');
|
|
130
|
+
const agentId = c.req.query('agentId') || undefined;
|
|
131
|
+
const limitStr = c.req.query('limit');
|
|
132
|
+
const offsetStr = c.req.query('offset');
|
|
133
|
+
// Validate status
|
|
134
|
+
let status;
|
|
135
|
+
if (statusRaw) {
|
|
136
|
+
if (!VALID_STATUSES.has(statusRaw)) {
|
|
137
|
+
return c.json({ error: `Invalid status: ${statusRaw}`, status: 400 }, 400);
|
|
138
|
+
}
|
|
139
|
+
status = statusRaw;
|
|
140
|
+
}
|
|
141
|
+
// Validate limit (1-100, default 20)
|
|
142
|
+
let limit = 20;
|
|
143
|
+
if (limitStr !== undefined && limitStr !== '') {
|
|
144
|
+
const val = parseInt(limitStr, 10);
|
|
145
|
+
if (isNaN(val) || val < 1 || val > 100) {
|
|
146
|
+
return c.json({ error: 'limit must be an integer between 1 and 100', status: 400 }, 400);
|
|
147
|
+
}
|
|
148
|
+
limit = val;
|
|
149
|
+
}
|
|
150
|
+
// Validate offset
|
|
151
|
+
let offset = 0;
|
|
152
|
+
if (offsetStr !== undefined && offsetStr !== '') {
|
|
153
|
+
const val = parseInt(offsetStr, 10);
|
|
154
|
+
if (isNaN(val) || val < 0) {
|
|
155
|
+
return c.json({ error: 'offset must be a non-negative integer', status: 400 }, 400);
|
|
156
|
+
}
|
|
157
|
+
offset = val;
|
|
158
|
+
}
|
|
159
|
+
const filters = { status, agentId, limit, offset };
|
|
160
|
+
const { benchmarks, total } = benchmarkStore.list(tenantId, filters);
|
|
161
|
+
return c.json({
|
|
162
|
+
benchmarks,
|
|
163
|
+
total,
|
|
164
|
+
hasMore: offset + benchmarks.length < total,
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
// ─── GET /:id — Get benchmark detail ────────────────────
|
|
168
|
+
app.get('/:id', async (c) => {
|
|
169
|
+
const benchmarkStore = getBenchmarkStore(c);
|
|
170
|
+
if (!benchmarkStore) {
|
|
171
|
+
return c.json({ error: 'Database not available', status: 500 }, 500);
|
|
172
|
+
}
|
|
173
|
+
const tenantId = getTenantId(c);
|
|
174
|
+
const id = c.req.param('id');
|
|
175
|
+
const benchmark = benchmarkStore.getById(tenantId, id);
|
|
176
|
+
if (!benchmark) {
|
|
177
|
+
return c.json({ error: 'Benchmark not found', status: 404 }, 404);
|
|
178
|
+
}
|
|
179
|
+
// Enrich variants with session counts
|
|
180
|
+
const tenantStore = getTenantStore(store, c);
|
|
181
|
+
const variantsWithCounts = await Promise.all(benchmark.variants.map(async (v) => {
|
|
182
|
+
try {
|
|
183
|
+
const { total } = await tenantStore.querySessions({
|
|
184
|
+
tenantId: v.tenantId,
|
|
185
|
+
agentId: v.agentId,
|
|
186
|
+
tags: [v.tag],
|
|
187
|
+
from: benchmark.timeRange?.from,
|
|
188
|
+
to: benchmark.timeRange?.to,
|
|
189
|
+
limit: 1, // Minimal fetch — we only need the count
|
|
190
|
+
});
|
|
191
|
+
return { ...v, sessionCount: total };
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return { ...v, sessionCount: 0 };
|
|
195
|
+
}
|
|
196
|
+
}));
|
|
197
|
+
return c.json({
|
|
198
|
+
...benchmark,
|
|
199
|
+
variants: variantsWithCounts,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
// ─── PUT /:id/status — Transition status ────────────────
|
|
203
|
+
app.put('/:id/status', async (c) => {
|
|
204
|
+
const benchmarkStore = getBenchmarkStore(c);
|
|
205
|
+
if (!benchmarkStore) {
|
|
206
|
+
return c.json({ error: 'Database not available', status: 500 }, 500);
|
|
207
|
+
}
|
|
208
|
+
const tenantId = getTenantId(c);
|
|
209
|
+
const id = c.req.param('id');
|
|
210
|
+
const body = await c.req.json().catch(() => null);
|
|
211
|
+
if (!body || !body.status) {
|
|
212
|
+
return c.json({ error: 'status is required', status: 400 }, 400);
|
|
213
|
+
}
|
|
214
|
+
if (!VALID_STATUSES.has(body.status)) {
|
|
215
|
+
return c.json({ error: `Invalid status: ${body.status}`, status: 400 }, 400);
|
|
216
|
+
}
|
|
217
|
+
const newStatus = body.status;
|
|
218
|
+
// Get current benchmark
|
|
219
|
+
const current = benchmarkStore.getById(tenantId, id);
|
|
220
|
+
if (!current) {
|
|
221
|
+
return c.json({ error: 'Benchmark not found', status: 404 }, 404);
|
|
222
|
+
}
|
|
223
|
+
// When transitioning to "running": validate ≥1 session per variant
|
|
224
|
+
if (newStatus === 'running') {
|
|
225
|
+
const tenantStore = getTenantStore(store, c);
|
|
226
|
+
for (const v of current.variants) {
|
|
227
|
+
const { sessions } = await tenantStore.querySessions({
|
|
228
|
+
tenantId: v.tenantId,
|
|
229
|
+
agentId: v.agentId,
|
|
230
|
+
tags: [v.tag],
|
|
231
|
+
from: current.timeRange?.from,
|
|
232
|
+
to: current.timeRange?.to,
|
|
233
|
+
limit: 1,
|
|
234
|
+
});
|
|
235
|
+
if (sessions.length === 0) {
|
|
236
|
+
return c.json({
|
|
237
|
+
error: `Variant "${v.name}" has no sessions. Each variant must have at least 1 session to start.`,
|
|
238
|
+
status: 409,
|
|
239
|
+
}, 409);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const updated = benchmarkStore.updateStatus(tenantId, id, newStatus);
|
|
245
|
+
// When transitioning to "completed": compute and cache results
|
|
246
|
+
if (newStatus === 'completed') {
|
|
247
|
+
const tenantStore = getTenantStore(store, c);
|
|
248
|
+
await engine.computeResults(updated, tenantStore, benchmarkStore);
|
|
249
|
+
}
|
|
250
|
+
return c.json(updated);
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
const message = err instanceof Error ? err.message : 'Failed to update status';
|
|
254
|
+
if (message.includes('Invalid status transition')) {
|
|
255
|
+
return c.json({ error: message, status: 409 }, 409);
|
|
256
|
+
}
|
|
257
|
+
return c.json({ error: message, status: 400 }, 400);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
// ─── GET /:id/results — Get comparison results ──────────
|
|
261
|
+
app.get('/:id/results', async (c) => {
|
|
262
|
+
const benchmarkStore = getBenchmarkStore(c);
|
|
263
|
+
if (!benchmarkStore) {
|
|
264
|
+
return c.json({ error: 'Database not available', status: 500 }, 500);
|
|
265
|
+
}
|
|
266
|
+
const tenantId = getTenantId(c);
|
|
267
|
+
const id = c.req.param('id');
|
|
268
|
+
const benchmark = benchmarkStore.getById(tenantId, id);
|
|
269
|
+
if (!benchmark) {
|
|
270
|
+
return c.json({ error: 'Benchmark not found', status: 404 }, 404);
|
|
271
|
+
}
|
|
272
|
+
// Draft benchmarks can't have results
|
|
273
|
+
if (benchmark.status === 'draft') {
|
|
274
|
+
return c.json({ error: 'Cannot get results for a draft benchmark. Start it first.', status: 400 }, 400);
|
|
275
|
+
}
|
|
276
|
+
const tenantStore = getTenantStore(store, c);
|
|
277
|
+
const results = await engine.computeResults(benchmark, tenantStore, benchmarkStore);
|
|
278
|
+
// Strip distributions unless requested
|
|
279
|
+
const includeDistributions = c.req.query('includeDistributions') === 'true';
|
|
280
|
+
if (!includeDistributions) {
|
|
281
|
+
for (const v of results.variants) {
|
|
282
|
+
for (const key of Object.keys(v.metrics)) {
|
|
283
|
+
const stats = v.metrics[key];
|
|
284
|
+
if (stats) {
|
|
285
|
+
delete stats.values;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return c.json(results);
|
|
291
|
+
});
|
|
292
|
+
// ─── DELETE /:id — Delete benchmark ─────────────────────
|
|
293
|
+
app.delete('/:id', async (c) => {
|
|
294
|
+
const benchmarkStore = getBenchmarkStore(c);
|
|
295
|
+
if (!benchmarkStore) {
|
|
296
|
+
return c.json({ error: 'Database not available', status: 500 }, 500);
|
|
297
|
+
}
|
|
298
|
+
const tenantId = getTenantId(c);
|
|
299
|
+
const id = c.req.param('id');
|
|
300
|
+
const benchmark = benchmarkStore.getById(tenantId, id);
|
|
301
|
+
if (!benchmark) {
|
|
302
|
+
return c.json({ error: 'Benchmark not found', status: 404 }, 404);
|
|
303
|
+
}
|
|
304
|
+
if (benchmark.status === 'running' || benchmark.status === 'completed') {
|
|
305
|
+
return c.json({ error: `Cannot delete a ${benchmark.status} benchmark`, status: 409 }, 409);
|
|
306
|
+
}
|
|
307
|
+
benchmarkStore.delete(tenantId, id);
|
|
308
|
+
return c.body(null, 204);
|
|
309
|
+
});
|
|
310
|
+
return app;
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=benchmarks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmarks.js","sourceRoot":"","sources":["../../src/routes/benchmarks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrail REST API Routes (v0.8.0 — Stories 2.1, 2.2)
|
|
3
|
+
*
|
|
4
|
+
* POST /api/guardrails — Create rule
|
|
5
|
+
* GET /api/guardrails — List rules
|
|
6
|
+
* GET /api/guardrails/:id — Get rule
|
|
7
|
+
* PUT /api/guardrails/:id — Update rule
|
|
8
|
+
* DELETE /api/guardrails/:id — Delete rule
|
|
9
|
+
* GET /api/guardrails/:id/status — Get rule status + recent triggers
|
|
10
|
+
* GET /api/guardrails/history — List trigger history
|
|
11
|
+
*/
|
|
12
|
+
import { Hono } from 'hono';
|
|
13
|
+
import type { AuthVariables } from '../middleware/auth.js';
|
|
14
|
+
import type { GuardrailStore } from '../db/guardrail-store.js';
|
|
15
|
+
export declare function guardrailRoutes(guardrailStore: GuardrailStore): Hono<{
|
|
16
|
+
Variables: AuthVariables;
|
|
17
|
+
}, import("hono/types").BlankSchema, "/">;
|
|
18
|
+
//# sourceMappingURL=guardrails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/routes/guardrails.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAsC/D,wBAAgB,eAAe,CAAC,cAAc,EAAE,cAAc;eAC1B,aAAa;0CA2JhD"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrail REST API Routes (v0.8.0 — Stories 2.1, 2.2)
|
|
3
|
+
*
|
|
4
|
+
* POST /api/guardrails — Create rule
|
|
5
|
+
* GET /api/guardrails — List rules
|
|
6
|
+
* GET /api/guardrails/:id — Get rule
|
|
7
|
+
* PUT /api/guardrails/:id — Update rule
|
|
8
|
+
* DELETE /api/guardrails/:id — Delete rule
|
|
9
|
+
* GET /api/guardrails/:id/status — Get rule status + recent triggers
|
|
10
|
+
* GET /api/guardrails/history — List trigger history
|
|
11
|
+
*/
|
|
12
|
+
import { Hono } from 'hono';
|
|
13
|
+
import { ulid } from 'ulid';
|
|
14
|
+
import { CreateGuardrailRuleSchema, UpdateGuardrailRuleSchema } from '@agentlensai/core';
|
|
15
|
+
/**
|
|
16
|
+
* Validate that conditionConfig contains required fields for the given condition type.
|
|
17
|
+
* Returns an error message string if invalid, or null if valid.
|
|
18
|
+
*/
|
|
19
|
+
function validateConditionConfig(conditionType, config) {
|
|
20
|
+
switch (conditionType) {
|
|
21
|
+
case 'error_rate_threshold':
|
|
22
|
+
if (config.threshold !== undefined && (typeof config.threshold !== 'number' || config.threshold < 0 || config.threshold > 100)) {
|
|
23
|
+
return 'error_rate_threshold requires threshold to be a number between 0 and 100';
|
|
24
|
+
}
|
|
25
|
+
break;
|
|
26
|
+
case 'cost_limit':
|
|
27
|
+
if (config.maxCostUsd !== undefined && (typeof config.maxCostUsd !== 'number' || config.maxCostUsd < 0)) {
|
|
28
|
+
return 'cost_limit requires maxCostUsd to be a non-negative number';
|
|
29
|
+
}
|
|
30
|
+
if (config.scope !== undefined && config.scope !== 'session' && config.scope !== 'daily') {
|
|
31
|
+
return 'cost_limit scope must be "session" or "daily"';
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
case 'health_score_threshold':
|
|
35
|
+
if (config.minScore !== undefined && (typeof config.minScore !== 'number' || config.minScore < 0 || config.minScore > 100)) {
|
|
36
|
+
return 'health_score_threshold requires minScore to be a number between 0 and 100';
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
case 'custom_metric':
|
|
40
|
+
if (config.operator !== undefined) {
|
|
41
|
+
const validOps = ['gt', 'gte', 'lt', 'lte', 'eq'];
|
|
42
|
+
if (!validOps.includes(config.operator)) {
|
|
43
|
+
return `custom_metric operator must be one of: ${validOps.join(', ')}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
export function guardrailRoutes(guardrailStore) {
|
|
51
|
+
const app = new Hono();
|
|
52
|
+
// Helper to get tenant ID from auth
|
|
53
|
+
function getTenantId(c) {
|
|
54
|
+
const apiKeyInfo = c.get('apiKey');
|
|
55
|
+
return apiKeyInfo?.tenantId ?? 'default';
|
|
56
|
+
}
|
|
57
|
+
// POST / — Create guardrail rule
|
|
58
|
+
app.post('/', async (c) => {
|
|
59
|
+
const tenantId = getTenantId(c);
|
|
60
|
+
const rawBody = await c.req.json().catch(() => null);
|
|
61
|
+
if (!rawBody) {
|
|
62
|
+
return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
|
|
63
|
+
}
|
|
64
|
+
const result = CreateGuardrailRuleSchema.safeParse(rawBody);
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
return c.json({
|
|
67
|
+
error: 'Validation failed',
|
|
68
|
+
status: 400,
|
|
69
|
+
details: result.error.issues.map((i) => ({ path: i.path.join('.'), message: i.message })),
|
|
70
|
+
}, 400);
|
|
71
|
+
}
|
|
72
|
+
// H4: Validate that conditionConfig has required fields for the condition type
|
|
73
|
+
const configError = validateConditionConfig(result.data.conditionType, result.data.conditionConfig);
|
|
74
|
+
if (configError) {
|
|
75
|
+
return c.json({ error: 'Validation failed', status: 400, details: [{ path: 'conditionConfig', message: configError }] }, 400);
|
|
76
|
+
}
|
|
77
|
+
// H2: Validate webhook URL if action is notify_webhook
|
|
78
|
+
if (result.data.actionType === 'notify_webhook') {
|
|
79
|
+
const url = result.data.actionConfig?.url;
|
|
80
|
+
if (typeof url === 'string' && !/^https?:\/\//.test(url)) {
|
|
81
|
+
return c.json({ error: 'Validation failed', status: 400, details: [{ path: 'actionConfig.url', message: 'Webhook URL must start with http:// or https://' }] }, 400);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const now = new Date().toISOString();
|
|
85
|
+
const rule = {
|
|
86
|
+
id: ulid(),
|
|
87
|
+
tenantId,
|
|
88
|
+
...result.data,
|
|
89
|
+
createdAt: now,
|
|
90
|
+
updatedAt: now,
|
|
91
|
+
};
|
|
92
|
+
guardrailStore.createRule(rule);
|
|
93
|
+
return c.json(rule, 201);
|
|
94
|
+
});
|
|
95
|
+
// GET / — List guardrail rules
|
|
96
|
+
app.get('/', async (c) => {
|
|
97
|
+
const tenantId = getTenantId(c);
|
|
98
|
+
const agentId = c.req.query('agentId');
|
|
99
|
+
const rules = guardrailStore.listRules(tenantId, agentId || undefined);
|
|
100
|
+
return c.json({ rules });
|
|
101
|
+
});
|
|
102
|
+
// GET /history — List trigger history (BEFORE :id routes to avoid route conflict)
|
|
103
|
+
app.get('/history', async (c) => {
|
|
104
|
+
const tenantId = getTenantId(c);
|
|
105
|
+
const ruleId = c.req.query('ruleId');
|
|
106
|
+
const limit = parseInt(c.req.query('limit') ?? '50', 10);
|
|
107
|
+
const offset = parseInt(c.req.query('offset') ?? '0', 10);
|
|
108
|
+
const { triggers, total } = guardrailStore.listTriggerHistory(tenantId, {
|
|
109
|
+
ruleId: ruleId || undefined,
|
|
110
|
+
limit: Math.min(Math.max(1, limit), 200),
|
|
111
|
+
offset: Math.max(0, offset),
|
|
112
|
+
});
|
|
113
|
+
return c.json({ triggers, total });
|
|
114
|
+
});
|
|
115
|
+
// GET /:id — Get single rule
|
|
116
|
+
app.get('/:id', async (c) => {
|
|
117
|
+
const tenantId = getTenantId(c);
|
|
118
|
+
const ruleId = c.req.param('id');
|
|
119
|
+
const rule = guardrailStore.getRule(tenantId, ruleId);
|
|
120
|
+
if (!rule) {
|
|
121
|
+
return c.json({ error: 'Guardrail rule not found', status: 404 }, 404);
|
|
122
|
+
}
|
|
123
|
+
return c.json(rule);
|
|
124
|
+
});
|
|
125
|
+
// PUT /:id — Update rule
|
|
126
|
+
app.put('/:id', async (c) => {
|
|
127
|
+
const tenantId = getTenantId(c);
|
|
128
|
+
const ruleId = c.req.param('id');
|
|
129
|
+
const rawBody = await c.req.json().catch(() => null);
|
|
130
|
+
if (!rawBody) {
|
|
131
|
+
return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
|
|
132
|
+
}
|
|
133
|
+
const result = UpdateGuardrailRuleSchema.safeParse(rawBody);
|
|
134
|
+
if (!result.success) {
|
|
135
|
+
return c.json({
|
|
136
|
+
error: 'Validation failed',
|
|
137
|
+
status: 400,
|
|
138
|
+
details: result.error.issues.map((i) => ({ path: i.path.join('.'), message: i.message })),
|
|
139
|
+
}, 400);
|
|
140
|
+
}
|
|
141
|
+
// H2: Validate webhook URL if action is notify_webhook
|
|
142
|
+
if (result.data.actionType === 'notify_webhook') {
|
|
143
|
+
const url = result.data.actionConfig?.url;
|
|
144
|
+
if (typeof url === 'string' && !/^https?:\/\//.test(url)) {
|
|
145
|
+
return c.json({ error: 'Validation failed', status: 400, details: [{ path: 'actionConfig.url', message: 'Webhook URL must start with http:// or https://' }] }, 400);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Handle null agentId (clearing scope)
|
|
149
|
+
const updates = { ...result.data };
|
|
150
|
+
if (updates.agentId === null) {
|
|
151
|
+
updates.agentId = undefined;
|
|
152
|
+
}
|
|
153
|
+
const updated = guardrailStore.updateRule(tenantId, ruleId, updates);
|
|
154
|
+
if (!updated) {
|
|
155
|
+
return c.json({ error: 'Guardrail rule not found', status: 404 }, 404);
|
|
156
|
+
}
|
|
157
|
+
const rule = guardrailStore.getRule(tenantId, ruleId);
|
|
158
|
+
return c.json(rule);
|
|
159
|
+
});
|
|
160
|
+
// DELETE /:id — Delete rule
|
|
161
|
+
app.delete('/:id', async (c) => {
|
|
162
|
+
const tenantId = getTenantId(c);
|
|
163
|
+
const ruleId = c.req.param('id');
|
|
164
|
+
const deleted = guardrailStore.deleteRule(tenantId, ruleId);
|
|
165
|
+
if (!deleted) {
|
|
166
|
+
return c.json({ error: 'Guardrail rule not found', status: 404 }, 404);
|
|
167
|
+
}
|
|
168
|
+
return c.json({ ok: true });
|
|
169
|
+
});
|
|
170
|
+
// GET /:id/status — Get rule status + recent triggers
|
|
171
|
+
app.get('/:id/status', async (c) => {
|
|
172
|
+
const tenantId = getTenantId(c);
|
|
173
|
+
const ruleId = c.req.param('id');
|
|
174
|
+
const rule = guardrailStore.getRule(tenantId, ruleId);
|
|
175
|
+
if (!rule) {
|
|
176
|
+
return c.json({ error: 'Guardrail rule not found', status: 404 }, 404);
|
|
177
|
+
}
|
|
178
|
+
const state = guardrailStore.getState(tenantId, ruleId);
|
|
179
|
+
const recentTriggers = guardrailStore.getRecentTriggers(tenantId, ruleId, 10);
|
|
180
|
+
return c.json({ rule, state, recentTriggers });
|
|
181
|
+
});
|
|
182
|
+
return app;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=guardrails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.js","sourceRoot":"","sources":["../../src/routes/guardrails.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay REST Endpoint (Stories 2.2, 2.3)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/sessions/:id/replay — Returns ReplayState for session replay UI.
|
|
5
|
+
*
|
|
6
|
+
* Supports pagination, event type filtering, and server-side LRU caching.
|
|
7
|
+
*/
|
|
8
|
+
import { Hono } from 'hono';
|
|
9
|
+
import type { IEventStore, ReplayState } from '@agentlensai/core';
|
|
10
|
+
import type { AuthVariables } from '../middleware/auth.js';
|
|
11
|
+
interface CacheEntry {
|
|
12
|
+
state: ReplayState;
|
|
13
|
+
createdAt: number;
|
|
14
|
+
}
|
|
15
|
+
/** Simple Map-based LRU cache with TTL. Exported for testing. */
|
|
16
|
+
export declare const replayCache: Map<string, CacheEntry>;
|
|
17
|
+
/**
|
|
18
|
+
* Register the replay route directly on the provided Hono app.
|
|
19
|
+
* Path: GET /api/sessions/:id/replay
|
|
20
|
+
*
|
|
21
|
+
* Uses `registerReplayRoutes(app, store)` pattern (like health routes)
|
|
22
|
+
* since the path nests under /api/sessions which already has auth middleware.
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerReplayRoutes(app: Hono<{
|
|
25
|
+
Variables: AuthVariables;
|
|
26
|
+
}>, store: IEventStore): void;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/routes/replay.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAa,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAU3D,UAAU,UAAU;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iEAAiE;AACjE,eAAO,MAAM,WAAW,yBAAgC,CAAC;AAmDzD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,IAAI,CAAC;IAAE,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC,EACvC,KAAK,EAAE,WAAW,GACjB,IAAI,CAgGN"}
|