@qvac/sdk 0.9.1 → 0.10.0

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 (335) hide show
  1. package/NOTICE +15 -68
  2. package/dist/client/api/cancel.d.ts +1 -1
  3. package/dist/client/api/cancel.js +1 -1
  4. package/dist/client/api/completion-stream.d.ts +51 -12
  5. package/dist/client/api/completion-stream.js +129 -56
  6. package/dist/client/api/delete-cache.d.ts +1 -0
  7. package/dist/client/api/delete-cache.js +1 -0
  8. package/dist/client/api/diffusion.d.ts +17 -2
  9. package/dist/client/api/diffusion.js +21 -4
  10. package/dist/client/api/embed.d.ts +4 -0
  11. package/dist/client/api/finetune.d.ts +21 -0
  12. package/dist/client/api/get-loaded-model-info.d.ts +25 -0
  13. package/dist/client/api/get-loaded-model-info.js +36 -0
  14. package/dist/client/api/get-model-info.d.ts +9 -1
  15. package/dist/client/api/get-model-info.js +8 -0
  16. package/dist/client/api/heartbeat.d.ts +2 -2
  17. package/dist/client/api/heartbeat.js +2 -2
  18. package/dist/client/api/index.d.ts +3 -1
  19. package/dist/client/api/index.js +3 -1
  20. package/dist/client/api/invoke-plugin.d.ts +16 -0
  21. package/dist/client/api/invoke-plugin.js +16 -0
  22. package/dist/client/api/load-model.d.ts +36 -3
  23. package/dist/client/api/load-model.js +31 -10
  24. package/dist/client/api/provide.d.ts +11 -5
  25. package/dist/client/api/provide.js +11 -6
  26. package/dist/client/api/rag.d.ts +9 -0
  27. package/dist/client/api/rag.js +9 -0
  28. package/dist/client/api/registry.d.ts +26 -0
  29. package/dist/client/api/registry.js +26 -0
  30. package/dist/client/api/resume.d.ts +20 -4
  31. package/dist/client/api/resume.js +20 -4
  32. package/dist/client/api/state.d.ts +23 -0
  33. package/dist/client/api/state.js +29 -0
  34. package/dist/client/api/stop-provider.d.ts +11 -5
  35. package/dist/client/api/stop-provider.js +11 -5
  36. package/dist/client/api/suspend.d.ts +21 -3
  37. package/dist/client/api/suspend.js +21 -3
  38. package/dist/client/api/text-to-speech.d.ts +64 -6
  39. package/dist/client/api/text-to-speech.js +423 -47
  40. package/dist/client/api/transcribe.d.ts +29 -4
  41. package/dist/client/api/transcribe.js +86 -28
  42. package/dist/client/api/translate.d.ts +1 -0
  43. package/dist/client/api/translate.js +1 -0
  44. package/dist/client/rpc/bare-client.js +22 -1
  45. package/dist/client/rpc/expo-rpc-client.d.ts +1 -1
  46. package/dist/client/rpc/expo-rpc-client.js +96 -4
  47. package/dist/client/rpc/node-rpc-client.js +36 -0
  48. package/dist/examples/audio/mic-input.d.ts +9 -0
  49. package/dist/examples/audio/mic-input.js +111 -0
  50. package/dist/examples/completion-events.d.ts +18 -0
  51. package/dist/examples/completion-events.js +79 -0
  52. package/dist/examples/delegated-inference/composite.js +12 -18
  53. package/dist/examples/delegated-inference/consumer-profiled.js +4 -11
  54. package/dist/examples/delegated-inference/consumer.js +8 -13
  55. package/dist/examples/delegated-inference/provider.js +11 -16
  56. package/dist/examples/diffusion-flux2-klein-img2img.d.ts +2 -0
  57. package/dist/examples/diffusion-flux2-klein-img2img.js +60 -0
  58. package/dist/examples/diffusion-flux2-klein.js +1 -0
  59. package/dist/examples/diffusion-img2img.d.ts +2 -0
  60. package/dist/examples/diffusion-img2img.js +49 -0
  61. package/dist/examples/diffusion-txt2img.js +2 -1
  62. package/dist/examples/kv-cache-example.js +11 -12
  63. package/dist/examples/llamacpp-cache.d.ts +2 -0
  64. package/dist/examples/llamacpp-cache.js +107 -0
  65. package/dist/examples/llamacpp-dynamic-tools.d.ts +2 -0
  66. package/dist/examples/llamacpp-dynamic-tools.js +144 -0
  67. package/dist/examples/llamacpp-native-tools.js +2 -8
  68. package/dist/examples/llamacpp-structured-output.d.ts +2 -0
  69. package/dist/examples/llamacpp-structured-output.js +106 -0
  70. package/dist/examples/llamacpp-tools-pythonic.d.ts +2 -0
  71. package/dist/examples/llamacpp-tools-pythonic.js +69 -0
  72. package/dist/examples/plugins.js +4 -4
  73. package/dist/examples/suspend-resume.js +21 -4
  74. package/dist/examples/transcription/parakeet-microphone-record.js +6 -33
  75. package/dist/examples/transcription/whispercpp-filesystem-streaming.js +7 -5
  76. package/dist/examples/transcription/whispercpp-filesystem.js +12 -3
  77. package/dist/examples/transcription/whispercpp-microphone-record.js +6 -29
  78. package/dist/examples/translation/translation-stream.js +5 -5
  79. package/dist/examples/tts/llm-to-tts-streaming.d.ts +16 -0
  80. package/dist/examples/tts/llm-to-tts-streaming.js +143 -0
  81. package/dist/examples/tts/supertonic-multilingual.js +3 -3
  82. package/dist/examples/tts/supertonic.js +3 -3
  83. package/dist/examples/tts/utils.d.ts +11 -1
  84. package/dist/examples/tts/utils.js +70 -17
  85. package/dist/examples/voice-assistant/voice-assistant.d.ts +2 -0
  86. package/dist/examples/voice-assistant/voice-assistant.js +207 -0
  87. package/dist/expo/plugins/withMobileBundle.js +1 -1
  88. package/dist/index.d.ts +2 -2
  89. package/dist/index.js +2 -2
  90. package/dist/logging/logger.d.ts +11 -0
  91. package/dist/logging/logger.js +11 -0
  92. package/dist/logging/types.d.ts +18 -0
  93. package/dist/models/registry/models.d.ts +6620 -6185
  94. package/dist/models/registry/models.js +6296 -5318
  95. package/dist/models/update-models/codegen.js +84 -23
  96. package/dist/models/update-models/companions.d.ts +18 -0
  97. package/dist/models/update-models/companions.js +175 -0
  98. package/dist/models/update-models/naming.js +0 -25
  99. package/dist/models/update-models/registry.js +4 -2
  100. package/dist/models/update-models/types.d.ts +37 -9
  101. package/dist/profiling/index.d.ts +49 -0
  102. package/dist/profiling/index.js +49 -0
  103. package/dist/schemas/cancel.d.ts +6 -2
  104. package/dist/schemas/cancel.js +14 -7
  105. package/dist/schemas/common.d.ts +361 -279
  106. package/dist/schemas/common.js +26 -5
  107. package/dist/schemas/completion-event.d.ts +225 -0
  108. package/dist/schemas/completion-event.js +79 -0
  109. package/dist/schemas/completion-stream.d.ts +122 -31
  110. package/dist/schemas/completion-stream.js +137 -36
  111. package/dist/schemas/delegate.d.ts +0 -2
  112. package/dist/schemas/delegate.js +25 -6
  113. package/dist/schemas/download-asset.d.ts +5 -5
  114. package/dist/schemas/embed.js +21 -9
  115. package/dist/schemas/engine-addon-map.d.ts +11 -1
  116. package/dist/schemas/engine-addon-map.js +2 -2
  117. package/dist/schemas/finetune.d.ts +16 -16
  118. package/dist/schemas/finetune.js +8 -8
  119. package/dist/schemas/get-loaded-model-info.d.ts +52 -0
  120. package/dist/schemas/get-loaded-model-info.js +44 -0
  121. package/dist/schemas/get-model-info.d.ts +2 -2
  122. package/dist/schemas/get-model-info.js +103 -32
  123. package/dist/schemas/heartbeat.d.ts +0 -1
  124. package/dist/schemas/index.d.ts +4 -1
  125. package/dist/schemas/index.js +4 -1
  126. package/dist/schemas/llamacpp-config.d.ts +14 -4
  127. package/dist/schemas/llamacpp-config.js +5 -0
  128. package/dist/schemas/load-model.d.ts +3233 -1260
  129. package/dist/schemas/load-model.js +23 -16
  130. package/dist/schemas/model-src-utils.d.ts +7 -6
  131. package/dist/schemas/model-src-utils.js +27 -1
  132. package/dist/schemas/model-types.d.ts +4 -4
  133. package/dist/schemas/ocr.d.ts +1 -1
  134. package/dist/schemas/plugin.d.ts +27 -3
  135. package/dist/schemas/plugin.js +27 -2
  136. package/dist/schemas/profiling.js +11 -3
  137. package/dist/schemas/provide.d.ts +1 -3
  138. package/dist/schemas/provide.js +0 -3
  139. package/dist/schemas/rag.d.ts +4 -4
  140. package/dist/schemas/registry.d.ts +10 -10
  141. package/dist/schemas/registry.js +36 -14
  142. package/dist/schemas/reload-config.d.ts +73 -7
  143. package/dist/schemas/sdcpp-config.d.ts +16 -8
  144. package/dist/schemas/sdcpp-config.js +118 -25
  145. package/dist/schemas/sdk-config.d.ts +116 -90
  146. package/dist/schemas/sdk-config.js +16 -0
  147. package/dist/schemas/sdk-errors-client.d.ts +3 -0
  148. package/dist/schemas/sdk-errors-client.js +16 -1
  149. package/dist/schemas/sdk-errors-server.d.ts +3 -0
  150. package/dist/schemas/sdk-errors-server.js +23 -0
  151. package/dist/schemas/state.d.ts +23 -0
  152. package/dist/schemas/state.js +14 -0
  153. package/dist/schemas/stop-provide.d.ts +0 -5
  154. package/dist/schemas/stop-provide.js +2 -6
  155. package/dist/schemas/text-to-speech.d.ts +93 -27
  156. package/dist/schemas/text-to-speech.js +27 -0
  157. package/dist/schemas/tools.d.ts +16 -0
  158. package/dist/schemas/tools.js +15 -0
  159. package/dist/schemas/transcription-config.d.ts +8 -8
  160. package/dist/schemas/transcription.d.ts +33 -0
  161. package/dist/schemas/transcription.js +9 -0
  162. package/dist/schemas/translate.js +59 -19
  163. package/dist/schemas/translation-config.d.ts +157 -133
  164. package/dist/schemas/translation-config.js +34 -11
  165. package/dist/server/bare/delegate-rpc-client.d.ts +1 -1
  166. package/dist/server/bare/delegate-rpc-client.js +131 -122
  167. package/dist/server/bare/hyperswarm.d.ts +2 -2
  168. package/dist/server/bare/hyperswarm.js +12 -7
  169. package/dist/server/bare/ops/cancel.js +7 -0
  170. package/dist/server/bare/ops/kv-cache-utils.d.ts +7 -10
  171. package/dist/server/bare/ops/kv-cache-utils.js +31 -40
  172. package/dist/server/bare/ops/load-model.js +0 -1
  173. package/dist/server/bare/ops/transcribe.d.ts +11 -4
  174. package/dist/server/bare/ops/transcribe.js +25 -4
  175. package/dist/server/bare/ops/translate.js +42 -4
  176. package/dist/server/bare/ops/unload-model.js +21 -19
  177. package/dist/server/bare/plugins/llamacpp-completion/ops/cache-logger.js +1 -2
  178. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +13 -8
  179. package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +257 -85
  180. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +101 -0
  181. package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +135 -0
  182. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +83 -26
  183. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +57 -32
  184. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -16
  185. package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +69 -44
  186. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +113 -94
  187. package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +1 -1
  188. package/dist/server/bare/plugins/onnx-ocr/plugin.js +1 -2
  189. package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech-stream.d.ts +7 -0
  190. package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech-stream.js +116 -0
  191. package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.d.ts +2 -3
  192. package/dist/server/bare/plugins/onnx-tts/ops/text-to-speech.js +71 -9
  193. package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +41 -13
  194. package/dist/server/bare/plugins/onnx-tts/plugin.js +48 -3
  195. package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +15 -7
  196. package/dist/server/bare/plugins/parakeet-transcription/plugin.js +5 -6
  197. package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +6 -0
  198. package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +8 -5
  199. package/dist/server/bare/plugins/sdcpp-generation/plugin.js +13 -13
  200. package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +17 -1
  201. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +40 -17
  202. package/dist/server/bare/registry/config-registry.js +14 -3
  203. package/dist/server/bare/registry/model-registry.d.ts +8 -10
  204. package/dist/server/bare/registry/model-registry.js +17 -12
  205. package/dist/server/bare/registry/registry-client.js +79 -19
  206. package/dist/server/bare/runtime-lifecycle.d.ts +2 -1
  207. package/dist/server/bare/runtime-lifecycle.js +13 -2
  208. package/dist/server/bare/types/addon-responses.d.ts +8 -3
  209. package/dist/server/bare/utils/transcribe-metadata.d.ts +21 -0
  210. package/dist/server/bare/utils/transcribe-metadata.js +28 -0
  211. package/dist/server/bare/utils/tts-stats.d.ts +22 -0
  212. package/dist/server/bare/utils/tts-stats.js +10 -0
  213. package/dist/server/rpc/create-server.d.ts +4 -1
  214. package/dist/server/rpc/create-server.js +5 -1
  215. package/dist/server/rpc/delegate-transport.js +36 -29
  216. package/dist/server/rpc/handle-request.js +28 -3
  217. package/dist/server/rpc/handler-registry.js +7 -1
  218. package/dist/server/rpc/handler-utils.d.ts +5 -0
  219. package/dist/server/rpc/handler-utils.js +22 -0
  220. package/dist/server/rpc/handlers/cancel-delegated.js +3 -5
  221. package/dist/server/rpc/handlers/cancelHandler.js +4 -5
  222. package/dist/server/rpc/handlers/completion-stream-delegated.js +14 -5
  223. package/dist/server/rpc/handlers/delete-cache.js +8 -3
  224. package/dist/server/rpc/handlers/download-asset.js +3 -24
  225. package/dist/server/rpc/handlers/get-loaded-model-info.d.ts +3 -0
  226. package/dist/server/rpc/handlers/get-loaded-model-info.js +39 -0
  227. package/dist/server/rpc/handlers/get-model-info.js +93 -84
  228. package/dist/server/rpc/handlers/heartbeat-delegated.js +2 -2
  229. package/dist/server/rpc/handlers/index.d.ts +6 -0
  230. package/dist/server/rpc/handlers/index.js +6 -0
  231. package/dist/server/rpc/handlers/load-model/download-manager.d.ts +35 -9
  232. package/dist/server/rpc/handlers/load-model/download-manager.js +142 -38
  233. package/dist/server/rpc/handlers/load-model/download-stats.d.ts +5 -4
  234. package/dist/server/rpc/handlers/load-model/download-stats.js +49 -8
  235. package/dist/server/rpc/handlers/load-model/handler.js +31 -50
  236. package/dist/server/rpc/handlers/load-model/http.d.ts +2 -2
  237. package/dist/server/rpc/handlers/load-model/http.js +97 -142
  238. package/dist/server/rpc/handlers/load-model/hyperdrive.d.ts +2 -2
  239. package/dist/server/rpc/handlers/load-model/hyperdrive.js +55 -74
  240. package/dist/server/rpc/handlers/load-model/index.d.ts +0 -1
  241. package/dist/server/rpc/handlers/load-model/index.js +0 -1
  242. package/dist/server/rpc/handlers/load-model/registry-client-options.d.ts +27 -0
  243. package/dist/server/rpc/handlers/load-model/registry-client-options.js +19 -0
  244. package/dist/server/rpc/handlers/load-model/registry-companion-set.d.ts +22 -0
  245. package/dist/server/rpc/handlers/load-model/registry-companion-set.js +208 -0
  246. package/dist/server/rpc/handlers/load-model/registry-download-utils.d.ts +24 -0
  247. package/dist/server/rpc/handlers/load-model/registry-download-utils.js +114 -0
  248. package/dist/server/rpc/handlers/load-model/registry.d.ts +2 -20
  249. package/dist/server/rpc/handlers/load-model/registry.js +109 -381
  250. package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +15 -0
  251. package/dist/server/rpc/handlers/load-model/resolve-session.js +61 -0
  252. package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -3
  253. package/dist/server/rpc/handlers/load-model/resolve.js +14 -23
  254. package/dist/server/rpc/handlers/load-model/types.d.ts +11 -4
  255. package/dist/server/rpc/handlers/load-model/types.js +28 -1
  256. package/dist/server/rpc/handlers/load-model-delegated.js +2 -4
  257. package/dist/server/rpc/handlers/plugin-dispatch.js +9 -5
  258. package/dist/server/rpc/handlers/plugin-invoke.js +1 -1
  259. package/dist/server/rpc/handlers/provideHandler/connection.js +13 -2
  260. package/dist/server/rpc/handlers/provideHandler/handler.js +42 -29
  261. package/dist/server/rpc/handlers/state.d.ts +3 -0
  262. package/dist/server/rpc/handlers/state.js +7 -0
  263. package/dist/server/rpc/handlers/stopProvideHandler.d.ts +2 -2
  264. package/dist/server/rpc/handlers/stopProvideHandler.js +12 -7
  265. package/dist/server/rpc/handlers/text-to-speech-stream.d.ts +3 -0
  266. package/dist/server/rpc/handlers/text-to-speech-stream.js +4 -0
  267. package/dist/server/rpc/handlers/unload-model-delegated.js +3 -3
  268. package/dist/server/rpc/profiling/operation-metrics.js +20 -0
  269. package/dist/server/rpc/rpc-utils.js +1 -1
  270. package/dist/server/rpc/transport-selector.d.ts +3 -0
  271. package/dist/server/rpc/transport-selector.js +18 -0
  272. package/dist/server/utils/cache/auto-history.d.ts +4 -0
  273. package/dist/server/utils/cache/auto-history.js +22 -0
  274. package/dist/server/utils/{cache.d.ts → cache/paths.d.ts} +22 -5
  275. package/dist/server/utils/{cache.js → cache/paths.js} +36 -20
  276. package/dist/server/utils/cache/types.d.ts +8 -0
  277. package/dist/server/utils/cache/types.js +1 -0
  278. package/dist/server/utils/checksum.d.ts +2 -2
  279. package/dist/server/utils/checksum.js +1 -1
  280. package/dist/server/utils/clear-storage.d.ts +5 -0
  281. package/dist/server/utils/clear-storage.js +17 -0
  282. package/dist/server/utils/completion-normalizer.d.ts +17 -0
  283. package/dist/server/utils/completion-normalizer.js +301 -0
  284. package/dist/server/utils/expand-gguf-shards.d.ts +3 -0
  285. package/dist/server/utils/expand-gguf-shards.js +22 -0
  286. package/dist/server/utils/formatting.js +1 -1
  287. package/dist/server/utils/index.d.ts +4 -2
  288. package/dist/server/utils/index.js +4 -2
  289. package/dist/server/utils/qvac-paths.d.ts +2 -0
  290. package/dist/server/utils/qvac-paths.js +5 -0
  291. package/dist/server/utils/response-format.d.ts +3 -0
  292. package/dist/server/utils/response-format.js +20 -0
  293. package/dist/server/utils/tool-integration.d.ts +15 -4
  294. package/dist/server/utils/tool-integration.js +21 -41
  295. package/dist/server/utils/tools/dialect.d.ts +8 -0
  296. package/dist/server/utils/tools/dialect.js +12 -0
  297. package/dist/server/utils/tools/index.d.ts +3 -0
  298. package/dist/server/utils/tools/index.js +2 -0
  299. package/dist/server/utils/tools/parser.d.ts +6 -0
  300. package/dist/server/utils/tools/parser.js +40 -0
  301. package/dist/server/utils/tools/parsers/hermes.d.ts +4 -0
  302. package/dist/server/utils/tools/parsers/hermes.js +100 -0
  303. package/dist/server/utils/tools/parsers/json.d.ts +6 -0
  304. package/dist/server/utils/tools/parsers/json.js +115 -0
  305. package/dist/server/utils/tools/parsers/pythonic.d.ts +4 -0
  306. package/dist/server/utils/tools/parsers/pythonic.js +267 -0
  307. package/dist/server/utils/tools/shared.d.ts +18 -0
  308. package/dist/server/utils/tools/shared.js +54 -0
  309. package/dist/server/utils/worker-lock.d.ts +3 -0
  310. package/dist/server/utils/worker-lock.js +67 -0
  311. package/dist/server/worker-core.d.ts +16 -1
  312. package/dist/server/worker-core.js +114 -14
  313. package/dist/utils/aggregate-events.d.ts +16 -0
  314. package/dist/utils/aggregate-events.js +52 -0
  315. package/dist/utils/cache-normalize.d.ts +10 -0
  316. package/dist/utils/cache-normalize.js +23 -0
  317. package/dist/utils/encoding.d.ts +8 -0
  318. package/dist/utils/encoding.js +13 -0
  319. package/dist/utils/errors-client.d.ts +12 -0
  320. package/dist/utils/errors-client.js +20 -0
  321. package/dist/utils/errors-server.d.ts +14 -0
  322. package/dist/utils/errors-server.js +31 -0
  323. package/dist/utils/load-model-validation.d.ts +7 -0
  324. package/dist/utils/load-model-validation.js +18 -0
  325. package/expo/plugins/patches/android-link.mjs +1 -1
  326. package/expo/plugins/patches/ios-link.mjs +1 -1
  327. package/package.json +15 -19
  328. package/dist/examples/translation/translation-opus.d.ts +0 -2
  329. package/dist/examples/translation/translation-opus.js +0 -33
  330. package/dist/server/bare/utils/loader-adapter.d.ts +0 -7
  331. package/dist/server/bare/utils/loader-adapter.js +0 -9
  332. package/dist/server/utils/model-path.d.ts +0 -5
  333. package/dist/server/utils/model-path.js +0 -7
  334. package/dist/server/utils/tool-parser.d.ts +0 -7
  335. package/dist/server/utils/tool-parser.js +0 -248
