@bendyline/gezel-inference 1.0.1 → 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 +483 -413
- 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/openai-compat/engine-failure.d.ts +25 -0
- package/dist/http/openai-compat/engine-failure.js +76 -0
- package/dist/http/openai-compat/responses-streaming.js +2 -1
- package/dist/http/openai-compat/responses-translate.d.ts +1 -1
- package/dist/http/openai-compat/translate.d.ts +1 -1
- package/dist/http/routes/engines.js +35 -0
- package/dist/http/routes/v1-chat.js +11 -2
- package/dist/http/routes/v1-knowledge.js +4 -0
- package/dist/http/routes/v1-models.js +6 -4
- package/dist/http/routes/v1-responses.d.ts +1 -1
- package/dist/http/routes/v1-responses.js +7 -1
- package/dist/inference-service.d.ts +6 -1
- package/dist/inference-service.js +20 -9
- package/dist/knowledge/catalog-host.d.ts +9 -1
- package/dist/knowledge/catalog-host.js +2 -0
- package/dist/knowledge/manager.d.ts +21 -0
- package/dist/knowledge/manager.js +85 -7
- package/dist/knowledge/search-worker.d.ts +1 -1
- package/dist/knowledge/spatial-query.d.ts +2 -3
- package/dist/knowledge/spatial-query.js +12 -6
- package/dist/knowledge/worker-host.js +2 -0
- package/dist/memory/embed-core.d.ts +16 -0
- package/dist/memory/embed-core.js +31 -0
- package/dist/memory/embed-worker.d.ts +7 -3
- package/dist/memory/embed-worker.js +13 -4
- package/dist/memory/embeddings.d.ts +17 -2
- package/dist/memory/embeddings.js +36 -7
- package/dist/models/ensure.d.ts +2 -0
- package/dist/models/ensure.js +9 -0
- package/dist/perf/responsiveness.d.ts +14 -1
- package/dist/perf/responsiveness.js +81 -10
- package/dist/providers/audio/kokoro-frontend.js +1 -1
- package/dist/providers/ds4/build-provider.js +6 -0
- package/dist/providers/llama-cpp/backend-health.d.ts +30 -0
- package/dist/providers/llama-cpp/backend-health.js +64 -0
- package/dist/providers/llama-cpp/backend-quarantine.d.ts +40 -0
- package/dist/providers/llama-cpp/backend-quarantine.js +100 -0
- package/dist/providers/llama-cpp/build-provider.js +247 -199
- package/dist/providers/llama-cpp/cache-adapter.d.ts +7 -1
- package/dist/providers/llama-cpp/cache-adapter.js +11 -1
- package/dist/providers/llama-cpp/devices.d.ts +22 -2
- package/dist/providers/llama-cpp/devices.js +48 -5
- package/dist/providers/llama-cpp/engine-flags.d.ts +0 -18
- package/dist/providers/llama-cpp/engine-flags.js +12 -18
- package/dist/providers/llama-cpp/gguf-metadata.js +20 -3
- package/dist/providers/llama-cpp/gpu-startup-watch.d.ts +41 -0
- package/dist/providers/llama-cpp/gpu-startup-watch.js +58 -0
- package/dist/providers/llama-cpp/models.d.ts +6 -0
- package/dist/providers/llama-cpp/models.js +23 -2
- package/dist/providers/llama-cpp/offload-planner.d.ts +26 -18
- package/dist/providers/llama-cpp/offload-planner.js +30 -14
- package/dist/providers/llama-cpp/provider.d.ts +22 -3
- package/dist/providers/llama-cpp/provider.js +39 -3
- package/dist/providers/mlx/provider.js +5 -6
- package/dist/providers/mlx/python/qwen4_ple.py +9 -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.d.ts +21 -15
- package/dist/providers/native/capacity-broker.js +25 -13
- package/dist/providers/native/capacity-budget.d.ts +35 -3
- package/dist/providers/native/capacity-budget.js +49 -5
- package/dist/providers/native/device-capacity.d.ts +1 -1
- package/dist/providers/native/device-capacity.js +23 -5
- package/dist/providers/native/local-engine-runtime.js +31 -31
- package/dist/providers/native/measured-budget.js +2 -0
- package/dist/providers/native/supervisor.d.ts +25 -0
- package/dist/providers/native/supervisor.js +62 -8
- 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/dist/system/amdgpu-adapters.d.ts +18 -0
- package/dist/system/amdgpu-adapters.js +55 -0
- package/dist/system/gpu-carve-out.d.ts +84 -0
- package/dist/system/gpu-carve-out.js +111 -0
- package/dist/system/memory.d.ts +17 -1
- package/dist/system/memory.js +94 -13
- package/dist/transformers-cache.d.ts +2 -0
- package/dist/transformers-cache.js +6 -3
- package/package.json +5 -5
|
@@ -13,7 +13,7 @@ import { readFile, readdir, rm, stat } from 'node:fs/promises';
|
|
|
13
13
|
import { dirname, join, resolve } from 'node:path';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import { KnowledgeMachineInventorySchema, KnowledgeNearbyRequestSchema, createLogger, formatKnowledgeUri, parseKnowledgeUri, resolveSecurityPolicy, sameVectorSpace, } from '@bendyline/gezel';
|
|
16
|
-
import { compareCatalogVersions, knowledgeEmbeddingProfile, readGezkManifest, } from '@bendyline/gezel-knowledge';
|
|
16
|
+
import { compareCatalogVersions, knowledgeEmbeddingProfile, knowledgeProfileQueryModelBytes, missingPinnedFiles, readGezkManifest, } from '@bendyline/gezel-knowledge';
|
|
17
17
|
import { knowledgeCatalogVersionDir, knowledgeCatalogsDir, knowledgeDownloadsDir, } from '@bendyline/gezel/paths';
|
|
18
18
|
import { HANDBOEK_KNOWLEDGE_CATALOG, HANDBOEK_KNOWLEDGE_PUBLISHER } from './bundled-catalog.js';
|
|
19
19
|
import { scoreResult } from '@bendyline/gezel';
|
|
@@ -21,10 +21,11 @@ import { sharedKnowledgeRoot, sharedKnowledgeVersionDir, } from '../machine-engi
|
|
|
21
21
|
import { embedKnowledgeQuery, sharesDaemonEmbedder } from '../memory/embeddings.js';
|
|
22
22
|
import { ChatModelInstallRegistry } from '../models/install-registry.js';
|
|
23
23
|
import { beginPerfWork } from '../perf/responsiveness.js';
|
|
24
|
+
import { HF_CACHE_DIR_ENV, transformersCacheDir } from '../transformers-cache.js';
|
|
24
25
|
import { resolveKnowledgeCatalogSource } from './catalog-source.js';
|
|
25
26
|
import { KNOWLEDGE_DOWNLOAD_KEY_PATTERN, installKnowledgeCatalog, knowledgeDownloadKey, pruneOtherKnowledgeCatalogVersions, } from './install.js';
|
|
26
27
|
import { KnowledgeRegistry } from './registry.js';
|
|
27
|
-
import { KnowledgeSpatialCursorError, encodeNearbyCursor, nearbyCursorOffset,
|
|
28
|
+
import { KnowledgeSpatialCursorError, encodeNearbyCursor, nearbyCursorOffset, spatialDocumentIdentity, } from './spatial-query.js';
|
|
28
29
|
import { resolveKnowledgeVectorFloors } from './vector-floors.js';
|
|
29
30
|
const log = createLogger('knowledge');
|
|
30
31
|
/** Explicit-search shard budget (S) across every active catalog (§3.4). */
|
|
@@ -59,6 +60,9 @@ function semanticSearchModeFor(profile) {
|
|
|
59
60
|
return 'profile';
|
|
60
61
|
return 'keyword-only';
|
|
61
62
|
}
|
|
63
|
+
function searchModelOf(profile) {
|
|
64
|
+
return { profileId: profile.id, repo: profile.model.repo };
|
|
65
|
+
}
|
|
62
66
|
/**
|
|
63
67
|
* Explicit search fuses three arms per catalog by reciprocal rank (k = 60):
|
|
64
68
|
* the vector chunks (best chunk per document, by rerank cosine), the
|
|
@@ -409,10 +413,18 @@ export class KnowledgeManager {
|
|
|
409
413
|
const shared = await this.sharedInventoryKeys();
|
|
410
414
|
const activeIds = new Set(this.installs.active().map((a) => a.id));
|
|
411
415
|
const partials = await this.partialDownloadKeys();
|
|
416
|
+
const modelBytes = new Map();
|
|
417
|
+
for (const item of gilde.values()) {
|
|
418
|
+
const profileId = item.embeddingProfile.id;
|
|
419
|
+
if (!modelBytes.has(profileId)) {
|
|
420
|
+
modelBytes.set(profileId, await this.searchModelDownloadBytes(profileId));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
412
423
|
return [...gilde.values()]
|
|
413
424
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
414
425
|
.map((item) => {
|
|
415
426
|
const entry = registry.find((c) => c.ref.publisherId === item.publisherId && c.ref.catalogId === item.id);
|
|
427
|
+
const searchModelDownloadBytes = modelBytes.get(item.embeddingProfile.id);
|
|
416
428
|
return {
|
|
417
429
|
id: item.id,
|
|
418
430
|
publisherId: item.publisherId,
|
|
@@ -451,9 +463,24 @@ export class KnowledgeManager {
|
|
|
451
463
|
sharedOnDevice: shared.has(`${item.publisherId}/${item.id}/${item.version}/${item.sha256}`),
|
|
452
464
|
installing: activeIds.has(item.id),
|
|
453
465
|
incompleteDownload: partials.has(item.sha256.slice(0, 16)),
|
|
466
|
+
...(searchModelDownloadBytes ? { searchModelDownloadBytes } : {}),
|
|
454
467
|
};
|
|
455
468
|
});
|
|
456
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* The search model an install of a catalog with this profile would also
|
|
472
|
+
* download. None when the catalog searches with the daemon's own embedder
|
|
473
|
+
* (an install never fetches that) or the profile's pinned files are cached.
|
|
474
|
+
*/
|
|
475
|
+
async searchModelDownloadBytes(profileId) {
|
|
476
|
+
const profile = knowledgeEmbeddingProfile(profileId);
|
|
477
|
+
const bytes = knowledgeProfileQueryModelBytes(profileId);
|
|
478
|
+
if (!profile || bytes === null || semanticSearchModeFor(profile) !== 'profile')
|
|
479
|
+
return undefined;
|
|
480
|
+
const cacheDir = process.env[HF_CACHE_DIR_ENV] ?? transformersCacheDir(this.opts.home);
|
|
481
|
+
const missing = await missingPinnedFiles(profile, { cacheDir });
|
|
482
|
+
return missing.length > 0 ? bytes : undefined;
|
|
483
|
+
}
|
|
457
484
|
/** Resolve a catalogId (URI authority) to its mounted info, or null. */
|
|
458
485
|
mountedCatalog(catalogId) {
|
|
459
486
|
for (const info of this.mountedByKey.values()) {
|
|
@@ -676,7 +703,13 @@ export class KnowledgeManager {
|
|
|
676
703
|
let warning;
|
|
677
704
|
const mounted = this.mountedByKey.get(key);
|
|
678
705
|
if (mounted?.semanticSearch === 'profile') {
|
|
679
|
-
yield {
|
|
706
|
+
yield {
|
|
707
|
+
type: 'progress',
|
|
708
|
+
phase: 'embedder',
|
|
709
|
+
bytesDone: 0,
|
|
710
|
+
bytesTotal: 0,
|
|
711
|
+
searchModel: searchModelOf(mounted.embedding),
|
|
712
|
+
};
|
|
680
713
|
warning = yield* this.prewarmProfile(mounted.embedding);
|
|
681
714
|
}
|
|
682
715
|
yield { ...event, ...(warning ? { warning } : {}) };
|
|
@@ -746,7 +779,12 @@ export class KnowledgeManager {
|
|
|
746
779
|
(settled || Date.now() - lastReportAt >= EMBEDDER_PROGRESS_INTERVAL_MS)) {
|
|
747
780
|
progress.reported = latest;
|
|
748
781
|
lastReportAt = Date.now();
|
|
749
|
-
yield {
|
|
782
|
+
yield {
|
|
783
|
+
type: 'progress',
|
|
784
|
+
phase: 'embedder',
|
|
785
|
+
...latest,
|
|
786
|
+
searchModel: searchModelOf(profile),
|
|
787
|
+
};
|
|
750
788
|
continue;
|
|
751
789
|
}
|
|
752
790
|
if (settled)
|
|
@@ -802,6 +840,9 @@ export class KnowledgeManager {
|
|
|
802
840
|
phase,
|
|
803
841
|
bytesDone: lastEvent?.type === 'progress' ? lastEvent.bytesDone : 0,
|
|
804
842
|
bytesTotal: lastEvent?.type === 'progress' ? lastEvent.bytesTotal : 0,
|
|
843
|
+
...(lastEvent?.type === 'progress' && lastEvent.searchModel
|
|
844
|
+
? { searchModel: lastEvent.searchModel }
|
|
845
|
+
: {}),
|
|
805
846
|
};
|
|
806
847
|
});
|
|
807
848
|
}
|
|
@@ -1035,6 +1076,12 @@ export class KnowledgeManager {
|
|
|
1035
1076
|
async documentsPage(catalogId, opts) {
|
|
1036
1077
|
return this.opts.host.documentsPage(this.requireMounted(catalogId).key, opts);
|
|
1037
1078
|
}
|
|
1079
|
+
async documentInitials(catalogId, opts) {
|
|
1080
|
+
return this.opts.host.documentInitials(this.requireMounted(catalogId).key, opts);
|
|
1081
|
+
}
|
|
1082
|
+
async documentPosition(catalogId, documentId, opts) {
|
|
1083
|
+
return this.opts.host.documentPosition(this.requireMounted(catalogId).key, documentId, opts);
|
|
1084
|
+
}
|
|
1038
1085
|
async getDocument(catalogId, documentId) {
|
|
1039
1086
|
return this.opts.host.getDocument(this.requireMounted(catalogId).key, documentId);
|
|
1040
1087
|
}
|
|
@@ -1145,7 +1192,7 @@ export class KnowledgeManager {
|
|
|
1145
1192
|
documentId: document.id,
|
|
1146
1193
|
}),
|
|
1147
1194
|
};
|
|
1148
|
-
const identity =
|
|
1195
|
+
const identity = spatialDocumentIdentity(info.ref.publisherId, info.ref.catalogId, document.id);
|
|
1149
1196
|
const previous = unique.get(identity);
|
|
1150
1197
|
if (!previous || result.distanceMeters < previous.distanceMeters)
|
|
1151
1198
|
unique.set(identity, result);
|
|
@@ -1268,6 +1315,20 @@ export class KnowledgeManager {
|
|
|
1268
1315
|
}
|
|
1269
1316
|
fused.set(key, entry);
|
|
1270
1317
|
};
|
|
1318
|
+
// Every text passage the arms found, per document, best vector match first,
|
|
1319
|
+
// for `chunksPerDocument`. Fusion still ranks documents, not passages.
|
|
1320
|
+
const passagesByDocument = new Map();
|
|
1321
|
+
if ((opts.chunksPerDocument ?? 1) > 1) {
|
|
1322
|
+
for (const hit of response.chunks
|
|
1323
|
+
.filter((h) => !h.media)
|
|
1324
|
+
.sort((a, b) => (b.cosine ?? -1) - (a.cosine ?? -1))) {
|
|
1325
|
+
const key = `${hit.catalogKey}\u0000${hit.documentId}`;
|
|
1326
|
+
const passages = passagesByDocument.get(key) ?? [];
|
|
1327
|
+
if (!passages.some((known) => known.chunkUid === hit.chunkUid))
|
|
1328
|
+
passages.push(hit);
|
|
1329
|
+
passagesByDocument.set(key, passages);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1271
1332
|
// Vector arm: one rank per document, in rerank-cosine order. A hit under
|
|
1272
1333
|
// its profile's floor is not evidence at all — KNN always returns its
|
|
1273
1334
|
// nearest rows, and a rank-0 row fuses to relevance 1.0 however far away
|
|
@@ -1383,7 +1444,7 @@ export class KnowledgeManager {
|
|
|
1383
1444
|
const count = perCatalogCount.get(entry.catalogKey) ?? 0;
|
|
1384
1445
|
if (capPerCatalog && count >= PER_CATALOG_CAP)
|
|
1385
1446
|
continue;
|
|
1386
|
-
const spatialIdentity =
|
|
1447
|
+
const spatialIdentity = spatialDocumentIdentity(info.ref.publisherId, info.ref.catalogId, entry.documentId);
|
|
1387
1448
|
if (opts.spatial && seenSpatial.has(spatialIdentity))
|
|
1388
1449
|
continue;
|
|
1389
1450
|
const relevance = fusedRankRelevance(out.length);
|
|
@@ -1399,7 +1460,24 @@ export class KnowledgeManager {
|
|
|
1399
1460
|
continue;
|
|
1400
1461
|
perCatalogCount.set(entry.catalogKey, count + 1);
|
|
1401
1462
|
seenSpatial.add(spatialIdentity);
|
|
1402
|
-
|
|
1463
|
+
const docKey = `${entry.catalogKey}\u0000${entry.documentId}`;
|
|
1464
|
+
out.push({ ...result, ...matches.get(docKey) });
|
|
1465
|
+
if (out.length >= Math.max(10, opts.maxResults))
|
|
1466
|
+
break;
|
|
1467
|
+
// The document's other passages, each labelled by its own evidence: a
|
|
1468
|
+
// sibling is vector evidence only if it clears the floor by itself.
|
|
1469
|
+
const siblings = (passagesByDocument.get(docKey) ?? [])
|
|
1470
|
+
.filter((hit) => hit.chunkUid !== entry.chunk?.chunkUid)
|
|
1471
|
+
.slice(0, Math.max(0, (opts.chunksPerDocument ?? 1) - 1));
|
|
1472
|
+
const floor = this.vectorFloorFor(entry.catalogKey);
|
|
1473
|
+
for (const sibling of siblings) {
|
|
1474
|
+
const own = floor !== null && sibling.cosine !== undefined && sibling.cosine >= floor
|
|
1475
|
+
? { arm: 'vector', similarity: sibling.cosine }
|
|
1476
|
+
: { arm: 'fts' };
|
|
1477
|
+
out.push(this.toResult(info, sibling, fusedRankRelevance(out.length), own));
|
|
1478
|
+
if (out.length >= Math.max(10, opts.maxResults))
|
|
1479
|
+
break;
|
|
1480
|
+
}
|
|
1403
1481
|
if (out.length >= Math.max(10, opts.maxResults))
|
|
1404
1482
|
break;
|
|
1405
1483
|
}
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export interface KnowledgeWorkerRequest {
|
|
14
14
|
id: number;
|
|
15
|
-
op: 'mount' | 'unmount' | 'mounted' | 'prewarm' | 'validate' | 'topics' | 'documentsPage' | 'nearbyDocuments' | 'getDocument' | 'getChunk' | 'assets' | 'readAsset' | 'assetFile' | 'search' | 'dispose';
|
|
15
|
+
op: 'mount' | 'unmount' | 'mounted' | 'prewarm' | 'validate' | 'topics' | 'documentsPage' | 'documentInitials' | 'documentPosition' | 'nearbyDocuments' | 'getDocument' | 'getChunk' | 'assets' | 'readAsset' | 'assetFile' | 'search' | 'dispose';
|
|
16
16
|
args: unknown[];
|
|
17
17
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Radius pagination binds offsets to the authorized catalog snapshot and
|
|
3
3
|
* query. Cursors confer no authority; the manager recalculates scope on
|
|
4
|
-
* every request.
|
|
5
|
-
* declared regional catalog and source-document naming convention.
|
|
4
|
+
* every request.
|
|
6
5
|
*/
|
|
7
6
|
export declare class KnowledgeSpatialCursorError extends Error {
|
|
8
7
|
}
|
|
9
8
|
export declare function encodeNearbyCursor(snapshot: string, offset: number): string;
|
|
10
9
|
export declare function nearbyCursorOffset(cursor: string | undefined, snapshot: string): number;
|
|
11
|
-
export declare function
|
|
10
|
+
export declare function spatialDocumentIdentity(publisherId: string, catalogId: string, documentId: string): string;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Radius pagination binds offsets to the authorized catalog snapshot and
|
|
3
3
|
* query. Cursors confer no authority; the manager recalculates scope on
|
|
4
|
-
* every request.
|
|
5
|
-
* declared regional catalog and source-document naming convention.
|
|
4
|
+
* every request.
|
|
6
5
|
*/
|
|
7
6
|
export class KnowledgeSpatialCursorError extends Error {
|
|
8
7
|
}
|
|
@@ -24,10 +23,17 @@ export function nearbyCursorOffset(cursor, snapshot) {
|
|
|
24
23
|
throw new KnowledgeSpatialCursorError('Invalid or stale nearby cursor; restart the query.');
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Source-article ids (`wikipedia-<page-id>`) name the same article in every
|
|
28
|
+
* catalog of one publisher, and regional catalogs overlap heavily — Pacific
|
|
29
|
+
* Northwest and Canada & Alaska share 683 of the former's 1,106 articles — so
|
|
30
|
+
* those copies are one result. Any other id is unique only within its catalog.
|
|
31
|
+
* Keyed on the id, not the catalog name: an earlier catalog-name match never
|
|
32
|
+
* fired for the published `regional-*` catalogs, so every copy came through.
|
|
33
|
+
*/
|
|
34
|
+
const SOURCE_ARTICLE_ID = /^(wikipedia|wikivoyage)-[0-9]+$/;
|
|
35
|
+
export function spatialDocumentIdentity(publisherId, catalogId, documentId) {
|
|
36
|
+
return SOURCE_ARTICLE_ID.test(documentId)
|
|
31
37
|
? `${publisherId}\u0000${documentId}`
|
|
32
38
|
: `${publisherId}\u0000${catalogId}\u0000${documentId}`;
|
|
33
39
|
}
|
|
@@ -141,6 +141,8 @@ export function createWorkerCatalogHost() {
|
|
|
141
141
|
documentsPage: async (key, opts) =>
|
|
142
142
|
// biome-ignore lint/suspicious/noExplicitAny: structured-clone round trip preserves the shape
|
|
143
143
|
(await call('documentsPage', [key, opts])),
|
|
144
|
+
documentInitials: async (key, opts) => (await call('documentInitials', [key, opts])),
|
|
145
|
+
documentPosition: async (key, documentId, opts) => (await call('documentPosition', [key, documentId, opts])),
|
|
144
146
|
nearbyDocuments: async (key, radius, opts) => (await call('nearbyDocuments', [key, radius, opts])),
|
|
145
147
|
getDocument: async (key, documentId) =>
|
|
146
148
|
// biome-ignore lint/suspicious/noExplicitAny: structured-clone round trip preserves the shape
|
|
@@ -106,6 +106,22 @@ export declare function isRetryablePipelineLoadFailure(error: unknown): boolean;
|
|
|
106
106
|
* will not parse is a runtime problem instead, and retrying cannot fix it.
|
|
107
107
|
*/
|
|
108
108
|
export declare function isFreshDownloadFailure(error: unknown, graphVerified: boolean): boolean;
|
|
109
|
+
/** Work abandoned because the host asked, at service shutdown. Not a model fault. */
|
|
110
|
+
export declare class ModelWorkCancelledError extends Error {
|
|
111
|
+
constructor();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Abandon the model download in flight and stop embedding runs at their next
|
|
115
|
+
* sub-batch, so the worker reaches idle and can be terminated safely.
|
|
116
|
+
*
|
|
117
|
+
* Shutdown cannot simply terminate a busy worker (that aborts the process
|
|
118
|
+
* inside onnxruntime), and a first-run model download outlasted every drain:
|
|
119
|
+
* quitting during it held the app for 30 s, until the quit timeout. A download
|
|
120
|
+
* is plain network I/O, outside onnxruntime, so stopping it is safe; session
|
|
121
|
+
* creation and a single sub-batch still finish. Work that starts afterwards
|
|
122
|
+
* gets a fresh signal.
|
|
123
|
+
*/
|
|
124
|
+
export declare function cancelInFlightModelWork(): void;
|
|
109
125
|
/** Lazily create the feature-extraction pipeline; cached for the process. */
|
|
110
126
|
export declare function loadPipeline(): Promise<Pipeline>;
|
|
111
127
|
/**
|
|
@@ -191,6 +191,29 @@ export function isFreshDownloadFailure(error, graphVerified) {
|
|
|
191
191
|
/** Fresh starts after a failed download, per process, before vector memory is switched off. */
|
|
192
192
|
const MAX_FRESH_DOWNLOAD_RETRIES = 3;
|
|
193
193
|
let freshDownloadFailures = 0;
|
|
194
|
+
/** Work abandoned because the host asked, at service shutdown. Not a model fault. */
|
|
195
|
+
export class ModelWorkCancelledError extends Error {
|
|
196
|
+
constructor() {
|
|
197
|
+
super('model work was cancelled for shutdown');
|
|
198
|
+
this.name = 'ModelWorkCancelledError';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
let cancelController = new AbortController();
|
|
202
|
+
/**
|
|
203
|
+
* Abandon the model download in flight and stop embedding runs at their next
|
|
204
|
+
* sub-batch, so the worker reaches idle and can be terminated safely.
|
|
205
|
+
*
|
|
206
|
+
* Shutdown cannot simply terminate a busy worker (that aborts the process
|
|
207
|
+
* inside onnxruntime), and a first-run model download outlasted every drain:
|
|
208
|
+
* quitting during it held the app for 30 s, until the quit timeout. A download
|
|
209
|
+
* is plain network I/O, outside onnxruntime, so stopping it is safe; session
|
|
210
|
+
* creation and a single sub-batch still finish. Work that starts afterwards
|
|
211
|
+
* gets a fresh signal.
|
|
212
|
+
*/
|
|
213
|
+
export function cancelInFlightModelWork() {
|
|
214
|
+
cancelController.abort();
|
|
215
|
+
cancelController = new AbortController();
|
|
216
|
+
}
|
|
194
217
|
/** The daemon pin's ONNX graph, when it names one with a digest. */
|
|
195
218
|
function pinnedGraph() {
|
|
196
219
|
const pin = daemonEmbedderPin();
|
|
@@ -203,6 +226,7 @@ let pipelinePromise = null;
|
|
|
203
226
|
/** Lazily create the feature-extraction pipeline; cached for the process. */
|
|
204
227
|
export async function loadPipeline() {
|
|
205
228
|
if (!pipelinePromise) {
|
|
229
|
+
const signal = cancelController.signal;
|
|
206
230
|
pipelinePromise = (async () => {
|
|
207
231
|
let graphVerified = false;
|
|
208
232
|
try {
|
|
@@ -230,6 +254,7 @@ export async function loadPipeline() {
|
|
|
230
254
|
const outcome = await ensureVerifiedModelFile(cacheDir, modelId, graph, {
|
|
231
255
|
remoteHost: env.remoteHost,
|
|
232
256
|
remotePathTemplate: env.remotePathTemplate,
|
|
257
|
+
signal,
|
|
233
258
|
});
|
|
234
259
|
graphVerified = true;
|
|
235
260
|
if (outcome === 'downloaded')
|
|
@@ -241,6 +266,9 @@ export async function loadPipeline() {
|
|
|
241
266
|
});
|
|
242
267
|
}
|
|
243
268
|
catch (err) {
|
|
269
|
+
// Before the retry bookkeeping: a cancelled download is not a failed one.
|
|
270
|
+
if (signal.aborted)
|
|
271
|
+
throw new ModelWorkCancelledError();
|
|
244
272
|
const missing = isMissingModule(err, TRANSFORMERS_MODULE);
|
|
245
273
|
const message = missing
|
|
246
274
|
? 'Local memory embeddings are an optional npm feature. Install @huggingface/transformers@^4.3.1 alongside @bendyline/gezel-service (see the service README).'
|
|
@@ -272,9 +300,12 @@ export async function loadPipeline() {
|
|
|
272
300
|
export async function runEmbed(texts) {
|
|
273
301
|
if (texts.length === 0)
|
|
274
302
|
return [];
|
|
303
|
+
const signal = cancelController.signal;
|
|
275
304
|
const pipe = await loadPipeline();
|
|
276
305
|
const out = [];
|
|
277
306
|
for (let i = 0; i < texts.length; i += MAX_BATCH) {
|
|
307
|
+
if (signal.aborted)
|
|
308
|
+
throw new ModelWorkCancelledError();
|
|
278
309
|
const slice = texts.slice(i, i + MAX_BATCH).map(capText);
|
|
279
310
|
const result = await pipe(slice, { pooling: 'mean', normalize: true });
|
|
280
311
|
for (const v of result.tolist())
|
|
@@ -11,13 +11,17 @@
|
|
|
11
11
|
* with that profile;
|
|
12
12
|
* reportProgress: send the
|
|
13
13
|
* model download's bytes
|
|
14
|
+
* { cancel: true } — shutdown: stop the
|
|
15
|
+
* download and runs in flight
|
|
14
16
|
* worker → host: { id, progress } — interim, zero or more
|
|
15
17
|
* { id, vectors } — success
|
|
16
18
|
* { id, error, fatal, — failure (fatal ⇒ model
|
|
17
|
-
* retryable, optionalPeerMissing
|
|
18
|
-
*
|
|
19
|
+
* retryable, optionalPeerMissing, unloadable, disable for
|
|
20
|
+
* cancelled } good; optionalPeerMissing
|
|
19
21
|
* ⇒ the peer is just not
|
|
20
22
|
* installed; retryable ⇒ a
|
|
21
|
-
* short host-side cooldown
|
|
23
|
+
* short host-side cooldown;
|
|
24
|
+
* cancelled ⇒ stopped by a
|
|
25
|
+
* cancel, not a fault)
|
|
22
26
|
*/
|
|
23
27
|
export {};
|
|
@@ -11,22 +11,30 @@
|
|
|
11
11
|
* with that profile;
|
|
12
12
|
* reportProgress: send the
|
|
13
13
|
* model download's bytes
|
|
14
|
+
* { cancel: true } — shutdown: stop the
|
|
15
|
+
* download and runs in flight
|
|
14
16
|
* worker → host: { id, progress } — interim, zero or more
|
|
15
17
|
* { id, vectors } — success
|
|
16
18
|
* { id, error, fatal, — failure (fatal ⇒ model
|
|
17
|
-
* retryable, optionalPeerMissing
|
|
18
|
-
*
|
|
19
|
+
* retryable, optionalPeerMissing, unloadable, disable for
|
|
20
|
+
* cancelled } good; optionalPeerMissing
|
|
19
21
|
* ⇒ the peer is just not
|
|
20
22
|
* installed; retryable ⇒ a
|
|
21
|
-
* short host-side cooldown
|
|
23
|
+
* short host-side cooldown;
|
|
24
|
+
* cancelled ⇒ stopped by a
|
|
25
|
+
* cancel, not a fault)
|
|
22
26
|
*/
|
|
23
27
|
import { parentPort } from 'node:worker_threads';
|
|
24
|
-
import { PipelineLoadError, runEmbed, runProfileQueryEmbed } from './embed-core.js';
|
|
28
|
+
import { ModelWorkCancelledError, PipelineLoadError, cancelInFlightModelWork, runEmbed, runProfileQueryEmbed, } from './embed-core.js';
|
|
25
29
|
if (!parentPort) {
|
|
26
30
|
throw new Error('embed-worker must be run as a worker thread');
|
|
27
31
|
}
|
|
28
32
|
const port = parentPort;
|
|
29
33
|
port.on('message', (msg) => {
|
|
34
|
+
if ('cancel' in msg) {
|
|
35
|
+
cancelInFlightModelWork();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
30
38
|
void (async () => {
|
|
31
39
|
try {
|
|
32
40
|
const vectors = msg.profile
|
|
@@ -43,6 +51,7 @@ port.on('message', (msg) => {
|
|
|
43
51
|
fatal: err instanceof PipelineLoadError && !err.retryable,
|
|
44
52
|
retryable: err instanceof PipelineLoadError && err.retryable,
|
|
45
53
|
optionalPeerMissing: err instanceof PipelineLoadError && err.optionalPeerMissing,
|
|
54
|
+
cancelled: err instanceof ModelWorkCancelledError,
|
|
46
55
|
});
|
|
47
56
|
}
|
|
48
57
|
})();
|
|
@@ -35,6 +35,15 @@ export declare class EmbeddingsUnavailableError extends EmbeddingsDisabledError
|
|
|
35
35
|
readonly retryable = true;
|
|
36
36
|
constructor(reason: string);
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The service is stopping. Callers that keep per-item retry budgets check
|
|
40
|
+
* `shuttingDown` so a quit never counts against a file the way a real model
|
|
41
|
+
* failure does.
|
|
42
|
+
*/
|
|
43
|
+
export declare class EmbeddingsShuttingDownError extends EmbeddingsUnavailableError {
|
|
44
|
+
readonly shuttingDown = true;
|
|
45
|
+
constructor();
|
|
46
|
+
}
|
|
38
47
|
export type EmbeddingPipelineStatus = 'cold' | 'warming' | 'ready' | 'disabled' | 'unavailable';
|
|
39
48
|
export declare function embeddingsDisabledReason(): string | null;
|
|
40
49
|
export declare function embeddingPipelineStatus(): EmbeddingPipelineStatus;
|
|
@@ -49,8 +58,14 @@ export declare function embeddingsHealth(): {
|
|
|
49
58
|
};
|
|
50
59
|
/**
|
|
51
60
|
* Close for service shutdown: refuse new work (it would start a worker, or
|
|
52
|
-
* load the model in-process, as the process exits),
|
|
53
|
-
*
|
|
61
|
+
* load the model in-process, as the process exits), cancel what is in flight,
|
|
62
|
+
* then terminate the worker once it is idle, waiting at most `drainMs`.
|
|
63
|
+
*
|
|
64
|
+
* The cancel is what makes the wait short: a first-run model download stops at
|
|
65
|
+
* once, and an embedding run stops at its next sub-batch. Without it the drain
|
|
66
|
+
* expired with the download still going, and every caller awaiting that
|
|
67
|
+
* embedding (index enrichment among them) held shutdown until the app's quit
|
|
68
|
+
* timeout.
|
|
54
69
|
*/
|
|
55
70
|
export declare function shutdownEmbeddings(drainMs: number): Promise<void>;
|
|
56
71
|
/** Reopen after shutdown — an embedded service can be started again in the same process. */
|
|
@@ -21,7 +21,7 @@ import { Worker } from 'node:worker_threads';
|
|
|
21
21
|
import { createLogger, sameVectorSpace } from '@bendyline/gezel';
|
|
22
22
|
import { retireModelWorker } from '../utils/retire-model-worker.js';
|
|
23
23
|
import { findServiceWorkerEntry } from '../utils/service-worker-entry.js';
|
|
24
|
-
import { PipelineLoadError, daemonEmbedderPin, daemonEmbedderVerified, passageInstruction, queryInstruction, runEmbed, runProfileQueryEmbed, } from './embed-core.js';
|
|
24
|
+
import { ModelWorkCancelledError, PipelineLoadError, cancelInFlightModelWork, daemonEmbedderPin, daemonEmbedderVerified, passageInstruction, queryInstruction, runEmbed, runProfileQueryEmbed, } from './embed-core.js';
|
|
25
25
|
const log = createLogger('memory');
|
|
26
26
|
/**
|
|
27
27
|
* Marker class so callers can tell "the indexer is off on purpose" apart from a
|
|
@@ -46,6 +46,18 @@ export class EmbeddingsUnavailableError extends EmbeddingsDisabledError {
|
|
|
46
46
|
this.message = `memory embeddings temporarily unavailable: ${reason}`;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* The service is stopping. Callers that keep per-item retry budgets check
|
|
51
|
+
* `shuttingDown` so a quit never counts against a file the way a real model
|
|
52
|
+
* failure does.
|
|
53
|
+
*/
|
|
54
|
+
export class EmbeddingsShuttingDownError extends EmbeddingsUnavailableError {
|
|
55
|
+
shuttingDown = true;
|
|
56
|
+
constructor() {
|
|
57
|
+
super('the service is shutting down');
|
|
58
|
+
this.name = 'EmbeddingsShuttingDownError';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
49
61
|
// Once the model fails to load, we permanently flip into a disabled state.
|
|
50
62
|
// Every subsequent call throws the short marker error instead of re-attempting
|
|
51
63
|
// the import and re-printing the full model-load error on every chat turn.
|
|
@@ -142,6 +154,11 @@ function onMessage(msg) {
|
|
|
142
154
|
pending.delete(msg.id);
|
|
143
155
|
if (pending.size === 0)
|
|
144
156
|
worker?.unref();
|
|
157
|
+
if (msg.cancelled) {
|
|
158
|
+
// Our own shutdown cancel: no cooldown, no profile outage, nothing disabled.
|
|
159
|
+
p.reject(new EmbeddingsShuttingDownError());
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
145
162
|
if (msg.error) {
|
|
146
163
|
if (p.profileId) {
|
|
147
164
|
if (!p.localFilesOnly && (msg.fatal || msg.retryable))
|
|
@@ -214,6 +231,8 @@ async function embedInProcess(texts) {
|
|
|
214
231
|
return await runEmbed(texts);
|
|
215
232
|
}
|
|
216
233
|
catch (err) {
|
|
234
|
+
if (err instanceof ModelWorkCancelledError)
|
|
235
|
+
throw new EmbeddingsShuttingDownError();
|
|
217
236
|
if (err instanceof PipelineLoadError) {
|
|
218
237
|
if (err.retryable) {
|
|
219
238
|
markTemporarilyUnavailable(err.message);
|
|
@@ -256,17 +275,27 @@ function markDisabled(message, optionalPeerMissing = false) {
|
|
|
256
275
|
log.error('[memory] failed to load embedding pipeline; vector memory is now disabled for this process.');
|
|
257
276
|
log.error('[memory] underlying error:', message);
|
|
258
277
|
}
|
|
259
|
-
const SHUTTING_DOWN = 'the service is shutting down';
|
|
260
278
|
/**
|
|
261
279
|
* Close for service shutdown: refuse new work (it would start a worker, or
|
|
262
|
-
* load the model in-process, as the process exits),
|
|
263
|
-
*
|
|
280
|
+
* load the model in-process, as the process exits), cancel what is in flight,
|
|
281
|
+
* then terminate the worker once it is idle, waiting at most `drainMs`.
|
|
282
|
+
*
|
|
283
|
+
* The cancel is what makes the wait short: a first-run model download stops at
|
|
284
|
+
* once, and an embedding run stops at its next sub-batch. Without it the drain
|
|
285
|
+
* expired with the download still going, and every caller awaiting that
|
|
286
|
+
* embedding (index enrichment among them) held shutdown until the app's quit
|
|
287
|
+
* timeout.
|
|
264
288
|
*/
|
|
265
289
|
export async function shutdownEmbeddings(drainMs) {
|
|
266
290
|
closed = true;
|
|
267
291
|
const w = worker;
|
|
268
|
-
if (!w)
|
|
292
|
+
if (!w) {
|
|
293
|
+
// In-process fallback: any run in flight is on this thread.
|
|
294
|
+
cancelInFlightModelWork();
|
|
269
295
|
return;
|
|
296
|
+
}
|
|
297
|
+
if (pending.size > 0)
|
|
298
|
+
w.postMessage({ cancel: true });
|
|
270
299
|
if (await retireModelWorker(w, () => pending.size === 0, drainMs)) {
|
|
271
300
|
if (worker === w)
|
|
272
301
|
worker = null;
|
|
@@ -281,7 +310,7 @@ export function openEmbeddings() {
|
|
|
281
310
|
/** Embed `texts`, preferring the worker and degrading to in-process. */
|
|
282
311
|
async function embedMany(texts) {
|
|
283
312
|
if (closed)
|
|
284
|
-
throw new
|
|
313
|
+
throw new EmbeddingsShuttingDownError();
|
|
285
314
|
if (disabledByEnv())
|
|
286
315
|
throw new EmbeddingsDisabledError(ENV_DISABLED_REASON);
|
|
287
316
|
if (disabledReason)
|
|
@@ -368,7 +397,7 @@ export function sharesDaemonEmbedder(profile) {
|
|
|
368
397
|
export async function embedKnowledgeQuery(text, profile, opts = {}) {
|
|
369
398
|
const { onDownloadProgress, localFilesOnly = false } = opts;
|
|
370
399
|
if (closed)
|
|
371
|
-
throw new
|
|
400
|
+
throw new EmbeddingsShuttingDownError();
|
|
372
401
|
// The default daemon pipeline may fetch weights on a cold load.
|
|
373
402
|
if (!localFilesOnly && sharesDaemonEmbedder(profile)) {
|
|
374
403
|
const vector = await embedQuery(text);
|
package/dist/models/ensure.d.ts
CHANGED
|
@@ -111,6 +111,8 @@ export interface CreateEnsureModelOptions {
|
|
|
111
111
|
backend: EnsureBackend;
|
|
112
112
|
catalogId: string;
|
|
113
113
|
}) => void;
|
|
114
|
+
/** Test seam for catalog backend exclusions — defaults to the process's own. */
|
|
115
|
+
resolveLlamaBackend?: () => string | undefined;
|
|
114
116
|
}
|
|
115
117
|
/**
|
|
116
118
|
* Parse a qualified id (`<backend>:<catalogId>`) into its parts. Bare
|
package/dist/models/ensure.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { llamaCppBackendExclusion, llamaCppBackendLabel } from '@bendyline/gezel';
|
|
3
|
+
import { currentLlamaCppBackend } from '../providers/llama-cpp/backend-health.js';
|
|
2
4
|
/**
|
|
3
5
|
* Error class with a structured `code` so the route can pick the
|
|
4
6
|
* right HTTP status. Avoids regex-matching error message strings.
|
|
@@ -160,6 +162,13 @@ export async function createEnsureModelOrchestrator(opts) {
|
|
|
160
162
|
].filter((id) => Boolean(id));
|
|
161
163
|
throw new KnownEnsureError('no_source_for_backend', `chat-model "${catalogId}" has no ${backend} source${alternatives.length > 0 ? ` — try ${alternatives.map((id) => `"${id}"`).join(' or ')}` : ''}`);
|
|
162
164
|
}
|
|
165
|
+
if (backend === 'llama-cpp') {
|
|
166
|
+
const engineBackend = (opts.resolveLlamaBackend ?? currentLlamaCppBackend)();
|
|
167
|
+
const excluded = llamaCppBackendExclusion(detail.manifest.llamaCpp, engineBackend);
|
|
168
|
+
if (excluded && engineBackend) {
|
|
169
|
+
throw new KnownEnsureError('no_source_for_backend', `chat-model "${catalogId}" is not available on the ${llamaCppBackendLabel(engineBackend)} llama.cpp engine: ${excluded}${detail.manifest.mlx ? ` Try "mlx:${catalogId}".` : ''}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
163
172
|
if (await isInstalled(backend, catalogId)) {
|
|
164
173
|
return { status: 'ready', modelId: qualifiedModelId };
|
|
165
174
|
}
|
|
@@ -2,7 +2,7 @@ import { type ClientPerfReport, type PerfSnapshot, type PerfWork } from '@bendyl
|
|
|
2
2
|
/**
|
|
3
3
|
* A gap is measured to within one beat, so this has to stay well inside
|
|
4
4
|
* `STALL_RECORD_MS`. At the same period, a pause long enough to log leaves the
|
|
5
|
-
* watchdog late by more than half the
|
|
5
|
+
* watchdog late by more than half the heartbeat's lateness, which is what `isMainThreadBlock`
|
|
6
6
|
* needs to rule the main thread out.
|
|
7
7
|
*/
|
|
8
8
|
export declare const BEAT_MS = 250;
|
|
@@ -18,8 +18,21 @@ interface WatchdogReport {
|
|
|
18
18
|
* A stale heartbeat is a main-thread block only when the watchdog itself kept
|
|
19
19
|
* time. When the watchdog was late by a comparable amount, the process or the
|
|
20
20
|
* host was paused, and blaming whatever was in flight would be a false lead.
|
|
21
|
+
*
|
|
22
|
+
* Lateness against lateness: both threads sleep a beat between checks, so a
|
|
23
|
+
* pause delays each by about its length. Comparing the watchdog's lateness
|
|
24
|
+
* with the whole gap, beat included, filed short pauses as blocks.
|
|
21
25
|
*/
|
|
22
26
|
export declare function isMainThreadBlock(report: WatchdogReport): boolean;
|
|
27
|
+
/** What a gap cost: the main thread's CPU, and the process's major page faults. */
|
|
28
|
+
export interface StallCost {
|
|
29
|
+
/** Null where the runtime has no per-thread CPU accounting. */
|
|
30
|
+
cpuMs: number | null;
|
|
31
|
+
pageFaults: number;
|
|
32
|
+
}
|
|
33
|
+
/** Running code when it used at least half the stall; otherwise paging or waiting on something. */
|
|
34
|
+
export declare function stallVerdict(durationMs: number, cost: StallCost): 'running code' | 'paging' | 'blocked waiting' | undefined;
|
|
35
|
+
export declare function describeStallCost(durationMs: number, cost: StallCost): string;
|
|
23
36
|
interface ActiveWork {
|
|
24
37
|
label: string;
|
|
25
38
|
startedAt: number;
|