@elizaos/plugin-local-inference 2.0.0-beta.1 → 2.0.11-beta.7

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 (676) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/package.json +81 -15
  4. package/src/actions/generate-media.d.ts +59 -0
  5. package/src/actions/generate-media.d.ts.map +1 -0
  6. package/src/actions/generate-media.ts +647 -0
  7. package/src/actions/identify-speaker.d.ts +23 -0
  8. package/src/actions/identify-speaker.d.ts.map +1 -0
  9. package/src/actions/identify-speaker.ts +171 -0
  10. package/src/adapters/capacitor-llama/__tests__/compat-behavior.test.ts +218 -0
  11. package/src/adapters/capacitor-llama/__tests__/index.test.ts +68 -0
  12. package/src/adapters/capacitor-llama/__tests__/structured-output.test.ts +215 -0
  13. package/src/adapters/capacitor-llama/__tests__/text-streaming.test.ts +174 -0
  14. package/src/adapters/capacitor-llama/environment.ts +71 -0
  15. package/src/adapters/capacitor-llama/index.browser.ts +83 -0
  16. package/src/adapters/capacitor-llama/index.ts +807 -0
  17. package/src/adapters/capacitor-llama/loader.ts +109 -0
  18. package/src/adapters/capacitor-llama/structured-output.ts +165 -0
  19. package/src/adapters/capacitor-llama/text-streaming.ts +227 -0
  20. package/src/adapters/capacitor-llama/types.ts +374 -0
  21. package/src/backends/apple-foundation.ts +127 -0
  22. package/src/index.d.ts +7 -0
  23. package/src/index.d.ts.map +1 -0
  24. package/src/index.ts +54 -0
  25. package/src/local-inference-routes.d.ts +38 -0
  26. package/src/local-inference-routes.d.ts.map +1 -0
  27. package/src/local-inference-routes.test.ts +344 -0
  28. package/src/local-inference-routes.ts +1543 -0
  29. package/src/provider.d.ts +21 -0
  30. package/src/provider.d.ts.map +1 -0
  31. package/src/provider.ts +1171 -0
  32. package/src/routes/compat-helpers.d.ts +18 -0
  33. package/src/routes/compat-helpers.d.ts.map +1 -0
  34. package/src/routes/compat-helpers.ts +274 -0
  35. package/src/routes/family-member-route.d.ts +62 -0
  36. package/src/routes/family-member-route.d.ts.map +1 -0
  37. package/src/routes/family-member-route.ts +353 -0
  38. package/src/routes/index.d.ts +19 -0
  39. package/src/routes/index.d.ts.map +1 -0
  40. package/src/routes/index.ts +60 -0
  41. package/src/routes/live-diarization-route.d.ts +26 -0
  42. package/src/routes/live-diarization-route.d.ts.map +1 -0
  43. package/src/routes/live-diarization-route.test.ts +213 -0
  44. package/src/routes/live-diarization-route.ts +122 -0
  45. package/src/routes/local-inference-asr-route.d.ts +4 -0
  46. package/src/routes/local-inference-asr-route.d.ts.map +1 -0
  47. package/src/routes/local-inference-asr-route.test.ts +190 -0
  48. package/src/routes/local-inference-asr-route.ts +213 -0
  49. package/src/routes/local-inference-compat-routes.d.ts +16 -0
  50. package/src/routes/local-inference-compat-routes.d.ts.map +1 -0
  51. package/src/routes/local-inference-compat-routes.test.ts +423 -0
  52. package/src/routes/local-inference-compat-routes.ts +782 -0
  53. package/src/routes/local-inference-tts-route.d.ts +7 -0
  54. package/src/routes/local-inference-tts-route.d.ts.map +1 -0
  55. package/src/routes/local-inference-tts-route.test.ts +179 -0
  56. package/src/routes/local-inference-tts-route.ts +230 -0
  57. package/src/routes/voice-first-run-routes.d.ts +62 -0
  58. package/src/routes/voice-first-run-routes.d.ts.map +1 -0
  59. package/src/routes/voice-first-run-routes.ts +524 -0
  60. package/src/routes/voice-models-routes.d.ts +62 -0
  61. package/src/routes/voice-models-routes.d.ts.map +1 -0
  62. package/src/routes/voice-models-routes.ts +554 -0
  63. package/src/routes/voice-profile-plugin-routes.d.ts +19 -0
  64. package/src/routes/voice-profile-plugin-routes.d.ts.map +1 -0
  65. package/src/routes/voice-profile-plugin-routes.ts +138 -0
  66. package/src/routes/voice-profiles-management-routes.d.ts +52 -0
  67. package/src/routes/voice-profiles-management-routes.d.ts.map +1 -0
  68. package/src/routes/voice-profiles-management-routes.ts +476 -0
  69. package/src/routes/voice-speaker-profile-routes.d.ts +57 -0
  70. package/src/routes/voice-speaker-profile-routes.d.ts.map +1 -0
  71. package/src/routes/voice-speaker-profile-routes.ts +199 -0
  72. package/src/runtime/aosp-llama-loader-selection.test.ts +80 -0
  73. package/src/runtime/capacitor-llama.d.ts +25 -0
  74. package/src/runtime/embedding-manager-support.d.ts +77 -0
  75. package/src/runtime/embedding-manager-support.d.ts.map +1 -0
  76. package/src/runtime/embedding-manager-support.ts +497 -0
  77. package/src/runtime/embedding-presets.d.ts +16 -0
  78. package/src/runtime/embedding-presets.d.ts.map +1 -0
  79. package/src/runtime/embedding-presets.ts +81 -0
  80. package/src/runtime/embedding-warmup-policy.d.ts +14 -0
  81. package/src/runtime/embedding-warmup-policy.d.ts.map +1 -0
  82. package/src/runtime/embedding-warmup-policy.test.ts +53 -0
  83. package/src/runtime/embedding-warmup-policy.ts +48 -0
  84. package/src/runtime/ensure-local-inference-handler.d.ts +53 -0
  85. package/src/runtime/ensure-local-inference-handler.d.ts.map +1 -0
  86. package/src/runtime/ensure-local-inference-handler.test.ts +528 -0
  87. package/src/runtime/ensure-local-inference-handler.ts +1398 -0
  88. package/src/runtime/index.d.ts +14 -0
  89. package/src/runtime/index.d.ts.map +1 -0
  90. package/src/runtime/index.ts +27 -0
  91. package/src/runtime/mobile-local-inference-gate.d.ts +31 -0
  92. package/src/runtime/mobile-local-inference-gate.d.ts.map +1 -0
  93. package/src/runtime/mobile-local-inference-gate.test.ts +69 -0
  94. package/src/runtime/mobile-local-inference-gate.ts +44 -0
  95. package/src/runtime/voice-entity-binding.d.ts +103 -0
  96. package/src/runtime/voice-entity-binding.d.ts.map +1 -0
  97. package/src/runtime/voice-entity-binding.transcript.test.ts +69 -0
  98. package/src/runtime/voice-entity-binding.ts +328 -0
  99. package/src/services/README.md +71 -0
  100. package/src/services/__tests__/backend-selector.test.ts +101 -0
  101. package/src/services/__tests__/checkpoint-manager.test.ts +376 -0
  102. package/src/services/__tests__/gpu-autotune.test.ts +400 -0
  103. package/src/services/__tests__/llm-streaming-binding.test.ts +85 -0
  104. package/src/services/__tests__/planner-grammar.test.ts +372 -0
  105. package/src/services/__tests__/runtime-target.test.ts +176 -0
  106. package/src/services/active-model-switch-rollback.test.ts +183 -0
  107. package/src/services/active-model.d.ts +282 -0
  108. package/src/services/active-model.d.ts.map +1 -0
  109. package/src/services/active-model.ts +1213 -0
  110. package/src/services/asr/errors.d.ts +21 -0
  111. package/src/services/asr/errors.d.ts.map +1 -0
  112. package/src/services/asr/errors.ts +50 -0
  113. package/src/services/asr/hash.d.ts +28 -0
  114. package/src/services/asr/hash.d.ts.map +1 -0
  115. package/src/services/asr/hash.ts +49 -0
  116. package/src/services/asr/index.d.ts +76 -0
  117. package/src/services/asr/index.d.ts.map +1 -0
  118. package/src/services/asr/index.ts +178 -0
  119. package/src/services/asr/types.d.ts +91 -0
  120. package/src/services/asr/types.d.ts.map +1 -0
  121. package/src/services/asr/types.ts +95 -0
  122. package/src/services/assignments.d.ts +71 -0
  123. package/src/services/assignments.d.ts.map +1 -0
  124. package/src/services/assignments.test.ts +80 -0
  125. package/src/services/assignments.ts +230 -0
  126. package/src/services/backend-selector.ts +95 -0
  127. package/src/services/backend.d.ts +346 -0
  128. package/src/services/backend.d.ts.map +1 -0
  129. package/src/services/backend.ts +612 -0
  130. package/src/services/bundled-models.d.ts +34 -0
  131. package/src/services/bundled-models.d.ts.map +1 -0
  132. package/src/services/bundled-models.ts +129 -0
  133. package/src/services/cache-bridge.d.ts +206 -0
  134. package/src/services/cache-bridge.d.ts.map +1 -0
  135. package/src/services/cache-bridge.test.ts +516 -0
  136. package/src/services/cache-bridge.ts +423 -0
  137. package/src/services/catalog.d.ts +10 -0
  138. package/src/services/catalog.d.ts.map +1 -0
  139. package/src/services/catalog.test.ts +240 -0
  140. package/src/services/catalog.ts +27 -0
  141. package/src/services/checkpoint-client.d.ts +109 -0
  142. package/src/services/checkpoint-client.d.ts.map +1 -0
  143. package/src/services/checkpoint-client.ts +258 -0
  144. package/src/services/checkpoint-manager.ts +474 -0
  145. package/src/services/cloud-fallback.d.ts +102 -0
  146. package/src/services/cloud-fallback.d.ts.map +1 -0
  147. package/src/services/cloud-fallback.ts +230 -0
  148. package/src/services/conversation-registry.d.ts +142 -0
  149. package/src/services/conversation-registry.d.ts.map +1 -0
  150. package/src/services/conversation-registry.test.ts +235 -0
  151. package/src/services/conversation-registry.ts +264 -0
  152. package/src/services/desktop-fused-ffi-backend-runtime.d.ts +92 -0
  153. package/src/services/desktop-fused-ffi-backend-runtime.d.ts.map +1 -0
  154. package/src/services/desktop-fused-ffi-backend-runtime.ts +333 -0
  155. package/src/services/device-bridge.d.ts +188 -0
  156. package/src/services/device-bridge.d.ts.map +1 -0
  157. package/src/services/device-bridge.ts +1237 -0
  158. package/src/services/device-resource-metrics.d.ts +149 -0
  159. package/src/services/device-resource-metrics.d.ts.map +1 -0
  160. package/src/services/device-resource-metrics.test.ts +98 -0
  161. package/src/services/device-resource-metrics.ts +346 -0
  162. package/src/services/device-tier.d.ts +115 -0
  163. package/src/services/device-tier.d.ts.map +1 -0
  164. package/src/services/device-tier.test.ts +371 -0
  165. package/src/services/device-tier.ts +410 -0
  166. package/src/services/downloader.d.ts +82 -0
  167. package/src/services/downloader.d.ts.map +1 -0
  168. package/src/services/downloader.test.ts +724 -0
  169. package/src/services/downloader.ts +899 -0
  170. package/src/services/engine-direct-bundle.test.ts +58 -0
  171. package/src/services/engine-streaming.test.ts +80 -0
  172. package/src/services/engine.d.ts +534 -0
  173. package/src/services/engine.d.ts.map +1 -0
  174. package/src/services/engine.ts +1891 -0
  175. package/src/services/ensure-local-artifacts.integration.test.ts +273 -0
  176. package/src/services/ensure-local-artifacts.test.ts +368 -0
  177. package/src/services/ensure-local-artifacts.ts +351 -0
  178. package/src/services/external-scanner.d.ts +17 -0
  179. package/src/services/external-scanner.d.ts.map +1 -0
  180. package/src/services/external-scanner.ts +312 -0
  181. package/src/services/ffi-llm-mock.ts +354 -0
  182. package/src/services/ffi-llm-streaming-abi.ts +442 -0
  183. package/src/services/ffi-streaming-backend.d.ts +180 -0
  184. package/src/services/ffi-streaming-backend.d.ts.map +1 -0
  185. package/src/services/ffi-streaming-backend.ts +382 -0
  186. package/src/services/ffi-streaming-runner.d.ts +122 -0
  187. package/src/services/ffi-streaming-runner.d.ts.map +1 -0
  188. package/src/services/ffi-streaming-runner.test.ts +60 -0
  189. package/src/services/ffi-streaming-runner.ts +354 -0
  190. package/src/services/ffi-unload-ordering.test.ts +162 -0
  191. package/src/services/gpu-autotune.ts +534 -0
  192. package/src/services/gpu-detect.ts +139 -0
  193. package/src/services/handler-registry.d.ts +72 -0
  194. package/src/services/handler-registry.d.ts.map +1 -0
  195. package/src/services/handler-registry.ts +240 -0
  196. package/src/services/hardware.d.ts +63 -0
  197. package/src/services/hardware.d.ts.map +1 -0
  198. package/src/services/hardware.test.ts +183 -0
  199. package/src/services/hardware.ts +404 -0
  200. package/src/services/hf-search.d.ts +26 -0
  201. package/src/services/hf-search.d.ts.map +1 -0
  202. package/src/services/hf-search.test.ts +69 -0
  203. package/src/services/hf-search.ts +420 -0
  204. package/src/services/image-description-runtime.d.ts +14 -0
  205. package/src/services/image-description-runtime.d.ts.map +1 -0
  206. package/src/services/image-description-runtime.test.ts +61 -0
  207. package/src/services/image-description-runtime.ts +118 -0
  208. package/src/services/imagegen/aosp-unavailable.d.ts +134 -0
  209. package/src/services/imagegen/aosp-unavailable.d.ts.map +1 -0
  210. package/src/services/imagegen/aosp-unavailable.ts +229 -0
  211. package/src/services/imagegen/backend-selector.d.ts +118 -0
  212. package/src/services/imagegen/backend-selector.d.ts.map +1 -0
  213. package/src/services/imagegen/backend-selector.ts +281 -0
  214. package/src/services/imagegen/coreml-unavailable.d.ts +105 -0
  215. package/src/services/imagegen/coreml-unavailable.d.ts.map +1 -0
  216. package/src/services/imagegen/coreml-unavailable.ts +237 -0
  217. package/src/services/imagegen/errors.d.ts +16 -0
  218. package/src/services/imagegen/errors.d.ts.map +1 -0
  219. package/src/services/imagegen/errors.ts +40 -0
  220. package/src/services/imagegen/index.d.ts +58 -0
  221. package/src/services/imagegen/index.d.ts.map +1 -0
  222. package/src/services/imagegen/index.ts +144 -0
  223. package/src/services/imagegen/mflux.d.ts +74 -0
  224. package/src/services/imagegen/mflux.d.ts.map +1 -0
  225. package/src/services/imagegen/mflux.ts +313 -0
  226. package/src/services/imagegen/sd-cpp.d.ts +180 -0
  227. package/src/services/imagegen/sd-cpp.d.ts.map +1 -0
  228. package/src/services/imagegen/sd-cpp.ts +718 -0
  229. package/src/services/imagegen/tensorrt-unavailable.d.ts +83 -0
  230. package/src/services/imagegen/tensorrt-unavailable.d.ts.map +1 -0
  231. package/src/services/imagegen/tensorrt-unavailable.ts +295 -0
  232. package/src/services/imagegen/types.d.ts +181 -0
  233. package/src/services/imagegen/types.d.ts.map +1 -0
  234. package/src/services/imagegen/types.ts +193 -0
  235. package/src/services/index.d.ts +30 -0
  236. package/src/services/index.d.ts.map +1 -0
  237. package/src/services/index.ts +225 -0
  238. package/src/services/inference-capabilities.d.ts +132 -0
  239. package/src/services/inference-capabilities.d.ts.map +1 -0
  240. package/src/services/inference-capabilities.test.ts +75 -0
  241. package/src/services/inference-capabilities.ts +204 -0
  242. package/src/services/inference-telemetry.d.ts +59 -0
  243. package/src/services/inference-telemetry.d.ts.map +1 -0
  244. package/src/services/inference-telemetry.ts +143 -0
  245. package/src/services/ios-llama-streaming.ts +248 -0
  246. package/src/services/kv-spill.d.ts +189 -0
  247. package/src/services/kv-spill.d.ts.map +1 -0
  248. package/src/services/kv-spill.test.ts +222 -0
  249. package/src/services/kv-spill.ts +356 -0
  250. package/src/services/latency-trace.d.ts +346 -0
  251. package/src/services/latency-trace.d.ts.map +1 -0
  252. package/src/services/latency-trace.test.ts +266 -0
  253. package/src/services/latency-trace.ts +844 -0
  254. package/src/services/llama-server-metrics.ts +304 -0
  255. package/src/services/llm-streaming-binding.d.ts +96 -0
  256. package/src/services/llm-streaming-binding.d.ts.map +1 -0
  257. package/src/services/llm-streaming-binding.ts +136 -0
  258. package/src/services/load-args.d.ts +82 -0
  259. package/src/services/load-args.d.ts.map +1 -0
  260. package/src/services/load-args.ts +81 -0
  261. package/src/services/manifest/eliza-1.manifest.v1.json +708 -0
  262. package/src/services/manifest/index.d.ts +4 -0
  263. package/src/services/manifest/index.d.ts.map +1 -0
  264. package/src/services/manifest/index.ts +66 -0
  265. package/src/services/manifest/manifest.test.ts +693 -0
  266. package/src/services/manifest/schema.d.ts +715 -0
  267. package/src/services/manifest/schema.d.ts.map +1 -0
  268. package/src/services/manifest/schema.ts +655 -0
  269. package/src/services/manifest/types.d.ts +30 -0
  270. package/src/services/manifest/types.d.ts.map +1 -0
  271. package/src/services/manifest/types.ts +55 -0
  272. package/src/services/manifest/validator.d.ts +66 -0
  273. package/src/services/manifest/validator.d.ts.map +1 -0
  274. package/src/services/manifest/validator.ts +569 -0
  275. package/src/services/memory-arbiter.d.ts +343 -0
  276. package/src/services/memory-arbiter.d.ts.map +1 -0
  277. package/src/services/memory-arbiter.test.ts +419 -0
  278. package/src/services/memory-arbiter.ts +1000 -0
  279. package/src/services/memory-monitor.d.ts +119 -0
  280. package/src/services/memory-monitor.d.ts.map +1 -0
  281. package/src/services/memory-monitor.test.ts +208 -0
  282. package/src/services/memory-monitor.ts +296 -0
  283. package/src/services/memory-pressure.d.ts +127 -0
  284. package/src/services/memory-pressure.d.ts.map +1 -0
  285. package/src/services/memory-pressure.ts +413 -0
  286. package/src/services/mtp-doctor.d.ts +13 -0
  287. package/src/services/mtp-doctor.d.ts.map +1 -0
  288. package/src/services/mtp-doctor.ts +78 -0
  289. package/src/services/network-policy.d.ts +127 -0
  290. package/src/services/network-policy.d.ts.map +1 -0
  291. package/src/services/network-policy.ts +346 -0
  292. package/src/services/paths.d.ts +6 -0
  293. package/src/services/paths.d.ts.map +1 -0
  294. package/src/services/paths.ts +25 -0
  295. package/src/services/planner-skeleton.d.ts +124 -0
  296. package/src/services/planner-skeleton.d.ts.map +1 -0
  297. package/src/services/planner-skeleton.ts +175 -0
  298. package/src/services/providers.d.ts +38 -0
  299. package/src/services/providers.d.ts.map +1 -0
  300. package/src/services/providers.ts +507 -0
  301. package/src/services/ram-budget-cache.test.ts +163 -0
  302. package/src/services/ram-budget.d.ts +110 -0
  303. package/src/services/ram-budget.d.ts.map +1 -0
  304. package/src/services/ram-budget.ts +0 -0
  305. package/src/services/readiness.d.ts +9 -0
  306. package/src/services/readiness.d.ts.map +1 -0
  307. package/src/services/readiness.test.ts +87 -0
  308. package/src/services/readiness.ts +238 -0
  309. package/src/services/recommendation.d.ts +111 -0
  310. package/src/services/recommendation.d.ts.map +1 -0
  311. package/src/services/recommendation.ts +672 -0
  312. package/src/services/registry.d.ts +35 -0
  313. package/src/services/registry.d.ts.map +1 -0
  314. package/src/services/registry.ts +151 -0
  315. package/src/services/router-handler.d.ts +92 -0
  316. package/src/services/router-handler.d.ts.map +1 -0
  317. package/src/services/router-handler.test.ts +45 -0
  318. package/src/services/router-handler.ts +376 -0
  319. package/src/services/routing-policy.d.ts +55 -0
  320. package/src/services/routing-policy.d.ts.map +1 -0
  321. package/src/services/routing-policy.ts +228 -0
  322. package/src/services/routing-preferences.d.ts +8 -0
  323. package/src/services/routing-preferences.d.ts.map +1 -0
  324. package/src/services/routing-preferences.ts +15 -0
  325. package/src/services/runtime-target.d.ts +98 -0
  326. package/src/services/runtime-target.d.ts.map +1 -0
  327. package/src/services/runtime-target.ts +154 -0
  328. package/src/services/service.d.ts +128 -0
  329. package/src/services/service.d.ts.map +1 -0
  330. package/src/services/service.test.ts +223 -0
  331. package/src/services/service.ts +735 -0
  332. package/src/services/session-pool.d.ts +72 -0
  333. package/src/services/session-pool.d.ts.map +1 -0
  334. package/src/services/session-pool.ts +153 -0
  335. package/src/services/structured-output/deterministic-repair.d.ts +23 -0
  336. package/src/services/structured-output/deterministic-repair.d.ts.map +1 -0
  337. package/src/services/structured-output/deterministic-repair.test.ts +169 -0
  338. package/src/services/structured-output/deterministic-repair.ts +443 -0
  339. package/src/services/structured-output/index.ts +4 -0
  340. package/src/services/structured-output.d.ts +311 -0
  341. package/src/services/structured-output.d.ts.map +1 -0
  342. package/src/services/structured-output.test.ts +483 -0
  343. package/src/services/structured-output.ts +712 -0
  344. package/src/services/transcription-priority.test.ts +211 -0
  345. package/src/services/tts/errors.ts +46 -0
  346. package/src/services/tts/index.ts +214 -0
  347. package/src/services/tts/tts-audio-cache.ts +235 -0
  348. package/src/services/tts/types.ts +157 -0
  349. package/src/services/types.d.ts +19 -0
  350. package/src/services/types.d.ts.map +1 -0
  351. package/src/services/types.ts +55 -0
  352. package/src/services/verify-on-device.d.ts +34 -0
  353. package/src/services/verify-on-device.d.ts.map +1 -0
  354. package/src/services/verify-on-device.test.ts +87 -0
  355. package/src/services/verify-on-device.ts +127 -0
  356. package/src/services/verify.d.ts +8 -0
  357. package/src/services/verify.d.ts.map +1 -0
  358. package/src/services/verify.ts +13 -0
  359. package/src/services/vision/aosp-unavailable.d.ts +115 -0
  360. package/src/services/vision/aosp-unavailable.d.ts.map +1 -0
  361. package/src/services/vision/aosp-unavailable.ts +163 -0
  362. package/src/services/vision/capacitor-llama.d.ts +99 -0
  363. package/src/services/vision/capacitor-llama.d.ts.map +1 -0
  364. package/src/services/vision/capacitor-llama.ts +255 -0
  365. package/src/services/vision/cloud-fallback.d.ts +47 -0
  366. package/src/services/vision/cloud-fallback.d.ts.map +1 -0
  367. package/src/services/vision/cloud-fallback.test.ts +243 -0
  368. package/src/services/vision/cloud-fallback.ts +268 -0
  369. package/src/services/vision/fallback-chain.test.ts +86 -0
  370. package/src/services/vision/hash.d.ts +71 -0
  371. package/src/services/vision/hash.d.ts.map +1 -0
  372. package/src/services/vision/hash.ts +157 -0
  373. package/src/services/vision/index.d.ts +95 -0
  374. package/src/services/vision/index.d.ts.map +1 -0
  375. package/src/services/vision/index.ts +251 -0
  376. package/src/services/vision/llama-server.d.ts +73 -0
  377. package/src/services/vision/llama-server.d.ts.map +1 -0
  378. package/src/services/vision/llama-server.ts +177 -0
  379. package/src/services/vision/types.d.ts +153 -0
  380. package/src/services/vision/types.d.ts.map +1 -0
  381. package/src/services/vision/types.ts +154 -0
  382. package/src/services/vision/vast-fallback.d.ts +18 -0
  383. package/src/services/vision/vast-fallback.d.ts.map +1 -0
  384. package/src/services/vision/vast-fallback.ts +127 -0
  385. package/src/services/vision-embedding-cache.d.ts +98 -0
  386. package/src/services/vision-embedding-cache.d.ts.map +1 -0
  387. package/src/services/vision-embedding-cache.ts +189 -0
  388. package/src/services/voice/VOICE_WORKBENCH.md +88 -0
  389. package/src/services/voice/__test-helpers__/fake-ffi.ts +92 -0
  390. package/src/services/voice/__test-helpers__/synthetic-speech.ts +124 -0
  391. package/src/services/voice/__tests__/checkpoint-manager.test.ts +241 -0
  392. package/src/services/voice/__tests__/checkpoint-policy.test.ts +270 -0
  393. package/src/services/voice/__tests__/eager-context-builder.test.ts +257 -0
  394. package/src/services/voice/__tests__/eliza1-eot-scorer.test.ts +288 -0
  395. package/src/services/voice/__tests__/eot-classifier.test.ts +431 -0
  396. package/src/services/voice/__tests__/optimistic-rollback.test.ts +312 -0
  397. package/src/services/voice/__tests__/prefill-client.test.ts +266 -0
  398. package/src/services/voice/__tests__/prefix-preserving-queue.test.ts +208 -0
  399. package/src/services/voice/__tests__/streaming-asr.test.ts +450 -0
  400. package/src/services/voice/__tests__/streaming-transcriber.test.ts +339 -0
  401. package/src/services/voice/__tests__/turn-detector-resolver.test.ts +197 -0
  402. package/src/services/voice/__tests__/voice-state-machine-prefill.test.ts +275 -0
  403. package/src/services/voice/__tests__/voice-state-machine.test.ts +354 -0
  404. package/src/services/voice/audio-frame-consumer.d.ts +212 -0
  405. package/src/services/voice/audio-frame-consumer.d.ts.map +1 -0
  406. package/src/services/voice/audio-frame-consumer.test.ts +343 -0
  407. package/src/services/voice/audio-frame-consumer.ts +491 -0
  408. package/src/services/voice/barge-in.d.ts +112 -0
  409. package/src/services/voice/barge-in.d.ts.map +1 -0
  410. package/src/services/voice/barge-in.test.ts +244 -0
  411. package/src/services/voice/barge-in.ts +336 -0
  412. package/src/services/voice/cancellation-coordinator.d.ts +127 -0
  413. package/src/services/voice/cancellation-coordinator.d.ts.map +1 -0
  414. package/src/services/voice/cancellation-coordinator.test.ts +196 -0
  415. package/src/services/voice/cancellation-coordinator.ts +269 -0
  416. package/src/services/voice/checkpoint-manager.d.ts +199 -0
  417. package/src/services/voice/checkpoint-manager.d.ts.map +1 -0
  418. package/src/services/voice/checkpoint-manager.ts +401 -0
  419. package/src/services/voice/checkpoint-policy.ts +336 -0
  420. package/src/services/voice/composite-eot-classifier.test.ts +59 -0
  421. package/src/services/voice/e2e-harness.test.ts +182 -0
  422. package/src/services/voice/e2e-harness.ts +743 -0
  423. package/src/services/voice/eager-context-builder.d.ts +170 -0
  424. package/src/services/voice/eager-context-builder.d.ts.map +1 -0
  425. package/src/services/voice/eager-context-builder.ts +262 -0
  426. package/src/services/voice/eliza1-eot-scorer.d.ts +124 -0
  427. package/src/services/voice/eliza1-eot-scorer.d.ts.map +1 -0
  428. package/src/services/voice/eliza1-eot-scorer.ts +242 -0
  429. package/src/services/voice/embedding-server.ts +200 -0
  430. package/src/services/voice/embedding.d.ts +133 -0
  431. package/src/services/voice/embedding.d.ts.map +1 -0
  432. package/src/services/voice/embedding.test.ts +148 -0
  433. package/src/services/voice/embedding.ts +244 -0
  434. package/src/services/voice/emotion-attribution.d.ts +68 -0
  435. package/src/services/voice/emotion-attribution.d.ts.map +1 -0
  436. package/src/services/voice/emotion-attribution.test.ts +129 -0
  437. package/src/services/voice/emotion-attribution.ts +361 -0
  438. package/src/services/voice/engine-bridge-cancellation.test.ts +422 -0
  439. package/src/services/voice/engine-bridge.d.ts +746 -0
  440. package/src/services/voice/engine-bridge.d.ts.map +1 -0
  441. package/src/services/voice/engine-bridge.test.ts +384 -0
  442. package/src/services/voice/engine-bridge.ts +2226 -0
  443. package/src/services/voice/eot-classifier-ggml.d.ts +179 -0
  444. package/src/services/voice/eot-classifier-ggml.d.ts.map +1 -0
  445. package/src/services/voice/eot-classifier-ggml.ts +566 -0
  446. package/src/services/voice/eot-classifier.d.ts +214 -0
  447. package/src/services/voice/eot-classifier.d.ts.map +1 -0
  448. package/src/services/voice/eot-classifier.ts +533 -0
  449. package/src/services/voice/errors.d.ts +20 -0
  450. package/src/services/voice/errors.d.ts.map +1 -0
  451. package/src/services/voice/errors.ts +32 -0
  452. package/src/services/voice/expressive-tags.d.ts +158 -0
  453. package/src/services/voice/expressive-tags.d.ts.map +1 -0
  454. package/src/services/voice/expressive-tags.ts +405 -0
  455. package/src/services/voice/ffi-bindings.d.ts +636 -0
  456. package/src/services/voice/ffi-bindings.d.ts.map +1 -0
  457. package/src/services/voice/ffi-bindings.test.ts +671 -0
  458. package/src/services/voice/ffi-bindings.ts +3050 -0
  459. package/src/services/voice/first-line-cache.d.ts +181 -0
  460. package/src/services/voice/first-line-cache.d.ts.map +1 -0
  461. package/src/services/voice/first-line-cache.ts +725 -0
  462. package/src/services/voice/fused-eot-scorer.d.ts +51 -0
  463. package/src/services/voice/fused-eot-scorer.d.ts.map +1 -0
  464. package/src/services/voice/fused-eot-scorer.ts +135 -0
  465. package/src/services/voice/index.d.ts +91 -0
  466. package/src/services/voice/index.d.ts.map +1 -0
  467. package/src/services/voice/index.ts +481 -0
  468. package/src/services/voice/kokoro/__tests__/kokoro-backend.test.ts +151 -0
  469. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.real.test.ts +151 -0
  470. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.test.ts +60 -0
  471. package/src/services/voice/kokoro/__tests__/kokoro-engine-discovery.test.ts +277 -0
  472. package/src/services/voice/kokoro/__tests__/kokoro-ffi-runtime.test.ts +235 -0
  473. package/src/services/voice/kokoro/__tests__/kokoro-runtime.test.ts +95 -0
  474. package/src/services/voice/kokoro/__tests__/phonemizer.test.ts +53 -0
  475. package/src/services/voice/kokoro/__tests__/runtime-selection.test.ts +231 -0
  476. package/src/services/voice/kokoro/__tests__/voices.test.ts +57 -0
  477. package/src/services/voice/kokoro/index.ts +79 -0
  478. package/src/services/voice/kokoro/kokoro-backend.d.ts +72 -0
  479. package/src/services/voice/kokoro/kokoro-backend.d.ts.map +1 -0
  480. package/src/services/voice/kokoro/kokoro-backend.ts +207 -0
  481. package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts +58 -0
  482. package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts.map +1 -0
  483. package/src/services/voice/kokoro/kokoro-engine-discovery.ts +177 -0
  484. package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts +75 -0
  485. package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts.map +1 -0
  486. package/src/services/voice/kokoro/kokoro-ffi-runtime.ts +233 -0
  487. package/src/services/voice/kokoro/kokoro-runtime.d.ts +100 -0
  488. package/src/services/voice/kokoro/kokoro-runtime.d.ts.map +1 -0
  489. package/src/services/voice/kokoro/kokoro-runtime.ts +170 -0
  490. package/src/services/voice/kokoro/phoneme-stream.ts +123 -0
  491. package/src/services/voice/kokoro/phonemizer.d.ts +50 -0
  492. package/src/services/voice/kokoro/phonemizer.d.ts.map +1 -0
  493. package/src/services/voice/kokoro/phonemizer.ts +344 -0
  494. package/src/services/voice/kokoro/pick-runtime.d.ts +61 -0
  495. package/src/services/voice/kokoro/pick-runtime.d.ts.map +1 -0
  496. package/src/services/voice/kokoro/pick-runtime.test.ts +91 -0
  497. package/src/services/voice/kokoro/pick-runtime.ts +130 -0
  498. package/src/services/voice/kokoro/runtime-selection.d.ts +92 -0
  499. package/src/services/voice/kokoro/runtime-selection.d.ts.map +1 -0
  500. package/src/services/voice/kokoro/runtime-selection.ts +237 -0
  501. package/src/services/voice/kokoro/types.d.ts +82 -0
  502. package/src/services/voice/kokoro/types.d.ts.map +1 -0
  503. package/src/services/voice/kokoro/types.ts +95 -0
  504. package/src/services/voice/kokoro/voice-presets.d.ts +23 -0
  505. package/src/services/voice/kokoro/voice-presets.d.ts.map +1 -0
  506. package/src/services/voice/kokoro/voice-presets.ts +129 -0
  507. package/src/services/voice/kokoro/voices.d.ts +30 -0
  508. package/src/services/voice/kokoro/voices.d.ts.map +1 -0
  509. package/src/services/voice/kokoro/voices.ts +64 -0
  510. package/src/services/voice/lifecycle.d.ts +135 -0
  511. package/src/services/voice/lifecycle.d.ts.map +1 -0
  512. package/src/services/voice/lifecycle.test.ts +315 -0
  513. package/src/services/voice/lifecycle.ts +301 -0
  514. package/src/services/voice/live-diarization-session.d.ts +96 -0
  515. package/src/services/voice/live-diarization-session.d.ts.map +1 -0
  516. package/src/services/voice/live-diarization-session.ts +289 -0
  517. package/src/services/voice/mic-source.d.ts +136 -0
  518. package/src/services/voice/mic-source.d.ts.map +1 -0
  519. package/src/services/voice/mic-source.test.ts +210 -0
  520. package/src/services/voice/mic-source.ts +503 -0
  521. package/src/services/voice/optimistic-policy.d.ts +109 -0
  522. package/src/services/voice/optimistic-policy.d.ts.map +1 -0
  523. package/src/services/voice/optimistic-policy.test.ts +101 -0
  524. package/src/services/voice/optimistic-policy.ts +192 -0
  525. package/src/services/voice/optimistic-rollback.ts +343 -0
  526. package/src/services/voice/partial-stabilizer.d.ts +73 -0
  527. package/src/services/voice/partial-stabilizer.d.ts.map +1 -0
  528. package/src/services/voice/partial-stabilizer.test.ts +68 -0
  529. package/src/services/voice/partial-stabilizer.ts +140 -0
  530. package/src/services/voice/phoneme-tokenizer.d.ts +49 -0
  531. package/src/services/voice/phoneme-tokenizer.d.ts.map +1 -0
  532. package/src/services/voice/phoneme-tokenizer.ts +158 -0
  533. package/src/services/voice/phrase-cache.d.ts +76 -0
  534. package/src/services/voice/phrase-cache.d.ts.map +1 -0
  535. package/src/services/voice/phrase-cache.test.ts +242 -0
  536. package/src/services/voice/phrase-cache.ts +186 -0
  537. package/src/services/voice/phrase-chunker.d.ts +62 -0
  538. package/src/services/voice/phrase-chunker.d.ts.map +1 -0
  539. package/src/services/voice/phrase-chunker.test.ts +239 -0
  540. package/src/services/voice/phrase-chunker.ts +281 -0
  541. package/src/services/voice/pipeline-impls.d.ts +151 -0
  542. package/src/services/voice/pipeline-impls.d.ts.map +1 -0
  543. package/src/services/voice/pipeline-impls.l6.test.ts +110 -0
  544. package/src/services/voice/pipeline-impls.test.ts +292 -0
  545. package/src/services/voice/pipeline-impls.ts +315 -0
  546. package/src/services/voice/pipeline.d.ts +216 -0
  547. package/src/services/voice/pipeline.d.ts.map +1 -0
  548. package/src/services/voice/pipeline.ts +505 -0
  549. package/src/services/voice/prefill-client.d.ts +123 -0
  550. package/src/services/voice/prefill-client.d.ts.map +1 -0
  551. package/src/services/voice/prefill-client.ts +316 -0
  552. package/src/services/voice/prefix-preserving-queue.d.ts +113 -0
  553. package/src/services/voice/prefix-preserving-queue.d.ts.map +1 -0
  554. package/src/services/voice/prefix-preserving-queue.ts +162 -0
  555. package/src/services/voice/profile-store.d.ts +248 -0
  556. package/src/services/voice/profile-store.d.ts.map +1 -0
  557. package/src/services/voice/profile-store.ts +887 -0
  558. package/src/services/voice/ring-buffer.d.ts +40 -0
  559. package/src/services/voice/ring-buffer.d.ts.map +1 -0
  560. package/src/services/voice/ring-buffer.ts +105 -0
  561. package/src/services/voice/rollback-queue.d.ts +24 -0
  562. package/src/services/voice/rollback-queue.d.ts.map +1 -0
  563. package/src/services/voice/rollback-queue.ts +74 -0
  564. package/src/services/voice/samantha-preset-placeholder.d.ts +67 -0
  565. package/src/services/voice/samantha-preset-placeholder.d.ts.map +1 -0
  566. package/src/services/voice/samantha-preset-placeholder.test.ts +97 -0
  567. package/src/services/voice/samantha-preset-placeholder.ts +148 -0
  568. package/src/services/voice/samantha-preset-regenerator.d.ts +87 -0
  569. package/src/services/voice/samantha-preset-regenerator.d.ts.map +1 -0
  570. package/src/services/voice/samantha-preset-regenerator.ts +393 -0
  571. package/src/services/voice/scheduler.d.ts +146 -0
  572. package/src/services/voice/scheduler.d.ts.map +1 -0
  573. package/src/services/voice/scheduler.t2.test.ts +141 -0
  574. package/src/services/voice/scheduler.ts +927 -0
  575. package/src/services/voice/shared-resources.d.ts +190 -0
  576. package/src/services/voice/shared-resources.d.ts.map +1 -0
  577. package/src/services/voice/shared-resources.ts +320 -0
  578. package/src/services/voice/speaker/attribution-pipeline.d.ts +74 -0
  579. package/src/services/voice/speaker/attribution-pipeline.d.ts.map +1 -0
  580. package/src/services/voice/speaker/attribution-pipeline.ts +386 -0
  581. package/src/services/voice/speaker/diarizer-fused.d.ts +59 -0
  582. package/src/services/voice/speaker/diarizer-fused.d.ts.map +1 -0
  583. package/src/services/voice/speaker/diarizer-fused.real.test.ts +100 -0
  584. package/src/services/voice/speaker/diarizer-fused.ts +154 -0
  585. package/src/services/voice/speaker/diarizer.d.ts +75 -0
  586. package/src/services/voice/speaker/diarizer.d.ts.map +1 -0
  587. package/src/services/voice/speaker/diarizer.ts +218 -0
  588. package/src/services/voice/speaker/encoder-fused.d.ts +60 -0
  589. package/src/services/voice/speaker/encoder-fused.d.ts.map +1 -0
  590. package/src/services/voice/speaker/encoder-fused.real.test.ts +113 -0
  591. package/src/services/voice/speaker/encoder-fused.ts +138 -0
  592. package/src/services/voice/speaker/encoder-ggml.d.ts +33 -0
  593. package/src/services/voice/speaker/encoder-ggml.d.ts.map +1 -0
  594. package/src/services/voice/speaker/encoder-ggml.ts +79 -0
  595. package/src/services/voice/speaker/encoder.d.ts +37 -0
  596. package/src/services/voice/speaker/encoder.d.ts.map +1 -0
  597. package/src/services/voice/speaker/encoder.ts +105 -0
  598. package/src/services/voice/speaker-imprint.d.ts +83 -0
  599. package/src/services/voice/speaker-imprint.d.ts.map +1 -0
  600. package/src/services/voice/speaker-imprint.test.ts +185 -0
  601. package/src/services/voice/speaker-imprint.ts +312 -0
  602. package/src/services/voice/speaker-preset-cache.d.ts +77 -0
  603. package/src/services/voice/speaker-preset-cache.d.ts.map +1 -0
  604. package/src/services/voice/speaker-preset-cache.test.ts +154 -0
  605. package/src/services/voice/speaker-preset-cache.ts +195 -0
  606. package/src/services/voice/streaming-asr/streaming-pipeline-adapter.ts +292 -0
  607. package/src/services/voice/system-audio-sink.d.ts +73 -0
  608. package/src/services/voice/system-audio-sink.d.ts.map +1 -0
  609. package/src/services/voice/system-audio-sink.test.ts +29 -0
  610. package/src/services/voice/system-audio-sink.ts +366 -0
  611. package/src/services/voice/transcriber.d.ts +244 -0
  612. package/src/services/voice/transcriber.d.ts.map +1 -0
  613. package/src/services/voice/transcriber.test.ts +392 -0
  614. package/src/services/voice/transcriber.ts +704 -0
  615. package/src/services/voice/turn-controller.d.ts +183 -0
  616. package/src/services/voice/turn-controller.d.ts.map +1 -0
  617. package/src/services/voice/turn-controller.test.ts +575 -0
  618. package/src/services/voice/turn-controller.ts +596 -0
  619. package/src/services/voice/types.d.ts +643 -0
  620. package/src/services/voice/types.d.ts.map +1 -0
  621. package/src/services/voice/types.ts +699 -0
  622. package/src/services/voice/vad.d.ts +282 -0
  623. package/src/services/voice/vad.d.ts.map +1 -0
  624. package/src/services/voice/vad.test.ts +480 -0
  625. package/src/services/voice/vad.ts +827 -0
  626. package/src/services/voice/vad.v1-v4.test.ts +222 -0
  627. package/src/services/voice/voice-budget.d.ts +241 -0
  628. package/src/services/voice/voice-budget.d.ts.map +1 -0
  629. package/src/services/voice/voice-budget.test.ts +420 -0
  630. package/src/services/voice/voice-budget.ts +656 -0
  631. package/src/services/voice/voice-duet.test.ts +375 -0
  632. package/src/services/voice/voice-emotion-classifier.d.ts +95 -0
  633. package/src/services/voice/voice-emotion-classifier.d.ts.map +1 -0
  634. package/src/services/voice/voice-emotion-classifier.test.ts +210 -0
  635. package/src/services/voice/voice-emotion-classifier.ts +273 -0
  636. package/src/services/voice/voice-preset-format.d.ts +158 -0
  637. package/src/services/voice/voice-preset-format.d.ts.map +1 -0
  638. package/src/services/voice/voice-preset-format.ts +700 -0
  639. package/src/services/voice/voice-preset-generator.test.ts +89 -0
  640. package/src/services/voice/voice-profile-artifact.d.ts +116 -0
  641. package/src/services/voice/voice-profile-artifact.d.ts.map +1 -0
  642. package/src/services/voice/voice-profile-artifact.test.ts +138 -0
  643. package/src/services/voice/voice-profile-artifact.ts +518 -0
  644. package/src/services/voice/voice-profile-routes.d.ts +83 -0
  645. package/src/services/voice/voice-profile-routes.d.ts.map +1 -0
  646. package/src/services/voice/voice-profile-routes.test.ts +429 -0
  647. package/src/services/voice/voice-profile-routes.ts +425 -0
  648. package/src/services/voice/voice-scenario.ts +154 -0
  649. package/src/services/voice/voice-settings.d.ts +82 -0
  650. package/src/services/voice/voice-settings.d.ts.map +1 -0
  651. package/src/services/voice/voice-settings.ts +172 -0
  652. package/src/services/voice/voice-state-machine.d.ts +364 -0
  653. package/src/services/voice/voice-state-machine.d.ts.map +1 -0
  654. package/src/services/voice/voice-state-machine.ts +727 -0
  655. package/src/services/voice/voice-workbench-report.test.ts +168 -0
  656. package/src/services/voice/voice-workbench-report.ts +326 -0
  657. package/src/services/voice/voice-workbench.test.ts +158 -0
  658. package/src/services/voice/voice.test.ts +1070 -0
  659. package/src/services/voice/wake-word-ggml.d.ts +101 -0
  660. package/src/services/voice/wake-word-ggml.d.ts.map +1 -0
  661. package/src/services/voice/wake-word-ggml.ts +320 -0
  662. package/src/services/voice/wake-word.d.ts +255 -0
  663. package/src/services/voice/wake-word.d.ts.map +1 -0
  664. package/src/services/voice/wake-word.test.ts +298 -0
  665. package/src/services/voice/wake-word.ts +554 -0
  666. package/src/services/voice/wrap-with-first-line-cache.d.ts +70 -0
  667. package/src/services/voice/wrap-with-first-line-cache.d.ts.map +1 -0
  668. package/src/services/voice/wrap-with-first-line-cache.ts +267 -0
  669. package/src/services/voice-model-updater.d.ts +240 -0
  670. package/src/services/voice-model-updater.d.ts.map +1 -0
  671. package/src/services/voice-model-updater.ts +724 -0
  672. package/src/services/voice-prewarm.d.ts +3 -0
  673. package/src/services/voice-prewarm.d.ts.map +1 -0
  674. package/src/services/voice-prewarm.ts +51 -0
  675. package/dist/index.d.ts +0 -37
  676. package/dist/index.js +0 -1098
