ai-experiments 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +13 -1
  3. package/package.json +7 -7
  4. package/src/clickhouse-storage.ts +698 -0
  5. package/src/decide.ts +8 -10
  6. package/src/experiment.ts +12 -12
  7. package/src/index.ts +13 -3
  8. package/src/tracking.ts +3 -3
  9. package/test/cartesian.test.ts +287 -0
  10. package/test/clickhouse-storage.test.ts +470 -0
  11. package/test/decide.test.ts +414 -0
  12. package/test/experiment.test.ts +473 -0
  13. package/test/tracking.test.ts +347 -0
  14. package/vitest.config.ts +34 -0
  15. package/.turbo/turbo-build.log +0 -5
  16. package/dist/cartesian.d.ts +0 -140
  17. package/dist/cartesian.d.ts.map +0 -1
  18. package/dist/cartesian.js +0 -216
  19. package/dist/cartesian.js.map +0 -1
  20. package/dist/decide.d.ts +0 -152
  21. package/dist/decide.d.ts.map +0 -1
  22. package/dist/decide.js +0 -329
  23. package/dist/decide.js.map +0 -1
  24. package/dist/experiment.d.ts +0 -53
  25. package/dist/experiment.d.ts.map +0 -1
  26. package/dist/experiment.js +0 -292
  27. package/dist/experiment.js.map +0 -1
  28. package/dist/index.d.ts +0 -14
  29. package/dist/index.d.ts.map +0 -1
  30. package/dist/index.js +0 -19
  31. package/dist/index.js.map +0 -1
  32. package/dist/tracking.d.ts +0 -159
  33. package/dist/tracking.d.ts.map +0 -1
  34. package/dist/tracking.js +0 -310
  35. package/dist/tracking.js.map +0 -1
  36. package/dist/types.d.ts +0 -198
  37. package/dist/types.d.ts.map +0 -1
  38. package/dist/types.js +0 -5
  39. package/dist/types.js.map +0 -1
  40. package/src/cartesian.js +0 -215
  41. package/src/chdb-storage.js +0 -464
  42. package/src/chdb-storage.ts +0 -565
  43. package/src/decide.js +0 -328
  44. package/src/experiment.js +0 -291
  45. package/src/index.js +0 -20
  46. package/src/tracking.js +0 -309
  47. package/src/types.js +0 -4
