@gapi/gcli 1.8.238 → 1.8.239
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.
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
## 2026-10-11 - local-ai-runners: runner reports each model's loaded context window
|
|
2
|
+
|
|
3
|
+
**Feature:** local-ai-runners · **Type:** fix
|
|
4
|
+
|
|
5
|
+
- Heartbeat models now carry `runtimeContextLength` from Ollama `/api/ps`
|
|
6
|
+
(the window a loaded model really runs with, e.g. 32,768 for qwen3.8:27b
|
|
7
|
+
whose max is 262,144). The backend fits every request to it - fixes
|
|
8
|
+
`500 no user query found in messages` in long chats, caused by Ollama
|
|
9
|
+
silently truncating oversized prompts (backend half: graphql-server
|
|
10
|
+
`fix(local-ai-runners): fit requests to the model's real context window`).
|
|
11
|
+
- Models `/api/show` answers 404/410 for (a retired `glm-5.1:cloud` was
|
|
12
|
+
still listed by `/api/tags`) are no longer offered, and the answer is
|
|
13
|
+
cached instead of re-asked every heartbeat.
|
|
14
|
+
- **Test:** `npx jest test/runner.spec.ts --coverage=false` (fake Ollama
|
|
15
|
+
answers `/api/ps` with a loaded model at 65536 and 410 for a retired
|
|
16
|
+
cloud model; the heartbeat must carry the window and omit that model).
|
|
17
|
+
- **Debug:** `ollama ps` shows the live window the runner reports;
|
|
18
|
+
`db["local-runners"]` -> `models[].runtimeContextLength` on the backend.
|
|
19
|
+
An unloaded model reports nothing - the backend keeps its last value.
|
|
20
|
+
- **Verification:** gcli 27/27, tsc + eslint clean; live: runner reported
|
|
21
|
+
30 models (retired cloud model gone) and the dev backend stored
|
|
22
|
+
qwen3.8:27b runtimeContextLength 32768.
|
|
@@ -34,7 +34,14 @@ Ollama: `--ollama` > `$OLLAMA_HOST` (scheme optional, `0.0.0.0` -> `127.0.0.1`)
|
|
|
34
34
|
sequentially. `RunnerAuthError` = token refused -> exit 1 with a `--reset` hint.
|
|
35
35
|
- `ollama.ts` - `ALLOWED_REQUESTS` (only `POST /v1/chat/completions`; the
|
|
36
36
|
backend has its own copy), `OllamaModels` (`/api/tags` + cached
|
|
37
|
-
`/api/show` per digest; drops models without the `completion` capability
|
|
37
|
+
`/api/show` per digest; drops models without the `completion` capability
|
|
38
|
+
and models `/api/show` answers 404/410 for - e.g. retired `:cloud`
|
|
39
|
+
models, cached so they aren't re-asked every heartbeat). Each heartbeat
|
|
40
|
+
also reads `/api/ps` and reports `runtimeContextLength` - the window a
|
|
41
|
+
LOADED model actually runs with (OLLAMA_CONTEXT_LENGTH / Ollama default).
|
|
42
|
+
The backend fits every request to it, because Ollama silently drops the
|
|
43
|
+
oldest messages of a prompt that is too long (see the canonical doc,
|
|
44
|
+
"Context window").
|
|
38
45
|
- `config.ts` (saved runner, URL derivation), `start.ts`, `list.ts`,
|
|
39
46
|
`models.ts`, `delete.ts`, `index.ts` (command registration), `types.ts`.
|
|
40
47
|
- `src/services/gql-client.ts` - `createLocalRunner`, `listLocalRunners`,
|
package/package.json
CHANGED
package/release/index.js
CHANGED
|
@@ -317007,12 +317007,41 @@ var init_ollama = __esm({
|
|
|
317007
317007
|
throw new Error(`Ollama /api/tags returned ${res.status}`);
|
|
317008
317008
|
}
|
|
317009
317009
|
const { models = [] } = await res.json();
|
|
317010
|
-
const described = await Promise.all(
|
|
317011
|
-
models.map((tag) => this.describe(tag))
|
|
317012
|
-
|
|
317013
|
-
|
|
317010
|
+
const [described, loaded] = await Promise.all([
|
|
317011
|
+
Promise.all(models.map((tag) => this.describe(tag))),
|
|
317012
|
+
this.loadedContextLengths()
|
|
317013
|
+
]);
|
|
317014
|
+
for (const model of described) {
|
|
317015
|
+
model.runtimeContextLength = loaded.get(model.name);
|
|
317016
|
+
}
|
|
317017
|
+
return described.filter((m2) => !m2.unusable).filter(
|
|
317014
317018
|
(m2) => !m2.capabilities.length || m2.capabilities.includes("completion")
|
|
317015
|
-
)
|
|
317019
|
+
).map((m2) => {
|
|
317020
|
+
delete m2.unusable;
|
|
317021
|
+
return m2;
|
|
317022
|
+
});
|
|
317023
|
+
}
|
|
317024
|
+
/**
|
|
317025
|
+
* The context window each LOADED model actually runs with (`/api/ps`,
|
|
317026
|
+
* what `ollama ps` shows) - OLLAMA_CONTEXT_LENGTH / Ollama's VRAM-based
|
|
317027
|
+
* default, usually far below the model's own `contextLength`. Ollama
|
|
317028
|
+
* silently drops the oldest messages of a prompt that doesn't fit, so the
|
|
317029
|
+
* backend sizes requests with this. Unloaded models aren't listed; the
|
|
317030
|
+
* backend keeps the last known value.
|
|
317031
|
+
*/
|
|
317032
|
+
async loadedContextLengths() {
|
|
317033
|
+
try {
|
|
317034
|
+
const res = await this.fetchFn(`${this.baseUrl}/api/ps`);
|
|
317035
|
+
if (!res.ok) {
|
|
317036
|
+
return /* @__PURE__ */ new Map();
|
|
317037
|
+
}
|
|
317038
|
+
const { models = [] } = await res.json();
|
|
317039
|
+
return new Map(
|
|
317040
|
+
models.filter((m2) => typeof m2.context_length === "number").map((m2) => [m2.name ?? m2.model, m2.context_length])
|
|
317041
|
+
);
|
|
317042
|
+
} catch {
|
|
317043
|
+
return /* @__PURE__ */ new Map();
|
|
317044
|
+
}
|
|
317016
317045
|
}
|
|
317017
317046
|
async describe(tag) {
|
|
317018
317047
|
const show = await this.show(tag);
|
|
@@ -317020,6 +317049,7 @@ var init_ollama = __esm({
|
|
|
317020
317049
|
([key]) => key.endsWith(".context_length")
|
|
317021
317050
|
)?.[1];
|
|
317022
317051
|
return {
|
|
317052
|
+
unusable: show?.unusable,
|
|
317023
317053
|
name: tag.name,
|
|
317024
317054
|
family: tag.details?.family,
|
|
317025
317055
|
parameterSize: tag.details?.parameter_size,
|
|
@@ -317039,6 +317069,11 @@ var init_ollama = __esm({
|
|
|
317039
317069
|
headers: { "Content-Type": "application/json" },
|
|
317040
317070
|
body: JSON.stringify({ model: tag.name })
|
|
317041
317071
|
});
|
|
317072
|
+
if (res.status === 404 || res.status === 410) {
|
|
317073
|
+
const gone = { unusable: true };
|
|
317074
|
+
this.showCache.set(key, gone);
|
|
317075
|
+
return gone;
|
|
317076
|
+
}
|
|
317042
317077
|
if (!res.ok) {
|
|
317043
317078
|
return void 0;
|
|
317044
317079
|
}
|