@@ -2,16 +2,14 @@ import { spawn } from "child_process";
2
2
  import { join, dirname } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import { completion, loadModel, close, LLAMA_3_2_1B_INST_Q4_0, } from "@qvac/sdk";
5
- // ── Config ──────────────────────────────────────────────────────────
6
- // Any 64-char hex string works as a topic — it's a shared secret that
7
- // lets the consumer discover the provider on Hyperswarm.
8
- const TOPIC = "66646f696865726f6569686a726530776a66646f696865726f6569686a726530";
9
- const PROVIDER_STARTUP_TIMEOUT_MS = 30_000;
5
+ // The consumer connects to the provider directly via its public key over the
6
+ // DHT (`dht.connect(publicKey)`). No topic or discovery step is involved —
7
+ // the provider just needs its DHT server listening on its keyPair.
8
+ const PROVIDER_STARTUP_TIMEOUT_MS = 60_000;
10
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
11
10
  const providerScript = join(__dirname, "provider.ts");
12
- // ── Provider lifecycle ──────────────────────────────────────────────
13
11
  function spawnProviderProcess() {
14
- const child = spawn("bun", ["run", providerScript, TOPIC], {
12
+ const child = spawn("bun", ["run", providerScript], {
15
13
  stdio: ["pipe", "pipe", "pipe"],
16
14
  });
17
15
  child.stderr?.on("data", (chunk) => {
@@ -25,9 +23,9 @@ function terminateProvider(provider) {
25
23
  }
26
24
  }
27
25
  // The provider's Hyperswarm identity (and therefore its public key) is
28
- // generated at startup — it can't be known ahead of time. We parse it
29
- // from the provider's stdout where it prints:
30
- // "🆔 Provider Public Key (unique): <hex>"
26
+ // generated at startup — it can't be known ahead of time. We parse it from
27
+ // the provider's stdout where it prints:
28
+ // "🆔 Provider Public Key: <hex>"
31
29
  function waitForProviderPublicKey(provider) {
32
30
  return new Promise((resolve, reject) => {
33
31
  let output = "";
@@ -38,7 +36,7 @@ function waitForProviderPublicKey(provider) {
38
36
  const str = chunk.toString();
39
37
  output += str;
40
38
  process.stdout.write(str);
41
- const match = output.match(/Provider Public Key \(unique\): ([a-f0-9]+)/i);
39
+ const match = output.match(/Provider Public Key: ([a-f0-9]+)/i);
42
40
  if (match) {
43
41
  clearTimeout(timeout);
44
42
  resolve(match[1]);
@@ -54,14 +52,12 @@ function waitForProviderPublicKey(provider) {
54
52
  });
55
53
  });
56
54
  }
57
- // ── Consumer: delegated model load + completion ─────────────────────
58
- async function runDelegatedCompletion(topic, providerPublicKey) {
55
+ async function runDelegatedCompletion(providerPublicKey) {
59
56
  console.log("→ Loading model via delegation...");
60
57
  const modelId = await loadModel({
61
58
  modelSrc: LLAMA_3_2_1B_INST_Q4_0,
62
59
  modelType: "llm",
63
60
  delegate: {
64
- topic,
65
61
  providerPublicKey,
66
62
  timeout: 30_000,
67
63
  },
@@ -83,14 +79,12 @@ async function runDelegatedCompletion(topic, providerPublicKey) {
83
79
  const stats = await response.stats;
84
80
  console.log(`\n📊 Stats: ${JSON.stringify(stats)}`);
85
81
  }
86
- // ── Main ────────────────────────────────────────────────────────────
87
82
  const provider = spawnProviderProcess();
88
83
  try {
89
84
  console.log("🔧 Waiting for provider to start and announce its key...\n");
90
85
  const publicKey = await waitForProviderPublicKey(provider);
91
- console.log(`\n📡 Provider ready — key: ${publicKey}`);
92
- console.log(`📡 Topic: ${TOPIC}\n`);
93
- await runDelegatedCompletion(TOPIC, publicKey);
86
+ console.log(`\n📡 Provider ready — key: ${publicKey}\n`);
87
+ await runDelegatedCompletion(publicKey);
94
88
  void close();
95
89
  }
96
90
  finally {
@@ -1,27 +1,20 @@
1
1
  import { close, completion, LLAMA_3_2_1B_INST_Q4_0, loadModel, profiler, } from "@qvac/sdk";
2
- const topicHex = process.argv[2];
3
- if (!topicHex) {
4
- console.error("❌ Usage: bun run consumer-profiled.ts <topic> <publicKey>");
5
- process.exit(1);
6
- }
7
- const providerPublicKey = process.argv[3];
2
+ const providerPublicKey = process.argv[2];
8
3
  if (!providerPublicKey) {
9
- console.error("❌ Usage: bun run consumer-profiled.ts <topic> <publicKey>");
4
+ console.error("❌ Usage: bun run consumer-profiled.ts <providerPublicKey>");
10
5
  process.exit(1);
11
6
  }
12
7
  try {
13
8
  profiler.enable({ mode: "verbose", includeServerBreakdown: true });
14
9
  console.log("✓ Profiler enabled");
15
- console.log(`\n📡 Topic: ${topicHex}`);
16
- console.log(`🔑 Provider: ${providerPublicKey}\n`);
10
+ console.log(`\n🔑 Provider: ${providerPublicKey}\n`);
17
11
  console.log("→ Loading model (delegated, unary)...");
18
12
  const modelId = await loadModel({
19
13
  modelSrc: LLAMA_3_2_1B_INST_Q4_0,
20
14
  modelType: "llm",
21
15
  delegate: {
22
- topic: topicHex,
23
16
  providerPublicKey,
24
- timeout: 30_000,
17
+ timeout: 60_000,
25
18
  },
26
19
  });
27
20
  console.log(`✓ Model loaded: ${modelId}\n`);
@@ -1,22 +1,14 @@
1
1
  import { completion, LLAMA_3_2_1B_INST_Q4_0, loadModel, close, } from "@qvac/sdk";
2
- // Topic hex (required)
3
- const topicHex = process.argv[2];
4
- if (!topicHex) {
5
- console.error("❌ Topic hex is required. Usage: node consumer.ts <topic-hex> <provider-public-key> [consumer-seed]");
6
- process.exit(1);
7
- }
8
- // Provider public key (required)
9
- const providerPublicKey = process.argv[3];
2
+ const providerPublicKey = process.argv[2];
10
3
  if (!providerPublicKey) {
11
- console.error("❌ Provider public key is required. Usage: node consumer.ts <topic-hex> <provider-public-key> [consumer-seed]");
4
+ console.error("❌ Provider public key is required. Usage: node consumer.ts <provider-public-key> [consumer-seed]");
12
5
  process.exit(1);
13
6
  }
14
7
  try {
15
8
  // Optional: Consumer seed for deterministic consumer identity (for firewall testing)
16
- const consumerSeed = process.argv[4];
9
+ const consumerSeed = process.argv[3];
17
10
  process.env["QVAC_HYPERSWARM_SEED"] = consumerSeed;
18
11
  console.log(`🚀 Testing delegated inference`);
19
- console.log(`📡 Topic: ${topicHex}`);
20
12
  console.log(`🔑 Provider: ${providerPublicKey}`);
21
13
  if (consumerSeed) {
22
14
  console.log(`🔑 Consumer seed: ${consumerSeed.substring(0, 16)}... (deterministic identity)`);
@@ -28,9 +20,12 @@ try {
28
20
  modelSrc: LLAMA_3_2_1B_INST_Q4_0,
29
21
  modelType: "llm",
30
22
  delegate: {
31
- topic: topicHex,
32
23
  providerPublicKey,
33
- timeout: 5_000, // Optional: 5 second timeout for delegated requests
24
+ // Generous timeout for the first call on a cold DHT: bootstrapping
25
+ // hyperdht and looking up the provider's key can take 15–45s on the
26
+ // very first run. Subsequent connections in the same process are
27
+ // sub-second because the DHT is already warm.
28
+ timeout: 60_000,
34
29
  fallbackToLocal: true, // Optional: Fall back to local inference if delegation fails
35
30
  // forceNewConnection: true, // Optional: Force a new connection instead of reusing cached one
36
31
  },
@@ -1,20 +1,17 @@
1
1
  import { startQVACProvider } from "@qvac/sdk";
2
- // Random topic if not provided
3
- const topic = process.argv[2] ||
4
- "66646f696865726f6569686a726530776a66646f696865726f6569686a726530";
5
2
  // Optional: Seed for deterministic provider identity (64-character hex string)
6
- const seed = process.argv[3];
7
- process.env["QVAC_HYPERSWARM_SEED"] = seed;
3
+ const seed = process.argv[2];
4
+ if (seed) {
5
+ process.env["QVAC_HYPERSWARM_SEED"] = seed;
6
+ }
8
7
  // Optional: Consumer public key for firewall (allow only this consumer)
9
- const allowedConsumerPublicKey = process.argv[4];
10
- console.log(`🚀 Starting provider service with topic: ${topic}...`);
8
+ const allowedConsumerPublicKey = process.argv[3];
9
+ console.log(`🚀 Starting provider service...`);
11
10
  try {
12
11
  if (allowedConsumerPublicKey) {
13
12
  console.log(`🔒 Firewall enabled: only allowing consumer ${allowedConsumerPublicKey}`);
14
13
  }
15
- // Start the provider service with optional firewall and seed
16
14
  const response = await startQVACProvider({
17
- topic,
18
15
  firewall: allowedConsumerPublicKey
19
16
  ? {
20
17
  mode: "allow",
@@ -26,14 +23,13 @@ try {
26
23
  console.log("🔗 Provider is now available for delegated inference requests");
27
24
  console.log("");
28
25
  console.log("📋 Connection Details:");
29
- console.log(` 📡 Topic (shared): ${topic}`);
30
- console.log(` 🆔 Provider Public Key (unique): ${response.publicKey}`);
26
+ console.log(` 🆔 Provider Public Key: ${response.publicKey}`);
31
27
  console.log("");
32
28
  console.log("💡 Consumer command:");
33
- console.log(` node consumer.ts ${topic} ${response.publicKey}`);
29
+ console.log(` node consumer.ts ${response.publicKey}`);
34
30
  console.log("");
35
31
  console.log("💡 To reproduce this provider identity:");
36
- console.log(` node provider.ts ${topic} ${seed || "<random-seed>"}`);
32
+ console.log(` node provider.ts ${seed || "<random-seed>"}`);
37
33
  if (!seed) {
38
34
  console.log(" (Note: seed was random this time, set one for reproducible identity)");
39
35
  }
@@ -41,9 +37,8 @@ try {
41
37
  console.log("🔒 For firewall testing:");
42
38
  console.log(" 1. Generate a consumer seed (64-char hex)");
43
39
  console.log(" 2. Get consumer public key: getConsumerPublicKey(consumerSeed)");
44
- console.log(" 3. Restart provider with consumer public key as 4th argument");
45
- console.log(` 4. Run consumer with: node consumer.ts ${topic} ${response.publicKey} <consumer-seed>`);
46
- // Keep the process running
40
+ console.log(" 3. Restart provider with consumer public key as 2nd argument");
41
+ console.log(` 4. Run consumer with: node consumer.ts ${response.publicKey} <consumer-seed>`);
47
42
  console.log("📡 Provider is running... Press Ctrl+C to stop");
48
43
  process.on("SIGINT", () => {
49
44
  console.log("\n🛑 Provider service stopped");
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diffusion-flux2-klein-img2img.d.ts.map
@@ -0,0 +1,60 @@
1
+ import { loadModel, unloadModel, diffusion, FLUX_2_KLEIN_4B_Q4_0, FLUX_2_KLEIN_4B_VAE, QWEN3_4B_Q4_K_M, } from "@qvac/sdk";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ // img2img with FLUX.2 [klein] split-layout — uses in-context conditioning ("flux2_flow").
5
+ const inputPath = process.argv[2];
6
+ const prompt = process.argv[3] || "oil painting style, vibrant colors";
7
+ const outputDir = process.argv[4] || ".";
8
+ const diffusionModelSrc = process.argv[5] || FLUX_2_KLEIN_4B_Q4_0;
9
+ const llmModelSrc = process.argv[6] || QWEN3_4B_Q4_K_M;
10
+ const vaeModelSrc = process.argv[7] || FLUX_2_KLEIN_4B_VAE;
11
+ if (!inputPath) {
12
+ console.error("❌ Error: input image path is required");
13
+ console.error("Usage: bun run bare:example dist/examples/diffusion-flux2-klein-img2img.js <inputImage> [prompt] [outputDir] [diffusionModelSrc] [llmModelSrc] [vaeModelSrc]");
14
+ process.exit(1);
15
+ }
16
+ try {
17
+ console.log("Loading FLUX.2 [klein] split-layout model...");
18
+ const modelId = await loadModel({
19
+ modelSrc: diffusionModelSrc,
20
+ modelType: "diffusion",
21
+ modelConfig: {
22
+ device: "gpu",
23
+ threads: 4,
24
+ llmModelSrc,
25
+ vaeModelSrc,
26
+ prediction: "flux2_flow",
27
+ },
28
+ onProgress: (p) => console.log(`Loading: ${p.percentage.toFixed(1)}%`),
29
+ });
30
+ console.log(`Model loaded: ${modelId}`);
31
+ const init_image = new Uint8Array(fs.readFileSync(inputPath));
32
+ console.log(`\nTransforming "${inputPath}" with prompt: "${prompt}"`);
33
+ const { progressStream, outputs, stats } = diffusion({
34
+ modelId,
35
+ prompt,
36
+ init_image,
37
+ steps: 20,
38
+ guidance: 3.5,
39
+ cfg_scale: 1,
40
+ seed: -1,
41
+ });
42
+ for await (const { step, totalSteps } of progressStream) {
43
+ process.stdout.write(`\rStep ${step}/${totalSteps}`);
44
+ }
45
+ console.log();
46
+ const buffers = await outputs;
47
+ for (let i = 0; i < buffers.length; i++) {
48
+ const outputPath = path.join(outputDir, `flux2_img2img_${i}.png`);
49
+ fs.writeFileSync(outputPath, buffers[i]);
50
+ console.log(`Saved: ${outputPath}`);
51
+ }
52
+ console.log("\nStats:", await stats);
53
+ await unloadModel({ modelId, clearStorage: false });
54
+ console.log("Done.");
55
+ process.exit(0);
56
+ }
57
+ catch (error) {
58
+ console.error("❌ Error:", error);
59
+ process.exit(1);
60
+ }
@@ -28,6 +28,7 @@ const { progressStream, outputs, stats } = diffusion({
28
28
  height: 512,
29
29
  steps: 20,
30
30
  guidance: 3.5,
31
+ cfg_scale: 1,
31
32
  seed: -1,
32
33
  });
33
34
  for await (const { step, totalSteps } of progressStream) {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diffusion-img2img.d.ts.map
@@ -0,0 +1,49 @@
1
+ import { loadModel, unloadModel, diffusion, SD_V2_1_1B_Q8_0 } from "@qvac/sdk";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ // img2img example — transforms an input image guided by a text prompt.
5
+ const inputPath = process.argv[2];
6
+ const prompt = process.argv[3] || "oil painting style, vibrant colors";
7
+ const outputDir = process.argv[4] || ".";
8
+ const modelSrc = process.argv[5] || SD_V2_1_1B_Q8_0;
9
+ if (!inputPath) {
10
+ console.error("❌ Error: input image path is required");
11
+ console.error("Usage: bun run bare:example dist/examples/diffusion-img2img.js <inputImage> [prompt] [outputDir] [modelSrc]");
12
+ process.exit(1);
13
+ }
14
+ try {
15
+ console.log("Loading diffusion model...");
16
+ const modelId = await loadModel({
17
+ modelSrc,
18
+ modelType: "diffusion",
19
+ });
20
+ console.log(`Model loaded: ${modelId}`);
21
+ const init_image = new Uint8Array(fs.readFileSync(inputPath));
22
+ console.log(`\nTransforming "${inputPath}" with prompt: "${prompt}"`);
23
+ const { progressStream, outputs, stats } = diffusion({
24
+ modelId,
25
+ prompt,
26
+ init_image,
27
+ strength: 0.5,
28
+ steps: 30,
29
+ seed: -1,
30
+ });
31
+ for await (const { step, totalSteps } of progressStream) {
32
+ process.stdout.write(`\rStep ${step}/${totalSteps}`);
33
+ }
34
+ console.log();
35
+ const buffers = await outputs;
36
+ for (let i = 0; i < buffers.length; i++) {
37
+ const outputPath = path.join(outputDir, `img2img_${i}.png`);
38
+ fs.writeFileSync(outputPath, buffers[i]);
39
+ console.log(`Saved: ${outputPath}`);
40
+ }
41
+ console.log("\nStats:", await stats);
42
+ await unloadModel({ modelId, clearStorage: false });
43
+ console.log("Done.");
44
+ process.exit(0);
45
+ }
46
+ catch (error) {
47
+ console.error("❌ Error:", error);
48
+ process.exit(1);
49
+ }
@@ -21,7 +21,8 @@ const { progressStream, outputs, stats } = diffusion({
21
21
  width: 512,
22
22
  height: 512,
23
23
  steps: 20,
24
- cfg_scale: 7.0,
24
+ guidance: 3.5,
25
+ cfg_scale: 1,
25
26
  seed: -1,
26
27
  });
27
28
  for await (const { step, totalSteps } of progressStream) {
@@ -11,8 +11,8 @@ try {
11
11
  },
12
12
  });
13
13
  console.log("🧠 Testing KV Cache functionality...\n");
14
- // First conversation with cache enabled
15
- console.log("📝 First conversation (building cache):");
14
+ // First conversation with auto-keyed cache enabled
15
+ console.log("📝 First conversation (building cache for the next turn):");
16
16
  const history1 = [
17
17
  { role: "user", content: "What is the capital of France?" },
18
18
  ];
@@ -22,24 +22,23 @@ try {
22
22
  stream: true,
23
23
  kvCache: true,
24
24
  }); // kvCache = true
25
- let response1 = "";
26
25
  for await (const token of result1.tokenStream) {
27
- response1 += token;
28
26
  process.stdout.write(token);
29
27
  }
30
- const stats1 = await result1.stats;
28
+ const final1 = await result1.final;
29
+ const stats1 = final1.stats;
31
30
  console.log(`\n⏱️ First completion stats: ${JSON.stringify(stats1)}\n`);
32
- // Continue conversation (should reuse cache from previous conversation)
33
- console.log("🔄 Continuing conversation (reusing cache):");
31
+ // Continue conversation (should reuse the completed first-turn cache).
32
+ console.log("🔄 Continuing conversation (reusing previous turn cache):");
34
33
  const history2 = [
35
34
  { role: "user", content: "What is the capital of France?" },
36
- { role: "assistant", content: response1.trim() },
35
+ { role: "assistant", content: final1.cacheableAssistantContent ?? final1.contentText },
37
36
  { role: "user", content: "What about Germany?" },
38
37
  ];
39
- // This should:
40
- // 1. Find existing cache from [user: "What is the capital of France?"] (history minus last message)
41
- // 2. Load that cache and process the new "What about Germany?" message
42
- // 3. Save the updated cache and rename it to include all messages
38
+ // Auto-keyed caching should:
39
+ // 1. Find the cache saved after turn 1 under [user, assistant]
40
+ // 2. Load that cache and process only the new "What about Germany?" user turn
41
+ // 3. Save the updated cache and rename it to include the new assistant response
43
42
  const result2 = completion({
44
43
  modelId,
45
44
  history: history2,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=llamacpp-cache.d.ts.map
@@ -0,0 +1,107 @@
1
+ import { completion, deleteCache, LLAMA_3_2_1B_INST_Q4_0, loadModel, unloadModel, VERBOSITY, } from "@qvac/sdk";
2
+ const sharedCacheKey = "llamacpp-cache-demo-shared";
3
+ const isolatedCacheKey = "llamacpp-cache-demo-isolated";
4
+ const systemPrompt = {
5
+ role: "system",
6
+ content: "You are a concise travel assistant. Keep track of the user's preferences across turns.",
7
+ };
8
+ function printStats(stats) {
9
+ console.log("\nPerformance Stats:", {
10
+ timeToFirstToken: stats?.timeToFirstToken,
11
+ tokensPerSecond: stats?.tokensPerSecond,
12
+ cacheTokens: stats?.cacheTokens,
13
+ });
14
+ }
15
+ async function runCachedCompletion(params) {
16
+ console.log(`\n=== ${params.label} ===`);
17
+ console.log(`Cache key: ${params.kvCache || "disabled"}`);
18
+ console.log("AI Response:");
19
+ const result = completion({
20
+ modelId: params.modelId,
21
+ history: params.history,
22
+ stream: true,
23
+ kvCache: params.kvCache,
24
+ });
25
+ let text = "";
26
+ for await (const token of result.tokenStream) {
27
+ text += token;
28
+ process.stdout.write(token);
29
+ }
30
+ const stats = await result.stats;
31
+ printStats(stats);
32
+ return { text, stats };
33
+ }
34
+ let modelId;
35
+ try {
36
+ console.log("Loading llama.cpp model with cache support...");
37
+ modelId = await loadModel({
38
+ modelSrc: LLAMA_3_2_1B_INST_Q4_0,
39
+ modelType: "llm",
40
+ modelConfig: {
41
+ ctx_size: 4096,
42
+ verbosity: VERBOSITY.ERROR,
43
+ },
44
+ onProgress: (progress) => {
45
+ console.log(`Loading: ${progress.percentage.toFixed(1)}%`);
46
+ },
47
+ });
48
+ console.log(`Model loaded successfully. Model ID: ${modelId}`);
49
+ await deleteCache({ kvCacheKey: sharedCacheKey });
50
+ await deleteCache({ kvCacheKey: isolatedCacheKey });
51
+ const firstUserMessage = {
52
+ role: "user",
53
+ content: "I am planning a weekend in Lisbon. I prefer museums, seafood, and walking routes. Suggest a day plan in 3 bullets.",
54
+ };
55
+ const firstTurn = await runCachedCompletion({
56
+ label: "Turn 1: create a named cache",
57
+ modelId,
58
+ kvCache: sharedCacheKey,
59
+ history: [systemPrompt, firstUserMessage],
60
+ });
61
+ await runCachedCompletion({
62
+ label: "Turn 2: reuse the same cache key",
63
+ modelId,
64
+ kvCache: sharedCacheKey,
65
+ history: [
66
+ systemPrompt,
67
+ firstUserMessage,
68
+ { role: "assistant", content: firstTurn.text.trim() },
69
+ {
70
+ role: "user",
71
+ content: "Now revise the plan for a rainy day, but keep the same preferences.",
72
+ },
73
+ ],
74
+ });
75
+ await runCachedCompletion({
76
+ label: "Turn 3: use a different cache key for an isolated session",
77
+ modelId,
78
+ kvCache: isolatedCacheKey,
79
+ history: [
80
+ systemPrompt,
81
+ {
82
+ role: "user",
83
+ content: "I am planning a weekend in Berlin. I prefer nightlife and late starts. Suggest a day plan in 3 bullets.",
84
+ },
85
+ ],
86
+ });
87
+ console.log("\nSummary:");
88
+ console.log(`- Reusing "${sharedCacheKey}" keeps conversation context across turns.`);
89
+ console.log(`- Switching to "${isolatedCacheKey}" starts a separate cache session.`);
90
+ console.log('- Use deleteCache({ kvCacheKey: "your-session" }) to clear a saved session.');
91
+ await deleteCache({ kvCacheKey: sharedCacheKey });
92
+ await deleteCache({ kvCacheKey: isolatedCacheKey });
93
+ await unloadModel({ modelId, clearStorage: false });
94
+ process.exit(0);
95
+ }
96
+ catch (error) {
97
+ if (modelId) {
98
+ try {
99
+ await unloadModel({ modelId, clearStorage: false });
100
+ }
101
+ catch (cleanupError) {
102
+ console.warn("⚠️ cleanup failed:", cleanupError);
103
+ }
104
+ }
105
+ console.error("❌ Error:", error);
106
+ process.exit(1);
107
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=llamacpp-dynamic-tools.d.ts.map
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Dynamic-tools mode example.
3
+ *
4
+ * In `dynamic` mode each user prompt can carry its own tool set: the addon
5
+ * anchors tools after the last user message, runs the tool-call chain, then
6
+ * trims the tools + chain output from the kv-cache so a later turn can ship
7
+ * a different tool list without poisoning the cache. Compare with
8
+ * `llamacpp-native-tools.ts`, which uses the default `static` mode where
9
+ * one shared tool set lives at the top of the session.
10
+ *
11
+ * Run with:
12
+ * bun run build
13
+ * bun run bare:example dist/examples/llamacpp-dynamic-tools.js
14
+ */
15
+ import { z } from "zod";
16
+ import { completion, loadModel, unloadModel, QWEN3_1_7B_INST_Q4, } from "@qvac/sdk";
17
+ const weatherSchema = z.object({
18
+ city: z.string().describe("City name"),
19
+ });
20
+ const horoscopeSchema = z.object({
21
+ sign: z.string().describe("An astrological sign, e.g. Taurus or Aquarius"),
22
+ });
23
+ const dateSchema = z.object({});
24
+ const toolSchemas = {
25
+ get_weather: weatherSchema,
26
+ get_horoscope: horoscopeSchema,
27
+ get_date: dateSchema,
28
+ };
29
+ const weatherTools = [
30
+ {
31
+ name: "get_weather",
32
+ description: "Get current weather for a city",
33
+ parameters: weatherSchema,
34
+ },
35
+ ];
36
+ const horoscopeTools = [
37
+ {
38
+ name: "get_horoscope",
39
+ description: "Get today's horoscope for an astrological sign",
40
+ parameters: horoscopeSchema,
41
+ },
42
+ ];
43
+ const dateTools = [
44
+ {
45
+ name: "get_date",
46
+ description: "Get today's date",
47
+ parameters: dateSchema,
48
+ },
49
+ ];
50
+ function executeToolCall(call) {
51
+ if (call.name === "get_weather") {
52
+ const args = call.arguments;
53
+ return `The weather in ${args.city} is rainy, 8°C with heavy clouds.`;
54
+ }
55
+ if (call.name === "get_horoscope") {
56
+ const args = call.arguments;
57
+ return `Horoscope for ${args.sign}: a great day for new beginnings.`;
58
+ }
59
+ if (call.name === "get_date") {
60
+ return new Date().toISOString().slice(0, 10);
61
+ }
62
+ return `Unknown tool: ${call.name}`;
63
+ }
64
+ async function chatTurn({ modelId, kvCache, history, tools }) {
65
+ const result = completion({
66
+ modelId,
67
+ history,
68
+ tools,
69
+ kvCache,
70
+ stream: true,
71
+ });
72
+ const tokensTask = (async () => {
73
+ for await (const token of result.tokenStream) {
74
+ process.stdout.write(token);
75
+ }
76
+ })();
77
+ const toolEventsTask = (async () => {
78
+ for await (const evt of result.toolCallStream) {
79
+ console.log(`\n→ tool call: ${evt.call.name}(${JSON.stringify(evt.call.arguments)})`);
80
+ }
81
+ })();
82
+ await Promise.all([tokensTask, toolEventsTask]);
83
+ const text = await result.text;
84
+ const toolCalls = await result.toolCalls;
85
+ if (toolCalls.length === 0) {
86
+ history.push({ role: "assistant", content: text });
87
+ return;
88
+ }
89
+ for (const call of toolCalls) {
90
+ const schema = toolSchemas[call.name];
91
+ if (schema) {
92
+ const parsed = schema.safeParse(call.arguments);
93
+ if (!parsed.success) {
94
+ console.warn(` ✗ validation failed for ${call.name}:`, parsed.error);
95
+ }
96
+ }
97
+ }
98
+ history.push({ role: "assistant", content: text });
99
+ for (const call of toolCalls) {
100
+ history.push({ role: "tool", content: executeToolCall(call) });
101
+ }
102
+ // Follow-up turn so the model can incorporate the tool results.
103
+ await chatTurn({ modelId, kvCache, history, tools });
104
+ }
105
+ async function main() {
106
+ const modelId = await loadModel({
107
+ modelSrc: QWEN3_1_7B_INST_Q4,
108
+ modelType: "llm",
109
+ modelConfig: {
110
+ ctx_size: 4096,
111
+ tools: true,
112
+ toolsMode: "dynamic",
113
+ },
114
+ onProgress: (progress) => console.log(`Loading: ${progress.percentage.toFixed(1)}%`),
115
+ });
116
+ console.log(`✅ Model loaded: ${modelId}`);
117
+ const kvCache = `dynamic-tools-${Date.now()}`;
118
+ const history = [
119
+ {
120
+ role: "system",
121
+ content: "You are a helpful assistant that uses tools when they are available. " +
122
+ "User's cat is named Windy and dog is named Butch.",
123
+ },
124
+ ];
125
+ // Turn 1 — only weather tools available.
126
+ history.push({ role: "user", content: "What's the weather in Tokyo?" });
127
+ console.log("\n🤖 Turn 1 (tools=weather):\n");
128
+ await chatTurn({ modelId, kvCache, history, tools: weatherTools });
129
+ // Turn 2 — same session, swap to horoscope tools. Dynamic mode lets the
130
+ // model see a different tool set without invalidating the kv-cache.
131
+ history.push({ role: "user", content: "Now check my horoscope for Aquarius." });
132
+ console.log("\n\n🤖 Turn 2 (tools=horoscope):\n");
133
+ await chatTurn({ modelId, kvCache, history, tools: horoscopeTools });
134
+ // Turn 3 — swap to a parameterless tool to confirm empty-arg flows work.
135
+ history.push({ role: "user", content: "What's today's date?" });
136
+ console.log("\n\n🤖 Turn 3 (tools=date):\n");
137
+ await chatTurn({ modelId, kvCache, history, tools: dateTools });
138
+ console.log("\n\n🎉 Done.");
139
+ await unloadModel({ modelId, clearStorage: false });
140
+ }
141
+ main().catch((err) => {
142
+ console.error("❌ Error:", err);
143
+ process.exit(1);
144
+ });