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.
Files changed (43) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +17 -0
  3. package/README.md +13 -1
  4. package/dist/clickhouse-storage.d.ts +227 -0
  5. package/dist/clickhouse-storage.d.ts.map +1 -0
  6. package/dist/clickhouse-storage.js +522 -0
  7. package/dist/clickhouse-storage.js.map +1 -0
  8. package/dist/decide.d.ts.map +1 -1
  9. package/dist/decide.js +6 -2
  10. package/dist/decide.js.map +1 -1
  11. package/dist/experiment.js +9 -9
  12. package/dist/experiment.js.map +1 -1
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +4 -2
  16. package/dist/index.js.map +1 -1
  17. package/dist/tracking.js +3 -3
  18. package/dist/tracking.js.map +1 -1
  19. package/package.json +16 -16
  20. package/src/clickhouse-storage.ts +698 -0
  21. package/src/decide.ts +8 -10
  22. package/src/experiment.ts +12 -12
  23. package/src/index.ts +13 -3
  24. package/src/tracking.ts +3 -3
  25. package/test/cartesian.test.ts +287 -0
  26. package/test/clickhouse-storage.test.ts +470 -0
  27. package/test/decide.test.ts +414 -0
  28. package/test/experiment.test.ts +473 -0
  29. package/test/tracking.test.ts +347 -0
  30. package/vitest.config.ts +34 -0
  31. package/LICENSE +0 -21
  32. package/dist/chdb-storage.d.ts +0 -135
  33. package/dist/chdb-storage.d.ts.map +0 -1
  34. package/dist/chdb-storage.js +0 -468
  35. package/dist/chdb-storage.js.map +0 -1
  36. package/src/cartesian.js +0 -215
  37. package/src/chdb-storage.js +0 -464
  38. package/src/chdb-storage.ts +0 -567
  39. package/src/decide.js +0 -328
  40. package/src/experiment.js +0 -291
  41. package/src/index.js +0 -20
  42. package/src/tracking.js +0 -309
  43. package/src/types.js +0 -4
@@ -1,4 +1,4 @@
1
1
 
2
- > ai-experiments@2.1.3 build /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-experiments
2
+ > ai-experiments@2.3.0 build /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-experiments
3
3
  > tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # ai-experiments
2
2
 
3
+ ## 2.4.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4d58f5f]
8
+ - ai-functions@2.4.0
9
+ - ai-database@2.4.0
10
+
11
+ ## 2.3.0
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [9e2779a]
16
+ - Updated dependencies [b7c7c57]
17
+ - ai-functions@2.3.0
18
+ - ai-database@2.3.0
19
+
3
20
  ## 2.1.3
4
21
 
5
22
  ### Patch Changes
package/README.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # ai-experiments
2
2
 
