ai-experiments 2.1.3 → 2.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +17 -0
- package/README.md +13 -1
- package/dist/clickhouse-storage.d.ts +227 -0
- package/dist/clickhouse-storage.d.ts.map +1 -0
- package/dist/clickhouse-storage.js +522 -0
- package/dist/clickhouse-storage.js.map +1 -0
- package/dist/decide.d.ts.map +1 -1
- package/dist/decide.js +6 -2
- package/dist/decide.js.map +1 -1
- package/dist/experiment.js +9 -9
- package/dist/experiment.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/tracking.js +3 -3
- package/dist/tracking.js.map +1 -1
- package/package.json +16 -16
- package/src/clickhouse-storage.ts +698 -0
- package/src/decide.ts +8 -10
- package/src/experiment.ts +12 -12
- package/src/index.ts +13 -3
- package/src/tracking.ts +3 -3
- package/test/cartesian.test.ts +287 -0
- package/test/clickhouse-storage.test.ts +470 -0
- package/test/decide.test.ts +414 -0
- package/test/experiment.test.ts +473 -0
- package/test/tracking.test.ts +347 -0
- package/vitest.config.ts +34 -0
- package/LICENSE +0 -21
- package/dist/chdb-storage.d.ts +0 -135
- package/dist/chdb-storage.d.ts.map +0 -1
- package/dist/chdb-storage.js +0 -468
- package/dist/chdb-storage.js.map +0 -1
- package/src/cartesian.js +0 -215
- package/src/chdb-storage.js +0 -464
- package/src/chdb-storage.ts +0 -567
- package/src/decide.js +0 -328
- package/src/experiment.js +0 -291
- package/src/index.js +0 -20
- package/src/tracking.js +0 -309
- package/src/types.js +0 -4
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClickHouse storage backend for AI experiments.
|
|
3
|
+
*
|
|
4
|
+
* Migrated (per [ADR-0003](../../../docs/adr/0003-storage-strategy-pg-clickhouse-default.md))
|
|
5
|
+
* from the standalone embedded `chdb` adapter to the canonical
|
|
6
|
+
* `ClickHouseProvider` from `ai-database`. Experiment runs are recorded as
|
|
7
|
+
* SVO Actions on the canonical `actions` table:
|
|
8
|
+
*
|
|
9
|
+
* - `verb` — event type (e.g. `variant.complete`)
|
|
10
|
+
* - `subject` — `experimentId`
|
|
11
|
+
* - `object` — `variantId`
|
|
12
|
+
* - `roles` — `{ runId }` (other named roles can be carried here)
|
|
13
|
+
* - `data` — JSON payload (`experimentName`, `variantName`, `dimensions`,
|
|
14
|
+
* `runId`, `success`, `durationMs`, `result`, `metricName`,
|
|
15
|
+
* `metricValue`, `errorMessage`, `errorStack`, `metadata`)
|
|
16
|
+
* - `status` — `'completed'` for successful runs, `'failed'` otherwise
|
|
17
|
+
*
|
|
18
|
+
* Writes go through {@link ClickHouseProvider.recordAction} (and
|
|
19
|
+
* {@link ClickHouseProvider.commitBatch} when bulk-storing). Aggregations
|
|
20
|
+
* use {@link ClickHouseProvider.analyticsQuery} against the `actions` table
|
|
21
|
+
* with `JSONExtract*` against the `data` column. This is the **option (c)
|
|
22
|
+
* hybrid** approach from the migration plan: SVO surface for writes, raw
|
|
23
|
+
* analytical SQL for the experiment-specific aggregations the SVO surface
|
|
24
|
+
* doesn't expose directly (cartesian grids, time-series rollups,
|
|
25
|
+
* best-variant selection by metric).
|
|
26
|
+
*
|
|
27
|
+
* Deployment shift: the previous `chdb` adapter used embedded ClickHouse
|
|
28
|
+
* (file-system path). This adapter is HTTP-based — callers wire up a
|
|
29
|
+
* ClickHouse server (self-hosted or ClickHouse Cloud) via
|
|
30
|
+
* {@link createClickHouseHttpFetcher}. Tests use a fake fetcher.
|
|
31
|
+
*
|
|
32
|
+
* @packageDocumentation
|
|
33
|
+
*/
|
|
34
|
+
import { createClickHouseProvider, bootstrapClickHouseSchema, } from 'ai-database';
|
|
35
|
+
const DEFAULT_NAMESPACE = 'experiments';
|
|
36
|
+
/**
|
|
37
|
+
* Generate a stable event id. Timestamp + monotonic counter inside the
|
|
38
|
+
* process — ClickHouse de-dupes on `actions.id` only via explicit DELETE,
|
|
39
|
+
* so collisions across processes are tolerated (each becomes a separate
|
|
40
|
+
* row).
|
|
41
|
+
*/
|
|
42
|
+
function makeEventIdFactory() {
|
|
43
|
+
let counter = 0;
|
|
44
|
+
return () => `evt-${Date.now()}-${++counter}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Map a {@link TrackingEvent} to the SVO Action shape.
|
|
48
|
+
*
|
|
49
|
+
* `verb` carries the event type (`variant.complete`, etc.).
|
|
50
|
+
* `subject` = `experimentId`, `object` = `variantId`. Everything else
|
|
51
|
+
* lands in `data` so analytical queries can `JSONExtract*` whatever they
|
|
52
|
+
* need without changing the schema.
|
|
53
|
+
*/
|
|
54
|
+
function eventToActionInput(event, eventId) {
|
|
55
|
+
const data = event.data;
|
|
56
|
+
const experimentId = String(data['experimentId'] ?? '');
|
|
57
|
+
const variantId = String(data['variantId'] ?? '');
|
|
58
|
+
const runId = String(data['runId'] ?? '');
|
|
59
|
+
const success = data['success'] !== false;
|
|
60
|
+
const status = event.type === 'variant.error' || !success ? 'failed' : 'completed';
|
|
61
|
+
// Capture every payload field — aggregation SQL uses JSONExtract* against
|
|
62
|
+
// the `data` JSON blob.
|
|
63
|
+
const errorMessage = data['error'] instanceof Error
|
|
64
|
+
? data['error'].message
|
|
65
|
+
: data['errorMessage'] !== undefined
|
|
66
|
+
? String(data['errorMessage'])
|
|
67
|
+
: '';
|
|
68
|
+
const errorStack = data['error'] instanceof Error ? data['error'].stack ?? '' : '';
|
|
69
|
+
const durationMs = Number(data['duration'] ?? data['durationMs'] ?? 0);
|
|
70
|
+
const payload = {
|
|
71
|
+
eventId,
|
|
72
|
+
eventType: event.type,
|
|
73
|
+
timestamp: event.timestamp.toISOString(),
|
|
74
|
+
experimentId,
|
|
75
|
+
experimentName: String(data['experimentName'] ?? ''),
|
|
76
|
+
variantId,
|
|
77
|
+
variantName: String(data['variantName'] ?? ''),
|
|
78
|
+
runId,
|
|
79
|
+
success: success ? 1 : 0,
|
|
80
|
+
durationMs,
|
|
81
|
+
dimensions: data['dimensions'] ?? data['config'] ?? {},
|
|
82
|
+
result: data['result'] ?? {},
|
|
83
|
+
metricName: String(data['metricName'] ?? ''),
|
|
84
|
+
metricValue: Number(data['metricValue'] ?? 0),
|
|
85
|
+
errorMessage,
|
|
86
|
+
errorStack,
|
|
87
|
+
metadata: data['metadata'] ?? {},
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
id: eventId,
|
|
91
|
+
verb: event.type,
|
|
92
|
+
subject: experimentId,
|
|
93
|
+
object: variantId,
|
|
94
|
+
roles: runId ? { runId } : {},
|
|
95
|
+
data: payload,
|
|
96
|
+
status,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* ClickHouse-backed experiment storage built on the canonical
|
|
101
|
+
* {@link ClickHouseProvider} adapter. Implements
|
|
102
|
+
* {@link TrackingBackend}.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* import { configureTracking } from 'ai-experiments'
|
|
107
|
+
* import { createClickHouseExperimentStorage } from 'ai-experiments/storage'
|
|
108
|
+
* import { createClickHouseHttpFetcher } from 'ai-database'
|
|
109
|
+
*
|
|
110
|
+
* const fetcher = createClickHouseHttpFetcher({
|
|
111
|
+
* url: process.env.CLICKHOUSE_URL!,
|
|
112
|
+
* username: process.env.CLICKHOUSE_USER,
|
|
113
|
+
* password: process.env.CLICKHOUSE_PASSWORD,
|
|
114
|
+
* database: 'aidb',
|
|
115
|
+
* })
|
|
116
|
+
* const storage = createClickHouseExperimentStorage({ fetcher })
|
|
117
|
+
*
|
|
118
|
+
* configureTracking({ backend: storage })
|
|
119
|
+
*
|
|
120
|
+
* // Later: analyse results
|
|
121
|
+
* const best = await storage.getBestVariant('my-experiment')
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export class ClickHouseExperimentStorage {
|
|
125
|
+
provider;
|
|
126
|
+
fetcher;
|
|
127
|
+
database;
|
|
128
|
+
namespace;
|
|
129
|
+
autoBootstrap;
|
|
130
|
+
bootstrapped = false;
|
|
131
|
+
nextEventId = makeEventIdFactory();
|
|
132
|
+
constructor(options) {
|
|
133
|
+
this.fetcher = options.fetcher;
|
|
134
|
+
this.database = options.database ?? 'aidb';
|
|
135
|
+
this.namespace = options.namespace ?? DEFAULT_NAMESPACE;
|
|
136
|
+
this.autoBootstrap = options.autoBootstrap ?? false;
|
|
137
|
+
this.provider = createClickHouseProvider({
|
|
138
|
+
fetcher: options.fetcher,
|
|
139
|
+
database: this.database,
|
|
140
|
+
namespace: this.namespace,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/** Underlying canonical CH provider (escape hatch for advanced callers). */
|
|
144
|
+
getProvider() {
|
|
145
|
+
return this.provider;
|
|
146
|
+
}
|
|
147
|
+
async ensureBootstrap() {
|
|
148
|
+
if (this.bootstrapped)
|
|
149
|
+
return;
|
|
150
|
+
if (this.autoBootstrap) {
|
|
151
|
+
await bootstrapClickHouseSchema(this.fetcher, { database: this.database });
|
|
152
|
+
}
|
|
153
|
+
this.bootstrapped = true;
|
|
154
|
+
}
|
|
155
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
156
|
+
// TrackingBackend implementation
|
|
157
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
158
|
+
async track(event) {
|
|
159
|
+
await this.ensureBootstrap();
|
|
160
|
+
const eventId = this.nextEventId();
|
|
161
|
+
const action = eventToActionInput(event, eventId);
|
|
162
|
+
await this.provider.recordAction({
|
|
163
|
+
verb: action.verb,
|
|
164
|
+
subject: action.subject,
|
|
165
|
+
object: action.object,
|
|
166
|
+
roles: action.roles,
|
|
167
|
+
data: action.data,
|
|
168
|
+
status: action.status,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async flush() {
|
|
172
|
+
// Writes go straight through HTTP — nothing to flush.
|
|
173
|
+
}
|
|
174
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
175
|
+
// Direct experiment-result API
|
|
176
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
177
|
+
async storeResult(result) {
|
|
178
|
+
await this.track({
|
|
179
|
+
type: 'variant.complete',
|
|
180
|
+
timestamp: result.completedAt,
|
|
181
|
+
data: {
|
|
182
|
+
experimentId: result.experimentId,
|
|
183
|
+
variantId: result.variantId,
|
|
184
|
+
variantName: result.variantName,
|
|
185
|
+
runId: result.runId,
|
|
186
|
+
success: result.success,
|
|
187
|
+
duration: result.duration,
|
|
188
|
+
result: result.result,
|
|
189
|
+
metricValue: result.metricValue,
|
|
190
|
+
error: result.error,
|
|
191
|
+
metadata: result.metadata,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Bulk-store results via {@link ClickHouseProvider.commitBatch}. One
|
|
197
|
+
* HTTP POST per call carries the entire batch as `JSONEachRow`.
|
|
198
|
+
*/
|
|
199
|
+
async storeResults(results) {
|
|
200
|
+
if (results.length === 0)
|
|
201
|
+
return;
|
|
202
|
+
await this.ensureBootstrap();
|
|
203
|
+
const actions = results.map((r) => {
|
|
204
|
+
const action = eventToActionInput({
|
|
205
|
+
type: 'variant.complete',
|
|
206
|
+
timestamp: r.completedAt,
|
|
207
|
+
data: {
|
|
208
|
+
experimentId: r.experimentId,
|
|
209
|
+
variantId: r.variantId,
|
|
210
|
+
variantName: r.variantName,
|
|
211
|
+
runId: r.runId,
|
|
212
|
+
success: r.success,
|
|
213
|
+
duration: r.duration,
|
|
214
|
+
result: r.result,
|
|
215
|
+
metricValue: r.metricValue,
|
|
216
|
+
error: r.error,
|
|
217
|
+
metadata: r.metadata,
|
|
218
|
+
},
|
|
219
|
+
}, this.nextEventId());
|
|
220
|
+
return action;
|
|
221
|
+
});
|
|
222
|
+
await this.provider.commitBatch({ actions });
|
|
223
|
+
}
|
|
224
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
225
|
+
// Analytics queries (raw SQL via canonical adapter's analyticsQuery)
|
|
226
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
227
|
+
/** Quote a SQL string literal — same shape as the canonical adapter. */
|
|
228
|
+
quote(value) {
|
|
229
|
+
return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Get all experiments seen in the actions table (variant.complete events).
|
|
233
|
+
*/
|
|
234
|
+
async getExperiments() {
|
|
235
|
+
await this.ensureBootstrap();
|
|
236
|
+
const ns = this.quote(this.namespace);
|
|
237
|
+
const sql = `
|
|
238
|
+
SELECT
|
|
239
|
+
subject AS experimentId,
|
|
240
|
+
any(JSONExtractString(data, 'experimentName')) AS experimentName,
|
|
241
|
+
uniq(object) AS variantCount,
|
|
242
|
+
count() AS runCount,
|
|
243
|
+
toString(min(created_at)) AS firstRun,
|
|
244
|
+
toString(max(created_at)) AS lastRun
|
|
245
|
+
FROM ${this.database}.actions
|
|
246
|
+
WHERE ns = ${ns} AND verb = 'variant.complete' AND subject != ''
|
|
247
|
+
GROUP BY subject
|
|
248
|
+
ORDER BY lastRun DESC
|
|
249
|
+
`;
|
|
250
|
+
const rows = await this.provider.analyticsQuery(sql);
|
|
251
|
+
return rows.map((row) => ({
|
|
252
|
+
experimentId: String(row['experimentId'] ?? ''),
|
|
253
|
+
experimentName: String(row['experimentName'] ?? ''),
|
|
254
|
+
variantCount: Number(row['variantCount'] ?? 0),
|
|
255
|
+
runCount: Number(row['runCount'] ?? 0),
|
|
256
|
+
firstRun: String(row['firstRun'] ?? ''),
|
|
257
|
+
lastRun: String(row['lastRun'] ?? ''),
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
async getVariantStats(experimentId) {
|
|
261
|
+
await this.ensureBootstrap();
|
|
262
|
+
const ns = this.quote(this.namespace);
|
|
263
|
+
const exp = this.quote(experimentId);
|
|
264
|
+
const sql = `
|
|
265
|
+
SELECT
|
|
266
|
+
object AS variantId,
|
|
267
|
+
any(JSONExtractString(data, 'variantName')) AS variantName,
|
|
268
|
+
count() AS runCount,
|
|
269
|
+
countIf(JSONExtractInt(data, 'success') = 1) AS successCount,
|
|
270
|
+
countIf(JSONExtractInt(data, 'success') = 1) / count() AS successRate,
|
|
271
|
+
avg(JSONExtractFloat(data, 'durationMs')) AS avgDuration,
|
|
272
|
+
avg(JSONExtractFloat(data, 'metricValue')) AS avgMetric,
|
|
273
|
+
min(JSONExtractFloat(data, 'metricValue')) AS minMetric,
|
|
274
|
+
max(JSONExtractFloat(data, 'metricValue')) AS maxMetric
|
|
275
|
+
FROM ${this.database}.actions
|
|
276
|
+
WHERE ns = ${ns} AND verb = 'variant.complete' AND subject = ${exp}
|
|
277
|
+
GROUP BY object
|
|
278
|
+
ORDER BY avgMetric DESC
|
|
279
|
+
`;
|
|
280
|
+
const rows = await this.provider.analyticsQuery(sql);
|
|
281
|
+
return rows.map((row) => ({
|
|
282
|
+
variantId: String(row['variantId'] ?? ''),
|
|
283
|
+
variantName: String(row['variantName'] ?? ''),
|
|
284
|
+
runCount: Number(row['runCount'] ?? 0),
|
|
285
|
+
successCount: Number(row['successCount'] ?? 0),
|
|
286
|
+
successRate: Number(row['successRate'] ?? 0),
|
|
287
|
+
avgDuration: Number(row['avgDuration'] ?? 0),
|
|
288
|
+
avgMetric: Number(row['avgMetric'] ?? 0),
|
|
289
|
+
minMetric: Number(row['minMetric'] ?? 0),
|
|
290
|
+
maxMetric: Number(row['maxMetric'] ?? 0),
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
async getBestVariant(experimentId, options = {}) {
|
|
294
|
+
await this.ensureBootstrap();
|
|
295
|
+
const { metric = 'avgMetric', minimumRuns = 1 } = options;
|
|
296
|
+
const ns = this.quote(this.namespace);
|
|
297
|
+
const exp = this.quote(experimentId);
|
|
298
|
+
const orderBy = metric === 'avgDuration' ? 'ASC' : 'DESC';
|
|
299
|
+
const metricExpr = metric === 'successRate'
|
|
300
|
+
? "countIf(JSONExtractInt(data, 'success') = 1) / count()"
|
|
301
|
+
: metric === 'avgDuration'
|
|
302
|
+
? "avg(JSONExtractFloat(data, 'durationMs'))"
|
|
303
|
+
: "avg(JSONExtractFloat(data, 'metricValue'))";
|
|
304
|
+
const sql = `
|
|
305
|
+
SELECT
|
|
306
|
+
object AS variantId,
|
|
307
|
+
any(JSONExtractString(data, 'variantName')) AS variantName,
|
|
308
|
+
${metricExpr} AS metricValue,
|
|
309
|
+
count() AS runCount
|
|
310
|
+
FROM ${this.database}.actions
|
|
311
|
+
WHERE ns = ${ns} AND verb = 'variant.complete' AND subject = ${exp}
|
|
312
|
+
GROUP BY object
|
|
313
|
+
HAVING runCount >= ${Number(minimumRuns)}
|
|
314
|
+
ORDER BY metricValue ${orderBy}
|
|
315
|
+
LIMIT 1
|
|
316
|
+
`;
|
|
317
|
+
const rows = await this.provider.analyticsQuery(sql);
|
|
318
|
+
if (rows.length === 0)
|
|
319
|
+
return null;
|
|
320
|
+
const row = rows[0];
|
|
321
|
+
return {
|
|
322
|
+
variantId: String(row['variantId'] ?? ''),
|
|
323
|
+
variantName: String(row['variantName'] ?? ''),
|
|
324
|
+
metricValue: Number(row['metricValue'] ?? 0),
|
|
325
|
+
runCount: Number(row['runCount'] ?? 0),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
async getCartesianAnalysis(experimentId, dimension) {
|
|
329
|
+
await this.ensureBootstrap();
|
|
330
|
+
const ns = this.quote(this.namespace);
|
|
331
|
+
const exp = this.quote(experimentId);
|
|
332
|
+
const dimQuoted = this.quote(dimension);
|
|
333
|
+
const sql = `
|
|
334
|
+
SELECT
|
|
335
|
+
JSONExtractString(JSONExtractRaw(data, 'dimensions'), ${dimQuoted}) AS dimensionValue,
|
|
336
|
+
count() AS runCount,
|
|
337
|
+
avg(JSONExtractFloat(data, 'metricValue')) AS avgMetric,
|
|
338
|
+
countIf(JSONExtractInt(data, 'success') = 1) / count() AS successRate
|
|
339
|
+
FROM ${this.database}.actions
|
|
340
|
+
WHERE ns = ${ns} AND verb = 'variant.complete' AND subject = ${exp}
|
|
341
|
+
GROUP BY dimensionValue
|
|
342
|
+
HAVING dimensionValue != ''
|
|
343
|
+
ORDER BY avgMetric DESC
|
|
344
|
+
`;
|
|
345
|
+
const rows = await this.provider.analyticsQuery(sql);
|
|
346
|
+
return rows.map((row) => ({
|
|
347
|
+
dimensionValue: String(row['dimensionValue'] ?? ''),
|
|
348
|
+
runCount: Number(row['runCount'] ?? 0),
|
|
349
|
+
avgMetric: Number(row['avgMetric'] ?? 0),
|
|
350
|
+
successRate: Number(row['successRate'] ?? 0),
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
async getCartesianGrid(experimentId, dimensions) {
|
|
354
|
+
await this.ensureBootstrap();
|
|
355
|
+
const ns = this.quote(this.namespace);
|
|
356
|
+
const exp = this.quote(experimentId);
|
|
357
|
+
const dimExtracts = dimensions
|
|
358
|
+
.map((d) => `JSONExtractString(JSONExtractRaw(data, 'dimensions'), ${this.quote(d)}) AS dim_${d}`)
|
|
359
|
+
.join(', ');
|
|
360
|
+
const dimGroupBy = dimensions.map((d) => `dim_${d}`).join(', ');
|
|
361
|
+
const sql = `
|
|
362
|
+
SELECT
|
|
363
|
+
${dimExtracts},
|
|
364
|
+
count() AS runCount,
|
|
365
|
+
avg(JSONExtractFloat(data, 'metricValue')) AS avgMetric,
|
|
366
|
+
countIf(JSONExtractInt(data, 'success') = 1) / count() AS successRate
|
|
367
|
+
FROM ${this.database}.actions
|
|
368
|
+
WHERE ns = ${ns} AND verb = 'variant.complete' AND subject = ${exp}
|
|
369
|
+
GROUP BY ${dimGroupBy}
|
|
370
|
+
ORDER BY avgMetric DESC
|
|
371
|
+
`;
|
|
372
|
+
const rows = await this.provider.analyticsQuery(sql);
|
|
373
|
+
return rows.map((row) => {
|
|
374
|
+
const dims = {};
|
|
375
|
+
for (const d of dimensions) {
|
|
376
|
+
dims[d] = String(row[`dim_${d}`] ?? '');
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
dimensions: dims,
|
|
380
|
+
runCount: Number(row['runCount'] ?? 0),
|
|
381
|
+
avgMetric: Number(row['avgMetric'] ?? 0),
|
|
382
|
+
successRate: Number(row['successRate'] ?? 0),
|
|
383
|
+
};
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
async getTimeSeries(experimentId, options = {}) {
|
|
387
|
+
await this.ensureBootstrap();
|
|
388
|
+
const { interval = 'day', variantId } = options;
|
|
389
|
+
const ns = this.quote(this.namespace);
|
|
390
|
+
const exp = this.quote(experimentId);
|
|
391
|
+
const dateFunc = interval === 'hour'
|
|
392
|
+
? 'toStartOfHour(created_at)'
|
|
393
|
+
: interval === 'week'
|
|
394
|
+
? 'toStartOfWeek(created_at)'
|
|
395
|
+
: 'toStartOfDay(created_at)';
|
|
396
|
+
const variantFilter = variantId ? `AND object = ${this.quote(variantId)}` : '';
|
|
397
|
+
const sql = `
|
|
398
|
+
SELECT
|
|
399
|
+
toString(${dateFunc}) AS period,
|
|
400
|
+
count() AS runCount,
|
|
401
|
+
avg(JSONExtractFloat(data, 'metricValue')) AS avgMetric,
|
|
402
|
+
countIf(JSONExtractInt(data, 'success') = 1) / count() AS successRate
|
|
403
|
+
FROM ${this.database}.actions
|
|
404
|
+
WHERE ns = ${ns} AND verb = 'variant.complete' AND subject = ${exp}
|
|
405
|
+
${variantFilter}
|
|
406
|
+
GROUP BY period
|
|
407
|
+
ORDER BY period ASC
|
|
408
|
+
`;
|
|
409
|
+
const rows = await this.provider.analyticsQuery(sql);
|
|
410
|
+
return rows.map((row) => ({
|
|
411
|
+
period: String(row['period'] ?? ''),
|
|
412
|
+
runCount: Number(row['runCount'] ?? 0),
|
|
413
|
+
avgMetric: Number(row['avgMetric'] ?? 0),
|
|
414
|
+
successRate: Number(row['successRate'] ?? 0),
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
417
|
+
async getEvents(experimentId, options = {}) {
|
|
418
|
+
await this.ensureBootstrap();
|
|
419
|
+
const { eventType, variantId, limit = 100 } = options;
|
|
420
|
+
const ns = this.quote(this.namespace);
|
|
421
|
+
const exp = this.quote(experimentId);
|
|
422
|
+
const filters = [`ns = ${ns}`, `subject = ${exp}`];
|
|
423
|
+
if (eventType)
|
|
424
|
+
filters.push(`verb = ${this.quote(eventType)}`);
|
|
425
|
+
if (variantId)
|
|
426
|
+
filters.push(`object = ${this.quote(variantId)}`);
|
|
427
|
+
const sql = `
|
|
428
|
+
SELECT
|
|
429
|
+
verb AS eventType,
|
|
430
|
+
toString(created_at) AS createdAt,
|
|
431
|
+
data
|
|
432
|
+
FROM ${this.database}.actions
|
|
433
|
+
WHERE ${filters.join(' AND ')}
|
|
434
|
+
ORDER BY created_at DESC
|
|
435
|
+
LIMIT ${Number(limit)}
|
|
436
|
+
`;
|
|
437
|
+
const rows = await this.provider.analyticsQuery(sql);
|
|
438
|
+
return rows.map((row) => {
|
|
439
|
+
const dataStr = row['data'];
|
|
440
|
+
let payload = {};
|
|
441
|
+
if (typeof dataStr === 'string' && dataStr.length > 0) {
|
|
442
|
+
try {
|
|
443
|
+
payload = JSON.parse(dataStr);
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
payload = {};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else if (dataStr && typeof dataStr === 'object') {
|
|
450
|
+
payload = dataStr;
|
|
451
|
+
}
|
|
452
|
+
const createdAt = String(row['createdAt'] ?? '');
|
|
453
|
+
const ts = createdAt ? new Date(createdAt) : new Date();
|
|
454
|
+
return {
|
|
455
|
+
type: String(row['eventType'] ?? ''),
|
|
456
|
+
timestamp: Number.isNaN(ts.getTime()) ? new Date() : ts,
|
|
457
|
+
data: payload,
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Raw analytical SQL pass-through. Routes to
|
|
463
|
+
* {@link ClickHouseProvider.analyticsQuery}.
|
|
464
|
+
*/
|
|
465
|
+
async query(sql) {
|
|
466
|
+
return this.provider.analyticsQuery(sql);
|
|
467
|
+
}
|
|
468
|
+
/** No-op (kept for backwards compat with the chdb-era API). */
|
|
469
|
+
close() {
|
|
470
|
+
// HTTP fetcher has nothing to release.
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Bootstrap the canonical CH schema (`things`, `actions`, `verbs`).
|
|
475
|
+
* Convenience wrapper around the canonical
|
|
476
|
+
* {@link bootstrapClickHouseSchema}.
|
|
477
|
+
*/
|
|
478
|
+
export async function bootstrapExperimentsSchema(fetcher, options = {}) {
|
|
479
|
+
await bootstrapClickHouseSchema(fetcher, options.database !== undefined ? { database: options.database } : {});
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Create a ClickHouse-backed experiment storage backend.
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```ts
|
|
486
|
+
* import { configureTracking } from 'ai-experiments'
|
|
487
|
+
* import { createClickHouseExperimentStorage } from 'ai-experiments/storage'
|
|
488
|
+
* import { createClickHouseHttpFetcher } from 'ai-database'
|
|
489
|
+
*
|
|
490
|
+
* const fetcher = createClickHouseHttpFetcher({
|
|
491
|
+
* url: process.env.CLICKHOUSE_URL!,
|
|
492
|
+
* username: process.env.CLICKHOUSE_USER,
|
|
493
|
+
* password: process.env.CLICKHOUSE_PASSWORD,
|
|
494
|
+
* database: 'aidb',
|
|
495
|
+
* })
|
|
496
|
+
* const storage = createClickHouseExperimentStorage({ fetcher })
|
|
497
|
+
*
|
|
498
|
+
* configureTracking({ backend: storage })
|
|
499
|
+
*
|
|
500
|
+
* const best = await storage.getBestVariant('my-experiment')
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
export function createClickHouseExperimentStorage(options) {
|
|
504
|
+
return new ClickHouseExperimentStorage(options);
|
|
505
|
+
}
|
|
506
|
+
// =============================================================================
|
|
507
|
+
// Backwards-compat aliases (chdb-era names)
|
|
508
|
+
// =============================================================================
|
|
509
|
+
/**
|
|
510
|
+
* @deprecated Renamed to {@link ClickHouseExperimentStorage}. The class
|
|
511
|
+
* now wraps the canonical `ClickHouseProvider` from `ai-database` instead
|
|
512
|
+
* of embedded chdb. The old `dataPath` constructor option is ignored —
|
|
513
|
+
* supply a `fetcher` instead.
|
|
514
|
+
*/
|
|
515
|
+
export const ChdbStorage = ClickHouseExperimentStorage;
|
|
516
|
+
/**
|
|
517
|
+
* @deprecated Renamed to {@link createClickHouseExperimentStorage}.
|
|
518
|
+
*/
|
|
519
|
+
export function createChdbBackend(options) {
|
|
520
|
+
return createClickHouseExperimentStorage(options);
|
|
521
|
+
}
|
|
522
|
+
//# sourceMappingURL=clickhouse-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickhouse-storage.js","sourceRoot":"","sources":["../src/clickhouse-storage.ts"],"names":[],"mappings":"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"}
|
package/dist/decide.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decide.d.ts","sourceRoot":"","sources":["../src/decide.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAG/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAsB,MAAM,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"decide.d.ts","sourceRoot":"","sources":["../src/decide.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAG/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAsB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CA2CrF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,CAAC,CA0BjF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAiD5B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACrD,OAAO,EAAE,CAAC,EAAE,EACZ,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACjD,CAAC,CA6BH;AAuDD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EACxC,OAAO,EAAE,CAAC,EAAE,EACZ,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EACjD,MAAM,EAAE;IACN,yCAAyC;IACzC,iBAAiB,EAAE,MAAM,CAAA;IACzB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAA;CACnB,GACA,CAAC,CAwCH"}
|
package/dist/decide.js
CHANGED
|
@@ -166,7 +166,11 @@ export async function decideEpsilonGreedy(options) {
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
// Exploitation: best option
|
|
169
|
-
const result = await decide({
|
|
169
|
+
const result = await decide({
|
|
170
|
+
options: choices,
|
|
171
|
+
score,
|
|
172
|
+
...(context !== undefined && { context }),
|
|
173
|
+
});
|
|
170
174
|
track({
|
|
171
175
|
type: 'decision.made',
|
|
172
176
|
timestamp: new Date(),
|
|
@@ -213,7 +217,7 @@ export function decideThompsonSampling(options, priors) {
|
|
|
213
217
|
};
|
|
214
218
|
});
|
|
215
219
|
// Select option with highest sample
|
|
216
|
-
const best = samples.reduce((prev, current) => current.sample > prev.sample ? current : prev);
|
|
220
|
+
const best = samples.reduce((prev, current) => (current.sample > prev.sample ? current : prev));
|
|
217
221
|
track({
|
|
218
222
|
type: 'decision.made',
|
|
219
223
|
timestamp: new Date(),
|
package/dist/decide.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decide.js","sourceRoot":"","sources":["../src/decide.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,
|
|
1
|
+
{"version":3,"file":"decide.js","sourceRoot":"","sources":["../src/decide.ts"],"names":[],"mappings":"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"}
|
package/dist/experiment.js
CHANGED
|
@@ -55,9 +55,9 @@ export async function Experiment(config, options = {}) {
|
|
|
55
55
|
// Parallel execution with optional concurrency limit
|
|
56
56
|
const executeVariant = async (variant) => {
|
|
57
57
|
return runVariant(config, variant, contextData, {
|
|
58
|
-
onVariantStart,
|
|
59
|
-
onVariantComplete,
|
|
60
|
-
onVariantError,
|
|
58
|
+
...(onVariantStart !== undefined && { onVariantStart }),
|
|
59
|
+
...(onVariantComplete !== undefined && { onVariantComplete }),
|
|
60
|
+
...(onVariantError !== undefined && { onVariantError }),
|
|
61
61
|
});
|
|
62
62
|
};
|
|
63
63
|
if (maxConcurrency && maxConcurrency > 0) {
|
|
@@ -82,9 +82,9 @@ export async function Experiment(config, options = {}) {
|
|
|
82
82
|
// Sequential execution
|
|
83
83
|
for (const variant of config.variants) {
|
|
84
84
|
const result = await runVariant(config, variant, contextData, {
|
|
85
|
-
onVariantStart,
|
|
86
|
-
onVariantComplete,
|
|
87
|
-
onVariantError,
|
|
85
|
+
...(onVariantStart !== undefined && { onVariantStart }),
|
|
86
|
+
...(onVariantComplete !== undefined && { onVariantComplete }),
|
|
87
|
+
...(onVariantError !== undefined && { onVariantError }),
|
|
88
88
|
});
|
|
89
89
|
results.push(result);
|
|
90
90
|
// Stop on first error if configured
|
|
@@ -110,7 +110,7 @@ export async function Experiment(config, options = {}) {
|
|
|
110
110
|
experimentId: config.id,
|
|
111
111
|
experimentName: config.name,
|
|
112
112
|
results,
|
|
113
|
-
bestVariant,
|
|
113
|
+
...(bestVariant !== undefined && { bestVariant }),
|
|
114
114
|
totalDuration,
|
|
115
115
|
successCount: results.filter((r) => r.success).length,
|
|
116
116
|
failureCount: results.filter((r) => !r.success).length,
|
|
@@ -144,7 +144,7 @@ async function runVariant(config, variant, contextData, callbacks) {
|
|
|
144
144
|
variantId: variant.id,
|
|
145
145
|
runId,
|
|
146
146
|
startedAt: startTime,
|
|
147
|
-
data: contextData,
|
|
147
|
+
...(contextData !== undefined && { data: contextData }),
|
|
148
148
|
};
|
|
149
149
|
// Track variant start
|
|
150
150
|
track({
|
|
@@ -184,7 +184,7 @@ async function runVariant(config, variant, contextData, callbacks) {
|
|
|
184
184
|
variantName: variant.name,
|
|
185
185
|
runId,
|
|
186
186
|
result,
|
|
187
|
-
metricValue,
|
|
187
|
+
...(metricValue !== undefined && { metricValue }),
|
|
188
188
|
duration,
|
|
189
189
|
startedAt: startTime,
|
|
190
190
|
completedAt: endTime,
|