@@ -0,0 +1,782 @@
1
+ /**
2
+ * HTTP routes for the local-inference / model management feature.
3
+ *
4
+ * Route shape and auth follow the established `*-compat-routes.ts` pattern:
5
+ * - `handleLocalInferenceCompatRoutes` returns `true` when it handles a
6
+ * request and `false` to pass through to the next handler.
7
+ * - Regular reads use `ensureCompatApiAuthorized`.
8
+ * - Mutating routes (download start/cancel, active switch, uninstall)
9
+ * use `ensureCompatSensitiveRouteAuthorized`.
10
+ * - SSE allows `?token=...` as an alternative to the auth header, via
11
+ * `isStreamAuthorized`.
12
+ */
13
+
14
+ import type http from "node:http";
15
+ import {
16
+ CandidateModelActivationError,
17
+ type KvOffloadMode,
18
+ type LocalInferenceLoadOverrides,
19
+ validateLocalInferenceLoadArgs,
20
+ } from "../services/active-model";
21
+ import { deviceBridge } from "../services/device-bridge";
22
+ import {
23
+ handlerRegistry,
24
+ toPublicRegistration,
25
+ } from "../services/handler-registry";
26
+ import { snapshotProviders } from "../services/providers";
27
+ import {
28
+ type RoutingPolicy,
29
+ readRoutingPreferences,
30
+ setPolicy,
31
+ setPreferredProvider,
32
+ } from "../services/routing-preferences";
33
+ import { localInferenceService } from "../services/service";
34
+ import type { AgentModelSlot, CatalogModel } from "../services/types";
35
+ import { AGENT_MODEL_SLOTS } from "../services/types";
36
+ import {
37
+ type CompatRuntimeState,
38
+ ensureCompatSensitiveRouteAuthorized,
39
+ ensureRouteAuthorized,
40
+ getCompatApiToken,
41
+ getProvidedApiToken,
42
+ readCompatJsonBody,
43
+ sendJsonError as sendJsonErrorResponse,
44
+ sendJson as sendJsonResponse,
45
+ tokenMatches,
46
+ } from "./compat-helpers";
47
+
48
+ function isCatalogModel(value: unknown): value is CatalogModel {
49
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
50
+ return false;
51
+ }
52
+ const record = value as Partial<CatalogModel>;
53
+ return (
54
+ typeof record.id === "string" &&
55
+ typeof record.displayName === "string" &&
56
+ typeof record.hfRepo === "string" &&
57
+ typeof record.ggufFile === "string" &&
58
+ typeof record.params === "string" &&
59
+ typeof record.quant === "string" &&
60
+ typeof record.sizeGb === "number" &&
61
+ typeof record.minRamGb === "number" &&
62
+ typeof record.category === "string" &&
63
+ typeof record.bucket === "string" &&
64
+ typeof record.blurb === "string"
65
+ );
66
+ }
67
+
68
+ function isStreamAuthorized(
69
+ req: http.IncomingMessage,
70
+ res: http.ServerResponse,
71
+ url: URL,
72
+ ): boolean {
73
+ const expected = getCompatApiToken();
74
+ if (!expected) return true;
75
+
76
+ const headerToken = getProvidedApiToken(req);
77
+ const queryToken = url.searchParams.get("token")?.trim();
78
+ if (
79
+ (headerToken && tokenMatches(expected, headerToken)) ||
80
+ (queryToken && tokenMatches(expected, queryToken))
81
+ ) {
82
+ return true;
83
+ }
84
+
85
+ res.writeHead(401, { "Content-Type": "application/json" });
86
+ res.end(JSON.stringify({ error: "Unauthorized" }));
87
+ return false;
88
+ }
89
+
90
+ function writeSseEvent(
91
+ res: http.ServerResponse,
92
+ payload: Record<string, unknown>,
93
+ ): void {
94
+ res.write(`data: ${JSON.stringify(payload)}\n\n`);
95
+ }
96
+
97
+ function stringBody(
98
+ body: Record<string, unknown> | null,
99
+ key: string,
100
+ ): string | null {
101
+ if (!body) return null;
102
+ const raw = body[key];
103
+ return typeof raw === "string" && raw.trim().length > 0 ? raw.trim() : null;
104
+ }
105
+
106
+ /**
107
+ * Strict parser for the per-load `overrides` field on
108
+ * `POST /api/local-inference/active`. Returns either a validated
109
+ * `LocalInferenceLoadOverrides` value or a non-null `error` string.
110
+ *
111
+ * The parser is the single boundary where untrusted JSON becomes typed
112
+ * load args — `validateLocalInferenceLoadArgs` re-runs invariant checks
113
+ * after merging with catalog defaults to catch any catalog-side rule
114
+ * we haven't taught the route layer yet.
115
+ */
116
+ function parseLocalInferenceLoadOverrides(raw: unknown):
117
+ | { overrides: LocalInferenceLoadOverrides; error: null }
118
+ | {
119
+ overrides: null;
120
+ error: string;
121
+ } {
122
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
123
+ return { overrides: null, error: "overrides must be an object" };
124
+ }
125
+ const record = raw as Record<string, unknown>;
126
+ const out: LocalInferenceLoadOverrides = {};
127
+
128
+ if (record.contextSize !== undefined) {
129
+ if (
130
+ typeof record.contextSize !== "number" ||
131
+ !Number.isInteger(record.contextSize) ||
132
+ record.contextSize < 256
133
+ ) {
134
+ return {
135
+ overrides: null,
136
+ error: "overrides.contextSize must be an integer >= 256",
137
+ };
138
+ }
139
+ out.contextSize = record.contextSize;
140
+ }
141
+ for (const key of ["cacheTypeK", "cacheTypeV"] as const) {
142
+ const value = record[key];
143
+ if (value === undefined) continue;
144
+ if (typeof value !== "string" || value.trim().length === 0) {
145
+ return {
146
+ overrides: null,
147
+ error: `overrides.${key} must be a non-empty string`,
148
+ };
149
+ }
150
+ out[key] = value.trim().toLowerCase();
151
+ }
152
+ if (record.gpuLayers !== undefined) {
153
+ if (
154
+ typeof record.gpuLayers !== "number" ||
155
+ !Number.isInteger(record.gpuLayers) ||
156
+ record.gpuLayers < 0
157
+ ) {
158
+ return {
159
+ overrides: null,
160
+ error: "overrides.gpuLayers must be a non-negative integer",
161
+ };
162
+ }
163
+ out.gpuLayers = record.gpuLayers;
164
+ }
165
+ if (record.kvOffload !== undefined) {
166
+ const value = record.kvOffload;
167
+ if (typeof value === "string") {
168
+ if (value !== "cpu" && value !== "gpu" && value !== "split") {
169
+ return {
170
+ overrides: null,
171
+ error:
172
+ 'overrides.kvOffload must be "cpu", "gpu", "split", or { gpuLayers: number }',
173
+ };
174
+ }
175
+ out.kvOffload = value as KvOffloadMode;
176
+ } else if (
177
+ value !== null &&
178
+ typeof value === "object" &&
179
+ typeof (value as { gpuLayers?: unknown }).gpuLayers === "number" &&
180
+ Number.isInteger((value as { gpuLayers: number }).gpuLayers) &&
181
+ (value as { gpuLayers: number }).gpuLayers >= 0
182
+ ) {
183
+ out.kvOffload = {
184
+ gpuLayers: (value as { gpuLayers: number }).gpuLayers,
185
+ };
186
+ } else {
187
+ return {
188
+ overrides: null,
189
+ error:
190
+ 'overrides.kvOffload must be "cpu", "gpu", "split", or { gpuLayers: number }',
191
+ };
192
+ }
193
+ }
194
+ for (const key of ["flashAttention", "mmap", "mlock"] as const) {
195
+ const value = record[key];
196
+ if (value === undefined) continue;
197
+ if (typeof value !== "boolean") {
198
+ return {
199
+ overrides: null,
200
+ error: `overrides.${key} must be a boolean`,
201
+ };
202
+ }
203
+ out[key] = value;
204
+ }
205
+
206
+ // Run the same validation `resolveLocalInferenceLoadArgs` will run. The
207
+ // optimized desktop FFI runtime can honor the elizaOS fork's KV-cache
208
+ // types; unsupported runtimes fail later at the backend capability gate
209
+ // instead of silently loading fp16.
210
+ try {
211
+ validateLocalInferenceLoadArgs(out, { allowFork: true });
212
+ } catch (err) {
213
+ return {
214
+ overrides: null,
215
+ error: err instanceof Error ? err.message : "invalid overrides",
216
+ };
217
+ }
218
+ return { overrides: out, error: null };
219
+ }
220
+
221
+ /**
222
+ * Match POST/DELETE/GET for `/api/local-inference/installed/:id`.
223
+ * Returns the trimmed id or null.
224
+ */
225
+ function matchInstalledId(pathname: string): string | null {
226
+ const match = /^\/api\/local-inference\/installed\/([^/]+)$/.exec(pathname);
227
+ return match?.[1] ?? null;
228
+ }
229
+
230
+ export async function handleLocalInferenceCompatRoutes(
231
+ req: http.IncomingMessage,
232
+ res: http.ServerResponse,
233
+ state: CompatRuntimeState,
234
+ ): Promise<boolean> {
235
+ const method = (req.method ?? "GET").toUpperCase();
236
+ const url = new URL(req.url ?? "/", "http://localhost");
237
+ const pathname = url.pathname;
238
+
239
+ if (!pathname.startsWith("/api/local-inference/")) return false;
240
+
241
+ // ── SSE: download progress stream ───────────────────────────────────
242
+ if (
243
+ method === "GET" &&
244
+ pathname === "/api/local-inference/downloads/stream"
245
+ ) {
246
+ if (!isStreamAuthorized(req, res, url)) return true;
247
+
248
+ res.writeHead(200, {
249
+ "Content-Type": "text/event-stream",
250
+ "Cache-Control": "no-cache, no-transform",
251
+ Connection: "keep-alive",
252
+ "X-Accel-Buffering": "no",
253
+ });
254
+
255
+ // Send initial snapshot so a freshly-opened stream immediately reflects
256
+ // whatever is in flight.
257
+ writeSseEvent(res, {
258
+ type: "snapshot",
259
+ downloads: localInferenceService.getDownloads(),
260
+ active: localInferenceService.getActive(),
261
+ });
262
+
263
+ const unsubscribeDownloads = localInferenceService.subscribeDownloads(
264
+ (event) => {
265
+ writeSseEvent(res, {
266
+ type: event.type,
267
+ job: event.job,
268
+ });
269
+ },
270
+ );
271
+ const unsubscribeActive = localInferenceService.subscribeActive(
272
+ (active) => {
273
+ writeSseEvent(res, {
274
+ type: "active",
275
+ active,
276
+ });
277
+ },
278
+ );
279
+
280
+ const heartbeat = setInterval(() => {
281
+ res.write(": heartbeat\n\n");
282
+ }, 15_000);
283
+ if (typeof heartbeat === "object" && "unref" in heartbeat) {
284
+ heartbeat.unref();
285
+ }
286
+
287
+ const cleanup = () => {
288
+ clearInterval(heartbeat);
289
+ unsubscribeDownloads();
290
+ unsubscribeActive();
291
+ };
292
+ req.on("close", cleanup);
293
+ req.on("aborted", cleanup);
294
+ return true;
295
+ }
296
+
297
+ // ── GET: full hub snapshot (catalog + installed + hardware + state) ─
298
+ if (method === "GET" && pathname === "/api/local-inference/hub") {
299
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
300
+ try {
301
+ const snapshot = await localInferenceService.snapshot();
302
+ sendJsonResponse(res, 200, snapshot);
303
+ } catch (err) {
304
+ sendJsonErrorResponse(
305
+ res,
306
+ 500,
307
+ err instanceof Error ? err.message : "Failed to load hub",
308
+ );
309
+ }
310
+ return true;
311
+ }
312
+
313
+ // ── GET: hardware probe only ────────────────────────────────────────
314
+ if (method === "GET" && pathname === "/api/local-inference/hardware") {
315
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
316
+ try {
317
+ const probe = await localInferenceService.getHardware();
318
+ sendJsonResponse(res, 200, probe);
319
+ } catch (err) {
320
+ sendJsonErrorResponse(
321
+ res,
322
+ 500,
323
+ err instanceof Error ? err.message : "Failed to probe hardware",
324
+ );
325
+ }
326
+ return true;
327
+ }
328
+
329
+ // ── GET: curated catalog ────────────────────────────────────────────
330
+ if (method === "GET" && pathname === "/api/local-inference/catalog") {
331
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
332
+ sendJsonResponse(res, 200, {
333
+ models: localInferenceService.getCatalog(),
334
+ });
335
+ return true;
336
+ }
337
+
338
+ // ── GET: installed models ───────────────────────────────────────────
339
+ if (method === "GET" && pathname === "/api/local-inference/installed") {
340
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
341
+ try {
342
+ const models = await localInferenceService.getInstalled();
343
+ sendJsonResponse(res, 200, { models });
344
+ } catch (err) {
345
+ sendJsonErrorResponse(
346
+ res,
347
+ 500,
348
+ err instanceof Error ? err.message : "Failed to list installed models",
349
+ );
350
+ }
351
+ return true;
352
+ }
353
+
354
+ // ── POST: start download ────────────────────────────────────────────
355
+ // Body: either `{ modelId }` for a curated entry, or
356
+ // `{ spec: CatalogModel }` for a HuggingFace-search result.
357
+ if (method === "POST" && pathname === "/api/local-inference/downloads") {
358
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
359
+ const body = await readCompatJsonBody(req, res);
360
+ if (!body) return true;
361
+ const modelId = stringBody(body, "modelId");
362
+ const rawSpec = body.spec;
363
+ try {
364
+ let job: Awaited<ReturnType<typeof localInferenceService.startDownload>>;
365
+ if (rawSpec) {
366
+ if (!isCatalogModel(rawSpec)) {
367
+ sendJsonErrorResponse(res, 400, "Invalid model spec");
368
+ return true;
369
+ }
370
+ job = await localInferenceService.startDownload(rawSpec);
371
+ } else if (modelId) {
372
+ job = await localInferenceService.startDownload(modelId);
373
+ } else {
374
+ sendJsonErrorResponse(res, 400, "modelId or spec is required");
375
+ return true;
376
+ }
377
+ sendJsonResponse(res, 202, { job });
378
+ } catch (err) {
379
+ sendJsonErrorResponse(
380
+ res,
381
+ 400,
382
+ err instanceof Error ? err.message : "Failed to start download",
383
+ );
384
+ }
385
+ return true;
386
+ }
387
+
388
+ // ── GET: provider status snapshot ──────────────────────────────────
389
+ if (method === "GET" && pathname === "/api/local-inference/providers") {
390
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
391
+ try {
392
+ const providers = await snapshotProviders();
393
+ sendJsonResponse(res, 200, { providers });
394
+ } catch (err) {
395
+ sendJsonErrorResponse(
396
+ res,
397
+ 500,
398
+ err instanceof Error ? err.message : "Failed to read providers",
399
+ );
400
+ }
401
+ return true;
402
+ }
403
+
404
+ // ── GET: registered model handlers across all providers ────────────
405
+ if (method === "GET" && pathname === "/api/local-inference/routing") {
406
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
407
+ try {
408
+ const [prefs, registrations] = await Promise.all([
409
+ readRoutingPreferences(),
410
+ Promise.resolve(handlerRegistry.getAll().map(toPublicRegistration)),
411
+ ]);
412
+ sendJsonResponse(res, 200, {
413
+ registrations,
414
+ preferences: prefs,
415
+ });
416
+ } catch (err) {
417
+ sendJsonErrorResponse(
418
+ res,
419
+ 500,
420
+ err instanceof Error ? err.message : "Failed to read routing state",
421
+ );
422
+ }
423
+ return true;
424
+ }
425
+
426
+ // ── POST: set preferred provider for a slot (manual override) ──────
427
+ if (
428
+ method === "POST" &&
429
+ pathname === "/api/local-inference/routing/preferred"
430
+ ) {
431
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
432
+ const body = await readCompatJsonBody(req, res);
433
+ if (!body) return true;
434
+ const slot = stringBody(body, "slot") as AgentModelSlot | null;
435
+ if (!slot || !AGENT_MODEL_SLOTS.includes(slot)) {
436
+ sendJsonErrorResponse(
437
+ res,
438
+ 400,
439
+ "slot is required and must be a valid AgentModelSlot",
440
+ );
441
+ return true;
442
+ }
443
+ const raw = body.provider;
444
+ const provider =
445
+ raw === null
446
+ ? null
447
+ : typeof raw === "string" && raw.trim().length > 0
448
+ ? raw.trim()
449
+ : null;
450
+ try {
451
+ const prefs = await setPreferredProvider(slot, provider);
452
+ sendJsonResponse(res, 200, { preferences: prefs });
453
+ } catch (err) {
454
+ sendJsonErrorResponse(
455
+ res,
456
+ 500,
457
+ err instanceof Error
458
+ ? err.message
459
+ : "Failed to write preferred provider",
460
+ );
461
+ }
462
+ return true;
463
+ }
464
+
465
+ // ── POST: set routing policy for a slot ─────────────────────────────
466
+ if (method === "POST" && pathname === "/api/local-inference/routing/policy") {
467
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
468
+ const body = await readCompatJsonBody(req, res);
469
+ if (!body) return true;
470
+ const slot = stringBody(body, "slot") as AgentModelSlot | null;
471
+ if (!slot || !AGENT_MODEL_SLOTS.includes(slot)) {
472
+ sendJsonErrorResponse(
473
+ res,
474
+ 400,
475
+ "slot is required and must be a valid AgentModelSlot",
476
+ );
477
+ return true;
478
+ }
479
+ const raw = body.policy;
480
+ const validPolicies: RoutingPolicy[] = [
481
+ "manual",
482
+ "cheapest",
483
+ "fastest",
484
+ "prefer-local",
485
+ "round-robin",
486
+ ];
487
+ const policy =
488
+ raw === null
489
+ ? null
490
+ : typeof raw === "string" &&
491
+ validPolicies.includes(raw as RoutingPolicy)
492
+ ? (raw as RoutingPolicy)
493
+ : null;
494
+ if (raw !== null && policy === null) {
495
+ sendJsonErrorResponse(
496
+ res,
497
+ 400,
498
+ `policy must be one of ${validPolicies.join(", ")} or null`,
499
+ );
500
+ return true;
501
+ }
502
+ try {
503
+ const prefs = await setPolicy(slot, policy);
504
+ sendJsonResponse(res, 200, { preferences: prefs });
505
+ } catch (err) {
506
+ sendJsonErrorResponse(
507
+ res,
508
+ 500,
509
+ err instanceof Error ? err.message : "Failed to write routing policy",
510
+ );
511
+ }
512
+ return true;
513
+ }
514
+
515
+ // ── GET: model-type assignments ─────────────────────────────────────
516
+ if (method === "GET" && pathname === "/api/local-inference/assignments") {
517
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
518
+ try {
519
+ const assignments = await localInferenceService.getAssignments();
520
+ sendJsonResponse(res, 200, { assignments });
521
+ } catch (err) {
522
+ sendJsonErrorResponse(
523
+ res,
524
+ 500,
525
+ err instanceof Error ? err.message : "Failed to read assignments",
526
+ );
527
+ }
528
+ return true;
529
+ }
530
+
531
+ // ── POST: set / clear a model-type assignment ───────────────────────
532
+ if (method === "POST" && pathname === "/api/local-inference/assignments") {
533
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
534
+ const body = await readCompatJsonBody(req, res);
535
+ if (!body) return true;
536
+ const slot = stringBody(body, "slot") as AgentModelSlot | null;
537
+ if (!slot || !AGENT_MODEL_SLOTS.includes(slot)) {
538
+ sendJsonErrorResponse(
539
+ res,
540
+ 400,
541
+ `slot must be one of ${AGENT_MODEL_SLOTS.join(", ")}`,
542
+ );
543
+ return true;
544
+ }
545
+ // modelId can be null to clear the slot
546
+ const rawModelId = body.modelId;
547
+ const modelId =
548
+ rawModelId === null
549
+ ? null
550
+ : typeof rawModelId === "string" && rawModelId.trim().length > 0
551
+ ? rawModelId.trim()
552
+ : null;
553
+ try {
554
+ const assignments = await localInferenceService.setSlotAssignment(
555
+ slot,
556
+ modelId,
557
+ );
558
+ sendJsonResponse(res, 200, { assignments });
559
+ } catch (err) {
560
+ sendJsonErrorResponse(
561
+ res,
562
+ 500,
563
+ err instanceof Error ? err.message : "Failed to write assignment",
564
+ );
565
+ }
566
+ return true;
567
+ }
568
+
569
+ // ── GET: device bridge status (paired mobile device connectivity) ───
570
+ if (method === "GET" && pathname === "/api/local-inference/device") {
571
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
572
+ sendJsonResponse(res, 200, deviceBridge.status());
573
+ return true;
574
+ }
575
+
576
+ // ── SSE: device bridge status stream ────────────────────────────────
577
+ if (method === "GET" && pathname === "/api/local-inference/device/stream") {
578
+ if (!isStreamAuthorized(req, res, url)) return true;
579
+
580
+ res.writeHead(200, {
581
+ "Content-Type": "text/event-stream",
582
+ "Cache-Control": "no-cache, no-transform",
583
+ Connection: "keep-alive",
584
+ "X-Accel-Buffering": "no",
585
+ });
586
+
587
+ writeSseEvent(res, { type: "status", status: deviceBridge.status() });
588
+ const unsubscribe = deviceBridge.subscribeStatus((status) => {
589
+ writeSseEvent(res, { type: "status", status });
590
+ });
591
+ const heartbeat = setInterval(() => {
592
+ res.write(": heartbeat\n\n");
593
+ }, 15_000);
594
+ if (typeof heartbeat === "object" && "unref" in heartbeat) {
595
+ heartbeat.unref();
596
+ }
597
+ const cleanup = () => {
598
+ clearInterval(heartbeat);
599
+ unsubscribe();
600
+ };
601
+ req.on("close", cleanup);
602
+ req.on("aborted", cleanup);
603
+ return true;
604
+ }
605
+
606
+ // ── GET: explicit custom model search (Hugging Face / ModelScope) ───
607
+ if (method === "GET" && pathname === "/api/local-inference/hf-search") {
608
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
609
+ const q = url.searchParams.get("q")?.trim() ?? "";
610
+ if (q.length === 0) {
611
+ sendJsonResponse(res, 200, { models: [] });
612
+ return true;
613
+ }
614
+ const limitRaw = url.searchParams.get("limit");
615
+ const limit = limitRaw
616
+ ? Math.max(1, Math.min(50, Number.parseInt(limitRaw, 10) || 12))
617
+ : 12;
618
+ const hub =
619
+ url.searchParams.get("hub")?.trim().toLowerCase() === "modelscope"
620
+ ? "modelscope"
621
+ : "huggingface";
622
+ try {
623
+ const models =
624
+ typeof localInferenceService.searchModelHub === "function"
625
+ ? await localInferenceService.searchModelHub(q, hub, limit)
626
+ : await localInferenceService.searchHuggingFace(q, limit);
627
+ sendJsonResponse(res, 200, { models });
628
+ } catch (err) {
629
+ sendJsonErrorResponse(
630
+ res,
631
+ 502,
632
+ err instanceof Error ? err.message : "Model search failed",
633
+ );
634
+ }
635
+ return true;
636
+ }
637
+
638
+ // ── DELETE: cancel download ─────────────────────────────────────────
639
+ {
640
+ const match = /^\/api\/local-inference\/downloads\/([^/]+)$/.exec(pathname);
641
+ if (method === "DELETE" && match) {
642
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
643
+ const cancelled = localInferenceService.cancelDownload(match[1] ?? "");
644
+ sendJsonResponse(res, cancelled ? 200 : 404, { cancelled });
645
+ return true;
646
+ }
647
+ }
648
+
649
+ // ── GET: active model ───────────────────────────────────────────────
650
+ if (method === "GET" && pathname === "/api/local-inference/active") {
651
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
652
+ sendJsonResponse(res, 200, localInferenceService.getActive());
653
+ return true;
654
+ }
655
+
656
+ // ── POST: switch active model ───────────────────────────────────────
657
+ // Accepts either:
658
+ // { "modelId": "..." } — legacy shape
659
+ // { "modelId": "...", "overrides": { ... } } — per-load overrides
660
+ // Overrides honour: contextSize, cacheTypeK, cacheTypeV, gpuLayers,
661
+ // kvOffload, flashAttention, mmap, mlock. Validation is delegated to
662
+ // `validateLocalInferenceLoadArgs` (desktop-only acceptance set by
663
+ // default; AOSP / paired-device callers route through their own
664
+ // adapter and bypass this path).
665
+ if (method === "POST" && pathname === "/api/local-inference/active") {
666
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
667
+ const body = await readCompatJsonBody(req, res);
668
+ if (!body) return true;
669
+ const modelId = stringBody(body, "modelId");
670
+ if (!modelId) {
671
+ sendJsonErrorResponse(res, 400, "modelId is required");
672
+ return true;
673
+ }
674
+ let overrides: LocalInferenceLoadOverrides | undefined;
675
+ if (body.overrides !== undefined && body.overrides !== null) {
676
+ const parsed = parseLocalInferenceLoadOverrides(body.overrides);
677
+ if (parsed.error !== null) {
678
+ sendJsonErrorResponse(res, 400, parsed.error);
679
+ return true;
680
+ }
681
+ overrides = parsed.overrides;
682
+ }
683
+ try {
684
+ const active = await localInferenceService.setActive(
685
+ state.current,
686
+ modelId,
687
+ overrides,
688
+ );
689
+ sendJsonResponse(res, 200, active);
690
+ } catch (err) {
691
+ // #7679: refuse to activate a candidate-only / weights-staged
692
+ // bundle whose manifest reports `evals.textEval.passed=false`.
693
+ // Surface the structured payload (modelId, manifestVersion,
694
+ // failedEvals) verbatim so the UI can render an actionable
695
+ // "this tier isn't ready" message instead of `[unused]` tokens
696
+ // downstream.
697
+ if (err instanceof CandidateModelActivationError) {
698
+ sendJsonResponse(res, 422, {
699
+ error: err.message,
700
+ modelId: err.modelId,
701
+ manifestVersion: err.manifestVersion,
702
+ failedEvals: err.failedEvals,
703
+ });
704
+ return true;
705
+ }
706
+ sendJsonErrorResponse(
707
+ res,
708
+ 400,
709
+ err instanceof Error ? err.message : "Failed to set active model",
710
+ );
711
+ }
712
+ return true;
713
+ }
714
+
715
+ // ── DELETE: clear active model ──────────────────────────────────────
716
+ if (method === "DELETE" && pathname === "/api/local-inference/active") {
717
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
718
+ try {
719
+ const active = await localInferenceService.clearActive(state.current);
720
+ sendJsonResponse(res, 200, active);
721
+ } catch (err) {
722
+ sendJsonErrorResponse(
723
+ res,
724
+ 500,
725
+ err instanceof Error ? err.message : "Failed to unload model",
726
+ );
727
+ }
728
+ return true;
729
+ }
730
+
731
+ // ── POST: verify installed model ────────────────────────────────────
732
+ {
733
+ const match = /^\/api\/local-inference\/installed\/([^/]+)\/verify$/.exec(
734
+ pathname,
735
+ );
736
+ if (method === "POST" && match) {
737
+ if (!(await ensureRouteAuthorized(req, res, state))) return true;
738
+ try {
739
+ const result = await localInferenceService.verifyModel(match[1] ?? "");
740
+ sendJsonResponse(res, 200, result);
741
+ } catch (err) {
742
+ sendJsonErrorResponse(
743
+ res,
744
+ 404,
745
+ err instanceof Error ? err.message : "Failed to verify model",
746
+ );
747
+ }
748
+ return true;
749
+ }
750
+ }
751
+
752
+ // ── DELETE: uninstall model ─────────────────────────────────────────
753
+ {
754
+ const id = matchInstalledId(pathname);
755
+ if (method === "DELETE" && id) {
756
+ if (!ensureCompatSensitiveRouteAuthorized(req, res)) return true;
757
+ try {
758
+ const result = await localInferenceService.uninstall(id);
759
+ if (result.removed) {
760
+ sendJsonResponse(res, 200, { removed: true });
761
+ } else if (result.reason === "external") {
762
+ sendJsonErrorResponse(
763
+ res,
764
+ 409,
765
+ "Model was discovered from another tool; Eliza will not delete files it does not own",
766
+ );
767
+ } else {
768
+ sendJsonErrorResponse(res, 404, "Model not installed");
769
+ }
770
+ } catch (err) {
771
+ sendJsonErrorResponse(
772
+ res,
773
+ 500,
774
+ err instanceof Error ? err.message : "Failed to uninstall model",
775
+ );
776
+ }
777
+ return true;
778
+ }
779
+ }
780
+
781
+ return false;
782
+ }