Skip to content

Commit 4700c7e

Browse files
authored
fix: add GEPA feedback type hooks to AxCompileOptions (#376)
* feat: type GEPA compile options feedback hooks * chore: biome format
1 parent da5bb12 commit 4700c7e

File tree

7 files changed

+46
-22
lines changed

7 files changed

+46
-22
lines changed

docs/OPTIMIZE.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -841,13 +841,21 @@ const result = await optimizer.compile(
841841
multiMetric as any,
842842
{
843843
validationExamples: val,
844+
feedbackExamples: val,
845+
feedbackFn: ({ prediction, example }) =>
846+
prediction?.isSafe === example?.isSafe
847+
? '✅ Matched label'
848+
: [
849+
`Expected: ${example?.isSafe ?? 'unknown'}`,
850+
`Received: ${prediction?.isSafe ?? 'unknown'}`,
851+
],
844852
// Required to bound evaluation cost
845853
maxMetricCalls: 200,
846854
// Optional: provide a tie-break scalarizer for selection logic
847855
// paretoMetricKey: 'accuracy',
848856
// or
849857
// paretoScalarize: (s) => 0.7*s.accuracy + 0.3*s.brevity,
850-
} as any
858+
}
851859
);
852860

853861
console.log(`✅ Found ${result.paretoFrontSize} Pareto points`);
@@ -904,6 +912,10 @@ if (result.optimizedProgram) {
904912
}
905913
```
906914

915+
> 💡 **Feedback hook**: `feedbackFn` lets you surface rich guidance for each evaluation, whether it's a short string or multiple
916+
> bullet points. The hook receives the raw `prediction` and original `example`, making it easy to emit reviewer-style comments
917+
> alongside scores. Pair it with `feedbackExamples` to keep cost-efficient review sets separate from validation metrics.
918+
907919
#### GEPA-Flow (Multi-Module)
908920

909921
```typescript
@@ -917,7 +929,7 @@ const pipeline = flow<{ emailText: string }>()
917929
.m((s) => ({ priority: s.classifierResult.priority, rationale: s.rationaleResult.rationale }));
918930

919931
const optimizer = new AxGEPAFlow({ studentAI: ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY!, config: { model: 'gpt-4o-mini' } }), numTrials: 16 });
920-
const result = await optimizer.compile(pipeline as any, train, multiMetric as any, { validationExamples: val, maxMetricCalls: 240 } as any);
932+
const result = await optimizer.compile(pipeline as any, train, multiMetric as any, { validationExamples: val, maxMetricCalls: 240 });
921933
console.log(`Front size: ${result.paretoFrontSize}, Hypervolume: ${result.hypervolume}`);
922934
```
923935

@@ -976,7 +988,7 @@ const multiMetric = ({ prediction, example }) => ({
976988
#### Understanding the Results
977989

978990
```typescript
979-
const result = await optimizer.compile(program, examples, multiMetric, { maxMetricCalls: 200 } as any);
991+
const result = await optimizer.compile(program, examples, multiMetric, { maxMetricCalls: 200 });
980992

981993
// Key properties of AxParetoResult:
982994
console.log(`Pareto frontier size: ${result.paretoFrontSize}`);

src/ax/dsp/common_types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ export interface AxCompileOptions {
176176
saveCheckpointOnComplete?: boolean;
177177
// GEPA core options (adapter-based)
178178
gepaAdapter?: AxGEPAAdapter<any, any, any>;
179+
validationExamples?: readonly AxTypedExample<any>[];
180+
feedbackExamples?: readonly AxTypedExample<any>[];
181+
feedbackFn?: (
182+
args: Readonly<{ prediction: unknown; example: AxExample }>
183+
) => string | string[] | undefined;
179184
skipPerfectScore?: boolean;
180185
perfectScore?: number;
181186
maxMetricCalls?: number;

src/docs/src/content/docs/optimize.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -846,13 +846,21 @@ const result = await optimizer.compile(
846846
multiMetric as any,
847847
{
848848
validationExamples: val,
849+
feedbackExamples: val,
850+
feedbackFn: ({ prediction, example }) =>
851+
prediction?.isSafe === example?.isSafe
852+
? '✅ Matched label'
853+
: [
854+
`Expected: ${example?.isSafe ?? 'unknown'}`,
855+
`Received: ${prediction?.isSafe ?? 'unknown'}`,
856+
],
849857
// Required to bound evaluation cost
850858
maxMetricCalls: 200,
851859
// Optional: provide a tie-break scalarizer for selection logic
852860
// paretoMetricKey: 'accuracy',
853861
// or
854862
// paretoScalarize: (s) => 0.7*s.accuracy + 0.3*s.brevity,
855-
} as any
863+
}
856864
);
857865

