@cerebriline/llms 0.0.0-stage → 4.100.241
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.
- package/LICENSE +201 -0
- package/README.md +15 -2
- package/dist/catalog/catalog-cline-recommended.d.ts +14 -0
- package/dist/catalog/catalog-cline-recommended.d.ts.map +1 -0
- package/dist/catalog/catalog-live.d.ts +64 -0
- package/dist/catalog/catalog-live.d.ts.map +1 -0
- package/dist/catalog/catalog.generated-access.d.ts +5 -0
- package/dist/catalog/catalog.generated-access.d.ts.map +1 -0
- package/dist/catalog/catalog.generated.d.ts +17 -0
- package/dist/catalog/catalog.generated.d.ts.map +1 -0
- package/dist/catalog/model-filters.d.ts +9 -0
- package/dist/catalog/model-filters.d.ts.map +1 -0
- package/dist/catalog/model-id-aliases.d.ts +11 -0
- package/dist/catalog/model-id-aliases.d.ts.map +1 -0
- package/dist/catalog/model-operation.d.ts +27 -0
- package/dist/catalog/model-operation.d.ts.map +1 -0
- package/dist/catalog/types.d.ts +367 -0
- package/dist/catalog/types.d.ts.map +1 -0
- package/dist/index.browser.d.ts +12 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.browser.js +16 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +272 -0
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +241 -0
- package/dist/providers/agent-slots.d.ts +100 -0
- package/dist/providers/agent-slots.d.ts.map +1 -0
- package/dist/providers/ai-sdk.d.ts +158 -0
- package/dist/providers/ai-sdk.d.ts.map +1 -0
- package/dist/providers/async.d.ts +2 -0
- package/dist/providers/async.d.ts.map +1 -0
- package/dist/providers/billing.d.ts +5 -0
- package/dist/providers/billing.d.ts.map +1 -0
- package/dist/providers/builtin-types.d.ts +34 -0
- package/dist/providers/builtin-types.d.ts.map +1 -0
- package/dist/providers/builtins-runtime.d.ts +3 -0
- package/dist/providers/builtins-runtime.d.ts.map +1 -0
- package/dist/providers/builtins.d.ts +30 -0
- package/dist/providers/builtins.d.ts.map +1 -0
- package/dist/providers/compat.d.ts +75 -0
- package/dist/providers/compat.d.ts.map +1 -0
- package/dist/providers/config.d.ts +521 -0
- package/dist/providers/config.d.ts.map +1 -0
- package/dist/providers/error-classification.d.ts +23 -0
- package/dist/providers/error-classification.d.ts.map +1 -0
- package/dist/providers/errors.d.ts +33 -0
- package/dist/providers/errors.d.ts.map +1 -0
- package/dist/providers/factory-registry.d.ts +92 -0
- package/dist/providers/factory-registry.d.ts.map +1 -0
- package/dist/providers/format.d.ts +2 -0
- package/dist/providers/format.d.ts.map +1 -0
- package/dist/providers/gateway.d.ts +123 -0
- package/dist/providers/gateway.d.ts.map +1 -0
- package/dist/providers/handler.d.ts +75 -0
- package/dist/providers/handler.d.ts.map +1 -0
- package/dist/providers/http.d.ts +10 -0
- package/dist/providers/http.d.ts.map +1 -0
- package/dist/providers/ids.d.ts +77 -0
- package/dist/providers/ids.d.ts.map +1 -0
- package/dist/providers/messages.d.ts +5 -0
- package/dist/providers/messages.d.ts.map +1 -0
- package/dist/providers/middleware/backoff.d.ts +11 -0
- package/dist/providers/middleware/backoff.d.ts.map +1 -0
- package/dist/providers/middleware/retry-empty-response.d.ts +74 -0
- package/dist/providers/middleware/retry-empty-response.d.ts.map +1 -0
- package/dist/providers/middleware/retry-rate-limit.d.ts +49 -0
- package/dist/providers/middleware/retry-rate-limit.d.ts.map +1 -0
- package/dist/providers/middleware/split-tool-images.d.ts +32 -0
- package/dist/providers/middleware/split-tool-images.d.ts.map +1 -0
- package/dist/providers/middleware/stream-part-classification.d.ts +33 -0
- package/dist/providers/middleware/stream-part-classification.d.ts.map +1 -0
- package/dist/providers/model-capabilities.d.ts +15 -0
- package/dist/providers/model-capabilities.d.ts.map +1 -0
- package/dist/providers/model-facts.d.ts +115 -0
- package/dist/providers/model-facts.d.ts.map +1 -0
- package/dist/providers/model-operations.d.ts +78 -0
- package/dist/providers/model-operations.d.ts.map +1 -0
- package/dist/providers/model-registry.d.ts +25 -0
- package/dist/providers/model-registry.d.ts.map +1 -0
- package/dist/providers/model-tools.d.ts +22 -0
- package/dist/providers/model-tools.d.ts.map +1 -0
- package/dist/providers/openai-codex-models.d.ts +11 -0
- package/dist/providers/openai-codex-models.d.ts.map +1 -0
- package/dist/providers/provider-ids.generated.d.ts +13 -0
- package/dist/providers/provider-ids.generated.d.ts.map +1 -0
- package/dist/providers/provider-keys.d.ts +13 -0
- package/dist/providers/provider-keys.d.ts.map +1 -0
- package/dist/providers/provider-request-capture.d.ts +10 -0
- package/dist/providers/provider-request-capture.d.ts.map +1 -0
- package/dist/providers/providers.generated.d.ts +19 -0
- package/dist/providers/providers.generated.d.ts.map +1 -0
- package/dist/providers/reasoning-history.d.ts +140 -0
- package/dist/providers/reasoning-history.d.ts.map +1 -0
- package/dist/providers/registry.d.ts +19 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/request-headers.d.ts +30 -0
- package/dist/providers/request-headers.d.ts.map +1 -0
- package/dist/providers/request-timings.d.ts +20 -0
- package/dist/providers/request-timings.d.ts.map +1 -0
- package/dist/providers/routing/anthropic-compatible.d.ts +35 -0
- package/dist/providers/routing/anthropic-compatible.d.ts.map +1 -0
- package/dist/providers/routing/bedrock-cache-point.d.ts +26 -0
- package/dist/providers/routing/bedrock-cache-point.d.ts.map +1 -0
- package/dist/providers/routing/generic-compatible.d.ts +10 -0
- package/dist/providers/routing/generic-compatible.d.ts.map +1 -0
- package/dist/providers/routing/glm-thinking.d.ts +16 -0
- package/dist/providers/routing/glm-thinking.d.ts.map +1 -0
- package/dist/providers/routing/minimax-thinking.d.ts +6 -0
- package/dist/providers/routing/minimax-thinking.d.ts.map +1 -0
- package/dist/providers/routing/portable-reasoning.d.ts +11 -0
- package/dist/providers/routing/portable-reasoning.d.ts.map +1 -0
- package/dist/providers/routing/provider-option-rules.d.ts +13 -0
- package/dist/providers/routing/provider-option-rules.d.ts.map +1 -0
- package/dist/providers/routing/provider-options-types.d.ts +57 -0
- package/dist/providers/routing/provider-options-types.d.ts.map +1 -0
- package/dist/providers/routing/provider-options.d.ts +27 -0
- package/dist/providers/routing/provider-options.d.ts.map +1 -0
- package/dist/providers/routing/reasoning-codecs.d.ts +4 -0
- package/dist/providers/routing/reasoning-codecs.d.ts.map +1 -0
- package/dist/providers/routing/reasoning-options.d.ts +8 -0
- package/dist/providers/routing/reasoning-options.d.ts.map +1 -0
- package/dist/providers/routing/utils.d.ts +31 -0
- package/dist/providers/routing/utils.d.ts.map +1 -0
- package/dist/providers/sdk-warnings.d.ts +14 -0
- package/dist/providers/sdk-warnings.d.ts.map +1 -0
- package/dist/providers/server-health.d.ts +66 -0
- package/dist/providers/server-health.d.ts.map +1 -0
- package/dist/providers/stream.d.ts +124 -0
- package/dist/providers/stream.d.ts.map +1 -0
- package/dist/providers/types.d.ts +10 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/url.d.ts +3 -0
- package/dist/providers/url.d.ts.map +1 -0
- package/dist/providers/utils.d.ts +2 -0
- package/dist/providers/utils.d.ts.map +1 -0
- package/dist/providers/vendors/anthropic.d.ts +4 -0
- package/dist/providers/vendors/anthropic.d.ts.map +1 -0
- package/dist/providers/vendors/bedrock.d.ts +24 -0
- package/dist/providers/vendors/bedrock.d.ts.map +1 -0
- package/dist/providers/vendors/cline.d.ts +37 -0
- package/dist/providers/vendors/cline.d.ts.map +1 -0
- package/dist/providers/vendors/community.d.ts +8 -0
- package/dist/providers/vendors/community.d.ts.map +1 -0
- package/dist/providers/vendors/google.d.ts +12 -0
- package/dist/providers/vendors/google.d.ts.map +1 -0
- package/dist/providers/vendors/llamacpp-sampling.d.ts +130 -0
- package/dist/providers/vendors/llamacpp-sampling.d.ts.map +1 -0
- package/dist/providers/vendors/llamacpp-timings.d.ts +33 -0
- package/dist/providers/vendors/llamacpp-timings.d.ts.map +1 -0
- package/dist/providers/vendors/minimax-thinking.d.ts +5 -0
- package/dist/providers/vendors/minimax-thinking.d.ts.map +1 -0
- package/dist/providers/vendors/mistral.d.ts +4 -0
- package/dist/providers/vendors/mistral.d.ts.map +1 -0
- package/dist/providers/vendors/ollama-account.d.ts +128 -0
- package/dist/providers/vendors/ollama-account.d.ts.map +1 -0
- package/dist/providers/vendors/ollama-reachability.d.ts +42 -0
- package/dist/providers/vendors/ollama-reachability.d.ts.map +1 -0
- package/dist/providers/vendors/ollama-stall-watchdog.d.ts +86 -0
- package/dist/providers/vendors/ollama-stall-watchdog.d.ts.map +1 -0
- package/dist/providers/vendors/ollama-tool-images.d.ts +67 -0
- package/dist/providers/vendors/ollama-tool-images.d.ts.map +1 -0
- package/dist/providers/vendors/ollama.d.ts +256 -0
- package/dist/providers/vendors/ollama.d.ts.map +1 -0
- package/dist/providers/vendors/openai-compatible.d.ts +16 -0
- package/dist/providers/vendors/openai-compatible.d.ts.map +1 -0
- package/dist/providers/vendors/openai.d.ts +4 -0
- package/dist/providers/vendors/openai.d.ts.map +1 -0
- package/dist/providers/vendors/opencoti-agent-window.d.ts +44 -0
- package/dist/providers/vendors/opencoti-agent-window.d.ts.map +1 -0
- package/dist/providers/vendors/opencoti-kv-pressure.d.ts +322 -0
- package/dist/providers/vendors/opencoti-kv-pressure.d.ts.map +1 -0
- package/dist/providers/vendors/opencoti-liveness.d.ts +177 -0
- package/dist/providers/vendors/opencoti-liveness.d.ts.map +1 -0
- package/dist/providers/vendors/opencoti-window.d.ts +56 -0
- package/dist/providers/vendors/opencoti-window.d.ts.map +1 -0
- package/dist/providers/vendors/opencoti.d.ts +335 -0
- package/dist/providers/vendors/opencoti.d.ts.map +1 -0
- package/dist/providers/vendors/polykv-lead.d.ts +94 -0
- package/dist/providers/vendors/polykv-lead.d.ts.map +1 -0
- package/dist/providers/vendors/polykv-swarm.d.ts +705 -0
- package/dist/providers/vendors/polykv-swarm.d.ts.map +1 -0
- package/dist/providers/vendors/polykv.d.ts +958 -0
- package/dist/providers/vendors/polykv.d.ts.map +1 -0
- package/dist/providers/vendors/types.d.ts +176 -0
- package/dist/providers/vendors/types.d.ts.map +1 -0
- package/dist/providers/vendors/vertex.d.ts +4 -0
- package/dist/providers/vendors/vertex.d.ts.map +1 -0
- package/dist/providers/vendors/xollama-engine.d.ts +57 -0
- package/dist/providers/vendors/xollama-engine.d.ts.map +1 -0
- package/dist/providers/vendors/xollama.d.ts +188 -0
- package/dist/providers/vendors/xollama.d.ts.map +1 -0
- package/dist/providers.browser.d.ts +3 -0
- package/dist/providers.browser.d.ts.map +1 -0
- package/dist/providers.browser.js +16 -0
- package/dist/providers.d.ts +12 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +272 -0
- package/dist/services/langfuse-telemetry.d.ts +19 -0
- package/dist/services/langfuse-telemetry.d.ts.map +1 -0
- package/dist/transcription.d.ts +53 -0
- package/dist/transcription.d.ts.map +1 -0
- package/package.json +84 -5
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for opencoti-llamafile's PolyKV control plane.
|
|
3
|
+
*
|
|
4
|
+
* PolyKV is a tree of KV pools. A pool owns a token range `[D, L)` and shares
|
|
5
|
+
* everything before `D` with its parent, so the system prompt and tool
|
|
6
|
+
* definitions -- the part of an agent's context that never changes -- can be
|
|
7
|
+
* prefilled once, pinned, and attached by every later request instead of being
|
|
8
|
+
* re-sent and re-computed each turn.
|
|
9
|
+
*
|
|
10
|
+
* That is the whole reason this exists here. Measured on the Ollama path this
|
|
11
|
+
* session: 12,859 tokens of system prompt and tool schemas on every request,
|
|
12
|
+
* and a turn whose prefill ran past five minutes and killed the run. Under
|
|
13
|
+
* PolyKV that prefix is a pinned ancestor pool and the per-turn prefill is the
|
|
14
|
+
* suffix only.
|
|
15
|
+
*
|
|
16
|
+
* Two rules from the engine's own design (`docs/features/polykv_api.md`) are
|
|
17
|
+
* load-bearing for any caller:
|
|
18
|
+
*
|
|
19
|
+
* - **Compaction is a prompt rewrite, never an in-place KV op** (§13.2).
|
|
20
|
+
* Compacted text is new text; its tokens cannot match the old cells. The
|
|
21
|
+
* correct primitive is `fork(ancestor, D = D_sys, summary + recent tail)`,
|
|
22
|
+
* after which sessions migrate and the old subtree is released. Net cells go
|
|
23
|
+
* down rather than up.
|
|
24
|
+
* - **A pin left on an abandoned subtree is a leak** (§13.2). Discipline is
|
|
25
|
+
* unpin-after-migrate; the server reports orphans so they can be swept.
|
|
26
|
+
*/
|
|
27
|
+
/** A pool's identity and how much of the prefix it carries. */
|
|
28
|
+
export interface PolykvPool {
|
|
29
|
+
pool_id: string;
|
|
30
|
+
parent?: string;
|
|
31
|
+
branch_pos?: number;
|
|
32
|
+
prefix_len: number;
|
|
33
|
+
/** A `shared: true` create found an existing root instead of making one. */
|
|
34
|
+
reused?: boolean;
|
|
35
|
+
/** Said, not refused: e.g. an owner that holds no live allocation. */
|
|
36
|
+
warning?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The session whose window the pool lives in; `""` when it is unowned
|
|
39
|
+
* (charged server-wide). Absent on an engine that does not say.
|
|
40
|
+
*/
|
|
41
|
+
owner?: string;
|
|
42
|
+
}
|
|
43
|
+
/** What `POST /sessions/{id}/close` did, in the engine's words. */
|
|
44
|
+
export interface PolykvCloseReport {
|
|
45
|
+
/** The session held a booking, now released (or deferred to its drain). */
|
|
46
|
+
found: boolean;
|
|
47
|
+
/** The session's idle slot KV was dropped (`kv-drop-on-close`). */
|
|
48
|
+
kvDropped: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* What `GET /polykv/pools/{id}/capacity` answers.
|
|
52
|
+
*
|
|
53
|
+
* Transcribed from the engine's own response builder
|
|
54
|
+
* (`server-context.cpp:7047-7125`), not from the design document -- the two
|
|
55
|
+
* disagree, and the builder is the one that runs.
|
|
56
|
+
*
|
|
57
|
+
* **This is not a pollable endpoint on c7.** Every GET folds the settle and
|
|
58
|
+
* bias EWMAs the admission gate learns from, so a dashboard that polls it
|
|
59
|
+
* perturbs the thing it is reporting on. Call it once per admission decision
|
|
60
|
+
* and read live state from `/polykv/pools`, `/props` and `/polykv/tps`, which
|
|
61
|
+
* are served from a published snapshot and are safe to poll while the server
|
|
62
|
+
* is busy. c8 makes the fold explicit and the plain GET read-only.
|
|
63
|
+
*
|
|
64
|
+
* Several fields are deliberately `null` rather than `0` when the number would
|
|
65
|
+
* be a lie -- `vram_free_mib` with no GPU layers, every `swa_*` on a model
|
|
66
|
+
* without a sliding window. Absent must not be read as zero.
|
|
67
|
+
*/
|
|
68
|
+
export interface PolykvCapacity {
|
|
69
|
+
pool_id?: string;
|
|
70
|
+
can_admit: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Why, in the engine's own words. Worth surfacing verbatim: the elastic
|
|
73
|
+
* refusal is kept distinct from `kv headroom exhausted` on purpose, because
|
|
74
|
+
* "raise --max-parallel" and "this context does not fit" need different
|
|
75
|
+
* answers and a merged string cannot carry which one it is.
|
|
76
|
+
*/
|
|
77
|
+
reason?: string;
|
|
78
|
+
/** Whether this read folded the admission learner. c8 and later only. */
|
|
79
|
+
folded?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Which window the `kv_*` and `headroom_*` figures above describe.
|
|
82
|
+
*
|
|
83
|
+
* **The field that stops this endpoint being misread.** Under guarantees a
|
|
84
|
+
* pool owned by a session reports them against the OWNER'S window rather
|
|
85
|
+
* than against the server, so a caller that takes them off the first pool it
|
|
86
|
+
* finds and calls the result "KV headroom" is stating one session's private
|
|
87
|
+
* occupancy as the machine's free capacity -- wrong in the most misleading
|
|
88
|
+
* direction there is, since a full server with one idle session then reads
|
|
89
|
+
* as nearly empty.
|
|
90
|
+
*
|
|
91
|
+
* `session` is that case. `unallocated` is an unowned pool, whose free cells
|
|
92
|
+
* are what admission can still grant and so genuinely are the server's.
|
|
93
|
+
* `cache` is a server without guarantees, where the distinction does not
|
|
94
|
+
* exist. Absent is a build from before the split, which has no per-session
|
|
95
|
+
* windows to confuse these with.
|
|
96
|
+
*/
|
|
97
|
+
pressure_scope?: "session" | "unallocated" | "cache";
|
|
98
|
+
/** The session owning this pool, when one does. */
|
|
99
|
+
owner?: string | null;
|
|
100
|
+
/** Raw `used/window`, 0..1. Never a ramp. */
|
|
101
|
+
pressure?: number;
|
|
102
|
+
/** The denominator `pressure` is taken against. */
|
|
103
|
+
window_cells?: number;
|
|
104
|
+
headroom_sessions?: number;
|
|
105
|
+
kv_headroom_pct?: number;
|
|
106
|
+
kv_cells_free?: number;
|
|
107
|
+
kv_cells_total?: number;
|
|
108
|
+
marginal_tokens_mean?: number;
|
|
109
|
+
expected_tokens?: number;
|
|
110
|
+
mean_active_tps?: number;
|
|
111
|
+
n_active_sessions?: number;
|
|
112
|
+
projected_mean_tps_if_admitted?: number;
|
|
113
|
+
projected_mean_tps_model?: number;
|
|
114
|
+
projected_mean_tps_measured?: number;
|
|
115
|
+
projection_bias_ewma?: number;
|
|
116
|
+
drop_per_admit_ewma?: number;
|
|
117
|
+
/** The projection came from the last settled mean, not from live decode. */
|
|
118
|
+
projected_idle_estimate?: boolean;
|
|
119
|
+
settling?: boolean;
|
|
120
|
+
settle_remaining_ms?: number;
|
|
121
|
+
n_warming?: number;
|
|
122
|
+
n_pool_sessions?: number;
|
|
123
|
+
guaranteed?: boolean;
|
|
124
|
+
/** A continuation of a session the engine already knows, never a new admit. */
|
|
125
|
+
known_session?: boolean;
|
|
126
|
+
n_prefilling?: number;
|
|
127
|
+
prefill_backlog_tokens?: number;
|
|
128
|
+
n_soon_sessions?: number;
|
|
129
|
+
slots_live?: number;
|
|
130
|
+
slots_max?: number;
|
|
131
|
+
slots_free?: number;
|
|
132
|
+
vram_free_mib?: number | null;
|
|
133
|
+
elastic_reason?: string;
|
|
134
|
+
swa_active?: boolean;
|
|
135
|
+
swa_cells_total?: number | null;
|
|
136
|
+
swa_cells_free?: number | null;
|
|
137
|
+
swa_cells_needed?: number | null;
|
|
138
|
+
swa_seq_budget?: number | null;
|
|
139
|
+
swa_window?: number | null;
|
|
140
|
+
swa_capacity_sessions?: number | null;
|
|
141
|
+
kv_streams_total?: number | null;
|
|
142
|
+
kv_streams_free?: number | null;
|
|
143
|
+
kv_cells_per_stream?: number | null;
|
|
144
|
+
/**
|
|
145
|
+
* How close the pool is to exhausting context, measured by the engine.
|
|
146
|
+
*
|
|
147
|
+
* The reason to care: every compaction trigger on the Ollama path is an
|
|
148
|
+
* estimate, and the estimates were wrong in both directions on a single
|
|
149
|
+
* session -- a chars-per-token ratio swinging 3.16 to 5.42, an overhead
|
|
150
|
+
* term reading 53,323 tokens for a 12,700-token payload. This number is
|
|
151
|
+
* measured by the thing that owns the cells.
|
|
152
|
+
*
|
|
153
|
+
* **Superseded by `pressure` where the server offers it.** This one is a
|
|
154
|
+
* 0.70..1.0 ramp over the same denominator, kept for compatibility; a
|
|
155
|
+
* pre-scaled figure stops matching the threshold the user set, which is the
|
|
156
|
+
* number they are looking at. Take the raw ratio and own the policy here.
|
|
157
|
+
*/
|
|
158
|
+
compaction_pressure?: number;
|
|
159
|
+
/** The policy in force, echoed back. */
|
|
160
|
+
admission?: {
|
|
161
|
+
set?: boolean;
|
|
162
|
+
target_tps_per_session?: number;
|
|
163
|
+
mode?: "advisory" | "enforced";
|
|
164
|
+
on_saturation?: "reject" | "warn";
|
|
165
|
+
guarantee_min_sessions?: number;
|
|
166
|
+
settle_tokens?: number;
|
|
167
|
+
settle_max_ms?: number;
|
|
168
|
+
prefill_max_slots?: number;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/** The body of `POST /polykv/pools/{id}/admission`, as the engine reads it. */
|
|
172
|
+
export interface PolykvAdmissionPolicy {
|
|
173
|
+
target_tps_per_session?: number;
|
|
174
|
+
mode?: "advisory" | "enforced";
|
|
175
|
+
on_saturation?: "reject" | "warn";
|
|
176
|
+
guarantee_min_sessions?: number;
|
|
177
|
+
settle_tokens?: number;
|
|
178
|
+
settle_max_ms?: number;
|
|
179
|
+
prefill_max_slots?: number;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The admission policy a profile's PolyKV section asks for, or nothing.
|
|
183
|
+
*
|
|
184
|
+
* A floor with no mode is sent as `enforced`. The engine's own default is
|
|
185
|
+
* `advisory`, which reports and refuses nothing, and the section's floor field
|
|
186
|
+
* promises the opposite: "a new session that would push the projected mean
|
|
187
|
+
* below this is refused rather than admitted". Before this was sent at all,
|
|
188
|
+
* a 15 tok/s floor on both of a user's nodes let 60 agents run at 5 tok/s --
|
|
189
|
+
* the server's own boot floor -- because no pool ever carried the policy.
|
|
190
|
+
*/
|
|
191
|
+
export declare function polykvAdmissionPolicy(settings: {
|
|
192
|
+
targetTpsPerSession?: number;
|
|
193
|
+
mode?: "advisory" | "enforced";
|
|
194
|
+
onSaturation?: "reject" | "warn";
|
|
195
|
+
guaranteeMinSessions?: number;
|
|
196
|
+
settleTokens?: number;
|
|
197
|
+
settleMaxMs?: number;
|
|
198
|
+
prefillMaxSlots?: number;
|
|
199
|
+
} | undefined): PolykvAdmissionPolicy | undefined;
|
|
200
|
+
export interface PolykvClientOptions {
|
|
201
|
+
/** Server root, with or without a trailing `/v1`. */
|
|
202
|
+
baseUrl: string;
|
|
203
|
+
/**
|
|
204
|
+
* The fetch to use. Supply the one that honours an undici dispatcher when
|
|
205
|
+
* there is one: pool creation prefills, and prefill is exactly the thing
|
|
206
|
+
* that outlives undici's default five-minute header timeout.
|
|
207
|
+
*/
|
|
208
|
+
fetch?: typeof fetch;
|
|
209
|
+
headers?: Record<string, string>;
|
|
210
|
+
signal?: AbortSignal;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Raised when the engine refuses a new session for want of capacity.
|
|
214
|
+
*
|
|
215
|
+
* Carries `retryAfterMs` because the server says how long to wait (RFC 9110)
|
|
216
|
+
* and a caller that ignores it is the reason saturation looks like a network
|
|
217
|
+
* fault.
|
|
218
|
+
*/
|
|
219
|
+
export declare class PolykvSaturatedError extends Error {
|
|
220
|
+
readonly retryAfterMs: number | undefined;
|
|
221
|
+
readonly reason?: string | undefined;
|
|
222
|
+
/**
|
|
223
|
+
* The HTTP status line, which is the half that is right on both
|
|
224
|
+
* releases. c7 answers a `429` with a body that says
|
|
225
|
+
* `503`/`unavailable_error`, so a client that classified on the body
|
|
226
|
+
* would call every c7 refusal a failed capacity check.
|
|
227
|
+
*/
|
|
228
|
+
readonly status: number;
|
|
229
|
+
constructor(message: string, retryAfterMs: number | undefined, reason?: string | undefined,
|
|
230
|
+
/**
|
|
231
|
+
* The HTTP status line, which is the half that is right on both
|
|
232
|
+
* releases. c7 answers a `429` with a body that says
|
|
233
|
+
* `503`/`unavailable_error`, so a client that classified on the body
|
|
234
|
+
* would call every c7 refusal a failed capacity check.
|
|
235
|
+
*/
|
|
236
|
+
status?: number);
|
|
237
|
+
/**
|
|
238
|
+
* `--polykv-adm-on-error deny` refusing because the check itself failed.
|
|
239
|
+
*
|
|
240
|
+
* Distinct from pacing on purpose. A `429` means the server knows it is
|
|
241
|
+
* full and has said when to come back, so waiting works. A `503` means the
|
|
242
|
+
* server does not know whether it has room and would rather refuse than
|
|
243
|
+
* guess -- nothing about waiting fixes that, and retrying it on a timer
|
|
244
|
+
* turns one failed check into a loop.
|
|
245
|
+
*/
|
|
246
|
+
get capacityCheckFailed(): boolean;
|
|
247
|
+
}
|
|
248
|
+
/** Strip a trailing `/v1` (and any trailing slash): the control plane sits at the root. */
|
|
249
|
+
export declare function polykvRoot(baseUrl: string): string;
|
|
250
|
+
/**
|
|
251
|
+
* How a pool's prefix is supplied. Exactly one of these, never two.
|
|
252
|
+
*
|
|
253
|
+
* `prompt` and `from_session` are the two that work. `tokens` is kept because
|
|
254
|
+
* the engine still accepts it, but it puts the caller in charge of reproducing
|
|
255
|
+
* the server's tokenization -- BOS, special tokens, and the chat template --
|
|
256
|
+
* and getting any of that wrong yields a pool that shares nothing while
|
|
257
|
+
* reporting success. `prompt` hands that job back to the server, and
|
|
258
|
+
* `from_session` removes the question entirely by never putting tokens on the
|
|
259
|
+
* wire.
|
|
260
|
+
*/
|
|
261
|
+
export interface PolykvPoolOwner {
|
|
262
|
+
/**
|
|
263
|
+
* The session whose window this pool lives in.
|
|
264
|
+
*
|
|
265
|
+
* **What makes a worker free.** A request that attaches to a pool owned by
|
|
266
|
+
* a session is priced as a worker OF that session: it books nothing
|
|
267
|
+
* server-wide and is charged against the owner's free window. An unowned
|
|
268
|
+
* pool is charged to nobody, and every worker attaching to it books a
|
|
269
|
+
* guaranteed window of its own -- measured live on 8240, a 12-worker swarm
|
|
270
|
+
* under a 65,536-token lead took THIRTEEN allocations of 65,536, 82% of the
|
|
271
|
+
* server's cells, for a round that should have cost one.
|
|
272
|
+
*
|
|
273
|
+
* The server defaults it to `from_session`, or to the parent's owner on a
|
|
274
|
+
* fork; it is sent explicitly because the pools that matter most here --
|
|
275
|
+
* the root pool, built from a templated prompt -- have neither.
|
|
276
|
+
*/
|
|
277
|
+
session_id?: string;
|
|
278
|
+
}
|
|
279
|
+
export interface PolykvPrefixSource {
|
|
280
|
+
/** Pre-tokenized ids. The caller owns the tokenization. Prefer the others. */
|
|
281
|
+
tokens?: number[];
|
|
282
|
+
/** Text the server tokenizes itself, the way it tokenizes a completion. */
|
|
283
|
+
prompt?: string;
|
|
284
|
+
/** Snapshot a live session's own token history, zero-copy. */
|
|
285
|
+
from_session?: string;
|
|
286
|
+
/** Snapshot a slot by index. `from_session` is the one a client should use. */
|
|
287
|
+
from_slot?: number;
|
|
288
|
+
}
|
|
289
|
+
export interface PolykvClient {
|
|
290
|
+
/**
|
|
291
|
+
* Create a root pool.
|
|
292
|
+
*
|
|
293
|
+
* `ephemeral` defaults to `true` for `from_session`/`from_slot` pools and
|
|
294
|
+
* `false` for `tokens`/`prompt` ones -- a server-side default worth knowing,
|
|
295
|
+
* because an ephemeral pool is swept once it has been attached, is an
|
|
296
|
+
* unpinned leaf, and goes 60s without a processing attach. Pin anything that
|
|
297
|
+
* has to outlive an idle gap.
|
|
298
|
+
*/
|
|
299
|
+
createPool(body: PolykvPrefixSource & PolykvPoolOwner & {
|
|
300
|
+
pin?: boolean;
|
|
301
|
+
ephemeral?: boolean;
|
|
302
|
+
/** Declared expected prefix length, validated server-side. */
|
|
303
|
+
expect_len?: number;
|
|
304
|
+
/**
|
|
305
|
+
* Find-or-create (`polykv_shared_root_v1`): an unowned root with
|
|
306
|
+
* exactly these tokens and the same `ephemeral` is returned with
|
|
307
|
+
* `reused: true`. A 400 with an owner or a snapshot source.
|
|
308
|
+
*/
|
|
309
|
+
shared?: boolean;
|
|
310
|
+
}): Promise<PolykvPool>;
|
|
311
|
+
/**
|
|
312
|
+
* Branch a pool at `branch_pos`.
|
|
313
|
+
*
|
|
314
|
+
* `branch_pos === parent.prefix_len` extends it; anything less is a
|
|
315
|
+
* copy-on-write branch, and it defaults to the parent's length.
|
|
316
|
+
*
|
|
317
|
+
* **The prefix given here is the child's FULL path `[0, L)`, never the
|
|
318
|
+
* suffix being added.** The engine validates it token-exact against the
|
|
319
|
+
* parent over `[0, branch_pos)` with `std::equal` and answers `400` on a
|
|
320
|
+
* mismatch -- so a caller that sends only the new tail has its every fork
|
|
321
|
+
* rejected, which looks from here like the feature being unavailable.
|
|
322
|
+
*/
|
|
323
|
+
forkPool(poolId: string, body: PolykvPrefixSource & PolykvPoolOwner & {
|
|
324
|
+
branch_pos?: number;
|
|
325
|
+
pin?: boolean;
|
|
326
|
+
ephemeral?: boolean;
|
|
327
|
+
}): Promise<PolykvPool>;
|
|
328
|
+
/**
|
|
329
|
+
* Give a session's guaranteed window back, before its idle TTL does.
|
|
330
|
+
*
|
|
331
|
+
* Called on conversation end, always. Skipping it holds the whole booked
|
|
332
|
+
* allocation for the TTL -- five minutes on the build this was written
|
|
333
|
+
* against -- so a user who ends one 256k conversation and opens another
|
|
334
|
+
* waits out their own last session. The TTL is the crash net, not the
|
|
335
|
+
* mechanism.
|
|
336
|
+
*
|
|
337
|
+
* Answers whether anything was actually released. The server replies
|
|
338
|
+
* `200 {"found": false}` for a session it never held, so the status line
|
|
339
|
+
* says only that the request arrived; `found` says whether it did anything.
|
|
340
|
+
* A `false` for a session we believed we held means our id and the
|
|
341
|
+
* server's have diverged, which is worth knowing and not worth guessing at.
|
|
342
|
+
*/
|
|
343
|
+
closeSession(sessionId: string): Promise<boolean>;
|
|
344
|
+
/**
|
|
345
|
+
* {@link closeSession}, with what the engine dropped. A session with slot
|
|
346
|
+
* affinity and no booking -- a pooled worker, a compaction critic -- answers
|
|
347
|
+
* `found: false, kv_dropped: true`: nothing was booked, and its resident
|
|
348
|
+
* cells are gone all the same.
|
|
349
|
+
*
|
|
350
|
+
* A close also ends the session's standing as a running one: the engine's
|
|
351
|
+
* admission gate lets a running session's next turn through as a
|
|
352
|
+
* continuation (bug-3670), and a closed session's next turn is gated as a
|
|
353
|
+
* new arrival. `keepRunning` is for a close whose session goes on -- a
|
|
354
|
+
* compaction giving back the booking its writer took -- and asks the
|
|
355
|
+
* engine to keep that standing. An engine without it ignores the field.
|
|
356
|
+
*/
|
|
357
|
+
closeSessionReport(sessionId: string, options?: {
|
|
358
|
+
keepRunning?: boolean;
|
|
359
|
+
}): Promise<PolykvCloseReport>;
|
|
360
|
+
/**
|
|
361
|
+
* Drop the resident KV of the slot a session last ran in, keeping its
|
|
362
|
+
* booking (`POST /slots/{id}?action=erase`). `undefined` when no slot names
|
|
363
|
+
* the session; the number the engine says it erased otherwise.
|
|
364
|
+
*/
|
|
365
|
+
eraseSessionSlot(sessionId: string): Promise<number | undefined>;
|
|
366
|
+
/**
|
|
367
|
+
* The ids of the pools the server holds now.
|
|
368
|
+
*
|
|
369
|
+
* How a client learns a pool it named is gone: the engine releases every
|
|
370
|
+
* pool a session owns when that session's window lapses, and a request
|
|
371
|
+
* naming a released pool is not refused -- it is a server-side warning and
|
|
372
|
+
* a full reprocess, invisible from here.
|
|
373
|
+
*/
|
|
374
|
+
listPoolIds(): Promise<Set<string>>;
|
|
375
|
+
pin(poolId: string): Promise<void>;
|
|
376
|
+
unpin(poolId: string): Promise<void>;
|
|
377
|
+
/** Set a pool's admission policy (`POST /polykv/pools/{id}/admission`). */
|
|
378
|
+
setAdmission(poolId: string, policy: PolykvAdmissionPolicy): Promise<void>;
|
|
379
|
+
releasePool(poolId: string): Promise<void>;
|
|
380
|
+
/**
|
|
381
|
+
* Ask what the gate would say, optionally folding the learner.
|
|
382
|
+
*
|
|
383
|
+
* `fold` is the c8 half of a c7 hazard. On c7 every GET folds the settle
|
|
384
|
+
* and bias EWMAs whether you wanted it to or not, which is why the status
|
|
385
|
+
* panel may not poll this endpoint there. `capacity_readonly_v1` makes the
|
|
386
|
+
* plain GET read-only and moves the fold behind `?fold=1`, so an admission
|
|
387
|
+
* decision asks for it and a panel read does not.
|
|
388
|
+
*
|
|
389
|
+
* Asking for it on a c7 server is harmless: it folds either way, and the
|
|
390
|
+
* unknown query parameter is ignored. `folded` echoing back is how a caller
|
|
391
|
+
* learns which of the two it is talking to.
|
|
392
|
+
*/
|
|
393
|
+
capacity(poolId: string, query?: {
|
|
394
|
+
expected_tokens?: number;
|
|
395
|
+
fold?: boolean;
|
|
396
|
+
}): Promise<PolykvCapacity>;
|
|
397
|
+
tokenize(content: string): Promise<number[]>;
|
|
398
|
+
/**
|
|
399
|
+
* Render messages through the server's own chat template.
|
|
400
|
+
*
|
|
401
|
+
* The engine prefills the *templated* token stream, so a prefix assembled
|
|
402
|
+
* here from the raw system prompt and a JSON dump of the tool schemas is a
|
|
403
|
+
* different sequence and matches nothing -- the pool reports success and
|
|
404
|
+
* shares zero tokens. Only the server knows which template it loaded
|
|
405
|
+
* (`/props.chat_template`) and how it renders tools, so it renders the
|
|
406
|
+
* prefix and we pin what comes back.
|
|
407
|
+
*/
|
|
408
|
+
applyTemplate(body: {
|
|
409
|
+
messages: readonly unknown[];
|
|
410
|
+
tools?: readonly unknown[];
|
|
411
|
+
/**
|
|
412
|
+
* The rest of the chat request, as it will be sent: see
|
|
413
|
+
* `templateFieldsOf`. The server parses this body with the same code
|
|
414
|
+
* it parses a chat request with, so fields such as
|
|
415
|
+
* `reasoning_budget_tokens` change the rendering here exactly as they
|
|
416
|
+
* do there.
|
|
417
|
+
*/
|
|
418
|
+
fields?: Readonly<Record<string, unknown>>;
|
|
419
|
+
}): Promise<string>;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Which pool each live conversation is currently attached to.
|
|
423
|
+
*
|
|
424
|
+
* A registry rather than a field on the provider config because the two move at
|
|
425
|
+
* different rates: the config is resolved once for a session, and the pool it
|
|
426
|
+
* points at is replaced every time a compaction re-roots the conversation.
|
|
427
|
+
* Compaction is a prompt rewrite -- the compacted text cannot match the old
|
|
428
|
+
* cells -- so the correct primitive is a fork with a new id, and a session that
|
|
429
|
+
* kept reading its pool from a frozen config would keep attaching to the pool
|
|
430
|
+
* that was just released.
|
|
431
|
+
*
|
|
432
|
+
* Keyed by the session id the host already has. Entries are cleared when the
|
|
433
|
+
* session ends; a leaked entry costs a lookup, a leaked *pin* costs the cells,
|
|
434
|
+
* which is why release is a separate and deliberate step.
|
|
435
|
+
*/
|
|
436
|
+
export interface PolykvSessionState {
|
|
437
|
+
poolId: string;
|
|
438
|
+
/**
|
|
439
|
+
* Where this pool's own range begins -- the length of the prefix it shares
|
|
440
|
+
* with its ancestors, and the `branch_pos` a re-root forks at.
|
|
441
|
+
*/
|
|
442
|
+
prefixTokens: number;
|
|
443
|
+
/**
|
|
444
|
+
* `"lead"`: the pool is the conversation's place in the server-wide lead
|
|
445
|
+
* tree (`polykv-lead.ts`) -- a shared root, or the conversation's own
|
|
446
|
+
* sub-pool of it. Not this session's to re-root or release by id: the root
|
|
447
|
+
* is every session's, and the lead tree releases what it made.
|
|
448
|
+
*
|
|
449
|
+
* `"borrowed"`: someone else's pool this session only attaches to -- a
|
|
450
|
+
* swarm worker on the lead's snapshot or borrowed root. Its to read, never
|
|
451
|
+
* its to re-root, unpin or release: that pool is the lead's, and every
|
|
452
|
+
* other worker of the round is attached to it.
|
|
453
|
+
*/
|
|
454
|
+
layout?: "lead" | "borrowed";
|
|
455
|
+
/**
|
|
456
|
+
* Send this session's requests as continuations of the pool
|
|
457
|
+
* (`pool_continue_v1`): only the last `continueTail` messages go on the
|
|
458
|
+
* wire, as the turns that follow the pool's stored stream. Set only on a
|
|
459
|
+
* pool that ends where an assistant reply ends -- a `from_session` pool of
|
|
460
|
+
* a finished turn -- and only by a caller that knows the engine has it.
|
|
461
|
+
*/
|
|
462
|
+
continueTail?: number;
|
|
463
|
+
/**
|
|
464
|
+
* The `templateSignature` of the request fields a session's own root was
|
|
465
|
+
* rendered with. A root rendered without the request's fields -- Gemma-4's
|
|
466
|
+
* `<|think|>` follows `reasoning_budget_tokens` -- diverges at token 4;
|
|
467
|
+
* the vendor renders it again with the fields of the first request that
|
|
468
|
+
* attaches it.
|
|
469
|
+
*/
|
|
470
|
+
templateSignature?: string;
|
|
471
|
+
}
|
|
472
|
+
export declare function setPolykvSession(sessionId: string, state: PolykvSessionState): void;
|
|
473
|
+
export declare function getPolykvSession(sessionId: string | undefined): PolykvSessionState | undefined;
|
|
474
|
+
export declare function clearPolykvSession(sessionId: string): void;
|
|
475
|
+
/**
|
|
476
|
+
* The window a session was granted, and what it asked for when it was.
|
|
477
|
+
*
|
|
478
|
+
* `granted` and `asked` are the numbers on the wire: `num_ctx` and the
|
|
479
|
+
* `X-Context-Window` that answered it. Under `polykv_private_window_v1` both
|
|
480
|
+
* are the PRIVATE budget, and the shared prefix the conversation attached to
|
|
481
|
+
* rides above them -- `sharedTokens` -- so the window the conversation can
|
|
482
|
+
* actually fill is `granted + sharedTokens` ({@link polykvEffectiveWindow}).
|
|
483
|
+
*/
|
|
484
|
+
export interface PolykvWindowGrant {
|
|
485
|
+
granted: number;
|
|
486
|
+
/**
|
|
487
|
+
* The window the conversation asked for when it was opened.
|
|
488
|
+
*
|
|
489
|
+
* Kept from the FIRST grant: every later admission of the session asks for
|
|
490
|
+
* exactly the granted window, so the ask on the wire stops saying what the
|
|
491
|
+
* user configured the moment the resume rule takes over.
|
|
492
|
+
*/
|
|
493
|
+
asked?: number;
|
|
494
|
+
sharedTokens?: number;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Be told whenever a session's grant is first learned or changes.
|
|
498
|
+
*
|
|
499
|
+
* The host persists it from here, because the grant arrives on a response --
|
|
500
|
+
* long after the session record was written at start.
|
|
501
|
+
*/
|
|
502
|
+
export declare function onPolykvWindowGrant(listener: (sessionId: string, grant: PolykvWindowGrant) => void): () => void;
|
|
503
|
+
/**
|
|
504
|
+
* Record what `X-Context-Window` reported for this session.
|
|
505
|
+
*
|
|
506
|
+
* `detail` describes the ask that produced it. It is taken only when the
|
|
507
|
+
* session has no grant yet -- see {@link PolykvWindowGrant.asked} -- so a
|
|
508
|
+
* hydrated record or a resumed ask can never overwrite the original ask with
|
|
509
|
+
* the granted one.
|
|
510
|
+
*/
|
|
511
|
+
export declare function recordPolykvGrantedWindow(sessionId: string, window: number, detail?: {
|
|
512
|
+
asked?: number;
|
|
513
|
+
sharedTokens?: number;
|
|
514
|
+
}): void;
|
|
515
|
+
/** The window this conversation already holds, if we have seen one granted. */
|
|
516
|
+
export declare function getPolykvGrantedWindow(sessionId: string | undefined): number | undefined;
|
|
517
|
+
/** The whole grant record, for persisting and for the context bar. */
|
|
518
|
+
export declare function getPolykvWindowGrant(sessionId: string | undefined): PolykvWindowGrant | undefined;
|
|
519
|
+
/**
|
|
520
|
+
* The window the conversation can actually fill: the grant plus the shared
|
|
521
|
+
* prefix riding above it. `undefined` when no grant is known.
|
|
522
|
+
*/
|
|
523
|
+
export declare function polykvEffectiveWindow(grant: PolykvWindowGrant | undefined): number | undefined;
|
|
524
|
+
/**
|
|
525
|
+
* Read a grant back from stored metadata, rejecting anything malformed.
|
|
526
|
+
*
|
|
527
|
+
* The shape is {@link PolykvWindowGrant}; a record that does not carry a
|
|
528
|
+
* positive `granted` is not a grant, and hydrating from it would pin a resumed
|
|
529
|
+
* conversation to a window nobody was ever given.
|
|
530
|
+
*/
|
|
531
|
+
export declare function readPolykvWindowGrant(value: unknown): PolykvWindowGrant | undefined;
|
|
532
|
+
/**
|
|
533
|
+
* Forget a session's grant. A test seam and an explicit reset -- NOT called
|
|
534
|
+
* when a session closes, see {@link POLYKV_GRANTED_WINDOWS}.
|
|
535
|
+
*/
|
|
536
|
+
export declare function clearPolykvGrantedWindow(sessionId: string): void;
|
|
537
|
+
/**
|
|
538
|
+
* What the LAST admitted response said about the window, per session.
|
|
539
|
+
*
|
|
540
|
+
* Separate from the grant on purpose. The grant is the booking, remembered for
|
|
541
|
+
* the resume rule; this is the per-response reading, and a response without
|
|
542
|
+
* `X-Context-Window` is not guaranteed -- an overcommit, or a server that is
|
|
543
|
+
* not enforcing -- so it reads as UNKNOWN (`granted: undefined`), never as
|
|
544
|
+
* "the same as last time".
|
|
545
|
+
*/
|
|
546
|
+
export interface PolykvWindowObservation {
|
|
547
|
+
granted?: number;
|
|
548
|
+
asked?: number;
|
|
549
|
+
sharedTokens?: number;
|
|
550
|
+
}
|
|
551
|
+
export declare function recordPolykvWindowObservation(sessionId: string, observation: PolykvWindowObservation): void;
|
|
552
|
+
export declare function getPolykvWindowObservation(sessionId: string | undefined): PolykvWindowObservation | undefined;
|
|
553
|
+
export declare function resetPolykvSessions(): void;
|
|
554
|
+
/**
|
|
555
|
+
* What one `GET /props` tells us about a server, cached per root.
|
|
556
|
+
*
|
|
557
|
+
* Every branch in this provider keys off one of these, so they are read
|
|
558
|
+
* together, once, rather than probed separately at each call site.
|
|
559
|
+
*/
|
|
560
|
+
export interface OpencotiProps {
|
|
561
|
+
/**
|
|
562
|
+
* The release, parsed from `build_info` (`opencoti-0.10.5-c7-...`).
|
|
563
|
+
*
|
|
564
|
+
* It decides which signals can be trusted, and the differences are not
|
|
565
|
+
* cosmetic: on c7 a `429` carries a body that says `503`, `GET /capacity`
|
|
566
|
+
* mutates the admission learner, the completion response does not say
|
|
567
|
+
* whether the pool attached, and `/polykv/tps` reports `-1` as the pool key.
|
|
568
|
+
* A client that assumes the c8 shapes misreads all four in silence.
|
|
569
|
+
*
|
|
570
|
+
* `undefined` when the server did not say -- which is every non-opencoti
|
|
571
|
+
* server, and is why nothing may branch on it being absent except to fall
|
|
572
|
+
* back to the conservative path.
|
|
573
|
+
*/
|
|
574
|
+
release?: string;
|
|
575
|
+
/**
|
|
576
|
+
* Whether `/props` answered at all.
|
|
577
|
+
*
|
|
578
|
+
* The three other fields cannot carry this: a plain llama.cpp server and a
|
|
579
|
+
* server that is not there both report `poolsEnabled: false, elastic:
|
|
580
|
+
* false`, and the right thing to say about them is opposite -- one has a
|
|
581
|
+
* fixed `--parallel` count, the other was never asked. A panel that
|
|
582
|
+
* collapses them tells a user their server has neither controller on
|
|
583
|
+
* because the question failed, which is a guess wearing a fact's clothes.
|
|
584
|
+
*/
|
|
585
|
+
reachable: boolean;
|
|
586
|
+
/** Pools are compiled in AND the server was launched with `--polykv-max-pools`. */
|
|
587
|
+
poolsEnabled: boolean;
|
|
588
|
+
/** The elastic slot controller is armed, so the slot count is not fixed. */
|
|
589
|
+
elastic: boolean;
|
|
590
|
+
/** `slots_live` at the time of the probe; the ceiling is `slotsMax`. */
|
|
591
|
+
slotsLive?: number;
|
|
592
|
+
slotsMax?: number;
|
|
593
|
+
/** The chat template the server will apply. The pool prefix must match it. */
|
|
594
|
+
chatTemplate?: string;
|
|
595
|
+
/** Advertised capabilities, e.g. `slots_nonblocking_v1`. */
|
|
596
|
+
features: readonly string[];
|
|
597
|
+
/**
|
|
598
|
+
* The temperature the server samples at when a request names none:
|
|
599
|
+
* `default_generation_settings.params.temperature` (or, on an older build,
|
|
600
|
+
* `default_generation_settings.temperature`). Read, never sent -- it is the
|
|
601
|
+
* model's own sampler, which a spawn's `temperature: "random"` jitters.
|
|
602
|
+
*/
|
|
603
|
+
defaultTemperature?: number;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* The advertised capabilities this client branches on.
|
|
607
|
+
*
|
|
608
|
+
* **These, and never a parsed release.** The published binary stamps no `c<N>`
|
|
609
|
+
* anywhere in `/props`, and the build that does stamp one carries `-c7-` on the
|
|
610
|
+
* artifact that ships the c8 behaviours -- so a version gate reads the wrong
|
|
611
|
+
* answer on both the server it was written for and the one it was written
|
|
612
|
+
* against. A flag is self-identifying; a version string that is not sent is
|
|
613
|
+
* not.
|
|
614
|
+
*
|
|
615
|
+
* Listed here rather than inlined at each branch so that the set is readable as
|
|
616
|
+
* a set, and so that a server advertising something we do not consume yet is
|
|
617
|
+
* visible as a gap rather than as nothing.
|
|
618
|
+
*/
|
|
619
|
+
export declare const OPENCOTI_FEATURES: {
|
|
620
|
+
/** `GET /capacity` no longer folds the learner; `?fold=1` asks for it. */
|
|
621
|
+
readonly capacityReadonly: "capacity_readonly_v1";
|
|
622
|
+
/** The completion response carries `opencoti {pool_id, n_pool_shared}`. */
|
|
623
|
+
readonly attachInResponse: "attach_in_response_v1";
|
|
624
|
+
/** `X-PolyKV-Settle-Waived` tells a timer waiver from a measured admit. */
|
|
625
|
+
readonly settleWaivedHeader: "settle_waived_header_v1";
|
|
626
|
+
/** `/polykv/tps` reports the real pool binding, plus a stable `alloc_key`. */
|
|
627
|
+
readonly tpsRealPoolId: "tps_real_pool_id_v1";
|
|
628
|
+
/** An elastic slot is a guaranteed allocation booked by `num_ctx`. */
|
|
629
|
+
readonly guaranteedAlloc: "elastic_guaranteed_alloc_v1";
|
|
630
|
+
/** `GET /kv`: the pollable allocation snapshot. */
|
|
631
|
+
readonly kvStatus: "kv_status_v1";
|
|
632
|
+
/** Pools are sub-pools of their owning session, max 8 per session. */
|
|
633
|
+
readonly subpools: "polykv_subpools_v1";
|
|
634
|
+
/** `POST /sessions/{id}/close` releases a window before its TTL. */
|
|
635
|
+
readonly sessionClose: "session_close_v1";
|
|
636
|
+
/** `num_ctx_min`: the floor is negotiated server-side, in one admission. */
|
|
637
|
+
readonly ctxMinNegotiation: "ctx_min_negotiation_v1";
|
|
638
|
+
/** `/capacity` carries `pressure`, `window_cells`, `owner`, `pressure_scope`. */
|
|
639
|
+
readonly sessionPressure: "session_pressure_v1";
|
|
640
|
+
/** The tps floor is settable at runtime via `POST /elastic`. */
|
|
641
|
+
readonly elasticRw: "elastic_rw_v1";
|
|
642
|
+
/** `hold_ticks` -- how long a grow condition must hold -- is settable too. */
|
|
643
|
+
readonly elasticHoldRw: "elastic_hold_rw_v1";
|
|
644
|
+
/**
|
|
645
|
+
* A slot whose own cache covers a named pool's whole prefix is rebased onto
|
|
646
|
+
* the pool, so a continuation turn keeps its share (`n_pool_shared = P`).
|
|
647
|
+
*/
|
|
648
|
+
readonly rebase: "polykv_rebase_v1";
|
|
649
|
+
/** Under the tps floor a busy top slot drains instead of taking new work. */
|
|
650
|
+
readonly elasticDrain: "elastic_drain_v1";
|
|
651
|
+
/**
|
|
652
|
+
* `num_ctx` is the PRIVATE budget: an attached pool's shared prefix rides
|
|
653
|
+
* above it, capped at the per-session maximum.
|
|
654
|
+
*/
|
|
655
|
+
readonly privateWindow: "polykv_private_window_v1";
|
|
656
|
+
/** `POST /polykv/pools {shared: true}` is find-or-create for an unowned root. */
|
|
657
|
+
readonly sharedRoot: "polykv_shared_root_v1";
|
|
658
|
+
/** `POST /sessions/close {session_id}`: the close for ids the path cannot carry. */
|
|
659
|
+
readonly sessionCloseBody: "session_close_body_v1";
|
|
660
|
+
/** The response's `opencoti` block carries `pool_match` and `pool_len`. */
|
|
661
|
+
readonly poolMatchInResponse: "pool_match_in_response_v1";
|
|
662
|
+
/**
|
|
663
|
+
* `stream_options.keepalive`: the SSE stream opens at once and every silent
|
|
664
|
+
* period carries a `: keepalive <phase>` comment (`opencoti-liveness.ts`).
|
|
665
|
+
*/
|
|
666
|
+
readonly streamKeepalive: "stream_keepalive_v1";
|
|
667
|
+
/** SO_KEEPALIVE on the server's sockets. Server-side only: nothing to send. */
|
|
668
|
+
readonly tcpKeepalive: "tcp_keepalive_v1";
|
|
669
|
+
/** `boot_id` / `started_at` on `/health` and `/props`, `X-OpenCoti-Boot-Id` on completions. */
|
|
670
|
+
readonly bootId: "boot_id_v1";
|
|
671
|
+
/** `opencoti.pool_unknown`: the named pool is not in this process. */
|
|
672
|
+
readonly poolUnknownInResponse: "pool_unknown_in_response_v1";
|
|
673
|
+
/**
|
|
674
|
+
* `GET /kv` carries `pressure` -- the server's refusals over the last
|
|
675
|
+
* `window_s` -- and every admission 429 carries it as `error.pressure`.
|
|
676
|
+
*/
|
|
677
|
+
readonly kvPressure: "kv_pressure_v1";
|
|
678
|
+
/** `POST /kv/sessions/{id}/resize {num_ctx}`: a live booking can shrink and grow. */
|
|
679
|
+
readonly kvResize: "kv_resize_v1";
|
|
680
|
+
/**
|
|
681
|
+
* `{num_ctx, deferred: true}` on the resize route: a busy session's resize
|
|
682
|
+
* is queued (202) and applied at its idle moment; the `/kv` row carries
|
|
683
|
+
* `resize_pending` (b110, mail #306).
|
|
684
|
+
*/
|
|
685
|
+
readonly kvResizeDeferred: "kv_resize_deferred_v1";
|
|
686
|
+
/**
|
|
687
|
+
* A busy session's resize applies while its requests run, when the new
|
|
688
|
+
* window still covers what is used and what the running requests were
|
|
689
|
+
* admitted with (patch 0422, b157, mail #460). Also learned per server
|
|
690
|
+
* from a resize answer that says `live` -- see `opencotiResizesLive`.
|
|
691
|
+
*/
|
|
692
|
+
readonly kvResizeLive: "kv_resize_live_v1";
|
|
693
|
+
/**
|
|
694
|
+
* What the engine holds that nobody is charged for (b115, patch 0399,
|
|
695
|
+
* mail #316): `idle_resident` on `/kv.pressure` and on the resize result
|
|
696
|
+
* -- cells physically held by idle slots, a released worker's "ghost" --
|
|
697
|
+
* `error_kind: "evicted_kv_full"` on the partial eviction, and
|
|
698
|
+
* `resize_pending_refused_at` / `_s` on a `/kv` row whose queued resize
|
|
699
|
+
* was refused at its idle moment.
|
|
700
|
+
*/
|
|
701
|
+
readonly kvObservable: "kv_observable_v1";
|
|
702
|
+
/**
|
|
703
|
+
* `unowned: true` on pool create and fork (b124, patch 0406, mail #342): a
|
|
704
|
+
* pool charged to nobody even when built `from_session` of a session that
|
|
705
|
+
* holds a booking; every attacher books its own window. A 400 together
|
|
706
|
+
* with `session_id`.
|
|
707
|
+
*/
|
|
708
|
+
readonly poolUnowned: "pool_unowned_v1";
|
|
709
|
+
/**
|
|
710
|
+
* `continue_pool: true` + `pool_id` on a chat request (b124, patch 0406):
|
|
711
|
+
* the prompt is the pool's stored tokens followed by `messages` rendered as
|
|
712
|
+
* a continuation, so `cache_n == len(pool)` by construction. `messages` are
|
|
713
|
+
* only the new turns, and the pool must end where an assistant reply ends.
|
|
714
|
+
*/
|
|
715
|
+
readonly poolContinue: "pool_continue_v1";
|
|
716
|
+
};
|
|
717
|
+
export type OpencotiFeature = (typeof OPENCOTI_FEATURES)[keyof typeof OPENCOTI_FEATURES];
|
|
718
|
+
/** Whether a server advertised a capability. Absent always means "no". */
|
|
719
|
+
export declare function hasOpencotiFeature(features: readonly string[] | undefined, feature: OpencotiFeature): boolean;
|
|
720
|
+
/**
|
|
721
|
+
* The server's default temperature, from a `/props` read that has already
|
|
722
|
+
* answered -- never a fetch. `undefined` when it was not read yet, did not
|
|
723
|
+
* answer, or states none.
|
|
724
|
+
*/
|
|
725
|
+
export declare function readOpencotiDefaultTemperature(baseUrl: string | undefined): number | undefined;
|
|
726
|
+
/**
|
|
727
|
+
* Read a server's opencoti capabilities, once.
|
|
728
|
+
*
|
|
729
|
+
* `/props` rather than `/polykv/pools`: a server booted without
|
|
730
|
+
* `--polykv-max-pools` -- the default -- errors on every `/polykv/*` route, so
|
|
731
|
+
* probing there cannot tell "pools are off" from "the server is not there", and
|
|
732
|
+
* both were being reported as the latter. `/props` answers on every build, and
|
|
733
|
+
* carries the elastic state and the chat template in the same response.
|
|
734
|
+
*
|
|
735
|
+
* A server that cannot be reached, or that has no opencoti block, answers
|
|
736
|
+
* "not opencoti": the fixed slot count is the safe reading when the question
|
|
737
|
+
* cannot be asked.
|
|
738
|
+
*/
|
|
739
|
+
export declare function probeOpencotiProps(baseUrl: string | undefined, fetchImpl?: typeof fetch): Promise<OpencotiProps>;
|
|
740
|
+
/**
|
|
741
|
+
* Whether this server has PolyKV turned on.
|
|
742
|
+
*
|
|
743
|
+
* It matters because PolyKV changes what a slot *is*. Without it a server has
|
|
744
|
+
* `--parallel N` slots and an N+1st request waits; with it agents attach to a
|
|
745
|
+
* pool and share a slot, and whether one more may start is decided by the
|
|
746
|
+
* engine's admission control against real KV headroom rather than by counting.
|
|
747
|
+
* Capping agents at the slot count in that case would refuse work the server
|
|
748
|
+
* would have taken.
|
|
749
|
+
*/
|
|
750
|
+
export declare function probePolykvEnabled(baseUrl: string | undefined, fetchImpl?: typeof fetch): Promise<boolean>;
|
|
751
|
+
/** Test seam, and the way a changed server launch is picked up. */
|
|
752
|
+
export declare function resetPolykvAvailability(): void;
|
|
753
|
+
export declare function createPolykvClient(options: PolykvClientOptions): PolykvClient;
|
|
754
|
+
/** One pool in the tree, as the status panel shows it. */
|
|
755
|
+
export interface OpencotiStatusPool {
|
|
756
|
+
poolId: string;
|
|
757
|
+
/** The pool this one branches from, absent for a root. */
|
|
758
|
+
parent?: string;
|
|
759
|
+
/** Where the child's own tokens start; everything before is shared. */
|
|
760
|
+
branchPos?: number;
|
|
761
|
+
prefixLen?: number;
|
|
762
|
+
pinned: boolean;
|
|
763
|
+
ephemeral: boolean;
|
|
764
|
+
/**
|
|
765
|
+
* Pinned, childless, and referenced by no slot's current or last task.
|
|
766
|
+
*
|
|
767
|
+
* Typically left behind by a compaction re-root that did not unpin. It
|
|
768
|
+
* blocks reclaim forever, which is the leak this client's release and unpin
|
|
769
|
+
* fixes exist to stop, so it is named rather than counted.
|
|
770
|
+
*/
|
|
771
|
+
orphanedPin: boolean;
|
|
772
|
+
/** The session a `from_session` snapshot was taken from. */
|
|
773
|
+
sourceSession?: string;
|
|
774
|
+
children: number;
|
|
775
|
+
/** The floor this pool's admission policy holds new sessions to, when set. */
|
|
776
|
+
admissionFloor?: number;
|
|
777
|
+
/** `enforced` refuses below the floor; `advisory` only reports. */
|
|
778
|
+
admissionMode?: string;
|
|
779
|
+
}
|
|
780
|
+
/** One live session, keyed the way the server allows. */
|
|
781
|
+
export interface OpencotiStatusSession {
|
|
782
|
+
sessionId: string;
|
|
783
|
+
/**
|
|
784
|
+
* The pool this session is bound to.
|
|
785
|
+
*
|
|
786
|
+
* Present only under `tps_real_pool_id_v1`. On c7 the field is on the wire
|
|
787
|
+
* but reports `-1` for every registry pool -- it is the legacy donor-slot
|
|
788
|
+
* key -- so it identifies nothing and grouping by it collapses every
|
|
789
|
+
* session into one. Absent is the honest reading there.
|
|
790
|
+
*/
|
|
791
|
+
poolId?: string;
|
|
792
|
+
/**
|
|
793
|
+
* The allocation this session holds, stable across a slot move.
|
|
794
|
+
*
|
|
795
|
+
* Elastic growth can move a session to a different slot, so a slot index is
|
|
796
|
+
* not an identity over time and this is.
|
|
797
|
+
*/
|
|
798
|
+
allocKey?: string;
|
|
799
|
+
/**
|
|
800
|
+
* The slot's throughput EWMA, absent while it is warming.
|
|
801
|
+
*
|
|
802
|
+
* A processing slot reporting `0` has not warmed its EWMA yet; it is not a
|
|
803
|
+
* slot producing nothing, and averaging its zero is what dragged the
|
|
804
|
+
* admission mean down after every spawn.
|
|
805
|
+
*/
|
|
806
|
+
tps?: number;
|
|
807
|
+
active: boolean;
|
|
808
|
+
ctxUsed?: number;
|
|
809
|
+
ctxTotal?: number;
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Everything the settings panel shows about a live server.
|
|
813
|
+
*
|
|
814
|
+
* Read from `/props`, `/polykv/pools` and `/polykv/tps`, which are served from
|
|
815
|
+
* a published snapshot and are safe to read while the server is busy -- and,
|
|
816
|
+
* **only when the server advertises `capacity_readonly_v1`**, from `/capacity`
|
|
817
|
+
* as well.
|
|
818
|
+
*
|
|
819
|
+
* That last condition is the whole of it. On the published c7 engine every GET
|
|
820
|
+
* of `/capacity` folds the settle and bias EWMAs, so a panel that refreshed
|
|
821
|
+
* would corrupt the admission projection it was drawing; `kv_headroom_pct` and
|
|
822
|
+
* the SWA arm live only there and were therefore simply absent. The flag says
|
|
823
|
+
* the plain GET is read-only and the fold has moved behind `?fold=1`, which is
|
|
824
|
+
* what makes the fourth read safe. Without it we do not ask, and the fields
|
|
825
|
+
* stay absent rather than being guessed at from somewhere else.
|
|
826
|
+
*/
|
|
827
|
+
export interface OpencotiAllocation {
|
|
828
|
+
sessionId: string;
|
|
829
|
+
/** The window this session booked, in cells. */
|
|
830
|
+
window: number;
|
|
831
|
+
used: number;
|
|
832
|
+
free: number;
|
|
833
|
+
/**
|
|
834
|
+
* Raw `used/window`, 0..1, never a ramp.
|
|
835
|
+
*
|
|
836
|
+
* The compaction trigger. It is deliberately taken raw from the server and
|
|
837
|
+
* compared against a threshold we own, because the number the user set is
|
|
838
|
+
* the number they should see: a server-side ramp would make the figure in
|
|
839
|
+
* our UI stop matching the one in theirs.
|
|
840
|
+
*/
|
|
841
|
+
pressure: number;
|
|
842
|
+
/** Sub-pools this session holds, of `poolsMaxPerSlot`. */
|
|
843
|
+
pools: number;
|
|
844
|
+
/**
|
|
845
|
+
* A resize queued for this session's idle moment (`kv_resize_deferred_v1`):
|
|
846
|
+
* the window it will be resized to.
|
|
847
|
+
*/
|
|
848
|
+
resizePending?: number;
|
|
849
|
+
/**
|
|
850
|
+
* Why a queued resize has not applied at an idle moment -- the engine's
|
|
851
|
+
* resize refusal kind, e.g. `used_exceeds_window` for a shrink below what
|
|
852
|
+
* the session holds. It stays queued.
|
|
853
|
+
*/
|
|
854
|
+
resizePendingReason?: string;
|
|
855
|
+
/**
|
|
856
|
+
* When the queued resize was last refused at an idle moment, epoch seconds
|
|
857
|
+
* on the server's clock (`kv_observable_v1`). Set only while it stays
|
|
858
|
+
* queued: the engine will try the same target again at the next idle
|
|
859
|
+
* moment, and a client that re-sends it adds nothing.
|
|
860
|
+
*/
|
|
861
|
+
resizePendingRefusedAt?: number;
|
|
862
|
+
/** Seconds since that refusal, when the row was written. */
|
|
863
|
+
resizePendingRefusedS?: number;
|
|
864
|
+
}
|
|
865
|
+
export interface OpencotiStatus {
|
|
866
|
+
/** Whether `/props` answered at all. */
|
|
867
|
+
reachable: boolean;
|
|
868
|
+
release?: string;
|
|
869
|
+
/**
|
|
870
|
+
* KV headroom, and the SWA arm beside it.
|
|
871
|
+
*
|
|
872
|
+
* From `GET /kv` where the server offers it, which needs no pool to address
|
|
873
|
+
* and is server-wide by construction; otherwise from a pool's `/capacity`,
|
|
874
|
+
* which needs `capacity_readonly_v1` before it may be read at all.
|
|
875
|
+
*
|
|
876
|
+
* **`kvScope` says which window these describe** and must be read with
|
|
877
|
+
* them -- see `PolykvCapacity.pressure_scope`. `kvCells*` covers the BASE
|
|
878
|
+
* pool only: on an iSWA model the sliding-window ring is a separate
|
|
879
|
+
* account, so total occupancy is neither of them and is not their sum.
|
|
880
|
+
*/
|
|
881
|
+
kvHeadroomPct?: number;
|
|
882
|
+
kvCellsFree?: number;
|
|
883
|
+
kvCellsTotal?: number;
|
|
884
|
+
kvCellsUsed?: number;
|
|
885
|
+
/** `server` or one session's private window. Never assumed. */
|
|
886
|
+
kvScope?: "server" | "session";
|
|
887
|
+
/** The session those figures belong to, when `kvScope` is `session`. */
|
|
888
|
+
kvScopeOwner?: string;
|
|
889
|
+
/** The largest window a new session could book right now. */
|
|
890
|
+
largestAdmissible?: number;
|
|
891
|
+
/** Whether the server is enforcing guaranteed allocations at all. */
|
|
892
|
+
guaranteed?: boolean;
|
|
893
|
+
/** Sub-pools one session may hold. */
|
|
894
|
+
poolsMaxPerSlot?: number;
|
|
895
|
+
/** How long an idle window is held before the server reclaims it. */
|
|
896
|
+
allocTtlSeconds?: number;
|
|
897
|
+
/** One row per session holding a window. Empty on a server without them. */
|
|
898
|
+
allocations: OpencotiAllocation[];
|
|
899
|
+
swaActive?: boolean;
|
|
900
|
+
swaCellsFree?: number | null;
|
|
901
|
+
swaCellsTotal?: number | null;
|
|
902
|
+
swaWindow?: number | null;
|
|
903
|
+
poolsEnabled: boolean;
|
|
904
|
+
elastic: boolean;
|
|
905
|
+
/**
|
|
906
|
+
* The elastic controller's own word for where it is.
|
|
907
|
+
*
|
|
908
|
+
* Surfaced verbatim because the engine keeps `saturated, hold` distinct
|
|
909
|
+
* from `kv headroom exhausted` deliberately: one says raise
|
|
910
|
+
* `--max-parallel`, the other says this context does not fit.
|
|
911
|
+
*/
|
|
912
|
+
elasticReason?: string;
|
|
913
|
+
slotsLive?: number;
|
|
914
|
+
slotsMax?: number;
|
|
915
|
+
/**
|
|
916
|
+
* Free VRAM, when the engine can measure it.
|
|
917
|
+
*
|
|
918
|
+
* The engine sends `null`, not `0`, when the number would be a lie -- no
|
|
919
|
+
* GPU layers, or integrated memory where the device reports host RAM -- so
|
|
920
|
+
* this stays `undefined` there. A reader that saw `0` could not tell "no
|
|
921
|
+
* headroom" from "no measurement".
|
|
922
|
+
*/
|
|
923
|
+
vramFreeMib?: number;
|
|
924
|
+
tpsFloor?: number;
|
|
925
|
+
grows?: number;
|
|
926
|
+
shrinks?: number;
|
|
927
|
+
poolsMax?: number;
|
|
928
|
+
treeDepth?: number;
|
|
929
|
+
pools: readonly OpencotiStatusPool[];
|
|
930
|
+
sessions: readonly OpencotiStatusSession[];
|
|
931
|
+
}
|
|
932
|
+
/** `GET /kv`'s `allocations[]`, one row per session holding a window. */
|
|
933
|
+
export declare function parseOpencotiAllocations(kv: Record<string, unknown> | undefined): OpencotiAllocation[];
|
|
934
|
+
/**
|
|
935
|
+
* The per-session allocations, and nothing else, off `GET /kv`.
|
|
936
|
+
*
|
|
937
|
+
* For the compaction trigger, which wants one session's raw `pressure` and
|
|
938
|
+
* none of the rest of the status read. `undefined` when the server does not
|
|
939
|
+
* offer the route (`kv_status_v1`) or did not answer: "cannot say" is not
|
|
940
|
+
* "no pressure", and the caller falls back to what it had.
|
|
941
|
+
*/
|
|
942
|
+
export declare function readOpencotiAllocations(baseUrl: string | undefined, fetchImpl?: typeof fetch): Promise<OpencotiAllocation[] | undefined>;
|
|
943
|
+
/**
|
|
944
|
+
* Read a server's live PolyKV state for display.
|
|
945
|
+
*
|
|
946
|
+
* Nothing here may fail a caller: every read that does not answer leaves its
|
|
947
|
+
* part of the shape empty, and a server that cannot be reached at all reports
|
|
948
|
+
* itself as such rather than as a server with no pools -- those are different
|
|
949
|
+
* things and the panel says which.
|
|
950
|
+
*
|
|
951
|
+
* Pool timestamps are deliberately absent. On c7 `created_ts` and
|
|
952
|
+
* `last_access_ts` are `ggml_time_us()` monotonic stamps since boot, not epoch
|
|
953
|
+
* seconds, so rendering either as a wall-clock time shows a date in 1970 and
|
|
954
|
+
* rendering the difference from now shows an age of decades. c8 anchors them;
|
|
955
|
+
* until then the honest number is none.
|
|
956
|
+
*/
|
|
957
|
+
export declare function readOpencotiStatus(baseUrl: string | undefined, fetchImpl?: typeof fetch): Promise<OpencotiStatus>;
|
|
958
|
+
//# sourceMappingURL=polykv.d.ts.map
|