Command Line (CLI)

TensorSharp.Cli is the console host for local prompts, multimodal experiments, prompt inspection, JSONL batch workflows, the interactive REPL, and built-in benchmarks. The binary lands in TensorSharp.Cli/bin/... after the build.

Quick start in ~30 seconds

Start at the TensorSharp repository root after installing the .NET 10 SDK for your platform. The verified quick start builds the native GGML backend and runs the repository's benchmark-verified gemma-4-E4B-it-Q8_0.gguf (7.48 GiB). The commands take about 30 seconds to copy and run; the model download and the first restore/build take longer and depend on your connection and machine. A native source build also needs Git, network access, CMake, and a working C++ toolchain.

Linux + NVIDIA

TENSORSHARP_GGML_NATIVE_ENABLE_CUDA=ON dotnet build TensorSharp.slnx -c Release -p:TensorSharpSkipMlxNative=true
curl --create-dirs --fail -L "https://huggingface.co/ggml-org/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-Q8_0.gguf?download=true" -o models/gemma-4-E4B-it-Q8_0.gguf
echo "Explain local inference in one paragraph." > prompt.txt
dotnet run --project TensorSharp.Cli -c Release --no-build -- --model models/gemma-4-E4B-it-Q8_0.gguf --input prompt.txt --max-tokens 128 --backend ggml_cuda

Windows PowerShell + NVIDIA

$env:TENSORSHARP_GGML_NATIVE_ENABLE_CUDA = 'ON'
dotnet build TensorSharp.slnx -c Release -p:TensorSharpSkipMlxNative=true
curl.exe --create-dirs --fail -L "https://huggingface.co/ggml-org/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-Q8_0.gguf?download=true" -o models\gemma-4-E4B-it-Q8_0.gguf
Set-Content prompt.txt "Explain local inference in one paragraph."
dotnet run --project TensorSharp.Cli -c Release --no-build -- --model models\gemma-4-E4B-it-Q8_0.gguf --input prompt.txt --max-tokens 128 --backend ggml_cuda

Omit --input and the CLI falls back to its built-in What is 1+1? prompt; for your own one-shot text prompt, save the text to a file and pass --input. --prompt is only the Qwen-Image-Edit instruction.

Gemma 4 E4B on other native backends

On Apple Silicon, omit the CUDA environment assignment and use ggml_metal; on a supported Windows/Linux Vulkan GPU, request TENSORSHARP_GGML_NATIVE_ENABLE_VULKAN=ON instead and use ggml_vulkan; ggml_cpu runs the native CPU kernels and needs no GPU. The lower-memory gemma-4-E4B-it-Q4_K_M.gguf is in the same repository. Text needs no mmproj. Add the matching mmproj-gemma-4-E4B-it-Q8_0.gguf with --mmproj only for image, video, or audio input. See Getting Started for full platform syntax.

Examples

Run these source commands from the repository root. Replace the sample paths and choose a native backend that your build supports.

# Text inference (macOS)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --output result.txt \
    --max-tokens 200 --backend ggml_metal

# Text inference on Windows/Linux + NVIDIA GPU
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --output result.txt \
    --max-tokens 200 --backend ggml_cuda

# Text inference on any Vulkan GPU (AMD / Intel / NVIDIA)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --output result.txt \
    --max-tokens 200 --backend ggml_vulkan

# Multi-GPU host: list the visible Vulkan devices, then pick one by index
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- --list-gpus
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --max-tokens 200 \
    --backend ggml_vulkan --gpu-device 1

# Interactive turn-by-turn chat (REPL) with KV-cache reuse and slash commands
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --backend ggml_metal --interactive
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --backend ggml_metal -i \
    --system "You are a terse assistant." --temperature 0.7 --top-p 0.9 --think

# DSpark block speculative decoding (DeepSeek V4): --model is the FIRST shard of
# the split GGUF, --draft-model the separate DSpark drafter. Needs greedy sampling.
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <DeepSeek-V4-Flash-...-00001-of-00005.gguf> --backend ggml_cuda \
    --draft-model <DSpark-drafter.gguf> --input prompt.txt --max-tokens 200 --temperature 0

Multimodal

