TensorRT-LLM inference on NVIDIA Triton for NixOS
0
stars
8
commits
Haskell
primary language
Mar 7, 2026
updated
TensorRT-LLM inference on NVIDIA Triton, with Servant-based tool server.
| Package | Description |
|---|---|
tool-server | Servant API with OpenAPI3 — code sandbox, attestation, tools |
openai-proxy | Haskell AI Gateway — OpenAI-compatible chat completions |
trtllm-validate | Engine validation CLI |
trtllm-engine | TRT-LLM engine building infrastructure |
# Run the demo (OpenAI Proxy + Tool Server + Open WebUI)
nix run github:straylight-software/triton-tensorrt-llm
# Point to your Triton server
nix run github:straylight-software/triton-tensorrt-llm -- --triton-url http://gpu-server:8000
# Then open http://localhost:3000 for chat UI
# OpenAI Proxy only
nix run .#openai-proxy
# Tool Server only (code sandbox + attestation)
nix run .#tool-server
# Open WebUI only
nix run .#open-webui
# Build a TRT-LLM engine (requires GPU)
nix build .#qwen3-32b-engine --option sandbox false
# Validate an engine
nix run .#trtllm-validate -- detect-quant /path/to/model
The default nix run starts a full stack:
| Service | Port | Description |
|---|---|---|
| Open WebUI | 3000 | Chat interface |
| OpenAI Proxy | 9000 | OpenAI-compatible API |
| Tool Server | 9001 | Code sandbox + attestation |
# Default: connects to localhost:8000 for Triton
nix run .
# Connect to remote Triton
nix run . -- --triton-url http://192.168.1.100:8000
# Custom model name
nix run . -- --model llama3
# Proxy only (no WebUI)
nix run . -- --proxy-only
| Variable | Default | Description |
|---|---|---|
TRITON_URL | http://localhost:8000 | Triton server URL |
MODEL_NAME | qwen3 | Model name |
OPENAI_PROXY_PORT | 9000 | OpenAI proxy port |
TOOL_SERVER_PORT | 9001 | Tool server port |
OPEN_WEBUI_PORT | 3000 | Open WebUI port |
The tool-server provides a Servant-based API with automatic OpenAPI 3.0 spec generation.
| Method | Endpoint | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /openapi.json | OpenAPI 3.0 specification |
| Code Sandbox | ||
| POST | /code/workspace | Create isolated workspace |
| GET | /code/workspace/:id | Get workspace info |
| GET | /code/workspaces | List all workspaces |
| POST | /code/write | Write file to workspace |
| POST | /code/read | Read file from workspace |
| GET | /code/files/:workspace_id | List files in workspace |
| POST | /code/ast_search | AST pattern search |
| POST | /code/ast_replace | AST pattern replace |
| POST | /code/compile | Compile/type-check file |
| Identity & Attestation | ||
| GET | /identity/info | Get agent identity (fingerprint, DID) |
| POST | /attest | Create signed attestation |
| GET | /attest/log | Get attestation chain |
| GET | /attest/:commit/verify | Verify attestation signature |
| Tools | ||
| GET | /tools/search | Web search via SearXNG |
| GET | /tools/code_search | Code-focused web search |
| GET | /tools/read_url | Read URL content via Jina |
| Coeffects | ||
| GET | /coeffects/manifest | Resource requirements per endpoint |
# Start server
TOOL_SERVER_PORT=9001 nix run .#tool-server
# Get OpenAPI spec
curl http://localhost:9001/openapi.json | jq .
# Create workspace
curl -X POST http://localhost:9001/code/workspace \
-H "Content-Type: application/json" \
-d '{"workspace_id": "my-project"}'
# Write a file
curl -X POST http://localhost:9001/code/write \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "my-project",
"path": "src/Main.hs",
"content": "module Main where\n\nmain :: IO ()\nmain = putStrLn \"Hello\""
}'
# Compile (type-check)
curl -X POST http://localhost:9001/code/compile \
-H "Content-Type: application/json" \
-d '{"workspace_id": "my-project", "path": "src/Main.hs"}'
# AST search
curl -X POST http://localhost:9001/code/ast_search \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "my-project",
"pattern": "putStrLn $_",
"language": "haskell"
}'
# Create attestation (requires identity configured)
curl -X POST http://localhost:9001/attest \
-H "Content-Type: application/json" \
-d '{
"attest_type": "task",
"context": "Implemented feature X",
"thought": "Considered approaches A and B, chose A for simplicity",
"action": "Created 3 files, modified 2",
"coeffects": {"filesystem": "readWrite", "network": "read"}
}'
The "Language Coset" for compilation:
| Language | Extension | Compiler | Check Mode |
|---|---|---|---|
| Rust | .rs | rustc | --emit=metadata |
| Haskell | .hs, .lhs | ghc | -fno-code |
| Lean | .lean | lean | --run |
| Dhall | .dhall | dhall | type |
| PureScript | .purs | purs | compile |
lib/
├── CodeSandbox.hs # Pure domain logic (reusable)
└── Attestation.hs # Identity & signing (reusable)
tool-server/
├── API.hs # Servant API type definition
├── API/Types.hs # Request/Response with OpenAPI3 schemas
└── Server.hs # Thin handlers over lib/
The openai-proxy provides an OpenAI-compatible interface to TensorRT-LLM.
| Endpoint | Description |
|---|---|
POST /v1/chat/completions | Chat completions (streaming & non-streaming) |
GET /v1/models | List available models |
GET /health | Health check |
GET /tools/search | Web search |
GET /tools/read_url | URL reader |
| Environment Variable | Default | Description |
|---|---|---|
OPENAI_PROXY_PORT | 9000 | Server port |
TRITON_URL | http://localhost:8000 | Triton backend URL |
MODEL_NAME | qwen3 | Model name for API |
SEARXNG_URL | — | SearXNG instance URL |
STRIP_THINKING | true | Strip <think> blocks |
METRICS_ENABLED | false | Enable ClickHouse metrics |
The module is self-contained — it pulls in all dependencies including:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
triton-tensorrt-llm.url = "github:straylight-software/triton-tensorrt-llm";
};
outputs = { self, nixpkgs, triton-tensorrt-llm, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# Apply the overlay to get packages
{ nixpkgs.overlays = [ triton-tensorrt-llm.overlays.default ]; }
# Import the NixOS module
triton-tensorrt-llm.nixosModules.default
# Configure the service
{
services.triton-trtllm = {
enable = true;
model = "qwen3";
enginePath = /var/lib/trtllm/engines/qwen3;
triton = {
enable = true;
httpPort = 8000;
};
openaiProxy = {
enable = true;
port = 9000;
};
toolServer = {
enable = true;
port = 9001;
};
searxng.enable = true; # Native SearXNG, no Docker
};
}
];
};
};
}
| Option | Default | Description |
|---|---|---|
enable | false | Enable the service |
model | "qwen3" | Model name for service identification |
enginePath | — | Path to TRT-LLM engine directory |
triton.enable | true | Enable Triton server |
triton.httpPort | 8000 | Triton HTTP port |
openaiProxy.enable | true | Enable OpenAI proxy |
openaiProxy.port | 9000 | OpenAI proxy port |
toolServer.enable | false | Enable tool server |
toolServer.port | 9001 | Tool server port |
searxng.enable | false | Enable SearXNG (native) |
searxng.port | 8888 | SearXNG port |
You can override packages if needed:
services.triton-trtllm = {
enable = true;
package.openaiProxy = myCustomProxy;
package.toolServer = myCustomToolServer;
# ...
};
# Enter dev shell
nix develop
# Run tool-server tests
cd tool-server && cabal test
# Run openai-proxy property tests
nix build .#openai-proxy.tests.proxy-proptest
# Check flake
nix flake check
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ AI Agent / │────▶│ tool-server │────▶│ Compilers │
│ Open WebUI │ │ (port 9001) │ │ ast-grep │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
│ ┌────────┴────────┐
│ ▼ ▼
│ ┌──────────┐ ┌────────────┐
│ │ Identity │ │ Attestation│
│ │ Ed25519 │ │ Git Repo │
│ └──────────┘ └────────────┘
│
▼
┌──────────────────┐ ┌─────────────────┐
│ openai-proxy │────▶│ Triton Server │
│ (port 9000) │ │ (port 8000) │
└────────┬─────────┘ └─────────────────┘
│
┌────────┼────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌─────────┐
│ SearXNG │ │ Jina │ │ Metrics │
│ (8888) │ │ (remote) │ │ (CH) │
└─────────┘ └──────────┘ └─────────┘
The tool-server tracks resource requirements (coeffects) per endpoint:
{
"tools": {
"code/compile": {"filesystem": "readWrite"},
"code/ast_search": {"filesystem": "read"},
"code/ast_replace": {"filesystem": "readWrite"},
"attest": {"filesystem": "readWrite", "crypto": "sign"},
"tools/search": {"network": "read"},
"tools/read_url": {"network": "read"}
},
"algebra": "Coeffect semiring: (R, join, 0, meet, 1)"
}
This supports the Aleph coeffect system for provenance tracking.
MIT
8 commits
Haskell
55.9%
Nix
33.8%
Python
8.8%
Shell
1.5%
TensorRT-LLM inference on NVIDIA Triton for NixOS
0
stars
8
commits
Haskell
primary language
Mar 7, 2026
updated
TensorRT-LLM inference on NVIDIA Triton, with Servant-based tool server.
| Package | Description |
|---|---|
tool-server | Servant API with OpenAPI3 — code sandbox, attestation, tools |
openai-proxy | Haskell AI Gateway — OpenAI-compatible chat completions |
trtllm-validate | Engine validation CLI |
trtllm-engine | TRT-LLM engine building infrastructure |
# Run the demo (OpenAI Proxy + Tool Server + Open WebUI)
nix run github:straylight-software/triton-tensorrt-llm
# Point to your Triton server
nix run github:straylight-software/triton-tensorrt-llm -- --triton-url http://gpu-server:8000
# Then open http://localhost:3000 for chat UI
# OpenAI Proxy only
nix run .#openai-proxy
# Tool Server only (code sandbox + attestation)
nix run .#tool-server
# Open WebUI only
nix run .#open-webui
# Build a TRT-LLM engine (requires GPU)
nix build .#qwen3-32b-engine --option sandbox false
# Validate an engine
nix run .#trtllm-validate -- detect-quant /path/to/model
The default nix run starts a full stack:
| Service | Port | Description |
|---|---|---|
| Open WebUI | 3000 | Chat interface |
| OpenAI Proxy | 9000 | OpenAI-compatible API |
| Tool Server | 9001 | Code sandbox + attestation |
# Default: connects to localhost:8000 for Triton
nix run .
# Connect to remote Triton
nix run . -- --triton-url http://192.168.1.100:8000
# Custom model name
nix run . -- --model llama3
# Proxy only (no WebUI)
nix run . -- --proxy-only
| Variable | Default | Description |
|---|---|---|
TRITON_URL | http://localhost:8000 | Triton server URL |
MODEL_NAME | qwen3 | Model name |
OPENAI_PROXY_PORT | 9000 | OpenAI proxy port |
TOOL_SERVER_PORT | 9001 | Tool server port |
OPEN_WEBUI_PORT | 3000 | Open WebUI port |
The tool-server provides a Servant-based API with automatic OpenAPI 3.0 spec generation.
| Method | Endpoint | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /openapi.json | OpenAPI 3.0 specification |
| Code Sandbox | ||
| POST | /code/workspace | Create isolated workspace |
| GET | /code/workspace/:id | Get workspace info |
| GET | /code/workspaces | List all workspaces |
| POST | /code/write | Write file to workspace |
| POST | /code/read | Read file from workspace |
| GET | /code/files/:workspace_id | List files in workspace |
| POST | /code/ast_search | AST pattern search |
| POST | /code/ast_replace | AST pattern replace |
| POST | /code/compile | Compile/type-check file |
| Identity & Attestation | ||
| GET | /identity/info | Get agent identity (fingerprint, DID) |
| POST | /attest | Create signed attestation |
| GET | /attest/log | Get attestation chain |
| GET | /attest/:commit/verify | Verify attestation signature |
| Tools | ||
| GET | /tools/search | Web search via SearXNG |
| GET | /tools/code_search | Code-focused web search |
| GET | /tools/read_url | Read URL content via Jina |
| Coeffects | ||
| GET | /coeffects/manifest | Resource requirements per endpoint |
# Start server
TOOL_SERVER_PORT=9001 nix run .#tool-server
# Get OpenAPI spec
curl http://localhost:9001/openapi.json | jq .
# Create workspace
curl -X POST http://localhost:9001/code/workspace \
-H "Content-Type: application/json" \
-d '{"workspace_id": "my-project"}'
# Write a file
curl -X POST http://localhost:9001/code/write \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "my-project",
"path": "src/Main.hs",
"content": "module Main where\n\nmain :: IO ()\nmain = putStrLn \"Hello\""
}'
# Compile (type-check)
curl -X POST http://localhost:9001/code/compile \
-H "Content-Type: application/json" \
-d '{"workspace_id": "my-project", "path": "src/Main.hs"}'
# AST search
curl -X POST http://localhost:9001/code/ast_search \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "my-project",
"pattern": "putStrLn $_",
"language": "haskell"
}'
# Create attestation (requires identity configured)
curl -X POST http://localhost:9001/attest \
-H "Content-Type: application/json" \
-d '{
"attest_type": "task",
"context": "Implemented feature X",
"thought": "Considered approaches A and B, chose A for simplicity",
"action": "Created 3 files, modified 2",
"coeffects": {"filesystem": "readWrite", "network": "read"}
}'
The "Language Coset" for compilation:
| Language | Extension | Compiler | Check Mode |
|---|---|---|---|
| Rust | .rs | rustc | --emit=metadata |
| Haskell | .hs, .lhs | ghc | -fno-code |
| Lean | .lean | lean | --run |
| Dhall | .dhall | dhall | type |
| PureScript | .purs | purs | compile |
lib/
├── CodeSandbox.hs # Pure domain logic (reusable)
└── Attestation.hs # Identity & signing (reusable)
tool-server/
├── API.hs # Servant API type definition
├── API/Types.hs # Request/Response with OpenAPI3 schemas
└── Server.hs # Thin handlers over lib/
The openai-proxy provides an OpenAI-compatible interface to TensorRT-LLM.
| Endpoint | Description |
|---|---|
POST /v1/chat/completions | Chat completions (streaming & non-streaming) |
GET /v1/models | List available models |
GET /health | Health check |
GET /tools/search | Web search |
GET /tools/read_url | URL reader |
| Environment Variable | Default | Description |
|---|---|---|
OPENAI_PROXY_PORT | 9000 | Server port |
TRITON_URL | http://localhost:8000 | Triton backend URL |
MODEL_NAME | qwen3 | Model name for API |
SEARXNG_URL | — | SearXNG instance URL |
STRIP_THINKING | true | Strip <think> blocks |
METRICS_ENABLED | false | Enable ClickHouse metrics |
The module is self-contained — it pulls in all dependencies including:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
triton-tensorrt-llm.url = "github:straylight-software/triton-tensorrt-llm";
};
outputs = { self, nixpkgs, triton-tensorrt-llm, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# Apply the overlay to get packages
{ nixpkgs.overlays = [ triton-tensorrt-llm.overlays.default ]; }
# Import the NixOS module
triton-tensorrt-llm.nixosModules.default
# Configure the service
{
services.triton-trtllm = {
enable = true;
model = "qwen3";
enginePath = /var/lib/trtllm/engines/qwen3;
triton = {
enable = true;
httpPort = 8000;
};
openaiProxy = {
enable = true;
port = 9000;
};
toolServer = {
enable = true;
port = 9001;
};
searxng.enable = true; # Native SearXNG, no Docker
};
}
];
};
};
}
| Option | Default | Description |
|---|---|---|
enable | false | Enable the service |
model | "qwen3" | Model name for service identification |
enginePath | — | Path to TRT-LLM engine directory |
triton.enable | true | Enable Triton server |
triton.httpPort | 8000 | Triton HTTP port |
openaiProxy.enable | true | Enable OpenAI proxy |
openaiProxy.port | 9000 | OpenAI proxy port |
toolServer.enable | false | Enable tool server |
toolServer.port | 9001 | Tool server port |
searxng.enable | false | Enable SearXNG (native) |
searxng.port | 8888 | SearXNG port |
You can override packages if needed:
services.triton-trtllm = {
enable = true;
package.openaiProxy = myCustomProxy;
package.toolServer = myCustomToolServer;
# ...
};
# Enter dev shell
nix develop
# Run tool-server tests
cd tool-server && cabal test
# Run openai-proxy property tests
nix build .#openai-proxy.tests.proxy-proptest
# Check flake
nix flake check
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ AI Agent / │────▶│ tool-server │────▶│ Compilers │
│ Open WebUI │ │ (port 9001) │ │ ast-grep │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
│ ┌────────┴────────┐
│ ▼ ▼
│ ┌──────────┐ ┌────────────┐
│ │ Identity │ │ Attestation│
│ │ Ed25519 │ │ Git Repo │
│ └──────────┘ └────────────┘
│
▼
┌──────────────────┐ ┌─────────────────┐
│ openai-proxy │────▶│ Triton Server │
│ (port 9000) │ │ (port 8000) │
└────────┬─────────┘ └─────────────────┘
│
┌────────┼────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌─────────┐
│ SearXNG │ │ Jina │ │ Metrics │
│ (8888) │ │ (remote) │ │ (CH) │
└─────────┘ └──────────┘ └─────────┘
The tool-server tracks resource requirements (coeffects) per endpoint:
{
"tools": {
"code/compile": {"filesystem": "readWrite"},
"code/ast_search": {"filesystem": "read"},
"code/ast_replace": {"filesystem": "readWrite"},
"attest": {"filesystem": "readWrite", "crypto": "sign"},
"tools/search": {"network": "read"},
"tools/read_url": {"network": "read"}
},
"algebra": "Coeffect semiring: (R, join, 0, meet, 1)"
}
This supports the Aleph coeffect system for provenance tracking.
MIT
8 commits
Haskell
55.9%
Nix
33.8%
Python
8.8%
Shell
1.5%