arc-llama 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. arc_llama-0.1.0/.github/workflows/ci.yml +37 -0
  2. arc_llama-0.1.0/.gitignore +30 -0
  3. arc_llama-0.1.0/LICENSE +21 -0
  4. arc_llama-0.1.0/PKG-INFO +295 -0
  5. arc_llama-0.1.0/README.md +260 -0
  6. arc_llama-0.1.0/pyproject.toml +65 -0
  7. arc_llama-0.1.0/src/arc_llama/__init__.py +3 -0
  8. arc_llama-0.1.0/src/arc_llama/__main__.py +4 -0
  9. arc_llama-0.1.0/src/arc_llama/arch.py +225 -0
  10. arc_llama-0.1.0/src/arc_llama/benchmark.py +441 -0
  11. arc_llama-0.1.0/src/arc_llama/cli.py +705 -0
  12. arc_llama-0.1.0/src/arc_llama/config.py +287 -0
  13. arc_llama-0.1.0/src/arc_llama/detect.py +247 -0
  14. arc_llama-0.1.0/src/arc_llama/gguf_meta.py +112 -0
  15. arc_llama-0.1.0/src/arc_llama/launcher.py +247 -0
  16. arc_llama-0.1.0/src/arc_llama/models.py +410 -0
  17. arc_llama-0.1.0/src/arc_llama/recipes.py +159 -0
  18. arc_llama-0.1.0/src/arc_llama/router.py +209 -0
  19. arc_llama-0.1.0/src/arc_llama/server.py +349 -0
  20. arc_llama-0.1.0/src/arc_llama/static/app.js +242 -0
  21. arc_llama-0.1.0/src/arc_llama/static/index.html +57 -0
  22. arc_llama-0.1.0/src/arc_llama/static/style.css +184 -0
  23. arc_llama-0.1.0/src/arc_llama/tui.py +378 -0
  24. arc_llama-0.1.0/tests/conftest.py +61 -0
  25. arc_llama-0.1.0/tests/test_benchmark.py +310 -0
  26. arc_llama-0.1.0/tests/test_config.py +59 -0
  27. arc_llama-0.1.0/tests/test_detect.py +128 -0
  28. arc_llama-0.1.0/tests/test_gguf_meta.py +65 -0
  29. arc_llama-0.1.0/tests/test_launcher.py +227 -0
  30. arc_llama-0.1.0/tests/test_models.py +86 -0
  31. arc_llama-0.1.0/tests/test_recipes.py +194 -0
  32. arc_llama-0.1.0/tests/test_router.py +64 -0
  33. arc_llama-0.1.0/tests/test_server.py +146 -0
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ name: Python ${{ matrix.python-version }}
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12"]
16
+
17
+ steps:
18
+ - name: Check out repository
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+ cache: pip
26
+
27
+ - name: Install package
28
+ run: python -m pip install -e '.[dev]'
29
+
30
+ - name: Lint
31
+ run: ruff check .
32
+
33
+ - name: Test
34
+ run: pytest
35
+
36
+ - name: Build package
37
+ run: python -m build
@@ -0,0 +1,30 @@
1
+ .mypy_cache/
2
+ .ruff_cache/
3
+ .uv-cache/
4
+ __pycache__/
5
+ *.py[cod]
6
+ *.egg-info/
7
+ .eggs/
8
+ build/
9
+ dist/
10
+ .venv/
11
+ venv/
12
+ .env
13
+ .env.local
14
+
15
+ # arc-llama runtime state
16
+ *.log
17
+ *.pid
18
+ state/
19
+ models/
20
+
21
+ # IDE
22
+ .vscode/
23
+ .idea/
24
+ *.swp
25
+ .DS_Store
26
+
27
+ # pytest / coverage
28
+ .pytest_cache/
29
+ .coverage
30
+ htmlcov/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 arc-llama contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,295 @@
1
+ Metadata-Version: 2.4
2
+ Name: arc-llama
3
+ Version: 0.1.0
4
+ Summary: Plug-and-play llama.cpp runtime for Intel Arc GPUs. Auto-detects your card, picks safe SYCL defaults, and exposes an OpenAI-compatible API.
5
+ Project-URL: Homepage, https://github.com/offbyonebit/arc-llama
6
+ Project-URL: Issues, https://github.com/offbyonebit/arc-llama/issues
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: alchemist,arc,battlemage,inference,intel,llama.cpp,llm,sycl
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: click>=8.1
17
+ Requires-Dist: fastapi>=0.110
18
+ Requires-Dist: gguf>=0.10
19
+ Requires-Dist: httpx>=0.27
20
+ Requires-Dist: huggingface-hub>=0.20
21
+ Requires-Dist: rich>=13.0
22
+ Requires-Dist: tomli-w>=1.0
23
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
24
+ Requires-Dist: uvicorn[standard]>=0.27
25
+ Provides-Extra: dev
26
+ Requires-Dist: build>=1.2; extra == 'dev'
27
+ Requires-Dist: mypy>=1.10; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.5; extra == 'dev'
31
+ Requires-Dist: textual>=0.60; extra == 'dev'
32
+ Provides-Extra: tui
33
+ Requires-Dist: textual>=0.60; extra == 'tui'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # arc-llama
37
+
38
+ > Plug-and-play `llama.cpp` runtime for Intel Arc GPUs.
39
+
40
+ `arc-llama` is a single command-line tool that detects your Intel Arc card,
41
+ applies the right SYCL/oneAPI environment for your generation, downloads or
42
+ registers GGUF models, and runs an OpenAI-compatible server in front of them.
43
+ It encodes the gotchas (SIGSEGVs in the persistent device-code cache, IPEX-LLM
44
+ bundle env-var traps, KV-cache quant behaviour per architecture) so you don't
45
+ have to discover them the hard way.
46
+
47
+ It's built for the day you unbox an Arc card, install drivers, and want
48
+ something useful before lunch.
49
+
50
+ > [!IMPORTANT]
51
+ > **Status: 0.1 alpha.** Core code is in place. End-to-end runs and tests
52
+ > haven't been exercised yet , issue and PR feedback welcome.
53
+
54
+ ## What you get
55
+
56
+ - **Auto-discovery of GPUs *and models*.** `arc-llama init` finds your Intel
57
+ card and walks the configured scan paths for `.gguf` files, registering
58
+ every one with a sensible recipe , context length sized to your VRAM,
59
+ KV-cache class inferred from the filename. You should never need
60
+ `arc-llama add` for a GGUF that's already on disk.
61
+ - **Auto-discovery** of every Intel GPU on the host (`Alchemist`, `Battlemage`,
62
+ Lunar Lake iGPU). PCI device-ID table covers the common SKUs and falls back
63
+ to OpenCL device-name parsing for the rest.
64
+ - **Per-arch SYCL profiles** , env vars like `SYCL_CACHE_PERSISTENT=0` are
65
+ applied automatically, and known-bad ones (e.g. `GGML_SYCL_DISABLE_OPT`,
66
+ `SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS`) are stripped from the
67
+ inherited shell environment.
68
+ - **Smart defaults** for `-ctx`, `--cache-type-k/v`, and `-ngl` based on the
69
+ detected VRAM and the model file size , never starts a model you can't fit.
70
+ - **Model registry** in TOML at `$XDG_CONFIG_HOME/arc-llama/config.toml`,
71
+ trivially editable.
72
+ - **One process per model**, swapped in/out by an internal router. Default
73
+ policy is single-resident across all GPUs (good for thermals); flip it to
74
+ multi-resident if you have headroom.
75
+ - **OpenAI-compatible API** at `http://127.0.0.1:11437/v1/...`. Plug it into
76
+ Open WebUI, OpenCode, anything that speaks OpenAI.
77
+ - **A web UI** at `http://127.0.0.1:11437/` , ships with the install. Model
78
+ picker, load/stop buttons, **inline ctx + KV-quant editing**, GPU + VRAM
79
+ panel. Pure HTML/JS, no build step.
80
+ - **A terminal UI** (`arc-llama tui`) using Textual , same load/stop/edit
81
+ controls, no browser needed. Optional install: `pip install 'arc-llama[tui]'`.
82
+ - **No magic with your existing stack.** It uses your `llama-server` binary;
83
+ you're never locked into a specific build.
84
+
85
+ ## Quick start
86
+
87
+ ```bash
88
+ # 1. Install (editable, while we're in alpha)
89
+ git clone https://github.com/offbyonebit/arc-llama
90
+ cd arc-llama
91
+ pip install -e .
92
+
93
+ # 2. Detect GPUs and write a starter config
94
+ arc-llama init --llama-server /path/to/your/built/llama-server
95
+
96
+ # 3. Look at what was found
97
+ arc-llama doctor
98
+ arc-llama gpus
99
+
100
+ # 4. Auto-register every GGUF found under your scan paths.
101
+ # `init` ran this once; rerun any time you drop new files in.
102
+ arc-llama scan
103
+ # (or for one-offs: arc-llama add /path/to/some.gguf,
104
+ # or HF: arc-llama add unsloth/gemma-4-31B-it-GGUF:Q4_K_M --from-hf)
105
+
106
+ # 5. Run the OpenAI-compatible server (also serves the web UI at /)
107
+ arc-llama serve
108
+
109
+ # 6. (Optional) Open the terminal UI in another window
110
+ arc-llama tui
111
+
112
+ # 7. (Optional) Install a systemd --user unit
113
+ arc-llama systemd --write
114
+ systemctl --user daemon-reload
115
+ systemctl --user enable --now arc-llama.service
116
+ ```
117
+
118
+ Then point any OpenAI-compatible client at `http://127.0.0.1:11437/v1`:
119
+
120
+ ```bash
121
+ curl http://127.0.0.1:11437/v1/chat/completions \
122
+ -H "Content-Type: application/json" \
123
+ -d '{
124
+ "model": "gemma-4-31b-q4_k_m",
125
+ "messages": [{"role": "user", "content": "hi"}]
126
+ }'
127
+ ```
128
+
129
+ ## Requirements
130
+
131
+ - Linux, kernel **6.8+** for Battlemage (`xe` driver) or 5.17+ for Alchemist
132
+ (`i915`).
133
+ - ReBAR enabled in BIOS , without it llama.cpp falls back to slow paths on Arc.
134
+ - A `llama-server` built with the SYCL backend. The Intel oneAPI Base Toolkit
135
+ is the supported build path:
136
+ ```bash
137
+ source /opt/intel/oneapi/setvars.sh
138
+ cmake -B build -DGGML_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx
139
+ cmake --build build --config Release -j
140
+ ```
141
+ - User in the `render` and `video` groups (`arc-llama doctor` will tell you).
142
+
143
+ ## Multi-GPU
144
+
145
+ `arc-llama init` registers every Intel GPU it finds. Each model in the config
146
+ is bound to a specific PCI slot, and the SYCL device selector
147
+ (`ONEAPI_DEVICE_SELECTOR=level_zero:N`) is set per-model. Add your second card,
148
+ re-run `arc-llama init --force` to refresh `[[gpus]]`, then add models against
149
+ either GPU.
150
+
151
+ The default swap policy is **single-resident across all GPUs** , pick a model,
152
+ the router stops anything else first. Flip `server.single_resident = false` in
153
+ the config if you want different-GPU models to coexist.
154
+
155
+ ## Configuration reference
156
+
157
+ `$XDG_CONFIG_HOME/arc-llama/config.toml`:
158
+
159
+ ```toml
160
+ version = 1
161
+
162
+ [server]
163
+ host = "127.0.0.1"
164
+ port = 11437
165
+ single_resident = true
166
+
167
+ [paths]
168
+ llama_server = "/usr/local/bin/llama-server"
169
+ models_dir = "~/.local/share/arc-llama/models"
170
+ state_dir = "~/.local/state/arc-llama"
171
+
172
+ [[gpus]]
173
+ pci_slot = "0000:03:00.0"
174
+ sycl_index = 0
175
+ arch = "battlemage"
176
+ vram_mb = 24480
177
+ enabled = true
178
+ name = "Arc Pro B60"
179
+
180
+ [[models]]
181
+ name = "qwen3-7b"
182
+ display_name = "Qwen 3 7B"
183
+ path = "/home/me/models/qwen3-7b-q4_k_m.gguf"
184
+ gpu_pci_slot = "0000:03:00.0"
185
+ port = 18080
186
+ kv_class = "default"
187
+ aliases = ["qwen3-7b-q4_k_m.gguf"]
188
+
189
+ [models.recipe]
190
+ ctx = 32768
191
+ cache_type_k = "q8_0"
192
+ cache_type_v = "q8_0"
193
+ n_gpu_layers = 999
194
+ parallel = 1
195
+ extra_flags = []
196
+ ```
197
+
198
+ `kv_class` controls the KV-cache size estimate that `arc-llama add` uses to
199
+ pick a context length. Currently:
200
+
201
+ | value | per-token f16 KV | typical for |
202
+ |-------------------|------------------|----------------------------------------------|
203
+ | `default` | ~80 KiB | most ≤30B dense models, conservative ceiling |
204
+ | `qwen3_27b_dense` | ~70 KiB | Qwen 3 27B dense |
205
+ | `moe_a3b` | ~24 KiB | Qwen 3 30B/35B-A3B MoE |
206
+ | `gemma_swa` | ~16 KiB | Gemma 3/4 (interleaved sliding-window attn) |
207
+
208
+ ## Architecture
209
+
210
+ ```
211
+ ┌──────────────────────┐
212
+ │ OpenAI client │ Open WebUI, OpenCode, curl, ...
213
+ │ (port 11437) │
214
+ └──────────┬───────────┘
215
+
216
+
217
+ ┌──────────────────────┐
218
+ │ arc-llama serve │ FastAPI, /v1/chat/completions etc.
219
+ │ (router + state) │
220
+ └──────────┬───────────┘
221
+ │ ensure_active(model)
222
+
223
+ ┌──────────────────────┐
224
+ │ Router │ swaps llama-server subprocesses per request
225
+ │ (single/multi-res) │ applies arch SYCL env, picks safe ctx/KV
226
+ └──────────┬───────────┘
227
+ │ subprocess.Popen
228
+
229
+ ┌──────────────────────┐
230
+ │ llama-server (SYCL) │ one per registered model, on demand
231
+ │ bound to GPU N │
232
+ └──────────────────────┘
233
+ ```
234
+
235
+ The router serialises swaps with an `asyncio.Lock`, so concurrent requests for
236
+ the same model fan out to one warm backend. Health is polled at
237
+ `{backend_url}/health`; cold-start budget is 120 s by default to absorb the
238
+ SYCL JIT recompile that plain `llama.cpp` pays on each fresh launch.
239
+
240
+ ## Why not just use Ollama / vLLM?
241
+
242
+ - **Ollama (IPEX-LLM bundle):** the Intel-supported port has reproducible
243
+ inference bugs on Battlemage with Qwen2.5-class models , sequential calls
244
+ collapse to NaN-derived gibberish. arc-llama runs `llama-server` directly so
245
+ you avoid that path entirely.
246
+ - **vLLM-XPU:** still maturing on Arc; weaker quant support. Worth trying for
247
+ dense >30B if you want throughput, but not yet a one-command experience.
248
+ - **Plain `llama-server` + scripts:** what most Arc owners do today. arc-llama
249
+ is the formalisation of those scripts, with the gotchas baked in.
250
+
251
+ ## UIs
252
+
253
+ Two front-ends are bundled and both talk to the same admin endpoints
254
+ (`/admin/status`, `/admin/load/{name}`, `/admin/stop/{name}`, `/admin/stop-all`):
255
+
256
+ - **Web UI** at `http://<host>:<port>/` (default `127.0.0.1:11437`). Single
257
+ static page polled every 5 s. Status, GPUs, model list, per-model
258
+ Load/Stop buttons, "Stop all" panic button. No build step, no JS deps.
259
+ - **Terminal UI** via `arc-llama tui` , Textual-based. Bindings: `r` refresh,
260
+ `l` load selected model, `s` stop selected, `S` stop all, `q` quit. Run it
261
+ alongside `arc-llama serve` (or against a remote one with `--server`).
262
+
263
+ Both use brightness/dim for status (loaded vs idle) , no red/green palettes.
264
+
265
+ ## Roadmap
266
+
267
+ - Smoke test on Alchemist (A770, A380) and Battlemage (B580) hardware.
268
+ - `arc-llama benchmark` , quick prompt-eval/gen tok/s harness.
269
+ - IPEX-LLM Ollama as an optional backend for users who prefer it.
270
+ - Container image with `llama-server` + arc-llama prebuilt.
271
+
272
+ ## Contributing
273
+
274
+ PRs and issues welcome. The most useful contributions today are:
275
+
276
+ 1. Confirming or fixing PCI device-ID → arch mappings for your card. If
277
+ `arc-llama gpus` shows `unknown` for a working Arc card, please open an
278
+ issue with `lspci -nn` output.
279
+ 2. Reporting architectures where the default SYCL env profile crashes or
280
+ underperforms.
281
+ 3. Trying the smoke tests on hardware other than the maintainer's Battlemage
282
+ B60 development box.
283
+
284
+ ## Support
285
+
286
+ This project is free and I don't ask for anything. If it's useful to you,
287
+ a star on the repo is appreciated, and if you want to follow along with
288
+ other things I'm building, you can find them under
289
+ [@offbyonebit](https://github.com/offbyonebit).
290
+
291
+ If you'd like to support development, you can [sponsor me on GitHub](https://github.com/sponsors/offbyonebit).
292
+
293
+ ## License
294
+
295
+ MIT , see [LICENSE](LICENSE).
@@ -0,0 +1,260 @@
1
+ # arc-llama
2
+
3
+ > Plug-and-play `llama.cpp` runtime for Intel Arc GPUs.
4
+
5
+ `arc-llama` is a single command-line tool that detects your Intel Arc card,
6
+ applies the right SYCL/oneAPI environment for your generation, downloads or
7
+ registers GGUF models, and runs an OpenAI-compatible server in front of them.
8
+ It encodes the gotchas (SIGSEGVs in the persistent device-code cache, IPEX-LLM
9
+ bundle env-var traps, KV-cache quant behaviour per architecture) so you don't
10
+ have to discover them the hard way.
11
+
12
+ It's built for the day you unbox an Arc card, install drivers, and want
13
+ something useful before lunch.
14
+
15
+ > [!IMPORTANT]
16
+ > **Status: 0.1 alpha.** Core code is in place. End-to-end runs and tests
17
+ > haven't been exercised yet , issue and PR feedback welcome.
18
+
19
+ ## What you get
20
+
21
+ - **Auto-discovery of GPUs *and models*.** `arc-llama init` finds your Intel
22
+ card and walks the configured scan paths for `.gguf` files, registering
23
+ every one with a sensible recipe , context length sized to your VRAM,
24
+ KV-cache class inferred from the filename. You should never need
25
+ `arc-llama add` for a GGUF that's already on disk.
26
+ - **Auto-discovery** of every Intel GPU on the host (`Alchemist`, `Battlemage`,
27
+ Lunar Lake iGPU). PCI device-ID table covers the common SKUs and falls back
28
+ to OpenCL device-name parsing for the rest.
29
+ - **Per-arch SYCL profiles** , env vars like `SYCL_CACHE_PERSISTENT=0` are
30
+ applied automatically, and known-bad ones (e.g. `GGML_SYCL_DISABLE_OPT`,
31
+ `SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS`) are stripped from the
32
+ inherited shell environment.
33
+ - **Smart defaults** for `-ctx`, `--cache-type-k/v`, and `-ngl` based on the
34
+ detected VRAM and the model file size , never starts a model you can't fit.
35
+ - **Model registry** in TOML at `$XDG_CONFIG_HOME/arc-llama/config.toml`,
36
+ trivially editable.
37
+ - **One process per model**, swapped in/out by an internal router. Default
38
+ policy is single-resident across all GPUs (good for thermals); flip it to
39
+ multi-resident if you have headroom.
40
+ - **OpenAI-compatible API** at `http://127.0.0.1:11437/v1/...`. Plug it into
41
+ Open WebUI, OpenCode, anything that speaks OpenAI.
42
+ - **A web UI** at `http://127.0.0.1:11437/` , ships with the install. Model
43
+ picker, load/stop buttons, **inline ctx + KV-quant editing**, GPU + VRAM
44
+ panel. Pure HTML/JS, no build step.
45
+ - **A terminal UI** (`arc-llama tui`) using Textual , same load/stop/edit
46
+ controls, no browser needed. Optional install: `pip install 'arc-llama[tui]'`.
47
+ - **No magic with your existing stack.** It uses your `llama-server` binary;
48
+ you're never locked into a specific build.
49
+
50
+ ## Quick start
51
+
52
+ ```bash
53
+ # 1. Install (editable, while we're in alpha)
54
+ git clone https://github.com/offbyonebit/arc-llama
55
+ cd arc-llama
56
+ pip install -e .
57
+
58
+ # 2. Detect GPUs and write a starter config
59
+ arc-llama init --llama-server /path/to/your/built/llama-server
60
+
61
+ # 3. Look at what was found
62
+ arc-llama doctor
63
+ arc-llama gpus
64
+
65
+ # 4. Auto-register every GGUF found under your scan paths.
66
+ # `init` ran this once; rerun any time you drop new files in.
67
+ arc-llama scan
68
+ # (or for one-offs: arc-llama add /path/to/some.gguf,
69
+ # or HF: arc-llama add unsloth/gemma-4-31B-it-GGUF:Q4_K_M --from-hf)
70
+
71
+ # 5. Run the OpenAI-compatible server (also serves the web UI at /)
72
+ arc-llama serve
73
+
74
+ # 6. (Optional) Open the terminal UI in another window
75
+ arc-llama tui
76
+
77
+ # 7. (Optional) Install a systemd --user unit
78
+ arc-llama systemd --write
79
+ systemctl --user daemon-reload
80
+ systemctl --user enable --now arc-llama.service
81
+ ```
82
+
83
+ Then point any OpenAI-compatible client at `http://127.0.0.1:11437/v1`:
84
+
85
+ ```bash
86
+ curl http://127.0.0.1:11437/v1/chat/completions \
87
+ -H "Content-Type: application/json" \
88
+ -d '{
89
+ "model": "gemma-4-31b-q4_k_m",
90
+ "messages": [{"role": "user", "content": "hi"}]
91
+ }'
92
+ ```
93
+
94
+ ## Requirements
95
+
96
+ - Linux, kernel **6.8+** for Battlemage (`xe` driver) or 5.17+ for Alchemist
97
+ (`i915`).
98
+ - ReBAR enabled in BIOS , without it llama.cpp falls back to slow paths on Arc.
99
+ - A `llama-server` built with the SYCL backend. The Intel oneAPI Base Toolkit
100
+ is the supported build path:
101
+ ```bash
102
+ source /opt/intel/oneapi/setvars.sh
103
+ cmake -B build -DGGML_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx
104
+ cmake --build build --config Release -j
105
+ ```
106
+ - User in the `render` and `video` groups (`arc-llama doctor` will tell you).
107
+
108
+ ## Multi-GPU
109
+
110
+ `arc-llama init` registers every Intel GPU it finds. Each model in the config
111
+ is bound to a specific PCI slot, and the SYCL device selector
112
+ (`ONEAPI_DEVICE_SELECTOR=level_zero:N`) is set per-model. Add your second card,
113
+ re-run `arc-llama init --force` to refresh `[[gpus]]`, then add models against
114
+ either GPU.
115
+
116
+ The default swap policy is **single-resident across all GPUs** , pick a model,
117
+ the router stops anything else first. Flip `server.single_resident = false` in
118
+ the config if you want different-GPU models to coexist.
119
+
120
+ ## Configuration reference
121
+
122
+ `$XDG_CONFIG_HOME/arc-llama/config.toml`:
123
+
124
+ ```toml
125
+ version = 1
126
+
127
+ [server]
128
+ host = "127.0.0.1"
129
+ port = 11437
130
+ single_resident = true
131
+
132
+ [paths]
133
+ llama_server = "/usr/local/bin/llama-server"
134
+ models_dir = "~/.local/share/arc-llama/models"
135
+ state_dir = "~/.local/state/arc-llama"
136
+
137
+ [[gpus]]
138
+ pci_slot = "0000:03:00.0"
139
+ sycl_index = 0
140
+ arch = "battlemage"
141
+ vram_mb = 24480
142
+ enabled = true
143
+ name = "Arc Pro B60"
144
+
145
+ [[models]]
146
+ name = "qwen3-7b"
147
+ display_name = "Qwen 3 7B"
148
+ path = "/home/me/models/qwen3-7b-q4_k_m.gguf"
149
+ gpu_pci_slot = "0000:03:00.0"
150
+ port = 18080
151
+ kv_class = "default"
152
+ aliases = ["qwen3-7b-q4_k_m.gguf"]
153
+
154
+ [models.recipe]
155
+ ctx = 32768
156
+ cache_type_k = "q8_0"
157
+ cache_type_v = "q8_0"
158
+ n_gpu_layers = 999
159
+ parallel = 1
160
+ extra_flags = []
161
+ ```
162
+
163
+ `kv_class` controls the KV-cache size estimate that `arc-llama add` uses to
164
+ pick a context length. Currently:
165
+
166
+ | value | per-token f16 KV | typical for |
167
+ |-------------------|------------------|----------------------------------------------|
168
+ | `default` | ~80 KiB | most ≤30B dense models, conservative ceiling |
169
+ | `qwen3_27b_dense` | ~70 KiB | Qwen 3 27B dense |
170
+ | `moe_a3b` | ~24 KiB | Qwen 3 30B/35B-A3B MoE |
171
+ | `gemma_swa` | ~16 KiB | Gemma 3/4 (interleaved sliding-window attn) |
172
+
173
+ ## Architecture
174
+
175
+ ```
176
+ ┌──────────────────────┐
177
+ │ OpenAI client │ Open WebUI, OpenCode, curl, ...
178
+ │ (port 11437) │
179
+ └──────────┬───────────┘
180
+
181
+
182
+ ┌──────────────────────┐
183
+ │ arc-llama serve │ FastAPI, /v1/chat/completions etc.
184
+ │ (router + state) │
185
+ └──────────┬───────────┘
186
+ │ ensure_active(model)
187
+
188
+ ┌──────────────────────┐
189
+ │ Router │ swaps llama-server subprocesses per request
190
+ │ (single/multi-res) │ applies arch SYCL env, picks safe ctx/KV
191
+ └──────────┬───────────┘
192
+ │ subprocess.Popen
193
+
194
+ ┌──────────────────────┐
195
+ │ llama-server (SYCL) │ one per registered model, on demand
196
+ │ bound to GPU N │
197
+ └──────────────────────┘
198
+ ```
199
+
200
+ The router serialises swaps with an `asyncio.Lock`, so concurrent requests for
201
+ the same model fan out to one warm backend. Health is polled at
202
+ `{backend_url}/health`; cold-start budget is 120 s by default to absorb the
203
+ SYCL JIT recompile that plain `llama.cpp` pays on each fresh launch.
204
+
205
+ ## Why not just use Ollama / vLLM?
206
+
207
+ - **Ollama (IPEX-LLM bundle):** the Intel-supported port has reproducible
208
+ inference bugs on Battlemage with Qwen2.5-class models , sequential calls
209
+ collapse to NaN-derived gibberish. arc-llama runs `llama-server` directly so
210
+ you avoid that path entirely.
211
+ - **vLLM-XPU:** still maturing on Arc; weaker quant support. Worth trying for
212
+ dense >30B if you want throughput, but not yet a one-command experience.
213
+ - **Plain `llama-server` + scripts:** what most Arc owners do today. arc-llama
214
+ is the formalisation of those scripts, with the gotchas baked in.
215
+
216
+ ## UIs
217
+
218
+ Two front-ends are bundled and both talk to the same admin endpoints
219
+ (`/admin/status`, `/admin/load/{name}`, `/admin/stop/{name}`, `/admin/stop-all`):
220
+
221
+ - **Web UI** at `http://<host>:<port>/` (default `127.0.0.1:11437`). Single
222
+ static page polled every 5 s. Status, GPUs, model list, per-model
223
+ Load/Stop buttons, "Stop all" panic button. No build step, no JS deps.
224
+ - **Terminal UI** via `arc-llama tui` , Textual-based. Bindings: `r` refresh,
225
+ `l` load selected model, `s` stop selected, `S` stop all, `q` quit. Run it
226
+ alongside `arc-llama serve` (or against a remote one with `--server`).
227
+
228
+ Both use brightness/dim for status (loaded vs idle) , no red/green palettes.
229
+
230
+ ## Roadmap
231
+
232
+ - Smoke test on Alchemist (A770, A380) and Battlemage (B580) hardware.
233
+ - `arc-llama benchmark` , quick prompt-eval/gen tok/s harness.
234
+ - IPEX-LLM Ollama as an optional backend for users who prefer it.
235
+ - Container image with `llama-server` + arc-llama prebuilt.
236
+
237
+ ## Contributing
238
+
239
+ PRs and issues welcome. The most useful contributions today are:
240
+
241
+ 1. Confirming or fixing PCI device-ID → arch mappings for your card. If
242
+ `arc-llama gpus` shows `unknown` for a working Arc card, please open an
243
+ issue with `lspci -nn` output.
244
+ 2. Reporting architectures where the default SYCL env profile crashes or
245
+ underperforms.
246
+ 3. Trying the smoke tests on hardware other than the maintainer's Battlemage
247
+ B60 development box.
248
+
249
+ ## Support
250
+
251
+ This project is free and I don't ask for anything. If it's useful to you,
252
+ a star on the repo is appreciated, and if you want to follow along with
253
+ other things I'm building, you can find them under
254
+ [@offbyonebit](https://github.com/offbyonebit).
255
+
256
+ If you'd like to support development, you can [sponsor me on GitHub](https://github.com/sponsors/offbyonebit).
257
+
258
+ ## License
259
+
260
+ MIT , see [LICENSE](LICENSE).