3
- AI-powered experimentation primitives for testing and evaluating models.
3
+ ![Stability: Experimental](https://img.shields.io/badge/stability-experimental-red)
4
+
5
+ Runtime A/B testing for AI variants in production. Traffic splitting, multi-armed bandits, parameter sweeps, and ClickHouse-backed outcome tracking via the canonical `ai-database` adapter.
6
+
7
+ ## Lifecycle
8
+
9
+ **Runtime (production).** `ai-experiments` is for splitting live traffic across model/prompt/parameter variants and measuring outcomes against real users. Reach for it when you need to learn which variant wins under production conditions — not in a test suite.
10
+
11
+ ## Not for
12
+
13
+ - **Unit testing.** If you want to assert that a specific input produces a specific output during development or CI, use [`ai-tests`](../ai-tests) plus `vitest`. `ai-experiments` exists to *measure* outcomes across users, not to *assert* them against fixtures.
14
+ - **Sandboxing untrusted code.** That's [`ai-evaluate`](../ai-evaluate)'s job.
15
+ - **Offline-only experiments.** While `Experiment()` works offline for parameter sweeps, the package's value comes from production tracking — if you're never deploying the variants, a plain `for` loop is simpler.
4
16
 
5
17
  ## Overview
6
18
 
@@ -0,0 +1,227 @@
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 { type ClickHouseHttpFetcher, type ClickHouseProvider } from 'ai-database';
35
+ import type { TrackingBackend, TrackingEvent, ExperimentResult } from './types.js';
36
+ /** Options for {@link ClickHouseExperimentStorage}. */
37
+ export interface ClickHouseExperimentStorageOptions {
38
+ /** HTTP fetcher (typically constructed via `createClickHouseHttpFetcher`). */
39
+ fetcher: ClickHouseHttpFetcher;
40
+ /**
41
+ * ClickHouse database. Must already exist (or call
42
+ * {@link bootstrapExperimentsSchema}).
43
+ *
44
+ * @default 'aidb'
45
+ */
46
+ database?: string;
47
+ /**
48
+ * Namespace partition for experiment events.
49
+ *
50
+ * @default 'experiments'
51
+ */
52
+ namespace?: string;
53
+ /**
54
+ * Whether to bootstrap the canonical schema (`things`/`actions`/`verbs`)
55
+ * before the first write.
56
+ *
57
+ * @default false
58
+ */
59
+ autoBootstrap?: boolean;
60
+ }
61
+ /**
62
+ * Backwards-compat alias for the prior `ChdbStorageOptions`. The
63
+ * `dataPath` and `autoInit` fields are retained as no-ops to ease the
64
+ * upgrade — they are ignored in favor of HTTP fetcher options.
65
+ */
66
+ export interface ChdbStorageOptions extends Partial<ClickHouseExperimentStorageOptions> {
67
+ /** @deprecated Embedded chdb is no longer used; supply `fetcher`. */
68
+ dataPath?: string;
69
+ /** @deprecated Replaced by `autoBootstrap`. */
70
+ autoInit?: boolean;
71
+ }
72
+ /**
73
+ * ClickHouse-backed experiment storage built on the canonical
74
+ * {@link ClickHouseProvider} adapter. Implements
75
+ * {@link TrackingBackend}.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import { configureTracking } from 'ai-experiments'
80
+ * import { createClickHouseExperimentStorage } from 'ai-experiments/storage'
81
+ * import { createClickHouseHttpFetcher } from 'ai-database'
82
+ *
83
+ * const fetcher = createClickHouseHttpFetcher({
84
+ * url: process.env.CLICKHOUSE_URL!,
85
+ * username: process.env.CLICKHOUSE_USER,
86
+ * password: process.env.CLICKHOUSE_PASSWORD,
87
+ * database: 'aidb',
88
+ * })
89
+ * const storage = createClickHouseExperimentStorage({ fetcher })
90
+ *
91
+ * configureTracking({ backend: storage })
92
+ *
93
+ * // Later: analyse results
94
+ * const best = await storage.getBestVariant('my-experiment')
95
+ * ```
96
+ */
97
+ export declare class ClickHouseExperimentStorage implements TrackingBackend {
98
+ private readonly provider;
99
+ private readonly fetcher;
100
+ private readonly database;
101
+ private readonly namespace;
102
+ private readonly autoBootstrap;
103
+ private bootstrapped;
104
+ private readonly nextEventId;
105
+ constructor(options: ClickHouseExperimentStorageOptions);
106
+ /** Underlying canonical CH provider (escape hatch for advanced callers). */
107
+ getProvider(): ClickHouseProvider;
108
+ private ensureBootstrap;
109
+ track(event: TrackingEvent): Promise<void>;
110
+ flush(): Promise<void>;
111
+ storeResult(result: ExperimentResult): Promise<void>;
112
+ /**
113
+ * Bulk-store results via {@link ClickHouseProvider.commitBatch}. One
114
+ * HTTP POST per call carries the entire batch as `JSONEachRow`.
115
+ */
116
+ storeResults(results: ExperimentResult[]): Promise<void>;
117
+ /** Quote a SQL string literal — same shape as the canonical adapter. */
118
+ private quote;
119
+ /**
120
+ * Get all experiments seen in the actions table (variant.complete events).
121
+ */
122
+ getExperiments(): Promise<Array<{
123
+ experimentId: string;
124
+ experimentName: string;
125
+ variantCount: number;
126
+ runCount: number;
127
+ firstRun: string;
128
+ lastRun: string;
129
+ }>>;
130
+ getVariantStats(experimentId: string): Promise<Array<{
131
+ variantId: string;
132
+ variantName: string;
133
+ runCount: number;
134
+ successCount: number;
135
+ successRate: number;
136
+ avgDuration: number;
137
+ avgMetric: number;
138
+ minMetric: number;
139
+ maxMetric: number;
140
+ }>>;
141
+ getBestVariant(experimentId: string, options?: {
142
+ metric?: 'avgMetric' | 'successRate' | 'avgDuration';
143
+ minimumRuns?: number;
144
+ }): Promise<{
145
+ variantId: string;
146
+ variantName: string;
147
+ metricValue: number;
148
+ runCount: number;
149
+ } | null>;
150
+ getCartesianAnalysis(experimentId: string, dimension: string): Promise<Array<{
151
+ dimensionValue: string;
152
+ runCount: number;
153
+ avgMetric: number;
154
+ successRate: number;
155
+ }>>;
156
+ getCartesianGrid(experimentId: string, dimensions: string[]): Promise<Array<{
157
+ dimensions: Record<string, string>;
158
+ runCount: number;
159
+ avgMetric: number;
160
+ successRate: number;
161
+ }>>;
162
+ getTimeSeries(experimentId: string, options?: {
163
+ interval?: 'hour' | 'day' | 'week';
164
+ variantId?: string;
165
+ }): Promise<Array<{
166
+ period: string;
167
+ runCount: number;
168
+ avgMetric: number;
169
+ successRate: number;
170
+ }>>;
171
+ getEvents(experimentId: string, options?: {
172
+ eventType?: string;
173
+ variantId?: string;
174
+ limit?: number;
175
+ }): Promise<TrackingEvent[]>;
176
+ /**
177
+ * Raw analytical SQL pass-through. Routes to
178
+ * {@link ClickHouseProvider.analyticsQuery}.
179
+ */
180
+ query(sql: string): Promise<Array<Record<string, unknown>>>;
181
+ /** No-op (kept for backwards compat with the chdb-era API). */
182
+ close(): void;
183
+ }
184
+ /**
185
+ * Bootstrap the canonical CH schema (`things`, `actions`, `verbs`).
186
+ * Convenience wrapper around the canonical
187
+ * {@link bootstrapClickHouseSchema}.
188
+ */
189
+ export declare function bootstrapExperimentsSchema(fetcher: ClickHouseHttpFetcher, options?: {
190
+ database?: string;
191
+ }): Promise<void>;
192
+ /**
193
+ * Create a ClickHouse-backed experiment storage backend.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * import { configureTracking } from 'ai-experiments'
198
+ * import { createClickHouseExperimentStorage } from 'ai-experiments/storage'
199
+ * import { createClickHouseHttpFetcher } from 'ai-database'
200
+ *
201
+ * const fetcher = createClickHouseHttpFetcher({
202
+ * url: process.env.CLICKHOUSE_URL!,
203
+ * username: process.env.CLICKHOUSE_USER,
204
+ * password: process.env.CLICKHOUSE_PASSWORD,
205
+ * database: 'aidb',
206
+ * })
207
+ * const storage = createClickHouseExperimentStorage({ fetcher })
208
+ *
209
+ * configureTracking({ backend: storage })
210
+ *
211
+ * const best = await storage.getBestVariant('my-experiment')
212
+ * ```
213
+ */
214
+ export declare function createClickHouseExperimentStorage(options: ClickHouseExperimentStorageOptions): ClickHouseExperimentStorage;
215
+ /**
216
+ * @deprecated Renamed to {@link ClickHouseExperimentStorage}. The class
217
+ * now wraps the canonical `ClickHouseProvider` from `ai-database` instead
218
+ * of embedded chdb. The old `dataPath` constructor option is ignored —
219
+ * supply a `fetcher` instead.
220
+ */
221
+ export declare const ChdbStorage: typeof ClickHouseExperimentStorage;
222
+ export type ChdbStorage = ClickHouseExperimentStorage;
223
+ /**
224
+ * @deprecated Renamed to {@link createClickHouseExperimentStorage}.
225
+ */
226
+ export declare function createChdbBackend(options: ClickHouseExperimentStorageOptions): ClickHouseExperimentStorage;
227
+ //# sourceMappingURL=clickhouse-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickhouse-storage.d.ts","sourceRoot":"","sources":["../src/clickhouse-storage.ts"],"names":[],"mappings":"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"}