@fugood/buttress-server 2.25.0-beta.9 → 2.25.0

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 (42) hide show
  1. package/README.md +338 -19
  2. package/config/sample.toml +9 -0
  3. package/lib/autodiscover/index.d.ts +20 -0
  4. package/lib/autodiscover/sign.d.ts +10 -0
  5. package/lib/autodiscover/types.d.ts +32 -0
  6. package/lib/autodiscover/udp.d.ts +22 -0
  7. package/lib/cli.d.ts +3 -0
  8. package/lib/index.d.ts +29 -0
  9. package/lib/index.mjs +1282 -63
  10. package/lib/package.d.ts +7 -0
  11. package/lib/routes/anthropic-messages.d.ts +55 -0
  12. package/lib/routes/file.d.ts +10 -0
  13. package/lib/routes/index.d.ts +5 -0
  14. package/lib/routes/info.check.d.ts +1 -0
  15. package/lib/routes/info.d.ts +4 -0
  16. package/lib/routes/llm-shared.d.ts +54 -0
  17. package/lib/routes/openai-compat.d.ts +17 -0
  18. package/lib/routes/status.d.ts +4 -0
  19. package/lib/services/common.d.ts +29 -0
  20. package/lib/services/create-llm-service.d.ts +24 -0
  21. package/lib/services/ggml-llm.d.ts +5 -0
  22. package/lib/services/ggml-stt.d.ts +26 -0
  23. package/lib/services/index.d.ts +39 -0
  24. package/lib/services/mlx-llm.d.ts +5 -0
  25. package/lib/services/onnx-stt.d.ts +77 -0
  26. package/lib/services/onnx-tts.d.ts +48 -0
  27. package/lib/types.d.ts +158 -0
  28. package/lib/utils/SessionFileManager.d.ts +16 -0
  29. package/lib/utils/buttressAuth.d.ts +25 -0
  30. package/lib/utils/config.d.ts +21 -0
  31. package/lib/utils/httpAuthGuard.d.ts +1 -0
  32. package/lib/utils/net.d.ts +6 -0
  33. package/lib/utils/router.d.ts +2 -0
  34. package/lib/utils/serialize.d.ts +2 -0
  35. package/lib/utils/serverCaps.d.ts +4 -0
  36. package/lib/utils/sessionGuard.d.ts +33 -0
  37. package/lib/utils/test-caps.d.ts +61 -0
  38. package/lib/utils/workspaceState.d.ts +21 -0
  39. package/package.json +10 -8
  40. package/public/status.html +162 -1
  41. package/lib/chunk-C8PTHxhX.mjs +0 -2
  42. package/lib/index.d.mts +0 -370
package/README.md CHANGED
@@ -20,50 +20,369 @@ npx bricks-buttress --config ./config.toml
20
20
  npx bricks-buttress
