mlx-bun 0.2.0 → 0.3.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 +71 -588
- package/docs/README.md +91 -0
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/design/batching.md +569 -0
- package/docs/design/decode-speed-program.md +3178 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +579 -334
- package/docs/design/kv-cache.md +680 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1627 -0
- package/docs/design/turboquant.md +2102 -0
- package/docs/design/unified-engine-frontier-plan.md +1831 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +34 -1
- package/docs/planning/release-notes-v0.3.0.md +35 -0
- package/docs/planning/video-series.md +2 -2
- package/docs/reference/benchmarks.md +1162 -0
- package/docs/reference/cli.md +394 -159
- package/docs/reference/distribution.md +321 -182
- package/docs/reference/environment.md +348 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +566 -168
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +104 -6
- package/docs/reference/server-api.md +886 -425
- package/docs/reference/server-config.md +638 -330
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +166 -0
- package/package.json +9 -4
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1646 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +47 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/gateway-binding.ts +71 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/memory-guard.ts +40 -0
- package/src/backends/mlx/methods.ts +90 -0
- package/src/backends/mlx/model-prompt.ts +164 -0
- package/src/backends/mlx/model-serving.ts +79 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/rollback.ts +51 -0
- package/src/backends/mlx/serial-executor.ts +273 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-views.ts +26 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/cli.ts +58 -25
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +49 -0
- package/src/contracts/generation.ts +103 -0
- package/src/contracts/host.ts +7 -0
- package/src/contracts/jobs.ts +66 -0
- package/src/contracts/pi-provider.ts +30 -0
- package/src/contracts/pi.ts +259 -0
- package/src/contracts/resources.ts +18 -0
- package/src/contracts/scheduling.ts +27 -0
- package/src/contracts/tsconfig.json +14 -0
- package/src/diffusion/diffusion-generate.ts +218 -153
- package/src/embed.ts +1 -1
- package/src/engine/admission.ts +59 -0
- package/src/engine/cancellation.ts +38 -0
- package/src/engine/engine.ts +56 -0
- package/src/engine/execution-coordinator.ts +70 -0
- package/src/engine/execution-plan.ts +51 -0
- package/src/engine/resources.ts +42 -0
- package/src/engine/scheduler.ts +67 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +236 -3
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +8 -96
- package/src/fill/echo-index.ts +188 -0
- package/src/fill/fill-session.ts +452 -0
- package/src/fill/proposal.ts +68 -0
- package/src/fill/schema-rows.ts +506 -0
- package/src/fill/tool-boundary.ts +103 -0
- package/src/fit.ts +1 -1
- package/src/generate.ts +656 -268
- package/src/grammar.ts +12 -7
- package/src/harness-pi.ts +14 -33
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +7 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/graph.ts +33 -0
- package/src/inference/prefill.ts +39 -0
- package/src/inference/rollback.ts +7 -0
- package/src/jobs/db.ts +4 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-entry.ts +12 -12
- package/src/jobs/runner.ts +98 -13
- package/src/jobs/task-client.ts +19 -0
- package/src/jobs/types.ts +2 -58
- package/src/kv-store.ts +109 -49
- package/src/lab/README.md +18 -0
- package/src/{assets → lab/curve}/curve-designer.html +2 -2
- package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
- package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
- package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +1 -1
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +30 -169
- package/src/memory/prompts.ts +1 -1
- package/src/memory/rest.ts +1 -1
- package/src/memory/schedule.ts +1 -1
- package/src/memory/stages.ts +4 -1
- package/src/memory/vault.ts +1 -3
- package/src/mlx/array.ts +9 -10
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +58 -11
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +33 -13
- package/src/mlx/quantized-rows.ts +36 -0
- package/src/mlx/token-bitmask.ts +52 -0
- package/src/mlx/turboquant-kv-decode.ts +82 -0
- package/src/mlx/turboquant-kv-inverse.ts +122 -0
- package/src/mlx/turboquant-ops.ts +33 -3
- package/src/mlx/turboquant-tables.ts +2 -2
- package/src/model/batched-mask.ts +8 -2
- package/src/model/batched-quant.ts +1 -1
- package/src/model/batched-rotating-quant.ts +2 -2
- package/src/model/batched-rotating.ts +1 -1
- package/src/model/compiled-decode.ts +1 -1
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +76 -33
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/gemma4-base.ts +26 -9
- package/src/model/gemma4.ts +6 -6
- package/src/model/generated/gemma4-12b.ts +2 -2
- package/src/model/generated/gemma4-26b.ts +2 -2
- package/src/model/generated/gemma4-e4b.ts +2 -2
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/implementation.ts +69 -0
- package/src/model/minicpm5.ts +6 -8
- package/src/model/profile.ts +12 -2
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +140 -56
- package/src/model/support.ts +1 -1
- package/src/model/trellis-balanced-scatter.ts +51 -0
- package/src/model/trellis-linear.ts +672 -0
- package/src/model/trellis-shared-m.ts +151 -0
- package/src/model/trellis-shared-scatter.ts +144 -0
- package/src/model/trellis-splitk-prefill.ts +158 -0
- package/src/model/trellis-tiled-prefill.ts +136 -0
- package/src/model/trellis-vector-expand.ts +62 -0
- package/src/model/trellis-wide-prefill.ts +94 -0
- package/src/model/universal/dense.ts +1 -1
- package/src/model/universal/modules.ts +8 -1
- package/src/native-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/prompt-cache.ts +96 -53
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/config-writer.ts +10 -3
- package/src/quantize/rotate.ts +4 -4
- package/src/quantize/trellis.ts +586 -0
- package/src/responses.ts +28 -1
- package/src/runtime-config.ts +18 -4
- package/src/sampler.ts +4 -4
- package/src/serve/batch-scheduler.ts +8 -1671
- package/src/serve/chat-request.ts +391 -0
- package/src/serve/chat-stage.ts +195 -0
- package/src/serve/checkpoint-identity.ts +64 -0
- package/src/serve/completion-executor.ts +15 -7
- package/src/serve/discovery-routes.ts +12 -13
- package/src/serve/generation-gateway.ts +154 -193
- package/src/serve/http.ts +159 -0
- package/src/serve/inference-request.ts +79 -0
- package/src/serve/isolate.ts +297 -105
- package/src/serve/lab-routes.ts +3 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +7 -5
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +512 -0
- package/src/serve/openai-wire.ts +103 -0
- package/src/serve/parent-application.ts +56 -0
- package/src/serve/pipeline.ts +33 -0
- package/src/serve/preparation.ts +33 -0
- package/src/serve/request-plan.ts +19 -15
- package/src/serve/request-prep.ts +346 -0
- package/src/serve/response-lease.ts +24 -0
- package/src/serve/responses-client.ts +69 -0
- package/src/serve/session-completion-engine.ts +110 -0
- package/src/serve/session-search.ts +1 -1
- package/src/serve/static-routes.ts +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +317 -2649
- package/src/spec/assistant-source.ts +10 -44
- package/src/spec/deepspec-source.ts +6 -6
- package/src/spec/dflash-source.ts +8 -8
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +2 -2
- package/src/spec/dspark/generate-dflash.ts +2 -2
- package/src/spec/dspark/generate.ts +2 -2
- package/src/spec/dspark/loader.ts +1 -1
- package/src/spec/dspark/module-dflash.ts +4 -3
- package/src/spec/dspark/module.ts +3 -3
- package/src/spec/dspark/quantize-drafter.ts +1 -1
- package/src/spec/generate.ts +1 -1
- package/src/spec/glm52-mtp-source.ts +1 -1
- package/src/spec/ngram-source.ts +1 -1
- package/src/spec/prefix-state.ts +45 -0
- package/src/spec/qwen-mtp-source.ts +166 -69
- package/src/spec/serve-loop.ts +154 -156
- package/src/spec/source.ts +42 -10
- package/src/spec/two-model.ts +1 -1
- package/src/ssd-cache.ts +80 -12
- package/src/ssd-durability.ts +25 -13
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +2 -2
- package/src/train/forward.ts +6 -0
- package/src/train/loss.ts +1 -1
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +5 -3
- package/src/train/trainer.ts +6 -6
- package/src/vision/prompt.ts +1 -1
- package/src/vision/qwen3vl-tower.ts +52 -10
- package/src/web/app.html +1 -1
- package/src/web/app.js +5 -3
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +4 -4
- package/src/web/src/composer.ts +4 -4
- package/src/web/src/hub.ts +1 -1
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +1 -1
- package/src/web/src/model-picker.ts +1 -1
- package/src/web/src/palette.ts +1 -1
- package/src/web/src/protocol.ts +3 -39
- package/src/web/src/sessions.ts +1 -1
- package/src/web/src/ui-catalog.ts +4 -18
- package/src/web-assets.ts +47 -0
- package/src/weights.ts +55 -9
- package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/archive/investigations/hlg-ab.html +0 -34
- package/docs/archive/investigations/hlg-explorer.html +0 -238
- package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
- package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/archive/investigations/hlg-report.html +0 -262
- package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/archive/mlx-bun-lab-report.html +0 -275
- package/docs/archive/wwdc-mlx-bun.html +0 -848
- package/docs/design/adapters-end-to-end.md +0 -205
- package/docs/design/batching-perf-path.md +0 -255
- package/docs/design/batching-v2-plan.md +0 -529
- package/docs/design/bucketing-stage.md +0 -197
- package/docs/design/compat-cli-surface-design.md +0 -230
- package/docs/design/docs-reorg-plan.md +0 -165
- package/docs/design/dspark-serving-program.md +0 -371
- package/docs/design/dspark-speculative-decoding.md +0 -318
- package/docs/design/grammar-spec-batching-integration.md +0 -301
- package/docs/design/memory-inference-path.md +0 -422
- package/docs/design/memory-synthesis.md +0 -287
- package/docs/design/memory-system.md +0 -237
- package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
- package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
- package/docs/design/omlx-adoption-map.md +0 -98
- package/docs/design/orpo-dynamic-lambda.md +0 -220
- package/docs/design/orpo-future-enhancements.md +0 -340
- package/docs/design/paged-kv-cache.md +0 -214
- package/docs/design/parallel-slots.md +0 -567
- package/docs/design/parity-tier-dag.md +0 -161
- package/docs/design/repo-cleanup-plan.md +0 -231
- package/docs/design/runtime-isolation.md +0 -263
- package/docs/design/segmented-backward-training.md +0 -543
- package/docs/design/spec-decode-larger-targets.md +0 -229
- package/docs/design/ssd-kv-cold-tier.md +0 -415
- package/docs/design/structured-output.md +0 -484
- package/docs/design/tauri-desktop-app.md +0 -127
- package/docs/design/the-dreaming-master-plan.md +0 -445
- package/docs/design/turboquant-kv.md +0 -235
- package/docs/design/turboquant-weights.md +0 -467
- package/docs/design/w4a16-compute-precision-spike.md +0 -232
- package/docs/design/web-chat-beat-matrix.md +0 -315
- package/docs/design/write-pipeline-entity.md +0 -468
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -101
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -11
- /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
- /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
- /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
- /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
- /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
- /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Packed Trellis prefill with MLX 0.32.2 GemvWide arithmetic.
|
|
2
|
+
// Ported from ml-explore/mlx v0.32.2, mlx/backend/metal/kernels/gemv.h
|
|
3
|
+
// (MIT). Decode weights to bf16 inside the native dot/unroll/shuffle order;
|
|
4
|
+
// share each decoded row across every input vector in this narrow regime.
|
|
5
|
+
import { MetalKernel } from "../mlx/metal-kernel";
|
|
6
|
+
import { type MlxArray } from "../mlx/array";
|
|
7
|
+
import { Dtype, deviceArchitecture } from "../mlx/ffi";
|
|
8
|
+
import { type TrellisGeometry } from "./trellis-linear";
|
|
9
|
+
|
|
10
|
+
/** Matches the bundled runtime's native reduction on M3 and newer GPUs. */
|
|
11
|
+
export function nativeTrellisWidePrefill(m: number): boolean {
|
|
12
|
+
return m >= 5 && m <= 15 &&
|
|
13
|
+
Number(/^applegpu_[a-z](\d{2})/.exec(deviceArchitecture())?.[1] ?? 0) >= 15;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function wideTrellisPrefillEligible(g: TrellisGeometry, m: number, dtype: Dtype): boolean {
|
|
17
|
+
return dtype === Dtype.bfloat16 && nativeTrellisWidePrefill(m) &&
|
|
18
|
+
g.axis === 1 && g.T === 256 && g.L === 12 && [2, 3, 4].includes(g.k) &&
|
|
19
|
+
g.inFeatures === 5120 && g.outFeatures === 17408 && !g.blockInterleave;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let kernel: MetalKernel | undefined;
|
|
23
|
+
function wideKernel() {
|
|
24
|
+
return kernel ??= new MetalKernel({ name: 'mlx_bun_trellis_wide_prefill',
|
|
25
|
+
inputNames: ['x', 'codes', 'scales'], outputNames: ['out'], source: String.raw`
|
|
26
|
+
constexpr uint unroll=8, rows_per_simd=32/KL, simdgroups=KL/8;
|
|
27
|
+
const uint lane=thread_index_in_simdgroup, sg=simdgroup_index_in_threadgroup;
|
|
28
|
+
const uint kl=lane%KL, row=threadgroup_position_in_grid.y*4+sg*rows_per_simd+lane/KL;
|
|
29
|
+
const uint vec0=threadgroup_position_in_grid.x*SM;
|
|
30
|
+
constexpr uint wpb=BT*BITS/32, row_words=KC*BITS/32;
|
|
31
|
+
const float scale=float(scales[row]);
|
|
32
|
+
constexpr uint n_v4=KC/4, n_main=n_v4-n_v4%(KL*unroll);
|
|
33
|
+
float result[SM]={0};
|
|
34
|
+
for(uint base=0;base<n_main;base+=KL*unroll) {
|
|
35
|
+
float4 wf[unroll];
|
|
36
|
+
#pragma clang loop unroll(full)
|
|
37
|
+
for(uint i=0;i<unroll;i++) {
|
|
38
|
+
#pragma clang loop unroll(full)
|
|
39
|
+
for(uint c=0;c<4;c++) {
|
|
40
|
+
const uint k=(base+i*KL+kl)*4+c, block=k/BT, t=k%BT;
|
|
41
|
+
const device uint* bw=codes+(ulong)row*row_words+block*wpb;
|
|
42
|
+
const uint pos=(BT-1-t)*BITS, wi=pos>>5, off=pos&31u;
|
|
43
|
+
uint win=bw[wi]>>off;
|
|
44
|
+
if(off+L>32)win|=bw[wi+1==wpb?0:wi+1]<<(32-off);
|
|
45
|
+
win&=(1u<<L)-1;
|
|
46
|
+
const uint z=win*34038481u+76625530u;
|
|
47
|
+
const uint p=(z&0x00FF00FFu)+((z>>8)&0x00FF00FFu);
|
|
48
|
+
const int code=int((p&0xFFFFu)+(p>>16))-510;
|
|
49
|
+
const float reciprocal=1.0f/147.800537109375f;
|
|
50
|
+
const float approx=float(code)*reciprocal;
|
|
51
|
+
const float residual=metal::fma(-approx,147.800537109375f,float(code));
|
|
52
|
+
const float value=metal::fma(residual,reciprocal,approx);
|
|
53
|
+
wf[i][c]=float(T(value*scale));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
#pragma clang loop unroll(full)
|
|
57
|
+
for(uint v=0;v<SM;v++) {
|
|
58
|
+
const uint m=min(vec0+v,uint(M-1));
|
|
59
|
+
float acc=0;
|
|
60
|
+
#pragma clang loop unroll(full)
|
|
61
|
+
for(uint i=0;i<unroll;i++) {
|
|
62
|
+
const ulong offset=(ulong)m*KC+(base+i*KL+kl)*4;
|
|
63
|
+
const float4 xv=float4(float(x[offset]),float(x[offset+1]),float(x[offset+2]),float(x[offset+3]));
|
|
64
|
+
acc+=metal::dot(wf[i],xv);
|
|
65
|
+
}
|
|
66
|
+
result[v]+=acc;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
#pragma clang loop unroll(full)
|
|
70
|
+
for(uint v=0;v<SM;v++) {
|
|
71
|
+
#pragma clang loop unroll(full)
|
|
72
|
+
for(ushort off=KL/2;off>=1;off>>=1) result[v]+=simd_shuffle_down(result[v],off);
|
|
73
|
+
if(kl==0 && vec0+v<M) out[(ulong)(vec0+v)*N+row]=T(result[v]);
|
|
74
|
+
}
|
|
75
|
+
` });
|
|
76
|
+
}
|
|
77
|
+
/** Requires the caller to prove aligned, row-contiguous x, as Qwen does
|
|
78
|
+
* with its RMSNorm output. Preserve native fallback when that proof is absent. */
|
|
79
|
+
export function wideTrellisPrefill(x: MlxArray, codes: MlxArray, scales: MlxArray, g: TrellisGeometry): MlxArray {
|
|
80
|
+
const [m, k] = x.shape;
|
|
81
|
+
if (x.ndim !== 2 || x.dtype !== Dtype.bfloat16 || !m || m < 5 || m > 15 ||
|
|
82
|
+
k !== 5120 || g.inFeatures !== k || g.axis !== 1 || g.T !== 256 || g.L !== 12 ||
|
|
83
|
+
![2, 3, 4].includes(g.k) || g.blockInterleave || g.rows < 4 || g.rows % 4 ||
|
|
84
|
+
g.outFeatures !== g.rows || g.cols !== k || codes.dtype !== Dtype.uint32 ||
|
|
85
|
+
codes.ndim !== 2 || codes.shape[0] !== g.rows || codes.shape[1] !== k * g.k / 32 || scales.size !== g.rows)
|
|
86
|
+
throw new Error("unsupported wide trellis prefill geometry, dtype or layout");
|
|
87
|
+
const kl = m <= 5 ? 32 : 16;
|
|
88
|
+
return wideKernel().apply([x, codes, scales], {
|
|
89
|
+
outputs: [{ shape: [m, g.outFeatures], dtype: x.dtype }],
|
|
90
|
+
grid: [32, g.outFeatures / 4 * (kl / 8), 1], threadGroup: [32, kl / 8, 1],
|
|
91
|
+
templateDtypes: { T: x.dtype },
|
|
92
|
+
templateInts: { M: m, SM: m, KL: kl, KC: k, N: g.outFeatures, BT: g.T, L: g.L, BITS: g.k },
|
|
93
|
+
})[0]!;
|
|
94
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// like qwen3.ts, with every arch delta selected by the UniversalArgs
|
|
4
4
|
// descriptor (archs.ts). Monolith path only: no compiled
|
|
5
5
|
// decode, no kv-quant — slow, never broken. The bar is L1 bit-exactness
|
|
6
|
-
// vs mlx-lm on this machine's GPU (tests/universal-parity.test.ts).
|
|
6
|
+
// vs mlx-lm on this machine's GPU (tests/parity/universal-parity.test.ts).
|
|
7
7
|
//
|
|
8
8
|
// Porting discipline: each branch transcribes its mlx-lm source op-for-op
|
|
9
9
|
// (q/k-norm before vs after reshape, rope order, scale points, softcap,
|
|
@@ -11,6 +11,7 @@ import type { Weights } from "../../weights";
|
|
|
11
11
|
import { MlxArray } from "../../mlx/array";
|
|
12
12
|
import * as ops from "../../mlx/ops";
|
|
13
13
|
import { QuantizedEmbedding, QuantizedLinear, RMSNorm } from "../gemma4-base";
|
|
14
|
+
import { TrellisLinear } from "../trellis-linear";
|
|
14
15
|
|
|
15
16
|
/** Records every tensor a universal load consumes; `finish` diffs against
|
|
16
17
|
* the shard index so a descriptor mistake is a LOAD error (unconsumed /
|
|
@@ -66,7 +67,7 @@ export class DenseLinear {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
/** Either linear flavor behind one call surface. */
|
|
69
|
-
export type AnyLinear = QuantizedLinear | DenseLinear;
|
|
70
|
+
export type AnyLinear = QuantizedLinear | DenseLinear | TrellisLinear;
|
|
70
71
|
|
|
71
72
|
/** Quantized when `.scales` exists (MLX-quantized checkpoints), else dense
|
|
72
73
|
* bf16/f16/f32 (Phase 1.5). Both flavors carry the optional ADDITIVE
|
|
@@ -74,6 +75,12 @@ export type AnyLinear = QuantizedLinear | DenseLinear;
|
|
|
74
75
|
export function loadLinear(
|
|
75
76
|
weights: Weights, path: string, config: ModelConfig, audit: WeightAudit,
|
|
76
77
|
): AnyLinear {
|
|
78
|
+
if (TrellisLinear.isTrellis(config, path)) {
|
|
79
|
+
const lin = TrellisLinear.load(weights, path, config);
|
|
80
|
+
audit.use(`${path}.weight`);
|
|
81
|
+
audit.use(`${path}.scales`);
|
|
82
|
+
return lin;
|
|
83
|
+
}
|
|
77
84
|
if (weights.has(`${path}.scales`)) {
|
|
78
85
|
const lin = QuantizedLinear.load(weights, path, config);
|
|
79
86
|
audit.use(`${path}.weight`);
|
package/src/native-pack.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//
|
|
9
9
|
// Resolution order for the libs (mirrored in src/mlx/ffi.ts):
|
|
10
10
|
// MLX_BUN_LIBMLXC env > beside the executable (sidecar dist/ layout,
|
|
11
|
-
// docs/reference/
|
|
11
|
+
// docs/reference/distribution.md) > the native-pack cache dir > homebrew.
|
|
12
12
|
// Dev trees and embedders therefore never download anything.
|
|
13
13
|
|
|
14
14
|
import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
|
|
@@ -17,22 +17,23 @@ import { dirname, join } from "node:path";
|
|
|
17
17
|
import { downloadOne } from "./download";
|
|
18
18
|
import { runtimeValue } from "./runtime-config";
|
|
19
19
|
|
|
20
|
-
export const NATIVE_PACK_VERSION = "0.
|
|
20
|
+
export const NATIVE_PACK_VERSION = "0.4.0";
|
|
21
|
+
export const MLX_CORE_VERSION = "0.32.2";
|
|
21
22
|
export const NATIVE_PACK_FILES = [
|
|
22
23
|
"libmlxc.dylib", "libmlx.dylib", "libjaccl.dylib", "mlx.metallib",
|
|
23
24
|
"libmlx_bun_expert_io.dylib", "mlx-bun-frame-extract",
|
|
24
25
|
] as const;
|
|
25
26
|
|
|
26
27
|
// RELEASE-TIME BAKE: scripts/build-native-pack.sh prints the sha256/size for
|
|
27
|
-
// the freshly built tarball — publish `native-v0.
|
|
28
|
+
// the freshly built tarball — publish `native-v0.4.0` with both assets and
|
|
28
29
|
// paste the values here BEFORE tagging the package release (the download
|
|
29
30
|
// 404s until the GitHub release exists; dev trees resolve via homebrew/env
|
|
30
31
|
// and never hit this path).
|
|
31
32
|
const SHA256: Record<string, string> = {
|
|
32
|
-
arm64: "
|
|
33
|
+
arm64: "f2b0857658aa0826aee14b94a6b83d4c86c08f2c07d716991de6afd038f5ca5d",
|
|
33
34
|
};
|
|
34
35
|
const SIZE: Record<string, number> = {
|
|
35
|
-
arm64:
|
|
36
|
+
arm64: 41931583,
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
export function nativePackName(arch = process.arch): string {
|
package/src/pi-provider.ts
CHANGED
|
@@ -17,37 +17,11 @@ import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
|
17
17
|
* derive it from the registry's `find()` return type. */
|
|
18
18
|
export type PiModel = NonNullable<ReturnType<ModelRegistry["find"]>>;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
export
|
|
25
|
-
export const PI_API = "openai-completions" as const;
|
|
26
|
-
|
|
27
|
-
export const DEFAULT_CONTEXT_WINDOW = 32_768;
|
|
28
|
-
export const DEFAULT_MAX_TOKENS = 8_192;
|
|
29
|
-
|
|
30
|
-
export interface PiProviderOptions {
|
|
31
|
-
/** Advertised context window. Default: DEFAULT_CONTEXT_WINDOW. */
|
|
32
|
-
contextWindow?: number;
|
|
33
|
-
/** Advertised max output tokens. Default: DEFAULT_MAX_TOKENS. */
|
|
34
|
-
maxTokens?: number;
|
|
35
|
-
/** Human-readable model label (e.g. the real repo id). Default: "mlx-bun (local)". */
|
|
36
|
-
name?: string;
|
|
37
|
-
/** The loaded model has a switchable reasoning channel (server's
|
|
38
|
-
* `ctx.template.supportsThinking` — true for any model whose chat template
|
|
39
|
-
* gates on enable_thinking: Qwen3.5, MiniCPM5, …). When true Pi engages
|
|
40
|
-
* reasoning and sends the on/off switch the server maps to enable_thinking.
|
|
41
|
-
* Default false. */
|
|
42
|
-
reasoning?: boolean;
|
|
43
|
-
/** The loaded model can accept images (server's `ctx.vision != null`).
|
|
44
|
-
* This MUST match the real model: pi-ai serializes user content as OpenAI
|
|
45
|
-
* multimodal content-parts (`[{type:"text",…}]`) for image-capable models
|
|
46
|
-
* and as a plain string otherwise. A text-only chat template renders nothing
|
|
47
|
-
* for a parts array, so declaring a non-vision model as image-capable makes
|
|
48
|
-
* the user's turn vanish ("I don't see any message"). Default false. */
|
|
49
|
-
vision?: boolean;
|
|
50
|
-
}
|
|
20
|
+
import { PI_PROVIDER_ID, PI_API_KEY, PI_API, PI_LOCAL_MODEL_ID, piModelDefinition,
|
|
21
|
+
type PiModelOptions } from "./contracts/pi-provider";
|
|
22
|
+
export { PI_PROVIDER_ID, PI_API_KEY, PI_API, PI_LOCAL_MODEL_ID,
|
|
23
|
+
DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_TOKENS } from "./contracts/pi-provider";
|
|
24
|
+
export interface PiProviderOptions extends PiModelOptions {}
|
|
51
25
|
|
|
52
26
|
export interface PiProviderWiring {
|
|
53
27
|
authStorage: AuthStorage;
|
|
@@ -70,39 +44,7 @@ export function buildPiProvider(baseUrl: string, opts: PiProviderOptions = {}):
|
|
|
70
44
|
baseUrl,
|
|
71
45
|
apiKey: PI_API_KEY,
|
|
72
46
|
api: PI_API,
|
|
73
|
-
models: [
|
|
74
|
-
{
|
|
75
|
-
id: PI_LOCAL_MODEL_ID,
|
|
76
|
-
name: opts.name ?? "mlx-bun (local)",
|
|
77
|
-
api: PI_API,
|
|
78
|
-
reasoning: opts.reasoning ?? false,
|
|
79
|
-
// Compat flags that make pi emit the wire format our local server's
|
|
80
|
-
// chat templates expect (docs/custom-provider.md "Model Definition
|
|
81
|
-
// Reference"):
|
|
82
|
-
// - supportsDeveloperRole:false → pi sends the system prompt as the
|
|
83
|
-
// `system` role, not `developer` (our templates only know
|
|
84
|
-
// system/user/assistant/tool).
|
|
85
|
-
// - thinkingFormat:"qwen-chat-template" (reasoning models only) → pi
|
|
86
|
-
// sends chat_template_kwargs.enable_thinking, the boolean our
|
|
87
|
-
// Qwen3.5/MiniCPM5 templates gate on. pi computes the boolean from
|
|
88
|
-
// the selected thinking level (off → false), so no thinkingLevelMap.
|
|
89
|
-
compat: {
|
|
90
|
-
supportsDeveloperRole: false,
|
|
91
|
-
...(opts.reasoning ? { thinkingFormat: "qwen-chat-template" as const } : {}),
|
|
92
|
-
},
|
|
93
|
-
// input MUST match the loaded model. pi-ai serializes user content as a
|
|
94
|
-
// plain string for text-only models and as OpenAI content-parts
|
|
95
|
-
// (`[{type:"text",…}]`) once a model declares image input — and a
|
|
96
|
-
// text-only chat template renders NOTHING for a parts array, dropping
|
|
97
|
-
// the user's turn ("I don't see any message"). So declare image input
|
|
98
|
-
// only for an actual vision model; the web `ready` frame's `vision`
|
|
99
|
-
// flag is the same signal that drives the UI's image affordance.
|
|
100
|
-
input: opts.vision ? ["text", "image"] : ["text"],
|
|
101
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
102
|
-
contextWindow: opts.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
103
|
-
maxTokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
104
|
-
},
|
|
105
|
-
],
|
|
47
|
+
models: [piModelDefinition(opts)],
|
|
106
48
|
});
|
|
107
49
|
|
|
108
50
|
const model = modelRegistry.find(PI_PROVIDER_ID, PI_LOCAL_MODEL_ID);
|
package/src/pi-web.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { APP_ROUTE_IDS } from "./contracts/pi";
|
|
2
|
+
export { APP_ROUTE_IDS } from "./contracts/pi";
|
|
3
|
+
import type { ApprovalDecision, ImageAttachment, ClientMessage, UiSnapshotElement, UiSnapshot, WizardStep, AppUiContext, AppRouteId, ReadyGenDefaults, ServerMessage, HistoryToolItem, HistoryItem, SessionListItem, SamplingOverrides, Lane } from "./contracts/pi";
|
|
4
|
+
export type { ApprovalDecision, ImageAttachment, ClientMessage, UiSnapshotElement, UiSnapshot, WizardStep, AppUiContext, AppRouteId, ReadyGenDefaults, ServerMessage, HistoryToolItem, HistoryItem, SessionListItem, SamplingOverrides } from "./contracts/pi";
|
|
1
5
|
// mlx-bun pi-web — server-side embed of the pi agent SDK that bridges a
|
|
2
6
|
// browser WebSocket to an in-process AgentSession driving mlx-bun's own
|
|
3
7
|
// local model (Phase 16, web half).
|
|
@@ -52,7 +56,7 @@ import { MEMORY_TOOL_NAMES, REFERENCE_TOOL_NAMES } from "./memory/tools";
|
|
|
52
56
|
import { buildPiAgentSurface } from "./pi-session";
|
|
53
57
|
import { buildPiProvider, DEFAULT_CONTEXT_WINDOW, PI_LOCAL_MODEL_ID } from "./pi-provider";
|
|
54
58
|
import { downloadsSnapshot } from "./download";
|
|
55
|
-
import { getLane
|
|
59
|
+
import { getLane } from "./serve/lane-registry";
|
|
56
60
|
import { isToolAlwaysAllowed, setToolAlwaysAllowed, listAlwaysAllowedTools } from "./tool-approvals";
|
|
57
61
|
|
|
58
62
|
/**
|
|
@@ -356,188 +360,7 @@ export interface PiWsData {
|
|
|
356
360
|
|
|
357
361
|
type PiWs = ServerWebSocket<PiWsData>;
|
|
358
362
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
/** Decision a browser returns for a gated tool call. `editedArgs`, when
|
|
362
|
-
* present on an "allow", replaces the tool's proposed input before it runs
|
|
363
|
-
* (LM Studio's editable-args pattern — plan §5.4/§6.5). `alwaysAllow` marks
|
|
364
|
-
* the decision as "and remember this tool" — handled by resolveApproval,
|
|
365
|
-
* which persists it to the durable tool-approvals config (src/tool-
|
|
366
|
-
* approvals.ts) before settling the pending promise. */
|
|
367
|
-
export type ApprovalDecision = "allow" | "deny";
|
|
368
|
-
|
|
369
|
-
/** An image attachment from the browser: base64 (no data: prefix) + mime. */
|
|
370
|
-
export interface ImageAttachment {
|
|
371
|
-
data: string;
|
|
372
|
-
mimeType: string;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Client -> server frames. Exported type-only for the web frontend
|
|
376
|
-
* (src/web/src/*.ts import it via `import type` — see tsconfig.web.json
|
|
377
|
-
* and scripts/build-web.ts) so the WS contract is checked at compile time
|
|
378
|
-
* on both ends instead of drifting between server TS and untyped inline
|
|
379
|
-
* browser JS (the observed Phase-0 bug class). */
|
|
380
|
-
export type ClientMessage =
|
|
381
|
-
| { type: "prompt"; text: string; images?: ImageAttachment[] }
|
|
382
|
-
| { type: "abort" }
|
|
383
|
-
// `editedArgs`: when the user edited the proposed arguments in the
|
|
384
|
-
// approval card's textarea before approving, the (re-parsed) JSON object
|
|
385
|
-
// to substitute for the tool's original input — mutated onto the pi SDK's
|
|
386
|
-
// ToolCallEvent.input in place (the SDK's documented mutation contract;
|
|
387
|
-
// see installApprovalGate). Ignored on "deny". `alwaysAllow`: persist this
|
|
388
|
-
// tool to the durable always-allow config (src/tool-approvals.ts) so
|
|
389
|
-
// future calls to the SAME TOOL NAME skip the card entirely, on this and
|
|
390
|
-
// every other session.
|
|
391
|
-
| { type: "approval"; callId: string; decision: ApprovalDecision; editedArgs?: Record<string, unknown>; alwaysAllow?: boolean }
|
|
392
|
-
// Toggle the model's reasoning channel on/off (only meaningful when the
|
|
393
|
-
// model supports thinking — ready.thinking). Maps to Pi's session thinking
|
|
394
|
-
// level: "medium" (on) ↔ "off". Pi sends it as enable_thinking to the server.
|
|
395
|
-
| { type: "set_thinking"; enabled: boolean }
|
|
396
|
-
// Opt-in toggle (plan §5.4/§6.5/§9 Phase 2, default OFF): "let the agent
|
|
397
|
-
// touch files on this machine". Persisted per-browser by the client
|
|
398
|
-
// (localStorage `mlxbun.codingTools`); the ALLOWLIST decision is enforced
|
|
399
|
-
// here, server-side. buildPiAgentSurface's tool list is fixed for the
|
|
400
|
-
// life of an AgentSession (pi's DefaultResourceLoader config is baked in
|
|
401
|
-
// at createAgentSessionServices time), so this can't rewire an
|
|
402
|
-
// in-progress session — it takes effect starting with the next
|
|
403
|
-
// new_session/open_session/fork_session (createRuntimeFactory reads
|
|
404
|
-
// this.codingToolsRequested fresh every time the SDK calls it, including
|
|
405
|
-
// from AgentSessionRuntime.newSession()'s internal re-invocation). The
|
|
406
|
-
// `coding_tools` ServerMessage below reports the honest current-vs-pending
|
|
407
|
-
// state so the UI can say so instead of implying it applies immediately.
|
|
408
|
-
| { type: "set_coding_tools"; enabled: boolean }
|
|
409
|
-
// Select the active LoRA adapter for subsequent turns (null = none/base).
|
|
410
|
-
// app.html mounts it (POST /v1/adapters) before sending this; the
|
|
411
|
-
// before_provider_request hook injects it into the provider payload.
|
|
412
|
-
| { type: "set_adapter"; id: string | null }
|
|
413
|
-
// Per-request sampling overrides. Each field is optional; null/undefined
|
|
414
|
-
// means "leave it to the server default" (the mode-aware recommended
|
|
415
|
-
// value resolved in toOptions). A present numeric value is injected into
|
|
416
|
-
// the provider payload and always wins. The before_provider_request hook
|
|
417
|
-
// injects whatever is set here.
|
|
418
|
-
//
|
|
419
|
-
// `scope` (plan §9 Phase 3 / beat matrix Axis 4 "per-message sampling
|
|
420
|
-
// scope"): "session" (default, omitting the field means this too) persists
|
|
421
|
-
// the override on the connection until changed — today's behavior,
|
|
422
|
-
// unchanged. "next_turn" stores a ONE-SHOT override applied to exactly
|
|
423
|
-
// the next prompt's injection, then cleared automatically — it never
|
|
424
|
-
// touches the session-level values (a later plain set_sampling with
|
|
425
|
-
// scope:"session" or omitted still sees whatever was there before this
|
|
426
|
-
// message). Composition when both are present: next_turn's SET fields
|
|
427
|
-
// win over session's for that one request; anything next_turn leaves
|
|
428
|
-
// null/unset falls back to the session-level override for that field
|
|
429
|
-
// (composeSampling — session-level itself falls back to the server's
|
|
430
|
-
// mode-aware default per injectSampling's existing contract).
|
|
431
|
-
| {
|
|
432
|
-
type: "set_sampling";
|
|
433
|
-
scope?: "session" | "next_turn";
|
|
434
|
-
temperature?: number | null;
|
|
435
|
-
top_p?: number | null;
|
|
436
|
-
top_k?: number | null;
|
|
437
|
-
min_p?: number | null;
|
|
438
|
-
xtc_probability?: number | null;
|
|
439
|
-
xtc_threshold?: number | null;
|
|
440
|
-
repetition_penalty?: number | null;
|
|
441
|
-
repetition_context_size?: number | null;
|
|
442
|
-
presence_penalty?: number | null;
|
|
443
|
-
frequency_penalty?: number | null;
|
|
444
|
-
seed?: number | null;
|
|
445
|
-
}
|
|
446
|
-
// Per-session USER system prompt, layered ON TOP OF (not replacing) the
|
|
447
|
-
// built-in surface prompt (buildWebChatSystemPrompt + memoryHint). null
|
|
448
|
-
// clears it. Applied by the before_agent_start hook on the NEXT turn (pi
|
|
449
|
-
// calls it fresh inside every session.prompt(), so this is a true
|
|
450
|
-
// per-turn injection, not a session-creation-only setting — see
|
|
451
|
-
// installSystemPromptHook). Presets v1 (composer.ts) send this alongside
|
|
452
|
-
// set_sampling to apply a saved bundle.
|
|
453
|
-
| { type: "set_system_prompt"; text: string | null }
|
|
454
|
-
// Session management (recent-chats sidebar + new chat).
|
|
455
|
-
| { type: "new_session" }
|
|
456
|
-
| { type: "list_sessions" }
|
|
457
|
-
| { type: "open_session"; path: string }
|
|
458
|
-
| { type: "fork_session"; path: string }
|
|
459
|
-
| { type: "delete_session"; path: string }
|
|
460
|
-
// Message actions (plan §5.2). All three navigate the session's leaf
|
|
461
|
-
// pointer via AgentSession.navigateTree() then re-prompt — a sibling
|
|
462
|
-
// branch in the SAME session file, never a new one (see the helpers above
|
|
463
|
-
// findLastUserMessageEntry for the full rationale). Regenerate re-sends the
|
|
464
|
-
// last user message's original content verbatim; edit_resend re-sends it
|
|
465
|
-
// with edited text (images preserved); switch_sibling moves the active
|
|
466
|
-
// leaf to a previously-created sibling (an earlier edit/regeneration)
|
|
467
|
-
// without sending anything new.
|
|
468
|
-
| { type: "regenerate" }
|
|
469
|
-
| { type: "edit_resend"; text: string }
|
|
470
|
-
| { type: "switch_sibling"; entryId: string }
|
|
471
|
-
// App-aware assistant (plan §6.6, §9 Phase 3, beat matrix Axis 12): pushed
|
|
472
|
-
// by chat.ts on every route change AND on wizard-step change (quantize/
|
|
473
|
-
// finetune/dataset), never on a timer. `context` is opaque here (the
|
|
474
|
-
// browser's AppContext shape, src/web/src/assistant.ts) — stored verbatim
|
|
475
|
-
// as the connection's currentAppContext and returned by the
|
|
476
|
-
// get_current_app_context tool, plus mined for the compact one-line
|
|
477
|
-
// ambient context auto-prepended to the NEXT prompt (see
|
|
478
|
-
// installAppContextHook / ambientContextLine). This is a DIFFERENT frame
|
|
479
|
-
// from the server->client `context` ServerMessage (context-window usage,
|
|
480
|
-
// above) — same name, opposite direction, disambiguated by which type
|
|
481
|
-
// union it's a member of; the two are never in scope at the same call
|
|
482
|
-
// site (ClientMessage here vs. ServerMessage below).
|
|
483
|
-
| { type: "context"; context: AppUiContext };
|
|
484
|
-
|
|
485
|
-
// ---- App-aware assistant (plan §6.6, §9 Phase 3, beat matrix Axis 12) --
|
|
486
|
-
//
|
|
487
|
-
// Mirrors src/web/src/assistant.ts's browser-side types (which are the
|
|
488
|
-
// source of truth for shape — this server-side copy is intentionally loose
|
|
489
|
-
// (mostly `unknown`/optional) since the server never constructs a
|
|
490
|
-
// UiSnapshot itself, only stores and forwards what the browser sends. Kept
|
|
491
|
-
// here (not imported) because src/web/src/assistant.ts is bundled into the
|
|
492
|
-
// browser and importing it here would pull browser code into the server
|
|
493
|
-
// build — the inverse of the type-only ClientMessage/ServerMessage import
|
|
494
|
-
// direction the frontend uses for pi-web.ts.
|
|
495
|
-
|
|
496
|
-
/** One element in a captured DOM snapshot — see assistant.ts's
|
|
497
|
-
* UiSnapshotElement for the authoritative shape/doc. */
|
|
498
|
-
export interface UiSnapshotElement {
|
|
499
|
-
ref: string;
|
|
500
|
-
tag: string;
|
|
501
|
-
label: string;
|
|
502
|
-
kind: "interactive" | "region";
|
|
503
|
-
role?: string;
|
|
504
|
-
selector: string;
|
|
505
|
-
spotlightId?: string;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
export interface UiSnapshot {
|
|
509
|
-
route: string;
|
|
510
|
-
capturedAt: string;
|
|
511
|
-
elements: UiSnapshotElement[];
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/** The wizard-step process-state fields (§6.6 "carries process state, not
|
|
515
|
-
* just elements") — present only on routes with a step wizard. */
|
|
516
|
-
export interface WizardStep {
|
|
517
|
-
index: number;
|
|
518
|
-
count: number;
|
|
519
|
-
label: string;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/** The full context payload pushed by chat.ts on route/step change — see
|
|
523
|
-
* assistant.ts's buildAppContext. Stored verbatim per-connection. */
|
|
524
|
-
export interface AppUiContext {
|
|
525
|
-
route: string;
|
|
526
|
-
view?: string;
|
|
527
|
-
step?: WizardStep;
|
|
528
|
-
snapshot: UiSnapshot;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/** Route catalog mirror (src/web/src/ui-catalog.ts's ROUTE_IDS) for
|
|
532
|
-
* server-side validation of navigate_app's route/page param BEFORE it
|
|
533
|
-
* ever reaches the browser — an unknown route is rejected here as a tool
|
|
534
|
-
* error rather than sent as a `ui_navigate` frame the router can't match.
|
|
535
|
-
* Keep in sync with ui-catalog.ts's ROUTE_IDS by hand (no shared-JSON
|
|
536
|
-
* import between the server and browser bundles — see that file's
|
|
537
|
-
* top-of-file comment for why). */
|
|
538
|
-
export const APP_ROUTE_IDS = ["chat", "quantize", "finetune", "dataset", "status"] as const;
|
|
539
|
-
export type AppRouteId = (typeof APP_ROUTE_IDS)[number];
|
|
540
|
-
|
|
363
|
+
/** Validate assistant navigation against the shared browser/server route IDs. */
|
|
541
364
|
export function isAppRouteId(v: string): v is AppRouteId {
|
|
542
365
|
return (APP_ROUTE_IDS as readonly string[]).includes(v);
|
|
543
366
|
}
|
|
@@ -570,117 +393,6 @@ export function ambientContextLine(ctx: AppUiContext | null | undefined): string
|
|
|
570
393
|
return `[user is on: ${place}${stepPart}]`;
|
|
571
394
|
}
|
|
572
395
|
|
|
573
|
-
/** Model-author recommended sampling (generation_config.json, server-CLI
|
|
574
|
-
* overrides applied) — sent once on `ready` so the sampling popover can show
|
|
575
|
-
* per-model defaults instead of one hardcoded shape (web-ui-pass-plan.md
|
|
576
|
-
* #14). A field is null when neither the model nor the server configured one
|
|
577
|
-
* (the built-in fallback in toOptions still applies server-side). */
|
|
578
|
-
export interface ReadyGenDefaults {
|
|
579
|
-
temperature: number | null;
|
|
580
|
-
topP: number | null;
|
|
581
|
-
topK: number | null;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/** Server -> client frames. Exported type-only — see ClientMessage above. */
|
|
585
|
-
export type ServerMessage =
|
|
586
|
-
// `vision`: whether the loaded model can accept images (drives the UI's
|
|
587
|
-
// image-attach affordance — false on e4b until the SigLIP sidecar lands).
|
|
588
|
-
// `audio`: whether the loaded model can accept `input_audio` parts (audio
|
|
589
|
-
// tower loaded or loadable). Discovery-only for now: pi's Model.input type
|
|
590
|
-
// has no "audio" modality, so unlike `vision` this flag never feeds the
|
|
591
|
-
// provider declaration — clients use it instead of probing for a 400.
|
|
592
|
-
// `thinking`: whether the model has a switchable reasoning channel (drives
|
|
593
|
-
// the UI's thinking on/off toggle; false hides it).
|
|
594
|
-
| { type: "ready"; model: string; vision: boolean; audio: boolean; thinking: boolean; genDefaults: ReadyGenDefaults }
|
|
595
|
-
| { type: "turn_start" }
|
|
596
|
-
| { type: "text_delta"; delta: string }
|
|
597
|
-
| { type: "thinking_delta"; delta: string }
|
|
598
|
-
| { type: "tool_start"; callId: string; tool: string; args: unknown }
|
|
599
|
-
| { type: "tool_approval_request"; callId: string; tool: string; args: unknown }
|
|
600
|
-
| { type: "tool_update"; callId: string; chunk: unknown }
|
|
601
|
-
| { type: "tool_end"; callId: string; ok: boolean; result: unknown }
|
|
602
|
-
// `lane`: the serving lane the just-finished turn ran on (serial /
|
|
603
|
-
// serial+spec / batched), correlated via the lane registry keyed by the
|
|
604
|
-
// AssistantMessage's responseId (the pi SDK's own usage parsing drops
|
|
605
|
-
// custom fields — see src/serve/lane-registry.ts). Absent when the turn
|
|
606
|
-
// produced no assistant message with a responseId (e.g. an aborted turn
|
|
607
|
-
// before any model call) — never guessed client-side (risk #5).
|
|
608
|
-
| { type: "turn_end"; lane?: Lane }
|
|
609
|
-
| { type: "queue_update"; steering: readonly string[]; followUp: readonly string[] }
|
|
610
|
-
// Replay a session's transcript (rebuilds the thread); and the sidebar list.
|
|
611
|
-
| { type: "history"; items: HistoryItem[] }
|
|
612
|
-
| { type: "sessions"; items: SessionListItem[]; activePath?: string }
|
|
613
|
-
// Sibling group for the LAST user message (edit-and-resend's `< i/n >`
|
|
614
|
-
// toggle, plan §5.2). Sent after every history rebuild and after a
|
|
615
|
-
// completed turn. `count` <= 1 means no toggle to show (the common case:
|
|
616
|
-
// no edits/regenerations yet). `entryId` is the currently-active sibling's
|
|
617
|
-
// id; `siblingIds` is the full ordered group so the client can resolve
|
|
618
|
-
// "previous/next" locally and send switch_sibling with a concrete id
|
|
619
|
-
// (never inferred/guessed — the server is the source of truth for order).
|
|
620
|
-
| { type: "siblings"; entryId?: string; index: number; count: number; siblingIds: string[] }
|
|
621
|
-
// Context-window usage indicator. tokens/percent are null right after a
|
|
622
|
-
// compaction until the next assistant reply (pi can't estimate yet).
|
|
623
|
-
| { type: "context"; tokens: number | null; contextWindow: number; percent: number | null }
|
|
624
|
-
// Effective vs. pending codingTools state (set_coding_tools above).
|
|
625
|
-
// `active` is what the CURRENT session actually built with (gates
|
|
626
|
-
// whether the approval card can ever fire); `pending` is the browser's
|
|
627
|
-
// most recent request, which only takes effect on the next
|
|
628
|
-
// new/opened/forked session — surfaced so the settings copy can say
|
|
629
|
-
// "will apply to your next new chat" instead of lying about immediacy.
|
|
630
|
-
| { type: "coding_tools"; active: boolean; pending: boolean }
|
|
631
|
-
// Full always-allow set for THIS machine (src/tool-approvals.ts), sent on
|
|
632
|
-
// `ready` and after any change, so the settings panel can list/forget
|
|
633
|
-
// durable per-tool approvals without a separate REST round-trip.
|
|
634
|
-
| { type: "tool_approvals"; alwaysAllow: string[] }
|
|
635
|
-
// App-aware assistant (plan §6.6): the navigate_app / spotlight_ui tools'
|
|
636
|
-
// side-channel notifications, mapped 1:1 from the tool call's params —
|
|
637
|
-
// never guessed or re-derived, so chat.ts's router/spotlight get exactly
|
|
638
|
-
// what the model asked for. `route` here is always a validated
|
|
639
|
-
// AppRouteId (isAppRouteId already ran server-side before this frame is
|
|
640
|
-
// ever sent — see navigate_app's execute). ui_spotlight's fields mirror
|
|
641
|
-
// SpotlightRequest 1:1 (assistant.ts resolves ref/label/selector/target
|
|
642
|
-
// client-side, in that precedence order, against its last snapshot).
|
|
643
|
-
| { type: "ui_navigate"; route: AppRouteId }
|
|
644
|
-
| { type: "ui_spotlight"; ref?: string; label?: string; selector?: string; target?: string; message?: string; route?: AppRouteId }
|
|
645
|
-
| { type: "error"; message: string };
|
|
646
|
-
|
|
647
|
-
// ---- Session serialization (pure, unit-tested) -----------------------
|
|
648
|
-
|
|
649
|
-
/** A finished tool call as rendered in replayed history. */
|
|
650
|
-
export interface HistoryToolItem {
|
|
651
|
-
callId: string;
|
|
652
|
-
name: string;
|
|
653
|
-
args: unknown;
|
|
654
|
-
/** Tool result text, filled from the matching toolResult message. */
|
|
655
|
-
result: string;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/** One replayed transcript turn: a user or assistant message (assistant
|
|
659
|
-
* messages carry any tool calls they made, with results merged in). */
|
|
660
|
-
export interface HistoryItem {
|
|
661
|
-
role: "user" | "assistant";
|
|
662
|
-
text: string;
|
|
663
|
-
/** Model reasoning/thinking, kept separate from the final answer. */
|
|
664
|
-
thinking?: string;
|
|
665
|
-
tools: HistoryToolItem[];
|
|
666
|
-
/** Session entry id (user items only). Lets the browser correlate "the
|
|
667
|
-
* last user message" in the replayed thread with the `siblings` frame's
|
|
668
|
-
* entryId, so it knows which DOM node gets the edit/sibling-toggle UI
|
|
669
|
-
* (plan §5.2). Undefined for assistant items — they aren't fork targets. */
|
|
670
|
-
entryId?: string;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/** A row in the recent-chats sidebar, derived from pi's SessionInfo. */
|
|
674
|
-
export interface SessionListItem {
|
|
675
|
-
path: string;
|
|
676
|
-
id: string;
|
|
677
|
-
title: string;
|
|
678
|
-
/** Last-modified epoch ms (newest first). */
|
|
679
|
-
modified: number;
|
|
680
|
-
messageCount: number;
|
|
681
|
-
forked: boolean;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
396
|
/** Flatten a content value (string | content-parts) to its text. */
|
|
685
397
|
function contentText(content: unknown): string {
|
|
686
398
|
if (typeof content === "string") return content;
|
|
@@ -964,25 +676,6 @@ export function injectAdapter(
|
|
|
964
676
|
return { ...payload, adapter: selected };
|
|
965
677
|
}
|
|
966
678
|
|
|
967
|
-
/** Per-request sampling overrides carried on the session and injected into the
|
|
968
|
-
* provider payload by the before_provider_request hook. Each field is either a
|
|
969
|
-
* user-set override or null/undefined ("use the server's mode-aware default").
|
|
970
|
-
* Field names match the server's ChatRequest wire names (not toOptions' camelCase)
|
|
971
|
-
* since these are injected directly into the outgoing chat-completions payload. */
|
|
972
|
-
export interface SamplingOverrides {
|
|
973
|
-
temperature?: number | null;
|
|
974
|
-
top_p?: number | null;
|
|
975
|
-
top_k?: number | null;
|
|
976
|
-
min_p?: number | null;
|
|
977
|
-
xtc_probability?: number | null;
|
|
978
|
-
xtc_threshold?: number | null;
|
|
979
|
-
repetition_penalty?: number | null;
|
|
980
|
-
repetition_context_size?: number | null;
|
|
981
|
-
presence_penalty?: number | null;
|
|
982
|
-
frequency_penalty?: number | null;
|
|
983
|
-
seed?: number | null;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
679
|
/** Every SamplingOverrides field name, in wire order — the single source of
|
|
987
680
|
* truth `injectSampling` and the `set_sampling` handler both walk, so a new
|
|
988
681
|
* field only needs adding here + the interface above. */
|
|
@@ -1158,7 +851,7 @@ function appAwareTextResult(text: string, details: unknown = {}): { content: [{
|
|
|
1158
851
|
* null before the first one arrives); `notify` sends a ServerMessage
|
|
1159
852
|
* (ui_navigate/ui_spotlight) to that same browser tab. Pure enough to
|
|
1160
853
|
* unit test the tool -> frame mapping via a fake notify collecting sent
|
|
1161
|
-
* frames (tests/pi-web.test.ts). */
|
|
854
|
+
* frames (tests/research/pi-web.test.ts). */
|
|
1162
855
|
export function createAppAwareTools(
|
|
1163
856
|
getContext: () => AppUiContext | null,
|
|
1164
857
|
notify: (msg: ServerMessage) => void,
|
|
@@ -1267,7 +960,7 @@ class PiWebSession {
|
|
|
1267
960
|
* "Per-message sampling scope" block comment above composeSampling for
|
|
1268
961
|
* the full lifecycle. Wrapped in SamplingScopeState (not two loose
|
|
1269
962
|
* fields) so the exact same applySetSampling/consumeForRequest pure
|
|
1270
|
-
* functions run here and in tests/pi-web.test.ts. */
|
|
963
|
+
* functions run here and in tests/research/pi-web.test.ts. */
|
|
1271
964
|
private samplingScope: SamplingScopeState = initialSamplingScopeState();
|
|
1272
965
|
/** User's custom system-prompt text for this connection (set via
|
|
1273
966
|
* set_system_prompt), null = none. Read by the before_agent_start hook
|