llama-cpp-pro 0.2.2

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 (310) hide show
  1. package/CHANGELOG.md +295 -0
  2. package/LICENSE +21 -0
  3. package/LlamaCpp.podspec +33 -0
  4. package/LlamaCppCapacitor.podspec +33 -0
  5. package/Package.swift +29 -0
  6. package/README.md +93 -0
  7. package/android/build.gradle +88 -0
  8. package/android/src/main/AndroidManifest.xml +4 -0
  9. package/android/src/main/CMakeLists-arm64.txt +138 -0
  10. package/android/src/main/CMakeLists-x86_64.txt +141 -0
  11. package/android/src/main/CMakeLists.txt +138 -0
  12. package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCpp.java +1340 -0
  13. package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCppPlugin.java +814 -0
  14. package/android/src/main/java/ai/annadata/plugin/capacitor/ModelAdmissionController.java +214 -0
  15. package/android/src/main/jni-chat-session.cpp +261 -0
  16. package/android/src/main/jni-lora.cpp +197 -0
  17. package/android/src/main/jni-multimodal.cpp +167 -0
  18. package/android/src/main/jni-tts.cpp +290 -0
  19. package/android/src/main/jni-utils.h +148 -0
  20. package/android/src/main/jni.cpp +1808 -0
  21. package/android/src/main/jniLibs/arm64-v8a/libllama-cpp-arm64.so +0 -0
  22. package/android/src/main/res/.gitkeep +0 -0
  23. package/build-native.sh +280 -0
  24. package/cmake/desktop-metal-embed.cmake +30 -0
  25. package/cmake/desktop-sources.cmake +100 -0
  26. package/cmake/ggml-backends.cmake +44 -0
  27. package/cpp/LICENSE +21 -0
  28. package/cpp/README.md +15 -0
  29. package/cpp/anyascii.c +22223 -0
  30. package/cpp/anyascii.h +42 -0
  31. package/cpp/cap-completion.cpp +942 -0
  32. package/cpp/cap-completion.h +127 -0
  33. package/cpp/cap-embedding.cpp +193 -0
  34. package/cpp/cap-embedding.h +35 -0
  35. package/cpp/cap-ios-bridge.cpp +1810 -0
  36. package/cpp/cap-ios-bridge.h +61 -0
  37. package/cpp/cap-llama.cpp +412 -0
  38. package/cpp/cap-llama.h +161 -0
  39. package/cpp/cap-mtmd.hpp +602 -0
  40. package/cpp/cap-native-server.cpp +1095 -0
  41. package/cpp/cap-native-server.h +40 -0
  42. package/cpp/cap-tts.cpp +591 -0
  43. package/cpp/cap-tts.h +59 -0
  44. package/cpp/cap-wasm-fs.cpp +156 -0
  45. package/cpp/cap-wasm-jspi.cpp +19 -0
  46. package/cpp/cap-wasm-jspi.h +30 -0
  47. package/cpp/cap-wasm-vfs.cpp +22 -0
  48. package/cpp/chat-parser.cpp +393 -0
  49. package/cpp/chat-parser.h +120 -0
  50. package/cpp/chat.cpp +2315 -0
  51. package/cpp/chat.h +221 -0
  52. package/cpp/common.cpp +1664 -0
  53. package/cpp/common.h +744 -0
  54. package/cpp/ggml-alloc.c +1028 -0
  55. package/cpp/ggml-alloc.h +76 -0
  56. package/cpp/ggml-backend-impl.h +255 -0
  57. package/cpp/ggml-backend-reg.cpp +600 -0
  58. package/cpp/ggml-backend.cpp +2121 -0
  59. package/cpp/ggml-backend.h +354 -0
  60. package/cpp/ggml-common.h +1878 -0
  61. package/cpp/ggml-cpp.h +39 -0
  62. package/cpp/ggml-cpu/amx/amx.cpp +221 -0
  63. package/cpp/ggml-cpu/amx/amx.h +8 -0
  64. package/cpp/ggml-cpu/amx/common.h +91 -0
  65. package/cpp/ggml-cpu/amx/mmq.cpp +2512 -0
  66. package/cpp/ggml-cpu/amx/mmq.h +10 -0
  67. package/cpp/ggml-cpu/arch/arm/cpu-feats.cpp +94 -0
  68. package/cpp/ggml-cpu/arch/arm/quants.c +3650 -0
  69. package/cpp/ggml-cpu/arch/arm/repack.cpp +1891 -0
  70. package/cpp/ggml-cpu/arch/x86/cpu-feats.cpp +327 -0
  71. package/cpp/ggml-cpu/arch/x86/quants.c +3820 -0
  72. package/cpp/ggml-cpu/arch/x86/repack.cpp +6307 -0
  73. package/cpp/ggml-cpu/arch-fallback.h +215 -0
  74. package/cpp/ggml-cpu/binary-ops.cpp +158 -0
  75. package/cpp/ggml-cpu/binary-ops.h +16 -0
  76. package/cpp/ggml-cpu/common.h +73 -0
  77. package/cpp/ggml-cpu/ggml-cpu-impl.h +559 -0
  78. package/cpp/ggml-cpu/ggml-cpu.c +3578 -0
  79. package/cpp/ggml-cpu/ggml-cpu.cpp +672 -0
  80. package/cpp/ggml-cpu/ops.cpp +10587 -0
  81. package/cpp/ggml-cpu/ops.h +114 -0
  82. package/cpp/ggml-cpu/quants.c +1193 -0
  83. package/cpp/ggml-cpu/quants.h +97 -0
  84. package/cpp/ggml-cpu/repack.cpp +1982 -0
  85. package/cpp/ggml-cpu/repack.h +120 -0
  86. package/cpp/ggml-cpu/simd-mappings.h +1184 -0
  87. package/cpp/ggml-cpu/traits.cpp +36 -0
  88. package/cpp/ggml-cpu/traits.h +38 -0
  89. package/cpp/ggml-cpu/unary-ops.cpp +186 -0
  90. package/cpp/ggml-cpu/unary-ops.h +28 -0
  91. package/cpp/ggml-cpu/vec.cpp +348 -0
  92. package/cpp/ggml-cpu/vec.h +1121 -0
  93. package/cpp/ggml-cpu.h +145 -0
  94. package/cpp/ggml-impl.h +622 -0
  95. package/cpp/ggml-metal-impl.h +688 -0
  96. package/cpp/ggml-metal.h +66 -0
  97. package/cpp/ggml-metal.m +6833 -0
  98. package/cpp/ggml-metal.metal +10754 -0
  99. package/cpp/ggml-opt.cpp +1093 -0
  100. package/cpp/ggml-opt.h +256 -0
  101. package/cpp/ggml-quants.c +5324 -0
  102. package/cpp/ggml-quants.h +106 -0
  103. package/cpp/ggml-threading.cpp +12 -0
  104. package/cpp/ggml-threading.h +14 -0
  105. package/cpp/ggml.c +7108 -0
  106. package/cpp/ggml.h +2492 -0
  107. package/cpp/gguf.cpp +1358 -0
  108. package/cpp/gguf.h +202 -0
  109. package/cpp/json-partial.cpp +256 -0
  110. package/cpp/json-partial.h +38 -0
  111. package/cpp/json-schema-to-grammar.cpp +985 -0
  112. package/cpp/json-schema-to-grammar.h +21 -0
  113. package/cpp/llama-adapter.cpp +388 -0
  114. package/cpp/llama-adapter.h +76 -0
  115. package/cpp/llama-arch.cpp +2355 -0
  116. package/cpp/llama-arch.h +499 -0
  117. package/cpp/llama-batch.cpp +875 -0
  118. package/cpp/llama-batch.h +160 -0
  119. package/cpp/llama-chat.cpp +783 -0
  120. package/cpp/llama-chat.h +65 -0
  121. package/cpp/llama-context.cpp +2788 -0
  122. package/cpp/llama-context.h +306 -0
  123. package/cpp/llama-cparams.cpp +5 -0
  124. package/cpp/llama-cparams.h +41 -0
  125. package/cpp/llama-cpp.h +30 -0
  126. package/cpp/llama-grammar.cpp +1229 -0
  127. package/cpp/llama-grammar.h +173 -0
  128. package/cpp/llama-graph.cpp +1891 -0
  129. package/cpp/llama-graph.h +810 -0
  130. package/cpp/llama-hparams.cpp +180 -0
  131. package/cpp/llama-hparams.h +233 -0
  132. package/cpp/llama-impl.cpp +167 -0
  133. package/cpp/llama-impl.h +61 -0
  134. package/cpp/llama-io.cpp +15 -0
  135. package/cpp/llama-io.h +35 -0
  136. package/cpp/llama-kv-cache-iswa.cpp +318 -0
  137. package/cpp/llama-kv-cache-iswa.h +135 -0
  138. package/cpp/llama-kv-cache.cpp +2059 -0
  139. package/cpp/llama-kv-cache.h +374 -0
  140. package/cpp/llama-kv-cells.h +491 -0
  141. package/cpp/llama-memory-hybrid.cpp +258 -0
  142. package/cpp/llama-memory-hybrid.h +137 -0
  143. package/cpp/llama-memory-recurrent.cpp +1146 -0
  144. package/cpp/llama-memory-recurrent.h +179 -0
  145. package/cpp/llama-memory.cpp +59 -0
  146. package/cpp/llama-memory.h +119 -0
  147. package/cpp/llama-mmap.cpp +609 -0
  148. package/cpp/llama-mmap.h +68 -0
  149. package/cpp/llama-model-loader.cpp +1166 -0
  150. package/cpp/llama-model-loader.h +170 -0
  151. package/cpp/llama-model-saver.cpp +282 -0
  152. package/cpp/llama-model-saver.h +37 -0
  153. package/cpp/llama-model.cpp +19061 -0
  154. package/cpp/llama-model.h +491 -0
  155. package/cpp/llama-sampling.cpp +2575 -0
  156. package/cpp/llama-sampling.h +32 -0
  157. package/cpp/llama-vocab.cpp +3792 -0
  158. package/cpp/llama-vocab.h +176 -0
  159. package/cpp/llama.cpp +358 -0
  160. package/cpp/llama.h +1373 -0
  161. package/cpp/log.cpp +428 -0
  162. package/cpp/log.h +103 -0
  163. package/cpp/minja/chat-template.hpp +550 -0
  164. package/cpp/minja/minja.hpp +3009 -0
  165. package/cpp/nlohmann/json.hpp +25526 -0
  166. package/cpp/nlohmann/json_fwd.hpp +187 -0
  167. package/cpp/regex-partial.cpp +204 -0
  168. package/cpp/regex-partial.h +56 -0
  169. package/cpp/sampling.cpp +579 -0
  170. package/cpp/sampling.h +107 -0
  171. package/cpp/tools/mtmd/clip-impl.h +473 -0
  172. package/cpp/tools/mtmd/clip.cpp +4322 -0
  173. package/cpp/tools/mtmd/clip.h +106 -0
  174. package/cpp/tools/mtmd/miniaudio/miniaudio.h +93468 -0
  175. package/cpp/tools/mtmd/mtmd-audio.cpp +769 -0
  176. package/cpp/tools/mtmd/mtmd-audio.h +47 -0
  177. package/cpp/tools/mtmd/mtmd-helper.cpp +460 -0
  178. package/cpp/tools/mtmd/mtmd-helper.h +95 -0
  179. package/cpp/tools/mtmd/mtmd.cpp +1066 -0
  180. package/cpp/tools/mtmd/mtmd.h +302 -0
  181. package/cpp/tools/mtmd/stb/stb_image.h +7988 -0
  182. package/cpp/unicode-data.cpp +7034 -0
  183. package/cpp/unicode-data.h +20 -0
  184. package/cpp/unicode.cpp +1061 -0
  185. package/cpp/unicode.h +68 -0
  186. package/cpp/vendor/cpp-httplib/httplib.cpp +16509 -0
  187. package/cpp/vendor/cpp-httplib/httplib.h +3883 -0
  188. package/desktop/electron-builder.config.cjs +157 -0
  189. package/desktop/entitlements.mac.plist +12 -0
  190. package/desktop/package.json +11 -0
  191. package/desktop/resolve-package-root.cjs +88 -0
  192. package/desktop/src/main/backend-selector.cjs +148 -0
  193. package/desktop/src/main/gpu-probe.cjs +142 -0
  194. package/desktop/src/main/index.cjs +58 -0
  195. package/desktop/src/main/ipc-handlers.cjs +212 -0
  196. package/desktop/src/main/model-store.cjs +50 -0
  197. package/desktop/src/main/preload.cjs +39 -0
  198. package/desktop/src/main/sidecar-client.cjs +76 -0
  199. package/desktop/src/main/sidecar-manager.cjs +364 -0
  200. package/dist/docs.json +14357 -0
  201. package/dist/esm/definitions.d.ts +731 -0
  202. package/dist/esm/definitions.js +2 -0
  203. package/dist/esm/definitions.js.map +1 -0
  204. package/dist/esm/desktop.d.ts +37 -0
  205. package/dist/esm/desktop.js +133 -0
  206. package/dist/esm/desktop.js.map +1 -0
  207. package/dist/esm/index.d.ts +200 -0
  208. package/dist/esm/index.js +612 -0
  209. package/dist/esm/index.js.map +1 -0
  210. package/dist/esm/isomorphic/desktop.runtime.d.ts +37 -0
  211. package/dist/esm/isomorphic/desktop.runtime.js +36 -0
  212. package/dist/esm/isomorphic/desktop.runtime.js.map +1 -0
  213. package/dist/esm/isomorphic/errors.d.ts +6 -0
  214. package/dist/esm/isomorphic/errors.js +9 -0
  215. package/dist/esm/isomorphic/errors.js.map +1 -0
  216. package/dist/esm/isomorphic/model.admission.d.ts +17 -0
  217. package/dist/esm/isomorphic/model.admission.js +27 -0
  218. package/dist/esm/isomorphic/model.admission.js.map +1 -0
  219. package/dist/esm/isomorphic/model.scheduler.d.ts +31 -0
  220. package/dist/esm/isomorphic/model.scheduler.js +95 -0
  221. package/dist/esm/isomorphic/model.scheduler.js.map +1 -0
  222. package/dist/esm/isomorphic/provider.desktop.d.ts +40 -0
  223. package/dist/esm/isomorphic/provider.desktop.js +411 -0
  224. package/dist/esm/isomorphic/provider.desktop.js.map +1 -0
  225. package/dist/esm/isomorphic/provider.factory.d.ts +2 -0
  226. package/dist/esm/isomorphic/provider.factory.js +16 -0
  227. package/dist/esm/isomorphic/provider.factory.js.map +1 -0
  228. package/dist/esm/isomorphic/provider.interface.d.ts +76 -0
  229. package/dist/esm/isomorphic/provider.interface.js +2 -0
  230. package/dist/esm/isomorphic/provider.interface.js.map +1 -0
  231. package/dist/esm/isomorphic/provider.native.d.ts +18 -0
  232. package/dist/esm/isomorphic/provider.native.js +173 -0
  233. package/dist/esm/isomorphic/provider.native.js.map +1 -0
  234. package/dist/esm/isomorphic/provider.web.d.ts +88 -0
  235. package/dist/esm/isomorphic/provider.web.js +573 -0
  236. package/dist/esm/isomorphic/provider.web.js.map +1 -0
  237. package/dist/esm/isomorphic/sidecar-sse.d.ts +14 -0
  238. package/dist/esm/isomorphic/sidecar-sse.js +76 -0
  239. package/dist/esm/isomorphic/sidecar-sse.js.map +1 -0
  240. package/dist/esm/isomorphic/wasmMemoryCalibration.d.ts +26 -0
  241. package/dist/esm/isomorphic/wasmMemoryCalibration.js +60 -0
  242. package/dist/esm/isomorphic/wasmMemoryCalibration.js.map +1 -0
  243. package/dist/esm/isomorphic/wasmMemoryPolicy.d.ts +55 -0
  244. package/dist/esm/isomorphic/wasmMemoryPolicy.js +93 -0
  245. package/dist/esm/isomorphic/wasmMemoryPolicy.js.map +1 -0
  246. package/dist/esm/storage/manifest.d.ts +13 -0
  247. package/dist/esm/storage/manifest.js +87 -0
  248. package/dist/esm/storage/manifest.js.map +1 -0
  249. package/dist/esm/storage/opfs.store.d.ts +31 -0
  250. package/dist/esm/storage/opfs.store.js +241 -0
  251. package/dist/esm/storage/opfs.store.js.map +1 -0
  252. package/dist/esm/web.d.ts +206 -0
  253. package/dist/esm/web.js +521 -0
  254. package/dist/esm/web.js.map +1 -0
  255. package/dist/esm/workers/async-file.d.ts +13 -0
  256. package/dist/esm/workers/async-file.js +12 -0
  257. package/dist/esm/workers/async-file.js.map +1 -0
  258. package/dist/esm/workers/heapfs.d.ts +55 -0
  259. package/dist/esm/workers/heapfs.js +115 -0
  260. package/dist/esm/workers/heapfs.js.map +1 -0
  261. package/dist/esm/workers/wasm.engine.d.ts +86 -0
  262. package/dist/esm/workers/wasm.engine.js +504 -0
  263. package/dist/esm/workers/wasm.engine.js.map +1 -0
  264. package/dist/esm/workers/worker.protocol.d.ts +160 -0
  265. package/dist/esm/workers/worker.protocol.js +2 -0
  266. package/dist/esm/workers/worker.protocol.js.map +1 -0
  267. package/dist/plugin.cjs +3179 -0
  268. package/dist/plugin.cjs.map +1 -0
  269. package/dist/plugin.js +3181 -0
  270. package/dist/plugin.js.map +1 -0
  271. package/dist/wasm/llama_engine.d.ts +74 -0
  272. package/dist/wasm/llama_engine.js +1829 -0
  273. package/dist/wasm/llama_engine.wasm +0 -0
  274. package/dist/wasm/llama_engine_emscripten.mjs +2 -0
  275. package/dist/wasm/package.json +16 -0
  276. package/dist/workers/llm.worker.js +1226 -0
  277. package/dist/workers/llm.worker.js.map +7 -0
  278. package/extraResources/llama-wasm/llama_engine.d.ts +74 -0
  279. package/extraResources/llama-wasm/llama_engine.js +1829 -0
  280. package/extraResources/llama-wasm/llama_engine.wasm +0 -0
  281. package/extraResources/llama-wasm/llama_engine_emscripten.mjs +2 -0
  282. package/extraResources/llama-wasm/package.json +16 -0
  283. package/extraResources/sidecar/README.md +11 -0
  284. package/extraResources/sidecar/darwin-arm64 +0 -0
  285. package/extraResources/sidecar/darwin-x64 +0 -0
  286. package/ios/CMakeLists-arm64.txt +161 -0
  287. package/ios/CMakeLists-x86_64.txt +188 -0
  288. package/ios/CMakeLists.txt +161 -0
  289. package/ios/Frameworks/llama-cpp.framework/Info.plist +28 -0
  290. package/ios/Frameworks/llama-cpp.framework/llama-cpp +0 -0
  291. package/ios/Sources/LlamaCppCapacitor/LlamaCpp.swift +1365 -0
  292. package/ios/Sources/LlamaCppCapacitor/LlamaCppPlugin.swift +694 -0
  293. package/ios/Sources/LlamaCppCapacitor/LlamaNativeBridge.swift +349 -0
  294. package/ios/Sources/LlamaCppCapacitor/ModelAdmissionController.swift +177 -0
  295. package/ios/embed-metal-shaders.sh +24 -0
  296. package/ios/metal-embed.cmake +29 -0
  297. package/package.json +281 -0
  298. package/scripts/build-js-preserve-wasm.cjs +53 -0
  299. package/scripts/build-sidecar-linux.sh +6 -0
  300. package/scripts/build-sidecar-win.bat +19 -0
  301. package/scripts/build-sidecar.sh +184 -0
  302. package/scripts/embed-llama-ios-app-framework.sh +63 -0
  303. package/scripts/ensure-desktop-sidecar-bundle.cjs +103 -0
  304. package/scripts/ensure-llama-ios-xcframework.sh +108 -0
  305. package/scripts/fix-esm-extensions.cjs +45 -0
  306. package/scripts/prepare-js-dist.cjs +28 -0
  307. package/scripts/stage-desktop-resources.cjs +116 -0
  308. package/sidecar/CMakeLists.txt +192 -0
  309. package/sidecar/cap-sidecar-main.cpp +68 -0
  310. package/types/llama-cpp-pro.d.ts +441 -0