21
21
  ```
22
22
 
23
+ ## Workspace Binding (`bricks buttress`)
24
+
25
+ By default, a buttress-server runs in **public mode**: any client on the LAN can connect, no auth required. To restrict access to a single BRICKS workspace and enable workspace-scoped JWT auth, **bind** the server with the `bricks buttress` CLI commands. Once bound, the server only accepts WebSocket / file-transfer requests carrying a valid access token signed by that workspace's issuer.
26
+
27
+ The `bricks` CLI is the tool that performs the binding and writes the local state file. Install it first — see the [bricks-cli docs](https://docs.bricks.tools/cli) — then `bricks auth login` with the workspace owner's account before running the commands below.
28
+
29
+ ### Bind a server to a workspace
30
+
31
+ ```bash
32
+ # Pair the local machine's buttress-server with the workspace of the current bricks-cli profile
33
+ bricks buttress bind
34
+
35
+ # Override the auto-detected server id, give it a friendly name, or write to a custom state dir
36
+ bricks buttress bind --server-id buttress-mac-studio --name "Studio LLM" --state-dir /etc/buttress
37
+
38
+ # For headless/remote setups: emit state.json to stdout instead of writing to disk
39
+ bricks buttress bind --print > /etc/buttress/state.json
40
+ ```
41
+
42
+ The state file (`~/.bricks-cli/buttress/state.json` by default, or `$BRICKS_BUTTRESS_STATE_DIR`) stores:
43
+
44
+ - `workspace.id` / `workspace.name` — which workspace this server belongs to
45
+ - `workspace.serverId` — the server's stable id (defaults to `buttress-<machineId>`)
46
+ - `workspace.issuerPublicKey` + `workspace.kid` — Ed25519 SPKI used to verify access tokens
47
+
48
+ **Restart `bricks-buttress` after binding** for the change to take effect — the state file is read once at startup.
49
+
50
+ ### Inspect bindings
51
+
52
+ ```bash
53
+ # Show local state.json + the workspace-side bound list
54
+ bricks buttress status
55
+
56
+ # Same, JSON-formatted
57
+ bricks buttress status --json
58
+ ```
59
+
60
+ ### Discover servers on the LAN
61
+
62
+ ```bash
63
+ # UDP scan + HTTP /buttress/info verification (3s timeout by default)
64
+ bricks buttress scan
65
+
66
+ # UDP only (skip the /buttress/info round-trip)
67
+ bricks buttress scan --udp-only
68
+
69
+ # Machine-readable
70
+ bricks buttress scan --json
71
+ ```
72
+
73
+ `scan` lists every buttress-server visible on the LAN, including unbound (public) ones, with their version, auth state (`open` vs `JWT required` + kid), bound workspace, and per-generator hardware caps (`score`, GPU, usable memory). Servers whose workspace matches your current `bricks-cli` profile are highlighted; this is purely a discovery command and does not mint any tokens.
74
+
75
+ ### Unbind
76
+
77
+ ```bash
78
+ # Remove the binding from the workspace and delete the local state.json
79
+ bricks buttress unbind
80
+
81
+ # Keep the local state file (useful if you only want to revoke server-side)
82
+ bricks buttress unbind --keep-local
83
+ ```
84
+
85
+ After unbinding, restart the server to return it to public mode.
86
+
87
+ ### Issue a long-lived access token
88
+
89
+ For headless callers (CI, ctor agents) that already hold a workspace token, mint a long-lived buttress access token instead of relying on a per-launcher session token:
90
+
91
+ ```bash
92
+ # Default 30-day TTL
93
+ bricks buttress issue-token
94
+
95
+ # Custom TTL (seconds), JSON output for scripting
96
+ bricks buttress issue-token --ttl 3600 --json
97
+ ```
98
+
99
+ The token claims `{ k: 'ba', w_id, st: 'ws', sid, jti, exp }` and any buttress-server bound to the same workspace will accept it.
100
+
23
101
  ## Configuration
24
102
 
25
- Configuration can be provided via:
26
- - `--config` / `-c` flag with TOML file path
103
+ Configuration is loaded from a TOML file passed via `--config` / `-c`. Every top-level table is optional — missing sections fall back to defaults. See `config/sample.toml` for an end-to-end example.
104
+
105
+ ### Top-level sections
106
+
107
+ | Section | Purpose |
108
+ | ------------------------- | -------------------------------------------------------------------------------------------------- |
109
+ | `[env]` | Environment variables exported into the process **only if not already set** |
110
+ | `[server]` | HTTP/RPC listener (port, log level, body limits) |
111
+ | `[runtime]` | Global defaults shared by every generator (most `[generators.model]` keys may live here too) |
112
+ | `[runtime.session_cache]` | KV-cache reuse store — see [Session State Cache](#session-state-cache) |
113
+ | `[autodiscover]` | LAN UDP / HTTP / mDNS discovery toggles |
114
+ | `[openai_compat]` | Enable `/oai-compat/v1/*` — see [Compatibility Endpoints](#compatibility-endpoints-experimental) |
115
+ | `[anthropic_messages]` | Enable `/anthropic-messages` — see [Compatibility Endpoints](#compatibility-endpoints-experimental)|
116
+ | `[[generators]]` | Array of generator instances — one entry per loaded model |
27
117
 
28
- ### Configuration Format (TOML)
118
+ ### `[env]`
29
119
 
30
120
  ```toml
31
- # Environment variables (only set if not already defined in system)
32
121
  [env]
33
- HF_TOKEN = "your_huggingface_token_here"
122
+ HUGGINGFACE_TOKEN = "hf_xxx" # ggml backends read this; HF_TOKEN is not picked up automatically
34
123
  CUDA_VISIBLE_DEVICES = "0"
124
+ ```
35
125
 
36
- [server]
37
- port = 2080
38
- log_level = "info"
126
+ Values here are exported only when the variable isn't already set in the process — see [Environment Variable Priority](#environment-variable-priority). For HuggingFace auth across all backends, `[runtime] huggingface_token = "hf_xxx"` works regardless of variable name.
127
+
128
+ ### `[server]`
129
+
130
+ | Key | Type | Default |
131
+ | ----------------- | -------------- | ---------------------------------------------------------------------- |
132
+ | `id` | string | `buttress-<machineId>` — stable id used for autodiscover / binding |
133
+ | `name` | string | `Buttress Server (<short id>)` — display name |
134
+ | `port` | number | `2080` (overridden by `--port`) |
135
+ | `log_level` | `"debug"`/`"info"`/`"warn"`/`"error"` | unset |
136
+ | `max_body_size` | string\|number | `"50MB"` — e.g. `"100MB"`, `"1GB"`, or raw bytes |
137
+ | `session_timeout` | string\|number | `60000` ms — accepts ms numbers or duration strings (`"30s"`) |
138
+ | `temp_file_dir` | string | `$TMPDIR/.buttress` |
139
+
140
+ ### `[runtime]` — global generator defaults
141
+
142
+ Most ggml-llm `[generators.model]` keys can also live in `[runtime]` as defaults. Per-generator values win; otherwise the runtime default applies.
143
+
144
+ | Key | Type | Notes |
145
+ | ---------------------------- | ----------------------------- | ---------------------------------------------------------------------- |
146
+ | `cache_dir` | string | Model + metadata cache root (default `~/.buttress/models`) |
147
+ | `huggingface_token` | string | Falls back to `$HUGGINGFACE_TOKEN` |
148
+ | `http_headers` | table | Extra headers attached to HF / HTTP downloads |
149
+ | `context_release_delay_ms` | number | Idle time before unloading a context (default `10000`; `0` = immediate)|
150
+ | `prefer_variants` | string[] | Override variant probe order (ggml backends) |
151
+ | `n_threads` | number | CPU thread count |
152
+ | `n_ctx` | number | Context window (per-model value wins; auto-capped at training context) |
153
+ | `n_gpu_layers` | number\|`"auto"` | Layers offloaded to GPU (default `"auto"`) |
154
+ | `n_batch` / `n_ubatch` | number | Prompt batch / micro-batch size. **Note:** `n_batch` has a model-level default of `512` that shadows the runtime value unless `[generators.model] n_batch` is set explicitly. |
155
+ | `n_parallel` | number | Parallel sequences (default `4`) |
156
+ | `n_cpu_moe` | number | MoE expert layers offloaded to CPU |
157
+ | `flash_attn_type` | `"on"` / `"off"` / `"auto"` | When a GPU backend is selected, defaults to `"auto"`; on CPU, defaults to `"off"`. Explicit `"on"` / `"off"` / `"auto"` overrides. |
158
+ | `cache_type_k`, `cache_type_v` | string | KV-cache dtype (`f16`, `f32`, `q8_0`, `q4_0`, …) |
159
+ | `kv_unified` | boolean | Use a unified KV cache across sequences |
160
+ | `swa_full` | boolean | Materialize full attention even for sliding-window layers |
161
+ | `ctx_shift` | boolean | Allow llama.cpp's rolling context shift |
162
+ | `use_mmap`, `use_mlock` | boolean | Memory-mapping / locking |
163
+ | `no_extra_bufts` | boolean | Disable extra compute buffer types |
164
+ | `cpu_mask`, `cpu_strict` | string / boolean | CPU affinity (advanced) |
165
+ | `devices` | string[] | Restrict to specific GGML devices |
166
+ | Speculative keys | various | `speculative`, `spec_type`, `spec_draft_n_max/n_min/p_min/p_split`, plus draft-model GPU/cache settings |
167
+
168
+ ### `[autodiscover]`
169
+
170
+ Set `autodiscover = true` for defaults, `false` (or omit) to disable, or a table for fine control:
39
171
 
40
- [runtime]
41
- cache_dir = "~/.buttress/models"
172
+ ```toml
173
+ [autodiscover]
174
+ udp.port = 8089
175
+ udp.announcements = { enabled = true, interval = 5000 }
176
+ udp.requests = { enabled = true, responseDelay = 100 }
177
+ http.enabled = true
178
+ http.path = "/buttress/info"
179
+ http.cors = true
180
+ # mdns.enabled = false # Bonjour/Avahi advertisement (optional)
181
+ ```
42
182
 
43
- # Session state cache for ggml-llm (saves KV cache to disk for prompt reuse)
44
- [runtime.session_cache]
45
- enabled = true
46
- max_size_bytes = "10GB" # Supports string (e.g., "10GB", "500MB") or number
47
- max_entries = 1000
183
+ ### `[[generators]]`
48
184
 
49
- # GGML LLM generator
185
+ Every generator entry has a `type`, an optional `[generators.backend]` table, and a `[generators.model]` table:
186
+
187
+ ```toml
188
+ [[generators]]
189
+ type = "ggml-llm" # or "ggml-stt" / "mlx-llm"
190
+
191
+ [generators.backend]
192
+ # (see per-type sections below)
193
+
194
+ [generators.model]
195
+ repo_id = "..."
196
+ # (see per-type sections below)
197
+ ```
198
+
199
+ #### Common `[generators.model]` keys
200
+
201
+ Shared by **all** generator types:
202
+
203
+ | Key | Type | Notes |
204
+ | ------------------------- | --------- | -------------------------------------------------------------------------------- |
205
+ | `repo_id` *(required)* | string | HuggingFace repo (`org/repo`) |
206
+ | `revision` | string | Default `"main"` |
207
+ | `download` | boolean | Pre-download at server startup (default `false`) |
208
+
209
+ Additional keys honored by **ggml-llm** and **ggml-stt** (mlx-llm gets quantization from the repo itself and does not use these):
210
+
211
+ | Key | Type | Notes |
212
+ | ------------------------- | --------- | -------------------------------------------------------------------------------- |
213
+ | `filename` | string | Pin a specific artifact in the repo |
214
+ | `url` | string | Direct download URL (skips manifest lookup) |
215
+ | `quantization` | string | Preferred quant tag — e.g. `q4_0`, `q8_0`, `mxfp4` |
216
+ | `preferred_quantizations` | string[] | Ordered fallback list when `quantization` doesn't match (alias: `quantizations`) |
217
+ | `allow_local_file` | boolean | Required to use `local_path` / `mmproj_local_path` |
218
+ | `local_path` | string | Use a local file as the load path. Repo metadata is still resolved from HF, so `repo_id` is still required. |
219
+ | `api_base`, `base_url` | string | Override HF API / blob hosts (mirrors / proxies) |
220
+
221
+ ---
222
+
223
+ ### `ggml-llm` (llama.cpp via `@fugood/llama.node`)
224
+
225
+ Loads a GGUF LLM. Runtime keys above can be overridden per-generator under `[generators.model]`; `[generators.backend]` only controls backend selection and resource planning.
226
+
227
+ **`[generators.backend]`**
228
+
229
+ | Key | Type | Default | Notes |
230
+ | --------------------- | -------- | --------------------------------------------- | ---------------------------------------------------------------- |
231
+ | `variant` | string | auto | Force `cuda` / `vulkan` / `snapdragon` / `default` |
232
+ | `variant_preference` | string[] | `["cuda","vulkan","snapdragon","default"]` | Probe order when `variant` is unset |
233
+ | `gpu_memory_fraction` | number | `0.85` | Max GPU fraction the hardware guardrails may plan against |
234
+ | `cpu_memory_fraction` | number | `0.5` | Max RAM fraction for CPU-side buffers |
235
+
236
+ **`[generators.model]`** — in addition to the common keys above:
237
+
238
+ | Key | Type | Notes |
239
+ | ----------------------------------------------------------------------------- | ---------------- | -------------------------------------------------------------------- |
240
+ | `n_ctx` | number | Context window. Auto-capped at the model's training context. |
241
+ | `n_gpu_layers` | number\|`"auto"` | Layers offloaded to GPU (default `"auto"`) |
242
+ | `n_batch` | number | Prompt batch size (default `512`) |
243
+ | `n_ubatch`, `n_threads`, `n_parallel`, `n_cpu_moe` | number | Same semantics as the `[runtime]` defaults |
244
+ | `flash_attn_type`, `cache_type_k`, `cache_type_v`, `kv_unified`, `swa_full`, `ctx_shift`, `use_mmap`, `use_mlock`, `no_extra_bufts`, `cpu_mask`, `cpu_strict`, `devices` | various | Per-model overrides for the `[runtime]` defaults |
245
+
246
+ **Multimodal (mtmd)** — auto-downloads the matching `mmproj-*.gguf` from the same repo and calls `initMultimodal`:
247
+
248
+ | Key | Type | Notes |
249
+ | ------------------------- | ------- | ------------------------------------------------------------------ |
250
+ | `enable_mtmd` | boolean | Default `false` |
251
+ | `mmproj_filename` | string | Pin a specific projector file |
252
+ | `mmproj_url` | string | Direct URL override |
253
+ | `mmproj_local_path` | string | Local projector (requires `allow_local_file = true`) |
254
+ | `mmproj_use_gpu` | boolean | `null` = auto (true when `n_gpu_layers > 0`) |
255
+ | `mmproj_image_min_tokens` | number | Min visual tokens (dynamic-resolution models; `-1` = unset) |
256
+ | `mmproj_image_max_tokens` | number | Max visual tokens (`-1` = unset) |
257
+
258
+ **Speculative decoding**
259
+
260
+ `model_draft` may be a direct URL/path string or a table with the same
261
+ `repo_id`/`filename`/`url`/`local_path` model keys described above. Local draft paths require
262
+ `allow_local_file = true`. When `download = true`, Buttress pre-downloads both target and draft
263
+ models and includes both in its memory plan.
264
+
265
+ | Key | Type | Notes |
266
+ | -------------------------- | --------------- | ------------------------------------------------------------ |
267
+ | `model_draft` | string \| table | Optional separate GGUF draft model |
268
+ | `speculative` | bool \| string \| table | Enable speculative decoding and optionally select a strategy |
269
+ | `spec_type` | string | Strategy, such as `"draft-mtp"` |
270
+ | `spec_draft_n_max` | int | Max drafted tokens per step |
271
+ | `spec_draft_n_min` | int | Min drafted tokens |
272
+ | `spec_draft_p_min` | number | Min acceptance probability |
273
+ | `spec_draft_p_split` | number | Split threshold |
274
+ | `spec_draft_n_gpu_layers` | int | Draft-model layers offloaded to GPU (`-1` auto, `-2` all) |
275
+ | `spec_draft_cache_type_k` | string | Draft-model K-cache dtype |
276
+ | `spec_draft_cache_type_v` | string | Draft-model V-cache dtype |
277
+
278
+ **Example**
279
+
280
+ ```toml
50
281
  [[generators]]
51
282
  type = "ggml-llm"
52
283
  [generators.backend]
53
284
  variant_preference = ["cuda", "vulkan", "default"]
285
+ gpu_memory_fraction = 0.95
54
286
  [generators.model]
55
287
  repo_id = "ggml-org/gpt-oss-20b-GGUF"
56
288
  quantization = "mxfp4"
57
289
  n_ctx = 12800
290
+ download = true
291
+ ```
292
+
293
+ A separate draft model can be configured inline:
294
+
295
+ ```toml
296
+ [generators.model]
297
+ repo_id = "org/target-model-GGUF"
298
+ model_draft = { repo_id = "org/draft-model-GGUF", filename = "draft-q8_0.gguf" }
299
+ speculative = { type = "draft-mtp" }
300
+ spec_draft_n_max = 4
301
+ spec_draft_n_gpu_layers = -1
302
+ spec_draft_cache_type_k = "f16"
303
+ spec_draft_cache_type_v = "f16"
304
+ ```
305
+
306
+ ---
307
+
308
+ ### `ggml-stt` (whisper.cpp via `@fugood/whisper.node`)
309
+
310
+ Loads a Whisper GGML model for speech-to-text.
311
+
312
+ **`[generators.backend]`**
313
+
314
+ | Key | Type | Default | Notes |
315
+ | --------------------- | -------- | ----------------------------- | ---------------------------------- |
316
+ | `variant` | string | auto | `cuda` / `vulkan` / `default` |
317
+ | `variant_preference` | string[] | `["cuda","vulkan","default"]` | Probe order |
318
+ | `gpu_memory_fraction` | number | `0.85` | |
319
+ | `cpu_memory_fraction` | number | `0.5` | |
58
320
 
59
- # GGML STT (Speech-to-Text) generator
321
+ **`[generators.model]`** common keys plus:
322
+
323
+ | Key | Type | Default | Notes |
324
+ | ------------------------- | ----------------------------- | -------------------------------- | ---------------------------------------------------- |
325
+ | `repo_id` | string | `"BricksDisplay/whisper-ggml"` | Defaulted (unlike ggml-llm) |
326
+ | `preferred_quantizations` | string[] | `["q8_0", <no-quant>, "q5_1"]` | Default fallback chain |
327
+ | `use_gpu` | boolean | `true` | Force-disable GPU even when available |
328
+ | `use_flash_attn` | `"on"` / `"off"` / `"auto"` / boolean | `"auto"` | `"auto"` enables flash-attn when GPU is in use. `true`/`false` are accepted as shortcuts for `"on"`/`"off"`. |
329
+
330
+ **Runtime extras** — under `[runtime]` for ggml-stt only:
331
+
332
+ | Key | Type | Notes |
333
+ | ------------- | ------ | ------------------------------------------- |
334
+ | `max_threads` | number | Caps the whisper.cpp thread count |
335
+
336
+ **Example**
337
+
338
+ ```toml
60
339
  [[generators]]
61
340
  type = "ggml-stt"
62
341
  [generators.backend]
63
- variant_preference = ["coreml", "default"]
342
+ variant_preference = ["cuda", "vulkan", "default"]
64
343
  [generators.model]
65
344
  repo_id = "BricksDisplay/whisper-ggml"
66
- filename = "ggml-small.bin"
345
+ filename = "ggml-large-v3-turbo-q8_0.bin"
346
+ use_gpu = true
347
+ use_flash_attn = "on"
348
+ download = true
349
+ ```
350
+
351
+ ---
352
+
353
+ ### `mlx-llm` (Apple Silicon, Python `mlx-lm` / `mlx-vlm` bridge)
354
+
355
+ Loads an MLX-format model on Apple Silicon. On first use, the backend creates a virtualenv at `{cache_dir}/mlx-env` and installs `mlx_lm_package`, `mlx_vlm_package`, plus `torch` and `torchvision` (required by some VLM processors). If an existing venv already has `mlx_vlm` and `torch` importable, the install step is skipped. There is no `[generators.backend]` section.
356
+
357
+ **`[generators.model]`** — common `repo_id` / `revision` / `download` plus:
358
+
359
+ | Key | Type | Default | Notes |
360
+ | ------------------ | ------------------- | --------- | ----------------------------------------------------------------------------- |
361
+ | `adapter_path` | string | — | Local LoRA adapter directory |
362
+ | `vlm` | `"auto"` / boolean | `"auto"` | Force VLM (`true`) vs text-only (`false`); `"auto"` infers from the repo |
363
+ | `tokenizer_config` | table | — | Forwarded to `mlx_lm.load(..., tokenizer_config=...)` |
364
+ | `model_config` | table | — | Forwarded to `mlx_lm.load(..., model_config=...)` |
365
+
366
+ `quantization`, `filename`, and `preferred_quantizations` are **not** used — the MLX repo itself determines the quantization.
367
+
368
+ **Runtime extras** — under `[runtime]` for mlx-llm:
369
+
370
+ | Key | Type | Default | Notes |
371
+ | ------------------- | ------ | ----------------------------- | -------------------------------------------------------------------- |
372
+ | `mlx_env_dir` | string | `{cache_dir}/mlx-env` | Location of the auto-managed Python venv |
373
+ | `mlx_lm_package` | string | `"mlx-lm==0.31.1"` | pip spec used when provisioning the venv |
374
+ | `mlx_vlm_package` | string | `"mlx-vlm==0.4.0"` | pip spec used when provisioning the venv |
375
+ | `session_cache.*` | table | enabled, `5GB`, 100 entries | Separate cache from ggml-llm (lives in `{cache_dir}/mlx-session-cache`) |
376
+
377
+ **Example**
378
+
379
+ ```toml
380
+ [[generators]]
381
+ type = "mlx-llm"
382
+ [generators.model]
383
+ repo_id = "mlx-community/Qwen2.5-VL-3B-Instruct-4bit"
384
+ vlm = true
385
+ download = true
67
386
  ```
68
387
 
69
388
  ### Programmatic Usage
@@ -73,6 +73,15 @@ quantization = "mxfp4"
73
73
  download = true
74
74
  n_ctx = 12800 # Max: 131072
75
75
 
76
+ # Optional separate draft model for speculative decoding. Buttress includes the
77
+ # draft model in pre-downloads and memory planning.
78
+ # model_draft = { repo_id = "org/draft-model-GGUF", filename = "draft-q8_0.gguf" }
79
+ # speculative = { type = "draft-mtp" }
80
+ # spec_draft_n_max = 4
81
+ # spec_draft_n_gpu_layers = -1
82
+ # spec_draft_cache_type_k = "f16"
83
+ # spec_draft_cache_type_v = "f16"
84
+
76
85
  [[generators]]
77
86
  type = "ggml-llm"
78
87
  [generators.backend]
@@ -0,0 +1,20 @@
1
+ import type { AutodiscoverConfig } from '../types';
2
+ import type { GetServerInfoFn } from './types';
3
+ import { type AnnounceSigner } from './udp';
4
+ export type { GetServerInfoFn } from './types';
5
+ export { signEnvelope, buildAnnounceSigner, type AnnounceSigner } from './sign';
6
+ /**
7
+ * Autodiscover service that manages discovery transports.
8
+ * Currently supports UDP announcements/responses.
9
+ * HTTP discovery is handled by the info route.
10
+ */
11
+ export declare class AutodiscoverService {
12
+ private config;
13
+ private getServerInfo;
14
+ private signer;
15
+ private transports;
16
+ private started;
17
+ constructor(config: AutodiscoverConfig, getServerInfo: GetServerInfoFn, signer: AnnounceSigner | null);
18
+ start(): Promise<void>;
19
+ stop(): Promise<void>;
20
+ }
@@ -0,0 +1,10 @@
1
+ import crypto from 'node:crypto';
2
+ import type { WorkspaceState } from '../utils/workspaceState';
3
+ import { type IDiscoveryProtocol } from './types';
4
+ export interface AnnounceSigner {
5
+ kid: string;
6
+ privateKey: crypto.KeyObject;
7
+ }
8
+ export declare const canonicalBytes: (t: 'ANNOUNCE' | 'RESPONSE', d: unknown, ts: number) => Buffer;
9
+ export declare const signEnvelope: (signer: AnnounceSigner | null, t: 'ANNOUNCE' | 'RESPONSE', d: IDiscoveryProtocol['d']) => IDiscoveryProtocol | null;
10
+ export declare const buildAnnounceSigner: (workspaceState: WorkspaceState) => AnnounceSigner | null;
@@ -0,0 +1,32 @@
1
+ import type { ServerInfo } from '../types';
2
+ export declare const PROTOCOL_VERSION = "2.0";
3
+ export declare const DEFAULT_PORT = 8089;
4
+ export interface ITransport {
5
+ name: string;
6
+ start(): Promise<void>;
7
+ stop(): Promise<void>;
8
+ }
9
+ interface DiscoveryAnnounce {
10
+ info: Partial<ServerInfo>;
11
+ }
12
+ export interface DiscoveryRequest {
13
+ id: string;
14
+ filters?: {
15
+ generators?: string[];
16
+ min_version?: string;
17
+ };
18
+ }
19
+ interface DiscoveryResponse {
20
+ request_id: string;
21
+ info: Partial<ServerInfo>;
22
+ }
23
+ export interface IDiscoveryProtocol {
24
+ t: 'ANNOUNCE' | 'QUERY' | 'RESPONSE';
25
+ v: string;
26
+ d: DiscoveryAnnounce | DiscoveryRequest | DiscoveryResponse;
27
+ ts?: number;
28
+ kid?: string;
29
+ sig?: string;
30
+ }
31
+ export type GetServerInfoFn = () => ServerInfo;
32
+ export {};
@@ -0,0 +1,22 @@
1
+ import type { AutodiscoverConfig } from '../types';
2
+ import { ITransport, GetServerInfoFn } from './types';
3
+ import { type AnnounceSigner } from './sign';
4
+ export type { AnnounceSigner } from './sign';
5
+ export declare class UdpTransport implements ITransport {
6
+ name: string;
7
+ private receiver;
8
+ private senders;
9
+ private announcementTimer;
10
+ private config;
11
+ private getServerInfo;
12
+ private port;
13
+ private signer;
14
+ constructor(config: AutodiscoverConfig['udp'], getServerInfo: GetServerInfoFn, signer: AnnounceSigner | null);
15
+ start(): Promise<void>;
16
+ stop(): Promise<void>;
17
+ private bindReceiver;
18
+ private createSenders;
19
+ private handleMessage;
20
+ private sendAnnouncement;
21
+ private sendResponse;
22
+ }
package/lib/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export default _default;
3
+ declare function _default(): Promise<void>;
package/lib/index.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { AnyElysia } from 'elysia';
2
+ import * as backendCore from '@fugood/buttress-backend-core';
3
+ import { AutodiscoverService } from './autodiscover';
4
+ import type { Config } from './types';
5
+ export { startModelDownload } from '@fugood/buttress-backend-core';
6
+ export { processConfig } from './utils/config';
7
+ export declare const checkForUpdates: () => Promise<string | null>;
8
+ export declare const compareVersions: (current: string, latest: string) => boolean;
9
+ export declare const logUpdateMessage: (latestVersion: string) => void;
10
+ export declare const checkAndNotifyUpdates: () => Promise<void>;
11
+ export type Backend = typeof backendCore;
12
+ export interface StartServerOptions {
13
+ backend?: Backend;
14
+ router?: AnyElysia;
15
+ config: Config;
16
+ enableOpenAICompat?: boolean;
17
+ enableAnthropicMessages?: boolean;
18
+ }
19
+ export declare const createServer: ({ backend, router, config, enableOpenAICompat, enableAnthropicMessages, }: StartServerOptions) => Promise<{
20
+ app: AnyElysia;
21
+ config: Config;
22
+ }>;
23
+ export declare const startServer: ({ backend, router, config, enableOpenAICompat, enableAnthropicMessages, }: StartServerOptions) => Promise<{
24
+ app: AnyElysia;
25
+ port: number;
26
+ openaiEnabled: boolean;
27
+ anthropicMessagesEnabled: boolean;
28
+ autoDiscover: AutodiscoverService | null;
29
+ }>;