@agentlensai/server 0.5.0 → 0.7.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/health-snapshot-store.d.ts +33 -0
- package/dist/db/health-snapshot-store.d.ts.map +1 -0
- package/dist/db/health-snapshot-store.js +112 -0
- package/dist/db/health-snapshot-store.js.map +1 -0
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +67 -0
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/sqlite-store.d.ts +5 -0
- package/dist/db/sqlite-store.d.ts.map +1 -1
- package/dist/db/sqlite-store.js +15 -0
- package/dist/db/sqlite-store.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/alert-engine.d.ts.map +1 -1
- package/dist/lib/alert-engine.js +5 -2
- package/dist/lib/alert-engine.js.map +1 -1
- package/dist/lib/analysis/cost-analysis.d.ts.map +1 -1
- package/dist/lib/analysis/cost-analysis.js +5 -2
- package/dist/lib/analysis/cost-analysis.js.map +1 -1
- package/dist/lib/analysis/error-patterns.d.ts.map +1 -1
- package/dist/lib/analysis/error-patterns.js +5 -2
- package/dist/lib/analysis/error-patterns.js.map +1 -1
- package/dist/lib/analysis/performance-trends.d.ts.map +1 -1
- package/dist/lib/analysis/performance-trends.js +4 -1
- package/dist/lib/analysis/performance-trends.js.map +1 -1
- package/dist/lib/analysis/tool-sequences.d.ts.map +1 -1
- package/dist/lib/analysis/tool-sequences.js +5 -2
- package/dist/lib/analysis/tool-sequences.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/context/retrieval.d.ts +4 -0
- package/dist/lib/context/retrieval.d.ts.map +1 -1
- package/dist/lib/context/retrieval.js +4 -0
- package/dist/lib/context/retrieval.js.map +1 -1
- package/dist/lib/embeddings/local.js +2 -2
- package/dist/lib/embeddings/local.js.map +1 -1
- package/dist/lib/health/computer.d.ts +28 -0
- package/dist/lib/health/computer.d.ts.map +1 -0
- package/dist/lib/health/computer.js +270 -0
- package/dist/lib/health/computer.js.map +1 -0
- package/dist/lib/optimization/classifier.d.ts +34 -0
- package/dist/lib/optimization/classifier.d.ts.map +1 -0
- package/dist/lib/optimization/classifier.js +108 -0
- package/dist/lib/optimization/classifier.js.map +1 -0
- package/dist/lib/optimization/engine.d.ts +24 -0
- package/dist/lib/optimization/engine.d.ts.map +1 -0
- package/dist/lib/optimization/engine.js +202 -0
- package/dist/lib/optimization/engine.js.map +1 -0
- package/dist/lib/optimization/index.d.ts +10 -0
- package/dist/lib/optimization/index.d.ts.map +1 -0
- package/dist/lib/optimization/index.js +9 -0
- package/dist/lib/optimization/index.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/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/health.d.ts +21 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +142 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/optimize.d.ts +15 -0
- package/dist/routes/optimize.d.ts.map +1 -0
- package/dist/routes/optimize.js +55 -0
- package/dist/routes/optimize.js.map +1 -0
- package/dist/routes/recall.d.ts +2 -0
- package/dist/routes/recall.d.ts.map +1 -1
- package/dist/routes/recall.js +21 -1
- package/dist/routes/recall.js.map +1 -1
- package/dist/routes/reflect.d.ts.map +1 -1
- package/dist/routes/reflect.js +0 -1
- package/dist/routes/reflect.js.map +1 -1
- 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/dist/routes/tenant-helper.d.ts.map +1 -1
- package/dist/routes/tenant-helper.js +12 -0
- package/dist/routes/tenant-helper.js.map +1 -1
- 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,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health REST Endpoints (Story 1.4)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/agents/:id/health?window=7 — Single agent health score
|
|
5
|
+
* GET /api/health/overview?window=7 — All agents overview
|
|
6
|
+
* GET /api/health/history?agentId=X&days=30 — Snapshot history
|
|
7
|
+
* GET /api/config/health-weights — Current health weights
|
|
8
|
+
* PUT /api/config/health-weights — Update weights (501 placeholder)
|
|
9
|
+
*/
|
|
10
|
+
import { Hono } from 'hono';
|
|
11
|
+
import type { IEventStore } from '@agentlensai/core';
|
|
12
|
+
import type { AuthVariables } from '../middleware/auth.js';
|
|
13
|
+
import type { SqliteDb } from '../db/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* Register all health-related routes directly on the provided Hono app.
|
|
16
|
+
* Call this from index.ts passing the main app.
|
|
17
|
+
*/
|
|
18
|
+
export declare function registerHealthRoutes(app: Hono<{
|
|
19
|
+
Variables: AuthVariables;
|
|
20
|
+
}>, store: IEventStore, db?: SqliteDb): void;
|
|
21
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAsB/C;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,IAAI,CAAC;IAAE,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC,EACvC,KAAK,EAAE,WAAW,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,CA4IN"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health REST Endpoints (Story 1.4)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/agents/:id/health?window=7 — Single agent health score
|
|
5
|
+
* GET /api/health/overview?window=7 — All agents overview
|
|
6
|
+
* GET /api/health/history?agentId=X&days=30 — Snapshot history
|
|
7
|
+
* GET /api/config/health-weights — Current health weights
|
|
8
|
+
* PUT /api/config/health-weights — Update weights (501 placeholder)
|
|
9
|
+
*/
|
|
10
|
+
import { Hono } from 'hono';
|
|
11
|
+
import { DEFAULT_HEALTH_WEIGHTS, HealthWeightsSchema as WeightsSchema } from '@agentlensai/core';
|
|
12
|
+
import { getTenantStore } from './tenant-helper.js';
|
|
13
|
+
import { HealthComputer } from '../lib/health/computer.js';
|
|
14
|
+
import { HealthSnapshotStore } from '../db/health-snapshot-store.js';
|
|
15
|
+
/** Parse and validate `window` query param (integer 1-90, default 7) */
|
|
16
|
+
function parseWindow(raw) {
|
|
17
|
+
if (raw === undefined || raw === '')
|
|
18
|
+
return 7;
|
|
19
|
+
const val = parseInt(raw, 10);
|
|
20
|
+
if (isNaN(val) || val < 1 || val > 90) {
|
|
21
|
+
return { error: 'window must be an integer between 1 and 90' };
|
|
22
|
+
}
|
|
23
|
+
return val;
|
|
24
|
+
}
|
|
25
|
+
/** Parse and validate `days` query param (integer 1-365, default 30) */
|
|
26
|
+
function parseDays(raw) {
|
|
27
|
+
if (raw === undefined || raw === '')
|
|
28
|
+
return 30;
|
|
29
|
+
const val = parseInt(raw, 10);
|
|
30
|
+
if (isNaN(val) || val < 1 || val > 365) {
|
|
31
|
+
return { error: 'days must be an integer between 1 and 365' };
|
|
32
|
+
}
|
|
33
|
+
return val;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Register all health-related routes directly on the provided Hono app.
|
|
37
|
+
* Call this from index.ts passing the main app.
|
|
38
|
+
*/
|
|
39
|
+
export function registerHealthRoutes(app, store, db) {
|
|
40
|
+
const snapshotStore = db ? new HealthSnapshotStore(db) : null;
|
|
41
|
+
// ─── GET /api/agents/:id/health ───────────────────────
|
|
42
|
+
app.get('/api/agents/:id/health', async (c) => {
|
|
43
|
+
const tenantStore = getTenantStore(store, c);
|
|
44
|
+
const agentId = c.req.param('id');
|
|
45
|
+
const window = parseWindow(c.req.query('window'));
|
|
46
|
+
if (typeof window === 'object') {
|
|
47
|
+
return c.json({ error: window.error, status: 400 }, 400);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const computer = new HealthComputer(DEFAULT_HEALTH_WEIGHTS);
|
|
51
|
+
const score = await computer.compute(tenantStore, agentId, window);
|
|
52
|
+
if (!score) {
|
|
53
|
+
return c.json({ error: 'No sessions found for agent in window' }, 404);
|
|
54
|
+
}
|
|
55
|
+
// Lazy snapshot: save if first query of the day
|
|
56
|
+
if (snapshotStore) {
|
|
57
|
+
const apiKeyInfo = c.get('apiKey');
|
|
58
|
+
const tenantId = apiKeyInfo?.tenantId ?? 'default';
|
|
59
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
60
|
+
const existing = snapshotStore.get(tenantId, agentId, today);
|
|
61
|
+
if (!existing) {
|
|
62
|
+
const dim = (name) => score.dimensions.find((d) => d.name === name)?.score ?? 0;
|
|
63
|
+
snapshotStore.save(tenantId, {
|
|
64
|
+
agentId,
|
|
65
|
+
date: today,
|
|
66
|
+
overallScore: score.overallScore,
|
|
67
|
+
errorRateScore: dim('error_rate'),
|
|
68
|
+
costEfficiencyScore: dim('cost_efficiency'),
|
|
69
|
+
toolSuccessScore: dim('tool_success'),
|
|
70
|
+
latencyScore: dim('latency'),
|
|
71
|
+
completionRateScore: dim('completion_rate'),
|
|
72
|
+
sessionCount: score.sessionCount,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return c.json(score);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const message = error instanceof Error ? error.message : 'Internal error';
|
|
80
|
+
return c.json({ error: message, status: 500 }, 500);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// ─── GET /api/health/overview ─────────────────────────
|
|
84
|
+
app.get('/api/health/overview', async (c) => {
|
|
85
|
+
const tenantStore = getTenantStore(store, c);
|
|
86
|
+
const window = parseWindow(c.req.query('window'));
|
|
87
|
+
if (typeof window === 'object') {
|
|
88
|
+
return c.json({ error: window.error, status: 400 }, 400);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const computer = new HealthComputer(DEFAULT_HEALTH_WEIGHTS);
|
|
92
|
+
const agents = await computer.computeOverview(tenantStore, window);
|
|
93
|
+
return c.json({
|
|
94
|
+
agents,
|
|
95
|
+
computedAt: new Date().toISOString(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const message = error instanceof Error ? error.message : 'Internal error';
|
|
100
|
+
return c.json({ error: message, status: 500 }, 500);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// ─── GET /api/health/history ──────────────────────────
|
|
104
|
+
app.get('/api/health/history', async (c) => {
|
|
105
|
+
const agentId = c.req.query('agentId');
|
|
106
|
+
if (!agentId) {
|
|
107
|
+
return c.json({ error: 'Missing required query parameter: agentId', status: 400 }, 400);
|
|
108
|
+
}
|
|
109
|
+
const days = parseDays(c.req.query('days'));
|
|
110
|
+
if (typeof days === 'object') {
|
|
111
|
+
return c.json({ error: days.error, status: 400 }, 400);
|
|
112
|
+
}
|
|
113
|
+
if (!snapshotStore) {
|
|
114
|
+
return c.json({ error: 'Snapshot store not available', status: 500 }, 500);
|
|
115
|
+
}
|
|
116
|
+
const apiKeyInfo = c.get('apiKey');
|
|
117
|
+
const tenantId = apiKeyInfo?.tenantId ?? 'default';
|
|
118
|
+
const snapshots = snapshotStore.getHistory(tenantId, agentId, days);
|
|
119
|
+
return c.json({ snapshots, agentId, days });
|
|
120
|
+
});
|
|
121
|
+
// ─── GET /api/config/health-weights ───────────────────
|
|
122
|
+
app.get('/api/config/health-weights', async (c) => {
|
|
123
|
+
return c.json(DEFAULT_HEALTH_WEIGHTS);
|
|
124
|
+
});
|
|
125
|
+
// ─── PUT /api/config/health-weights ───────────────────
|
|
126
|
+
app.put('/api/config/health-weights', async (c) => {
|
|
127
|
+
const rawBody = await c.req.json().catch(() => null);
|
|
128
|
+
if (!rawBody) {
|
|
129
|
+
return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
|
|
130
|
+
}
|
|
131
|
+
// Validate with schema
|
|
132
|
+
const result = WeightsSchema.safeParse(rawBody);
|
|
133
|
+
if (!result.success) {
|
|
134
|
+
return c.json({
|
|
135
|
+
error: result.error.issues.map((i) => i.message).join('; '),
|
|
136
|
+
status: 400,
|
|
137
|
+
}, 400);
|
|
138
|
+
}
|
|
139
|
+
return c.json({ error: 'Weight customization coming in a future release', status: 501 }, 501);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimize REST Endpoint (Story 2.4)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/optimize/recommendations?agentId=X&period=7&limit=10
|
|
5
|
+
*
|
|
6
|
+
* Returns cost optimization recommendations based on LLM call patterns.
|
|
7
|
+
* Requires auth (tenant-scoped).
|
|
8
|
+
*/
|
|
9
|
+
import { Hono } from 'hono';
|
|
10
|
+
import type { IEventStore } from '@agentlensai/core';
|
|
11
|
+
import type { AuthVariables } from '../middleware/auth.js';
|
|
12
|
+
export declare function optimizeRoutes(store: IEventStore): Hono<{
|
|
13
|
+
Variables: AuthVariables;
|
|
14
|
+
}, import("hono/types").BlankSchema, "/">;
|
|
15
|
+
//# sourceMappingURL=optimize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimize.d.ts","sourceRoot":"","sources":["../../src/routes/optimize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW;eACb,aAAa;0CAqDhD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimize REST Endpoint (Story 2.4)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/optimize/recommendations?agentId=X&period=7&limit=10
|
|
5
|
+
*
|
|
6
|
+
* Returns cost optimization recommendations based on LLM call patterns.
|
|
7
|
+
* Requires auth (tenant-scoped).
|
|
8
|
+
*/
|
|
9
|
+
import { Hono } from 'hono';
|
|
10
|
+
import { getTenantStore } from './tenant-helper.js';
|
|
11
|
+
import { OptimizationEngine } from '../lib/optimization/index.js';
|
|
12
|
+
export function optimizeRoutes(store) {
|
|
13
|
+
const app = new Hono();
|
|
14
|
+
// GET /api/optimize/recommendations
|
|
15
|
+
app.get('/recommendations', async (c) => {
|
|
16
|
+
const tenantStore = getTenantStore(store, c);
|
|
17
|
+
// Parse and validate query params
|
|
18
|
+
const agentId = c.req.query('agentId') || undefined;
|
|
19
|
+
const periodStr = c.req.query('period');
|
|
20
|
+
const limitStr = c.req.query('limit');
|
|
21
|
+
// period: integer 1-90, default 7
|
|
22
|
+
let period = 7;
|
|
23
|
+
if (periodStr !== undefined && periodStr !== '') {
|
|
24
|
+
const parsed = parseInt(periodStr, 10);
|
|
25
|
+
if (isNaN(parsed) || parsed < 1 || parsed > 90) {
|
|
26
|
+
return c.json({ error: 'Invalid period: must be an integer between 1 and 90', status: 400 }, 400);
|
|
27
|
+
}
|
|
28
|
+
period = parsed;
|
|
29
|
+
}
|
|
30
|
+
// limit: integer 1-50, default 10
|
|
31
|
+
let limit = 10;
|
|
32
|
+
if (limitStr !== undefined && limitStr !== '') {
|
|
33
|
+
const parsed = parseInt(limitStr, 10);
|
|
34
|
+
if (isNaN(parsed) || parsed < 1 || parsed > 50) {
|
|
35
|
+
return c.json({ error: 'Invalid limit: must be an integer between 1 and 50', status: 400 }, 400);
|
|
36
|
+
}
|
|
37
|
+
limit = parsed;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const engine = new OptimizationEngine();
|
|
41
|
+
const result = await engine.getRecommendations(tenantStore, {
|
|
42
|
+
agentId,
|
|
43
|
+
period,
|
|
44
|
+
limit,
|
|
45
|
+
});
|
|
46
|
+
return c.json(result);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : 'Internal error';
|
|
50
|
+
return c.json({ error: message, status: 500 }, 500);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return app;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=optimize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimize.js","sourceRoot":"","sources":["../../src/routes/optimize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAgC,CAAC;IAErD,oCAAoC;IACpC,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACtC,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAE7C,kCAAkC;QAClC,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC;QACpD,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEtC,kCAAkC;QAClC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACvC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;gBAC/C,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,qDAAqD,EAAE,MAAM,EAAE,GAAG,EAAE,EAC7E,GAAG,CACJ,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,MAAM,CAAC;QAClB,CAAC;QAED,kCAAkC;QAClC,IAAI,KAAK,GAAG,EAAE,CAAC;QACf,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACtC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;gBAC/C,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,oDAAoD,EAAE,MAAM,EAAE,GAAG,EAAE,EAC5E,GAAG,CACJ,CAAC;YACJ,CAAC;YACD,KAAK,GAAG,MAAM,CAAC;QACjB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE;gBAC1D,OAAO;gBACP,MAAM;gBACN,KAAK;aACN,CAAC,CAAC;YAEH,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC1E,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/routes/recall.d.ts
CHANGED
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
* and returns enriched results.
|
|
8
8
|
*/
|
|
9
9
|
import { Hono } from 'hono';
|
|
10
|
+
import type { IEventStore } from '@agentlensai/core';
|
|
10
11
|
import type { AuthVariables } from '../middleware/auth.js';
|
|
11
12
|
import type { EmbeddingService } from '../lib/embeddings/index.js';
|
|
12
13
|
import type { EmbeddingStore } from '../db/embedding-store.js';
|
|
13
14
|
export interface RecallRouteDeps {
|
|
14
15
|
embeddingService: EmbeddingService | null;
|
|
15
16
|
embeddingStore: EmbeddingStore | null;
|
|
17
|
+
eventStore?: IEventStore;
|
|
16
18
|
}
|
|
17
19
|
export declare function recallRoutes(deps: RecallRouteDeps): Hono<{
|
|
18
20
|
Variables: AuthVariables;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/routes/recall.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,0BAA0B,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/routes/recall.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,0BAA0B,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe;eACd,aAAa;0CAsGhD"}
|