# Image inference (Gemma 3/4, Qwen 3.5-family)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --image photo.png --max-tokens 200 --backend ggml_metal

# Video inference (Gemma 4)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --video clip.mp4 --max-tokens 200 --backend ggml_metal

# Audio inference (Gemma 4)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --audio speech.wav --max-tokens 200 --backend ggml_metal

# PDF document Q&A (--input holds the question; scanned/image-only PDFs
# need a vision model + --mmproj, born-digital PDFs work with any model)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --pdf report.pdf --input question.txt \
    --max-tokens 400 --backend ggml_metal

Reasoning, tools & sampling

# Thinking / reasoning mode
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --max-tokens 400 --backend ggml_metal --think

# Tool calling
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --max-tokens 300 --backend ggml_metal \
    --tools tools.json

# With sampling parameters (the CLI defaults to greedy decoding)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --max-tokens 200 --backend ggml_metal \
    --temperature 0.7 --top-p 0.9 --top-k 40 --repeat-penalty 1.2 --seed 42

Image editing (Qwen-Image-Edit)

# Prompt + input image -> edited image. The VAE + Qwen2.5-VL text-encoder
# companions are resolved next to the DiT GGUF (or pass --qwen-image-vae /
# --qwen-image-vl / --qwen-image-mmproj).
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <qwen-image-edit-DiT.gguf> --image input.png \
    --prompt "Make the sky a dramatic sunset." --output edited.png \
    --backend ggml_cuda --diffusion-steps 30 --cfg 2.5 --diffusion-seed 0

DiffusionGemma & inspection

# DiffusionGemma text-diffusion generation
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <diffusion-gemma.gguf> --input prompt.txt --backend ggml_metal \
    --max-tokens 256 --diffusion-steps 48 --diffusion-seed 0

# Inspect the rendered prompt and tokenization without running inference
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input prompt.txt --dump-prompt

Batch & benchmarks

# Batch processing (JSONL)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --input-jsonl requests.jsonl \
    --output results.txt --backend ggml_metal

# Multi-turn chat simulation with KV-cache reuse (mirrors the web UI behavior)
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --multi-turn-jsonl chat.jsonl \
    --backend ggml_metal --max-tokens 200

# Throughput benchmark: best-of-N prefill and decode timing
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --model <model.gguf> --backend ggml_metal \
    --benchmark --bench-prefill 256 --bench-decode 128 --bench-runs 3

The JSONL format is one JSON object per line:

{"id": "q1", "messages": [{"role": "user", "content": "What is 2+3?"}], "max_tokens": 50}
{"id": "q2", "messages": [{"role": "user", "content": "Write a haiku."}], "max_tokens": 100, "temperature": 0.8}

Configuration file (--config)

Instead of a long command line, pass a JSON file with --config. The server reads the same format. Command-line options always win—file values are applied first, then anything you also pass on the command line overrides them, so one file can be reused across machines while you override just what differs. Repeat --config to layer files (later files win). Comments and trailing commas are allowed.

# Use the file, but override the backend for this run
dotnet run --project TensorSharp.Cli/TensorSharp.Cli.csproj -- \
    --config config/cli-basic.json --backend ggml_cpu

Keys are the same long option names below (with or without the leading --). A string/number becomes --key value, true becomes the bare switch --key, and an array becomes a repeated flag.

Variables. Define shared values once under "variables" and reference them with ${name} in any string value (an undefined name falls back to an environment variable of the same name). Declare as many roots as you need.

Auto-download. Any file option can be an object with a local path and one or more urls. If path is missing it downloads from the first working URL (mirrors tried in order), saves it there, and reuses it next time; progress prints to stderr, and an optional sha256 verifies the file.

{
  "variables": { "modelRoot": "C:/models", "hf": "https://huggingface.co" },
  "backend": "ggml_cuda",
  "max-tokens": 256,
  "temperature": 0.7,
  "model": {
    "path": "${modelRoot}/Qwen3.5-9B-Q8_0.gguf",
    "urls": [ "${hf}/unsloth/Qwen3.5-9B-GGUF/resolve/main/Qwen3.5-9B-Q8_0.gguf" ]
  }
}