@@ -0,0 +1,942 @@
1
+ #include "cap-completion.h"
2
+ #include "cap-llama.h"
3
+ #include "cap-tts.h"
4
+ #include "cap-mtmd.hpp"
5
+ #include <algorithm> // For std::sort in speculative decoding
6
+ #include <chrono>
7
+ #include <cmath>
8
+
9
+ // Include multimodal support
10
+ #include "tools/mtmd/mtmd.h"
11
+ #include "tools/mtmd/mtmd-helper.h"
12
+ #include "tools/mtmd/clip.h"
13
+
14
+ namespace capllama {
15
+
16
+ static bool ends_with(const std::string &str, const std::string &suffix)
17
+ {
18
+ return str.size() >= suffix.size() &&
19
+ 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
20
+ }
21
+
22
+ static size_t find_partial_stop_string(const std::string &stop,
23
+ const std::string &text)
24
+ {
25
+ if (!text.empty() && !stop.empty())
26
+ {
27
+ const char text_last_char = text.back();
28
+ for (int64_t char_index = stop.size() - 1; char_index >= 0; char_index--)
29
+ {
30
+ if (stop[char_index] == text_last_char)
31
+ {
32
+ const std::string current_partial = stop.substr(0, char_index + 1);
33
+ if (ends_with(text, current_partial))
34
+ {
35
+ return text.size() - char_index - 1;
36
+ }
37
+ }
38
+ }
39
+ }
40
+ return std::string::npos;
41
+ }
42
+
43
+ // Helper function to format rerank task: [BOS]query[EOS][SEP]doc[EOS]
44
+ static std::vector<llama_token> format_rerank(const llama_vocab * vocab, const std::vector<llama_token> & query, const std::vector<llama_token> & doc) {
45
+ std::vector<llama_token> result;
46
+
47
+ // Get EOS token - use SEP token as fallback if EOS is not available
48
+ llama_token eos_token = llama_vocab_eos(vocab);
49
+ if (eos_token == LLAMA_TOKEN_NULL) {
50
+ eos_token = llama_vocab_sep(vocab);
51
+ }
52
+
53
+ result.reserve(doc.size() + query.size() + 4);
54
+ if (llama_vocab_get_add_bos(vocab)) {
55
+ result.push_back(llama_vocab_bos(vocab));
56
+ }
57
+ result.insert(result.end(), query.begin(), query.end());
58
+ if (llama_vocab_get_add_eos(vocab)) {
59
+ result.push_back(eos_token);
60
+ }
61
+ if (llama_vocab_get_add_sep(vocab)) {
62
+ result.push_back(llama_vocab_sep(vocab));
63
+ }
64
+ result.insert(result.end(), doc.begin(), doc.end());
65
+ if (llama_vocab_get_add_eos(vocab)) {
66
+ result.push_back(eos_token);
67
+ }
68
+
69
+ return result;
70
+ }
71
+
72
+ /** When the sampler picks EOG on the first new token, take the best non-EOG logit instead. */
73
+ static llama_token sample_completion_token(
74
+ struct common_sampler * ctx_sampling,
75
+ llama_context * ctx,
76
+ const llama_vocab * vocab,
77
+ bool allow_eog)
78
+ {
79
+ llama_token id = common_sampler_sample(ctx_sampling, ctx, -1);
80
+ if (allow_eog || !llama_vocab_is_eog(vocab, id)) {
81
+ return id;
82
+ }
83
+
84
+ const float * logits = llama_get_logits_ith(ctx, -1);
85
+ if (!logits) {
86
+ return id;
87
+ }
88
+
89
+ const int n_vocab = llama_vocab_n_tokens(vocab);
90
+ llama_token best = LLAMA_TOKEN_NULL;
91
+ float best_logit = -INFINITY;
92
+ for (llama_token tid = 0; tid < n_vocab; ++tid) {
93
+ if (llama_vocab_is_eog(vocab, tid)) {
94
+ continue;
95
+ }
96
+ if (logits[tid] > best_logit) {
97
+ best_logit = logits[tid];
98
+ best = tid;
99
+ }
100
+ }
101
+
102
+ if (best != LLAMA_TOKEN_NULL && std::isfinite(best_logit)) {
103
+ #ifdef __EMSCRIPTEN__
104
+ fprintf(stderr, "@@WASM_GEN@@ first_token_eog=%d fallback=%d logit=%.3f\n",
105
+ id, best, best_logit);
106
+ #endif
107
+ return best;
108
+ }
109
+ return id;
110
+ }
111
+
112
+ // Constructor
113
+ llama_cap_context_completion::llama_cap_context_completion(llama_cap_context* parent)
114
+ : parent_ctx(parent) {
115
+ }
116
+
117
+ // Destructor
118
+ llama_cap_context_completion::~llama_cap_context_completion() {
119
+ if (ctx_sampling != nullptr) {
120
+ common_sampler_free(ctx_sampling);
121
+ ctx_sampling = nullptr;
122
+ }
123
+ }
124
+
125
+ void llama_cap_context_completion::rewind() {
126
+ is_interrupted = false;
127
+ parent_ctx->params.antiprompt.clear();
128
+ parent_ctx->params.sampling.grammar.clear();
129
+ num_prompt_tokens = 0;
130
+ num_tokens_predicted = 0;
131
+ prefill_text = "";
132
+ generated_text = "";
133
+ generated_text.reserve(parent_ctx->params.n_ctx);
134
+ truncated = false;
135
+ context_full = false;
136
+ stopped_eos = false;
137
+ stopped_word = false;
138
+ stopped_limit = false;
139
+ stopping_word = "";
140
+ incomplete = false;
141
+ n_remain = 0;
142
+ n_past = 0;
143
+ embd.clear();
144
+ parent_ctx->params.sampling.n_prev = parent_ctx->n_ctx;
145
+ if (parent_ctx->isVocoderEnabled()) {
146
+ parent_ctx->tts_wrapper->audio_tokens.clear();
147
+ parent_ctx->tts_wrapper->next_token_uses_guide_token = true;
148
+ parent_ctx->tts_wrapper->guide_tokens.clear();
149
+ }
150
+ }
151
+
152
+ bool llama_cap_context_completion::initSampling() {
153
+ if (ctx_sampling != nullptr) {
154
+ common_sampler_free(ctx_sampling);
155
+ }
156
+ ctx_sampling = common_sampler_init(parent_ctx->model, parent_ctx->params.sampling);
157
+ return ctx_sampling != nullptr;
158
+ }
159
+
160
+ void llama_cap_context_completion::truncatePrompt(std::vector<llama_token> &prompt_tokens) {
161
+ const int n_left = parent_ctx->n_ctx - parent_ctx->params.n_keep;
162
+ const int n_block_size = n_left / 2;
163
+ const int erased_blocks = (prompt_tokens.size() - parent_ctx->params.n_keep - n_block_size) / n_block_size;
164
+
165
+ // Keep n_keep tokens at start of prompt (at most n_ctx - 4)
166
+ std::vector<llama_token> new_tokens(prompt_tokens.begin(), prompt_tokens.begin() + parent_ctx->params.n_keep);
167
+
168
+ new_tokens.insert(new_tokens.end(), prompt_tokens.begin() + parent_ctx->params.n_keep + erased_blocks * n_block_size, prompt_tokens.end());
169
+
170
+ LOG_INFO("input truncated, n_ctx: %d, n_keep: %d, n_left: %d, old_size: %d, new_size: %d",
171
+ parent_ctx->n_ctx,
172
+ parent_ctx->params.n_keep,
173
+ n_left,
174
+ prompt_tokens.size(),
175
+ new_tokens.size()
176
+ );
177
+
178
+ truncated = true;
179
+ prompt_tokens = new_tokens;
180
+ }
181
+
182
+ void llama_cap_context_completion::loadPrompt(const std::vector<std::string> &media_paths) {
183
+ bool has_media = !media_paths.empty();
184
+
185
+ if (!has_media) {
186
+ std::vector<llama_token> text_tokens;
187
+ // Text-only path — chat templates from the app already include BOS/start markers.
188
+ bool add_bos = true;
189
+ #ifdef __EMSCRIPTEN__
190
+ const std::string & pr = parent_ctx->params.prompt;
191
+ if (pr.find("<|startoftext|>") != std::string::npos ||
192
+ pr.find("<|im_start|>") != std::string::npos ||
193
+ pr.rfind("<|startoftext|>", 0) == 0) {
194
+ add_bos = false;
195
+ }
196
+ #endif
197
+ text_tokens = ::common_tokenize(parent_ctx->ctx, parent_ctx->params.prompt, add_bos, true);
198
+ num_prompt_tokens = text_tokens.size();
199
+
200
+ // LOG tokens
201
+ std::stringstream ss;
202
+ ss << "\n" << __func__ << ": prompt_tokens = ";
203
+ for (auto& token : text_tokens) {
204
+ ss << token << " ";
205
+ }
206
+ LOG_INFO("%s\n", ss.str().c_str());
207
+
208
+ if (parent_ctx->params.n_keep < 0) {
209
+ parent_ctx->params.n_keep = (int)num_prompt_tokens;
210
+ }
211
+ parent_ctx->params.n_keep = std::min(parent_ctx->n_ctx - 4, parent_ctx->params.n_keep);
212
+
213
+ // Handle truncation if needed
214
+ if (num_prompt_tokens >= (size_t)parent_ctx->n_ctx) {
215
+ if (!parent_ctx->params.ctx_shift) {
216
+ context_full = true;
217
+ return;
218
+ }
219
+ truncatePrompt(text_tokens);
220
+ num_prompt_tokens = text_tokens.size();
221
+ LM_GGML_ASSERT(num_prompt_tokens < (size_t)parent_ctx->n_ctx);
222
+ }
223
+
224
+ // Update sampling context
225
+ for (auto & token : text_tokens) {
226
+ common_sampler_accept(ctx_sampling, token, false);
227
+ }
228
+
229
+ // compare the evaluated prompt with the new prompt
230
+ n_past = common_part(embd, text_tokens);
231
+
232
+ embd = text_tokens;
233
+ if (n_past == num_prompt_tokens) {
234
+ // we have to evaluate at least 1 token to generate logits.
235
+ n_past--;
236
+ }
237
+
238
+ // Manage KV cache
239
+ auto * kv = llama_get_memory(parent_ctx->ctx);
240
+ llama_memory_seq_rm(kv, 0, n_past, -1);
241
+
242
+ LOG_VERBOSE("prompt ingested, n_past: %d, cached: %s, to_eval: %s",
243
+ n_past,
244
+ tokens_to_str(parent_ctx->ctx, embd.cbegin(), embd.cbegin() + n_past).c_str(),
245
+ tokens_to_str(parent_ctx->ctx, embd.cbegin() + n_past, embd.cend()).c_str()
246
+ );
247
+
248
+ // Prefill prompt here so the generate loop only samples new tokens.
249
+ #ifdef __EMSCRIPTEN__
250
+ const auto prefill_t0 = std::chrono::steady_clock::now();
251
+ #endif
252
+ while (n_past < (int) embd.size()) {
253
+ const int n_eval = std::min((int) embd.size() - n_past, parent_ctx->params.n_batch);
254
+ if (llama_decode(parent_ctx->ctx, llama_batch_get_one(embd.data() + n_past, n_eval))) {
255
+ LOG_ERROR("loadPrompt: prefill decode failed at n_past=%d n_eval=%d", n_past, n_eval);
256
+ has_next_token = false;
257
+ return;
258
+ }
259
+ n_past += n_eval;
260
+ }
261
+ #ifdef __EMSCRIPTEN__
262
+ const auto prefill_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
263
+ std::chrono::steady_clock::now() - prefill_t0).count();
264
+ fprintf(stderr, "@@WASM_GEN@@ prefill_done tokens=%zu n_batch=%d ms=%lld\n",
265
+ embd.size(), parent_ctx->params.n_batch, (long long) prefill_ms);
266
+ #endif
267
+ } else {
268
+ // Multimodal path - process all media paths
269
+ processMedia(parent_ctx->params.prompt, media_paths);
270
+ num_prompt_tokens = embd.size();
271
+ }
272
+
273
+ has_next_token = true;
274
+
275
+ LOG_INFO("[DEBUG] Input processed: n_past=%d, embd.size=%zu, num_prompt_tokens=%zu, has_media=%d",
276
+ n_past, embd.size(), num_prompt_tokens, has_media ? 1 : 0);
277
+ }
278
+
279
+ void llama_cap_context_completion::beginCompletion() {
280
+ beginCompletion(COMMON_CHAT_FORMAT_CONTENT_ONLY, COMMON_REASONING_FORMAT_NONE, false);
281
+ }
282
+
283
+ void llama_cap_context_completion::beginCompletion(int chat_format, common_reasoning_format reasoning_format, bool thinking_forced_open) {
284
+ // number of tokens to keep when resetting context
285
+ n_remain = parent_ctx->params.n_predict;
286
+ llama_perf_context_reset(parent_ctx->ctx);
287
+ is_predicting = true;
288
+
289
+ current_chat_format = chat_format;
290
+ current_reasoning_format = reasoning_format;
291
+ current_thinking_forced_open = thinking_forced_open;
292
+ }
293
+
294
+ void llama_cap_context_completion::endCompletion() {
295
+ is_predicting = false;
296
+ }
297
+
298
+ completion_token_output llama_cap_context_completion::nextToken()
299
+ {
300
+ completion_token_output result;
301
+ result.tok = -1;
302
+
303
+ if (embd.size() >= (size_t)parent_ctx->params.n_ctx)
304
+ {
305
+ if (!parent_ctx->params.ctx_shift) {
306
+ // If context shifting is disabled, stop generation
307
+ LOG_WARNING("context full, n_ctx: %d, tokens: %d", parent_ctx->params.n_ctx, embd.size());
308
+ has_next_token = false;
309
+ context_full = true;
310
+ return result;
311
+ }
312
+
313
+ // Shift context
314
+
315
+ const int n_left = n_past - parent_ctx->params.n_keep - 1;
316
+ const int n_discard = n_left/2;
317
+
318
+ auto * kv = llama_get_memory(parent_ctx->ctx);
319
+ llama_memory_seq_rm (kv, 0, parent_ctx->params.n_keep + 1 , parent_ctx->params.n_keep + n_discard + 1);
320
+ llama_memory_seq_add(kv, 0, parent_ctx->params.n_keep + 1 + n_discard, n_past, -n_discard);
321
+
322
+ for (size_t i = parent_ctx->params.n_keep + 1 + n_discard; i < embd.size(); i++)
323
+ {
324
+ embd[i - n_discard] = embd[i];
325
+ }
326
+ embd.resize(embd.size() - n_discard);
327
+
328
+ n_past -= n_discard;
329
+ truncated = true;
330
+
331
+ LOG_VERBOSE("context shifted, new n_past: %d, new size: %d", n_past, embd.size());
332
+ }
333
+
334
+ bool tg = true;
335
+ while (n_past < embd.size())
336
+ {
337
+ int n_eval = (int)embd.size() - n_past;
338
+ tg = n_eval == 1;
339
+ if (n_eval > parent_ctx->params.n_batch)
340
+ {
341
+ n_eval = parent_ctx->params.n_batch;
342
+ }
343
+ if (llama_decode(parent_ctx->ctx, llama_batch_get_one(&embd[n_past], n_eval)))
344
+ {
345
+ LOG_ERROR("failed to eval, n_eval: %d, n_past: %d, n_threads: %d, embd: %s",
346
+ n_eval,
347
+ n_past,
348
+ parent_ctx->params.cpuparams.n_threads,
349
+ tokens_to_str(parent_ctx->ctx, embd.cbegin() + n_past, embd.cend()).c_str()
350
+ );
351
+ has_next_token = false;
352
+ return result;
353
+ }
354
+ n_past += n_eval;
355
+
356
+ if(is_interrupted) {
357
+ LOG_INFO("Decoding Interrupted");
358
+ embd.resize(n_past);
359
+ has_next_token = false;
360
+ return result;
361
+ }
362
+ }
363
+
364
+ const llama_vocab* vocab = llama_model_get_vocab(parent_ctx->model);
365
+
366
+ if (parent_ctx->params.n_predict == 0)
367
+ {
368
+ has_next_token = false;
369
+ result.tok = llama_vocab_eos(vocab);
370
+ return result;
371
+ }
372
+
373
+ {
374
+ // out of user input, sample next token
375
+ #ifndef __EMSCRIPTEN__
376
+ std::vector<llama_token_data> candidates;
377
+ candidates.reserve(llama_vocab_n_tokens(vocab));
378
+ #endif
379
+
380
+ const bool allow_eog = num_tokens_predicted > 0;
381
+ llama_token new_token_id = sample_completion_token(
382
+ ctx_sampling, parent_ctx->ctx, vocab, allow_eog);
383
+
384
+ if (llama_vocab_is_eog(vocab, new_token_id)) {
385
+ has_next_token = false;
386
+ stopped_eos = true;
387
+ LOG_VERBOSE("EOS: %s", common_token_to_piece(parent_ctx->ctx, new_token_id).c_str());
388
+ return result;
389
+ }
390
+
391
+ if (parent_ctx->tts_wrapper != nullptr && parent_ctx->tts_wrapper->next_token_uses_guide_token && !parent_ctx->tts_wrapper->guide_tokens.empty() && !llama_vocab_is_control(vocab, new_token_id)) {
392
+ new_token_id = parent_ctx->tts_wrapper->guide_tokens[0];
393
+ parent_ctx->tts_wrapper->guide_tokens.erase(parent_ctx->tts_wrapper->guide_tokens.begin());
394
+ }
395
+ if (parent_ctx->tts_wrapper != nullptr) {
396
+ parent_ctx->tts_wrapper->next_token_uses_guide_token = (new_token_id == 198);
397
+ }
398
+ result.tok = new_token_id;
399
+
400
+ llama_token_data_array cur_p = *common_sampler_get_candidates(ctx_sampling);
401
+
402
+ const int32_t n_probs = parent_ctx->params.sampling.n_probs;
403
+
404
+ for (size_t i = 0; i < std::min(cur_p.size, (size_t)n_probs); ++i)
405
+ {
406
+ result.probs.push_back({cur_p.data[i].id, cur_p.data[i].p});
407
+ }
408
+
409
+ common_sampler_accept(ctx_sampling, result.tok, true);
410
+ if (tg) {
411
+ num_tokens_predicted++;
412
+ }
413
+ }
414
+
415
+ // add it to the context
416
+ embd.push_back(result.tok);
417
+ // decrement remaining sampling budget
418
+ --n_remain;
419
+
420
+ has_next_token = parent_ctx->params.n_predict == -1 || n_remain != 0;
421
+ return result;
422
+ }
423
+
424
+ size_t llama_cap_context_completion::findStoppingStrings(const std::string &text, const size_t last_token_size,
425
+ const stop_type type)
426
+ {
427
+ size_t stop_pos = std::string::npos;
428
+ for (const std::string &word : parent_ctx->params.antiprompt)
429
+ {
430
+ size_t pos;
431
+ if (type == STOP_FULL)
432
+ {
433
+ const size_t tmp = word.size() + last_token_size;
434
+ const size_t from_pos = text.size() > tmp ? text.size() - tmp : 0;
435
+ pos = text.find(word, from_pos);
436
+ }
437
+ else
438
+ {
439
+ pos = find_partial_stop_string(word, text);
440
+ }
441
+ if (pos != std::string::npos &&
442
+ (stop_pos == std::string::npos || pos < stop_pos))
443
+ {
444
+ if (type == STOP_FULL)
445
+ {
446
+ stopping_word = word;
447
+ stopped_word = true;
448
+ has_next_token = false;
449
+ }
450
+ stop_pos = pos;
451
+ }
452
+ }
453
+ return stop_pos;
454
+ }
455
+
456
+ completion_token_output llama_cap_context_completion::doCompletion()
457
+ {
458
+ completion_token_output token_with_probs = nextToken();
459
+
460
+ const std::string token_text = token_with_probs.tok == -1 ? "" : common_token_to_piece(parent_ctx->ctx, token_with_probs.tok);
461
+ generated_text += token_text;
462
+
463
+ if (parent_ctx->isVocoderEnabled()) {
464
+ tts_type type = parent_ctx->tts_wrapper->getTTSType(parent_ctx);
465
+ if (parent_ctx->tts_wrapper->type == UNKNOWN) {
466
+ parent_ctx->tts_wrapper->type = type;
467
+ }
468
+ if ((type == OUTETTS_V0_2 || type == OUTETTS_V0_3) && (token_with_probs.tok >= 151672 && token_with_probs.tok <= 155772)) {
469
+ parent_ctx->tts_wrapper->audio_tokens.push_back(token_with_probs.tok);
470
+ }
471
+ }
472
+
473
+ if (parent_ctx->params.sampling.n_probs > 0)
474
+ {
475
+ generated_token_probs.push_back(token_with_probs);
476
+ }
477
+
478
+ if (!parent_ctx->params.antiprompt.empty() && token_with_probs.tok >= 0) {
479
+ const size_t stop_pos = findStoppingStrings(generated_text, token_text.size(), STOP_FULL);
480
+ if (stop_pos != std::string::npos) {
481
+ generated_text = generated_text.substr(0, stop_pos);
482
+ }
483
+ }
484
+
485
+ // check if there is incomplete UTF-8 character at the end
486
+ for (unsigned i = 1; i < 5 && i <= generated_text.size(); ++i) {
487
+ unsigned char c = generated_text[generated_text.size() - i];
488
+ if ((c & 0xC0) == 0x80) {
489
+ // continuation byte: 10xxxxxx
490
+ continue;
491
+ }
492
+ if ((c & 0xE0) == 0xC0) {
493
+ // 2-byte character: 110xxxxx ...
494
+ incomplete = i < 2;
495
+ } else if ((c & 0xF0) == 0xE0) {
496
+ // 3-byte character: 1110xxxx ...
497
+ incomplete = i < 3;
498
+ } else if ((c & 0xF8) == 0xF0) {
499
+ // 4-byte character: 11110xxx ...
500
+ incomplete = i < 4;
501
+ }
502
+ // else 1-byte character or invalid byte
503
+ break;
504
+ }
505
+
506
+ if (incomplete && !has_next_token)
507
+ {
508
+ has_next_token = true;
509
+ n_remain++;
510
+ }
511
+
512
+ if (!has_next_token && n_remain == 0)
513
+ {
514
+ stopped_limit = true;
515
+ }
516
+
517
+ LOG_VERBOSE("next token, token: %s, token_text: %s, has_next_token: %d, n_remain: %d, num_tokens_predicted: %d, stopped_eos: %d, stopped_word: %d, stopped_limit: %d, stopping_word: %s",
518
+ common_token_to_piece(parent_ctx->ctx, token_with_probs.tok),
519
+ tokens_to_output_formatted_string(parent_ctx->ctx, token_with_probs.tok).c_str(),
520
+ has_next_token,
521
+ n_remain,
522
+ num_tokens_predicted,
523
+ stopped_eos,
524
+ stopped_word,
525
+ stopped_limit,
526
+ stopping_word.c_str()
527
+ );
528
+ return token_with_probs;
529
+ }
530
+
531
+ completion_partial_output llama_cap_context_completion::getPartialOutput(const std::string &token_text) {
532
+ common_chat_syntax syntax;
533
+ syntax.format = static_cast<common_chat_format>(current_chat_format);
534
+ syntax.reasoning_format = current_reasoning_format;
535
+ syntax.thinking_forced_open = current_thinking_forced_open;
536
+ syntax.parse_tool_calls = true;
537
+
538
+ common_chat_msg parsed_msg = common_chat_parse(prefill_text + generated_text, true, syntax);
539
+
540
+ completion_partial_output result;
541
+
542
+ result.content = parsed_msg.content;
543
+ result.reasoning_content = parsed_msg.reasoning_content;
544
+ result.accumulated_text = prefill_text + generated_text;
545
+ result.tool_calls = parsed_msg.tool_calls;
546
+
547
+ return result;
548
+ }
549
+
550
+ std::vector<float> llama_cap_context_completion::getEmbedding(common_params &embd_params)
551
+ {
552
+ static const int n_embd = llama_model_n_embd(llama_get_model(parent_ctx->ctx));
553
+ if (!embd_params.embedding)
554
+ {
555
+ LOG_WARNING("embedding disabled, embedding: %s", embd_params.embedding);
556
+ return std::vector<float>(n_embd, 0.0f);
557
+ }
558
+ float *data;
559
+
560
+ const enum llama_pooling_type pooling_type = llama_pooling_type(parent_ctx->ctx);
561
+ printf("pooling_type: %d\n", pooling_type);
562
+ if (pooling_type == LLAMA_POOLING_TYPE_NONE) {
563
+ data = llama_get_embeddings(parent_ctx->ctx);
564
+ } else {
565
+ data = llama_get_embeddings_seq(parent_ctx->ctx, 0);
566
+ }
567
+
568
+ if (!data) {
569
+ return std::vector<float>(n_embd, 0.0f);
570
+ }
571
+ std::vector<float> embedding(data, data + n_embd), out(data, data + n_embd);
572
+ common_embd_normalize(embedding.data(), out.data(), n_embd, embd_params.embd_normalize);
573
+ return out;
574
+ }
575
+
576
+ std::vector<float> llama_cap_context_completion::rerank(const std::string &query, const std::vector<std::string> &documents)
577
+ {
578
+ std::vector<float> scores;
579
+
580
+ // Check if this model supports reranking (requires rank pooling type)
581
+ const enum llama_pooling_type pooling_type = llama_pooling_type(parent_ctx->ctx);
582
+ if (pooling_type != LLAMA_POOLING_TYPE_RANK) {
583
+ throw std::runtime_error("reranking not supported, pooling_type: " + std::to_string(pooling_type));
584
+ }
585
+
586
+ if (!parent_ctx->params.embedding) {
587
+ throw std::runtime_error("embedding disabled but required for reranking");
588
+ }
589
+
590
+ const llama_vocab * vocab = llama_model_get_vocab(parent_ctx->model);
591
+ std::vector<llama_token> query_tokens = common_tokenize(vocab, query, false, true);
592
+
593
+ scores.reserve(documents.size());
594
+
595
+ for (size_t i = 0; i < documents.size(); ++i) {
596
+ rewind();
597
+ embd = {};
598
+
599
+ const std::string & document = documents[i];
600
+
601
+ std::vector<llama_token> doc_tokens = common_tokenize(vocab, document, false, true);
602
+
603
+ std::vector<llama_token> rerank_tokens = format_rerank(vocab, query_tokens, doc_tokens);
604
+
605
+ llama_memory_clear(llama_get_memory(parent_ctx->ctx), false);
606
+
607
+ // Process the rerank input
608
+ try {
609
+ parent_ctx->params.prompt = tokens_to_str(parent_ctx->ctx, rerank_tokens.begin(), rerank_tokens.end());
610
+ initSampling();
611
+ loadPrompt({}); // No media paths for rerank
612
+ beginCompletion();
613
+ doCompletion();
614
+
615
+ // Get the rerank score (single embedding value for rank pooling)
616
+ float *data = llama_get_embeddings_seq(parent_ctx->ctx, 0);
617
+ if (data) {
618
+ scores.push_back(data[0]); // For rank pooling, the score is the first (and only) dimension
619
+ } else {
620
+ scores.push_back(-1e6f); // Default low score if computation failed
621
+ }
622
+ } catch (const std::exception &e) {
623
+ LOG_WARNING("rerank computation failed for document %zu: %s", i, e.what());
624
+ scores.push_back(-1e6f);
625
+ }
626
+ endCompletion();
627
+
628
+ // Clear KV cache again to prepare for next document or restore original state
629
+ llama_memory_clear(llama_get_memory(parent_ctx->ctx), false);
630
+ }
631
+
632
+ return scores;
633
+ }
634
+
635
+ std::string llama_cap_context_completion::bench(int pp, int tg, int pl, int nr)
636
+ {
637
+ if (is_predicting) {
638
+ LOG_ERROR("cannot benchmark while predicting", "");
639
+ return std::string("[]");
640
+ }
641
+
642
+ is_predicting = true;
643
+
644
+ double pp_avg = 0;
645
+ double tg_avg = 0;
646
+
647
+ double pp_std = 0;
648
+ double tg_std = 0;
649
+
650
+ // TODO: move batch into llama_cap_context (related https://github.com/mybigday/llama.rn/issues/30)
651
+ llama_batch batch = llama_batch_init(
652
+ std::min(pp, parent_ctx->params.n_ubatch), // max n_tokens is limited by n_ubatch
653
+ 0, // No embeddings
654
+ 1 // Single sequence
655
+ );
656
+
657
+ for (int i = 0; i < nr; i++)
658
+ {
659
+ llama_batch_clear(&batch);
660
+
661
+ const int n_tokens = pp;
662
+
663
+ for (int i = 0; i < n_tokens; i++)
664
+ {
665
+ llama_batch_add(&batch, 0, i, {0}, false);
666
+ }
667
+ batch.logits[batch.n_tokens - 1] = 1; // true
668
+
669
+ llama_memory_clear(llama_get_memory(parent_ctx->ctx), true);
670
+
671
+ const int64_t t_pp_start = llama_time_us();
672
+ if (llama_decode(parent_ctx->ctx, batch) != 0)
673
+ {
674
+ LOG_ERROR("llama_decode() failed during prompt", "");
675
+ }
676
+ const int64_t t_pp_end = llama_time_us();
677
+
678
+ llama_memory_clear(llama_get_memory(parent_ctx->ctx), true);
679
+
680
+ if (is_interrupted) break;
681
+
682
+ const int64_t t_tg_start = llama_time_us();
683
+
684
+ for (int i = 0; i < tg; i++)
685
+ {
686
+ llama_batch_clear(&batch);
687
+
688
+ for (int j = 0; j < pl; j++)
689
+ {
690
+ llama_batch_add(&batch, 0, i, {j}, true);
691
+ }
692
+
693
+ if (llama_decode(parent_ctx->ctx, batch) != 0)
694
+ {
695
+ LOG_ERROR("llama_decode() failed during text generation", "");
696
+ }
697
+ if (is_interrupted) break;
698
+ }
699
+
700
+ const int64_t t_tg_end = llama_time_us();
701
+
702
+ llama_memory_clear(llama_get_memory(parent_ctx->ctx), true);
703
+
704
+ const double t_pp = (t_pp_end - t_pp_start) / 1000000.0;
705
+ const double t_tg = (t_tg_end - t_tg_start) / 1000000.0;
706
+
707
+ const double speed_pp = pp / t_pp;
708
+ const double speed_tg = (pl * tg) / t_tg;
709
+
710
+ pp_avg += speed_pp;
711
+ tg_avg += speed_tg;
712
+
713
+ pp_std += speed_pp * speed_pp;
714
+ tg_std += speed_tg * speed_tg;
715
+ }
716
+
717
+ pp_avg /= nr;
718
+ tg_avg /= nr;
719
+
720
+ if (nr > 1) {
721
+ pp_std = sqrt(pp_std / (nr - 1) - pp_avg * pp_avg * nr / (nr - 1));
722
+ tg_std = sqrt(tg_std / (nr - 1) - tg_avg * tg_avg * nr / (nr - 1));
723
+ } else {
724
+ pp_std = 0;
725
+ tg_std = 0;
726
+ }
727
+
728
+ if (is_interrupted) llama_memory_clear(llama_get_memory(parent_ctx->ctx), true);
729
+ endCompletion();
730
+
731
+ char model_desc[128];
732
+ llama_model_desc(parent_ctx->model, model_desc, sizeof(model_desc));
733
+ return std::string("[\"") + model_desc + std::string("\",") +
734
+ std::to_string(llama_model_size(parent_ctx->model)) + std::string(",") +
735
+ std::to_string(llama_model_n_params(parent_ctx->model)) + std::string(",") +
736
+ std::to_string(pp_avg) + std::string(",") +
737
+ std::to_string(pp_std) + std::string(",") +
738
+ std::to_string(tg_avg) + std::string(",") +
739
+ std::to_string(tg_std) +
740
+ std::string("]");
741
+ }
742
+
743
+ void llama_cap_context_completion::processMedia(
744
+ const std::string &prompt,
745
+ const std::vector<std::string> &media_paths
746
+ ) {
747
+ if (!parent_ctx->isMultimodalEnabled()) {
748
+ throw std::runtime_error("Multimodal is not enabled but image paths are provided");
749
+ }
750
+
751
+ // Delegate to the mtmd_wrapper method
752
+ parent_ctx->mtmd_wrapper->processMedia(
753
+ parent_ctx->ctx,
754
+ prompt,
755
+ media_paths,
756
+ parent_ctx->n_ctx,
757
+ parent_ctx->params.n_batch,
758
+ n_past,
759
+ embd,
760
+ context_full,
761
+ ctx_sampling
762
+ );
763
+ }
764
+
765
+ // Speculative decoding implementation
766
+ completion_token_output llama_cap_context_completion::nextTokenSpeculative() {
767
+ // Enable speculative mode
768
+ use_speculative = parent_ctx->isSpectulativeEnabled();
769
+
770
+ if (!use_speculative) {
771
+ // Fallback to regular token generation
772
+ return nextToken();
773
+ }
774
+
775
+ completion_token_output result;
776
+
777
+ // If we don't have drafted tokens, draft some
778
+ if (draft_tokens.empty()) {
779
+ draft_tokens = draftTokens(parent_ctx->speculative_samples);
780
+ n_drafted = draft_tokens.size();
781
+ }
782
+
783
+ // Try to verify and accept draft tokens
784
+ if (!draft_tokens.empty()) {
785
+ int accepted = verifyAndAcceptTokens(draft_tokens);
786
+ n_accepted += accepted;
787
+
788
+ if (accepted > 0) {
789
+ // Use the first accepted token
790
+ result.tok = draft_tokens[0];
791
+ draft_tokens.erase(draft_tokens.begin());
792
+
793
+ // Update context
794
+ embd.push_back(result.tok);
795
+ --n_remain;
796
+ num_tokens_predicted++;
797
+
798
+ has_next_token = parent_ctx->params.n_predict == -1 || n_remain != 0;
799
+ return result;
800
+ }
801
+ }
802
+
803
+ // If no tokens were accepted, fall back to regular sampling
804
+ draft_tokens.clear();
805
+ return nextToken();
806
+ }
807
+
808
+ std::vector<llama_token> llama_cap_context_completion::draftTokens(int n_draft) {
809
+ std::vector<llama_token> drafted;
810
+
811
+ // Check if draft model is available
812
+ if (!parent_ctx->draft_ctx || !parent_ctx->draft_model || !parent_ctx->isSpectulativeEnabled()) {
813
+ return drafted; // Return empty vector - will fallback to regular decoding
814
+ }
815
+
816
+ // Copy current context to draft model
817
+ // Note: KV cache copying may not be available in all llama.cpp versions
818
+ // For now, we'll skip this optimization and let the draft model generate from scratch
819
+ // This is still effective for speculative decoding
820
+
821
+ // Generate draft tokens using the smaller model
822
+ for (int i = 0; i < n_draft; i++) {
823
+ // Create batch with current context
824
+ llama_batch batch = llama_batch_init(1, 0, 1);
825
+
826
+ if (!embd.empty()) {
827
+ llama_batch_add(&batch, embd.back(), n_past + i, {0}, true);
828
+ }
829
+
830
+ // Decode with draft model
831
+ if (llama_decode(parent_ctx->draft_ctx, batch) != 0) {
832
+ llama_batch_free(batch);
833
+ break;
834
+ }
835
+
836
+ // Sample from draft model (using faster, simpler sampling)
837
+ const float temp = 0.8f; // Fixed temperature for draft
838
+ auto logits = llama_get_logits_ith(parent_ctx->draft_ctx, -1);
839
+ const int n_vocab = llama_vocab_n_tokens(llama_model_get_vocab(parent_ctx->draft_model));
840
+
841
+ // Simple sampling for draft model
842
+ std::vector<llama_token_data> candidates;
843
+ candidates.reserve(n_vocab);
844
+
845
+ for (int token_id = 0; token_id < n_vocab; token_id++) {
846
+ candidates.emplace_back(llama_token_data{token_id, logits[token_id], 0.0f});
847
+ }
848
+
849
+ llama_token_data_array candidates_p = {
850
+ candidates.data(),
851
+ candidates.size(),
852
+ -1,
853
+ false,
854
+ };
855
+
856
+ // Simple temperature sampling for draft model
857
+ for (int token_id = 0; token_id < n_vocab; token_id++) {
858
+ candidates[token_id].logit /= temp; // Apply temperature
859
+ }
860
+
861
+ // Sort by logit (simple greedy sampling for draft)
862
+ std::sort(candidates.begin(), candidates.end(),
863
+ [](const llama_token_data& a, const llama_token_data& b) {
864
+ return a.logit > b.logit;
865
+ });
866
+
867
+ llama_token token = candidates[0].id; // Take top token
868
+ drafted.push_back(token);
869
+
870
+ // Clean up
871
+ llama_batch_free(batch);
872
+
873
+ // Stop if we hit EOS
874
+ const llama_vocab * vocab = llama_model_get_vocab(parent_ctx->draft_model);
875
+ if (llama_vocab_is_eog(vocab, token)) {
876
+ break;
877
+ }
878
+ }
879
+
880
+ return drafted;
881
+ }
882
+
883
+ int llama_cap_context_completion::verifyAndAcceptTokens(const std::vector<llama_token> &draft_tokens) {
884
+ if (draft_tokens.empty() || !parent_ctx->ctx) {
885
+ return 0;
886
+ }
887
+
888
+ int accepted = 0;
889
+
890
+ // Verify each draft token against the main model
891
+ for (size_t i = 0; i < draft_tokens.size(); i++) {
892
+ // Create batch for verification
893
+ llama_batch batch = llama_batch_init(1, 0, 1);
894
+
895
+ if (!embd.empty()) {
896
+ llama_batch_add(&batch, embd.back(), n_past + accepted, {0}, true);
897
+ }
898
+
899
+ // Decode with main model
900
+ if (llama_decode(parent_ctx->ctx, batch) != 0) {
901
+ llama_batch_free(batch);
902
+ break;
903
+ }
904
+
905
+ // Get logits from main model
906
+ auto logits = llama_get_logits_ith(parent_ctx->ctx, -1);
907
+ const int n_vocab = llama_vocab_n_tokens(llama_model_get_vocab(parent_ctx->model));
908
+
909
+ // Sample from main model
910
+ std::vector<llama_token_data> candidates;
911
+ candidates.reserve(n_vocab);
912
+
913
+ for (int token_id = 0; token_id < n_vocab; token_id++) {
914
+ candidates.emplace_back(llama_token_data{token_id, logits[token_id], 0.0f});
915
+ }
916
+
917
+ llama_token_data_array candidates_p = {
918
+ candidates.data(),
919
+ candidates.size(),
920
+ -1,
921
+ false,
922
+ };
923
+
924
+ // Apply sampling from main model using common_sampler
925
+ llama_token main_token = common_sampler_sample(ctx_sampling, parent_ctx->ctx, -1);
926
+
927
+ // Accept if tokens match
928
+ if (main_token == draft_tokens[i]) {
929
+ accepted++;
930
+ common_sampler_accept(ctx_sampling, main_token, true);
931
+ } else {
932
+ // Reject and stop verification
933
+ break;
934
+ }
935
+
936
+ llama_batch_free(batch);
937
+ }
938
+
939
+ return accepted;
940
+ }
941
+
942
+ } // namespace capllama