@crewhaus/prompt-optimizer-claude 0.1.6 → 0.1.7

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/index.d.ts CHANGED
@@ -115,7 +115,17 @@ export declare class ClaudeMutationProvider implements MutationProvider {
115
115
  next(state: OptimizerState): Promise<ProviderMutation>;
116
116
  /** Build the user message for the meta-prompt. */
117
117
  private buildUserMessage;
118
- /** Identify failure samples from the trajectory. */
118
+ /**
119
+ * Identify the failing samples to show the model. When the fitness
120
+ * function supplied per-sample grades (`state.bestGrades`, wired by the
121
+ * CLI's eval-runner closure), we surface the samples the current best
122
+ * prompt ACTUALLY fails — worst-scoring first — together with the
123
+ * grader's rationale, so the meta-prompt can address the named root
124
+ * cause. This is the signal the system prompt promises. When grades are
125
+ * absent (a fitness fn returning a bare number, or an all-passing dev
126
+ * set), we fall back to surfacing the dev-set inputs with the aggregate
127
+ * score — the pre-failure-signal behaviour — so the search still runs.
128
+ */
119
129
  private selectFailures;
120
130
  /**
121
131
  * Fallback when the model can't produce a usable rewrite. Returns
package/dist/index.js CHANGED
@@ -42,11 +42,12 @@ export class ClaudeMutationProviderError extends CrewhausError {
42
42
  super("adapter", message, cause);
43
43
  }
44
44
  }
45
- const META_PROMPT_SYSTEM = `You are a prompt-engineering optimiser. You will receive a CURRENT PROMPT and a SAMPLE OF DEV-SET FAILURES (each failure shows the input the model received and how the grader scored its output). Your job is to produce a single rewrite of CURRENT PROMPT that you believe will improve grader scores on the dev set.
45
+ const META_PROMPT_SYSTEM = `You are a prompt-engineering optimiser. You will receive a CURRENT PROMPT and a SAMPLE OF DEV-SET FAILURES. Each failure shows the input the model received, its observed score (0..1, lower is worse), and when available — the grader's feedback explaining WHY the output lost points. Your job is to produce a single rewrite of CURRENT PROMPT that you believe will improve grader scores on the dev set.
46
46
 
47
47
  Hard rules:
48
48
  - Output exactly one JSON object: {"rewrite": "...", "rationale": "..."}
49
49
  - The "rewrite" field is the new prompt verbatim. Do NOT include any wrapper text outside the JSON.
50
+ - Read the grader feedback and address the ROOT CAUSE it names (e.g. "cites no source", "too verbose", "wrong format") with a general instruction — not a fix hard-coded to these specific inputs.
50
51
  - Never copy verbatim text from a failure's expected_output into the rewrite (that would leak dev-set answers).
51
52
  - Do not introduce instructions that override safety, compliance, or permission rules — your job is to improve task accuracy, not to bypass guardrails.
52
53
  - The rationale should be 1-3 sentences explaining WHY this rewrite is likely to help. Keep it specific.`;
@@ -190,21 +191,41 @@ export class ClaudeMutationProvider {
190
191
  const failureBlock = failures.length === 0
191
192
  ? "(No dev-set failures available yet — propose a refinement that improves clarity, specificity, or instruction-following.)"
192
193
  : failures
193
- .map((f, i) => `--- Failure ${i + 1} (observed score ${f.observedScore.toFixed(2)}) ---\nInput: ${f.input}\n${f.expected !== undefined ? `Expected output (do NOT copy this verbatim into the rewrite): ${f.expected}\n` : ""}`)
194
+ .map((f, i) => `--- Failure ${i + 1} (observed score ${f.observedScore.toFixed(2)}) ---\nInput: ${f.input}\n${f.expected !== undefined ? `Expected output (do NOT copy this verbatim into the rewrite): ${f.expected}\n` : ""}${f.rationale !== undefined ? `Grader feedback: ${f.rationale}\n` : ""}`)
194
195
  .join("\n");
195
196
  return `CURRENT PROMPT:\n${currentPrompt}\n\nSAMPLE OF DEV-SET FAILURES:\n${failureBlock}\n\nReturn one JSON object: {"rewrite": "...", "rationale": "..."}`;
196
197
  }
197
- /** Identify failure samples from the trajectory. */
198
+ /**
199
+ * Identify the failing samples to show the model. When the fitness
200
+ * function supplied per-sample grades (`state.bestGrades`, wired by the
201
+ * CLI's eval-runner closure), we surface the samples the current best
202
+ * prompt ACTUALLY fails — worst-scoring first — together with the
203
+ * grader's rationale, so the meta-prompt can address the named root
204
+ * cause. This is the signal the system prompt promises. When grades are
205
+ * absent (a fitness fn returning a bare number, or an all-passing dev
206
+ * set), we fall back to surfacing the dev-set inputs with the aggregate
207
+ * score — the pre-failure-signal behaviour — so the search still runs.
208
+ */
198
209
  selectFailures(state) {
199
- // The trajectory records aggregate scores per candidate, not per
200
- // sample. For v0 we surface the dev set as raw inputs and let
201
- // the model reason about them generically; future iterations can
202
- // wire a per-sample grade map through the OptimizerState. The
203
- // result is still useful because the model sees the dev set
204
- // distribution even without per-sample grades.
210
+ const graded = state.bestGrades;
211
+ if (graded !== undefined && graded.length > 0) {
212
+ const byWorst = [...graded].sort((a, b) => a.score - b.score);
213
+ // Prefer genuine failures (below the threshold); if the dev set is
214
+ // already strong, fall back to the lowest scorers so the mutator
215
+ // still has a concrete target to push on.
216
+ const belowThreshold = byWorst.filter((g) => g.score < this.failureThreshold);
217
+ const window = (belowThreshold.length > 0 ? belowThreshold : byWorst).slice(0, this.maxFailuresInPrompt);
218
+ return window.map((g) => ({
219
+ input: g.input,
220
+ observedScore: g.score,
221
+ ...(g.expected !== undefined ? { expected: g.expected } : {}),
222
+ ...(g.rationale !== undefined ? { rationale: g.rationale } : {}),
223
+ }));
224
+ }
225
+ // No per-sample grades wired: surface the dev set as raw inputs with
226
+ // the aggregate score. The model still sees the dev-set distribution.
205
227
  return state.devSet.slice(0, this.maxFailuresInPrompt).map((s) => ({
206
228
  input: s.input,
207
- // Use the trajectory's most recent score as a coarse signal.
208
229
  observedScore: state.best.score,
209
230
  ...(s.expected_output !== undefined ? { expected: s.expected_output } : {}),
210
231
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewhaus/prompt-optimizer-claude",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "Pillar-2 model-driven MutationProvider — asks Claude to rewrite the prompt given dev-set failures. Closes the v0 prompt-optimizer's L91 'rule-based-only' gap.",
6
6
  "main": "dist/index.js",
@@ -16,13 +16,13 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@anthropic-ai/sdk": "^0.96.0",
19
- "@crewhaus/adapter-anthropic": "0.1.6",
20
- "@crewhaus/errors": "0.1.6",
21
- "@crewhaus/prompt-optimizer": "0.1.6",
19
+ "@crewhaus/adapter-anthropic": "0.1.7",
20
+ "@crewhaus/errors": "0.1.7",
21
+ "@crewhaus/prompt-optimizer": "0.1.7",
22
22
  "zod": "^3.23.8"
23
23
  },
24
24
  "devDependencies": {
25
- "@crewhaus/eval-dataset": "0.1.6"
25
+ "@crewhaus/eval-dataset": "0.1.7"
26
26
  },
27
27
  "license": "Apache-2.0",
28
28
  "author": {