Ready-to-use examples live in the repository's config/ folder (cli-basic.json, server-basic.json, variables.json, auto-download.json, qwen-image-edit.json) — each uses real, public, ungated URLs, so it works on a fresh machine. See config/README.md for the full reference.

Command-line options

Running the CLI with no arguments — or with --help (also -h, -?, /?) — prints the full parameter reference: every option with its description, default, range, and an example. It exits before any logging or model machinery starts. Unknown arguments are ignored, so copy option names carefully—a misspelling may otherwise appear to succeed.

Input / output

OptionDescription
--model <path>Path to a GGUF model file (required).
--input <path>Text file containing the user prompt. One-shot text prompts always come from a file — --prompt is reserved for the Qwen-Image-Edit edit instruction.
--pdf <path>PDF document input (one-shot mode). Born-digital PDFs are inlined with their complete extracted text; scanned/image-only PDFs are rasterized to page images and require a vision model + --mmproj. --input becomes the question about the document; TS_PDF_MAX_PAGES caps the pages read (default: all).
--input-jsonl <path>JSONL file with batch requests (one JSON per line).
--multi-turn-jsonl <path>JSONL file for multi-turn chat simulation with KV-cache reuse.
--output <path>Write generated text to this file.
--image / --video / --audio <path>Media file for vision / video / audio inference (audio needs Gemma 4's audio encoder).
--mmproj <path>Path to the multimodal projector GGUF. Pass it explicitly: auto-detection recognizes only a small set of legacy companion filenames.

Runtime

OptionDescription
--max-tokens <N>Maximum tokens to generate (default: 100).
--backend <type>Compute backend: cpu, cuda, mlx, ggml_cpu, ggml_metal, ggml_cuda, ggml_vulkan (default: ggml_cpu).
--gpu-device <N>Vulkan device index for the ggml_vulkan backend on multi-GPU hosts (default: 0; env TS_GGML_VULKAN_DEVICE).
--list-gpusList the Vulkan devices ggml-vulkan can see (index + adapter name) and exit.
--helpPrint the full parameter reference (description, default, range, and an example per option) and exit; also shown when the CLI is started with no arguments.
--kv-cache-dtype <type>KV cache precision: f32, f16, q8_0, or q4_0 (default: auto — the backend/model pick; overrides the KV_CACHE_DTYPE env var). The block-quantized tiers require the native GGML flash-attention path; q4_0 (~1/7 the f32 footprint) targets very long 128K–256K contexts.
--interactive / -iStart the interactive REPL (see below).
--system <text> / --system-file <path>Seed the session's system prompt from text or a file. Only the interactive REPL and DiffusionGemma diffusion mode use it — plain one-shot text mode ignores the system prompt.
--thinkEnable thinking / reasoning mode (chain-of-thought).
--tools <path>JSON file with tool / function definitions.
--draft-model <path>Speculative-decoding drafter GGUF for architectures whose drafter ships as its own file — today DeepSeek V4's DSpark support module. It drafts a whole block per step and the trunk verifies it in one batched forward, so greedy output is unchanged. Needs --backend cuda or ggml_cuda and a pure-argmax sampler (any temperature, top-k/p, or penalty turns it off). Env: TS_DSV4_DSPARK. → DSpark
--spec-draft-n-max <N>Cap on tokens drafted per speculative block (default: the drafter's trained block size, 5 for DSpark).
--spec-draft-conf-min <p>Minimum cumulative acceptance probability for a drafted position to be kept (default 0.35). Lower drafts further and rolls back more; higher falls back to plain decode sooner.
--dump-promptRender the prompt + tokenization and exit (no generation).
--tp <N>Tensor parallelism degree — split the model across N GPUs in one process (default: 1). Requires --backend cuda, ggml_cuda, or ggml_vulkan; TENSORSHARP_TP_DEVICES picks which GPUs. → Multi-GPU & Multi-Node
--tp-node-id <N>This node's 0-based ID for multi-node distributed tensor parallelism. Use together with --tp-peers.
--tp-peers <list>Comma-separated host:port list of every node in the cluster (e.g. 192.168.1.10:9500,192.168.1.11:9500). Identical on all nodes; the port is not a default and must be reachable between them.
--config <path>Read options from a JSON config file (command-line options override it). Supports ${variables} and auto-downloading models. Repeatable.

Sampling

The CLI defaults to greedy decoding: temperature 0, top-k 0, top-p 1.0, min-p 0, penalties off, seed -1 (unlike the server, whose defaults match Ollama). Pass the flags below to opt into sampling; they also carry into the interactive REPL.

OptionDescription
--temperature <f>Sampling temperature (default 0 = greedy).
--top-k <N>Top-K filtering (default 0 = disabled).
--top-p <f>Nucleus sampling threshold (default 1.0 = disabled).
--min-p <f>Minimum probability filtering (default 0 = disabled).
--repeat-penalty <f>Repetition penalty (default 1.0 = none).
--presence-penalty <f> / --frequency-penalty <f>Presence / frequency penalties (default 0 = disabled).
--seed <N>Random seed (default -1 = non-deterministic).
--stop <string>Stop sequence (can be repeated).

DiffusionGemma, benchmarks & logging

OptionDescription
--diffusion-steps <N>DiffusionGemma denoising steps per block (default: 48).
--diffusion-seed <N>DiffusionGemma deterministic sampler seed (default: 0).
--diffusion-blocks <N>Block-autoregressive canvas count (0 derives it from --max-tokens).
--image <path> / --prompt <text> / --output <path>Qwen-Image-Edit: input image, edit instruction, and output PNG (default edited.png). Reuses --diffusion-steps / --diffusion-seed.
--cfg <F>Qwen-Image-Edit true-CFG guidance scale (omit for auto: 2.5, or 1.0 when a Lightning LoRA is loaded; <= 1 disables the negative pass).
--qwen-image-vae / --qwen-image-vl / --qwen-image-mmproj <path>Override the resolved Qwen-Image-Edit companion GGUFs (VAE / Qwen2.5-VL text encoder / mmproj).
--qwen-image-lora <path>Qwen-Image-Edit Lightning distillation LoRA (.safetensors), merged into the DiT at load time; auto-derives the denoise step count (e.g. 4 or 8) and switches CFG to 1.0 (no negative pass).
--benchmarkRun a synthetic prefill/decode throughput benchmark.
--bench-prefill / --bench-decode / --bench-runs <N>Synthetic prefill length, decode length, and run count.
--bench-kvcache / --bench-kv-turns <N>Multi-turn KV-cache reuse benchmark (with-cache vs forced-reset).
--warmup-runs <N>Throw-away forward passes before timing (default: 0).
--log-level <lvl>trace, debug, info, warning, error, critical, off.
--log-dir <path> / --log-file <0|1> / --log-console <0|1>JSON-line file logger directory and toggles.

See the full reference, including --test, --test-templates, and chunked-prefill correctness checks, on the API Reference page.

Interactive REPL commands

Launch with --interactive / -i. Anything that does not start with / is a user turn; type /help for the list. The prompt header shows the current model, backend, architecture, context length, projector, conversation depth, and pending attachments. Press Ctrl+C while generating to interrupt; at the prompt to exit.

Conversation

CommandDescription
/help, /?Show all interactive commands.
/exit, /quitLeave the session.
/reset, /newClear conversation history and KV cache.
/history · /save <file>Print the conversation / write the transcript to a file.
/system <text>Set the system prompt (an empty argument clears it).
/think on|off · /multiline on|offToggle reasoning mode / multi-line input.

Model & runtime

CommandDescription
/info, /statusShow loaded model, backend, architecture, context/vocab size, projector, depth.
/model <path>Load a different .gguf on the current backend (resets the session).
/backend <name>Reload the current model on a different backend.
/mmproj <path>Load or replace the multimodal projector. Alias: /projector.

Sampling (live) & uploads (next turn)

CommandDescription
/sampling, /showPrint current sampling configuration.
/max · /temp · /topk · /topp · /minpSet reply length / temperature / top-k / top-p / min-p.
/repeat · /presence · /frequency · /seedSet penalties and the random seed.
/stop <text> · /clearstopAdd / clear stop sequences.
/image <path> · /audio · /video · /textAttach media or inline a text file for the next turn.
/clearattachDrop pending attachments without sending a turn.