package/dist/types.d.ts DELETED
@@ -1,198 +0,0 @@
1
- /**
2
- * Core types for AI experimentation
3
- */
4
- /**
5
- * A variant within an experiment
6
- */
7
- export interface ExperimentVariant<TConfig = unknown> {
8
- /** Unique identifier for the variant */
9
- id: string;
10
- /** Human-readable name */
11
- name: string;
12
- /** Variant configuration */
13
- config: TConfig;
14
- /** Weight for weighted random selection (default: 1) */
15
- weight?: number;
16
- /** Optional description */
17
- description?: string;
18
- }
19
- /**
20
- * Configuration for an experiment
21
- */
22
- export interface ExperimentConfig<TConfig = unknown, TResult = unknown> {
23
- /** Unique experiment identifier */
24
- id: string;
25
- /** Human-readable name */
26
- name: string;
27
- /** Experiment description */
28
- description?: string;
29
- /** List of variants to test */
30
- variants: ExperimentVariant<TConfig>[];
31
- /** Function to execute for each variant */
32
- execute: (config: TConfig, context?: ExperimentContext) => Promise<TResult> | TResult;
33
- /** Optional success metric function */
34
- metric?: (result: TResult) => number | Promise<number>;
35
- /** Metadata for the experiment */
36
- metadata?: Record<string, unknown>;
37
- }
38
- /**
39
- * Context passed to experiment execution
40
- */
41
- export interface ExperimentContext {
42
- /** Experiment ID */
43
- experimentId: string;
44
- /** Variant ID */
45
- variantId: string;
46
- /** Run ID (unique per execution) */
47
- runId: string;
48
- /** Timestamp when execution started */
49
- startedAt: Date;
50
- /** Additional context data */
51
- data?: Record<string, unknown>;
52
- }
53
- /**
54
- * Result of executing an experiment variant
55
- */
56
- export interface ExperimentResult<TResult = unknown> {
57
- /** Experiment ID */
58
- experimentId: string;
59
- /** Variant ID */
60
- variantId: string;
61
- /** Variant name */
62
- variantName: string;
63
- /** Run ID */
64
- runId: string;
65
- /** Execution result */
66
- result: TResult;
67
- /** Computed metric value (if metric function provided) */
68
- metricValue?: number;
69
- /** Execution duration in milliseconds */
70
- duration: number;
71
- /** Timestamp when execution started */
72
- startedAt: Date;
73
- /** Timestamp when execution completed */
74
- completedAt: Date;
75
- /** Error if execution failed */
76
- error?: Error;
77
- /** Success flag */
78
- success: boolean;
79
- /** Additional metadata */
80
- metadata?: Record<string, unknown>;
81
- }
82
- /**
83
- * Summary of experiment results across all variants
84
- */
85
- export interface ExperimentSummary<TResult = unknown> {
86
- /** Experiment ID */
87
- experimentId: string;
88
- /** Experiment name */
89
- experimentName: string;
90
- /** Results for each variant */
91
- results: ExperimentResult<TResult>[];
92
- /** Best performing variant (by metric) */
93
- bestVariant?: {
94
- variantId: string;
95
- variantName: string;
96
- metricValue: number;
97
- };
98
- /** Total execution duration */
99
- totalDuration: number;
100
- /** Number of successful runs */
101
- successCount: number;
102
- /** Number of failed runs */
103
- failureCount: number;
104
- /** Timestamp when experiment started */
105
- startedAt: Date;
106
- /** Timestamp when experiment completed */
107
- completedAt: Date;
108
- }
109
- /**
110
- * Options for running an experiment
111
- */
112
- export interface RunExperimentOptions {
113
- /** Run variants in parallel (default: true) */
114
- parallel?: boolean;
115
- /** Maximum concurrent executions (default: unlimited) */
116
- maxConcurrency?: number;
117
- /** Stop on first error (default: false) */
118
- stopOnError?: boolean;
119
- /** Custom context data */
120
- context?: Record<string, unknown>;
121
- /** Event callbacks */
122
- onVariantStart?: (variantId: string, variantName: string) => void;
123
- onVariantComplete?: (result: ExperimentResult) => void;
124
- onVariantError?: (variantId: string, error: Error) => void;
125
- }
126
- /**
127
- * Parameters for cartesian product generation
128
- */
129
- export type CartesianParams = Record<string, unknown[]>;
130
- /**
131
- * Result of cartesian product - array of parameter combinations
132
- */
133
- export type CartesianResult<T extends CartesianParams> = Array<{
134
- [K in keyof T]: T[K][number];
135
- }>;
136
- /**
137
- * Decision options
138
- */
139
- export interface DecideOptions<T> {
140
- /** Options to choose from */
141
- options: T[];
142
- /** Scoring function for each option */
143
- score: (option: T) => number | Promise<number>;
144
- /** Context or prompt for decision making */
145
- context?: string;
146
- /** Whether to return all options sorted by score (default: false) */
147
- returnAll?: boolean;
148
- }
149
- /**
150
- * Result of a decision
151
- */
152
- export interface DecisionResult<T> {
153
- /** The selected option */
154
- selected: T;
155
- /** Score of the selected option */
156
- score: number;
157
- /** All options with their scores (if returnAll was true) */
158
- allOptions?: Array<{
159
- option: T;
160
- score: number;
161
- }>;
162
- }
163
- /**
164
- * Tracking event types
165
- */
166
- export type TrackingEventType = 'experiment.start' | 'experiment.complete' | 'variant.start' | 'variant.complete' | 'variant.error' | 'metric.computed' | 'decision.made';
167
- /**
168
- * Tracking event
169
- */
170
- export interface TrackingEvent {
171
- /** Event type */
172
- type: TrackingEventType;
173
- /** Timestamp */
174
- timestamp: Date;
175
- /** Event data */
176
- data: Record<string, unknown>;
177
- }
178
- /**
179
- * Tracking backend interface
180
- */
181
- export interface TrackingBackend {
182
- /** Track an event */
183
- track(event: TrackingEvent): void | Promise<void>;
184
- /** Flush pending events */
185
- flush?(): void | Promise<void>;
186
- }
187
- /**
188
- * Options for tracking configuration
189
- */
190
- export interface TrackingOptions {
191
- /** Custom tracking backend */
192
- backend?: TrackingBackend;
193
- /** Whether tracking is enabled (default: true) */
194
- enabled?: boolean;
195
- /** Additional metadata to include with all events */
196
- metadata?: Record<string, unknown>;
197
- }
198
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js DELETED
@@ -1,5 +0,0 @@
1
- /**
2
- * Core types for AI experimentation
3
- */
4
- export {};
5
- //# sourceMappingURL=types.js.map
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/src/cartesian.js DELETED
@@ -1,215 +0,0 @@
1
- /**
2
- * Cartesian product utilities for parameter exploration
3
- */
4
- /**
5
- * Generate cartesian product of parameter sets
6
- *
7
- * Takes an object where each key maps to an array of possible values,
8
- * and returns all possible combinations as an array of objects.
9
- *
10
- * @example
11
- * ```ts
12
- * import { cartesian } from 'ai-experiments'
13
- *
14
- * const combinations = cartesian({
15
- * model: ['sonnet', 'opus', 'gpt-4o'],
16
- * temperature: [0.3, 0.7, 1.0],
17
- * maxTokens: [100, 500, 1000],
18
- * })
19
- *
20
- * // Returns 27 combinations (3 * 3 * 3):
21
- * // [
22
- * // { model: 'sonnet', temperature: 0.3, maxTokens: 100 },
23
- * // { model: 'sonnet', temperature: 0.3, maxTokens: 500 },
24
- * // { model: 'sonnet', temperature: 0.3, maxTokens: 1000 },
25
- * // { model: 'sonnet', temperature: 0.7, maxTokens: 100 },
26
- * // ...
27
- * // ]
28
- *
29
- * // Use with experiments:
30
- * const variants = combinations.map((config, i) => ({
31
- * id: `variant-${i}`,
32
- * name: `${config.model} T=${config.temperature} max=${config.maxTokens}`,
33
- * config,
34
- * }))
35
- * ```
36
- */
37
- export function cartesian(params) {
38
- const keys = Object.keys(params);
39
- const values = keys.map((k) => params[k]);
40
- // Handle empty input
41
- if (keys.length === 0) {
42
- return [];
43
- }
44
- // Handle single parameter
45
- if (keys.length === 1) {
46
- const key = keys[0];
47
- return values[0].map((val) => ({ [key]: val }));
48
- }
49
- // Generate all combinations using recursive helper
50
- const combinations = cartesianProduct(values);
51
- // Map back to objects
52
- return combinations.map((combo) => {
53
- const obj = {};
54
- keys.forEach((key, i) => {
55
- obj[key] = combo[i];
56
- });
57
- return obj;
58
- });
59
- }
60
- /**
61
- * Recursive cartesian product implementation
62
- */
63
- function cartesianProduct(arrays) {
64
- if (arrays.length === 0)
65
- return [[]];
66
- const [first, ...rest] = arrays;
67
- // Base case: single array
68
- if (rest.length === 0) {
69
- return first.map((x) => [x]);
70
- }
71
- // Recursive case
72
- const restProduct = cartesianProduct(rest);
73
- const result = [];
74
- for (const x of first) {
75
- for (const restCombo of restProduct) {
76
- result.push([x, ...restCombo]);
77
- }
78
- }
79
- return result;
80
- }
81
- /**
82
- * Generate a grid of parameter combinations with filtering
83
- *
84
- * Similar to cartesian(), but allows filtering out invalid combinations.
85
- *
86
- * @example
87
- * ```ts
88
- * import { cartesianFilter } from 'ai-experiments'
89
- *
90
- * const combinations = cartesianFilter(
91
- * {
92
- * model: ['sonnet', 'opus'],
93
- * temperature: [0.3, 0.7, 1.0],
94
- * maxTokens: [100, 500],
95
- * },
96
- * // Filter out combinations where opus uses high temperature
97
- * (combo) => !(combo.model === 'opus' && combo.temperature > 0.7)
98
- * )
99
- * ```
100
- */
101
- export function cartesianFilter(params, filter) {
102
- const allCombinations = cartesian(params);
103
- return allCombinations.filter(filter);
104
- }
105
- /**
106
- * Generate a random sample from the cartesian product
107
- *
108
- * Useful when the full cartesian product is too large to test all combinations.
109
- *
110
- * @example
111
- * ```ts
112
- * import { cartesianSample } from 'ai-experiments'
113
- *
114
- * // Full product would be 1000 combinations (10 * 10 * 10)
115
- * // Sample just 20 random combinations
116
- * const sample = cartesianSample(
117
- * {
118
- * param1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
119
- * param2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
120
- * param3: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
121
- * },
122
- * 20
123
- * )
124
- * ```
125
- */
126
- export function cartesianSample(params, sampleSize, options = {}) {
127
- const { unique = true } = options;
128
- // Generate all combinations first
129
- const allCombinations = cartesian(params);
130
- // If sample size is larger than available combinations, return all
131
- if (sampleSize >= allCombinations.length) {
132
- return allCombinations;
133
- }
134
- // Shuffle and take first n items
135
- const shuffled = [...allCombinations];
136
- // Simple Fisher-Yates shuffle
137
- for (let i = shuffled.length - 1; i > 0; i--) {
138
- const j = Math.floor(Math.random() * (i + 1));
139
- [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
140
- }
141
- return shuffled.slice(0, sampleSize);
142
- }
143
- /**
144
- * Count the total number of combinations without generating them
145
- *
146
- * Useful for checking if cartesian product is feasible before generating.
147
- *
148
- * @example
149
- * ```ts
150
- * import { cartesianCount } from 'ai-experiments'
151
- *
152
- * const count = cartesianCount({
153
- * model: ['sonnet', 'opus', 'gpt-4o'],
154
- * temperature: [0.3, 0.5, 0.7, 0.9],
155
- * maxTokens: [100, 500, 1000, 2000],
156
- * })
157
- * // Returns 48 (3 * 4 * 4)
158
- *
159
- * if (count > 100) {
160
- * console.log('Too many combinations, use cartesianSample instead')
161
- * }
162
- * ```
163
- */
164
- export function cartesianCount(params) {
165
- const keys = Object.keys(params);
166
- if (keys.length === 0)
167
- return 0;
168
- return keys.reduce((total, key) => {
169
- const arr = params[key];
170
- return total * (arr?.length ?? 0);
171
- }, 1);
172
- }
173
- /**
174
- * Generate cartesian product with labels for each dimension
175
- *
176
- * Returns combinations with additional metadata about which dimension each value came from.
177
- *
178
- * @example
179
- * ```ts
180
- * import { cartesianWithLabels } from 'ai-experiments'
181
- *
182
- * const labeled = cartesianWithLabels({
183
- * model: ['sonnet', 'opus'],
184
- * temperature: [0.3, 0.7],
185
- * })
186
- * // [
187
- * // { values: { model: 'sonnet', temperature: 0.3 }, labels: { model: 0, temperature: 0 } },
188
- * // { values: { model: 'sonnet', temperature: 0.7 }, labels: { model: 0, temperature: 1 } },
189
- * // { values: { model: 'opus', temperature: 0.3 }, labels: { model: 1, temperature: 0 } },
190
- * // { values: { model: 'opus', temperature: 0.7 }, labels: { model: 1, temperature: 1 } },
191
- * // ]
192
- * ```
193
- */
194
- export function cartesianWithLabels(params) {
195
- const keys = Object.keys(params);
196
- const values = keys.map((k) => params[k]);
197
- if (keys.length === 0) {
198
- return [];
199
- }
200
- const combinations = cartesianProduct(values);
201
- return combinations.map((combo) => {
202
- const valuesObj = {};
203
- const labelsObj = {};
204
- keys.forEach((key, i) => {
205
- const value = combo[i];
206
- valuesObj[key] = value;
207
- const arr = params[key];
208
- labelsObj[key] = arr ? arr.indexOf(value) : -1;
209
- });
210
- return {
211
- values: valuesObj,
212
- labels: labelsObj,
213
- };
214
- });
215
- }