This guide is for operators, developers, and architects who deploy, secure, extend, or maintain TextGen. It covers the system architecture, the complete configuration surface, the loader and sampler backends, the API server, security model, operations, and internals deep enough to reason about — or contribute to — the codebase.
For end-user, install, and day-to-day usage, see the companion User Guide.
1. Executive Summary
TextGen is a Python application that wraps multiple local LLM inference runtimes behind a
single Gradio web UI and an optional FastAPI OpenAI/Anthropic-compatible HTTP server.
It is a desktop/self-hosted product, not a multi-tenant service: there is no database, no
user accounts beyond optional HTTP Basic auth, and the unit of state is the user_data/
directory on disk.
Architecturally it is three layers:
- A launcher/installer layer (
one_click.py,start_*.sh/.bat,desktop/main.js) that provisions an isolated Python 3.13 + PyTorch environment per hardware target, or ships as a self-contained portable build. - An orchestration layer (
server.py+modules/) that parses configuration, loads a model through one of five swappable loaders, builds the Gradio interface, and manages generation. - A serving layer (
modules/api/) that re-exposes the loaded model as OpenAI- and Anthropic-compatible HTTP endpoints.
Key design facts an operator must know:
- The active model is global, singleton state (
modules/shared.py:model). One model is loaded at a time (image models load separately and concurrently). - The llama.cpp loader is out-of-process: it spawns and supervises a
llama-serversubprocess. Clean shutdown explicitly stops it. - Configuration precedence is command line →
CMD_FLAGS.txt→settings.yaml→ built-in defaults. - The API is localhost-locked by default and only opens to the network on explicit opt-in.
2. Architecture
2.1 Component overview
┌──────────────────────────────────────────────┐
│ Launcher / Installer │
start_windows.bat ───►│ one_click.py: conda env + PyTorch per-GPU │
start_linux.sh │ desktop/main.js: Electron window (portable) │
└───────────────────┬──────────────────────────┘
│ python server.py [flags]
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ server.py (entry point) │
│ • modules/shared.py → argparse CLI, settings dict, global state │
│ • modules/paths.py → resolve user_data dir │
│ • loads settings.yaml, CMD_FLAGS.txt, mcp.json │
└───────────────┬───────────────────────────────────┬──────────────────────┘
│ create_interface() │ if --api / --nowebui
▼ ▼
┌───────────────────────────────────┐ ┌────────────────────────────────────┐
│ Gradio UI (modules/ui_*.py) │ │ FastAPI server (modules/api/) │
│ Tabs: Chat, Notebook, Parameters,│ │ uvicorn on :5000 │
│ Character, Model, Image, Train, │ │ /v1/* OpenAI + /v1/messages │
│ Session │ │ Anthropic │
└───────────────┬───────────────────┘ └────────────────┬───────────────────┘
│ │
└──────────────┬──────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Generation core │
│ modules/text_generation.py · modules/chat.py · modules/sampler_hijack │
└───────────────┬──────────────────────────────────────────────────────────┘
▼ modules/models.py dispatches to one loader
┌──────────────┬──────────────┬───────────────┬─────────────┬──────────────┐
│ llama.cpp │ Transformers │ ExLlamav3_HF │ ExLlamav3 │ TensorRT-LLM │
│ (subprocess) │ (in-process) │ (in-process) │ (in-process)│ (in-process) │
└──────────────┴──────────────┴───────────────┴─────────────┴──────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ user_data/ (the entire persistent state surface) │
│ models · loras · characters · presets · instruction-templates · grammars │
│ logs · cache · settings.yaml · CMD_FLAGS.txt · mcp.json │
└──────────────────────────────────────────────────────────────────────────┘
2.2 Repository structure
| Path | Responsibility |
|---|---|
server.py |
Entry point: parse config, load model, build Gradio UI, start API, restart loop |
download-model.py |
Standalone Hugging Face model downloader (ModelDownloader class) |
one_click.py |
Installer/updater: conda env, PyTorch per-GPU, requirements, update wizard |
modules/ |
Core engine — config, loaders, generation, UI, samplers, tools |
modules/api/ |
FastAPI OpenAI/Anthropic-compatible server |
modules/grammar/ |
GBNF grammar parsing + constrained-logits processor |
extensions/ |
Optional bundled extensions (TTS, STT, RAG, translation, …) |
user_data/ |
Per-user state (models, presets, characters, logs, config files) |
requirements/ |
full/ (per-GPU) and portable/ dependency sets |
css/, js/ |
Front-end assets (fonts, KaTeX, highlight.js, morphdom, UI logic) |
desktop/ |
Electron desktop wrapper (main.js, preload.js) |
docker/ |
Per-vendor Dockerfiles (nvidia, amd, intel, cpu, TensorRT-LLM) |
2.3 Key modules
| Module | Role |
|---|---|
modules/shared.py |
Single source of truth for the argparse CLI, the settings dict, and all global runtime state |
modules/paths.py |
resolve_user_data_dir() — portable/electron/in-tree precedence |
modules/models.py |
load_model() / unload_model() / unload_model_if_idle() dispatch |
modules/models_settings.py |
Reads model metadata, auto-detects loader and parameters |
modules/loaders.py |
loaders_and_params + loaders_samplers — the per-loader capability matrix |
modules/llama_cpp_server.py |
Spawns and supervises the llama-server subprocess |
modules/text_generation.py |
The generation entry points and stop handling |
modules/chat.py |
Chat history, templates, character handling, attachments |
modules/sampler_hijack.py |
Custom sampler implementations patched into Transformers |
modules/presets.py |
Default sampler values and preset YAML load/save |
modules/reasoning.py |
Detects model-family-specific "thinking" blocks |
modules/tool_use.py |
Tool calling + MCP server integration |
modules/web_search.py |
DuckDuckGo search with SSRF protection |
modules/extensions.py |
Extension discovery, setup, and hook application |
modules/ui_*.py |
One module per Gradio tab |
2.4 The startup sequence (server.py)
- Install SIGINT/SIGTERM handlers (second Ctrl-C forces exit; explicitly stops a running
LlamaServer). do_cmd_flags_warnings()— emit security warnings (see §6).- Load
settings.yaml(CLI--settingspath, elseuser_data/settings.yamlif present). - Detect MCP config (
user_data/mcp.json) and image-model CLI overrides. - Activate extensions from
settings['default_extensions'](the legacyopenaiextension is aliased to--api). - Resolve and optionally load the model (
--model, or--model-menufor a terminal picker). - If
--idle-timeout > 0, start the idle-unload watchdog thread. - If
--nowebui, start only the API; otherwisecreate_interface()builds Gradio, starts the API if requested, and enters a restart loop watchingshared.need_restart.
Note
The model is loaded before the UI is built. A bad
--modelor out-of-memory condition surfaces at startup, not on first chat.
3. Requirements & Environment
| Component | Requirement |
|---|---|
| Python | 3.13 exactly (the installer refuses to update across Python versions) |
| PyTorch | 2.9.0, installed from a per-GPU index (CUDA 12.8 / ROCm 7.2 / CPU / XPU) |
| NVIDIA | CUDA 12.8 (NVIDIA_CUDA128). The older CUDA 12.4 path was removed — needs a clean reinstall |
| AMD | ROCm 7.2, Linux only (no AMD-on-Windows install path) |
| Apple | M-series via the CPU/MPS PyTorch wheel |
| Intel | Arc (beta) via the XPU wheel |
| libstdc++ (Linux) | libstdcxx-ng >= 12.1.0 (installed via conda-forge) |
The installer writes its decisions to .installer_state.json (gpu_choice,
last_installed_commit, installed_wheels) so updates only re-download changed wheels.
3.1 Requirements layout
requirements/full/requirements.txt— NVIDIA CUDA 12.8 (the default full install).requirements/full/requirements_amd.txt,_apple_silicon.txt,_apple_intel.txt,_cpu_only.txt— per-hardware variants selected byget_requirements_file().requirements/portable/requirements.txt— the slim, llama.cpp-only portable set.
The full set pins a custom Gradio fork (oobabooga/gradio 4.37.2+custom.x, installed from
a wheel URL, not PyPI), plus prebuilt CUDA wheels for llama_cpp_binaries,
ik_llama_cpp_binaries, exllamav3, and flash_attn.
Important
Because Gradio is a pinned fork, do not
pip install gradioover it — you'll break the UI. Treat the requirements files as authoritative and let the update wizard manage them.
4. Configuration Mastery
4.1 Precedence model
command-line flags ► user_data/CMD_FLAGS.txt ► user_data/settings.yaml ► built-in defaults
(highest) (lowest)
CMD_FLAGS.txt— persistent CLI flags, one per line,#comments allowed. Prepended tosys.argvso real command-line flags still win.settings.yaml— persisted interface settings (thesettingsdict inshared.py). Auto-loaded fromuser_data/settings.yamlwithout needing--settings.config-user.yaml(in the model dir) — per-model setting overrides loaded byload_user_config().
4.2 The user_data/ directory
resolve_user_data_dir() picks the directory in this order: --user-data-dir flag →
(portable+electron) ../../user_data → (portable) ../user_data → in-tree user_data.
| Subfolder / file | Contents |
|---|---|
models/ |
Text model weights (GGUF, safetensors, …) |
loras/ |
LoRA adapters |
characters/ |
Character cards (YAML/JSON + optional images) |
presets/ |
Sampler presets (<name>.yaml) |
instruction-templates/ |
Jinja2 chat/instruction templates |
grammars/ |
GBNF grammar files |
image_models/, mmproj/ |
Diffusion image models; multimodal projectors |
logs/ |
Chat logs and notebook prompts |
cache/ |
Gradio temp, Electron data, profile-picture cache, disk KV cache |
settings.yaml, CMD_FLAGS.txt, mcp.json |
Config files |
Tip
user_data/is the only thing you need to back up. It is never modified by the updater. To migrate an install, copy this folder.
4.3 Full CLI reference
Flags are defined in modules/shared.py. Grouped by argparse section:
Basic
| Flag | Default | Purpose |
|---|---|---|
--user-data-dir |
auto | Override the user-data directory |
--model |
— | Model to load on startup |
--lora |
— | LoRA(s) to apply (space-separated) |
--model-dir, --lora-dir |
user_data/… |
Model/LoRA directories |
--model-menu |
off | Terminal model picker at launch |
--settings |
— | Path to a settings YAML |
--extensions |
— | Extensions to load (space-separated) |
--verbose |
off | Print prompts to the terminal |
--idle-timeout |
0 |
Minutes of inactivity before auto-unload (0 = never) |
--multi-user |
off | Stateless multi-user mode (small trusted teams only) |
Model loader & context
| Flag | Default | Purpose |
|---|---|---|
--loader |
auto | Force a loader: llama.cpp, Transformers, ExLlamav3_HF, ExLlamav3, TensorRT-LLM |
--ctx-size / --n_ctx / --max_seq_len |
0 |
Context tokens (0 = auto for llama.cpp, else 8192) |
--cache-type |
fp16 |
KV cache type (fp16, q8_0, q4_0; ExLlamaV3 q2–q8) |
llama.cpp
| Flag | Default | Purpose |
|---|---|---|
--gpu-layers / --n-gpu-layers |
-1 |
Layers on GPU (-1 = auto-fit) |
--cpu-moe |
off | Keep MoE experts on CPU |
--mmproj |
— | Multimodal projector for vision models |
--tensor-split |
— | Multi-GPU split proportions (e.g. 60,40) |
--split-mode |
layer |
layer/row/tensor/none |
--batch-size, --ubatch-size |
1024 |
App-level / device-level batch sizes |
--threads, --threads-batch |
0 |
CPU threads (0 = auto) |
--parallel |
1 |
Parallel request slots (context divided among them) |
--fit-target |
512 |
Target VRAM margin (MiB) for auto GPU layers |
--extra-flags |
— | Raw flags passed through to llama-server |
--ik |
off | Use ik_llama.cpp instead of upstream (needs its wheel) |
--streaming-llm, --no-mmap, --mlock, --no-kv-offload, --numa |
off | Memory/eval tuning |
Speculative decoding
| Flag | Default | Purpose |
|---|---|---|
--model-draft |
— | Draft model for speculative decoding |
--draft-max |
3 |
Tokens to draft per step |
--spec-type |
none |
draft-mtp, ngram-mod, ngram-simple, ngram-map-k, ngram-map-k4v |
--gpu-layers-draft |
256 |
Draft-model GPU layers |
--ctx-size-draft |
0 |
Draft context (0 = same as main) |
Transformers / Accelerate / bitsandbytes
| Flag | Default | Purpose |
|---|---|---|
--cpu |
off | CPU-only generation |
--cpu-memory |
0 |
Max CPU memory (GiB) for offload |
--disk |
off | Offload overflow layers to disk |
--load-in-8bit, --load-in-4bit |
off | bitsandbytes quantization |
--use_double_quant, --compute_dtype, --quant_type |
— | 4-bit tuning (nf4/fp4) |
--bf16 |
off | bfloat16 (Ampere+) |
--attn-implementation |
sdpa |
sdpa, eager, flash_attention_2 |
--trust-remote-code |
off | Allows model code execution — see §6 |
--force-safetensors |
off | Refuse non-safetensors (prevents arbitrary code) |
--no_use_fast |
off | Disable the fast tokenizer |
ExLlamaV3
| Flag | Default | Purpose |
|---|---|---|
--gpu-split |
— | Per-GPU VRAM (GB) for layers (e.g. 20,7,7) |
--enable-tp / --enable_tp |
off | Tensor parallelism across GPUs |
--tp-backend |
native |
native or nccl |
--cfg-cache |
off | Extra cache for CFG negative prompts |
Gradio / network
| Flag | Default | Purpose |
|---|---|---|
--listen |
off | Bind to the LAN (0.0.0.0 or --listen-host) |
--listen-port |
— | UI port |
--share |
off | Public Gradio share tunnel (proprietary binary) |
--auto-launch |
off | Open a browser on launch |
--gradio-auth / --gradio-auth-path |
— | HTTP Basic auth (user:pass[,user:pass…]) |
--ssl-keyfile, --ssl-certfile |
— | Serve the UI over HTTPS |
--subpath |
— | Mount under a subpath (reverse-proxy) |
--portable |
off | Hide non-portable features (training, image gen, ik) |
API
| Flag | Default | Purpose |
|---|---|---|
--api |
off | Enable the HTTP API |
--api-port |
5000 |
API port |
--api-key |
— | Bearer key for all endpoints |
--admin-key |
— | Separate key for model load/unload (defaults to --api-key) |
--public-api / --public-api-id |
off | Cloudflare tunnel for the API |
--api-enable-ipv6 / --api-disable-ipv4 |
off | Address family control |
--nowebui |
off | Run the API without the Gradio UI |
Image model
--image-model, --image-model-dir, --image-dtype (bfloat16/float16),
--image-attn-backend (sdpa/flash_attention_2), --image-cpu-offload, --image-compile,
--image-quant (none/bnb-8bit/bnb-4bit/torchao-*).
API generation defaults — every sampler (--temperature, --top-p, --top-k,
--min-p, --repetition-penalty, --dry-multiplier, --mirostat-mode, --enable-thinking,
--reasoning-effort, --chat-template-file, …) can be set as a CLI default; see §5.2.
Note
This is the complete top-level flag surface. Sampler flags map 1:1 to the
settingsdict keys documented in §5.2. Flags not listed here do not exist — don't assume undocumented options.
4.4 Environment variables
| Variable | Used by | Effect |
|---|---|---|
HF_TOKEN |
download-model.py |
Hugging Face auth token for gated/private models |
HF_USER / HF_PASS |
download-model.py |
HTTP Basic auth alternative |
HF_ENDPOINT |
download-model.py |
Override the Hugging Face base URL (mirror) |
GPU_CHOICE |
one_click.py |
Non-interactive GPU selection (A–D, N) |
INSTALL_EXTENSIONS |
one_click.py |
If truthy, install all extension requirements |
LAUNCH_AFTER_INSTALL |
one_click.py |
If falsey, exit after install instead of launching |
OPENEDAI_EMBEDDING_MODEL |
API | Embedding model (default sentence-transformers/all-mpnet-base-v2) |
OPENEDAI_EMBEDDING_DEVICE |
API | Embedding device (default cpu) |
OPENEDAI_PORT |
API | Override the API port |
OPENEDAI_CERT_PATH / OPENEDAI_KEY_PATH |
API | API TLS cert/key |
OPENEDAI_ENABLE_IPV6 / OPENEDAI_DISABLE_IPV4 |
API | API address family |
OPENEDAI_DEBUG |
API | Verbose API debug logging (default 0) |
TEXTGEN_ELECTRON |
UI | Set to 1 by the desktop wrapper to enable Electron-specific behavior |
5. Loaders & Samplers (the pluggable backends)
5.1 Loader capability matrix
Each loader supports a different set of load-time parameters, defined in
loaders_and_params (modules/loaders.py). modules/models_settings.py auto-detects the
loader from the model's files; --loader overrides it (fix_loader_name() normalizes
aliases like hf, llamacpp, exllama3).
| Loader | Process model | Quantization support | Multi-GPU | Speculative decoding | Vision |
|---|---|---|---|---|---|
| llama.cpp | Subprocess (llama-server) |
GGUF (built-in) | --tensor-split/--split-mode |
✅ (draft + ngram) | ✅ (--mmproj) |
| Transformers | In-process | 8-bit/4-bit (bitsandbytes), bf16 | --gpu-split, disk/CPU offload |
❌ | via model |
| ExLlamav3_HF | In-process | EXL3 + cache quant | --gpu-split, --enable-tp |
❌ | ❌ |
| ExLlamav3 | In-process | EXL3 + cache quant | --gpu-split, --enable-tp |
✅ (draft) | ❌ |
| TensorRT-LLM | In-process | engine-baked | engine-baked | ❌ | ❌ |
Important
The llama.cpp loader's out-of-process design means crashes show up as "Server process terminated unexpectedly with exit code: N" and a lingering
llama-servercan hold VRAM after an unclean exit. Supervise/kill that process in production deployments.
5.2 Sampler availability per loader
This matrix has no equivalent in the project README and is the highest-value reference for
anyone tuning generation. Defined in loaders_samplers (modules/loaders.py):
| Sampler family | Transformers | ExLlamav3_HF | ExLlamav3 | llama.cpp | TensorRT-LLM |
|---|---|---|---|---|---|
| temperature, top_p, top_k, min_p | ✅ | ✅ | ✅ | ✅ | ✅ |
| dynatemp / smoothing | ✅ | ✅ | ❌ | partial | ❌ |
| typical_p, top_a, top_n_sigma | ✅ | ✅ | ❌ | partial | ❌ |
| XTC (xtc_threshold/probability) | ✅ | ✅ | ❌ | ✅ | ❌ |
| epsilon/eta cutoff, TFS | ✅ | ✅ | ❌ | ❌ | ❌ |
| DRY (dry_multiplier/base/length) | ✅ | ✅ | ❌ | ✅ | ❌ |
| repetition/frequency/presence penalty | ✅ | ✅ | ✅ | ✅ | ✅ |
| mirostat | ✅ | ✅ | ❌ | ✅ | ❌ |
| guidance_scale (CFG), penalty_alpha | ✅ | ✅ | ❌ | ❌ | ❌ |
| grammar (GBNF) | ✅ | ✅ | ❌ | ✅ | ❌ |
| prompt_lookup_num_tokens | ✅ | ❌ | ❌ | ❌ | ❌ |
The UI calls blacklist_samplers() / make_loader_params_visible() to hide unsupported
controls. Setting an unsupported sampler over the API is silently ignored — it is not an
error, but it has no effect. Treat the table above as the negative-knowledge boundary.
5.3 Default sampler values
Neutral defaults come from default_preset_values (modules/presets.py): temperature=1,
top_p=1, top_k=0, min_p=0, repetition_penalty=1, dry_multiplier=0,
mirostat_mode=0, repetition_penalty_range=1024, and the sampler_priority ordering that
determines which samplers apply first. Presets in user_data/presets/*.yaml override these;
generate_preset_yaml() saves only the non-default deltas.
5.4 Reasoning-model parsing
modules/reasoning.py strips "thinking" blocks before display using a THINKING_FORMATS
table of model-family-specific tag pairs: <think>…</think>, GPT-OSS
<|channel|>analysis<|message|>…<|end|>, Gemma 4 <|channel>thought, Seed <seed:think>,
Solar <|think|>, and an end-only </think> variant. Parsing is heuristic per family —
a model that uses an unrecognized marker will show its reasoning inline. There is no universal
thinking format.
6. Security
Caution
TextGen is designed to run on a trusted machine for a trusted user. It is not a hardened multi-tenant service. Every "exposure" flag widens the trust boundary — understand each before enabling it.
6.1 Network exposure model
- UI is local by default.
--listenbinds it to the LAN;--sharecreates a public tunnel using a proprietary binary. With either, set--gradio-auth user:pass— the startup code warns loudly if you don't. - API is localhost-locked by two mechanisms in
modules/api/script.py:- A host-header validation middleware that rejects any
Hostother thanlocalhost/127.0.0.1unless--listen/--public-apiis set (returns{"detail": "Invalid host header"}). - A CORS policy that, when not exposed, only allows
localhost/127.0.0.1origins.
- A host-header validation middleware that rejects any
- Exposing the API requires
--listenor--public-api. When exposed, always set--api-key.
6.2 Authentication
| Surface | Mechanism |
|---|---|
| Web UI | Gradio HTTP Basic auth via --gradio-auth / --gradio-auth-path |
| API (general) | Authorization: Bearer <api-key> (verify_api_key) |
| API (admin: model load/unload) | Authorization: Bearer <admin-key> (verify_admin_key) |
API (Anthropic /v1/messages) |
x-api-key: <api-key> (verify_anthropic_key) |
Important
Set
--admin-keyto something different from--api-keyin any shared deployment. Otherwise every API client that can generate text can also unload/swap your model (the model/load endpoint controls what the whole instance serves). Issue #7577 documents that/v1/internal/model/loadhonors loader args — an admin-only capability by design.
6.3 Code-execution risks
--trust-remote-coderuns arbitrary Python shipped with a model. The startup warning is explicit: only use it with fully trusted models, and gate access with--gradio-auth. Prefer--force-safetensorsto refuse non-safetensors weights entirely.- Tools / MCP (
modules/tool_use.py,user_data/mcp.json) let the model invoke external commands/servers. Enableconfirm_tool_callsso calls require user approval.
6.4 SSRF protection in web search
modules/web_search.py validates every URL (and every redirect hop) before fetching:
rejects backslashes (the GHSA-27xf-58m5-vxmc parser-discrepancy bypass), non-http(s)
schemes, embedded credentials, and any hostname that resolves to a non-global IP. This
prevents the web-search feature from being steered at internal/metadata endpoints.
6.5 Multi-user caveats
--multi-user is for small trusted teams only. The startup warning enumerates the
limits: the Stop button stops generation for everyone, chat history isn't saved, only one user
generates at a time unless the backend is parallel-capable (llama.cpp --parallel N,
ExLlamaV3), and grayed-out UI actions can be bypassed client-side. Do not expose publicly.
7. The API Server (internals)
modules/api/script.py builds a FastAPI app served by uvicorn (default port 5000, overridable
via --api-port/OPENEDAI_PORT). It is started in-process from server.py when --api is
set, or standalone with --nowebui.
7.1 Endpoint inventory
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | /v1/completions |
api-key | OpenAI text completion (streaming via SSE) |
| POST | /v1/chat/completions |
api-key | OpenAI chat completion |
| POST | /v1/messages |
x-api-key | Anthropic Messages API |
| GET | /v1/models, /v1/models/{model} |
api-key | List/describe models |
| POST | /v1/audio/transcriptions |
api-key | Whisper speech-to-text |
| POST | /v1/images/generations |
api-key | Diffusion image generation |
| POST | /v1/embeddings |
api-key | Sentence-embedding vectors |
| POST | /v1/moderations |
api-key | Moderation scoring |
| GET | /v1/internal/health |
api-key | Health check |
| POST | /v1/internal/encode, /decode, /token-count |
api-key | Tokenizer utilities |
| POST | /v1/internal/logits |
api-key | Raw next-token logits |
| POST | /v1/internal/chat-prompt |
api-key | Render the chat prompt without generating |
| POST | /v1/internal/stop-generation |
api-key | Cancel an in-flight generation |
| GET | /v1/internal/model/info, /list |
api-key | Loaded-model info / available models |
| POST | /v1/internal/model/load, /unload |
admin-key | Load/unload a model |
| GET/POST | /v1/internal/lora/list, /load, /unload |
api-key / admin | LoRA management |
7.2 Streaming, cancellation, and errors
- Streaming responses use
sse-starlette'sEventSourceResponse; the server watches for client disconnects and sets a stop event so generation halts when a client goes away. n > 1is rejected with streaming enabled.- Error handlers translate internal exceptions into OpenAI-shaped
(
{"error": {"message", "type", …}}) or Anthropic-shaped ({"type": "error", "error": {…}}) bodies depending on the route.
7.3 Embeddings & TLS
The embedding endpoint loads a sentence-transformers model
(OPENEDAI_EMBEDDING_MODEL, default all-mpnet-base-v2) on OPENEDAI_EMBEDDING_DEVICE. The
API can serve TLS directly via OPENEDAI_CERT_PATH/OPENEDAI_KEY_PATH. If neither IPv4 nor
IPv6 is enabled it refuses to start: "you MUST enable IPv6 or IPv4 for the API to work."
8. Extensions
Extensions are folders under extensions/<name>/ with a script.py exposing hook functions
(setup, ui, plus generation hooks like input_modifier, output_modifier,
custom_generate_chat_prompt). They are discovered by modules/extensions.py, activated via
--extensions or settings['default_extensions'], and their CSS/JS is merged into the UI.
| Extension | Purpose |
|---|---|
coqui_tts, silero_tts |
Text-to-speech |
whisper_stt |
Speech-to-text input |
sd_api_pictures |
Generate images via an external Stable Diffusion API |
send_pictures |
Send images into multimodal chats |
superbooga, superboogav2 |
ChromaDB-backed retrieval/RAG long-term memory |
google_translate |
Translate input/output |
ngrok |
Expose the UI via an ngrok tunnel |
gallery |
Character gallery picker |
character_bias, long_replies, perplexity_colors |
Generation tweaks/visualizations |
example |
Template for writing your own extension |
Each extension declares its own requirements.txt; install them via the update wizard
(option B), INSTALL_EXTENSIONS=1 at install time, or directly with pip. Failures are
non-fatal — a missing requirement logs "Failed to load the extension …" and the rest of the
app continues.
Warning
superboogav2pulls heavy dependencies (chromadb, spacy, nltk, PyMuPDF, optuna). Install it only if you need RAG memory; on Windows some extension requirements are expected to fail harmlessly.
9. Operations
9.1 Running headless / as a service
For a server deployment, run the API without the UI:
python server.py --nowebui --api --api-key "$API_KEY" --admin-key "$ADMIN_KEY" \
--model my-model --listen --idle-timeout 30
Wrap it in a systemd unit (or the appropriate Docker image under docker/<vendor>/). The
process supervises its own llama-server child; ensure your service manager sends SIGTERM
(handled gracefully) rather than SIGKILL so the subprocess is stopped cleanly.
9.2 Docker
Per-vendor Dockerfiles live under docker/ (nvidia, amd, intel, cpu,
TensorRT-LLM), each with a docker-compose.yml. Mount user_data/ as a volume so models
and config persist across container rebuilds.
9.3 Reverse proxy
Behind nginx/Traefik, set --subpath /textgen so Gradio generates correct asset URLs, and
forward WebSocket upgrades (Gradio's queue uses them). Note issue #7545: heavy proxying
(e.g. Cloudflare in front of a remote host) can degrade UI responsiveness — prefer a direct
--listen path on a trusted network when latency matters.
9.4 Health, logs, and restarts
- Health:
GET /v1/internal/health(when the API is enabled). - Logs: Rich-formatted to the console; a 100-entry in-memory ring buffer is also kept
(
modules/logging_colors.py). Third-party loggers (urllib3, httpx, diffusers, torch) are quieted to ERROR. - Hot restart: setting
shared.need_restartrebuilds the Gradio interface without exiting the process (used by settings changes that require a UI reload). - Idle unload:
--idle-timeout Nruns a daemon thread that unloads the model after N minutes idle and reloads on demand — useful for shared GPUs.
9.5 Backup & migration
Back up user_data/. To migrate, copy that directory to the new host and reinstall the
application around it. Model checksums can be re-validated with
python download-model.py <repo> --check.
10. Code Quality & Conventions
- Global singleton state.
modules/shared.pyholds the model, tokenizer, settings, and Gradio component registry as module globals. This is deliberate (single-user desktop app) but means there is no isolation between requests — relevant when reasoning about--multi-user. - Sampler hijacking.
modules/sampler_hijack.pypatches custom samplers (TFS, top-a, mirostat variants, DRY, adaptive) into the Transformers generation path, validating ranges and raisingValueErroron bad values. This is the in-process analogue of the monkey-patching pattern common to Gradio ML UIs — upgradingtransformerscan silently break these patches; smoke-test generation after a Transformers bump. - Loader normalization.
fix_loader_name()accepts many spellings of each loader name; prefer the canonical forms in scripts. - Custom Gradio fork. The UI depends on
oobabooga/gradio(a pinned fork with custom patches), not upstream Gradio. Front-end behavior won't match mainline Gradio docs. - Front-end is imperative DOM manipulation.
js/main.jswires keyboard shortcuts, sidebars, hover menus, syntax highlighting (highlight.js), and math (KaTeX) directly against the rendered Gradio DOM viaMutationObservers — fragile across Gradio upgrades, another reason the fork is pinned.
11. Potential Improvements
These are observations for maintainers, derived from the source — not defects.
- Single global model. Serving more than one model simultaneously (beyond the separate
image model) would require refactoring
shared.modelinto a registry. Today,model/loadswaps the one global model for all clients. - DOM-coupled front-end. The reliance on
MutationObserver-driven DOM patching against a forked Gradio is the main maintenance tax. A move toward Gradio-native components or a custom front-end would reduce upgrade breakage. - Heuristic reasoning parser.
THINKING_FORMATSmust be extended by hand for each new model family's thinking markup; a model-declared format (via metadata) would be more robust. - Per-loader sampler drift. The
loaders_samplerssets are maintained manually; a model could request a sampler the loader silently ignores. Surfacing "ignored sampler" warnings on the API would aid debugging. - Extension requirement fragility. Extensions fail open (logged, skipped). A dependency health endpoint listing which extensions loaded vs. failed would help operators.
12. Quick Reference
Start (full, NVIDIA): ./start_linux.sh → pick A → Model tab → download → load.
Start (API only):
python server.py --nowebui --api --api-key KEY --admin-key ADMIN --model NAME
Persistent flags: user_data/CMD_FLAGS.txt (one per line).
Persistent UI settings: user_data/settings.yaml.
Config precedence: CLI → CMD_FLAGS.txt → settings.yaml → defaults.
Loaders: llama.cpp (GGUF, subprocess) · Transformers · ExLlamav3_HF · ExLlamav3 ·
TensorRT-LLM.
Default API port: 5000. Localhost-locked unless --listen/--public-api.
Auth headers: Authorization: Bearer <key> (OpenAI) · x-api-key: <key> (Anthropic) ·
admin-key for model/load|unload.
State to back up: user_data/ (never touched by updates).
Update: update_wizard_* → A. Major Python/CUDA bumps require deleting
installer_files/ and reinstalling.
Key env vars: HF_TOKEN, GPU_CHOICE, OPENEDAI_*, TEXTGEN_ELECTRON.