mlx-bun 0.0.13 → 0.2.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.
- package/README.md +31 -21
- package/bin/mlx-bun.mjs +1 -1
- package/docs/{design → archive}/hlg-sampling.md +3 -3
- package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
- package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
- package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
- package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
- package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
- package/docs/design/colibri-glm52-port.md +1 -1
- package/docs/design/compat-cli-surface-design.md +1 -1
- package/docs/design/decode-speed-program.md +1 -1
- package/docs/design/docs-reorg-plan.md +1 -1
- package/docs/design/dspark-serving-program.md +1 -1
- package/docs/design/dspark-speculative-decoding.md +53 -3
- package/docs/design/grammar-spec-batching-integration.md +1 -1
- package/docs/design/mlx-lm-serving-execution-seam.md +81 -0
- package/docs/design/orpo-dynamic-lambda.md +1 -1
- package/docs/design/orpo-future-enhancements.md +1 -1
- package/docs/design/orpo-training.md +2 -2
- package/docs/design/parity-tier-dag.md +1 -1
- package/docs/design/repo-cleanup-plan.md +46 -4
- package/docs/design/segmented-backward-training.md +2 -2
- package/docs/design/ssd-kv-cold-tier.md +127 -0
- package/docs/design/turboquant-weights.md +467 -0
- package/docs/design/w4a16-compute-precision-spike.md +1 -1
- package/docs/design/web-chat-redesign.md +5 -5
- package/docs/planning/chunk-task-roadmap.md +1 -1
- package/docs/planning/curve-sampler-research-plan.md +4 -4
- package/docs/planning/memory-docs-and-dag-plan.md +1 -1
- package/docs/planning/release-notes-v0.2.0.md +49 -0
- package/docs/planning/video-series.md +421 -0
- package/docs/reference/cli.md +15 -4
- package/docs/reference/distribution.md +1 -0
- package/docs/reference/features-matrix.md +3 -2
- package/docs/reference/library-api.md +41 -1
- package/docs/reference/models.md +33 -0
- package/docs/reference/server-api.md +69 -14
- package/docs/reference/server-config.md +57 -33
- package/package.json +2 -2
- package/src/anthropic.ts +92 -3
- package/src/cli.ts +131 -36
- package/src/eval/kl.ts +27 -20
- package/src/eval/runner.ts +23 -4
- package/src/eval/tasks/bfcl.ts +2 -1
- package/src/eval/tasks/gsm8k.ts +2 -1
- package/src/eval/tasks/hashhop.ts +2 -1
- package/src/eval/tasks/humaneval.ts +5 -4
- package/src/eval/tasks/ifeval.ts +2 -1
- package/src/eval/tasks/mmlu.ts +2 -1
- package/src/expert-io.ts +3 -1
- package/src/expert-offload.ts +3 -1
- package/src/expert-trace.ts +3 -1
- package/src/fit.ts +24 -43
- package/src/flags.ts +7 -9
- package/src/generate.ts +107 -92
- package/src/grammar.ts +2 -1
- package/src/harness-pi.ts +1 -1
- package/src/index.ts +20 -0
- package/src/jobs/job-entry.ts +3 -2
- package/src/kv-scheme.ts +199 -0
- package/src/kv-store.ts +370 -205
- package/src/media-fetch.ts +19 -3
- package/src/memory/model.ts +2 -1
- package/src/memory/vault.ts +2 -1
- package/src/memory-plan.ts +18 -0
- package/src/mlx/ffi.ts +8 -11
- package/src/mlx/ops.ts +31 -6
- package/src/model/batched-mask.ts +1 -1
- package/src/model/batched-quant.ts +1 -1
- package/src/model/batched-rotating-quant.ts +43 -97
- package/src/model/batched-rotating-state.ts +73 -0
- package/src/model/batched-rotating.ts +56 -105
- package/src/model/batched-row-storage.ts +127 -0
- package/src/model/compiled-decode.ts +3 -2
- package/src/model/factory.ts +44 -49
- package/src/model/gemma4-base.ts +163 -10
- package/src/model/generated/gemma4-12b.ts +50 -146
- package/src/model/generated/gemma4-26b.ts +32 -92
- package/src/model/generated/gemma4-e4b.ts +26 -74
- package/src/model/glm52-cache.ts +19 -9
- package/src/model/glm52-memory.ts +28 -1
- package/src/model/minicpm5.ts +2 -1
- package/src/model/profile.ts +433 -0
- package/src/model/qwen3-delta.ts +98 -2
- package/src/model/qwen3-mrope.ts +252 -0
- package/src/model/qwen3_5.ts +217 -25
- package/src/native/frame_extract.swift +140 -0
- package/src/native-pack.ts +13 -6
- package/src/pi-web.ts +6 -5
- package/src/prompt-cache.ts +2 -2
- package/src/quantize/atomic-output.ts +38 -0
- package/src/quantize/calibration.ts +2 -1
- package/src/quantize/config-writer.ts +6 -0
- package/src/quantize/index.ts +28 -1
- package/src/quantize/job.ts +11 -0
- package/src/quantize/quantizer.ts +265 -100
- package/src/quantize/rotate.ts +603 -0
- package/src/quantize/safetensors-writer.ts +79 -1
- package/src/quantize/sensitivity.ts +41 -59
- package/src/quantize/weight-transform.ts +292 -0
- package/src/responses.ts +102 -2
- package/src/runtime-config.ts +67 -0
- package/src/sampler.ts +272 -0
- package/src/serve/admin-routes.ts +184 -0
- package/src/serve/aux-routes.ts +132 -0
- package/src/serve/batch-scheduler.ts +200 -87
- package/src/serve/completion-executor.ts +346 -0
- package/src/serve/completion-sink.ts +157 -0
- package/src/serve/discovery-routes.ts +205 -0
- package/src/serve/generation-gateway.ts +116 -109
- package/src/serve/kv-budget.ts +14 -0
- package/src/serve/lab-routes.ts +338 -0
- package/src/serve/model-admin-routes.ts +153 -0
- package/src/serve/prompt-response-trace.ts +147 -0
- package/src/serve/request-plan.ts +158 -0
- package/src/serve/static-routes.ts +101 -0
- package/src/server.ts +704 -1400
- package/src/spec/dflash-source.ts +2 -1
- package/src/spec/dspark/module-dflash.ts +1 -1
- package/src/spec/glm52-mtp-source.ts +8 -0
- package/src/spec/qwen-mtp-source.ts +431 -0
- package/src/spec/serve-loop.ts +36 -68
- package/src/ssd-cache.ts +17 -14
- package/src/ssd-durability.ts +195 -0
- package/src/train/flash-cce.ts +12 -11
- package/src/train/forward.ts +32 -1
- package/src/train/loss.ts +2 -1
- package/src/train/segmented.ts +4 -3
- package/src/train/trainer.ts +3 -2
- package/src/vision/preprocess.ts +1 -1
- package/src/vision/prompt.ts +47 -1
- package/src/vision/qwen3vl-preprocess.ts +409 -0
- package/src/vision/qwen3vl-prompt.ts +196 -0
- package/src/vision/qwen3vl-tower.ts +513 -0
- package/src/vision/video-frames.ts +148 -0
- package/src/web/app.js +10 -3
- package/src/web/src/chat.ts +12 -5
- package/src/weights.ts +81 -26
- /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
- /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
- /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
- /package/docs/{investigations → archive/investigations}/curve-runs/bisector-route.json +0 -0
- /package/docs/{investigations → archive/investigations}/curve-runs/bisector-trace.json +0 -0
- /package/docs/{investigations → archive/investigations}/curve-runs/curve-terrain.json +0 -0
- /package/docs/{investigations → archive/investigations}/curve-runs/curve-terrain.svg +0 -0
- /package/docs/{investigations → archive/investigations}/curve-terrain.html +0 -0
- /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
- /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-ab.html +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-explorer.html +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-figs/cliff.svg +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-figs/cross.svg +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-figs/edges.svg +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-figs/k20-grids.svg +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-figs/k20-peaks.svg +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-figs/lw-curve.svg +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-figs/terrain.svg +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-report.html +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-ab.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-blind-prompts.md +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-blind.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-cliff.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-cross.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-edge.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-eval1.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-frontier.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-grid.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-grid2.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-lw-coarse.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-map.json +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/run0-original.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runA-recommended.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runAA-cliff-cpm5.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runAB-grid2-e4b.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runAC-peak-e4b-partial.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runAD-ab-samples.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runAE-edge-e4b.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runAF-logit-calibration.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runAG-eval-config2.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runB-gain-variants.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runC-pivots.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runD-oetf.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runE-eotf.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runF-shaper-working.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runG-shaper-sweep.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runH-wsweep.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runI-asweep.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runJ-knobmap.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runK-frontier.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runL-widemap.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runM-frontier-emb.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runN-verify.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runO-frontier-seed5000.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runP-grid-AxSm.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runQ-grid-tgW.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runR-blind-transcript.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runS-cross-coherence.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runT-cross-cpm5.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runU-cross-12b.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runV-lw-e4b.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runW-lw-12b.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runX-lw-cpm5.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runY-cliff-e4b.log +0 -0
- /package/docs/{investigations → archive/investigations}/hlg-runs/runZ-cliff-12b.log +0 -0
- /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
- /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
- /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
- /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
- /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
- /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
- /package/docs/{planning → archive/planning}/journal.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.13.md +0 -0
- /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GenerateOptions,
|
|
3
|
+
GenerateStats,
|
|
4
|
+
TokenLogprobs,
|
|
5
|
+
} from "../generate";
|
|
6
|
+
import type {
|
|
7
|
+
GenerationMechanism,
|
|
8
|
+
GenerationPlacement,
|
|
9
|
+
OnToken,
|
|
10
|
+
RequestShape,
|
|
11
|
+
Vision,
|
|
12
|
+
} from "./generation-gateway";
|
|
13
|
+
import {
|
|
14
|
+
type CompletionEvent,
|
|
15
|
+
type CompletionFinishReason,
|
|
16
|
+
type CompletionFlowControl,
|
|
17
|
+
type CompletionResult,
|
|
18
|
+
type CompletionSinkOptions,
|
|
19
|
+
CompletionSink,
|
|
20
|
+
type CompletionToolCall,
|
|
21
|
+
} from "./completion-sink";
|
|
22
|
+
import { recordLane, type Lane } from "./lane-registry";
|
|
23
|
+
import {
|
|
24
|
+
planRequest,
|
|
25
|
+
type RequestPlan,
|
|
26
|
+
type RequestPlanInput,
|
|
27
|
+
type RequestRejection,
|
|
28
|
+
} from "./request-plan";
|
|
29
|
+
import type { PromptResponseTrace } from "./prompt-response-trace";
|
|
30
|
+
|
|
31
|
+
export interface CompletionEngine {
|
|
32
|
+
place(shape: RequestShape): GenerationPlacement;
|
|
33
|
+
run(
|
|
34
|
+
promptIds: number[],
|
|
35
|
+
options: GenerateOptions & { stopSequences?: string[] },
|
|
36
|
+
onToken: OnToken,
|
|
37
|
+
vision: Vision | undefined,
|
|
38
|
+
shape: RequestShape,
|
|
39
|
+
placement: GenerationPlacement,
|
|
40
|
+
signal?: AbortSignal,
|
|
41
|
+
trace?: PromptResponseTrace,
|
|
42
|
+
): Promise<GenerateStats>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CompletionPreparation {
|
|
46
|
+
requestId: string;
|
|
47
|
+
plan: RequestPlanInput;
|
|
48
|
+
vision?: Vision;
|
|
49
|
+
pipeline: Omit<CompletionSinkOptions, "flowControl">;
|
|
50
|
+
createFlowControl?(placement: CompletionPlacement):
|
|
51
|
+
| CompletionSinkOptions["flowControl"];
|
|
52
|
+
onPlacement?(placement: CompletionPlacement): void;
|
|
53
|
+
idToToken(id: number): string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CompletionPlacement {
|
|
57
|
+
mechanism: GenerationMechanism;
|
|
58
|
+
lane: Lane;
|
|
59
|
+
shape: RequestShape;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface PreparedData extends Omit<CompletionPreparation, "plan"> {
|
|
63
|
+
plan: RequestPlan;
|
|
64
|
+
stream: boolean;
|
|
65
|
+
requestedMaxTokens: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const preparedBrand: unique symbol = Symbol("PreparedCompletion");
|
|
69
|
+
|
|
70
|
+
/** A single-use completion declaration. Protocol adapters can create one but
|
|
71
|
+
* cannot inspect or rewrite the resolved execution plan after preparation. */
|
|
72
|
+
export interface PreparedCompletion {
|
|
73
|
+
readonly [preparedBrand]: true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const preparedData = new WeakMap<PreparedCompletion, PreparedData>();
|
|
77
|
+
|
|
78
|
+
export function prepareCompletion(data: CompletionPreparation): PreparedCompletion {
|
|
79
|
+
const planned = planRequest(data.plan);
|
|
80
|
+
if (!planned.ok) {
|
|
81
|
+
planned.dispose();
|
|
82
|
+
throw new CompletionRejected(planned);
|
|
83
|
+
}
|
|
84
|
+
const prepared = Object.freeze({ [preparedBrand]: true }) as PreparedCompletion;
|
|
85
|
+
preparedData.set(prepared, {
|
|
86
|
+
requestId: data.requestId,
|
|
87
|
+
plan: planned,
|
|
88
|
+
stream: data.plan.stream,
|
|
89
|
+
requestedMaxTokens: data.plan.requestedMaxTokens,
|
|
90
|
+
...(data.vision ? { vision: data.vision } : {}),
|
|
91
|
+
pipeline: data.pipeline,
|
|
92
|
+
...(data.createFlowControl
|
|
93
|
+
? { createFlowControl: data.createFlowControl }
|
|
94
|
+
: {}),
|
|
95
|
+
...(data.onPlacement ? { onPlacement: data.onPlacement } : {}),
|
|
96
|
+
idToToken: data.idToToken,
|
|
97
|
+
});
|
|
98
|
+
return prepared;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface CompletionControl {
|
|
102
|
+
signal?: AbortSignal;
|
|
103
|
+
trace?: PromptResponseTrace;
|
|
104
|
+
onEvents?(events: readonly CompletionEvent[]):
|
|
105
|
+
| void
|
|
106
|
+
| false
|
|
107
|
+
| Promise<void | false>;
|
|
108
|
+
/** Receives one live, read-only usage view before execution. Retain the
|
|
109
|
+
* reference to close an errored protocol stream with accumulated usage.
|
|
110
|
+
* The executor updates it in place, avoiding per-token callback/allocation
|
|
111
|
+
* work on the decode path. */
|
|
112
|
+
onUsageProgress?(usage: Readonly<CompletionUsage>): void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface CompletionUsage {
|
|
116
|
+
promptTokens: number;
|
|
117
|
+
cachedTokens: number;
|
|
118
|
+
completionTokens: number;
|
|
119
|
+
totalTokens: number;
|
|
120
|
+
speculation?: GenerateStats["spec"];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface CompletionSummary {
|
|
124
|
+
content: string;
|
|
125
|
+
reasoning: string;
|
|
126
|
+
toolCalls: CompletionToolCall[];
|
|
127
|
+
stopped: boolean;
|
|
128
|
+
finishReason: CompletionFinishReason;
|
|
129
|
+
lane: Lane;
|
|
130
|
+
usage: CompletionUsage;
|
|
131
|
+
logprobs: { content: Record<string, unknown>[] } | null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export class CompletionRejected extends Error {
|
|
135
|
+
readonly status: number;
|
|
136
|
+
readonly error: RequestRejection["error"];
|
|
137
|
+
|
|
138
|
+
constructor(rejection: RequestRejection) {
|
|
139
|
+
super(rejection.error.message);
|
|
140
|
+
this.name = "CompletionRejected";
|
|
141
|
+
this.status = rejection.status;
|
|
142
|
+
this.error = rejection.error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class LogprobsCollector {
|
|
147
|
+
readonly #tokens: number[] = [];
|
|
148
|
+
readonly #tokenLogprobs: number[] = [];
|
|
149
|
+
readonly #topTokens: { id: number; token: string; logprob: number }[][] = [];
|
|
150
|
+
|
|
151
|
+
constructor(
|
|
152
|
+
private readonly wantLogprobs: boolean,
|
|
153
|
+
private readonly topK: number,
|
|
154
|
+
private readonly idToToken: (id: number) => string,
|
|
155
|
+
) {}
|
|
156
|
+
|
|
157
|
+
push(token: number, info?: TokenLogprobs): void {
|
|
158
|
+
this.#tokens.push(token);
|
|
159
|
+
if (this.wantLogprobs) this.#tokenLogprobs.push(info?.logprob ?? NaN);
|
|
160
|
+
if (this.topK > 0) {
|
|
161
|
+
this.#topTokens.push(
|
|
162
|
+
(info?.top ?? []).map((candidate) => ({
|
|
163
|
+
id: candidate.id,
|
|
164
|
+
token: this.idToToken(candidate.id),
|
|
165
|
+
logprob: candidate.logprob,
|
|
166
|
+
})),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
payload(): { content: Record<string, unknown>[] } | null {
|
|
172
|
+
if (this.#topTokens.length) {
|
|
173
|
+
return {
|
|
174
|
+
content: this.#topTokens.map((tokens) =>
|
|
175
|
+
tokens.length ? { ...tokens[0]!, top_logprobs: tokens } : {},
|
|
176
|
+
),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (this.#tokenLogprobs.length) {
|
|
180
|
+
return {
|
|
181
|
+
content: this.#tokens.map((id, index) => ({
|
|
182
|
+
id,
|
|
183
|
+
logprob: this.#tokenLogprobs[index]!,
|
|
184
|
+
})),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function initialLane(mechanism: GenerationMechanism, shape: RequestShape): Lane {
|
|
192
|
+
if (mechanism === "continuous") return "batched";
|
|
193
|
+
return shape.hasDraft ? "serial+spec" : "serial";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function finalLane(
|
|
197
|
+
mechanism: GenerationMechanism,
|
|
198
|
+
lane: Lane,
|
|
199
|
+
stats: GenerateStats,
|
|
200
|
+
): Lane {
|
|
201
|
+
if (mechanism === "continuous") return "batched";
|
|
202
|
+
return stats.spec ? "serial+spec" : lane;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function finishReason(
|
|
206
|
+
result: CompletionResult,
|
|
207
|
+
stats: GenerateStats,
|
|
208
|
+
maxTokens: number,
|
|
209
|
+
): CompletionFinishReason {
|
|
210
|
+
if (result.toolCalls.length) return "tool_calls";
|
|
211
|
+
if (result.stopped) return "stop";
|
|
212
|
+
return stats.generatedTokens >= maxTokens ? "length" : "stop";
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function combineControl(
|
|
216
|
+
sinkControl: CompletionFlowControl,
|
|
217
|
+
eventControl: void | false | Promise<void | false>,
|
|
218
|
+
): void | false | Promise<void | false> {
|
|
219
|
+
if (sinkControl === false || eventControl === false) return false;
|
|
220
|
+
if (!(sinkControl instanceof Promise) && !(eventControl instanceof Promise))
|
|
221
|
+
return undefined;
|
|
222
|
+
return Promise.all([sinkControl, eventControl]).then((values) =>
|
|
223
|
+
values.includes(false) ? false : undefined,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Owns one completion attempt from admission through terminal accounting.
|
|
228
|
+
* Model math and scheduling policy remain in the supplied CompletionEngine. */
|
|
229
|
+
export class CompletionExecutor {
|
|
230
|
+
constructor(private readonly engine: CompletionEngine) {}
|
|
231
|
+
|
|
232
|
+
async execute(
|
|
233
|
+
prepared: PreparedCompletion,
|
|
234
|
+
control: CompletionControl = {},
|
|
235
|
+
): Promise<CompletionSummary> {
|
|
236
|
+
const input = preparedData.get(prepared);
|
|
237
|
+
if (!input) throw new Error("prepared completion has already been executed");
|
|
238
|
+
preparedData.delete(prepared);
|
|
239
|
+
|
|
240
|
+
const planned = input.plan;
|
|
241
|
+
const closeCompletion = control.trace?.begin("completion.total");
|
|
242
|
+
const usageProgress: CompletionUsage | null = control.onUsageProgress
|
|
243
|
+
? {
|
|
244
|
+
promptTokens: planned.promptIds.length,
|
|
245
|
+
cachedTokens: 0,
|
|
246
|
+
completionTokens: 0,
|
|
247
|
+
totalTokens: planned.promptIds.length,
|
|
248
|
+
}
|
|
249
|
+
: null;
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
if (usageProgress) control.onUsageProgress!(usageProgress);
|
|
253
|
+
const closePlacement = control.trace?.begin("completion.placement");
|
|
254
|
+
const enginePlacement = this.engine.place(planned.shape);
|
|
255
|
+
closePlacement?.();
|
|
256
|
+
if (enginePlacement.shape !== planned.shape)
|
|
257
|
+
throw new Error("generation placement does not belong to this request shape");
|
|
258
|
+
const mechanism = enginePlacement.mechanism;
|
|
259
|
+
const lane = initialLane(mechanism, planned.shape);
|
|
260
|
+
recordLane(input.requestId, lane);
|
|
261
|
+
const placement = { mechanism, lane, shape: planned.shape };
|
|
262
|
+
input.onPlacement?.(placement);
|
|
263
|
+
const flowControl = input.createFlowControl?.(placement);
|
|
264
|
+
const sink = new CompletionSink({
|
|
265
|
+
...input.pipeline,
|
|
266
|
+
...(flowControl ? { flowControl } : {}),
|
|
267
|
+
});
|
|
268
|
+
const logprobs = planned.captureLogprobs
|
|
269
|
+
? new LogprobsCollector(
|
|
270
|
+
planned.wantLogprobs,
|
|
271
|
+
planned.topLogprobs,
|
|
272
|
+
input.idToToken,
|
|
273
|
+
)
|
|
274
|
+
: null;
|
|
275
|
+
control.signal?.throwIfAborted();
|
|
276
|
+
planned.transferOwnership();
|
|
277
|
+
|
|
278
|
+
const consumeToken: OnToken = (token, tokenLogprobs) => {
|
|
279
|
+
logprobs?.push(token, tokenLogprobs);
|
|
280
|
+
const pushed = sink.push(token);
|
|
281
|
+
const observed = pushed.events.length
|
|
282
|
+
? control.onEvents?.(pushed.events)
|
|
283
|
+
: undefined;
|
|
284
|
+
return combineControl(pushed.control, observed);
|
|
285
|
+
};
|
|
286
|
+
const onToken: OnToken = usageProgress
|
|
287
|
+
? (token, tokenLogprobs) => {
|
|
288
|
+
usageProgress.completionTokens++;
|
|
289
|
+
usageProgress.totalTokens++;
|
|
290
|
+
return consumeToken(token, tokenLogprobs);
|
|
291
|
+
}
|
|
292
|
+
: consumeToken;
|
|
293
|
+
const stats = await this.engine.run(
|
|
294
|
+
planned.promptIds,
|
|
295
|
+
planned.options,
|
|
296
|
+
onToken,
|
|
297
|
+
input.vision,
|
|
298
|
+
planned.shape,
|
|
299
|
+
enginePlacement,
|
|
300
|
+
control.signal,
|
|
301
|
+
control.trace,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
if (input.stream) control.signal?.throwIfAborted();
|
|
305
|
+
const result = sink.finish();
|
|
306
|
+
if (result.events.length) await control.onEvents?.(result.events);
|
|
307
|
+
|
|
308
|
+
const resolvedLane = finalLane(mechanism, lane, stats);
|
|
309
|
+
if (resolvedLane !== lane) recordLane(input.requestId, resolvedLane);
|
|
310
|
+
if (usageProgress) {
|
|
311
|
+
usageProgress.promptTokens = stats.promptTokens;
|
|
312
|
+
usageProgress.cachedTokens = stats.cachedTokens;
|
|
313
|
+
usageProgress.completionTokens = stats.generatedTokens;
|
|
314
|
+
usageProgress.totalTokens = stats.promptTokens + stats.generatedTokens;
|
|
315
|
+
if (stats.spec) usageProgress.speculation = stats.spec;
|
|
316
|
+
else delete usageProgress.speculation;
|
|
317
|
+
}
|
|
318
|
+
const summary = {
|
|
319
|
+
content: result.content,
|
|
320
|
+
reasoning: result.reasoning,
|
|
321
|
+
toolCalls: result.toolCalls,
|
|
322
|
+
stopped: result.stopped,
|
|
323
|
+
finishReason: finishReason(
|
|
324
|
+
result,
|
|
325
|
+
stats,
|
|
326
|
+
planned.options.maxTokens ?? input.requestedMaxTokens,
|
|
327
|
+
),
|
|
328
|
+
lane: resolvedLane,
|
|
329
|
+
usage: {
|
|
330
|
+
promptTokens: stats.promptTokens,
|
|
331
|
+
cachedTokens: stats.cachedTokens,
|
|
332
|
+
completionTokens: stats.generatedTokens,
|
|
333
|
+
totalTokens: stats.promptTokens + stats.generatedTokens,
|
|
334
|
+
...(stats.spec ? { speculation: stats.spec } : {}),
|
|
335
|
+
},
|
|
336
|
+
logprobs: logprobs?.payload() ?? null,
|
|
337
|
+
};
|
|
338
|
+
closeCompletion?.();
|
|
339
|
+
return summary;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
closeCompletion?.();
|
|
342
|
+
planned.dispose();
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
export interface CompletionToolCall {
|
|
2
|
+
id: string;
|
|
3
|
+
type: "function";
|
|
4
|
+
function: { name: string; arguments: string };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type CompletionEvent =
|
|
8
|
+
| { type: "content"; text: string }
|
|
9
|
+
| { type: "reasoning"; text: string }
|
|
10
|
+
| { type: "tool_calls"; calls: CompletionToolCall[] };
|
|
11
|
+
|
|
12
|
+
export type CompletionFlowControl = false | Promise<void> | undefined;
|
|
13
|
+
|
|
14
|
+
export interface CompletionPush {
|
|
15
|
+
events: CompletionEvent[];
|
|
16
|
+
control: CompletionFlowControl;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CompletionResult {
|
|
20
|
+
events: CompletionEvent[];
|
|
21
|
+
content: string;
|
|
22
|
+
reasoning: string;
|
|
23
|
+
toolCalls: CompletionToolCall[];
|
|
24
|
+
stopped: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type CompletionFinishReason = "stop" | "length" | "tool_calls";
|
|
28
|
+
|
|
29
|
+
export interface CompletionStreamProtocol {
|
|
30
|
+
start(): string[];
|
|
31
|
+
addEvents(events: CompletionEvent[]): string[];
|
|
32
|
+
finish(reason: CompletionFinishReason, usage: Record<string, unknown>): string[];
|
|
33
|
+
error(message: string): string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TokenTextRouter {
|
|
37
|
+
push(token: number): string;
|
|
38
|
+
flush(): string;
|
|
39
|
+
takeReasoning(): string;
|
|
40
|
+
toolCalls(): CompletionToolCall[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface TextStopper {
|
|
44
|
+
readonly stopped: boolean;
|
|
45
|
+
push(text: string): string;
|
|
46
|
+
flush(): string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ThinkingSplitter {
|
|
50
|
+
push(text: string): { content: string; reasoning: string };
|
|
51
|
+
flush(): { content: string; reasoning: string };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface CompletionSinkOptions {
|
|
55
|
+
router: TokenTextRouter;
|
|
56
|
+
stopper: TextStopper;
|
|
57
|
+
thinking: ThinkingSplitter;
|
|
58
|
+
collectToolCalls: boolean;
|
|
59
|
+
flowControl?: (emitted: boolean) => CompletionFlowControl;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** One generated-token sink for every text-serving surface.
|
|
63
|
+
*
|
|
64
|
+
* Chat uses the full router → stop matcher → thinking splitter stack.
|
|
65
|
+
* Text completions use the same sink with a plain router, thinking disabled,
|
|
66
|
+
* and tool-call collection off. `push()` returns both semantic events and the
|
|
67
|
+
* generation callback's control signal: false halts decode; a Promise yields
|
|
68
|
+
* before the next token. */
|
|
69
|
+
export class CompletionSink {
|
|
70
|
+
readonly #router: TokenTextRouter;
|
|
71
|
+
readonly #stopper: TextStopper;
|
|
72
|
+
readonly #thinking: ThinkingSplitter;
|
|
73
|
+
readonly #collectToolCalls: boolean;
|
|
74
|
+
readonly #flowControl?: (emitted: boolean) => CompletionFlowControl;
|
|
75
|
+
#content = "";
|
|
76
|
+
#reasoning = "";
|
|
77
|
+
|
|
78
|
+
constructor(options: CompletionSinkOptions) {
|
|
79
|
+
this.#router = options.router;
|
|
80
|
+
this.#stopper = options.stopper;
|
|
81
|
+
this.#thinking = options.thinking;
|
|
82
|
+
this.#collectToolCalls = options.collectToolCalls;
|
|
83
|
+
this.#flowControl = options.flowControl;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get stopped(): boolean {
|
|
87
|
+
return this.#stopper.stopped;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
push(token: number): CompletionPush {
|
|
91
|
+
const events: CompletionEvent[] = [];
|
|
92
|
+
const rawContent = this.#router.push(token);
|
|
93
|
+
this.#emitReasoning(events, this.#router.takeReasoning());
|
|
94
|
+
this.#emitParts(events, this.#thinking.push(this.#stopper.push(rawContent)));
|
|
95
|
+
return {
|
|
96
|
+
events,
|
|
97
|
+
control: this.#stopper.stopped
|
|
98
|
+
? false
|
|
99
|
+
: this.#flowControl?.(events.length > 0),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
finish(): CompletionResult {
|
|
104
|
+
const events: CompletionEvent[] = [];
|
|
105
|
+
if (!this.#stopper.stopped) {
|
|
106
|
+
const flushed = this.#router.flush();
|
|
107
|
+
this.#emitReasoning(events, this.#router.takeReasoning());
|
|
108
|
+
let tail = this.#stopper.push(flushed);
|
|
109
|
+
if (!this.#stopper.stopped) tail += this.#stopper.flush();
|
|
110
|
+
this.#emitParts(events, this.#thinking.push(tail));
|
|
111
|
+
}
|
|
112
|
+
this.#emitParts(events, this.#thinking.flush());
|
|
113
|
+
|
|
114
|
+
const toolCalls = this.#collectToolCalls ? this.#router.toolCalls() : [];
|
|
115
|
+
if (toolCalls.length) events.push({ type: "tool_calls", calls: toolCalls });
|
|
116
|
+
return {
|
|
117
|
+
events,
|
|
118
|
+
content: this.#content,
|
|
119
|
+
reasoning: this.#reasoning,
|
|
120
|
+
toolCalls,
|
|
121
|
+
stopped: this.#stopper.stopped,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#emitParts(
|
|
126
|
+
events: CompletionEvent[],
|
|
127
|
+
parts: { content: string; reasoning: string },
|
|
128
|
+
): void {
|
|
129
|
+
this.#emitReasoning(events, parts.reasoning);
|
|
130
|
+
if (parts.content) {
|
|
131
|
+
this.#content += parts.content;
|
|
132
|
+
events.push({ type: "content", text: parts.content });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#emitReasoning(events: CompletionEvent[], text: string): void {
|
|
137
|
+
if (!text) return;
|
|
138
|
+
this.#reasoning += text;
|
|
139
|
+
events.push({ type: "reasoning", text });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function createTimedFlowControl(
|
|
144
|
+
enabled: boolean,
|
|
145
|
+
intervalMs = 25,
|
|
146
|
+
now: () => number = () => performance.now(),
|
|
147
|
+
yieldTask: () => Promise<void> = () => new Promise<void>((resolve) => setImmediate(resolve)),
|
|
148
|
+
): (emitted: boolean) => CompletionFlowControl {
|
|
149
|
+
let lastYield = now();
|
|
150
|
+
return (emitted) => {
|
|
151
|
+
if (!enabled || !emitted) return undefined;
|
|
152
|
+
const current = now();
|
|
153
|
+
if (current - lastYield < intervalMs) return undefined;
|
|
154
|
+
lastYield = current;
|
|
155
|
+
return yieldTask();
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import pkgJson from "../../package.json" with { type: "json" };
|
|
2
|
+
import type { ServerContext } from "../server";
|
|
3
|
+
import type { GenerationGateway } from "./generation-gateway";
|
|
4
|
+
import { fit } from "../fit";
|
|
5
|
+
import { isEmbeddingModel } from "../embed";
|
|
6
|
+
import { Glm52Model } from "../model/glm52";
|
|
7
|
+
|
|
8
|
+
const pkgVersion = (pkgJson as { version: string }).version;
|
|
9
|
+
|
|
10
|
+
export type DiscoveryRoute =
|
|
11
|
+
| "library"
|
|
12
|
+
| "downloads"
|
|
13
|
+
| "api-index"
|
|
14
|
+
| "health"
|
|
15
|
+
| "models";
|
|
16
|
+
|
|
17
|
+
export function matchDiscoveryRoute(method: string, pathname: string): DiscoveryRoute | null {
|
|
18
|
+
if (method !== "GET") return null;
|
|
19
|
+
switch (pathname) {
|
|
20
|
+
case "/library": return "library";
|
|
21
|
+
case "/downloads": return "downloads";
|
|
22
|
+
case "/v1": return "api-index";
|
|
23
|
+
case "/health": return "health";
|
|
24
|
+
case "/v1/models": return "models";
|
|
25
|
+
default: return pathname.startsWith("/v1/models/") ? "models" : null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DiscoveryRoutes {
|
|
30
|
+
handle(url: URL, request: Request): Promise<Response | null>;
|
|
31
|
+
invalidateLibrary(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createDiscoveryRoutes(
|
|
35
|
+
ctx: ServerContext,
|
|
36
|
+
gateway: Pick<GenerationGateway, "batchMode">,
|
|
37
|
+
startedAt: number,
|
|
38
|
+
): DiscoveryRoutes {
|
|
39
|
+
let libraryCache: { at: number; rows: unknown[] } | null = null;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
invalidateLibrary() {
|
|
43
|
+
libraryCache = null;
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async handle(url, request) {
|
|
47
|
+
switch (matchDiscoveryRoute(request.method, url.pathname)) {
|
|
48
|
+
case "library": {
|
|
49
|
+
if (!libraryCache || Date.now() - libraryCache.at > 30_000) {
|
|
50
|
+
const { Registry, visionCapable, audioCapable } = await import("../registry");
|
|
51
|
+
const { loadModelConfig } = await import("../config");
|
|
52
|
+
const { supportTier } = await import("../model/support");
|
|
53
|
+
const registry = new Registry();
|
|
54
|
+
await registry.scan();
|
|
55
|
+
const rows = [];
|
|
56
|
+
for (const model of registry.listCanonical()) {
|
|
57
|
+
const tier = supportTier(model.modelType, model.repoId);
|
|
58
|
+
const supported = tier !== null;
|
|
59
|
+
let assessment = null;
|
|
60
|
+
try {
|
|
61
|
+
const config = await loadModelConfig(model.path);
|
|
62
|
+
const result = fit(
|
|
63
|
+
config,
|
|
64
|
+
model.sizeBytes,
|
|
65
|
+
8192,
|
|
66
|
+
undefined,
|
|
67
|
+
undefined,
|
|
68
|
+
model.expertsBytes,
|
|
69
|
+
);
|
|
70
|
+
assessment = {
|
|
71
|
+
fits: result.fits,
|
|
72
|
+
max_safe_context: result.maxSafeContext,
|
|
73
|
+
predicted_decode_tps: result.predictedDecodeTps,
|
|
74
|
+
};
|
|
75
|
+
} catch {}
|
|
76
|
+
rows.push({
|
|
77
|
+
repo_id: model.repoId,
|
|
78
|
+
model_type: model.modelType,
|
|
79
|
+
size_bytes: model.sizeBytes,
|
|
80
|
+
quant_bits: model.quantBits,
|
|
81
|
+
vision: visionCapable(model),
|
|
82
|
+
audio: audioCapable(model),
|
|
83
|
+
supported,
|
|
84
|
+
support_tier: tier,
|
|
85
|
+
serving: model.repoId === ctx.modelId,
|
|
86
|
+
assessment,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
libraryCache = { at: Date.now(), rows };
|
|
90
|
+
}
|
|
91
|
+
return Response.json({ models: libraryCache.rows });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
case "downloads": {
|
|
95
|
+
const { downloadsSnapshot } = await import("../download");
|
|
96
|
+
return Response.json({ downloads: downloadsSnapshot() });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
case "api-index":
|
|
100
|
+
return Response.json({
|
|
101
|
+
name: "mlx-bun",
|
|
102
|
+
version: pkgVersion,
|
|
103
|
+
model: ctx.modelId,
|
|
104
|
+
endpoints: [
|
|
105
|
+
"POST /v1/chat/completions",
|
|
106
|
+
"POST /v1/completions",
|
|
107
|
+
"POST /v1/messages",
|
|
108
|
+
"POST /v1/responses",
|
|
109
|
+
"POST /v1/embeddings",
|
|
110
|
+
"GET /v1/models",
|
|
111
|
+
"GET/POST/DELETE /v1/adapters",
|
|
112
|
+
"GET /health",
|
|
113
|
+
"GET /stats",
|
|
114
|
+
"GET /fit",
|
|
115
|
+
"GET /library",
|
|
116
|
+
"GET /downloads",
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
case "health":
|
|
121
|
+
return new Response('{"status": "ok"}', {
|
|
122
|
+
headers: { "content-type": "application/json" },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
case "models": {
|
|
126
|
+
const filterId = url.pathname.length > "/v1/models/".length - 1
|
|
127
|
+
? decodeURIComponent(url.pathname.slice("/v1/models/".length))
|
|
128
|
+
: null;
|
|
129
|
+
const created = Math.floor(startedAt / 1000);
|
|
130
|
+
const genDefaults = {
|
|
131
|
+
temperature: ctx.genDefaults.temperature ?? null,
|
|
132
|
+
top_p: ctx.genDefaults.topP ?? null,
|
|
133
|
+
top_k: ctx.genDefaults.topK ?? null,
|
|
134
|
+
};
|
|
135
|
+
const isGlm52 = ctx.model instanceof Glm52Model;
|
|
136
|
+
const data: Array<Record<string, unknown>> = [{
|
|
137
|
+
id: ctx.modelId,
|
|
138
|
+
object: "model",
|
|
139
|
+
created,
|
|
140
|
+
owned_by: "mlx-bun",
|
|
141
|
+
context_window:
|
|
142
|
+
ctx.glmMemoryPlan?.contextTokens ??
|
|
143
|
+
ctx.model.config.text.maxPositionEmbeddings,
|
|
144
|
+
reasoning: ctx.template.supportsThinking,
|
|
145
|
+
vision: !!(ctx.vision || ctx.loadVision),
|
|
146
|
+
audio: !!(ctx.audio || ctx.loadAudio),
|
|
147
|
+
batch_mode: gateway.batchMode,
|
|
148
|
+
tools: true,
|
|
149
|
+
structured_output: true,
|
|
150
|
+
embeddings: isEmbeddingModel(ctx.model),
|
|
151
|
+
adapters: !isGlm52,
|
|
152
|
+
training: !isGlm52,
|
|
153
|
+
dsa: ctx.model instanceof Glm52Model && ctx.model.capabilities.dsa,
|
|
154
|
+
mtp: ctx.draft?.provider.id === "glm52-native-mtp",
|
|
155
|
+
capabilities: {
|
|
156
|
+
chat_completions: true,
|
|
157
|
+
text_completions: true,
|
|
158
|
+
anthropic_messages: true,
|
|
159
|
+
responses: true,
|
|
160
|
+
streaming: true,
|
|
161
|
+
tools: true,
|
|
162
|
+
structured_output: true,
|
|
163
|
+
logprobs: true,
|
|
164
|
+
embeddings: isEmbeddingModel(ctx.model),
|
|
165
|
+
vision: !!(ctx.vision || ctx.loadVision),
|
|
166
|
+
audio: !!(ctx.audio || ctx.loadAudio),
|
|
167
|
+
adapters: !isGlm52,
|
|
168
|
+
training: !isGlm52,
|
|
169
|
+
},
|
|
170
|
+
gen_defaults: genDefaults,
|
|
171
|
+
}];
|
|
172
|
+
try {
|
|
173
|
+
const { Registry, visionCapable } = await import("../registry");
|
|
174
|
+
const { supportTier } = await import("../model/support");
|
|
175
|
+
const registry = new Registry();
|
|
176
|
+
try {
|
|
177
|
+
if (registry.list().length === 0) await registry.scan();
|
|
178
|
+
for (const model of registry.listCanonical()) {
|
|
179
|
+
if (model.repoId === ctx.modelId) continue;
|
|
180
|
+
const tier = supportTier(model.modelType, model.repoId);
|
|
181
|
+
if (tier === null) continue;
|
|
182
|
+
data.push({
|
|
183
|
+
id: model.repoId,
|
|
184
|
+
object: "model",
|
|
185
|
+
created,
|
|
186
|
+
vision: visionCapable(model),
|
|
187
|
+
tier,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
} finally {
|
|
191
|
+
registry.close();
|
|
192
|
+
}
|
|
193
|
+
} catch {}
|
|
194
|
+
return Response.json({
|
|
195
|
+
object: "list",
|
|
196
|
+
data: filterId ? data.filter((model) => model.id === filterId) : data,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
default:
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|