@bendyline/gezel-inference 1.0.2 → 1.0.3
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/dist/catalog/chat-models/de/deepseek-v4.1-flash-552b-q2/manifest.json +120 -0
- package/dist/catalog/chat-models/de/deepseek-v4.1-flash-552b-q2/versions/1.0.0/manifest.json +21 -0
- package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/manifest.json +6 -5
- package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/versions/1.0.2/manifest.json +27 -0
- package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/versions/1.0.3/manifest.json +27 -0
- package/dist/catalog/chat-models/index.json +1 -1
- package/dist/catalog/chat-models/ne/nemotron3.5-bolt-74b-iq2/manifest.json +157 -0
- package/dist/catalog/chat-models/ne/nemotron3.5-bolt-74b-iq2/versions/1.0.0/manifest.json +30 -0
- package/dist/catalog/chat-models/ne/nemotron3.5-super-vl-120b-nvfp4/manifest.json +156 -0
- package/dist/catalog/chat-models/ne/nemotron3.5-super-vl-120b-nvfp4/versions/1.0.0/manifest.json +28 -0
- package/dist/catalog/chat-models/raw-index.json +1 -1
- package/dist/catalog/knowledge-catalogs/be/bendyline-how-to-books/keywords.json +56 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-alaska-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-nevada-part-0001/keywords.json +13 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-us-300-399-part-0001/keywords.json +14 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-us-347-349/keywords.json +17 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-utah-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-utah-part-0002/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0002/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0003/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0004/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0005/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-wyoming-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/index.json +1 -1
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-aspnet-core-en/keywords.json +21 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-ai-platform-en/keywords.json +27 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-apps-compute-en/keywords.json +23 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-data-aiot-en/keywords.json +26 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-management-hybrid-en/keywords.json +28 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-network-security-en/keywords.json +29 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-dotnet-en/keywords.json +21 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-entity-framework-en/keywords.json +14 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-microsoft-365-en/keywords.json +20 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-powershell-en/keywords.json +24 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-sql-server-en/keywords.json +27 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-windows-apps-en/keywords.json +18 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-windows-server-en/keywords.json +28 -0
- package/dist/catalog/knowledge-catalogs/raw-index.json +1 -1
- package/dist/catalog/knowledge-catalogs/re/regional-american-south/keywords.json +1700 -0
- package/dist/catalog/knowledge-catalogs/re/regional-american-south/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-andes-amazon/keywords.json +750 -0
- package/dist/catalog/knowledge-catalogs/re/regional-andes-amazon/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-australia-nz/keywords.json +1281 -0
- package/dist/catalog/knowledge-catalogs/re/regional-australia-nz/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-brazil/keywords.json +1125 -0
- package/dist/catalog/knowledge-catalogs/re/regional-brazil/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-britain-ireland/keywords.json +3287 -0
- package/dist/catalog/knowledge-catalogs/re/regional-britain-ireland/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-california-southwest/keywords.json +1835 -0
- package/dist/catalog/knowledge-catalogs/re/regional-california-southwest/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-canada-alaska/keywords.json +1184 -0
- package/dist/catalog/knowledge-catalogs/re/regional-canada-alaska/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-central-eastern-europe/keywords.json +1503 -0
- package/dist/catalog/knowledge-catalogs/re/regional-central-eastern-europe/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-asia/keywords.json +2836 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-asia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-southern-africa/keywords.json +813 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-southern-africa/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mediterranean/keywords.json +2082 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mediterranean/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mexico-central-america/keywords.json +1481 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mexico-central-america/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-middle-east/keywords.json +1272 -0
- package/dist/catalog/knowledge-catalogs/re/regional-middle-east/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-midwest/keywords.json +758 -0
- package/dist/catalog/knowledge-catalogs/re/regional-midwest/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-north-africa/keywords.json +896 -0
- package/dist/catalog/knowledge-catalogs/re/regional-north-africa/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northeast/keywords.json +1281 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northeast/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-eurasia/keywords.json +346 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-eurasia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-europe/keywords.json +2688 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-europe/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-islands/keywords.json +973 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-islands/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-northwest/keywords.json +937 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-northwest/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-rockies-plains/keywords.json +608 -0
- package/dist/catalog/knowledge-catalogs/re/regional-rockies-plains/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-south-asia/keywords.json +633 -0
- package/dist/catalog/knowledge-catalogs/re/regional-south-asia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southeast-asia/keywords.json +2697 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southeast-asia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southern-cone/keywords.json +606 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southern-cone/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-west-central-africa/keywords.json +564 -0
- package/dist/catalog/knowledge-catalogs/re/regional-west-central-africa/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe/keywords.json +3247 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-east/manifest.json +47 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-east/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-west/manifest.json +47 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-west/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/keywords.json +5437 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/versions/2026.10.1/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/versions/2026.10.1/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/versions/2026.10.1/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/keywords.json +5583 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/keywords.json +6386 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/manifest.json +2 -2
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/keywords.json +5672 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/versions/2026.10.1/manifest.json +137 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-geography/manifest.json +68 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-geography/versions/2026.10.1/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-history/manifest.json +68 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-history/versions/2026.10.1/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/versions/2026.10.1/manifest.json +141 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/keywords.json +5719 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/versions/2026.10.1/manifest.json +193 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/keywords.json +16009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/keywords.json +5843 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/keywords.json +6264 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-arts/manifest.json +66 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-arts/versions/2026.10.1/manifest.json +209 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-business/manifest.json +61 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-business/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-entertainment/manifest.json +64 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-entertainment/versions/2026.10.1/manifest.json +201 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-military/manifest.json +60 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-military/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-other/manifest.json +55 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-other/versions/2026.10.1/manifest.json +165 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-politics/manifest.json +63 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-politics/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-religion/manifest.json +64 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-religion/versions/2026.10.1/manifest.json +201 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-science/manifest.json +65 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-science/versions/2026.10.1/manifest.json +205 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-sports/manifest.json +73 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-sports/versions/2026.10.1/manifest.json +237 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/keywords.json +5447 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/keywords.json +5209 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/versions/2026.10.1/manifest.json +193 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/keywords.json +6537 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/keywords.json +5989 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/provenance.json +1 -1
- package/dist/engines/native-file-manifest.json +182 -112
- package/dist/engines/native-manifest.d.ts +4 -4
- package/dist/engines/native-manifest.js +36 -34
- package/dist/engines/resolver.js +3 -1
- package/dist/fs/atomic.d.ts +4 -1
- package/dist/fs/atomic.js +7 -1
- package/dist/http/openai-compat/chat-target.d.ts +1 -1
- package/dist/http/openai-compat/chat-target.js +1 -1
- package/dist/http/routes/v1-chat.js +1 -1
- package/dist/http/routes/v1-knowledge.js +4 -0
- package/dist/http/routes/v1-responses.d.ts +1 -1
- package/dist/inference-service.d.ts +6 -1
- package/dist/inference-service.js +20 -9
- package/dist/knowledge/manager.d.ts +7 -0
- package/dist/knowledge/manager.js +32 -1
- package/dist/perf/responsiveness.js +4 -3
- package/dist/providers/audio/kokoro-frontend.js +1 -1
- package/dist/providers/ds4/build-provider.js +6 -0
- package/dist/providers/llama-cpp/build-provider.js +107 -56
- package/dist/providers/llama-cpp/cache-adapter.d.ts +7 -1
- package/dist/providers/llama-cpp/cache-adapter.js +9 -0
- package/dist/providers/llama-cpp/devices.d.ts +13 -2
- package/dist/providers/llama-cpp/devices.js +43 -6
- package/dist/providers/llama-cpp/models.js +4 -1
- package/dist/providers/llama-cpp/offload-planner.d.ts +23 -0
- package/dist/providers/llama-cpp/offload-planner.js +27 -0
- package/dist/providers/llama-cpp/provider.d.ts +14 -3
- package/dist/providers/llama-cpp/provider.js +35 -2
- package/dist/providers/mlx/provider.js +2 -2
- package/dist/providers/mlx/reasoning-stream.d.ts +15 -0
- package/dist/providers/mlx/reasoning-stream.js +35 -0
- package/dist/providers/native/capacity-broker.js +9 -3
- package/dist/providers/native/local-engine-runtime.js +15 -12
- package/dist/providers/native/supervisor.js +3 -1
- package/dist/providers/queue.d.ts +9 -2
- package/dist/providers/queue.js +17 -2
- package/dist/providers/session-hooks.d.ts +4 -1
- package/package.json +5 -5
|
@@ -123,7 +123,7 @@ export declare class LlamaCppCacheAdapter implements EngineCacheAdapter {
|
|
|
123
123
|
*/
|
|
124
124
|
private allocationTail;
|
|
125
125
|
private engineGeneration;
|
|
126
|
-
private
|
|
126
|
+
private slotCount;
|
|
127
127
|
private readonly resolveBaseUrl;
|
|
128
128
|
private readonly resolveAuthToken;
|
|
129
129
|
private readonly fetchImpl;
|
|
@@ -168,6 +168,12 @@ export declare class LlamaCppCacheAdapter implements EngineCacheAdapter {
|
|
|
168
168
|
*/
|
|
169
169
|
private slotActionsUnsupported;
|
|
170
170
|
constructor(opts: LlamaCppCacheAdapterOptions);
|
|
171
|
+
/**
|
|
172
|
+
* Follow an engine relaunched with a different `--parallel` count. Every
|
|
173
|
+
* binding goes with it: a session pinned to a slot the new engine lacks
|
|
174
|
+
* would send an `id_slot` the server rejects.
|
|
175
|
+
*/
|
|
176
|
+
setSlotCount(slotCount: number): void;
|
|
171
177
|
/** The provider survives engine restarts, but its resident slot contents do not. */
|
|
172
178
|
resetEngineState(): void;
|
|
173
179
|
/**
|
|
@@ -181,6 +181,15 @@ export class LlamaCppCacheAdapter {
|
|
|
181
181
|
throw new Error('Invalid llama disk cache budget');
|
|
182
182
|
this.diskCacheBudgetBytes = budget * 1024 * 1024;
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Follow an engine relaunched with a different `--parallel` count. Every
|
|
186
|
+
* binding goes with it: a session pinned to a slot the new engine lacks
|
|
187
|
+
* would send an `id_slot` the server rejects.
|
|
188
|
+
*/
|
|
189
|
+
setSlotCount(slotCount) {
|
|
190
|
+
this.slotCount = Math.max(1, Math.floor(slotCount));
|
|
191
|
+
this.resetEngineState();
|
|
192
|
+
}
|
|
184
193
|
/** The provider survives engine restarts, but its resident slot contents do not. */
|
|
185
194
|
resetEngineState() {
|
|
186
195
|
this.engineGeneration++;
|
|
@@ -37,6 +37,8 @@ export interface LlamaDeviceProbe {
|
|
|
37
37
|
* means the cached devices describe a machine that no longer exists.
|
|
38
38
|
*/
|
|
39
39
|
systemRamBytes?: number;
|
|
40
|
+
/** {@link llamaBinaryFingerprint} of the build that answered. */
|
|
41
|
+
binary?: string;
|
|
40
42
|
}
|
|
41
43
|
export interface NvidiaRuntimeDevice {
|
|
42
44
|
index: number;
|
|
@@ -75,11 +77,20 @@ export declare function maxGpuVramBytes(devices: LlamaDevice[]): number;
|
|
|
75
77
|
* (e.g. "AMD Radeon RX 7900 XTX") in a tier rationale. Pure — unit-tested.
|
|
76
78
|
*/
|
|
77
79
|
export declare function pickBestGpuDevice(devices: LlamaDevice[]): LlamaDevice | null;
|
|
80
|
+
/**
|
|
81
|
+
* Which build a cached listing came from: its path (the backend) and its
|
|
82
|
+
* size and mtime (the release, since a native update rewrites the files).
|
|
83
|
+
* A listing taken from the CPU build, or from the build an update replaced,
|
|
84
|
+
* said "no GPU" about a 32 GB Radeon and was served for the rest of the day
|
|
85
|
+
* (2026-10-19).
|
|
86
|
+
*/
|
|
87
|
+
export declare function llamaBinaryFingerprint(binaryPath: string): string;
|
|
78
88
|
/**
|
|
79
89
|
* Probe device memory by running `<binaryPath> --list-devices`, caching
|
|
80
90
|
* the result. Reads the cache when present (VRAM capacity is stable per
|
|
81
|
-
* machine)
|
|
82
|
-
*
|
|
91
|
+
* machine), the OS still reports the RAM it was probed with, and the same
|
|
92
|
+
* build answered it; an empty listing is re-asked hourly. Pass `force` to
|
|
93
|
+
* re-probe. Any failure (spawn error, no
|
|
83
94
|
* cache, unparseable output) resolves to an empty device list — the
|
|
84
95
|
* planner then simply does nothing and the engine's own `--fit`/`-ngl
|
|
85
96
|
* auto` takes over. `nowIso` and `systemRamBytes` are injectable for
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* `backend.json` pattern in `core/native/llama-backend.ts`.
|
|
17
17
|
*/
|
|
18
18
|
import { execFile } from 'node:child_process';
|
|
19
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
20
20
|
import { totalmem } from 'node:os';
|
|
21
21
|
import { delimiter, dirname, join } from 'node:path';
|
|
22
22
|
import { promisify } from 'node:util';
|
|
@@ -122,11 +122,34 @@ export function pickBestGpuDevice(devices) {
|
|
|
122
122
|
function cachePath(home) {
|
|
123
123
|
return join(home, 'engines', 'llama-cpp', 'devices.json');
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* An empty listing is re-asked after this long. A CPU build always answers
|
|
127
|
+
* "(none)", but so can a GPU build whose driver was mid-update, and a cached
|
|
128
|
+
* "no GPU" sizes every model against system RAM.
|
|
129
|
+
*/
|
|
130
|
+
const EMPTY_DEVICES_RETRY_MS = 60 * 60 * 1000;
|
|
131
|
+
/**
|
|
132
|
+
* Which build a cached listing came from: its path (the backend) and its
|
|
133
|
+
* size and mtime (the release, since a native update rewrites the files).
|
|
134
|
+
* A listing taken from the CPU build, or from the build an update replaced,
|
|
135
|
+
* said "no GPU" about a 32 GB Radeon and was served for the rest of the day
|
|
136
|
+
* (2026-10-19).
|
|
137
|
+
*/
|
|
138
|
+
export function llamaBinaryFingerprint(binaryPath) {
|
|
139
|
+
try {
|
|
140
|
+
const { size, mtimeMs } = statSync(binaryPath);
|
|
141
|
+
return `${binaryPath}|${size}|${Math.floor(mtimeMs)}`;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return binaryPath;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
125
147
|
/**
|
|
126
148
|
* Probe device memory by running `<binaryPath> --list-devices`, caching
|
|
127
149
|
* the result. Reads the cache when present (VRAM capacity is stable per
|
|
128
|
-
* machine)
|
|
129
|
-
*
|
|
150
|
+
* machine), the OS still reports the RAM it was probed with, and the same
|
|
151
|
+
* build answered it; an empty listing is re-asked hourly. Pass `force` to
|
|
152
|
+
* re-probe. Any failure (spawn error, no
|
|
130
153
|
* cache, unparseable output) resolves to an empty device list — the
|
|
131
154
|
* planner then simply does nothing and the engine's own `--fit`/`-ngl
|
|
132
155
|
* auto` takes over. `nowIso` and `systemRamBytes` are injectable for
|
|
@@ -137,11 +160,19 @@ export async function probeLlamaDevices(opts) {
|
|
|
137
160
|
const now = opts.nowIso ?? (() => new Date().toISOString());
|
|
138
161
|
const systemRamBytes = opts.systemRamBytes ?? totalmem();
|
|
139
162
|
const path = cachePath(home);
|
|
163
|
+
const binary = llamaBinaryFingerprint(binaryPath);
|
|
140
164
|
if (!force && existsSync(path)) {
|
|
141
165
|
try {
|
|
142
166
|
const cached = JSON.parse(readFileSync(path, 'utf8'));
|
|
143
|
-
// A cache without the RAM figure
|
|
144
|
-
|
|
167
|
+
// A cache without the RAM figure or the binary predates that check and
|
|
168
|
+
// re-probes once.
|
|
169
|
+
const emptyExpired = Array.isArray(cached.devices) &&
|
|
170
|
+
cached.devices.length === 0 &&
|
|
171
|
+
!(Date.parse(now()) - Date.parse(cached.probedAt) < EMPTY_DEVICES_RETRY_MS);
|
|
172
|
+
if (Array.isArray(cached.devices) &&
|
|
173
|
+
cached.systemRamBytes === systemRamBytes &&
|
|
174
|
+
cached.binary === binary &&
|
|
175
|
+
!emptyExpired) {
|
|
145
176
|
return { ...cached, cached: true };
|
|
146
177
|
}
|
|
147
178
|
}
|
|
@@ -171,7 +202,13 @@ export async function probeLlamaDevices(opts) {
|
|
|
171
202
|
// Leave devices empty — caller treats "no devices" as "no offload target".
|
|
172
203
|
return { devices, probedAt: now(), cached: false };
|
|
173
204
|
}
|
|
174
|
-
const probe = {
|
|
205
|
+
const probe = {
|
|
206
|
+
devices,
|
|
207
|
+
probedAt: now(),
|
|
208
|
+
cached: false,
|
|
209
|
+
systemRamBytes,
|
|
210
|
+
binary,
|
|
211
|
+
};
|
|
175
212
|
try {
|
|
176
213
|
mkdirSync(dirname(path), { recursive: true });
|
|
177
214
|
writeFileSync(path, JSON.stringify(probe, null, 2), 'utf8');
|
|
@@ -748,7 +748,10 @@ export class LlamaCppModelManager {
|
|
|
748
748
|
installed.fileSha256 = await hashModelPayloadFiles(itemDir, verifiedDigests);
|
|
749
749
|
await writeFile(join(itemDir, 'manifest.json'), JSON.stringify(installed, null, 2), 'utf8');
|
|
750
750
|
await makeSharedModelReadable(itemDir);
|
|
751
|
-
|
|
751
|
+
// ds4-server renders each family's prompt itself and never reads
|
|
752
|
+
// tokenizer.chat_template; DeepSeek V4.1 ships no Jinja template at all,
|
|
753
|
+
// and its first install warned that replies "may be incoherent".
|
|
754
|
+
const warning = summary.chatTemplateMissing && this.engine === 'llama-cpp'
|
|
752
755
|
? 'Model has no embedded chat template (tokenizer.chat_template is missing). llama-server will fall back to a generic template that may not match this model — replies may be incoherent. Consider a different quant of the same model.'
|
|
753
756
|
: undefined;
|
|
754
757
|
try {
|
|
@@ -166,6 +166,29 @@ export declare function planMoeOffload(input: MoeOffloadInput): MoeOffloadDecisi
|
|
|
166
166
|
export declare function degradeMoeOffloadDecision(decision: MoeOffloadDecision | undefined): MoeOffloadDecision | null;
|
|
167
167
|
/** Dense counterpart of {@link degradeMoeOffloadDecision}. */
|
|
168
168
|
export declare function degradeDenseFfnOffloadDecision(decision: MoeOffloadDecision | undefined, blockCount: number | undefined): MoeOffloadDecision | null;
|
|
169
|
+
/**
|
|
170
|
+
* Room an extra engine slot must leave on the card. Other programs' graphics
|
|
171
|
+
* memory moves while a model runs (a browser, a video, the desktop), and a
|
|
172
|
+
* two-slot launch planned to within a gigabyte or two died with a Vulkan
|
|
173
|
+
* out-of-memory when they took more (2026-10-19, a 32 GB card).
|
|
174
|
+
*/
|
|
175
|
+
export declare const EXTRA_SLOT_GPU_CUSHION_BYTES: number;
|
|
176
|
+
/**
|
|
177
|
+
* Fewer slots before system RAM. Keep `slots` only when the model stays
|
|
178
|
+
* fully on the GPU with {@link EXTRA_SLOT_GPU_CUSHION_BYTES} to spare;
|
|
179
|
+
* otherwise drop to one slot when that alone keeps it fully on the GPU. A
|
|
180
|
+
* model that needs system RAM even at one slot keeps its slots and the
|
|
181
|
+
* planner's split. `plan` prices one slot count against one VRAM pool.
|
|
182
|
+
*/
|
|
183
|
+
export declare function shedSlotsForGpuResidency(input: {
|
|
184
|
+
slots: number;
|
|
185
|
+
vramBytes: number;
|
|
186
|
+
plan: (slots: number, vramBytes: number) => MoeOffloadDecision;
|
|
187
|
+
}): {
|
|
188
|
+
slots: number;
|
|
189
|
+
decision: MoeOffloadDecision;
|
|
190
|
+
shed: boolean;
|
|
191
|
+
};
|
|
169
192
|
export interface KvReserveInput {
|
|
170
193
|
blockCount?: number | undefined;
|
|
171
194
|
embeddingLength?: number | undefined;
|
|
@@ -272,6 +272,33 @@ export function degradeDenseFfnOffloadDecision(decision, blockCount) {
|
|
|
272
272
|
}
|
|
273
273
|
return null;
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Room an extra engine slot must leave on the card. Other programs' graphics
|
|
277
|
+
* memory moves while a model runs (a browser, a video, the desktop), and a
|
|
278
|
+
* two-slot launch planned to within a gigabyte or two died with a Vulkan
|
|
279
|
+
* out-of-memory when they took more (2026-10-19, a 32 GB card).
|
|
280
|
+
*/
|
|
281
|
+
export const EXTRA_SLOT_GPU_CUSHION_BYTES = 2 * 1024 ** 3;
|
|
282
|
+
/**
|
|
283
|
+
* Fewer slots before system RAM. Keep `slots` only when the model stays
|
|
284
|
+
* fully on the GPU with {@link EXTRA_SLOT_GPU_CUSHION_BYTES} to spare;
|
|
285
|
+
* otherwise drop to one slot when that alone keeps it fully on the GPU. A
|
|
286
|
+
* model that needs system RAM even at one slot keeps its slots and the
|
|
287
|
+
* planner's split. `plan` prices one slot count against one VRAM pool.
|
|
288
|
+
*/
|
|
289
|
+
export function shedSlotsForGpuResidency(input) {
|
|
290
|
+
const decision = input.plan(input.slots, input.vramBytes);
|
|
291
|
+
const keep = { slots: input.slots, decision, shed: false };
|
|
292
|
+
if (input.slots <= 1 || input.vramBytes <= 0)
|
|
293
|
+
return keep;
|
|
294
|
+
const roomy = input.plan(input.slots, Math.max(0, input.vramBytes - EXTRA_SLOT_GPU_CUSHION_BYTES));
|
|
295
|
+
if (roomy.fullGpuResidency)
|
|
296
|
+
return keep;
|
|
297
|
+
const single = input.plan(1, input.vramBytes);
|
|
298
|
+
if (!single.fullGpuResidency)
|
|
299
|
+
return keep;
|
|
300
|
+
return { slots: 1, decision: single, shed: true };
|
|
301
|
+
}
|
|
275
302
|
/** Bytes per KV element for llama-server's `--cache-type-k/v` values. */
|
|
276
303
|
const KV_BYTES_PER_ELEMENT = {
|
|
277
304
|
f32: 4,
|
|
@@ -143,7 +143,9 @@ export declare class LlamaCppProvider implements LLMProvider {
|
|
|
143
143
|
/** Request-scoped effort shape for compatible wrappers such as ds4-server. */
|
|
144
144
|
private readonly reasoningEffortRequestShape;
|
|
145
145
|
/** Engine batch width; see the `batchMaxConcurrency` constructor opt. */
|
|
146
|
-
private
|
|
146
|
+
private batchMaxConcurrency;
|
|
147
|
+
/** See the `reserveBackgroundSlot` constructor opt. */
|
|
148
|
+
private readonly reservesBackgroundLane;
|
|
147
149
|
/**
|
|
148
150
|
* Number of real llama-server request slots (`--parallel`). The provider
|
|
149
151
|
* queue can intentionally be wider than this by one reserved background
|
|
@@ -152,7 +154,7 @@ export declare class LlamaCppProvider implements LLMProvider {
|
|
|
152
154
|
* boundary: cache slot save/restore plus the streamed engine request must
|
|
153
155
|
* never exceed the slots the native server actually owns.
|
|
154
156
|
*/
|
|
155
|
-
private
|
|
157
|
+
private engineRequestWidth;
|
|
156
158
|
private engineRequestsActive;
|
|
157
159
|
private readonly engineRequestWaiters;
|
|
158
160
|
/**
|
|
@@ -409,7 +411,7 @@ export declare class LlamaCppProvider implements LLMProvider {
|
|
|
409
411
|
*/
|
|
410
412
|
private cacheAdapter;
|
|
411
413
|
private slotSavePath?;
|
|
412
|
-
private
|
|
414
|
+
private launchedSlots;
|
|
413
415
|
setCacheAdapter(adapter: import('./cache-adapter.js').LlamaCppCacheAdapter): void;
|
|
414
416
|
getCacheAdapter(): import('./cache-adapter.js').LlamaCppCacheAdapter | null;
|
|
415
417
|
/**
|
|
@@ -430,6 +432,15 @@ export declare class LlamaCppProvider implements LLMProvider {
|
|
|
430
432
|
* would 400 silently, and the debug probe's slot narration was fiction.
|
|
431
433
|
*/
|
|
432
434
|
getLaunchedSlots(): number;
|
|
435
|
+
/**
|
|
436
|
+
* Follow a supervised engine that is relaunching with fewer `--parallel`
|
|
437
|
+
* slots because the wider launch ran the GPU out of memory. Runs between
|
|
438
|
+
* the failed start and the retry, while the request that asked for the
|
|
439
|
+
* engine still holds its lease. Refused when more requests hold leases
|
|
440
|
+
* than the narrower engine has slots: two turns on one slot would save and
|
|
441
|
+
* restore it under each other.
|
|
442
|
+
*/
|
|
443
|
+
narrowEngineSlots(slots: number): boolean;
|
|
433
444
|
/**
|
|
434
445
|
* Claim one physical llama-server request slot.
|
|
435
446
|
*
|
|
@@ -25,7 +25,7 @@ import * as os from 'node:os';
|
|
|
25
25
|
import { createLogger } from '@bendyline/gezel';
|
|
26
26
|
import { DEFAULT_NUM_CTX, LlamaCppSession, NativeEngineCrashedError, TOOL_GRAMMAR_FALLBACK_ORDER, constrainedToolNoSignalMsForModel, engineRequestAbortError, parseEnvMs, } from '@bendyline/gezel/local-loop';
|
|
27
27
|
import { ProviderDisposedError } from '../provider-disposal.js';
|
|
28
|
-
import { ProviderQueue, QUEUE_WAIT_NOTICE_DELAY_MS, QUEUE_WAIT_NOTICE_REPEAT_MS, backgroundLaneCap, defaultAmbientQuietMs, } from '../queue.js';
|
|
28
|
+
import { ProviderQueue, QUEUE_WAIT_NOTICE_DELAY_MS, QUEUE_WAIT_NOTICE_REPEAT_MS, backgroundLaneCap, defaultAmbientQuietMs, defaultQueueYieldQuantumMs, } from '../queue.js';
|
|
29
29
|
import { createToolExecutor } from '../tool-executor.js';
|
|
30
30
|
import { classifyStartupLine } from './stdout-parser.js';
|
|
31
31
|
// The session and its helpers live in core so a phone runs the same loop.
|
|
@@ -147,6 +147,8 @@ export class LlamaCppProvider {
|
|
|
147
147
|
reasoningEffortRequestShape;
|
|
148
148
|
/** Engine batch width; see the `batchMaxConcurrency` constructor opt. */
|
|
149
149
|
batchMaxConcurrency;
|
|
150
|
+
/** See the `reserveBackgroundSlot` constructor opt. */
|
|
151
|
+
reservesBackgroundLane;
|
|
150
152
|
/**
|
|
151
153
|
* Number of real llama-server request slots (`--parallel`). The provider
|
|
152
154
|
* queue can intentionally be wider than this by one reserved background
|
|
@@ -341,7 +343,10 @@ export class LlamaCppProvider {
|
|
|
341
343
|
// mid-turn compaction wedges the turn, the same failure the MLX
|
|
342
344
|
// single-slot path hit. See the matching note in the MLX provider.
|
|
343
345
|
const interactiveConcurrency = batchMax;
|
|
344
|
-
|
|
346
|
+
this.reservesBackgroundLane = opts.reserveBackgroundSlot !== false;
|
|
347
|
+
const queueConcurrency = this.reservesBackgroundLane
|
|
348
|
+
? Math.max(slots, interactiveConcurrency + 1)
|
|
349
|
+
: slots;
|
|
345
350
|
this.queue = new ProviderQueue({
|
|
346
351
|
// llama-server is launched with `--parallel ${slots}` (see
|
|
347
352
|
// `buildLlamaCppProvider` in chat/manager.ts) so the engine has matching
|
|
@@ -364,6 +369,9 @@ export class LlamaCppProvider {
|
|
|
364
369
|
// a multi-minute chore turn never lands right before their next
|
|
365
370
|
// move. Applies to ds4 too — its provider wraps this class.
|
|
366
371
|
ambientQuietMs: defaultAmbientQuietMs(),
|
|
372
|
+
// A tool loop hands the slot to a waiting conversation between
|
|
373
|
+
// iterations once it has held it this long (see `QueueLease`).
|
|
374
|
+
yieldQuantumMs: defaultQueueYieldQuantumMs(),
|
|
367
375
|
});
|
|
368
376
|
}
|
|
369
377
|
/**
|
|
@@ -458,6 +466,31 @@ export class LlamaCppProvider {
|
|
|
458
466
|
getLaunchedSlots() {
|
|
459
467
|
return this.launchedSlots;
|
|
460
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* Follow a supervised engine that is relaunching with fewer `--parallel`
|
|
471
|
+
* slots because the wider launch ran the GPU out of memory. Runs between
|
|
472
|
+
* the failed start and the retry, while the request that asked for the
|
|
473
|
+
* engine still holds its lease. Refused when more requests hold leases
|
|
474
|
+
* than the narrower engine has slots: two turns on one slot would save and
|
|
475
|
+
* restore it under each other.
|
|
476
|
+
*/
|
|
477
|
+
narrowEngineSlots(slots) {
|
|
478
|
+
const next = Math.max(1, Math.floor(slots));
|
|
479
|
+
if (next >= this.launchedSlots || this.engineRequestsActive > next)
|
|
480
|
+
return false;
|
|
481
|
+
this.launchedSlots = next;
|
|
482
|
+
this.engineRequestWidth = next;
|
|
483
|
+
this.batchMaxConcurrency = Math.min(this.batchMaxConcurrency, next);
|
|
484
|
+
this.queue.narrow({
|
|
485
|
+
concurrency: this.reservesBackgroundLane
|
|
486
|
+
? Math.max(next, this.batchMaxConcurrency + 1)
|
|
487
|
+
: next,
|
|
488
|
+
interactiveConcurrency: this.batchMaxConcurrency,
|
|
489
|
+
backgroundConcurrency: backgroundLaneCap(next),
|
|
490
|
+
});
|
|
491
|
+
this.cacheAdapter?.setSlotCount(next);
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
461
494
|
/**
|
|
462
495
|
* Claim one physical llama-server request slot.
|
|
463
496
|
*
|
|
@@ -58,7 +58,7 @@ import { MlxEngineGate } from './engine-gate.js';
|
|
|
58
58
|
import { EngineHold, engineHoldLabelFor } from './engine-hold.js';
|
|
59
59
|
import { EngineLogRouter } from './engine-log-router.js';
|
|
60
60
|
import { MlxEngineStatsSampler } from './engine-stats-sampler.js';
|
|
61
|
-
import { REASONING_OPEN_HEADER, StreamingReasoningSplit, engineOpensReasoning, requestOpensReasoning, } from './reasoning-stream.js';
|
|
61
|
+
import { REASONING_OPEN_HEADER, StreamingReasoningSplit, engineOpensReasoning, reopenTemplateReasoning, requestOpensReasoning, } from './reasoning-stream.js';
|
|
62
62
|
import { applyMlxRequestShape } from './request-shape.js';
|
|
63
63
|
import { PRE_FIRST_BYTE_BASE_MS, buildMidStreamDropMessage, buildPreFirstByteAbortMessage, computePreFirstByteBudgetMs, estimatePromptTokens, formatTps, isMidStreamConnectionDrop, mlxHttpError, } from './runtime-diagnostics.js';
|
|
64
64
|
import { classifyMlxFatalErrorLine, classifyMlxStartupLine, } from './stdout-parser.js';
|
|
@@ -2159,7 +2159,7 @@ class MlxSession extends StreamingSessionBase {
|
|
|
2159
2159
|
// accumulated into `lastTurnReasoning` so the chat bubble can
|
|
2160
2160
|
// render it behind a collapsed expander.
|
|
2161
2161
|
{
|
|
2162
|
-
const split = extractReasoningWithProfile(turnContent, this.deps.profile);
|
|
2162
|
+
const split = extractReasoningWithProfile(reopenTemplateReasoning(turnContent, opensInReasoning), this.deps.profile);
|
|
2163
2163
|
turnContent = split.visible;
|
|
2164
2164
|
if (split.reasoning) {
|
|
2165
2165
|
this.lastTurnReasoning =
|
|
@@ -56,6 +56,21 @@ export declare function templateTextOpensReasoning(template: string): TemplateRe
|
|
|
56
56
|
* into the reasoning pane. Absent means on, matching the engine's default.
|
|
57
57
|
*/
|
|
58
58
|
export declare function requestOpensReasoning(templateOpen: TemplateReasoningOpen | undefined, body: Record<string, unknown>): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Restore the reasoning opener the chat template supplied, before the
|
|
61
|
+
* end-of-turn reasoning pass.
|
|
62
|
+
*
|
|
63
|
+
* When the rendered prompt left generation inside a reasoning block, the
|
|
64
|
+
* model's text starts mid-thought and carries only the closer. The live split
|
|
65
|
+
* knows that, but `extractReasoning` reads a lone `<channel|>` as stray and
|
|
66
|
+
* keeps the thoughts as the reply: Gemma 4 E4B, after a tool response, saved
|
|
67
|
+
* "The user wants to know…I need to answer in one sentence.The launch is on
|
|
68
|
+
* Friday…", and the stall check then asked it to answer again.
|
|
69
|
+
*
|
|
70
|
+
* Unchanged when the turn did not open in reasoning, when the model opened a
|
|
71
|
+
* block itself, or when no closer arrived.
|
|
72
|
+
*/
|
|
73
|
+
export declare function reopenTemplateReasoning(text: string, opensInReasoning: boolean): string;
|
|
59
74
|
export interface ReasoningSplitChunk {
|
|
60
75
|
visible: string;
|
|
61
76
|
reasoning: string;
|
|
@@ -112,6 +112,41 @@ export function requestOpensReasoning(templateOpen, body) {
|
|
|
112
112
|
return true;
|
|
113
113
|
return kwargs.enable_thinking !== false;
|
|
114
114
|
}
|
|
115
|
+
/** The opener `extractReasoning` pairs with each closer a template-opened turn emits. */
|
|
116
|
+
const OPENER_FOR_CLOSER = [
|
|
117
|
+
['</think>', '<think>'],
|
|
118
|
+
['</reasoning>', '<reasoning>'],
|
|
119
|
+
['[/THINK]', '[THINK]'],
|
|
120
|
+
['<channel|>', '<|channel>thought\n'],
|
|
121
|
+
['<|end|>', '<|channel|>analysis<|message|>'],
|
|
122
|
+
];
|
|
123
|
+
/**
|
|
124
|
+
* Restore the reasoning opener the chat template supplied, before the
|
|
125
|
+
* end-of-turn reasoning pass.
|
|
126
|
+
*
|
|
127
|
+
* When the rendered prompt left generation inside a reasoning block, the
|
|
128
|
+
* model's text starts mid-thought and carries only the closer. The live split
|
|
129
|
+
* knows that, but `extractReasoning` reads a lone `<channel|>` as stray and
|
|
130
|
+
* keeps the thoughts as the reply: Gemma 4 E4B, after a tool response, saved
|
|
131
|
+
* "The user wants to know…I need to answer in one sentence.The launch is on
|
|
132
|
+
* Friday…", and the stall check then asked it to answer again.
|
|
133
|
+
*
|
|
134
|
+
* Unchanged when the turn did not open in reasoning, when the model opened a
|
|
135
|
+
* block itself, or when no closer arrived.
|
|
136
|
+
*/
|
|
137
|
+
export function reopenTemplateReasoning(text, opensInReasoning) {
|
|
138
|
+
if (!opensInReasoning || /^\s*(?:<think>|<reasoning>|\[THINK\]|<\|?channel\|?>)/i.test(text)) {
|
|
139
|
+
return text;
|
|
140
|
+
}
|
|
141
|
+
const lower = text.toLowerCase();
|
|
142
|
+
let first = null;
|
|
143
|
+
for (const [closer, opener] of OPENER_FOR_CLOSER) {
|
|
144
|
+
const index = lower.indexOf(closer.toLowerCase());
|
|
145
|
+
if (index !== -1 && (first === null || index < first.index))
|
|
146
|
+
first = { index, opener };
|
|
147
|
+
}
|
|
148
|
+
return first ? `${first.opener}${text}` : text;
|
|
149
|
+
}
|
|
115
150
|
/**
|
|
116
151
|
* Incremental reasoning splitter for a streamed MLX turn.
|
|
117
152
|
*
|
|
@@ -814,14 +814,20 @@ export function resolveLlamaCppContextRequirement(opts) {
|
|
|
814
814
|
const strictModelMax = opts.contextSizing === 'model-max' && opts.explicitContextWindow === undefined;
|
|
815
815
|
const requestedContextWindow = opts.explicitContextWindow ??
|
|
816
816
|
(strictModelMax ? opts.modelContextWindow : opts.adaptiveContextWindow);
|
|
817
|
+
// Env, per-model and machine-wide numeric settings all express deliberate
|
|
818
|
+
// operator intent. The automatic host floor must not silently turn a 32K
|
|
819
|
+
// memory-saving request back into 64K. Resolve this here so launch and
|
|
820
|
+
// preview cannot apply different floors to the same explicit request.
|
|
821
|
+
const hostFloor = opts.minViableContextTokens ?? minViableLocalContextTokens();
|
|
822
|
+
const minimum = opts.explicitContextWindow !== undefined && opts.explicitContextWindow > 0
|
|
823
|
+
? Math.min(hostFloor, opts.explicitContextWindow)
|
|
824
|
+
: hostFloor;
|
|
817
825
|
const resolved = resolveLocalContextRequirement({
|
|
818
826
|
...(opts.modelContextWindow !== undefined
|
|
819
827
|
? { modelContextWindow: opts.modelContextWindow }
|
|
820
828
|
: {}),
|
|
821
829
|
...(requestedContextWindow !== undefined ? { requestedContextWindow } : {}),
|
|
822
|
-
|
|
823
|
-
? { minViableContextTokens: opts.minViableContextTokens }
|
|
824
|
-
: {}),
|
|
830
|
+
minViableContextTokens: minimum,
|
|
825
831
|
});
|
|
826
832
|
if (strictModelMax) {
|
|
827
833
|
return {
|
|
@@ -223,11 +223,19 @@ export class LocalEngineRuntime {
|
|
|
223
223
|
return null;
|
|
224
224
|
if (this.engineRouterInitPromise)
|
|
225
225
|
return this.engineRouterInitPromise;
|
|
226
|
-
|
|
226
|
+
const pending = this.buildEngineRouter().then((r) => {
|
|
227
227
|
this.engineRouterCache = r;
|
|
228
228
|
return r;
|
|
229
|
+
}, (err) => {
|
|
230
|
+
// A failed build must not be cached. One that ran while a dev rebuild
|
|
231
|
+
// had emptied dist/ failed every later local turn with "Cannot find
|
|
232
|
+
// module …engine-router.js" until the app restarted (2026-10-19).
|
|
233
|
+
if (this.engineRouterInitPromise === pending)
|
|
234
|
+
this.engineRouterInitPromise = null;
|
|
235
|
+
throw err;
|
|
229
236
|
});
|
|
230
|
-
|
|
237
|
+
this.engineRouterInitPromise = pending;
|
|
238
|
+
return pending;
|
|
231
239
|
}
|
|
232
240
|
async buildEngineRouter() {
|
|
233
241
|
const { EngineRouter } = await import('./engine-router.js');
|
|
@@ -1136,17 +1144,12 @@ export class LocalEngineRuntime {
|
|
|
1136
1144
|
const manifestEngineConfig = await resolveCatalogLlamaCppEngineConfig(this.catalog, modelId);
|
|
1137
1145
|
const perModelCtxOverride = config.modelContextOverrides?.[`llama-cpp:${modelId}`];
|
|
1138
1146
|
const explicitContextWindow = envNumCtx ?? perModelCtxOverride ?? config.llamaCppNumCtx;
|
|
1139
|
-
//
|
|
1140
|
-
//
|
|
1141
|
-
// the request back to 64K. Env / machine-wide values keep their
|
|
1142
|
-
// historical floor semantics. Mirrors buildLlamaCppProvider.
|
|
1147
|
+
// The shared resolver honors every explicit numeric context below the
|
|
1148
|
+
// host floor. Keep per-model override state for the Settings auto marker.
|
|
1143
1149
|
const overrideActive = perModelCtxOverride !== undefined && explicitContextWindow === perModelCtxOverride;
|
|
1144
|
-
const minViableTokens = overrideActive
|
|
1145
|
-
? Math.min(contextFloor, perModelCtxOverride)
|
|
1146
|
-
: contextFloor;
|
|
1147
1150
|
const contextRequirement = resolveLlamaCppContextRequirement({
|
|
1148
1151
|
modelContextWindow: installed.contextWindow,
|
|
1149
|
-
minViableContextTokens:
|
|
1152
|
+
minViableContextTokens: contextFloor,
|
|
1150
1153
|
...(explicitContextWindow !== undefined ? { explicitContextWindow } : {}),
|
|
1151
1154
|
...(manifestEngineConfig?.contextSize !== undefined
|
|
1152
1155
|
? {
|
|
@@ -1162,7 +1165,7 @@ export class LocalEngineRuntime {
|
|
|
1162
1165
|
// per-model override deliberately does NOT short-circuit on a resident
|
|
1163
1166
|
// engine: the full plan must run so a stale resident window surfaces as
|
|
1164
1167
|
// restart-required instead of masquerading as the applied setting.
|
|
1165
|
-
const shortCircuit =
|
|
1168
|
+
const shortCircuit = residentContextWindow !== undefined && !overrideActive;
|
|
1166
1169
|
const { defaultLocalEngineSlots, estimatePerSlotKvBytes, kvQuantScale, llamaCppSlotCeiling, planAdaptiveContextGrowth, planCtxTokensForMemory, plannedLocalEngineSlots, } = await import('./capacity-broker.js');
|
|
1167
1170
|
const capacity = await this.previewCapacityInputs('llama-cpp', modelId, capacityOpts);
|
|
1168
1171
|
const { budgetBytes: admissionBudgetBytes, fastBudgetBytes, committedOtherBytes } = capacity;
|
|
@@ -1587,7 +1590,7 @@ export class LocalEngineRuntime {
|
|
|
1587
1590
|
}
|
|
1588
1591
|
return { grantedCtx, slots, kvCacheType: kv, plannedFieldsOk: true };
|
|
1589
1592
|
};
|
|
1590
|
-
const plan = llamaPlanPass(explicitContextWindow,
|
|
1593
|
+
const plan = llamaPlanPass(explicitContextWindow, contextFloor);
|
|
1591
1594
|
let autoContextWindow;
|
|
1592
1595
|
if (overrideActive) {
|
|
1593
1596
|
try {
|
|
@@ -55,7 +55,9 @@ const RESTART_WINDOW_MS = 60_000;
|
|
|
55
55
|
/**
|
|
56
56
|
* Ceiling on `recoverStartup` retries within one start. Two covers the
|
|
57
57
|
* full MoE degradation ladder (partial split → all experts to RAM →
|
|
58
|
-
* engine-owned fit); a hook that keeps
|
|
58
|
+
* engine-owned fit), or one slot and one offload rung; a hook that keeps
|
|
59
|
+
* asking past that is looping. Every failed start also spends the restart
|
|
60
|
+
* budget, which refuses a fourth start inside its window anyway.
|
|
59
61
|
*/
|
|
60
62
|
const MAX_STARTUP_RECOVERIES = 2;
|
|
61
63
|
const HEALTH_FAIL_THRESHOLD = 3;
|
|
@@ -5,11 +5,18 @@
|
|
|
5
5
|
* service's import point and keeps the one piece that reads the process
|
|
6
6
|
* environment.
|
|
7
7
|
*/
|
|
8
|
-
export { AbortedWhileQueuedError, DEFAULT_AMBIENT_QUIET_MS, ProviderQueue, QUEUE_WAIT_NOTICE_DELAY_MS, QUEUE_WAIT_NOTICE_REPEAT_MS, backgroundLaneCap, runInQueue, } from '@bendyline/gezel/runtime';
|
|
9
|
-
export type { EnqueueRequest, Lane, ProviderQueueOptions, QueueSnapshot, QueueWaitOpts, } from '@bendyline/gezel/runtime';
|
|
8
|
+
export { AbortedWhileQueuedError, DEFAULT_AMBIENT_QUIET_MS, DEFAULT_LOCAL_YIELD_QUANTUM_MS, ProviderQueue, QUEUE_WAIT_NOTICE_DELAY_MS, QUEUE_WAIT_NOTICE_REPEAT_MS, backgroundLaneCap, runInQueue, } from '@bendyline/gezel/runtime';
|
|
9
|
+
export type { EnqueueRequest, Lane, ProviderQueueOptions, QueueLease, QueueSnapshot, QueueWaitOpts, } from '@bendyline/gezel/runtime';
|
|
10
10
|
/**
|
|
11
11
|
* Ambient quiet window for local engine queues. Override with
|
|
12
12
|
* `GEZEL_AMBIENT_QUIET_MS` (milliseconds; `0` disables ambient gating
|
|
13
13
|
* entirely).
|
|
14
14
|
*/
|
|
15
15
|
export declare function defaultAmbientQuietMs(): number;
|
|
16
|
+
/**
|
|
17
|
+
* How long a local turn holds its queue slot before it hands the slot to
|
|
18
|
+
* another conversation that is waiting. Override with
|
|
19
|
+
* `GEZEL_QUEUE_YIELD_QUANTUM_MS` (milliseconds; `0` keeps a slot for the
|
|
20
|
+
* whole turn).
|
|
21
|
+
*/
|
|
22
|
+
export declare function defaultQueueYieldQuantumMs(): number;
|
package/dist/providers/queue.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* service's import point and keeps the one piece that reads the process
|
|
6
6
|
* environment.
|
|
7
7
|
*/
|
|
8
|
-
import { DEFAULT_AMBIENT_QUIET_MS } from '@bendyline/gezel/runtime';
|
|
9
|
-
export { AbortedWhileQueuedError, DEFAULT_AMBIENT_QUIET_MS, ProviderQueue, QUEUE_WAIT_NOTICE_DELAY_MS, QUEUE_WAIT_NOTICE_REPEAT_MS, backgroundLaneCap, runInQueue, } from '@bendyline/gezel/runtime';
|
|
8
|
+
import { DEFAULT_AMBIENT_QUIET_MS, DEFAULT_LOCAL_YIELD_QUANTUM_MS } from '@bendyline/gezel/runtime';
|
|
9
|
+
export { AbortedWhileQueuedError, DEFAULT_AMBIENT_QUIET_MS, DEFAULT_LOCAL_YIELD_QUANTUM_MS, ProviderQueue, QUEUE_WAIT_NOTICE_DELAY_MS, QUEUE_WAIT_NOTICE_REPEAT_MS, backgroundLaneCap, runInQueue, } from '@bendyline/gezel/runtime';
|
|
10
10
|
/**
|
|
11
11
|
* Ambient quiet window for local engine queues. Override with
|
|
12
12
|
* `GEZEL_AMBIENT_QUIET_MS` (milliseconds; `0` disables ambient gating
|
|
@@ -21,3 +21,18 @@ export function defaultAmbientQuietMs() {
|
|
|
21
21
|
}
|
|
22
22
|
return DEFAULT_AMBIENT_QUIET_MS;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* How long a local turn holds its queue slot before it hands the slot to
|
|
26
|
+
* another conversation that is waiting. Override with
|
|
27
|
+
* `GEZEL_QUEUE_YIELD_QUANTUM_MS` (milliseconds; `0` keeps a slot for the
|
|
28
|
+
* whole turn).
|
|
29
|
+
*/
|
|
30
|
+
export function defaultQueueYieldQuantumMs() {
|
|
31
|
+
const raw = process.env.GEZEL_QUEUE_YIELD_QUANTUM_MS;
|
|
32
|
+
if (raw !== undefined) {
|
|
33
|
+
const parsed = Number.parseInt(raw, 10);
|
|
34
|
+
if (Number.isFinite(parsed) && parsed >= 0)
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
return DEFAULT_LOCAL_YIELD_QUANTUM_MS;
|
|
38
|
+
}
|
|
@@ -22,5 +22,8 @@ export interface ToolGroundingHooks {
|
|
|
22
22
|
kind: 'allow';
|
|
23
23
|
args: Record<string, unknown>;
|
|
24
24
|
} | null;
|
|
25
|
-
|
|
25
|
+
/** `source.thirdParty` is true for a tool served by any server but gezel's own. */
|
|
26
|
+
labelEvidence(toolName: string, args: Record<string, unknown>, text: string, source?: {
|
|
27
|
+
thirdParty: boolean;
|
|
28
|
+
}): string | null;
|
|
26
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/gezel-inference",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Gezel's on-device inference runtime: native engines, local model management, embeddings, knowledge search, and OpenAI-compatible routes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -73,10 +73,10 @@
|
|
|
73
73
|
"!dist/**/__pycache__"
|
|
74
74
|
],
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@bendyline/gezel": "1.2.
|
|
77
|
-
"@bendyline/gezel-catalog": "1.2.
|
|
78
|
-
"@bendyline/gezel-client": "1.2.
|
|
79
|
-
"@bendyline/gezel-knowledge": "1.2.
|
|
76
|
+
"@bendyline/gezel": "1.2.8",
|
|
77
|
+
"@bendyline/gezel-catalog": "1.2.8",
|
|
78
|
+
"@bendyline/gezel-client": "1.2.8",
|
|
79
|
+
"@bendyline/gezel-knowledge": "1.2.8",
|
|
80
80
|
"hono": "4.13.9",
|
|
81
81
|
"tar": "7.5.21",
|
|
82
82
|
"undici": "8.11.2",
|