Skip to content

Repository files navigation

ForgeLLM

License: Apache-2.0 Python

A from-scratch Llama-family serving engine implementing block-based KV caching, continuous batching, streamed OpenAI-compatible generation, INT8 weight quantization, and GPU kernel profiling—without using an external generation or serving framework.

ForgeLLM is a systems portfolio project: the transformer forward pass, prefill/decode loop, cache allocator, sampler, scheduler, cancellation path, and telemetry live in this repository. PyTorch provides tensor primitives and GEMMs; tokenizers, safetensors, and Hugging Face Hub provide file-format and artifact integration. vLLM and Transformers appear only in isolated benchmark baselines.

Status: the CPU toy-model path is CI-tested and has a committed smoke sweep. Real-model CUDA results are intentionally not committed until produced by the target GPU; the repository never substitutes estimated or invented numbers.

Why this matters across industries

Serving-engine internals — KV-cache management, continuous batching, and scheduling under concurrent load — are the exact mechanics behind every production LLM deployment, making this directly relevant to any tech/AI role building or operating inference infrastructure. The engineering discipline generalizes further: the same batching/scheduling/cache-eviction problem shows up in any resource-constrained serving system, and the project's own refusal to report GPU numbers it hasn't actually produced on target hardware is the kind of evidence discipline that matters equally in quant/finance infrastructure claims and in consulting engagements where a client needs to trust a performance number, not just hear one.

ForgeLLM dashboard under concurrent load

Architecture at a glance

flowchart LR
    C["OpenAI client"] --> A["FastAPI · auth · rate limit"]
    A --> Q["Bounded admission queue"]
    Q --> S["Static / continuous scheduler"]
    S --> E["Explicit prefill + decode engine"]
    E --> T["Tokenizer"]
    E --> K["Block KV allocator"]
    E --> P["Seeded sampler"]
    E --> M["Llama / Mistral / Qwen2 model"]
    M --> B["PyTorch · Triton · CUDA extension"]
    B --> G["GPU"]
    S --> O["Prometheus + live dashboard"]
Loading

At each decode boundary, completed and cancelled sequences leave, queued work enters, and the remaining sequences form the next GPU batch. Long prompts are split into bounded chunks and interleaved with decode. Per-request RNGs make sampling reproducible even when batch membership changes.

What is implemented

  • Llama-family decoder: RoPE, RMSNorm, grouped-query attention, SwiGLU, causal masking, safetensors loading, and tied embeddings.
  • Explicit autoregressive prefill/decode with greedy, temperature, top-k, top-p, EOS, stop strings, token limits, and deterministic seeds.
  • Physical KV blocks with logical per-request block tables, worst-case admission reservation, reclamation, capacity errors, and occupancy/byte telemetry.
  • Decode-only block-table attention reads physical KV blocks directly; the Triton kernel uses online softmax without constructing padded batch-wide K/V.
  • No-batch, static-batch, and iteration-boundary continuous-batch schedulers; chunked prefill, token budgets, FCFS, and aged shortest-prompt-first policies.
  • Streaming/non-streaming POST /v1/chat/completions, disconnect cancellation, API keys, token-bucket limits, queue limits, timeouts, structured errors, tracing IDs, graceful shutdown, health/readiness, and Prometheus metrics.
  • Reference PyTorch, fused Triton RMSNorm, and a separately compiled C++/CUDA RMSNorm, with correctness and latency benchmarks.
  • Symmetric per-output-channel INT8 weights. Quantization is ours; GEMM is PyTorch after dequantization, so this is a memory experiment, not an optimized INT8 execution claim.
  • Automated load matrix, machine-readable JSON/CSV, plots, and honest external controls for naive Transformers and vLLM.

Quick start

The toy model validates the complete service without downloading weights. Use float32 on CPU:

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,bench]"
forge-llm serve --model toy --device cpu --dtype float32

In another terminal:

curl http://localhost:8000/v1/chat/completions \
  -H "Authorization: Bearer dev-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"toy","messages":[{"role":"user","content":"Hello"}],"max_tokens":16,"stream":true}'

