1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//! Every path `ferrox-server` serves, named once.
//!
//! Only routes that actually exist belong here. A constant for a
//! not-yet-implemented endpoint is worse than no constant at all: it
//! reads as a promise, and a client that imports it gets a 404 with the
//! contract crate's blessing.
/// Liveness + readiness + capability handshake. Never behind auth, so a
/// probe works regardless of `FERROX_API_KEY`.
pub const HEALTH: &str = "/health";
/// Prometheus text-exposition metrics.
pub const METRICS: &str = "/metrics";
/// Response- and prefix-cache counters.
pub const CACHE_STATS: &str = "/cache/stats";
pub const V1_MODELS: &str = "/v1/models";
pub const V1_CHAT_COMPLETIONS: &str = "/v1/chat/completions";
pub const V1_COMPLETIONS: &str = "/v1/completions";
pub const V1_TOKENIZE: &str = "/v1/tokenize";
pub const V1_DETOKENIZE: &str = "/v1/detokenize";
/// llama.cpp's **native** completion endpoint, which is not
/// [`V1_COMPLETIONS`] with a shorter path.
///
/// Different request shape (`n_predict`, `repeat_penalty`,
/// `cache_prompt`, …) and a different response shape (a flat object
/// with `content` and `stop`, not `choices`), and its stream is a
/// sequence of `data: {"content":…,"stop":false}` frames with **no**
/// `[DONE]` sentinel. This is what llama.cpp's own web UI, `llama.vim`
/// and a long tail of wrappers speak; `/v1/completions` is the OpenAI
/// dialect and stays what it is.
pub const COMPLETION: &str = "/completion";
/// llama.cpp mounts its native endpoint under both spellings
/// (`tools/server/server.cpp:240-241`), the plural being the one its
/// own web UI uses. **Not** an alias of [`V1_COMPLETIONS`]: dropping
/// the `/v1` changes the dialect, not just the path.
pub const COMPLETIONS: &str = "/completions";
/// llama.cpp's spelling of [`V1_TOKENIZE`], mounted on the same
/// handler.
///
/// The `/v1/` prefix was ferrox's invention: OpenAI has no tokenize
/// endpoint at all, and llama.cpp serves this one unprefixed
/// (`tools/server/server.cpp:259`). Every llama.cpp client therefore
/// asked for a path that did not exist, and got a 404 that named
/// nothing. Both spellings now answer, and the handler accepts
/// llama.cpp's `content` field alongside ferrox's `prompt`.
pub const TOKENIZE: &str = "/tokenize";
/// llama.cpp's spelling of [`V1_DETOKENIZE`], mounted on the same
/// handler (`tools/server/server.cpp:260`). The response carries the
/// text under both `content` (llama.cpp's key) and `text` (ferrox's).
pub const DETOKENIZE: &str = "/detokenize";
pub const V1_EMBEDDINGS: &str = "/v1/embeddings";
/// Cross-encoder reranking: one query against N documents, scored by
/// the checkpoint's own classification head (`cls` / `cls.output`), not
/// by the cosine similarity of two embeddings.
///
/// The path Cohere and Jina clients use, and one of the four llama.cpp
/// mounts. Not an OpenAI endpoint at all -- OpenAI has no reranker --
/// so the request and response shapes follow Jina/Cohere, which is what
/// every existing client already speaks.
pub const V1_RERANK: &str = "/v1/rerank";
/// llama.cpp's unprefixed spelling of [`V1_RERANK`], mounted on the
/// same handler (`tools/server/server.cpp` registers `/rerank`,
/// `/reranking`, `/v1/rerank` and `/v1/reranking`).
///
/// Same reasoning as [`TOKENIZE`]: the client's configured URL should
/// work unchanged rather than 404 on a prefix ferrox chose. Unlike
/// [`COMPLETION`], this one really is an alias -- same dialect, same
/// body, same response.
pub const RERANK: &str = "/rerank";
/// Anthropic-compatible messages endpoint.
pub const V1_MESSAGES: &str = "/v1/messages";
/// Anthropic's prompt-sizing endpoint: how many input tokens a request
/// *would* cost, without generating any. Behind the same key as
/// [`V1_MESSAGES`], because answering it requires the loaded
/// checkpoint's own tokenizer and chat template.
pub const V1_MESSAGES_COUNT_TOKENS: &str = "/v1/messages/count_tokens";
/// Explicit cancellation of one in-flight generation, by the
/// `request_id` the server states on the first streamed chunk.
///
/// Under `/v1` rather than at the root the plan sketched it at, for two
/// reasons that both matter: it acts on inference and so belongs behind
/// the same `FERROX_API_KEY` gate as the endpoint that started the
/// work, and `/v1` is where every other inference path already lives,
/// so a client configured with one base URL reaches all of them.
///
/// This is the second tier of cancellation, not the only one -- a
/// client that simply drops the connection is also honoured. It exists
/// because the first tier is unreliable: proxies buffer, and a page
/// unload races the abort it is supposed to send. `keepalive: true`
/// makes this one survive that.
pub const V1_CANCEL: &str = "/v1/cancel";
/// Reconnect into a stream started with `stream_resumable: true`,
/// resuming after the `Last-Event-ID` the client last saw.
///
/// **A template, not a literal** -- see [`ADMIN_TASK_CANCEL`] for why
/// this crate writes placeholders in the OpenAPI style. Build a
/// concrete path with [`v1_stream`].
///
/// Behind the same key as the endpoint that started the work: the
/// replay buffer holds the model's output, so reading it must cost
/// exactly what producing it cost.
pub const V1_STREAM: &str = "/v1/stream/{request_id}";
/// The same replay buffer over plain JSON, for the case SSE cannot
/// survive: a reverse proxy that buffers `text/event-stream` turns a
/// stream into one long silence, and cannot do that to a short response
/// that has already ended. Build a concrete path with
/// [`v1_stream_poll`].
pub const V1_STREAM_POLL: &str = "/v1/stream/{request_id}/poll";
// ---------------------------------------------------------------------
// Control surface.
//
// Everything under `/admin` either changes what the server serves or
// writes to disk, so all of it sits behind the same `FERROX_API_KEY`
// gate as `/v1/*` -- never on the unauthenticated `/health` side.
// ---------------------------------------------------------------------
/// Model inventory: what is on disk, what is loaded, what failed.
pub const ADMIN_MODELS: &str = "/admin/models";
/// Start loading a discovered model by its `id`. Answers `202` with a
/// task id; the load itself runs off the request.
pub const ADMIN_MODELS_LOAD: &str = "/admin/models/load";
/// Drop the active model. Synchronous: unloading is releasing one
/// `Arc`, and requests already decoding keep theirs.
pub const ADMIN_MODELS_UNLOAD: &str = "/admin/models/unload";
/// Fetch a `.gguf` from the Hugging Face Hub into the model directory.
/// Answers `202` with a task id.
pub const ADMIN_DOWNLOAD: &str = "/admin/download";
/// Every long-running job this server knows about, newest first.
pub const ADMIN_TASKS: &str = "/admin/tasks";
/// Request cancellation of one task. **A template, not a literal**: the
/// `{task_id}` placeholder is written in the OpenAPI style rather than
/// any one web framework's, because this crate is imported by clients
/// that have never heard of the server's router. Build a concrete path
/// with [`admin_task_cancel`].
pub const ADMIN_TASK_CANCEL: &str = "/admin/tasks/{task_id}/cancel";
/// Counters, uptime, and the recent-request ring buffer.
pub const ADMIN_STATS: &str = "/admin/stats";
/// The OpenAI **Responses** surface -- what `codex` speaks. A different
/// request/response shaping over the same generation path, not a second
/// engine.
pub const V1_RESPONSES: &str = "/v1/responses";
/// One stored response. This server is stateless, so it answers 404 --
/// deliberately, rather than 404-ing from the router, because the two
/// say different things: the route EXISTS and keeps nothing, which
/// tells a client to stop polling rather than to check its base URL.
pub const V1_RESPONSE: &str = "/v1/responses/{response_id}";
/// Cancel one stored response. Same stateless answer; a live generation
/// is stopped through [`V1_CANCEL`] with its `request_id`.
pub const V1_RESPONSE_CANCEL: &str = "/v1/responses/{response_id}/cancel";
/// Live serving telemetry: throughput over a trailing window, request
/// latency percentile, and the cache pools' occupancy. Distinct from
/// [`ADMIN_STATS`], which is this server's own operational ring; this
/// is the shape a desktop or dashboard polls.
pub const V1_STATS: &str = "/v1/stats";
/// Incremental page over the recent-request ring: `?since=<cursor>` and
/// `?limit=<n>`. The cursor is all-time, so a poller that keeps up
/// reads each row exactly once.
pub const V1_REQUESTS: &str = "/v1/requests";
/// The current cache geometry: how VRAM is split between the expert
/// cache and the KV pools, and what a re-split could move.
pub const V1_CACHE_STATUS: &str = "/v1/cache/status";
/// Re-split the caches on a live engine. New generation is refused
/// while a rebuild is in flight.
pub const V1_CACHE_REBUILD: &str = "/v1/cache/rebuild";
/// Close admission, drain, and seal the final accounting snapshot. A
/// supervisor calls this before it sends a signal, so process shutdown
/// cannot race the last sampled token.
pub const ADMIN_PREPARE_STOP: &str = "/v1/admin/prepare-stop";
/// The concrete cancel path for one task id.
/// The concrete resume path for one request id.
/// The concrete polling-fallback path for one request id.
/// Every fixed route above, for clients that want to enumerate the
/// surface (and for the round-trip test below).
///
/// [`ADMIN_TASK_CANCEL`], [`V1_STREAM`] and [`V1_STREAM_POLL`] are
/// deliberately absent: they are templates, and a caller iterating this
/// list to probe paths would get a 404 for a literal `{task_id}`.
pub const ALL: & = &;