mlx-bun 0.0.11 → 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 +43 -13
- package/THIRD_PARTY_LICENSES.md +9 -0
- package/docs/design/colibri-glm52-port.md +1479 -0
- package/docs/design/pre-colibri-stabilization.md +329 -0
- package/docs/investigations/colibri-oracle-pin.md +1284 -0
- package/docs/investigations/indexshare-performance-spike.md +357 -0
- package/docs/planning/release-notes-v0.0.12.md +80 -0
- package/docs/reference/cli.md +28 -2
- package/docs/reference/distribution.md +25 -1
- package/docs/reference/embedding.md +5 -2
- package/docs/reference/features-matrix.md +4 -2
- package/docs/reference/library-api.md +41 -13
- package/docs/reference/models.md +49 -2
- package/docs/reference/server-api.md +90 -9
- package/docs/reference/server-config.md +21 -14
- package/docs/reference/training.md +10 -1
- package/package.json +1 -1
- package/src/anthropic.ts +54 -44
- package/src/chat-template.ts +155 -10
- package/src/cli.ts +127 -8
- package/src/config.ts +71 -26
- package/src/diffusion/diffusion-generate.ts +14 -8
- package/src/download.ts +103 -12
- package/src/eval/ifeval.ts +63 -120
- package/src/eval/runner.ts +14 -5
- package/src/eval/tasks/ifeval.ts +167 -31
- package/src/expert-io.ts +391 -0
- package/src/expert-residency.ts +1134 -0
- package/src/expert-usage.ts +415 -0
- package/src/generate.ts +114 -17
- package/src/hf-push.ts +1 -1
- package/src/index.ts +14 -2
- package/src/jobs/runner.ts +37 -18
- package/src/kv-store.ts +147 -8
- package/src/lora.ts +77 -14
- package/src/media-fetch.ts +198 -56
- package/src/mlx/array.ts +43 -0
- package/src/mlx/autograd.ts +51 -14
- package/src/mlx/checkpoint.ts +46 -13
- package/src/mlx/custom-vjp.ts +83 -35
- package/src/model/factory.ts +96 -5
- package/src/model/gemma4-base.ts +32 -0
- 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/support.ts +8 -1
- package/src/native/expert_io.c +523 -0
- package/src/native-pack.ts +6 -4
- package/src/responses.ts +62 -49
- package/src/serve/batch-scheduler.ts +95 -16
- package/src/serve/generation-gateway.ts +41 -16
- package/src/server.ts +362 -81
- package/src/spec/glm52-mtp-source.ts +392 -0
- package/src/spec/serve-loop.ts +99 -18
- package/src/spec/source.ts +31 -6
- package/src/ssd-cache.ts +6 -2
- package/src/tool-call.ts +30 -2
- package/src/train/job.ts +0 -10
- package/src/train/lora-params.ts +17 -6
- package/src/train/trainer.ts +59 -26
- package/src/web/app.html +13 -2
- package/src/web/app.js +30 -3
- package/src/web/src/status.ts +51 -3
- package/src/web-tools.ts +54 -2
- package/src/weights.ts +82 -19
package/src/mlx/custom-vjp.ts
CHANGED
|
@@ -15,6 +15,24 @@ import { JSCallback, ptr, read } from "bun:ffi";
|
|
|
15
15
|
import { C, takeMlxError } from "./ffi";
|
|
16
16
|
import { MlxArray } from "./array";
|
|
17
17
|
|
|
18
|
+
type CallbackHandle = Pick<JSCallback, "ptr" | "close">;
|
|
19
|
+
type NativeCall<F extends (...args: any[]) => any> =
|
|
20
|
+
(...args: Parameters<F>) => ReturnType<F>;
|
|
21
|
+
|
|
22
|
+
/** Constructor-only dependency seam for deterministic native failure tests. */
|
|
23
|
+
export interface CustomVjpConstructorHooks {
|
|
24
|
+
callback?: (
|
|
25
|
+
callback: ConstructorParameters<typeof JSCallback>[0],
|
|
26
|
+
definition: ConstructorParameters<typeof JSCallback>[1],
|
|
27
|
+
) => CallbackHandle;
|
|
28
|
+
forwardClosureNew?: NativeCall<typeof C.mlx_closure_new_func_payload>;
|
|
29
|
+
vjpClosureNew?: NativeCall<typeof C.mlx_closure_custom_new_func_payload>;
|
|
30
|
+
transformedNew?: NativeCall<typeof C.mlx_closure_new>;
|
|
31
|
+
transform?: NativeCall<typeof C.mlx_custom_vjp>;
|
|
32
|
+
closureFree?: NativeCall<typeof C.mlx_closure_free>;
|
|
33
|
+
customClosureFree?: NativeCall<typeof C.mlx_closure_custom_free>;
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
function readVec(vec: bigint): MlxArray[] {
|
|
19
37
|
const n = Number(C.mlx_vector_array_size(vec));
|
|
20
38
|
const out: MlxArray[] = [];
|
|
@@ -41,19 +59,30 @@ function setVec(outPtr: number, arrays: MlxArray[]): void {
|
|
|
41
59
|
* pure functions of their arguments — the vjp re-reads everything from the
|
|
42
60
|
* graph during the enclosing backward. */
|
|
43
61
|
export class CustomVjp {
|
|
44
|
-
readonly #fwdCb:
|
|
45
|
-
readonly #vjpCb:
|
|
62
|
+
readonly #fwdCb: CallbackHandle;
|
|
63
|
+
readonly #vjpCb: CallbackHandle;
|
|
46
64
|
readonly #fwdClosure: bigint;
|
|
47
65
|
readonly #vjpClosure: bigint;
|
|
48
66
|
readonly #combined: bigint;
|
|
67
|
+
readonly #freeClosure: NativeCall<typeof C.mlx_closure_free>;
|
|
68
|
+
readonly #freeCustomClosure: NativeCall<typeof C.mlx_closure_custom_free>;
|
|
49
69
|
#error: string | null = null;
|
|
50
70
|
#disposed = false;
|
|
51
71
|
|
|
52
72
|
constructor(
|
|
53
73
|
fwd: (inputs: MlxArray[]) => MlxArray[],
|
|
54
74
|
vjp: (primals: MlxArray[], cotangents: MlxArray[], outputs: MlxArray[]) => MlxArray[],
|
|
75
|
+
hooks: CustomVjpConstructorHooks = {},
|
|
55
76
|
) {
|
|
56
|
-
|
|
77
|
+
const makeCallback = hooks.callback ?? ((callback, definition) => new JSCallback(callback, definition));
|
|
78
|
+
const forwardClosureNew = hooks.forwardClosureNew ?? C.mlx_closure_new_func_payload;
|
|
79
|
+
const vjpClosureNew = hooks.vjpClosureNew ?? C.mlx_closure_custom_new_func_payload;
|
|
80
|
+
const transformedNew = hooks.transformedNew ?? C.mlx_closure_new;
|
|
81
|
+
const transform = hooks.transform ?? C.mlx_custom_vjp;
|
|
82
|
+
this.#freeClosure = hooks.closureFree ?? C.mlx_closure_free;
|
|
83
|
+
this.#freeCustomClosure = hooks.customClosureFree ?? C.mlx_closure_custom_free;
|
|
84
|
+
|
|
85
|
+
const fwdCb = makeCallback(
|
|
57
86
|
(outPtr: number, inVec: bigint, _payload: number): number => {
|
|
58
87
|
try {
|
|
59
88
|
const inputs = readVec(inVec);
|
|
@@ -70,37 +99,56 @@ export class CustomVjp {
|
|
|
70
99
|
{ args: ["ptr", "u64", "ptr"], returns: "i32" },
|
|
71
100
|
);
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
let vjpCb: CallbackHandle;
|
|
103
|
+
try {
|
|
104
|
+
vjpCb = makeCallback(
|
|
105
|
+
(resPtr: number, primalsVec: bigint, cotsVec: bigint, outsVec: bigint, _payload: number): number => {
|
|
106
|
+
try {
|
|
107
|
+
const primals = readVec(primalsVec);
|
|
108
|
+
const cots = readVec(cotsVec);
|
|
109
|
+
const outs = readVec(outsVec);
|
|
110
|
+
const grads = vjp(primals, cots, outs);
|
|
111
|
+
setVec(resPtr, grads);
|
|
112
|
+
for (const a of [...primals, ...cots, ...outs]) a.dispose();
|
|
113
|
+
for (const g of grads) g.dispose();
|
|
114
|
+
return 0;
|
|
115
|
+
} catch (e) {
|
|
116
|
+
this.#error = e instanceof Error ? e.message : String(e);
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{ args: ["ptr", "u64", "u64", "u64", "ptr"], returns: "i32" },
|
|
121
|
+
);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
// Callback construction is itself fallible. The native-closure guard
|
|
124
|
+
// below starts only after both callbacks exist, so this partial state
|
|
125
|
+
// must release the already-created forward callback here.
|
|
126
|
+
fwdCb.close();
|
|
127
|
+
throw e;
|
|
128
|
+
}
|
|
94
129
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
130
|
+
let fwdClosure: bigint | null = null;
|
|
131
|
+
let vjpClosure: bigint | null = null;
|
|
132
|
+
let slot: BigUint64Array | null = null;
|
|
133
|
+
try {
|
|
134
|
+
fwdClosure = forwardClosureNew(fwdCb.ptr as never, null, null);
|
|
135
|
+
vjpClosure = vjpClosureNew(vjpCb.ptr as never, null, null);
|
|
136
|
+
slot = new BigUint64Array([transformedNew()]);
|
|
137
|
+
if (transform(ptr(slot), fwdClosure, vjpClosure) !== 0)
|
|
138
|
+
throw new Error(`mlx_custom_vjp failed: ${takeMlxError() ?? ""}`);
|
|
139
|
+
this.#fwdCb = fwdCb;
|
|
140
|
+
this.#vjpCb = vjpCb;
|
|
141
|
+
this.#fwdClosure = fwdClosure;
|
|
142
|
+
this.#vjpClosure = vjpClosure;
|
|
143
|
+
this.#combined = read.u64(ptr(slot), 0);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
if (slot) this.#freeClosure(read.u64(ptr(slot), 0));
|
|
146
|
+
if (fwdClosure !== null) this.#freeClosure(fwdClosure);
|
|
147
|
+
if (vjpClosure !== null) this.#freeCustomClosure(vjpClosure);
|
|
148
|
+
fwdCb.close();
|
|
149
|
+
vjpCb.close();
|
|
150
|
+
throw e;
|
|
102
151
|
}
|
|
103
|
-
this.#combined = read.u64(ptr(slot), 0);
|
|
104
152
|
}
|
|
105
153
|
|
|
106
154
|
apply(inputs: MlxArray[]): MlxArray[] {
|
|
@@ -127,9 +175,9 @@ export class CustomVjp {
|
|
|
127
175
|
dispose(): void {
|
|
128
176
|
if (this.#disposed) return;
|
|
129
177
|
this.#disposed = true;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
178
|
+
this.#freeClosure(this.#combined);
|
|
179
|
+
this.#freeClosure(this.#fwdClosure);
|
|
180
|
+
this.#freeCustomClosure(this.#vjpClosure);
|
|
133
181
|
this.#fwdCb.close();
|
|
134
182
|
this.#vjpCb.close();
|
|
135
183
|
}
|
package/src/model/factory.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
// Gemma4Model and only override forwardLayers (with their own
|
|
5
5
|
// cache-signature guard), so the choice is always safe.
|
|
6
6
|
|
|
7
|
-
import type
|
|
8
|
-
import
|
|
7
|
+
import { loadModelConfig, type ModelConfig } from "../config";
|
|
8
|
+
import { totalmem } from "node:os";
|
|
9
|
+
import { Weights } from "../weights";
|
|
9
10
|
import { Gemma4Model } from "./gemma4";
|
|
10
11
|
import { configFingerprint } from "./fingerprint";
|
|
11
12
|
import { GENERATED } from "./generated";
|
|
@@ -14,19 +15,109 @@ import { Qwen35Model } from "./qwen3_5";
|
|
|
14
15
|
import { Qwen3Model } from "./qwen3";
|
|
15
16
|
import { Qwen3MoeModel } from "./qwen3-moe";
|
|
16
17
|
import { DiffusionGemmaModel } from "./diffusion-gemma";
|
|
18
|
+
import { Glm52Model } from "./glm52";
|
|
19
|
+
import {
|
|
20
|
+
GLM52_G5_DEFAULT_CONTEXT_TOKENS,
|
|
21
|
+
GLM52_G5_DEFAULT_MAX_GENERATION_TOKENS,
|
|
22
|
+
GLM52_G5_DEFAULT_PROCESS_LIMIT_BYTES,
|
|
23
|
+
planGlm52MemoryForArtifact,
|
|
24
|
+
type Glm52MemoryPlan,
|
|
25
|
+
} from "./glm52-memory";
|
|
17
26
|
import { UniversalDenseModel } from "./universal/dense";
|
|
18
27
|
import { genericArgsFor, GENERIC_MODEL_TYPES, remapModelType } from "./universal/archs";
|
|
19
|
-
import { isDiffusionGemmaConfig, isMiniCPM5Config, isQwen35Config, isQwen3Config, isQwen3MoeConfig } from "./support";
|
|
28
|
+
import { isDiffusionGemmaConfig, isGlm52Config, isMiniCPM5Config, isQwen35Config, isQwen3Config, isQwen3MoeConfig } from "./support";
|
|
20
29
|
|
|
21
30
|
export type RuntimeModel =
|
|
22
31
|
| Gemma4Model | MiniCPM5Model | Qwen35Model | Qwen3Model | Qwen3MoeModel
|
|
23
|
-
| DiffusionGemmaModel | UniversalDenseModel;
|
|
32
|
+
| DiffusionGemmaModel | Glm52Model | UniversalDenseModel;
|
|
33
|
+
|
|
34
|
+
export interface Glm52RuntimeOpenOptions {
|
|
35
|
+
/** Whole-process ceiling. Defaults to the smaller of the validated 25 GiB
|
|
36
|
+
* preset and physical RAM. */
|
|
37
|
+
memoryBudgetBytes?: number;
|
|
38
|
+
/** Context reserved by the exact GLM resource equation. */
|
|
39
|
+
contextTokens?: number;
|
|
40
|
+
/** Generated-token allowance within contextTokens. */
|
|
41
|
+
maxGenerationTokens?: number;
|
|
42
|
+
/** Maximum ordinary continuous-batch rows. */
|
|
43
|
+
batchSize?: number;
|
|
44
|
+
/** Native checkpoint MTP row; on by default. */
|
|
45
|
+
enableMtp?: boolean;
|
|
46
|
+
mtpDraftTokens?: number;
|
|
47
|
+
/** Test/diagnostic override; normal callers use physical RAM. */
|
|
48
|
+
machineBytes?: number;
|
|
49
|
+
/** Native expert-I/O dylib override. Packaged/dev defaults are automatic. */
|
|
50
|
+
libraryPath?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Open the direct Colibri artifact through the validated bounded expert
|
|
54
|
+
* runtime. The header-only plan runs before any resident tensor or expert slab
|
|
55
|
+
* is opened, so an impossible configuration fails without committing memory. */
|
|
56
|
+
export async function openGlm52RuntimeModel(
|
|
57
|
+
modelDir: string,
|
|
58
|
+
options: Glm52RuntimeOpenOptions = {},
|
|
59
|
+
): Promise<{ model: Glm52Model; plan: Glm52MemoryPlan }> {
|
|
60
|
+
const machineBytes = options.machineBytes ?? totalmem();
|
|
61
|
+
const processLimitBytes = options.memoryBudgetBytes ??
|
|
62
|
+
Math.min(GLM52_G5_DEFAULT_PROCESS_LIMIT_BYTES, machineBytes);
|
|
63
|
+
const plan = await planGlm52MemoryForArtifact(modelDir, {
|
|
64
|
+
machineBytes,
|
|
65
|
+
processLimitBytes,
|
|
66
|
+
contextTokens: options.contextTokens ?? GLM52_G5_DEFAULT_CONTEXT_TOKENS,
|
|
67
|
+
maxGenerationTokens:
|
|
68
|
+
options.maxGenerationTokens ?? GLM52_G5_DEFAULT_MAX_GENERATION_TOKENS,
|
|
69
|
+
batchSize: options.batchSize ?? 1,
|
|
70
|
+
enableMtp: options.enableMtp !== false,
|
|
71
|
+
mtpDraftTokens: options.mtpDraftTokens,
|
|
72
|
+
});
|
|
73
|
+
const model = await Glm52Model.openStreamed(modelDir, {
|
|
74
|
+
budgetBytes: plan.processLimitBytes,
|
|
75
|
+
reserveBytes: plan.runtimeReserveBytes,
|
|
76
|
+
workingSlots: plan.mainWorkingSlots,
|
|
77
|
+
maxSlotsPerLayer: 1,
|
|
78
|
+
workers: 2,
|
|
79
|
+
libraryPath: options.libraryPath,
|
|
80
|
+
decodeKernel: "metal",
|
|
81
|
+
enableMtp: plan.enableMtp,
|
|
82
|
+
mtpDraftTokens: plan.mtpDraftTokens,
|
|
83
|
+
});
|
|
84
|
+
if (model.expertRuntime?.plan.plannedBytes !== plan.plannedProcessBytes) {
|
|
85
|
+
const actual = model.expertRuntime?.plan.plannedBytes ?? 0;
|
|
86
|
+
model.dispose();
|
|
87
|
+
throw new Error(
|
|
88
|
+
`GLM runtime plan ${actual} != preflight resource equation ` +
|
|
89
|
+
`${plan.plannedProcessBytes}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return { model, plan };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Artifact-aware construction. GLM-5.2's Colibri snapshot has no ordinary
|
|
97
|
+
* model.safetensors.index.json, so it must bypass Weights.open and use its
|
|
98
|
+
* dedicated header catalog/tensor source.
|
|
99
|
+
*/
|
|
100
|
+
export async function openModel(
|
|
101
|
+
modelDir: string,
|
|
102
|
+
options: Glm52RuntimeOpenOptions = {},
|
|
103
|
+
): Promise<RuntimeModel> {
|
|
104
|
+
const config = await loadModelConfig(modelDir);
|
|
105
|
+
if (isGlm52Config(config))
|
|
106
|
+
return (await openGlm52RuntimeModel(modelDir, options)).model;
|
|
107
|
+
return createModel(await Weights.open(modelDir), config);
|
|
108
|
+
}
|
|
24
109
|
|
|
25
110
|
/** Dispatch ladder (docs/design/generic-model-support.md §3.3):
|
|
26
111
|
* dedicated → generated/monolith (gemma4*) → generic (Tier-0 universal)
|
|
27
112
|
* → reject with a helpful error. Generic never shadows a dedicated port. */
|
|
28
113
|
export function createModel(weights: Weights, config: ModelConfig): RuntimeModel {
|
|
29
114
|
// 1. Dedicated classes.
|
|
115
|
+
if (isGlm52Config(config)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
"glm_moe_dsa uses the direct Colibri container; construct it with " +
|
|
118
|
+
"openModel(modelDir) or Glm52Model.open(modelDir)",
|
|
119
|
+
);
|
|
120
|
+
}
|
|
30
121
|
// DiffusionGemma is non-autoregressive — generate() detects it and routes to
|
|
31
122
|
// the denoising engine instead of the AR decode loop. It exposes the shared
|
|
32
123
|
// RuntimeModel surface (config/weightsBytes/loraState/makeCache) with the
|
|
@@ -54,7 +145,7 @@ export function createModel(weights: Weights, config: ModelConfig): RuntimeModel
|
|
|
54
145
|
throw new Error(
|
|
55
146
|
`unsupported model_type "${config.modelType}"` +
|
|
56
147
|
(arch !== config.modelType ? ` (mlx-lm remaps it to "${arch}")` : "") +
|
|
57
|
-
` — targeted: gemma4*, diffusion_gemma, qwen3_5, qwen3, qwen3_moe, MiniCPM5;` +
|
|
148
|
+
` — targeted: gemma4*, diffusion_gemma, glm_moe_dsa, qwen3_5, qwen3, qwen3_moe, MiniCPM5;` +
|
|
58
149
|
` generic (Tier-0): ${[...GENERIC_MODEL_TYPES].sort().join(", ")}`,
|
|
59
150
|
);
|
|
60
151
|
}
|
package/src/model/gemma4-base.ts
CHANGED
|
@@ -234,6 +234,38 @@ export interface Cache {
|
|
|
234
234
|
dispose(): void;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Capability contract for cache layouts that own their dynamic-row batching.
|
|
239
|
+
* The scheduler uses this for non-K/V state families (for example GLM's
|
|
240
|
+
* compressed MLA/DSA state) instead of teaching the scheduler their tensor
|
|
241
|
+
* layout. `mergeRows` writes into an empty cache and does not consume inputs;
|
|
242
|
+
* extracted rows are independent owned caches.
|
|
243
|
+
*/
|
|
244
|
+
export interface BatchableCache extends Cache {
|
|
245
|
+
readonly batchSize: number | null;
|
|
246
|
+
readonly rowOffsets: readonly number[];
|
|
247
|
+
readonly leftPad: readonly number[];
|
|
248
|
+
readonly maxTokens?: number;
|
|
249
|
+
makeEmptyBatch(): BatchableCache;
|
|
250
|
+
mergeRows(rows: readonly Cache[]): void;
|
|
251
|
+
extractRow(row: number): Cache;
|
|
252
|
+
filterRows(keep: readonly number[]): void;
|
|
253
|
+
projectedBytes(tokens: number): number;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function isBatchableCache(cache: Cache): cache is BatchableCache {
|
|
257
|
+
const candidate = cache as Partial<BatchableCache>;
|
|
258
|
+
return (
|
|
259
|
+
typeof candidate.makeEmptyBatch === "function" &&
|
|
260
|
+
typeof candidate.mergeRows === "function" &&
|
|
261
|
+
typeof candidate.extractRow === "function" &&
|
|
262
|
+
typeof candidate.filterRows === "function" &&
|
|
263
|
+
typeof candidate.projectedBytes === "function" &&
|
|
264
|
+
Array.isArray(candidate.rowOffsets) &&
|
|
265
|
+
Array.isArray(candidate.leftPad)
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
237
269
|
/** How one decode step interacts with a cache under compiled decode
|
|
238
270
|
* (returned by prepareDecodeStep, consumed by compiled-decode.ts):
|
|
239
271
|
* - "concat": the compiled graph fetches concat(active prefix, new kv)
|