Then open http://localhost:8000/dashboard. The default key field is dev-key; production deployments must set FORGE_ENVIRONMENT=production and a non-default FORGE_API_KEYS. Production mode refuses to start with the development key. Terminate TLS at a trusted reverse proxy or ingress.

Serve a supported open checkpoint (TinyLlama is the recommended first GPU target):

FORGE_API_KEYS=replace-me forge-llm serve \
  --model TinyLlama/TinyLlama-1.1B-Chat-v1.0 \
  --device cuda --dtype float16 --scheduler continuous

CUDA container:

docker build -f Dockerfile.cuda -t forge-llm:cuda .
docker run --gpus all -p 8000:8000 -e FORGE_MODEL="$MODEL" \
  -e FORGE_ENVIRONMENT=production -e FORGE_API_KEYS=replace-me forge-llm:cuda

The manually dispatched GPU acceptance workflow targets an isolated, self-hosted NVIDIA runner and uploads kernel, API, and optional full-matrix evidence tied to the exact commit SHA.

Supported architecture families are llama, mistral, and qwen2. Unsupported RoPE scaling is rejected at load time rather than silently producing wrong tokens.

Measurement, not marketing

The repository commits a labeled CPU toy smoke run; GPU result slots remain empty by design. Run the same matrix on your target GPU:

python -m benchmarks.run_matrix --experiment all \
  --model TinyLlama/TinyLlama-1.1B-Chat-v1.0
python -m benchmarks.plot_results benchmarks/results/matrix.csv
python -m benchmarks.kernel_benchmark
python -m benchmarks.paged_attention_benchmark
Experiment Variants Recorded outputs
Batching batch=1 vs continuous tok/s, req/s, p50/p95/p99, TTFT
Scheduling static vs continuous queue time, throughput, tail latency
Precision FP16/BF16 vs INT8 weights GPU memory, throughput, latency
KV cache enabled vs full recomputation generation throughput, memory
Saturation 1/2/4/8/16/32 clients saturation point and tail latency
Context increasing prompt lengths prefill throughput and TTFT

benchmarks/load_test.py writes per-request data plus summaries. Environment metadata includes GPU/VRAM, CPU/RAM, CUDA, PyTorch, model, input/output settings, and concurrency. See benchmark methodology before making comparisons.

Executed construction-environment smoke sweep

This is an API/scheduler regression measurement, not an LLM/GPU performance claim: 2-layer random toy model, CPU FP32, 24 streamed requests per point, 8-token limit, Windows 11, 8 logical CPUs, PyTorch 2.13.0 CPU.

Clients Generation tok/s E2E p50 E2E p95 E2E p99 TTFT p95
1 184.7 37.8 ms 47.1 ms 49.1 ms 16.2 ms
2 288.3 51.9 ms 65.8 ms 67.7 ms 22.7 ms
4 317.3 94.2 ms 111.1 ms 112.2 ms 41.4 ms
8 376.0 164.4 ms 178.6 ms 180.2 ms 72.4 ms

CPU toy-model concurrency smoke sweep

Throughput was still increasing at 8 clients in this smoke sweep while tail latency rose, so this bounded run does not claim a CPU saturation point. Raw per-request JSON and the CSV summary are in benchmarks/results/; reproduce all live checks with scripts/live_verify.ps1 on Windows.

Repository map

src/forge/          engine, scheduler, API, kernels, observability
csrc/               minimal C++/CUDA systems extension
tests/              unit, integration, API, concurrency, cache, kernel tests
benchmarks/         load matrix, plots, kernel test, external baselines
dashboard/          dependency-free real-time operations view
docs/               architecture, ADRs, report, profiling, limitations
examples/           streaming client
scripts/            build and operational helpers

Technical deep dive

Engineering stance

The block allocator is inspired by the idea of paged virtual memory, not by vLLM internals. Its gather path favors legibility and correctness and is expected to lose to vLLM's paged-attention kernels. The project is designed to reveal that gap with profiles and measurements, then make the next optimization obvious—not to obscure it with an unfair benchmark.

Apache-2.0 licensed. Contributions should include a correctness test and, for performance claims, the raw machine-readable result.

About

No description or website provided.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages