@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.
Files changed (264) hide show
  1. package/dist/catalog/chat-models/de/deepseek-v4.1-flash-552b-q2/manifest.json +120 -0
  2. package/dist/catalog/chat-models/de/deepseek-v4.1-flash-552b-q2/versions/1.0.0/manifest.json +21 -0
  3. package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/manifest.json +6 -5
  4. package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/versions/1.0.2/manifest.json +27 -0
  5. package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/versions/1.0.3/manifest.json +27 -0
  6. package/dist/catalog/chat-models/index.json +1 -1
  7. package/dist/catalog/chat-models/ne/nemotron3.5-bolt-74b-iq2/manifest.json +157 -0
  8. package/dist/catalog/chat-models/ne/nemotron3.5-bolt-74b-iq2/versions/1.0.0/manifest.json +30 -0
  9. package/dist/catalog/chat-models/ne/nemotron3.5-super-vl-120b-nvfp4/manifest.json +156 -0
  10. package/dist/catalog/chat-models/ne/nemotron3.5-super-vl-120b-nvfp4/versions/1.0.0/manifest.json +28 -0
  11. package/dist/catalog/chat-models/raw-index.json +1 -1
  12. package/dist/catalog/knowledge-catalogs/be/bendyline-how-to-books/keywords.json +56 -0
  13. package/dist/catalog/knowledge-catalogs/ca/caselaw-alaska-part-0001/keywords.json +12 -0
  14. package/dist/catalog/knowledge-catalogs/ca/caselaw-nevada-part-0001/keywords.json +13 -0
  15. package/dist/catalog/knowledge-catalogs/ca/caselaw-us-300-399-part-0001/keywords.json +14 -0
  16. package/dist/catalog/knowledge-catalogs/ca/caselaw-us-347-349/keywords.json +17 -0
  17. package/dist/catalog/knowledge-catalogs/ca/caselaw-utah-part-0001/keywords.json +12 -0
  18. package/dist/catalog/knowledge-catalogs/ca/caselaw-utah-part-0002/keywords.json +12 -0
  19. package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0001/keywords.json +12 -0
  20. package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0002/keywords.json +12 -0
  21. package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0003/keywords.json +12 -0
  22. package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0004/keywords.json +12 -0
  23. package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0005/keywords.json +12 -0
  24. package/dist/catalog/knowledge-catalogs/ca/caselaw-wyoming-part-0001/keywords.json +12 -0
  25. package/dist/catalog/knowledge-catalogs/index.json +1 -1
  26. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-aspnet-core-en/keywords.json +21 -0
  27. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-ai-platform-en/keywords.json +27 -0
  28. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-apps-compute-en/keywords.json +23 -0
  29. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-data-aiot-en/keywords.json +26 -0
  30. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-management-hybrid-en/keywords.json +28 -0
  31. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-network-security-en/keywords.json +29 -0
  32. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-dotnet-en/keywords.json +21 -0
  33. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-entity-framework-en/keywords.json +14 -0
  34. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-microsoft-365-en/keywords.json +20 -0
  35. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-powershell-en/keywords.json +24 -0
  36. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-sql-server-en/keywords.json +27 -0
  37. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-windows-apps-en/keywords.json +18 -0
  38. package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-windows-server-en/keywords.json +28 -0
  39. package/dist/catalog/knowledge-catalogs/raw-index.json +1 -1
  40. package/dist/catalog/knowledge-catalogs/re/regional-american-south/keywords.json +1700 -0
  41. package/dist/catalog/knowledge-catalogs/re/regional-american-south/versions/2026.10.5/manifest.json +133 -0
  42. package/dist/catalog/knowledge-catalogs/re/regional-andes-amazon/keywords.json +750 -0
  43. package/dist/catalog/knowledge-catalogs/re/regional-andes-amazon/versions/2026.10.5/manifest.json +133 -0
  44. package/dist/catalog/knowledge-catalogs/re/regional-australia-nz/keywords.json +1281 -0
  45. package/dist/catalog/knowledge-catalogs/re/regional-australia-nz/versions/2026.10.5/manifest.json +133 -0
  46. package/dist/catalog/knowledge-catalogs/re/regional-brazil/keywords.json +1125 -0
  47. package/dist/catalog/knowledge-catalogs/re/regional-brazil/versions/2026.10.5/manifest.json +133 -0
  48. package/dist/catalog/knowledge-catalogs/re/regional-britain-ireland/keywords.json +3287 -0
  49. package/dist/catalog/knowledge-catalogs/re/regional-britain-ireland/versions/2026.10.5/manifest.json +133 -0
  50. package/dist/catalog/knowledge-catalogs/re/regional-california-southwest/keywords.json +1835 -0
  51. package/dist/catalog/knowledge-catalogs/re/regional-california-southwest/versions/2026.10.5/manifest.json +133 -0
  52. package/dist/catalog/knowledge-catalogs/re/regional-canada-alaska/keywords.json +1184 -0
  53. package/dist/catalog/knowledge-catalogs/re/regional-canada-alaska/versions/2026.10.5/manifest.json +133 -0
  54. package/dist/catalog/knowledge-catalogs/re/regional-central-eastern-europe/keywords.json +1503 -0
  55. package/dist/catalog/knowledge-catalogs/re/regional-central-eastern-europe/versions/2026.10.5/manifest.json +133 -0
  56. package/dist/catalog/knowledge-catalogs/re/regional-east-asia/keywords.json +2836 -0
  57. package/dist/catalog/knowledge-catalogs/re/regional-east-asia/versions/2026.10.5/manifest.json +133 -0
  58. package/dist/catalog/knowledge-catalogs/re/regional-east-southern-africa/keywords.json +813 -0
  59. package/dist/catalog/knowledge-catalogs/re/regional-east-southern-africa/versions/2026.10.5/manifest.json +133 -0
  60. package/dist/catalog/knowledge-catalogs/re/regional-mediterranean/keywords.json +2082 -0
  61. package/dist/catalog/knowledge-catalogs/re/regional-mediterranean/versions/2026.10.5/manifest.json +133 -0
  62. package/dist/catalog/knowledge-catalogs/re/regional-mexico-central-america/keywords.json +1481 -0
  63. package/dist/catalog/knowledge-catalogs/re/regional-mexico-central-america/versions/2026.10.5/manifest.json +133 -0
  64. package/dist/catalog/knowledge-catalogs/re/regional-middle-east/keywords.json +1272 -0
  65. package/dist/catalog/knowledge-catalogs/re/regional-middle-east/versions/2026.10.5/manifest.json +133 -0
  66. package/dist/catalog/knowledge-catalogs/re/regional-midwest/keywords.json +758 -0
  67. package/dist/catalog/knowledge-catalogs/re/regional-midwest/versions/2026.10.5/manifest.json +133 -0
  68. package/dist/catalog/knowledge-catalogs/re/regional-north-africa/keywords.json +896 -0
  69. package/dist/catalog/knowledge-catalogs/re/regional-north-africa/versions/2026.10.5/manifest.json +133 -0
  70. package/dist/catalog/knowledge-catalogs/re/regional-northeast/keywords.json +1281 -0
  71. package/dist/catalog/knowledge-catalogs/re/regional-northeast/versions/2026.10.5/manifest.json +133 -0
  72. package/dist/catalog/knowledge-catalogs/re/regional-northern-eurasia/keywords.json +346 -0
  73. package/dist/catalog/knowledge-catalogs/re/regional-northern-eurasia/versions/2026.10.5/manifest.json +133 -0
  74. package/dist/catalog/knowledge-catalogs/re/regional-northern-europe/keywords.json +2688 -0
  75. package/dist/catalog/knowledge-catalogs/re/regional-northern-europe/versions/2026.10.5/manifest.json +133 -0
  76. package/dist/catalog/knowledge-catalogs/re/regional-pacific-islands/keywords.json +973 -0
  77. package/dist/catalog/knowledge-catalogs/re/regional-pacific-islands/versions/2026.10.5/manifest.json +133 -0
  78. package/dist/catalog/knowledge-catalogs/re/regional-pacific-northwest/keywords.json +937 -0
  79. package/dist/catalog/knowledge-catalogs/re/regional-pacific-northwest/versions/2026.10.5/manifest.json +133 -0
  80. package/dist/catalog/knowledge-catalogs/re/regional-rockies-plains/keywords.json +608 -0
  81. package/dist/catalog/knowledge-catalogs/re/regional-rockies-plains/versions/2026.10.5/manifest.json +133 -0
  82. package/dist/catalog/knowledge-catalogs/re/regional-south-asia/keywords.json +633 -0
  83. package/dist/catalog/knowledge-catalogs/re/regional-south-asia/versions/2026.10.5/manifest.json +133 -0
  84. package/dist/catalog/knowledge-catalogs/re/regional-southeast-asia/keywords.json +2697 -0
  85. package/dist/catalog/knowledge-catalogs/re/regional-southeast-asia/versions/2026.10.5/manifest.json +133 -0
  86. package/dist/catalog/knowledge-catalogs/re/regional-southern-cone/keywords.json +606 -0
  87. package/dist/catalog/knowledge-catalogs/re/regional-southern-cone/versions/2026.10.5/manifest.json +133 -0
  88. package/dist/catalog/knowledge-catalogs/re/regional-west-central-africa/keywords.json +564 -0
  89. package/dist/catalog/knowledge-catalogs/re/regional-west-central-africa/versions/2026.10.5/manifest.json +133 -0
  90. package/dist/catalog/knowledge-catalogs/re/regional-western-europe/keywords.json +3247 -0
  91. package/dist/catalog/knowledge-catalogs/re/regional-western-europe-east/manifest.json +47 -0
  92. package/dist/catalog/knowledge-catalogs/re/regional-western-europe-east/versions/2026.10.5/manifest.json +133 -0
  93. package/dist/catalog/knowledge-catalogs/re/regional-western-europe-west/manifest.json +47 -0
  94. package/dist/catalog/knowledge-catalogs/re/regional-western-europe-west/versions/2026.10.5/manifest.json +133 -0
  95. package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/keywords.json +5437 -0
  96. package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/manifest.json +1 -1
  97. package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/versions/2026.10.1/manifest.json +181 -0
  98. package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/keywords.json +4009 -0
  99. package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/manifest.json +1 -1
  100. package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/versions/2026.10.1/manifest.json +181 -0
  101. package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/keywords.json +4009 -0
  102. package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/manifest.json +1 -1
  103. package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/versions/2026.10.1/manifest.json +217 -0
  104. package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/keywords.json +5583 -0
  105. package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/manifest.json +1 -1
  106. package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/versions/2026.10.1/manifest.json +197 -0
  107. package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/keywords.json +6386 -0
  108. package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/manifest.json +2 -2
  109. package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/versions/2026.10.1/manifest.json +197 -0
  110. package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/keywords.json +4009 -0
  111. package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/manifest.json +1 -1
  112. package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/versions/2026.10.1/manifest.json +185 -0
  113. package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/keywords.json +5672 -0
  114. package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/manifest.json +1 -1
  115. package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/versions/2026.10.1/manifest.json +137 -0
  116. package/dist/catalog/knowledge-catalogs/wi/wikipedia-geography/manifest.json +68 -0
  117. package/dist/catalog/knowledge-catalogs/wi/wikipedia-geography/versions/2026.10.1/manifest.json +217 -0
  118. package/dist/catalog/knowledge-catalogs/wi/wikipedia-history/manifest.json +68 -0
  119. package/dist/catalog/knowledge-catalogs/wi/wikipedia-history/versions/2026.10.1/manifest.json +217 -0
  120. package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/keywords.json +4009 -0
  121. package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/manifest.json +1 -1
  122. package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/versions/2026.10.1/manifest.json +141 -0
  123. package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/keywords.json +4009 -0
  124. package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/manifest.json +1 -1
  125. package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/versions/2026.10.1/manifest.json +185 -0
  126. package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/keywords.json +5719 -0
  127. package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/manifest.json +1 -1
  128. package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/versions/2026.10.1/manifest.json +193 -0
  129. package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/keywords.json +4009 -0
  130. package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/manifest.json +1 -1
  131. package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/versions/2026.10.1/manifest.json +189 -0
  132. package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/keywords.json +16009 -0
  133. package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/manifest.json +1 -1
  134. package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/versions/2026.10.1/manifest.json +185 -0
  135. package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/keywords.json +5843 -0
  136. package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/manifest.json +1 -1
  137. package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/versions/2026.10.1/manifest.json +189 -0
  138. package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/keywords.json +6264 -0
  139. package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/manifest.json +1 -1
  140. package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/versions/2026.10.1/manifest.json +197 -0
  141. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-arts/manifest.json +66 -0
  142. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-arts/versions/2026.10.1/manifest.json +209 -0
  143. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-business/manifest.json +61 -0
  144. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-business/versions/2026.10.1/manifest.json +189 -0
  145. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-entertainment/manifest.json +64 -0
  146. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-entertainment/versions/2026.10.1/manifest.json +201 -0
  147. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-military/manifest.json +60 -0
  148. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-military/versions/2026.10.1/manifest.json +185 -0
  149. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-other/manifest.json +55 -0
  150. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-other/versions/2026.10.1/manifest.json +165 -0
  151. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-politics/manifest.json +63 -0
  152. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-politics/versions/2026.10.1/manifest.json +197 -0
  153. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-religion/manifest.json +64 -0
  154. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-religion/versions/2026.10.1/manifest.json +201 -0
  155. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-science/manifest.json +65 -0
  156. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-science/versions/2026.10.1/manifest.json +205 -0
  157. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-sports/manifest.json +73 -0
  158. package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-sports/versions/2026.10.1/manifest.json +237 -0
  159. package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/keywords.json +5447 -0
  160. package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/manifest.json +1 -1
  161. package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/versions/2026.10.1/manifest.json +197 -0
  162. package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/keywords.json +4009 -0
  163. package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/manifest.json +1 -1
  164. package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/versions/2026.10.1/manifest.json +189 -0
  165. package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/keywords.json +4009 -0
  166. package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/manifest.json +1 -1
  167. package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/versions/2026.10.1/manifest.json +185 -0
  168. package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/keywords.json +5209 -0
  169. package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/manifest.json +1 -1
  170. package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/versions/2026.10.1/manifest.json +193 -0
  171. package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/keywords.json +6537 -0
  172. package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/manifest.json +1 -1
  173. package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/versions/2026.10.1/manifest.json +185 -0
  174. package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/keywords.json +5989 -0
  175. package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/manifest.json +1 -1
  176. package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/versions/2026.10.1/manifest.json +189 -0
  177. package/dist/catalog/provenance.json +1 -1
  178. package/dist/engines/native-file-manifest.json +483 -413
  179. package/dist/engines/native-manifest.d.ts +4 -4
  180. package/dist/engines/native-manifest.js +36 -34
  181. package/dist/engines/resolver.js +3 -1
  182. package/dist/fs/atomic.d.ts +4 -1
  183. package/dist/fs/atomic.js +7 -1
  184. package/dist/http/openai-compat/chat-target.d.ts +1 -1
  185. package/dist/http/openai-compat/chat-target.js +1 -1
  186. package/dist/http/openai-compat/engine-failure.d.ts +25 -0
  187. package/dist/http/openai-compat/engine-failure.js +76 -0
  188. package/dist/http/openai-compat/responses-streaming.js +2 -1
  189. package/dist/http/openai-compat/responses-translate.d.ts +1 -1
  190. package/dist/http/openai-compat/translate.d.ts +1 -1
  191. package/dist/http/routes/engines.js +35 -0
  192. package/dist/http/routes/v1-chat.js +11 -2
  193. package/dist/http/routes/v1-knowledge.js +4 -0
  194. package/dist/http/routes/v1-models.js +6 -4
  195. package/dist/http/routes/v1-responses.d.ts +1 -1
  196. package/dist/http/routes/v1-responses.js +7 -1
  197. package/dist/inference-service.d.ts +6 -1
  198. package/dist/inference-service.js +20 -9
  199. package/dist/knowledge/catalog-host.d.ts +9 -1
  200. package/dist/knowledge/catalog-host.js +2 -0
  201. package/dist/knowledge/manager.d.ts +21 -0
  202. package/dist/knowledge/manager.js +85 -7
  203. package/dist/knowledge/search-worker.d.ts +1 -1
  204. package/dist/knowledge/spatial-query.d.ts +2 -3
  205. package/dist/knowledge/spatial-query.js +12 -6
  206. package/dist/knowledge/worker-host.js +2 -0
  207. package/dist/memory/embed-core.d.ts +16 -0
  208. package/dist/memory/embed-core.js +31 -0
  209. package/dist/memory/embed-worker.d.ts +7 -3
  210. package/dist/memory/embed-worker.js +13 -4
  211. package/dist/memory/embeddings.d.ts +17 -2
  212. package/dist/memory/embeddings.js +36 -7
  213. package/dist/models/ensure.d.ts +2 -0
  214. package/dist/models/ensure.js +9 -0
  215. package/dist/perf/responsiveness.d.ts +14 -1
  216. package/dist/perf/responsiveness.js +81 -10
  217. package/dist/providers/audio/kokoro-frontend.js +1 -1
  218. package/dist/providers/ds4/build-provider.js +6 -0
  219. package/dist/providers/llama-cpp/backend-health.d.ts +30 -0
  220. package/dist/providers/llama-cpp/backend-health.js +64 -0
  221. package/dist/providers/llama-cpp/backend-quarantine.d.ts +40 -0
  222. package/dist/providers/llama-cpp/backend-quarantine.js +100 -0
  223. package/dist/providers/llama-cpp/build-provider.js +247 -199
  224. package/dist/providers/llama-cpp/cache-adapter.d.ts +7 -1
  225. package/dist/providers/llama-cpp/cache-adapter.js +11 -1
  226. package/dist/providers/llama-cpp/devices.d.ts +22 -2
  227. package/dist/providers/llama-cpp/devices.js +48 -5
  228. package/dist/providers/llama-cpp/engine-flags.d.ts +0 -18
  229. package/dist/providers/llama-cpp/engine-flags.js +12 -18
  230. package/dist/providers/llama-cpp/gguf-metadata.js +20 -3
  231. package/dist/providers/llama-cpp/gpu-startup-watch.d.ts +41 -0
  232. package/dist/providers/llama-cpp/gpu-startup-watch.js +58 -0
  233. package/dist/providers/llama-cpp/models.d.ts +6 -0
  234. package/dist/providers/llama-cpp/models.js +23 -2
  235. package/dist/providers/llama-cpp/offload-planner.d.ts +26 -18
  236. package/dist/providers/llama-cpp/offload-planner.js +30 -14
  237. package/dist/providers/llama-cpp/provider.d.ts +22 -3
  238. package/dist/providers/llama-cpp/provider.js +39 -3
  239. package/dist/providers/mlx/provider.js +5 -6
  240. package/dist/providers/mlx/python/qwen4_ple.py +9 -2
  241. package/dist/providers/mlx/reasoning-stream.d.ts +15 -0
  242. package/dist/providers/mlx/reasoning-stream.js +35 -0
  243. package/dist/providers/native/capacity-broker.d.ts +21 -15
  244. package/dist/providers/native/capacity-broker.js +25 -13
  245. package/dist/providers/native/capacity-budget.d.ts +35 -3
  246. package/dist/providers/native/capacity-budget.js +49 -5
  247. package/dist/providers/native/device-capacity.d.ts +1 -1
  248. package/dist/providers/native/device-capacity.js +23 -5
  249. package/dist/providers/native/local-engine-runtime.js +31 -31
  250. package/dist/providers/native/measured-budget.js +2 -0
  251. package/dist/providers/native/supervisor.d.ts +25 -0
  252. package/dist/providers/native/supervisor.js +62 -8
  253. package/dist/providers/queue.d.ts +9 -2
  254. package/dist/providers/queue.js +17 -2
  255. package/dist/providers/session-hooks.d.ts +4 -1
  256. package/dist/system/amdgpu-adapters.d.ts +18 -0
  257. package/dist/system/amdgpu-adapters.js +55 -0
  258. package/dist/system/gpu-carve-out.d.ts +84 -0
  259. package/dist/system/gpu-carve-out.js +111 -0
  260. package/dist/system/memory.d.ts +17 -1
  261. package/dist/system/memory.js +94 -13
  262. package/dist/transformers-cache.d.ts +2 -0
  263. package/dist/transformers-cache.js +6 -3
  264. package/package.json +5 -5
@@ -1,27 +1,30 @@
1
1
  import { availableParallelism } from 'node:os';
2
2
  import { delimiter, dirname, join } from 'node:path';
3
- import { DEFAULT_LOCAL_ENGINE_IDLE_TIMEOUT_MS, createLogger, estimateLlamaCppResidentBytes, } from '@bendyline/gezel';
4
- import { recordLlamaQuarantine } from '@bendyline/gezel/native';
3
+ import { DEFAULT_LOCAL_ENGINE_IDLE_TIMEOUT_MS, createLogger, estimateLlamaCppResidentBytes, llamaCppBackendExclusion, llamaCppBackendLabel, } from '@bendyline/gezel';
4
+ import { LLAMA_QUARANTINE_STRIKES, clearLlamaStartupStrikes, recordLlamaStartupCrash, unrunnableBuildExit, } from '@bendyline/gezel/native';
5
5
  import { gezelPaths } from '@bendyline/gezel/paths';
6
6
  import { mmprojBudgetBytes, nativeVisionEnabledFor } from '../../chat/vision-capability.js';
7
7
  import { effectiveEngineRelease, isEnginePinned } from '../../engines/native-manifest.js';
8
8
  import { engineInstallPhase } from '../../engines/registry.js';
9
9
  import { localDeviceSettingsLabel, noModelYetMessage, notReadyError, } from '../active-install-message.js';
10
10
  import { resolveCatalogLlamaCppEngineConfig, resolveCatalogReasoningBudget, } from '../catalog-model-config.js';
11
- import { CapacityDeniedError, NOT_ENOUGH_MEMORY_MESSAGE, autoKvCacheMaxBytes, availableSystemRamBytes, formatContextCapacityDenial, minViableLocalContextTokens, planAdaptiveContextGrowth, pressureIdleGraceMs, resolveLlamaCppContextRequirement, } from '../native/capacity-broker.js';
11
+ import { CapacityDeniedError, NOT_ENOUGH_MEMORY_MESSAGE, availableSystemRamBytes, formatContextCapacityDenial, minViableLocalContextTokens, planAdaptiveContextGrowth, pressureIdleGraceMs, resolveLlamaCppContextRequirement, } from '../native/capacity-broker.js';
12
12
  import { measureAvailableGpuBytes } from '../native/device-capacity.js';
13
13
  import { engineApiKey, withEngineApiKey } from '../native/engine-api-key.js';
14
14
  import { makeEngineKey } from '../native/engine-key.js';
15
15
  import { pickFreePort } from '../native/port.js';
16
16
  import { NativeEngineSupervisor } from '../native/supervisor.js';
17
17
  import { patientFetch } from '../patient-fetch.js';
18
+ import { currentLlamaCppBackend, noteGpuStartup, noteHostOutOfMemory } from './backend-health.js';
19
+ import { adoptLlamaFallback } from './backend-quarantine.js';
18
20
  import { lastArgValue, readLlamaCppBuildMetadata } from './build-metadata.js';
19
21
  import { matchNvidiaRuntimeDevice, maxGpuVramBytes, pickBestGpuDevice, probeLlamaDevices, probeNvidiaRuntimeDevices, } from './devices.js';
20
22
  import { buildLlamaCppEngineArgs } from './engine-flags.js';
21
23
  import { readGgufSummaryAsync } from './gguf-metadata-async.js';
22
24
  import { effectiveLazyTensorBytes } from './gguf-metadata.js';
25
+ import { GpuStartupWatch } from './gpu-startup-watch.js';
23
26
  import { isGemmaModel, planLlamaCppKv, resolveLlamaCppKvCacheType, } from './kv-cache-type.js';
24
- import { degradeDenseFfnOffloadDecision, degradeMoeOffloadDecision, estimateExactPerSlotKvBytesF16, estimateKvReserveBytes, estimateWindowedKvLinearization, fitsSwaFullInFastMemory, planDenseFfnOffload, planMoeOffload, } from './offload-planner.js';
27
+ import { degradeDenseFfnOffloadDecision, degradeMoeOffloadDecision, estimateExactPerSlotKvBytesF16, estimateKvReserveBytes, estimateWindowedKvLinearization, planDenseFfnOffload, planMoeOffload, shedSlotsForGpuResidency, } from './offload-planner.js';
25
28
  import { LlamaCppProvider } from './provider.js';
26
29
  import { reasoningLaunchOverridesFromEnv } from './reasoning-launch.js';
27
30
  import { resolveSpecDraft } from './spec-draft.js';
@@ -69,6 +72,15 @@ export async function buildLlamaCppProvider(opts) {
69
72
  ? undefined
70
73
  : (process.env.GEZEL_LLAMA_SERVER_URL ?? config.llamaCppBaseUrl);
71
74
  const binary = process.env.GEZEL_LLAMA_SERVER_BIN;
75
+ // Read together with the binary: after a quarantine the env points at the
76
+ // fallback build, and a crash of THIS build must be charged to its own
77
+ // backend, not to whatever the env names by then.
78
+ const binaryBackend = process.env.GEZEL_LLAMA_SERVER_BACKEND;
79
+ // What the latest startup crash did to this build's standing, for the error
80
+ // the failed start raises. `onExit` runs before `ensureRunning` rejects.
81
+ let lastStartupCrash = null;
82
+ // Fresh for each launch: whether this start of a GPU build got its GPU.
83
+ let gpuWatch = null;
72
84
  // Preservation is a two-sided contract: launch llama-server with the
73
85
  // explicit preserve/no-preserve flag AND replay captured
74
86
  // `reasoning_content` only in the preserve case. v0.4.0 changed the server
@@ -269,6 +281,18 @@ export async function buildLlamaCppProvider(opts) {
269
281
  }
270
282
  throw notReadyError(noModelYetMessage('Local model', activeLlamaInstall(opts.llamaCppModels)), 'model-not-ready');
271
283
  }
284
+ // Install refuses an excluded backend; this catches a model installed
285
+ // before the exclusion was authored, or copied in by hand.
286
+ if (modelCatalogInfo && opts.catalog) {
287
+ const engineBackend = binaryBackend ?? currentLlamaCppBackend();
288
+ const detail = await opts.catalog.get('chat-model', modelCatalogInfo.id).catch(() => null);
289
+ const excluded = llamaCppBackendExclusion(detail?.manifest.kind === 'chat-model' ? detail.manifest.llamaCpp : undefined, engineBackend);
290
+ if (excluded && engineBackend) {
291
+ const err = new Error(`Local model: ${modelCatalogInfo.name} cannot run on the ${llamaCppBackendLabel(engineBackend)} llama.cpp engine. ${excluded} Pick another local model in Settings → ${localDeviceSettingsLabel()}.`);
292
+ err.isActionable = true;
293
+ throw err;
294
+ }
295
+ }
272
296
  // Per Phase 0 measurements on a cold Mac: Metal library compile is
273
297
  // ~14s and model load adds model-size-dependent time. Default
274
298
  // startupTimeoutMs is 60s which squeaks through for a 7B on a warm
@@ -323,13 +347,6 @@ export async function buildLlamaCppProvider(opts) {
323
347
  // path still clamps to the native train context.
324
348
  const explicitCtx = envNumCtx ?? perModelCtxOverride ?? config.llamaCppNumCtx;
325
349
  const hostFloorTokens = minViableLocalContextTokens();
326
- // A per-model override below the host floor is deliberate user intent —
327
- // lower the admission floor to the override instead of silently raising
328
- // the request back to 64K. Env / machine-wide values keep their
329
- // historical floor semantics.
330
- const minViableTokens = perModelCtxOverride !== undefined && explicitCtx === perModelCtxOverride
331
- ? Math.min(hostFloorTokens, perModelCtxOverride)
332
- : hostFloorTokens;
333
350
  const contextRequirement = resolveLlamaCppContextRequirement({
334
351
  modelContextWindow: modelCatalogInfo?.contextWindow,
335
352
  ...(explicitCtx !== undefined ? { explicitContextWindow: explicitCtx } : {}),
@@ -338,7 +355,7 @@ export async function buildLlamaCppProvider(opts) {
338
355
  ? { manifestContextSize: manifestEngineConfig.contextSize }
339
356
  : {}),
340
357
  contextSizing: config.llamaCppContextSizing ?? 'adaptive',
341
- minViableContextTokens: minViableTokens,
358
+ minViableContextTokens: hostFloorTokens,
342
359
  });
343
360
  // `let`: RAM-aware admission below may lower this (but never below the
344
361
  // model-aware minimum) before anything launch-visible consumes it.
@@ -621,18 +638,15 @@ export async function buildLlamaCppProvider(opts) {
621
638
  // back to the engine's own `--fit` / `-ngl auto`.
622
639
  let offloadDecision;
623
640
  let offloadBlockCount;
624
- // Whether weights + full-attention KV at the requested context fit the
625
- // fast-memory pool — VRAM on a discrete GPU. Gates the Gemma
626
- // `--swa-full` auto-default (see `EngineFlagInput.swaFullAutoFits`). Set
627
- // false to keep the full context on the windowed cache when Full would
628
- // spill or require a slot/context reduction; undefined means it fits or
629
- // the exact decision could not be computed.
630
- let swaFullAutoFits;
631
641
  // Broker-ledger reservation for this launch: resident weights + the KV
632
642
  // the engine will actually allocate at the granted window and cache
633
643
  // mode. Undefined when the GGUF is unreadable — the pool builder then
634
644
  // falls back to the legacy weights-multiplier reservation.
635
645
  let plannedReservationBytes;
646
+ // What prices that reservation, kept for the out-of-memory ladder: when it
647
+ // drops a slot, the relaunch is charged for the KV it actually allocates.
648
+ let reservationKvFor;
649
+ let reservationResidentBytes;
636
650
  // Graphics memory the MoE offload plan puts on the card. Device admission
637
651
  // charges this rather than the whole card; undefined keeps the old charge.
638
652
  let plannedGpuBytes;
@@ -709,21 +723,16 @@ export async function buildLlamaCppProvider(opts) {
709
723
  // under GEZEL_LLAMA_NUM_CTX — eval runs lift ceilings deliberately
710
724
  // and accept the memory consequences.
711
725
  //
712
- // The fit verdict additionally gates the Gemma `--swa-full`
713
- // auto-default, in BOTH branches: the full cache is a cache-operation
714
- // performance trade, while the context window is capability. Auto
715
- // therefore requires resident weights + exact full KV to fit the fast
716
- // pool (VRAM on a discrete GPU); otherwise it keeps the full window on
717
- // the windowed cache. Shared-KV layers and SWA-specific head dimensions
718
- // are load-bearing here — E4B owns only 24 KV caches for 42 logical
719
- // layers, and pricing all 42 at global dimensions overstated its two-slot
720
- // 64K KV allocation as 21 GiB instead of llama.cpp's actual 7 GiB.
721
- // When the windowed cache is what will run, the launch is NOT left
722
- // ungated: admission re-plans with the windowed-KV linearization
723
- // (SWA layers = fixed window-capped bytes, global layers = the only
724
- // per-token cost), so a host too small even for the windowed cache
725
- // still denies or clamps honestly instead of OOMing one machine
726
- // class below the 2026-08-05 incident.
726
+ // A sliding-window model runs llama.cpp's windowed cache unless
727
+ // `swaFull` is pinned, and admission prices what runs: it re-plans
728
+ // with the windowed-KV linearization (SWA layers = fixed window-capped
729
+ // bytes, global layers = the only per-token cost), so a host too small
730
+ // even for the windowed cache still denies or clamps honestly instead
731
+ // of OOMing one machine class below the 2026-08-05 incident.
732
+ // Shared-KV layers and SWA-specific head dimensions are load-bearing
733
+ // in that math — E4B owns only 24 KV caches for 42 logical layers, and
734
+ // pricing all 42 at global dimensions overstated its two-slot 64K KV
735
+ // allocation as 21 GiB instead of llama.cpp's actual 7 GiB.
727
736
  {
728
737
  const REFERENCE_CTX = 4096;
729
738
  const exactKvAtReference = estimateKvReserveBytes({
@@ -769,23 +778,14 @@ export async function buildLlamaCppProvider(opts) {
769
778
  ? {
770
779
  unifiedMemory: liveMemory.gpuMemoryKind === 'integrated' ||
771
780
  liveMemory.gpuMemoryKind === 'unified',
781
+ reservedGpuBytes: liveMemory.gpuReservedBytes ?? null,
782
+ gpuAddressableBytes: liveMemory.gpuAddressableBytes ?? null,
772
783
  }
773
784
  : {}),
774
785
  });
775
786
  const fastBudgetBytes = brokerSnap?.enforced
776
787
  ? (opts.broker?.fastBudgetBytes() ?? liveBudget.fastBytes)
777
788
  : liveBudget.fastBytes;
778
- const fullKvBytesAtRequested = exactKvAtReference !== undefined
779
- ? (exactKvAtReference / REFERENCE_CTX) * effectiveNumCtx * slots
780
- : undefined;
781
- const fullCacheFitsFastMemory = fullKvBytesAtRequested !== undefined
782
- ? fitsSwaFullInFastMemory({
783
- residentWeightsBytes: residentBytes,
784
- fullKvBytes: fullKvBytesAtRequested,
785
- fastBudgetBytes,
786
- committedOtherBytes,
787
- })
788
- : undefined;
789
789
  const admission = planCtxTokensForMemory({
790
790
  requestedPerTurnCtxTokens: effectiveNumCtx,
791
791
  slots,
@@ -797,25 +797,13 @@ export async function buildLlamaCppProvider(opts) {
797
797
  freeSystemRamBytes: availableSystemRamBytes(),
798
798
  vramBytes: liveBudget.vramBytes,
799
799
  });
800
+ // `--swa-full` runs only when pinned; every other launch of a
801
+ // sliding-window model gets llama.cpp's windowed cache.
800
802
  const explicitSwaFull = config.llamaCppSwaFull ?? manifestEngineConfig?.swaFull;
801
- const swaFullAutoDefault = explicitSwaFull === undefined &&
802
- isGemmaModel({
803
- architecture: modelCatalogInfo?.architecture,
804
- modelId: defaultModelId ?? undefined,
805
- });
806
- // A full-size cache past the automatic KV ceiling is declined even
807
- // where it fits: gemma4-31b's is 55 GB at 64K, 11x its windowed
808
- // cache, for a cross-request reuse gain nobody had measured.
809
- const kvCeilingBytes = autoKvCacheMaxBytes();
810
- const fullCacheOverCeiling = fullKvBytesAtRequested !== undefined && fullKvBytesAtRequested > kvCeilingBytes;
811
- const fullKvOverBudget = fullCacheFitsFastMemory === false ||
812
- fullCacheOverCeiling ||
813
- !admission.minimumSatisfied ||
814
- admission.slots < slots ||
815
- admission.clamped;
803
+ const fullKvOverBudget = !admission.minimumSatisfied || admission.slots < slots || admission.clamped;
816
804
  // The plan whose ladder (deny → shed slots → clamp) is enforced
817
805
  // below. Defaults to the full-attention plan; the windowed-cache
818
- // branches replace it with windowed math or — only when the GGUF
806
+ // branch replaces it with windowed math or — only when the GGUF
819
807
  // hides its SWA layout — null (no safe estimate; launch untouched,
820
808
  // the pre-windowed-admission behavior).
821
809
  let ladderPlan = admission;
@@ -833,17 +821,17 @@ export async function buildLlamaCppProvider(opts) {
833
821
  // full-attention), and the strict minimum rides inside
834
822
  // `contextRequirement.minimumPerTurnCtxTokens`, so the windowed
835
823
  // re-plan below sheds slots or denies but never shortens the window.
836
- const windowedCacheWillRun = fullKvOverBudget && (swaFullAutoDefault || explicitSwaFull === false);
837
- if (windowedCacheWillRun) {
838
- // The denial, the slot reduction, and the clamp above are all
839
- // driven by full-attention KV math, which overstates the windowed
840
- // cache's real allocation (Gemma 4: global layers are both fewer
841
- // AND cheaper per token than SWA layers — ~14× on 31b). Re-plan
842
- // with the windowed linearization: SWA layers become a fixed,
843
- // context-independent reservation; only global layers scale.
844
- // Without it a 31b-class Gemma on a ~32 GB host would launch
845
- // with NO context admission at all — the same lazy-Metal-wiring
846
- // OOM this gate exists to prevent, one machine class down.
824
+ if (explicitSwaFull !== true) {
825
+ // The admission above is full-attention KV math, which overstates
826
+ // the windowed cache's real allocation (Gemma 4: global layers are
827
+ // both fewer AND cheaper per token than SWA layers — ~11× on 31b
828
+ // at 64K). Re-plan with the windowed linearization: SWA layers
829
+ // become a fixed, context-independent reservation; only global
830
+ // layers scale. Without it a 31b-class Gemma on a ~32 GB host
831
+ // would launch with NO context admission at all — the same
832
+ // lazy-Metal-wiring OOM this gate exists to prevent, one machine
833
+ // class down — and growth would price every token at the full
834
+ // slope.
847
835
  const windowed = estimateWindowedKvLinearization({
848
836
  blockCount: summary.blockCount,
849
837
  embeddingLength: summary.embeddingLength,
@@ -864,42 +852,6 @@ export async function buildLlamaCppProvider(opts) {
864
852
  ssmConvKernel: summary.ssmConvKernel,
865
853
  kvCacheType,
866
854
  });
867
- if (swaFullAutoDefault) {
868
- // Declining `--swa-full` dominates every full-math remedy:
869
- // the context window is capability, the full cache is a
870
- // session-switch performance trade. Weights-level admission
871
- // stays with the capacity broker.
872
- swaFullAutoFits = false;
873
- const fastFitDetail = fullCacheFitsFastMemory === false && fullKvBytesAtRequested !== undefined
874
- ? `projected weights + full KV ${((residentBytes + fullKvBytesAtRequested) / 1024 ** 3).toFixed(1)} GiB exceeds available fast memory ${(Math.max(0, fastBudgetBytes - committedOtherBytes) / 1024 ** 3).toFixed(1)} GiB`
875
- : undefined;
876
- const overCeilingOnly = fullCacheOverCeiling &&
877
- fullCacheFitsFastMemory !== false &&
878
- admission.minimumSatisfied &&
879
- admission.slots >= slots &&
880
- !admission.clamped;
881
- if (overCeilingOnly && fullKvBytesAtRequested !== undefined) {
882
- // A policy choice on a machine that could hold the cache, not
883
- // a shortage: info, not warn.
884
- log.info([
885
- `[llama-cpp] ${modelCatalogInfo?.id ?? 'model'}: the full SWA cache at the requested `,
886
- `${effectiveNumCtx * slots}-token total context would take `,
887
- `${(fullKvBytesAtRequested / 1024 ** 3).toFixed(1)} GiB, past the `,
888
- `${(kvCeilingBytes / 1024 ** 3).toFixed(1)} GiB automatic KV ceiling — keeping the full `,
889
- 'context and declining the --swa-full auto-default instead; the windowed KV cache ',
890
- 'holds it (cross-request prefix reuse unavailable).',
891
- ].join(''));
892
- }
893
- else {
894
- log.warn([
895
- `[llama-cpp] ${modelCatalogInfo?.id ?? 'model'}: the full SWA working set at the requested `,
896
- `${effectiveNumCtx * slots}-token total context does not fit fast memory — keeping the full `,
897
- 'context and declining the --swa-full auto-default instead; the windowed KV cache ',
898
- 'is what actually fits (cross-request prefix reuse unavailable). ',
899
- `Fit detail: ${fastFitDetail ?? admission.reason ?? 'over budget at the requested slot count'}`,
900
- ].join(''));
901
- }
902
- }
903
855
  if (windowed) {
904
856
  ladderPlan = planCtxTokensForMemory({
905
857
  requestedPerTurnCtxTokens: effectiveNumCtx,
@@ -917,16 +869,23 @@ export async function buildLlamaCppProvider(opts) {
917
869
  fixedPerSlotBytes: windowed.fixedBytes,
918
870
  };
919
871
  }
920
- else if ((summary.slidingWindow ?? 0) > 0 || swaFullAutoDefault) {
921
- // A sliding-window model whose GGUF hides the layer layout:
922
- // full math over-states the real cache and there is no exact
923
- // substitute — leave the launch alone rather than over-clamp.
872
+ else if (fullKvOverBudget &&
873
+ (explicitSwaFull === false
874
+ ? (summary.slidingWindow ?? 0) > 0
875
+ : isGemmaModel({
876
+ architecture: modelCatalogInfo?.architecture,
877
+ modelId: defaultModelId ?? undefined,
878
+ }))) {
879
+ // A sliding-window model whose GGUF hides the layer layout,
880
+ // over budget on full math: that math over-states the real cache
881
+ // and there is no exact substitute — leave the launch alone
882
+ // rather than over-clamp.
924
883
  ladderPlan = null;
925
884
  ladderKvLinearization = null;
926
885
  }
927
- // else: `swaFull: false` pinned on a model with no SWA layers at
928
- // all — the flag is a no-op there and the full-attention plan IS
929
- // the real allocation; fall through with it intact.
886
+ // else: no SWA layers at all, or a layout-less model that fits on
887
+ // full math — the full-attention plan stands (it IS the real
888
+ // allocation, or safely above it).
930
889
  }
931
890
  if (ladderPlan !== null && envNumCtx === undefined) {
932
891
  const windowedNote = ladderPlan === admission ? '' : ' (windowed KV admission)';
@@ -989,19 +948,12 @@ export async function buildLlamaCppProvider(opts) {
989
948
  }
990
949
  }
991
950
  // Price the KV the engine will ACTUALLY allocate. Mirrors the
992
- // `--swa-full` tri-state in buildLlamaCppEngineArgs (explicit config
993
- // → manifest → Gemma auto-default gated on the fit verdict): an SWA
994
- // model on the default windowed cache carries only its global layers
995
- // per token plus a fixed window block per slot. Feeding the offload
996
- // planner full-attention math for a windowed launch over-biased
997
- // Gemma experts into system RAM for a cache that never materializes.
998
- const effectiveSwaFull = config.llamaCppSwaFull ??
999
- manifestEngineConfig?.swaFull ??
1000
- (swaFullAutoFits !== false &&
1001
- isGemmaModel({
1002
- architecture: modelCatalogInfo?.architecture,
1003
- modelId: defaultModelId ?? undefined,
1004
- }));
951
+ // opt-in `--swa-full` in buildLlamaCppEngineArgs: an SWA model on the
952
+ // default windowed cache carries only its global layers per token
953
+ // plus a fixed window block per slot. Feeding the offload planner
954
+ // full-attention math for a windowed launch over-biased Gemma experts
955
+ // into system RAM for a cache that never materializes.
956
+ const effectiveSwaFull = config.llamaCppSwaFull ?? manifestEngineConfig?.swaFull ?? false;
1005
957
  const windowedKvPlan = effectiveSwaFull
1006
958
  ? undefined
1007
959
  : estimateWindowedKvLinearization({
@@ -1024,9 +976,9 @@ export async function buildLlamaCppProvider(opts) {
1024
976
  ssmConvKernel: summary.ssmConvKernel,
1025
977
  kvCacheType,
1026
978
  });
1027
- const kvReserveBytes = windowedKvPlan
1028
- ? Math.round(windowedKvPlan.fixedBytes * slots +
1029
- windowedKvPlan.bytesPerToken * effectiveNumCtx * slots)
979
+ const kvReserveFor = (slotCount) => windowedKvPlan
980
+ ? Math.round(windowedKvPlan.fixedBytes * slotCount +
981
+ windowedKvPlan.bytesPerToken * effectiveNumCtx * slotCount)
1030
982
  : estimateKvReserveBytes({
1031
983
  blockCount: summary.blockCount,
1032
984
  embeddingLength: summary.embeddingLength,
@@ -1044,43 +996,47 @@ export async function buildLlamaCppProvider(opts) {
1044
996
  ssmInnerSize: summary.ssmInnerSize,
1045
997
  ssmStateSize: summary.ssmStateSize,
1046
998
  ssmConvKernel: summary.ssmConvKernel,
1047
- ctxTokens: effectiveNumCtx * slots,
999
+ ctxTokens: effectiveNumCtx * slotCount,
1048
1000
  kvCacheType,
1049
1001
  });
1050
- // The broker-ledger reservation for this launch: resident weights
1051
- // plus the KV the engine will allocate at the granted window (M1 —
1052
- // the weights-only ledger under-reserved dense models whose KV
1053
- // rivals their weights: qwen3.5-4b at 64K carries more KV than
1054
- // parameters). Consumed by the pool builder via
1055
- // `plannedReservationBytes` so co-residency admission can see it.
1056
- if (kvReserveBytes !== undefined) {
1057
- plannedReservationBytes = residentBytes + kvReserveBytes;
1058
- }
1002
+ // Plan against the card as it is now. Sized to the whole card, a plan
1003
+ // counts memory other programs hold: MoE admission then charged the
1004
+ // model the whole card and refused it (2026-10-11), and a dense
1005
+ // 31B planned into 30.3 GiB of a 32 GB card that the desktop and its
1006
+ // apps were already holding ~5 GB of died with a Vulkan out-of-memory
1007
+ // (2026-10-19).
1008
+ const freeGpuBytes = vramBytes > 0 ? await measureAvailableGpuBytes().catch(() => undefined) : undefined;
1009
+ // A lane count the person chose is not the planner's to spend, and
1010
+ // eval runs under GEZEL_LLAMA_NUM_CTX accept the memory consequences.
1011
+ const mayShedSlots = configuredSlots === undefined && envNumCtx === undefined;
1012
+ const placeSlots = (gpuBytes, plan) => mayShedSlots
1013
+ ? shedSlotsForGpuResidency({ slots, vramBytes: gpuBytes, plan })
1014
+ : { slots, decision: plan(slots, gpuBytes), shed: false };
1015
+ let placement;
1059
1016
  if (isMoE) {
1060
- // Plan against the card as it is now. Sized to the whole card, the
1061
- // plan counted memory other programs held, and admission then had
1062
- // to charge the model the whole card and refuse it (2026-10-11).
1063
- const freeGpuBytes = vramBytes > 0 ? await measureAvailableGpuBytes().catch(() => undefined) : undefined;
1064
- const moePlan = planMoeOffload({
1065
- isMoE,
1066
- residentBytes,
1067
- vramBytes: freeGpuBytes !== undefined ? Math.min(vramBytes, freeGpuBytes) : vramBytes,
1068
- ...(split
1069
- ? {
1070
- split,
1071
- ...(summary.blockCount !== undefined ? { blockCount: summary.blockCount } : {}),
1072
- ...(kvReserveBytes !== undefined ? { kvReserveBytes } : {}),
1073
- }
1074
- : {}),
1017
+ placement = placeSlots(freeGpuBytes !== undefined ? Math.min(vramBytes, freeGpuBytes) : vramBytes, (slotCount, gpuBytes) => {
1018
+ const kvReserveBytes = kvReserveFor(slotCount);
1019
+ return planMoeOffload({
1020
+ isMoE,
1021
+ residentBytes,
1022
+ vramBytes: gpuBytes,
1023
+ ...(split
1024
+ ? {
1025
+ split,
1026
+ ...(summary.blockCount !== undefined ? { blockCount: summary.blockCount } : {}),
1027
+ ...(kvReserveBytes !== undefined ? { kvReserveBytes } : {}),
1028
+ }
1029
+ : {}),
1030
+ });
1075
1031
  });
1076
- offloadDecision = moePlan;
1032
+ offloadDecision = placement.decision;
1077
1033
  // An explicit offload setting shadows the planner's flags, so its
1078
1034
  // footprint would describe a launch that is not the one made.
1079
1035
  const plannerPlacesExperts = (config.llamaCppCpuMoe ?? resolvedManifestEngineConfig?.cpuMoe) === undefined &&
1080
1036
  (config.llamaCppNCpuMoe ?? resolvedManifestEngineConfig?.nCpuMoe) === undefined &&
1081
1037
  (config.llamaCppNGpuLayers ?? resolvedManifestEngineConfig?.nGpuLayers) === undefined;
1082
1038
  if (plannerPlacesExperts)
1083
- plannedGpuBytes = moePlan.gpuBytes;
1039
+ plannedGpuBytes = placement.decision.gpuBytes;
1084
1040
  }
1085
1041
  else {
1086
1042
  const explicitNCpuFfn = config.llamaCppNCpuFfn ?? resolvedManifestEngineConfig?.nCpuFfn;
@@ -1089,16 +1045,21 @@ export async function buildLlamaCppProvider(opts) {
1089
1045
  // A finite whole-layer GPU count is a different placement strategy;
1090
1046
  // don't layer an automatic tensor override on top of it. `-1` means
1091
1047
  // all and is compatible with the planner's own attention-on-GPU plan.
1092
- offloadDecision =
1093
- explicitNCpuFfn === undefined &&
1094
- (explicitNGpuLayers === undefined || explicitNGpuLayers === -1)
1095
- ? planDenseFfnOffload({
1048
+ if (explicitNCpuFfn === undefined &&
1049
+ (explicitNGpuLayers === undefined || explicitNGpuLayers === -1)) {
1050
+ placement = placeSlots(
1051
+ // Respect the usable accelerator pool, any explicit capacity
1052
+ // ceiling, co-resident reservations, and what other programs
1053
+ // hold on the card now. The raw probe still gates this to a real
1054
+ // discrete-GPU build rather than treating a CPU/unified fast pool
1055
+ // as VRAM.
1056
+ vramBytes > 0
1057
+ ? Math.max(0, Math.min(budgetBytes - committedOtherBytes, freeGpuBytes ?? Number.POSITIVE_INFINITY))
1058
+ : 0, (slotCount, gpuBytes) => {
1059
+ const kvReserveBytes = kvReserveFor(slotCount);
1060
+ return planDenseFfnOffload({
1096
1061
  isMoE,
1097
- // Respect the usable accelerator pool, any explicit capacity
1098
- // ceiling, and co-resident reservations. The raw probe still
1099
- // gates this to a real discrete-GPU build rather than treating
1100
- // a CPU/unified fast pool as VRAM.
1101
- vramBytes: vramBytes > 0 ? Math.max(0, budgetBytes - committedOtherBytes) : 0,
1062
+ vramBytes: gpuBytes,
1102
1063
  ...(denseFfnSplit ? { split: denseFfnSplit } : {}),
1103
1064
  ...(summary.blockCount !== undefined ? { blockCount: summary.blockCount } : {}),
1104
1065
  ...(kvReserveBytes !== undefined ? { kvReserveBytes } : {}),
@@ -1107,9 +1068,30 @@ export async function buildLlamaCppProvider(opts) {
1107
1068
  ? brokerSnap.pools.ramShareBytes
1108
1069
  : hostCapacity.ramShareBytes,
1109
1070
  freeSystemRamBytes: availableSystemRamBytes(),
1110
- })
1111
- : undefined;
1071
+ });
1072
+ });
1073
+ offloadDecision = placement.decision;
1074
+ }
1075
+ else {
1076
+ offloadDecision = undefined;
1077
+ }
1112
1078
  }
1079
+ if (placement?.shed) {
1080
+ log.info(`[llama-cpp] ${modelCatalogInfo?.id ?? 'model'}: reducing engine slots ${slots} -> 1 to keep the model on the GPU with room to spare (other programs' graphics memory moves)`);
1081
+ slots = 1;
1082
+ }
1083
+ // The broker-ledger reservation for this launch: resident weights
1084
+ // plus the KV the engine will allocate at the granted window (M1 —
1085
+ // the weights-only ledger under-reserved dense models whose KV
1086
+ // rivals their weights: qwen3.5-4b at 64K carries more KV than
1087
+ // parameters). Consumed by the pool builder via
1088
+ // `plannedReservationBytes` so co-residency admission can see it.
1089
+ const kvReserveBytes = kvReserveFor(slots);
1090
+ if (kvReserveBytes !== undefined) {
1091
+ plannedReservationBytes = residentBytes + kvReserveBytes;
1092
+ }
1093
+ reservationKvFor = kvReserveFor;
1094
+ reservationResidentBytes = residentBytes;
1113
1095
  if (offloadDecision?.reason) {
1114
1096
  log.info(`[llama-cpp] offload plan (${modelCatalogInfo?.id ?? 'model'}): ${offloadDecision.reason}`);
1115
1097
  }
@@ -1202,44 +1184,105 @@ export async function buildLlamaCppProvider(opts) {
1202
1184
  if (snapshot.expected)
1203
1185
  return;
1204
1186
  logFile.writeIncident(snapshot);
1205
- // A build that dies by SIGILL before it ever answers /health has
1206
- // not failed at a task — it cannot run on this CPU at all, and
1207
- // relaunching it on every request just reproduces the crash while
1208
- // a working lower-tier build sits in the same directory. Write it
1209
- // down so the next launch routes around it.
1210
- //
1211
- // Deliberately narrow: only SIGILL, and only before readiness.
1212
- // A SIGSEGV during startup can be a corrupt model file, and any
1213
- // crash after readiness is attributable to the work rather than
1214
- // the binary — quarantining a backend over either would demote
1215
- // users to slow engines for transient reasons.
1216
- if (snapshot.signal !== 'SIGILL' || snapshot.reachedReady)
1187
+ if (snapshot.panicKind === 'host-out-of-memory') {
1188
+ noteHostOutOfMemory({
1189
+ model: modelCatalogInfo?.id ?? defaultModelId,
1190
+ whileStarting: !snapshot.reachedReady,
1191
+ });
1192
+ log.warn(`[llama-server] Linux's out-of-memory killer stopped the engine (${modelCatalogInfo?.id ?? 'model'}, incident=${snapshot.incidentId}, ${snapshot.reachedReady ? 'while serving' : 'while loading'})`);
1217
1193
  return;
1218
- const backend = process.env.GEZEL_LLAMA_SERVER_BACKEND;
1219
- if (!backend)
1194
+ }
1195
+ // A build that dies before it ever answers /health, without saying
1196
+ // why, may not be able to run here at all — and relaunching it on every
1197
+ // request just reproduces the crash while a working lower-tier build
1198
+ // sits in the next directory. Count it; a few in a row write it down so
1199
+ // the next launch routes around it. Which exits qualify (a silent
1200
+ // SIGILL; a GPU build's silent crash status on Windows or signal on
1201
+ // Linux; a CUDA build with no code for this GPU) is decided in
1202
+ // unrunnable-build.ts, how many in llama-quarantine.ts.
1203
+ const backend = binaryBackend;
1204
+ const unrunnable = unrunnableBuildExit(snapshot, backend);
1205
+ if (!backend || !unrunnable)
1220
1206
  return;
1221
- const entry = recordLlamaQuarantine(home, {
1207
+ const { strikes, entry } = recordLlamaStartupCrash(home, {
1222
1208
  backend,
1223
1209
  binaryPath: binary,
1224
- signal: 'SIGILL',
1225
- reason: 'crashed with SIGILL before the engine became ready — this build uses CPU instructions ' +
1226
- 'this machine does not support, or faulted inside GPU library startup',
1210
+ signal: unrunnable.signal,
1211
+ reason: unrunnable.reason,
1227
1212
  });
1228
- if (entry) {
1229
- log.warn(`[llama-server] quarantined the ${backend} build after SIGILL (incident=${snapshot.incidentId}); the next launch will fall back to another backend`);
1213
+ if (!entry) {
1214
+ if (strikes === 0)
1215
+ return;
1216
+ lastStartupCrash = {
1217
+ incidentId: snapshot.incidentId,
1218
+ verdict: { kind: 'strike', strikes, threshold: LLAMA_QUARANTINE_STRIKES },
1219
+ };
1220
+ log.warn(`[llama-server] the ${backend} build crashed before it was ready (${unrunnable.signal}, incident=${snapshot.incidentId}); ${strikes} of ${LLAMA_QUARANTINE_STRIKES} in a row switch it off`);
1221
+ return;
1222
+ }
1223
+ log.warn(`[llama-server] quarantined the ${backend} build after ${strikes} crashes in a row (${unrunnable.signal}, incident=${snapshot.incidentId}); the next launch will fall back to another backend`);
1224
+ // Discovery reads the quarantine only at startup. Point the env at the
1225
+ // fallback now, so the pool's rebuild of this failed engine on the next
1226
+ // request launches it instead of the build just written down. A pinned
1227
+ // backend is a decision, and discovery never routes around one either;
1228
+ // an env that has already moved on belongs to someone else's fallback.
1229
+ const pinned = config.llamaCppBackendOverride !== undefined && config.llamaCppBackendOverride !== 'auto';
1230
+ const fallback = pinned || process.env.GEZEL_LLAMA_SERVER_BIN !== binary
1231
+ ? null
1232
+ : adoptLlamaFallback(home, binary, backend);
1233
+ lastStartupCrash = {
1234
+ incidentId: snapshot.incidentId,
1235
+ verdict: { kind: 'quarantined', ...(fallback ? { switchedTo: fallback.backend } : {}) },
1236
+ };
1237
+ if (fallback) {
1238
+ log.warn(`[llama-server] the next launch uses the ${fallback.backend} build: ${fallback.path}`);
1230
1239
  }
1231
1240
  },
1232
- onRawLine: (line) => providerHolder.current?.onStdoutLine(line),
1233
- // GPU-OOM recovery ladder: when a start dies of VRAM exhaustion and the
1234
- // planner's offload decision was in play, degrade it one step
1235
- // (partial split → all expert/FFN weights to RAM → engine-owned fit) and let the
1236
- // supervisor retry — `resolveLaunch` below re-reads `offloadDecision`
1237
- // on every spawn. The degraded plan sticks for later restarts of this
1238
- // provider, so a recovered engine doesn't re-OOM on its next boot.
1241
+ // Any start that reaches readiness proves the build runs here, so the
1242
+ // crashes before it were not in a row. It does not prove the build got
1243
+ // its GPU: a GPU backend that cannot load or initialize leaves the engine
1244
+ // on the CPU without crashing (gpu-startup-watch.ts).
1245
+ onReady: () => {
1246
+ if (binaryBackend)
1247
+ clearLlamaStartupStrikes(home, binaryBackend);
1248
+ const unused = gpuWatch?.verdict() ?? null;
1249
+ noteGpuStartup(unused);
1250
+ if (unused) {
1251
+ log.warn(`[llama-server] the ${unused.backend} build started without its GPU (${unused.reason}${unused.detail ? `: ${unused.detail}` : ''}); every token runs on the CPU`);
1252
+ }
1253
+ },
1254
+ onRawLine: (line) => {
1255
+ gpuWatch?.observe(line);
1256
+ providerHolder.current?.onStdoutLine(line);
1257
+ },
1258
+ // GPU-OOM recovery ladder: when a start dies of VRAM exhaustion, first
1259
+ // give back the extra slots' KV (every weight stays on the GPU), then, if
1260
+ // the planner's offload decision was in play, degrade it one step
1261
+ // (partial split → all expert/FFN weights to RAM → engine-owned fit) and
1262
+ // let the supervisor retry — `resolveLaunch` below re-reads `slots` and
1263
+ // `offloadDecision` on every spawn. The degraded plan sticks for later
1264
+ // restarts of this provider, so a recovered engine doesn't re-OOM on its
1265
+ // next boot.
1239
1266
  recoverStartup: ({ panicKind }) => {
1240
1267
  if (panicKind !== 'cuda-out-of-memory' && panicKind !== 'vulkan-out-of-memory') {
1241
1268
  return false;
1242
1269
  }
1270
+ // A full-residency plan has no offload rung below it, so before this a
1271
+ // two-slot launch that missed by one KV allocation failed outright
1272
+ // (2026-10-19, Gemma 4 31B on a 32 GB card). This rung spends one of
1273
+ // the supervisor's two retries; an offload rung still has the other.
1274
+ if (slots > 1 && configuredSlots === undefined) {
1275
+ const from = slots;
1276
+ if (providerHolder.current?.narrowEngineSlots(1)) {
1277
+ slots = 1;
1278
+ const kvReserveBytes = reservationKvFor?.(1);
1279
+ if (kvReserveBytes !== undefined && reservationResidentBytes !== undefined) {
1280
+ plannedReservationBytes = reservationResidentBytes + kvReserveBytes;
1281
+ }
1282
+ log.warn(`[llama-cpp] ${panicKind} while starting ${modelCatalogInfo?.id ?? 'model'} — retrying with one engine slot instead of ${from}`);
1283
+ return true;
1284
+ }
1285
+ }
1243
1286
  // Explicit config/manifest offload settings shadow the planner
1244
1287
  // per-field (see `buildLlamaCppEngineArgs`); when every field the
1245
1288
  // ladder would change is pinned, a retry replays the same argv.
@@ -1275,6 +1318,13 @@ export async function buildLlamaCppProvider(opts) {
1275
1318
  return true;
1276
1319
  },
1277
1320
  resolveLaunch: async () => {
1321
+ gpuWatch = new GpuStartupWatch(binaryBackend);
1322
+ // Again at every launch, not only at build: llama.cpp from the 0.1.51
1323
+ // release refuses a --slot-save-path that does not exist ("not a
1324
+ // directory") and exits before ready, and an eval preflight lost its
1325
+ // build-time directory during a 50 s capacity wait (2026-10-18). The
1326
+ // person saw "no model is installed yet".
1327
+ await mkdirAsync(slotSavePath, { recursive: true }).catch(() => { });
1278
1328
  const port = cachedPort ?? (await pickFreePort());
1279
1329
  cachedPort = port;
1280
1330
  // Prepend the binary's own directory to PATH so the OS finds the
@@ -1301,9 +1351,6 @@ export async function buildLlamaCppProvider(opts) {
1301
1351
  // model output reaches `delta.content` for provider-side salvage.
1302
1352
  reasoningFormat: process.env.GEZEL_LLAMA_REASONING_FORMAT?.trim() || undefined,
1303
1353
  reasoningPreserve,
1304
- architecture: modelCatalogInfo?.architecture,
1305
- modelId: defaultModelId ?? undefined,
1306
- swaFullAutoFits,
1307
1354
  backend: llamaBuildMetadata?.backend ?? process.env.GEZEL_LLAMA_SERVER_BACKEND,
1308
1355
  hostCpuCount: availableParallelism(),
1309
1356
  });
@@ -1454,6 +1501,7 @@ export async function buildLlamaCppProvider(opts) {
1454
1501
  supervisor,
1455
1502
  logFile,
1456
1503
  slotSavePath,
1504
+ startupCrashVerdict: (incidentId) => lastStartupCrash?.incidentId === incidentId ? lastStartupCrash.verdict : undefined,
1457
1505
  // Lets `listModels()` enumerate every installed model (not just
1458
1506
  // the resident one) so `/v1/models` + pickers see the full set
1459
1507
  // the engine pool can serve.