@sanity/ailf 0.4.0 → 0.4.1

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.
@@ -25,7 +25,19 @@ export declare function detectFeatureArea(description: string): string;
25
25
  /**
26
26
  * Extract a numeric score (0–100) from a grading component result.
27
27
  *
28
- * Tries: direct score field → JSON-parsed reason → bare number in reason.
28
+ * Tries (in order):
29
+ * 1. JSON-parsed reason (grader's native 0–100 scale — most reliable)
30
+ * 2. Direct score field (may be Promptfoo-normalized to 0–1)
31
+ * 3. Bare number in reason text
32
+ *
33
+ * Promptfoo's `llm-rubric` assertion normalizes `component.score` to
34
+ * the 0–1 range for some providers (notably GPT models, ~50% of the
35
+ * time) while leaving others in the grader's native 0–100 range. The
36
+ * `reason` field always contains the raw grader JSON, so we prefer it.
37
+ *
38
+ * When falling back to `component.score`, values in (0, 1] are rescaled
39
+ * to 0–100 since the rubric explicitly requests a 0–100 score and a
40
+ * true score of 0 or 1 out of 100 is vanishingly unlikely.
29
41
  */
30
42
  export declare function parseRubricScore(component: ComponentResult): number;
31
43
  /**
@@ -85,14 +85,22 @@ export function detectFeatureArea(description) {
85
85
  /**
86
86
  * Extract a numeric score (0–100) from a grading component result.
87
87
  *
88
- * Tries: direct score field → JSON-parsed reason → bare number in reason.
88
+ * Tries (in order):
89
+ * 1. JSON-parsed reason (grader's native 0–100 scale — most reliable)
90
+ * 2. Direct score field (may be Promptfoo-normalized to 0–1)
91
+ * 3. Bare number in reason text
92
+ *
93
+ * Promptfoo's `llm-rubric` assertion normalizes `component.score` to
94
+ * the 0–1 range for some providers (notably GPT models, ~50% of the
95
+ * time) while leaving others in the grader's native 0–100 range. The
96
+ * `reason` field always contains the raw grader JSON, so we prefer it.
97
+ *
98
+ * When falling back to `component.score`, values in (0, 1] are rescaled
99
+ * to 0–100 since the rubric explicitly requests a 0–100 score and a
100
+ * true score of 0 or 1 out of 100 is vanishingly unlikely.
89
101
  */
90
102
  export function parseRubricScore(component) {
91
- // Direct score field
92
- if (typeof component.score === "number") {
93
- return component.score;
94
- }
95
- // Try to extract from reason (LLM rubric returns JSON)
103
+ // 1. Prefer reason-extracted score — always in the grader's native 0–100 scale
96
104
  if (component.reason) {
97
105
  try {
98
106
  const parsed = JSON.parse(component.reason);
@@ -102,15 +110,38 @@ export function parseRubricScore(component) {
102
110
  }
103
111
  }
104
112
  catch {
105
- // Try to find a bare number
106
- const match = component.reason.match(/(\d+)/);
107
- if (match) {
108
- return parseInt(match[1], 10);
109
- }
113
+ // Not valid JSON — fall through to direct score or bare number extraction
114
+ }
115
+ }
116
+ // 2. Direct score field — may be Promptfoo-normalized to 0–1
117
+ if (typeof component.score === "number") {
118
+ return normalizeScore(component.score);
119
+ }
120
+ // 3. Last resort: bare number in reason text
121
+ if (component.reason) {
122
+ const match = component.reason.match(/(\d+)/);
123
+ if (match) {
124
+ return parseInt(match[1], 10);
110
125
  }
111
126
  }
112
127
  return 0;
113
128
  }
129
+ /**
130
+ * Normalize a score that may be in either the 0–1 or 0–100 range.
131
+ *
132
+ * Promptfoo's `llm-rubric` assertion inconsistently normalizes
133
+ * `component.score` to 0–1 for some providers. Since the rubric
134
+ * explicitly requests scores on a 0–100 scale:
135
+ * - Scores in (0, 1] are rescaled to 0–100 (e.g., 0.95 → 95)
136
+ * - Score of exactly 0 stays 0 (genuine zero)
137
+ * - Scores > 1 are already on the 0–100 scale
138
+ */
139
+ function normalizeScore(score) {
140
+ if (score > 0 && score <= 1) {
141
+ return Math.round(score * 100);
142
+ }
143
+ return score;
144
+ }
114
145
  // ---------------------------------------------------------------------------
115
146
  // URL metadata extraction
116
147
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ailf",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "restricted"
@@ -40,9 +40,9 @@
40
40
  "@types/node": "^22.13.1",
41
41
  "tsx": "^4.19.2",
42
42
  "typescript": "^5.7.3",
43
+ "@sanity/ailf-tasks": "0.1.4",
43
44
  "@sanity/ailf-core": "0.1.0",
44
- "@sanity/ailf-shared": "0.1.0",
45
- "@sanity/ailf-tasks": "0.1.4"
45
+ "@sanity/ailf-shared": "0.1.0"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsc && tsx scripts/bundle-workspace-deps.ts",