Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
616fcca
Lane core: requests that arrive together share their prompt passes
philip-pentatonic Oct 10, 2026
82d8721
Nemotron: the checkpoint Source copies to a Target, so other CUDA fam…
philip-pentatonic Oct 10, 2026
99bcc5b
Qwen3.8-27B on CUDA: the qwen27 family's engine for NVIDIA GPUs, ever…
philip-pentatonic Oct 10, 2026
907e013
Native image input: images from a request's image parts, prepared bef…
BobClawblaw Oct 10, 2026
47c4098
Native image input: --vision-urls fetches public HTTPS image URLs as …
BobClawblaw Oct 10, 2026
b68e3da
Merge PR #608 (Qwen3.8-27B native CUDA engine) onto 1.0.7
BobClawblaw Oct 12, 2026
9f99ca9
Merge PR #598 and #614 (native image input, HTTPS image URLs) onto qw…
BobClawblaw Oct 12, 2026
fa22819
Qwen3.5 vision tower on CUDA: qwen_vision.cu kernels, the Tower, and …
BobClawblaw Oct 12, 2026
407cb28
Qwen image preprocessing on Linux: stb_image decode, EXIF, alpha, sma…
BobClawblaw Oct 12, 2026
ff7cc22
Native image input for Qwen3.8-27B on CUDA: tower rows spliced into t…
BobClawblaw Oct 12, 2026
5d8bf48
qwen27 CUDA: --kv-cache fp8, and attention partials sized for the adm…
BobClawblaw Oct 12, 2026
06cc811
qwen27 CUDA: a prompt cache, so a chat's next turn resumes its kept s…
BobClawblaw Oct 12, 2026
2c222ae
Merge main (1.0.8 work) into qwen27-cuda-1.0.7
BobClawblaw Oct 12, 2026
d49a906
qwen27 CUDA: no tooling that imports the Python engine in the tree
BobClawblaw Oct 12, 2026
e3b7210
Merge main (1.0.9) into qwen27-cuda-1.0.7
BobClawblaw Oct 13, 2026
72bf399
Merge main (1.0.10) into qwen27-cuda-1.0.7
BobClawblaw Oct 14, 2026
4e15fae
Merge main (1.0.11) into qwen27-cuda-1.0.7
BobClawblaw Oct 14, 2026
bcd433c
Lean rule: one-line comments and docstrings, and a vendored-source ex…
BobClawblaw Oct 14, 2026
5a4987b
Living Weights: the learner, pocket and fits move to zig/src/living, …
ZackSample Oct 15, 2026
75978a1
Living Weights learns into any family described as data: the family's…
ZackSample Oct 15, 2026
dac84b6
Merge lw-b-core (#878, Living Weights as a core module) onto #726 (Qw…
ZackSample Oct 16, 2026
e90a103
Living Weights: the learner as the 27B's lessons run it
ZackSample Oct 16, 2026
b9b3e83
CUDA: Living Weights' DeltaNet and gated attention backward as core k…
ZackSample Oct 16, 2026
018d2b2
Qwen3.8-27B on CUDA learns: Living Weights' pocket beside every MLP, …
ZackSample Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pub fn build(b: *std.Build) void {
}

/// `zig build native -Dcpu=apple_m1`: tensorfold-native with the Metal engines for the Python package's bundle (a native M5 build traps on M1-M4).
fn nativeServer(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, metal: *std.Build.Module, engine: *std.Build.Module, api: *std.Build.Module, engines: *std.Build.Module, build_options: *std.Build.Step.Options, test_step: ?*std.Build.Step) *std.Build.Step.Compile {
fn nativeServer(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, metal: *std.Build.Module, engine: *std.Build.Module, living: *std.Build.Module, api: *std.Build.Module, engines: *std.Build.Module, build_options: *std.Build.Step.Options, test_step: ?*std.Build.Step) *std.Build.Step.Compile {
// the HTTP side keeps its safety checks; the engine below it runs at `optimize`
const tokenizer = b.createModule(.{ .root_source_file = b.path("zig/src/core/tokenizer/tokenizer.zig"), .target = target, .optimize = .ReleaseSafe, .link_libc = true });
const template = b.createModule(.{ .root_source_file = b.path("zig/src/core/template/template.zig"), .target = target, .optimize = .ReleaseSafe, .link_libc = true });
Expand All @@ -201,7 +201,7 @@ fn nativeServer(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.b
.target = target,
.optimize = .ReleaseSafe,
.link_libc = true,
.imports = &.{ .{ .name = "engine_api", .module = api }, .{ .name = "tokenizer", .module = tokenizer }, .{ .name = "template", .module = template }, .{ .name = "native_engines", .module = engines }, .{ .name = "checkpoint_cli", .module = b.createModule(.{ .root_source_file = b.path("zig/src/cli/cli.zig"), .target = target, .optimize = .ReleaseSafe, .link_libc = true, .imports = &.{ .{ .name = "native_engines", .module = engines }, exlr8_build.cliImport(b, target, .ReleaseSafe, engine) } }) } },
.imports = &.{ .{ .name = "engine_api", .module = api }, .{ .name = "tokenizer", .module = tokenizer }, .{ .name = "template", .module = template }, .{ .name = "native_engines", .module = engines }, .{ .name = "living", .module = living }, .{ .name = "checkpoint_cli", .module = b.createModule(.{ .root_source_file = b.path("zig/src/cli/cli.zig"), .target = target, .optimize = .ReleaseSafe, .link_libc = true, .imports = &.{ .{ .name = "native_engines", .module = engines }, exlr8_build.cliImport(b, target, .ReleaseSafe, engine) } }) } },
}) });
exe.root_module.addOptions("build_options", build_options);
if (test_step == null) {
Expand All @@ -219,7 +219,7 @@ fn nativeServer(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.b
.target = target,
.optimize = .Debug, // the server's unit tests run with safety checks, as zig_test.sh builds them
.link_libc = true,
.imports = &.{ .{ .name = "engine_api", .module = api }, .{ .name = "tokenizer", .module = tokenizer }, .{ .name = "template", .module = template } },
.imports = &.{ .{ .name = "engine_api", .module = api }, .{ .name = "tokenizer", .module = tokenizer }, .{ .name = "template", .module = template }, .{ .name = "living", .module = living } },
});
test_step.?.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = server_tests })).step);
@import("zig/build/cortheon_fuzz.zig").add(b, target, api, tokenizer, template);
Expand Down Expand Up @@ -411,6 +411,7 @@ fn metalTargets(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.b
.{ .name = "tf-grid-sync-bench", .path = "zig/tests/grid_sync_bench.zig", .about = "A GPU-wide barrier in one persistent dispatch against dependent relaunches" },
.{ .name = "tf-weight-read-check", .path = "zig/tests/weight_read_check.zig", .about = "Check native file reads and failed-read cleanup", .c_source = "zig/tests/pread_fault.c" },
.{ .name = "tf-glm-run", .path = "zig/tests/glm_run.zig", .about = "GLM-5.3-Flash greedy replies at each draft depth against depth 0 and reference tokens" },
.{ .name = "tf-glm-vision", .path = "zig/tests/glm_vision.zig", .about = "GLM-5.3-Flash's image tower on an image or a reference dump's patches, compared with the dump" },
.{ .name = "tf-glm-attn-probe", .path = "zig/tests/glm_attn_probe.zig", .about = "GLM-5.3-Flash's dense latent attention kernels on given inputs (scores, probabilities, outputs)" },
.{ .name = "tf-moe-bench", .path = "zig/tests/moe_bench.zig", .about = "Routed-expert prompt kernels from .metal files on a skewed routing: chained speed, bits against the first" },
.{ .name = "tf-mm-bench", .path = "zig/tests/mm_bench.zig", .about = "Prompt matmul kernels from .metal files: chained speed at one shape, bits against the first, error against fp64" },
Expand Down Expand Up @@ -466,7 +467,7 @@ fn metalTargets(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.b
glm_checks.targets(b, target, optimize, metal, engine, sources);
flashnext_checks.targets(b, target, optimize, metal, engine);

_ = nativeServer(b, target, optimize, metal, engine, api, engines, build_options, test_step);
_ = nativeServer(b, target, optimize, metal, engine, mods.living, api, engines, build_options, test_step);

const cluster_metal = b.createModule(.{
.root_source_file = b.path("zig/src/cluster/metal_sink.zig"),
Expand Down Expand Up @@ -539,7 +540,7 @@ fn metalTargets(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.b
}

/// The server and distribution use the same embedded-source Metal engine.
fn metalEngineModules(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, draft_ids: *std.Build.Module) struct { metal: *std.Build.Module, lanes: *std.Build.Module, fabric: *std.Build.Module, sources: *std.Build.Module, engine: *std.Build.Module, qwen27: qwen27_build.Shared } {
fn metalEngineModules(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, draft_ids: *std.Build.Module) struct { metal: *std.Build.Module, lanes: *std.Build.Module, fabric: *std.Build.Module, sources: *std.Build.Module, engine: *std.Build.Module, living: *std.Build.Module, qwen27: qwen27_build.Shared } {
const metal = b.createModule(.{
.root_source_file = b.path("zig/src/metal/metal.zig"),
.target = target,
Expand All @@ -549,18 +550,22 @@ fn metalEngineModules(b: *std.Build, target: std.Build.ResolvedTarget, optimize:
metal.linkFramework("Metal", .{});
metal.linkFramework("Foundation", .{});
metal.linkFramework("IOSurface", .{}); // the Neural Engine's prompt share passes IOSurfaces
metal.linkFramework("ImageIO", .{}); // GLM-5.3-Flash's image input: decoding (families/glm/image.zig)
metal.linkFramework("CoreGraphics", .{});
metal.linkSystemLibrary("objc", .{});
const lanes = b.createModule(.{ .root_source_file = b.path("zig/src/core/lanes/lanes.zig"), .target = target, .optimize = optimize, .link_libc = true });
const fabric = b.createModule(.{ .root_source_file = b.path("zig/src/fabric/fabric.zig"), .target = target, .optimize = optimize, .link_libc = true });
const sources = b.createModule(.{ .root_source_file = b.path("zig/kernels/metal/sources.zig") });
// Living Weights' family-neutral learner, which Nemotron's lessons and the server's view of them share
const living = b.createModule(.{ .root_source_file = b.path("zig/src/living/living.zig"), .target = target, .optimize = optimize, .link_libc = true });
const engine = b.createModule(.{
.root_source_file = b.path("zig/src/tensorfold.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
.imports = &.{ .{ .name = "metal", .module = metal }, .{ .name = "kernel_sources", .module = sources }, .{ .name = "nemotron_draft_ids", .module = draft_ids }, .{ .name = "lanes", .module = lanes }, .{ .name = "fabric", .module = fabric } },
.imports = &.{ .{ .name = "metal", .module = metal }, .{ .name = "kernel_sources", .module = sources }, .{ .name = "nemotron_draft_ids", .module = draft_ids }, .{ .name = "lanes", .module = lanes }, .{ .name = "fabric", .module = fabric }, .{ .name = "living", .module = living } },
});
return .{ .metal = metal, .lanes = lanes, .fabric = fabric, .sources = sources, .engine = engine, .qwen27 = qwen27_build.engineModules(b, target, metal, sources, engine) };
return .{ .metal = metal, .lanes = lanes, .fabric = fabric, .sources = sources, .engine = engine, .living = living, .qwen27 = qwen27_build.engineModules(b, target, metal, sources, engine) };
}

pub fn distMetalServer(b: *std.Build, target: std.Build.ResolvedTarget, draft_ids: *std.Build.Module, build_options: *std.Build.Step.Options, sdk: []const u8) *std.Build.Step.Compile {
Expand All @@ -574,7 +579,7 @@ pub fn distMetalServer(b: *std.Build, target: std.Build.ResolvedTarget, draft_id
m.metal.addSystemIncludePath(b.graph.cwdRelativePath(b.pathJoin(&.{ sdk, "usr/include" })));
const api = b.createModule(.{ .root_source_file = b.path("zig/src/core/engine_api.zig"), .target = target, .optimize = .fast, .link_libc = true, .imports = &.{.{ .name = "lanes", .module = m.lanes }} });
const engines = b.createModule(.{ .root_source_file = b.path("zig/src/native/metal.zig"), .target = target, .optimize = .fast, .link_libc = true, .imports = &.{ .{ .name = "metal", .module = m.metal }, .{ .name = "engine_api", .module = api }, .{ .name = "tensorfold", .module = m.engine } } });
return nativeServer(b, target, .fast, m.metal, m.engine, api, engines, build_options, null);
return nativeServer(b, target, .fast, m.metal, m.engine, m.living, api, engines, build_options, null);
}

test {
Expand Down
6 changes: 4 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ chat fields that shape the prompt (`tools`, `reasoning_effort`, `chat_template_k
`tokens`, and `token_strs` with `return_token_strs: true`. The IDs are the ones the chat route runs for the same
request, images expanded. `/detokenize` takes `tokens` and returns `prompt`.
In the Python 0.6.6 engine, `--vision` lets supported Qwen3.5/3.8 dense checkpoints accept `image_url` content parts
alongside text in user messages and in tool results (`role: "tool"`), such as an agent's screenshots.
alongside text in user messages and in tool results (`role: "tool"`), such as an agent's screenshots. The native server
does the same with `--vision` for Qwen3.8-27B on CUDA and GLM-5.3-Flash on Metal, with data URLs, and public HTTPS URLs
under `--vision-urls` ([image input](vision.md#the-native-server)).
See [image input](vision.md) for data URLs, public image URLs, limits and cache behavior.
The native binary refuses `--vision` and answers image parts with HTTP 400.
Other native families refuse `--vision` and answer image parts with HTTP 400.
Unsupported image input, audio, video and non-text output requests receive HTTP 400.

## Decisions
Expand Down
28 changes: 28 additions & 0 deletions docs/vision.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,34 @@ This prevents identical image-placeholder token IDs from reusing another image's
Multi-turn image conversations work when the request includes the original image content parts, but image-prefix reuse and persisted image KV are not implemented.
For Qwen, each image request carries its own multimodal rotary positions and continuation offset, including during concurrent lane rounds. GLM uses its native KDA/NoPE attention state.

## The native server

`tensorfold-native serve` takes `--vision`, `--vision-max-images` (default 4) and `--vision-image-tokens` (default
4,096) for engines that read images; GLM-5.3-Flash on one Mac reads them so far, and other native engines refuse the
flag at startup.

- Images arrive as `image_url` data URLs in user messages and tool results. With `--vision-urls` (it needs
`--vision`), a part may name a public HTTPS URL instead, fetched as 0.6.6 fetched it: port 443 without credentials
or a fragment; every address the host resolves to public (no private, loopback, link-local, carrier-grade NAT,
documentation, multicast or metadata address, and for IPv6 global unicast only, without 6to4 or Teredo); TLS
verified for the host against the system's certificates, on the address that was checked; up to 3 redirects,
each checked again; a declared `image/jpeg`, `image/png` or `image/webp`, uncompressed; 10 MB an image and 20 MB
a request across data URLs and downloads; 10 s a download and 30 s a request's downloads. The refusals are
0.6.6's words; a TLS failure, like any other failed connection, is "image download failed or timed out". Host
names that aren't ASCII and IPv6 address literals are refused. Without the flag, an HTTPS URL is refused with the
flag's name.
- PNG and JPEG are decoded by the platform's decoder (ImageIO on macOS). WebP is refused by name until a decoder
exists; other formats are refused.
- The limits and the processing are 0.6.6's: EXIF orientation, alpha composited over white, the GLM5-Next processor's
sizing and PIL's bicubic resampler. Six drawn PNG fixtures give patches bit-equal to 0.6.6 with mlx-vlm
(`tools/zig/glm_vision_fixtures.py`). JPEG patches differ slightly, because ImageIO and libjpeg-turbo decode
differently.
- Images are counted from the request's image parts. A user or tool message whose text spells the model's image
markers (`<|image|>`, `<|begin_of_image|>`, `<|end_of_image|>`) is tokenized as ordinary text, so only the chat
template's own markers, one wrapper for each image part, become image tokens.
- The prompt cache keys each image's placeholder rows by the image's content, so a kept state resumes the same image
and never another one under the same placeholders. Image conversations reuse their history like text ones.

## Verification

Compare image requests with `draft: true` and `draft: false` at identical sampling settings and seed, then compare concurrent requests with their solo results.
Expand Down
Loading
Loading