858866
console.log(`✅ Found ${result.paretoFrontSize} Pareto points`);
@@ -909,6 +917,10 @@ if (result.optimizedProgram) {
909917
}
910918
```
911919

920+
> 💡 **Feedback hook**: `feedbackFn` lets you surface rich guidance for each evaluation, whether it's a short string or multiple
921+
> bullet points. The hook receives the raw `prediction` and original `example`, making it easy to emit reviewer-style comments
922+
> alongside scores. Pair it with `feedbackExamples` to keep cost-efficient review sets separate from validation metrics.
923+
912924
#### GEPA-Flow (Multi-Module)
913925

914926
```typescript
@@ -922,7 +934,7 @@ const pipeline = flow<{ emailText: string }>()
922934
.m((s) => ({ priority: s.classifierResult.priority, rationale: s.rationaleResult.rationale }));
923935

924936
const optimizer = new AxGEPAFlow({ studentAI: ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY!, config: { model: 'gpt-4o-mini' } }), numTrials: 16 });
925-
const result = await optimizer.compile(pipeline as any, train, multiMetric as any, { validationExamples: val, maxMetricCalls: 240 } as any);
937+
const result = await optimizer.compile(pipeline as any, train, multiMetric as any, { validationExamples: val, maxMetricCalls: 240 });
926938
console.log(`Front size: ${result.paretoFrontSize}, Hypervolume: ${result.hypervolume}`);
927939
```
928940

@@ -981,7 +993,7 @@ const multiMetric = ({ prediction, example }) => ({
981993
#### Understanding the Results
982994

983995
```typescript
984-
const result = await optimizer.compile(program, examples, multiMetric, { maxMetricCalls: 200 } as any);
996+
const result = await optimizer.compile(program, examples, multiMetric, { maxMetricCalls: 200 });
985997

986998
// Key properties of AxParetoResult:
987999
console.log(`Pareto frontier size: ${result.paretoFrontSize}`);

src/examples/gepa-flow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ async function main() {
101101
verbose: true,
102102
validationExamples: val,
103103
maxMetricCalls: 240,
104-
} as any
104+
}
105105
);
106106

107107
console.log('\n✅ Pareto optimization complete');

src/examples/gepa-quality-vs-speed-optimization.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ async function demonstrateGEPAOptimization() {
324324
auto: 'medium',
325325
verbose: true,
326326
maxMetricCalls: 150,
327-
} as any
327+
}
328328
);
329329

330330
console.log('\n✅ GEPA optimization completed!');

src/examples/gepa-train-inference.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,14 @@ async function main() {
9292
});
9393

9494
console.log('🔧 Running GEPA Pareto optimization (accuracy + brevity)...');
95-
const result = await optimizer.compile(
96-
program as any,
97-
train,
98-
metric as any,
99-
{
100-
auto: 'medium',
101-
verbose: true,
102-
validationExamples: val,
103-
maxMetricCalls: 200, // required to bound evaluation cost
104-
// Optionally guide scalarization with a specific metric key
105-
// paretoMetricKey: 'accuracy',
106-
} as any
107-
);
95+
const result = await optimizer.compile(program as any, train, metric as any, {
96+
auto: 'medium',
97+
verbose: true,
98+
validationExamples: val,
99+
maxMetricCalls: 200, // required to bound evaluation cost
100+
// Optionally guide scalarization with a specific metric key
101+
// paretoMetricKey: 'accuracy',
102+
});
108103

109104
console.log(`\n✅ Pareto optimization complete`);
110105
console.log(`Front size: ${result.paretoFrontSize}`);

src/examples/gepa.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async function main() {
8888
verbose: true,
8989
validationExamples: val,
9090
maxMetricCalls: 200,
91-
} as any
91+
}
9292
);
9393

9494
console.log('\n✅ Pareto optimization complete');

0 commit comments

Comments
 (0)