mlx-bun 0.0.10 → 0.0.12
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/README.md +107 -32
- package/THIRD_PARTY_LICENSES.md +10 -0
- package/docs/archive/mlx-bun-lab-report.html +275 -0
- package/docs/archive/wwdc-mlx-bun.html +848 -0
- package/docs/design/audio-input-plan.md +356 -0
- package/docs/design/batching-perf-path.md +58 -35
- package/docs/design/batching-v2-plan.md +23 -9
- package/docs/design/bucketing-stage.md +2 -1
- package/docs/design/colibri-glm52-port.md +1479 -0
- package/docs/design/compat-cli-surface-design.md +4 -3
- package/docs/design/decode-speed-program.md +172 -0
- package/docs/design/docs-reorg-plan.md +1 -1
- package/docs/design/dspark-serving-program.md +371 -0
- package/docs/design/dspark-speculative-decoding.md +167 -5
- package/docs/design/faithful-l1-consolidation.md +310 -0
- package/docs/design/generic-model-support.md +8 -5
- package/docs/design/grammar-spec-batching-integration.md +301 -0
- package/docs/design/minicpm5-decode-megakernel.md +16 -11
- package/docs/design/mlx-lm-tool-parity-plan.md +11 -1
- package/docs/design/omlx-adoption-map.md +14 -6
- package/docs/design/orpo-training.md +34 -30
- package/docs/design/paged-kv-cache.md +214 -0
- package/docs/design/parallel-slots.md +82 -49
- package/docs/design/parity-tier-dag.md +43 -33
- package/docs/design/pre-colibri-stabilization.md +329 -0
- package/docs/design/repo-cleanup-plan.md +189 -0
- package/docs/design/runtime-isolation.md +19 -3
- package/docs/design/segmented-backward-training.md +3 -2
- package/docs/design/ssd-kv-cold-tier.md +130 -0
- package/docs/design/structured-output.md +484 -0
- package/docs/design/the-dreaming-master-plan.md +2 -3
- package/docs/design/turboquant-kv.md +235 -0
- package/docs/design/unified-engine-frontier-plan.md +774 -0
- package/docs/design/web-chat-beat-matrix.md +315 -0
- package/docs/design/web-chat-redesign.md +1112 -0
- package/docs/investigations/colibri-oracle-pin.md +1284 -0
- package/docs/investigations/dspark-handoff.md +182 -16
- package/docs/investigations/indexshare-performance-spike.md +357 -0
- package/docs/investigations/lab-build-journal.md +2 -2
- package/docs/investigations/openwiki-evaluation.md +249 -0
- package/docs/planning/IDEAS.md +14 -0
- package/docs/planning/PRODUCT_ROADMAP.md +2 -3
- package/docs/planning/chat-ui-vision-memory-first.md +199 -0
- package/docs/planning/chat-ui-vision.md +50 -0
- package/docs/planning/memory-docs-and-dag-plan.md +15 -6
- package/docs/planning/release-notes-v0.0.11.md +239 -0
- package/docs/planning/release-notes-v0.0.12.md +80 -0
- package/docs/reference/cli.md +63 -18
- package/docs/reference/distribution.md +37 -6
- package/docs/reference/embedding.md +40 -16
- package/docs/reference/features-matrix.md +99 -0
- package/docs/reference/library-api.md +69 -20
- package/docs/reference/memory.md +26 -6
- package/docs/reference/models.md +60 -3
- package/docs/reference/orpo-quickstart.md +3 -4
- package/docs/reference/server-api.md +717 -24
- package/docs/reference/server-config.md +265 -129
- package/docs/reference/training.md +24 -16
- package/package.json +5 -3
- package/src/anthropic.ts +67 -44
- package/src/audio/conformer.ts +647 -0
- package/src/audio/decode.ts +133 -0
- package/src/audio/features.ts +202 -0
- package/src/audio/transcode.ts +67 -0
- package/src/chat-template.ts +155 -10
- package/src/cli.ts +483 -113
- package/src/config.ts +116 -24
- package/src/diffusion/diffusion-generate.ts +14 -8
- package/src/download.ts +143 -12
- package/src/eval/README.md +7 -6
- package/src/eval/ifeval.ts +63 -120
- package/src/eval/kl.ts +59 -3
- package/src/eval/quality-db.ts +2 -2
- package/src/eval/runner.ts +17 -8
- package/src/eval/tasks/ifeval.ts +167 -31
- package/src/expert-io.ts +391 -0
- package/src/expert-offload.ts +15 -2
- package/src/expert-residency.ts +1134 -0
- package/src/expert-usage.ts +415 -0
- package/src/flags.ts +13 -0
- package/src/generate.ts +438 -35
- package/src/grammar.ts +585 -0
- package/src/hf-push.ts +1 -1
- package/src/hub-rest.ts +242 -0
- package/src/index.ts +14 -2
- package/src/jobs/runner.ts +37 -18
- package/src/kv-store.ts +586 -93
- package/src/lora.ts +85 -14
- package/src/md-modules.d.ts +42 -0
- package/src/media-fetch.ts +426 -0
- package/src/memory/rest.ts +245 -0
- package/src/memory/vault.ts +74 -0
- package/src/mlx/array.ts +149 -19
- package/src/mlx/autograd.ts +53 -16
- package/src/mlx/checkpoint.ts +46 -13
- package/src/mlx/custom-vjp.ts +83 -35
- package/src/mlx/ffi.ts +27 -3
- package/src/mlx/metal-kernel.ts +7 -4
- package/src/mlx/ops.ts +153 -23
- package/src/mlx/turboquant-ops.ts +831 -0
- package/src/mlx/turboquant-tables.ts +237 -0
- package/src/model/batched-mask.ts +62 -1
- package/src/model/batched-quant.ts +206 -0
- package/src/model/batched-rotating-quant.ts +346 -0
- package/src/model/batched-rotating.ts +46 -1
- package/src/model/compiled-decode.ts +4 -15
- package/src/model/factory.ts +103 -6
- package/src/model/gemma4-base.ts +395 -39
- package/src/model/gemma4.ts +122 -33
- package/src/model/generated/gemma4-12b.ts +9 -13
- package/src/model/generated/gemma4-26b.ts +10 -5
- package/src/model/generated/gemma4-e4b.ts +14 -9
- package/src/model/glm52-atlas.ts +511 -0
- package/src/model/glm52-cache.ts +706 -0
- package/src/model/glm52-config.ts +262 -0
- package/src/model/glm52-container.ts +305 -0
- package/src/model/glm52-coupling.ts +388 -0
- package/src/model/glm52-dsa.ts +602 -0
- package/src/model/glm52-expert-layout.ts +253 -0
- package/src/model/glm52-layout.ts +152 -0
- package/src/model/glm52-memory.ts +473 -0
- package/src/model/glm52-mla.ts +786 -0
- package/src/model/glm52-moe.ts +223 -0
- package/src/model/glm52-pilot.ts +537 -0
- package/src/model/glm52-reference.ts +445 -0
- package/src/model/glm52-residency.ts +557 -0
- package/src/model/glm52-resident-weights.ts +135 -0
- package/src/model/glm52-streamed-experts.ts +453 -0
- package/src/model/glm52-streamed-metal.ts +610 -0
- package/src/model/glm52-weights.ts +503 -0
- package/src/model/glm52.ts +1153 -0
- package/src/model/minicpm5.ts +58 -102
- package/src/model/paged-kv.ts +253 -0
- package/src/model/qwen3-delta.ts +76 -15
- package/src/model/qwen3-moe.ts +404 -0
- package/src/model/qwen3.ts +9 -8
- package/src/model/qwen3_5.ts +88 -23
- package/src/model/support.ts +19 -1
- package/src/model/universal/dense.ts +40 -5
- package/src/model/universal/rope.ts +36 -21
- package/src/native/expert_io.c +523 -0
- package/src/native-pack.ts +6 -4
- package/src/pi-web.ts +1299 -82
- package/src/preflight.ts +27 -8
- package/src/prompt-cache.ts +293 -33
- package/src/quantize/quantizer.ts +16 -1
- package/src/registry.ts +57 -2
- package/src/responses.ts +62 -49
- package/src/serve/batch-scheduler.ts +1050 -78
- package/src/serve/generation-gateway.ts +252 -24
- package/src/serve/isolate.ts +417 -0
- package/src/serve/lane-registry.ts +44 -0
- package/src/serve/session-search.ts +326 -0
- package/src/server.ts +1701 -175
- package/src/spec/assistant-source.ts +150 -0
- package/src/spec/deepspec-source.ts +143 -0
- package/src/spec/dflash-source.ts +124 -0
- package/src/spec/dspark/ab-stats.ts +136 -0
- package/src/spec/dspark/calibration.ts +112 -0
- package/src/spec/dspark/deepspec-module.ts +801 -0
- package/src/spec/dspark/generate-dflash.ts +72 -20
- package/src/spec/dspark/loader.ts +23 -0
- package/src/spec/dspark/module-dflash.ts +278 -36
- package/src/spec/dspark/quantize-drafter.ts +91 -0
- package/src/spec/generate.ts +59 -25
- package/src/spec/glm52-mtp-source.ts +392 -0
- package/src/spec/ngram-source.ts +132 -0
- package/src/spec/serve-loop.ts +617 -0
- package/src/spec/source.ts +118 -0
- package/src/spec/two-model.ts +171 -0
- package/src/ssd-cache.ts +111 -35
- package/src/tokenizer.ts +288 -2
- package/src/tool-approvals.ts +131 -0
- package/src/tool-call.ts +438 -22
- package/src/train/flash-cce.ts +2 -2
- package/src/train/job.ts +0 -12
- package/src/train/lora-params.ts +17 -6
- package/src/train/trainer.ts +60 -30
- package/src/vision/prompt.ts +217 -48
- package/src/web/app.html +1197 -1791
- package/src/web/app.js +5694 -0
- package/src/web/icon.svg +13 -0
- package/src/web/manifest.webmanifest +24 -0
- package/src/web/src/adapters-panel.ts +288 -0
- package/src/web/src/api.ts +57 -0
- package/src/web/src/assistant.ts +485 -0
- package/src/web/src/chat.ts +1050 -0
- package/src/web/src/composer.ts +959 -0
- package/src/web/src/dataset.ts +132 -0
- package/src/web/src/finetune.ts +267 -0
- package/src/web/src/hub.ts +428 -0
- package/src/web/src/main.ts +98 -0
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +572 -0
- package/src/web/src/model-picker.ts +159 -0
- package/src/web/src/palette.ts +346 -0
- package/src/web/src/protocol.ts +62 -0
- package/src/web/src/quantize.ts +167 -0
- package/src/web/src/rag.ts +274 -0
- package/src/web/src/sessions.ts +469 -0
- package/src/web/src/shell.ts +804 -0
- package/src/web/src/status.ts +300 -0
- package/src/web/src/ui-catalog.ts +92 -0
- package/src/web/sw.js +71 -0
- package/src/web/vendor/README.md +61 -0
- package/src/web/vendor/hljs-LICENSE +29 -0
- package/src/web/vendor/hljs-theme.css +59 -0
- package/src/web/vendor/hljs.js +26 -0
- package/src/web-tools.ts +54 -2
- package/src/weights.ts +82 -19
- package/src/model/fused-decode-kernel.ts +0 -312
- package/src/model/fused-geglu-kernel.ts +0 -198
- package/src/model/fused-mlp-kernel.ts +0 -169
- package/src/model/fused-swiglu-kernel.ts +0 -240
- package/src/model/steel-linear-kernel.ts +0 -96
- package/src/status-page.html +0 -497
package/src/eval/ifeval.ts
CHANGED
|
@@ -1,138 +1,81 @@
|
|
|
1
|
-
//
|
|
2
|
-
// al. 2023, "Instruction-Following Evaluation for Large Language Models"). Each
|
|
3
|
-
// instance pairs a prompt with one or more *verifiable* instructions; a response
|
|
4
|
-
// is scored by deterministic checks (no LLM judge), so before/after ORPO
|
|
5
|
-
// comparisons are exact and reproducible.
|
|
1
|
+
// Compatibility facade for the canonical IFEval scorer.
|
|
6
2
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// instruction-level accuracy — both derived from `scoreInstance`.
|
|
3
|
+
// The complete verifier registry and strict/loose aggregation contract live in
|
|
4
|
+
// `tasks/ifeval.ts`, which is also used by `scripts/eval.ts`. Keep this module
|
|
5
|
+
// as the stable import surface used by `scripts/run-ifeval.ts` and older
|
|
6
|
+
// callers; it must not grow a second verifier implementation.
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
import {
|
|
9
|
+
scoreIfevalInstance,
|
|
10
|
+
scoreIfevalPairs,
|
|
11
|
+
type IfevalInstance,
|
|
12
|
+
type IfevalResult,
|
|
13
|
+
} from "./tasks/ifeval";
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
SUPPORTED_INSTRUCTIONS,
|
|
17
|
+
looseClean,
|
|
18
|
+
scoreIfevalInstance,
|
|
19
|
+
scoreIfevalPairs,
|
|
20
|
+
stripThinking,
|
|
21
|
+
verifyResponse,
|
|
22
|
+
type IfevalCoverage,
|
|
23
|
+
type IfevalInstance,
|
|
24
|
+
type IfevalInstanceScore,
|
|
25
|
+
type IfevalPair,
|
|
26
|
+
type IfevalResult,
|
|
27
|
+
type IfevalVerification,
|
|
28
|
+
} from "./tasks/ifeval";
|
|
29
|
+
|
|
30
|
+
/** Historical spelling retained for callers of this module. */
|
|
31
|
+
export type IFEvalInstance = IfevalInstance;
|
|
21
32
|
|
|
22
33
|
export interface InstanceResult {
|
|
23
|
-
/**
|
|
34
|
+
/** Unknown instructions are true here per the canonical prompt contract. */
|
|
24
35
|
perInstruction: boolean[];
|
|
25
|
-
/** Strict: all instructions passed. */
|
|
26
36
|
followedAll: boolean;
|
|
37
|
+
/** Consult this field rather than interpreting unknown IDs as verified. */
|
|
38
|
+
unhandled: string[];
|
|
27
39
|
}
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
case "exactly": return n === target;
|
|
44
|
-
default: return n >= target; // IFEval default for "around"/unspecified is lenient ≥
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/** The verifiable-instruction registry. Keyed by IFEval instruction_id. */
|
|
49
|
-
export const INSTRUCTIONS: Record<string, Checker> = {
|
|
50
|
-
// ---- keywords ----
|
|
51
|
-
"keywords:existence": (r, kw) =>
|
|
52
|
-
(kw.keywords as string[]).every((k) => norm(r).includes(norm(k))),
|
|
53
|
-
"keywords:frequency": (r, kw) => {
|
|
54
|
-
const k = norm(kw.keyword as string);
|
|
55
|
-
const count = norm(r).split(k).length - 1;
|
|
56
|
-
return cmp(count, kw.relation as string, kw.frequency as number);
|
|
57
|
-
},
|
|
58
|
-
"keywords:forbidden_words": (r, kw) =>
|
|
59
|
-
!(kw.forbidden_words as string[]).some((k) => norm(r).includes(norm(k))),
|
|
60
|
-
|
|
61
|
-
// ---- length constraints ----
|
|
62
|
-
"length_constraints:number_words": (r, kw) =>
|
|
63
|
-
cmp(words(r).length, kw.relation as string, kw.num_words as number),
|
|
64
|
-
"length_constraints:number_sentences": (r, kw) =>
|
|
65
|
-
cmp(sentences(r).length, kw.relation as string, kw.num_sentences as number),
|
|
66
|
-
"length_constraints:number_paragraphs": (r, kw) =>
|
|
67
|
-
cmp(paragraphs(r).length, kw.relation as string, kw.num_paragraphs as number),
|
|
68
|
-
|
|
69
|
-
// ---- detectable format ----
|
|
70
|
-
"detectable_format:number_bullet_lists": (r, kw) => {
|
|
71
|
-
const bullets = r.split("\n").filter((l) => /^\s*[*\-]\s+/.test(l)).length;
|
|
72
|
-
return cmp(bullets, kw.relation as string, kw.num_bullets as number);
|
|
73
|
-
},
|
|
74
|
-
"detectable_format:number_highlighted_sections": (r, kw) => {
|
|
75
|
-
const hi = (r.match(/\*[^*\n]+\*/g) ?? []).length;
|
|
76
|
-
return cmp(hi, kw.relation as string, kw.num_highlights as number);
|
|
77
|
-
},
|
|
78
|
-
"detectable_format:title": (r) => /<<[^>\n]+>>/.test(r),
|
|
79
|
-
"detectable_format:json_format": (r) => {
|
|
80
|
-
const t = r.trim().replace(/^```(?:json)?\s*|\s*```$/g, "").trim();
|
|
81
|
-
try { JSON.parse(t); return true; } catch { return false; }
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
// ---- case ----
|
|
85
|
-
"change_case:english_lowercase": (r) => r === r.toLowerCase(),
|
|
86
|
-
"change_case:english_capital": (r) => r === r.toUpperCase(),
|
|
87
|
-
|
|
88
|
-
// ---- start / end ----
|
|
89
|
-
"startend:end_checker": (r, kw) =>
|
|
90
|
-
norm(r.trimEnd()).endsWith(norm((kw.end_phrase as string).trim())),
|
|
91
|
-
"startend:quotation": (r) => {
|
|
92
|
-
const t = r.trim();
|
|
93
|
-
return t.startsWith('"') && t.endsWith('"') && t.length >= 2;
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
// ---- punctuation ----
|
|
97
|
-
"punctuation:no_comma": (r) => !r.includes(","),
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/** Score one IFEval instance (strict). Unknown instruction ids count as failed
|
|
101
|
-
* (so coverage gaps never inflate the score) — filter the dataset to the
|
|
102
|
-
* supported ids, or extend INSTRUCTIONS, to avoid that. */
|
|
103
|
-
export function scoreInstance(instance: IFEvalInstance, response: string): InstanceResult {
|
|
104
|
-
const perInstruction = instance.instruction_id_list.map((id, i) => {
|
|
105
|
-
const check = INSTRUCTIONS[id];
|
|
106
|
-
if (!check) return false;
|
|
107
|
-
try { return check(response, instance.kwargs[i] ?? {}, instance); } catch { return false; }
|
|
108
|
-
});
|
|
109
|
-
return { perInstruction, followedAll: perInstruction.every(Boolean) };
|
|
41
|
+
/**
|
|
42
|
+
* Historical strict-only helper, now delegated to the canonical scorer.
|
|
43
|
+
* New code should prefer `scoreIfevalInstance` for both strict and loose modes.
|
|
44
|
+
*/
|
|
45
|
+
export function scoreInstance(
|
|
46
|
+
instance: IFEvalInstance,
|
|
47
|
+
response: string,
|
|
48
|
+
): InstanceResult {
|
|
49
|
+
const { strict } = scoreIfevalInstance(instance, response);
|
|
50
|
+
return {
|
|
51
|
+
perInstruction: strict.instructionPasses.map((result) => result ?? true),
|
|
52
|
+
followedAll: strict.pass,
|
|
53
|
+
unhandled: strict.unhandled,
|
|
54
|
+
};
|
|
110
55
|
}
|
|
111
56
|
|
|
112
|
-
export interface IFEvalReport {
|
|
57
|
+
export interface IFEvalReport extends IfevalResult {
|
|
58
|
+
/** Historical alias for nTotal. */
|
|
113
59
|
n: number;
|
|
114
|
-
/**
|
|
60
|
+
/** Historical alias for strictAcc (prompt-level strict). */
|
|
115
61
|
promptAccuracy: number;
|
|
116
|
-
/**
|
|
62
|
+
/** Historical alias for strictInstructionAcc. */
|
|
117
63
|
instructionAccuracy: number;
|
|
118
64
|
}
|
|
119
65
|
|
|
120
|
-
/**
|
|
121
|
-
*
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
66
|
+
/**
|
|
67
|
+
* Historical aggregation helper with unambiguous canonical fields plus aliases.
|
|
68
|
+
* Unknown instructions do not fail prompt accuracy, are excluded from
|
|
69
|
+
* instruction accuracy, and remain visible in `coverage`.
|
|
70
|
+
*/
|
|
71
|
+
export function aggregate(
|
|
72
|
+
pairs: Array<{ instance: IFEvalInstance; response: string }>,
|
|
73
|
+
): IFEvalReport {
|
|
74
|
+
const result = scoreIfevalPairs(pairs);
|
|
130
75
|
return {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
76
|
+
...result,
|
|
77
|
+
n: result.nTotal,
|
|
78
|
+
promptAccuracy: result.strictAcc,
|
|
79
|
+
instructionAccuracy: result.strictInstructionAcc,
|
|
134
80
|
};
|
|
135
81
|
}
|
|
136
|
-
|
|
137
|
-
/** Instruction ids this implementation supports (filter datasets to these). */
|
|
138
|
-
export const SUPPORTED_INSTRUCTIONS = new Set(Object.keys(INSTRUCTIONS));
|
package/src/eval/kl.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { Dtype } from "../mlx/ffi";
|
|
|
26
26
|
import * as ops from "../mlx/ops";
|
|
27
27
|
import { Registry } from "../registry";
|
|
28
28
|
import { Weights } from "../weights";
|
|
29
|
-
import { loadModelConfig, type ModelConfig } from "../config";
|
|
29
|
+
import { loadModelConfig, type ModelConfig, type TurboQuantScheme } from "../config";
|
|
30
30
|
import { createModel, type RuntimeModel } from "../model/factory";
|
|
31
31
|
import { loadTokenizer, type LoadedTokenizer } from "../tokenizer";
|
|
32
32
|
import { maybeQuantizeKv } from "../generate";
|
|
@@ -223,10 +223,14 @@ function klScalar(p: Float32Array, q: Float32Array): number {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
/** Prefill (bf16) → quantize → teacher-forced decode; return per-step logit
|
|
226
|
-
* distributions for the last `tokens.length - prefillLen` positions.
|
|
226
|
+
* distributions for the last `tokens.length - prefillLen` positions.
|
|
227
|
+
* `kvOverride` replaces the default kvConfig-from-model-config scheme when
|
|
228
|
+
* given — the seam evaluateKlKvArm uses to run a TurboQuant (or bf16-null)
|
|
229
|
+
* arm instead of the env-flag self-flip. */
|
|
227
230
|
function decodeArm(
|
|
228
231
|
model: RuntimeModel, config: ModelConfig, tokens: number[], prefillLen: number,
|
|
229
232
|
flag: string, value: string,
|
|
233
|
+
kvOverride?: Parameters<typeof maybeQuantizeKv>[1],
|
|
230
234
|
): Float32Array[] {
|
|
231
235
|
const prev = process.env[flag];
|
|
232
236
|
process.env[flag] = value;
|
|
@@ -237,7 +241,10 @@ function decodeArm(
|
|
|
237
241
|
lp.dispose();
|
|
238
242
|
// quantize the populated caches (mixed 4/8 per kv_config) — exactly
|
|
239
243
|
// what generate() does between prefill and decode.
|
|
240
|
-
maybeQuantizeKv(
|
|
244
|
+
maybeQuantizeKv(
|
|
245
|
+
cache,
|
|
246
|
+
kvOverride ?? { kvConfig: config.kvQuant ?? undefined, quantizedKvStart: 0 },
|
|
247
|
+
);
|
|
241
248
|
const out: Float32Array[] = [];
|
|
242
249
|
for (let i = prefillLen; i < tokens.length; i++) {
|
|
243
250
|
const logits = model.forward([tokens[i]!], cache); // [1,1,V] quantized decode
|
|
@@ -292,3 +299,52 @@ export async function evaluateKlServingDecode(opts: {
|
|
|
292
299
|
refLabel: `serving-decode self:${opts.flag}=${opts.refValue}→${opts.candValue} (${decodeSteps} steps)`,
|
|
293
300
|
};
|
|
294
301
|
}
|
|
302
|
+
|
|
303
|
+
/** KV-scheme A/B on the teacher-forced serving-decode path: bf16 baseline
|
|
304
|
+
* (no kvOverride) vs an explicit KV scheme (e.g. TurboQuant), same model /
|
|
305
|
+
* same prompts / same weight load — the quality-vs-bpw curve gate
|
|
306
|
+
* (scripts/eval-turboquant-curve.ts). Unlike evaluateKlServingDecode (env-flag
|
|
307
|
+
* self-flip), the two arms differ by an actual GenerateOptions kv scheme
|
|
308
|
+
* object, so this is the seam for schemes that aren't env-lever-shaped. */
|
|
309
|
+
export async function evaluateKlKvArm(opts: {
|
|
310
|
+
candidate: string;
|
|
311
|
+
/** Undefined ⇒ bf16 (no quantization). */
|
|
312
|
+
candidateScheme?: { turboQuant?: TurboQuantScheme; kvBits?: number; kvConfig?: ModelConfig["kvQuant"] };
|
|
313
|
+
prompts: string[];
|
|
314
|
+
nPrompts?: number;
|
|
315
|
+
seqLen?: number;
|
|
316
|
+
decodeSteps?: number;
|
|
317
|
+
}): Promise<KLResult> {
|
|
318
|
+
const t0 = Date.now();
|
|
319
|
+
const n = opts.nPrompts ?? 64;
|
|
320
|
+
const seqLen = opts.seqLen ?? 256;
|
|
321
|
+
const decodeSteps = opts.decodeSteps ?? 32;
|
|
322
|
+
const { model, tokenizer, config } = await loadRunnable(opts.candidate);
|
|
323
|
+
const tokenized = prepPrompts(opts.prompts, tokenizer, seqLen, n);
|
|
324
|
+
const noopFlag = "MLX_BUN_EVAL_KV_ARM_NOOP"; // decodeArm always flips a flag; this one is inert
|
|
325
|
+
const refKv = {}; // bf16: maybeQuantizeKv no-ops with no kvBits/kvConfig/turboQuant
|
|
326
|
+
const candKv = opts.candidateScheme
|
|
327
|
+
? { ...opts.candidateScheme, quantizedKvStart: 0, kvConfig: opts.candidateScheme.kvConfig ?? undefined }
|
|
328
|
+
: {};
|
|
329
|
+
|
|
330
|
+
const perPrompt: Float32Array[] = [];
|
|
331
|
+
for (const tokens of tokenized) {
|
|
332
|
+
if (tokens.length < 2) continue;
|
|
333
|
+
const prefillLen = Math.max(1, tokens.length - decodeSteps);
|
|
334
|
+
const refSteps = decodeArm(model, config, tokens, prefillLen, noopFlag, "0", refKv);
|
|
335
|
+
const candSteps = decodeArm(model, config, tokens, prefillLen, noopFlag, "0", candKv);
|
|
336
|
+
const m = Math.min(refSteps.length, candSteps.length);
|
|
337
|
+
const kls = new Float32Array(m);
|
|
338
|
+
for (let s = 0; s < m; s++) kls[s] = klScalar(refSteps[s]!, candSteps[s]!);
|
|
339
|
+
perPrompt.push(kls);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const agg = aggregate(perPrompt);
|
|
343
|
+
return {
|
|
344
|
+
nPrompts: perPrompt.length, seqLen, meanKl: agg.mean, medianKl: agg.median, p95Kl: agg.p95,
|
|
345
|
+
elapsedSec: (Date.now() - t0) / 1000,
|
|
346
|
+
refLabel: opts.candidateScheme?.turboQuant
|
|
347
|
+
? `bf16 vs turbo k${opts.candidateScheme.turboQuant.kBits}v${opts.candidateScheme.turboQuant.vBits} (${decodeSteps} steps)`
|
|
348
|
+
: `bf16 vs kv-scheme (${decodeSteps} steps)`,
|
|
349
|
+
};
|
|
350
|
+
}
|
package/src/eval/quality-db.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
// One row = one (model, config) measured on one task (or the aggregate
|
|
7
7
|
// "capability" / "kl" rows). `config_json` records the perf levers that
|
|
8
|
-
// were active (compile / fused-
|
|
8
|
+
// were active (compile / fused-sdpa / kv-quant) so a row is
|
|
9
9
|
// self-describing: which arm of the head-to-head produced this number.
|
|
10
10
|
|
|
11
11
|
import { Database } from "bun:sqlite";
|
|
@@ -19,7 +19,7 @@ CREATE TABLE IF NOT EXISTS quality_runs (
|
|
|
19
19
|
model_path TEXT NOT NULL,
|
|
20
20
|
commit_sha TEXT,
|
|
21
21
|
task TEXT NOT NULL, -- 'kl' | 'gsm8k' | 'mmlu' | ... | 'capability'
|
|
22
|
-
config_json TEXT NOT NULL, -- {compile,
|
|
22
|
+
config_json TEXT NOT NULL, -- {compile,fusedSdpa,kvQuant,...} active levers
|
|
23
23
|
n_samples INTEGER NOT NULL,
|
|
24
24
|
-- task accuracy (percent 0-100); null for kl-only rows
|
|
25
25
|
pct REAL,
|
package/src/eval/runner.ts
CHANGED
|
@@ -9,8 +9,12 @@ import { generate, type GenerateOptions } from "../generate";
|
|
|
9
9
|
import * as ops from "../mlx/ops";
|
|
10
10
|
import { ChatTemplate } from "../chat-template";
|
|
11
11
|
import { loadModelConfig, type ModelConfig } from "../config";
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
openModel,
|
|
14
|
+
type Glm52RuntimeOpenOptions,
|
|
15
|
+
type RuntimeModel,
|
|
16
|
+
} from "../model/factory";
|
|
17
|
+
import { isGlm52Config } from "../model/support";
|
|
14
18
|
import { DiffusionGemmaModel } from "../model/diffusion-gemma";
|
|
15
19
|
import { loadTokenizer, type LoadedTokenizer } from "../tokenizer";
|
|
16
20
|
import { resolveModelDir } from "./kl";
|
|
@@ -65,11 +69,16 @@ export interface TaskModel {
|
|
|
65
69
|
activeAdapters?: string[];
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
export async function loadTaskModel(
|
|
72
|
+
export async function loadTaskModel(
|
|
73
|
+
query: string,
|
|
74
|
+
adapterDir?: string,
|
|
75
|
+
runtimeOptions: Glm52RuntimeOpenOptions = {},
|
|
76
|
+
): Promise<TaskModel> {
|
|
69
77
|
const dir = resolveModelDir(query);
|
|
70
78
|
const config = await loadModelConfig(dir);
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
if (adapterDir && isGlm52Config(config))
|
|
80
|
+
throw new Error("GLM-5.2 does not support LoRA adapters");
|
|
81
|
+
const model = await openModel(dir, runtimeOptions);
|
|
73
82
|
const activeAdapters: string[] = [];
|
|
74
83
|
if (adapterDir) {
|
|
75
84
|
// Mount a trained LoRA adapter so the eval measures base+adapter (e.g. the
|
|
@@ -99,11 +108,11 @@ export interface GenOpts {
|
|
|
99
108
|
* (its capability numbers are non-thinking; e.g. MiniCPM5 IFEval 64.7 is
|
|
100
109
|
* non-thinking). Set MLX_BUN_EVAL_THINK=1 or pass true to evaluate thinking mode. */
|
|
101
110
|
enableThinking?: boolean;
|
|
102
|
-
/** Explicit KV-quant scheme (cli `generate --l2/--
|
|
111
|
+
/** Explicit KV-quant scheme (cli `generate --l2/--kv-quant`, resolved with
|
|
103
112
|
* serve's semantics). Overrides the MLX_BUN_EVAL_KV_QUANT env default; an empty
|
|
104
113
|
* object forces bf16. When it names quantized KV, generation runs the product
|
|
105
114
|
* generate() path (same tokens as `serve` with the same scheme). */
|
|
106
|
-
kvScheme?: Pick<GenerateOptions, "kvBits" | "kvConfig" | "quantizedKvStart">;
|
|
115
|
+
kvScheme?: Pick<GenerateOptions, "kvBits" | "kvConfig" | "quantizedKvStart" | "turboQuant">;
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
/** Bit-exact greedy decode: raw `model.forward` + argmax loop, which matches mlx-lm
|
|
@@ -159,7 +168,7 @@ export async function generateText(tm: TaskModel, body: string, opts: GenOpts =
|
|
|
159
168
|
(process.env.MLX_BUN_EVAL_KV_QUANT === "1" && tm.config.kvQuant?.length)
|
|
160
169
|
? { kvConfig: tm.config.kvQuant, quantizedKvStart: 0 } : undefined;
|
|
161
170
|
const kvScheme = opts.kvScheme ?? envKv;
|
|
162
|
-
const kvActive = !!(kvScheme && (kvScheme.kvBits || kvScheme.kvConfig?.length));
|
|
171
|
+
const kvActive = !!(kvScheme && (kvScheme.kvBits || kvScheme.kvConfig?.length || kvScheme.turboQuant));
|
|
163
172
|
|
|
164
173
|
// Parity default — greedy + full-precision KV + no sampler arm: decode bit-exactly
|
|
165
174
|
// via the raw forward (matches mlx-lm token-for-token). Sampler arms / kv-quant fall
|
package/src/eval/tasks/ifeval.ts
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
// 'banana'", "respond in JSON"). Each example carries one or more constraint
|
|
6
6
|
// instructions; we generate a response and run the corresponding verifier.
|
|
7
7
|
//
|
|
8
|
-
// We report
|
|
9
|
-
// *
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
8
|
+
// We report strict and loose metrics at both official granularities:
|
|
9
|
+
// * prompt — fraction of examples where ALL instructions pass;
|
|
10
|
+
// * instruction — fraction of supported individual checks that pass.
|
|
11
|
+
// Loose mode first strips leading "Sure, here is…" boilerplate and outer code
|
|
12
|
+
// fences. The primary metric (`accuracy`) remains prompt-level strict, as
|
|
13
|
+
// headlined by ifeval.py's IFEvalResult.__str__. Unknown instruction IDs retain
|
|
14
|
+
// optiq's prompt-pass behavior but are excluded from instruction accuracy and
|
|
15
|
+
// reported through the coverage object.
|
|
15
16
|
//
|
|
16
17
|
// Faithful port note: several verifiers below read kwarg keys that the dataset
|
|
17
18
|
// rows never populate (e.g. _check_capital_words_count / _check_letter_frequency
|
|
@@ -21,18 +22,56 @@
|
|
|
21
22
|
|
|
22
23
|
import { generateText, loadJsonl, sampleIndices, type TaskModel } from "../runner";
|
|
23
24
|
|
|
24
|
-
interface
|
|
25
|
-
key: number;
|
|
25
|
+
export interface IfevalInstance {
|
|
26
26
|
prompt: string;
|
|
27
27
|
instruction_id_list: string[];
|
|
28
28
|
kwargs: Record<string, unknown>[];
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
interface IfevalRow extends IfevalInstance {
|
|
32
|
+
key: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IfevalCoverage {
|
|
36
|
+
/** Prompts for which every instruction ID has a registered verifier. */
|
|
37
|
+
fullySupportedPrompts: number;
|
|
38
|
+
promptCoverage: number;
|
|
39
|
+
/** Registered instruction checks; unknown IDs are excluded from accuracy. */
|
|
40
|
+
supportedInstructions: number;
|
|
41
|
+
totalInstructions: number;
|
|
42
|
+
instructionCoverage: number;
|
|
43
|
+
unhandledInstructionCounts: Record<string, number>;
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
export interface IfevalResult {
|
|
32
47
|
nTotal: number;
|
|
33
48
|
strictAcc: number; // 0..1 — prompt-level strict pass rate
|
|
34
49
|
looseAcc: number; // 0..1 — prompt-level loose pass rate
|
|
35
50
|
accuracy: number; // 0..1 — primary metric == strictAcc (ifeval.py headline)
|
|
51
|
+
strictInstructionAcc: number;
|
|
52
|
+
looseInstructionAcc: number;
|
|
53
|
+
coverage: IfevalCoverage;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface IfevalVerification {
|
|
57
|
+
/**
|
|
58
|
+
* One result per requested instruction. null means the instruction ID is
|
|
59
|
+
* unhandled and therefore excluded from instruction-level accuracy.
|
|
60
|
+
*/
|
|
61
|
+
instructionPasses: Array<boolean | null>;
|
|
62
|
+
/** OptiQ parity: unhandled IDs do not make the prompt fail. */
|
|
63
|
+
pass: boolean;
|
|
64
|
+
unhandled: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface IfevalInstanceScore {
|
|
68
|
+
strict: IfevalVerification;
|
|
69
|
+
loose: IfevalVerification;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface IfevalPair {
|
|
73
|
+
instance: IfevalInstance;
|
|
74
|
+
response: string;
|
|
36
75
|
}
|
|
37
76
|
|
|
38
77
|
// ---------------------------------------------------------------------------
|
|
@@ -278,20 +317,24 @@ const VERIFIERS = new Map<string, Verifier>([
|
|
|
278
317
|
["detectable_content:postscript", alwaysPass],
|
|
279
318
|
]);
|
|
280
319
|
|
|
320
|
+
export const SUPPORTED_INSTRUCTIONS = new Set(VERIFIERS.keys());
|
|
321
|
+
|
|
281
322
|
/**
|
|
282
323
|
* Verify one response against its instruction list.
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
324
|
+
* `instructionPasses` is aligned with the requested IDs and uses null for
|
|
325
|
+
* unhandled IDs. An ID with no registered verifier does not affect prompt
|
|
326
|
+
* `pass` and is recorded in `unhandled` — matching ifeval.py's `continue`
|
|
327
|
+
* policy exactly, so strict prompt accuracy reproduces OptiQ's. The canonical
|
|
328
|
+
* aggregate excludes those nulls from instruction accuracy and reports them
|
|
329
|
+
* through coverage.
|
|
288
330
|
*/
|
|
289
331
|
export function verifyResponse(
|
|
290
332
|
response: string,
|
|
291
333
|
instructionIds: string[],
|
|
292
334
|
kwargsList: Kw[],
|
|
293
|
-
):
|
|
335
|
+
): IfevalVerification {
|
|
294
336
|
const unhandled: string[] = [];
|
|
337
|
+
const instructionPasses: Array<boolean | null> = [];
|
|
295
338
|
let pass = true;
|
|
296
339
|
for (let i = 0; i < instructionIds.length; i++) {
|
|
297
340
|
const iid = instructionIds[i]!;
|
|
@@ -299,15 +342,19 @@ export function verifyResponse(
|
|
|
299
342
|
const verifier = VERIFIERS.get(iid);
|
|
300
343
|
if (verifier === undefined) {
|
|
301
344
|
unhandled.push(iid);
|
|
345
|
+
instructionPasses.push(null);
|
|
302
346
|
continue; // ifeval.py: treat an unhandled instruction as PASS (don't touch `pass`)
|
|
303
347
|
}
|
|
304
348
|
try {
|
|
305
|
-
|
|
349
|
+
const followed = verifier(response, kw);
|
|
350
|
+
instructionPasses.push(followed);
|
|
351
|
+
if (!followed) pass = false;
|
|
306
352
|
} catch {
|
|
353
|
+
instructionPasses.push(false);
|
|
307
354
|
pass = false;
|
|
308
355
|
}
|
|
309
356
|
}
|
|
310
|
-
return { pass, unhandled };
|
|
357
|
+
return { instructionPasses, pass, unhandled };
|
|
311
358
|
}
|
|
312
359
|
|
|
313
360
|
/** Loose-mode preprocessing — mirrors ifeval.py `_loose_clean`. */
|
|
@@ -328,6 +375,92 @@ export function stripThinking(response: string): string {
|
|
|
328
375
|
return idx === -1 ? response : response.slice(idx + "</think>".length);
|
|
329
376
|
}
|
|
330
377
|
|
|
378
|
+
/** Score one generated response with the canonical strict + loose contract. */
|
|
379
|
+
export function scoreIfevalInstance(
|
|
380
|
+
instance: IfevalInstance,
|
|
381
|
+
rawResponse: string,
|
|
382
|
+
): IfevalInstanceScore {
|
|
383
|
+
const response = stripThinking(rawResponse);
|
|
384
|
+
return {
|
|
385
|
+
strict: verifyResponse(
|
|
386
|
+
response,
|
|
387
|
+
instance.instruction_id_list,
|
|
388
|
+
instance.kwargs,
|
|
389
|
+
),
|
|
390
|
+
loose: verifyResponse(
|
|
391
|
+
looseClean(response),
|
|
392
|
+
instance.instruction_id_list,
|
|
393
|
+
instance.kwargs,
|
|
394
|
+
),
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Canonical IFEval aggregation.
|
|
400
|
+
*
|
|
401
|
+
* Prompt accuracy follows the OptiQ reference behavior: an unknown instruction
|
|
402
|
+
* is recorded but does not fail its prompt. Instruction accuracy has a stricter
|
|
403
|
+
* denominator contract: unknown instructions are excluded and their coverage
|
|
404
|
+
* is reported explicitly, so an incomplete verifier registry cannot silently
|
|
405
|
+
* inflate that metric.
|
|
406
|
+
*/
|
|
407
|
+
export function scoreIfevalPairs(pairs: IfevalPair[]): IfevalResult {
|
|
408
|
+
let strictPrompts = 0;
|
|
409
|
+
let loosePrompts = 0;
|
|
410
|
+
let fullySupportedPrompts = 0;
|
|
411
|
+
let strictInstructions = 0;
|
|
412
|
+
let looseInstructions = 0;
|
|
413
|
+
let supportedInstructions = 0;
|
|
414
|
+
let totalInstructions = 0;
|
|
415
|
+
const unhandledCounts = new Map<string, number>();
|
|
416
|
+
|
|
417
|
+
for (const { instance, response } of pairs) {
|
|
418
|
+
const score = scoreIfevalInstance(instance, response);
|
|
419
|
+
if (score.strict.pass) strictPrompts++;
|
|
420
|
+
if (score.loose.pass) loosePrompts++;
|
|
421
|
+
if (score.strict.unhandled.length === 0) fullySupportedPrompts++;
|
|
422
|
+
totalInstructions += instance.instruction_id_list.length;
|
|
423
|
+
|
|
424
|
+
for (let i = 0; i < score.strict.instructionPasses.length; i++) {
|
|
425
|
+
const strict = score.strict.instructionPasses[i];
|
|
426
|
+
if (strict === null) continue;
|
|
427
|
+
supportedInstructions++;
|
|
428
|
+
if (strict) strictInstructions++;
|
|
429
|
+
if (score.loose.instructionPasses[i]) looseInstructions++;
|
|
430
|
+
}
|
|
431
|
+
for (const id of score.strict.unhandled)
|
|
432
|
+
unhandledCounts.set(id, (unhandledCounts.get(id) ?? 0) + 1);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const nTotal = pairs.length;
|
|
436
|
+
const strictAcc = nTotal ? strictPrompts / nTotal : 0;
|
|
437
|
+
const looseAcc = nTotal ? loosePrompts / nTotal : 0;
|
|
438
|
+
return {
|
|
439
|
+
nTotal,
|
|
440
|
+
strictAcc,
|
|
441
|
+
looseAcc,
|
|
442
|
+
accuracy: strictAcc,
|
|
443
|
+
strictInstructionAcc: supportedInstructions
|
|
444
|
+
? strictInstructions / supportedInstructions
|
|
445
|
+
: 0,
|
|
446
|
+
looseInstructionAcc: supportedInstructions
|
|
447
|
+
? looseInstructions / supportedInstructions
|
|
448
|
+
: 0,
|
|
449
|
+
coverage: {
|
|
450
|
+
fullySupportedPrompts,
|
|
451
|
+
promptCoverage: nTotal ? fullySupportedPrompts / nTotal : 0,
|
|
452
|
+
supportedInstructions,
|
|
453
|
+
totalInstructions,
|
|
454
|
+
instructionCoverage: totalInstructions
|
|
455
|
+
? supportedInstructions / totalInstructions
|
|
456
|
+
: 0,
|
|
457
|
+
unhandledInstructionCounts: Object.fromEntries(
|
|
458
|
+
[...unhandledCounts.entries()].sort(([a], [b]) => a.localeCompare(b)),
|
|
459
|
+
),
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
331
464
|
export async function evaluateIfeval(
|
|
332
465
|
tm: TaskModel,
|
|
333
466
|
opts: { nSamples?: number; maxTokens?: number; seed?: number; frozen?: boolean } = {},
|
|
@@ -348,7 +481,7 @@ export async function evaluateIfeval(
|
|
|
348
481
|
|
|
349
482
|
let nStrict = 0;
|
|
350
483
|
let nLoose = 0;
|
|
351
|
-
const
|
|
484
|
+
const pairs: IfevalPair[] = [];
|
|
352
485
|
|
|
353
486
|
for (let k = 0; k < idx.length; k++) {
|
|
354
487
|
const item = rows[idx[k]!]!;
|
|
@@ -356,14 +489,16 @@ export async function evaluateIfeval(
|
|
|
356
489
|
const kwList = item.kwargs ?? iids.map(() => ({}));
|
|
357
490
|
|
|
358
491
|
const raw = await generateText(tm, item.prompt, { maxTokens, useChat: true });
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
492
|
+
const instance: IfevalInstance = {
|
|
493
|
+
prompt: item.prompt,
|
|
494
|
+
instruction_id_list: iids,
|
|
495
|
+
kwargs: kwList,
|
|
496
|
+
};
|
|
497
|
+
const score = scoreIfevalInstance(instance, raw);
|
|
498
|
+
pairs.push({ instance, response: raw });
|
|
364
499
|
|
|
365
|
-
if (strict.pass) nStrict++;
|
|
366
|
-
if (loose.pass) nLoose++;
|
|
500
|
+
if (score.strict.pass) nStrict++;
|
|
501
|
+
if (score.loose.pass) nLoose++;
|
|
367
502
|
|
|
368
503
|
if ((k + 1) % 10 === 0 || k + 1 === idx.length)
|
|
369
504
|
process.stderr.write(
|
|
@@ -372,8 +507,12 @@ export async function evaluateIfeval(
|
|
|
372
507
|
}
|
|
373
508
|
process.stderr.write("\n");
|
|
374
509
|
|
|
375
|
-
|
|
376
|
-
|
|
510
|
+
const result = scoreIfevalPairs(pairs);
|
|
511
|
+
const unhandledCounts = Object.entries(
|
|
512
|
+
result.coverage.unhandledInstructionCounts,
|
|
513
|
+
);
|
|
514
|
+
if (unhandledCounts.length) {
|
|
515
|
+
const top = unhandledCounts.sort((a, b) => b[1] - a[1]);
|
|
377
516
|
process.stderr.write(
|
|
378
517
|
" ifeval unported instruction ids (treated as PASS, per optiq): " +
|
|
379
518
|
top.map(([k, v]) => `${k}(${v})`).join(", ") +
|
|
@@ -381,8 +520,5 @@ export async function evaluateIfeval(
|
|
|
381
520
|
);
|
|
382
521
|
}
|
|
383
522
|
|
|
384
|
-
|
|
385
|
-
const strictAcc = nTotal ? nStrict / nTotal : 0;
|
|
386
|
-
const looseAcc = nTotal ? nLoose / nTotal : 0;
|
|
387
|
-
return { nTotal, strictAcc, looseAcc, accuracy: strictAcc };
|
|
523
|
+
return result;
|
|
388
524
|
}
|