You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

LLamaModel.cs 28 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. using LLama.Native;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Text;
  6. using LLama.Exceptions;
  7. using System.Linq;
  8. using LLama.Extensions;
  9. namespace LLama
  10. {
  11. using llama_token = Int32;
  12. public class LLamaModel: IChatModel, IDisposable
  13. {
  14. LLamaParams _params;
  15. SafeLLamaContextHandle _ctx;
  16. string _path_session;
  17. List<llama_token> _session_tokens;
  18. List<llama_token> _embed_inp;
  19. int _n_ctx;
  20. List<llama_token> _inp_pfx;
  21. List<llama_token> _inp_sfx;
  22. List<llama_token> _llama_token_newline;
  23. List<llama_token> _last_n_tokens;
  24. bool _is_interacting;
  25. bool _is_antiprompt;
  26. bool _input_echo;
  27. // HACK - because session saving incurs a non-negligible delay, for now skip re-saving session
  28. // if we loaded a session with at least 75% similarity. It's currently just used to speed up the
  29. // initial prompt so it doesn't need to be an exact match.
  30. bool _need_to_save_session;
  31. int _n_past;
  32. int _n_remain;
  33. int _n_consumed;
  34. int _n_session_consumed;
  35. List<llama_token> _embed;
  36. public string Name { get; set; }
  37. public SafeLLamaContextHandle NativeHandle => _ctx;
  38. public LLamaModel(string model_path, string model_name, bool echo_input = false, bool verbose = false, int seed = 0, int n_threads = -1, int n_predict = -1,
  39. int n_parts = -1, int n_ctx = 512, int n_batch = 512, int n_keep = 0, int n_gpu_layers = -1,
  40. Dictionary<llama_token, float> logit_bias = null, int top_k = 40, float top_p = 0.95f,
  41. float tfs_z = 1.00f, float typical_p = 1.00f, float temp = 0.80f, float repeat_penalty = 1.10f,
  42. int repeat_last_n = 64, float frequency_penalty = 0.00f, float presence_penalty = 0.00f,
  43. int mirostat = 0, float mirostat_tau = 5.00f, float mirostat_eta = 0.10f, string prompt = "",
  44. string path_session = "", string input_prefix = "", string input_suffix = "",
  45. List<string> antiprompt = null, string lora_adapter = "", string lora_base = "",
  46. bool memory_f16 = true, bool random_prompt = false, bool use_color = false, bool interactive = false,
  47. bool embedding = false, bool interactive_first = false, bool prompt_cache_all = false, bool instruct = false, bool penalize_nl = true,
  48. bool perplexity = false, bool use_mmap = true, bool use_mlock = false, bool mem_test = false,
  49. bool verbose_prompt = false, string encoding = "UTF-8") : this(new LLamaParams(seed: seed,
  50. n_threads: n_threads,
  51. n_predict: n_predict,
  52. n_ctx: n_ctx,
  53. n_batch: n_batch,
  54. n_keep: n_keep,
  55. n_gpu_layers: n_gpu_layers,
  56. logit_bias: logit_bias,
  57. top_k: top_k,
  58. top_p: top_p,
  59. tfs_z: tfs_z,
  60. typical_p: typical_p,
  61. temp: temp,
  62. repeat_penalty: repeat_penalty,
  63. repeat_last_n: repeat_last_n,
  64. frequency_penalty: frequency_penalty,
  65. presence_penalty: presence_penalty,
  66. mirostat: mirostat,
  67. mirostat_tau: mirostat_tau,
  68. mirostat_eta: mirostat_eta,
  69. model: model_path,
  70. prompt: prompt,
  71. path_session: path_session,
  72. input_prefix: input_prefix,
  73. input_suffix: input_suffix,
  74. antiprompt: antiprompt,
  75. lora_adapter: lora_adapter,
  76. lora_base: lora_base,
  77. memory_f16: memory_f16,
  78. random_prompt: random_prompt,
  79. use_color: use_color,
  80. interactive: interactive,
  81. embedding: embedding,
  82. interactive_first: interactive_first,
  83. prompt_cache_all: prompt_cache_all,
  84. instruct: instruct,
  85. penalize_nl: penalize_nl,
  86. perplexity: perplexity,
  87. use_mmap: use_mmap,
  88. use_mlock: use_mlock,
  89. mem_test: mem_test,
  90. verbose_prompt: verbose_prompt),
  91. model_name, echo_input, verbose, encoding)
  92. {
  93. }
  94. public unsafe LLamaModel(LLamaParams @params, string name = "", bool echo_input = false, bool verbose = false, string encoding = "UTF-8")
  95. {
  96. Name = name;
  97. _params = @params;
  98. _ctx = Utils.llama_init_from_gpt_params(ref _params);
  99. // Add a space in front of the first character to match OG llama tokenizer behavior
  100. _session_tokens = new List<llama_token>();
  101. _path_session = @params.path_session;
  102. if (!string.IsNullOrEmpty(_path_session))
  103. {
  104. if (verbose)
  105. {
  106. Logger.Default.Info($"Attempting to load saved session from '{_path_session}'");
  107. }
  108. if (!File.Exists(_path_session))
  109. {
  110. Logger.Default.Warn("Session file does not exist, will create.");
  111. }
  112. llama_token[] session_tokens = new llama_token[@params.n_ctx];
  113. ulong n_token_count_out = 0;
  114. if (!NativeApi.llama_load_session_file(_ctx, _path_session, session_tokens, (ulong)@params.n_ctx, &n_token_count_out))
  115. {
  116. throw new RuntimeError($"Failed to load session file {_path_session}");
  117. }
  118. _session_tokens = session_tokens.Take((int)n_token_count_out).ToList();
  119. if (verbose)
  120. {
  121. Logger.Default.Info($"Loaded a session with prompt size of {_session_tokens.Count} tokens");
  122. }
  123. }
  124. _n_ctx = NativeApi.llama_n_ctx(_ctx);
  125. WithPrompt(_params.prompt);
  126. // prefix & suffix for instruct mode
  127. _inp_pfx = Utils.llama_tokenize(_ctx, "\n\n### Instruction:\n\n", true, encoding);
  128. _inp_sfx = Utils.llama_tokenize(_ctx, "\n\n### Response:\n\n", false, encoding);
  129. // in instruct mode, we inject a prefix and a suffix to each input by the user
  130. if (_params.instruct)
  131. {
  132. _params.interactive_first = true;
  133. _params.antiprompt.Add("### Instruction:\n\n");
  134. }
  135. // enable interactive mode if reverse prompt or interactive start is specified
  136. if (_params.antiprompt.Count != 0 || _params.interactive_first)
  137. {
  138. _params.interactive = true;
  139. }
  140. // determine newline token
  141. _llama_token_newline = Utils.llama_tokenize(_ctx, "\n", false, encoding);
  142. if (_params.verbose_prompt)
  143. {
  144. Logger.Default.Info("\n");
  145. Logger.Default.Info($"prompt: '{_params.prompt}'");
  146. Logger.Default.Info($"number of tokens in prompt = {_embed_inp.Count}");
  147. for (int i = 0; i < _embed_inp.Count; i++)
  148. {
  149. Logger.Default.Info($"{_embed_inp[i]} -> '{NativeApi.llama_token_to_str(_ctx, _embed_inp[i])}'");
  150. }
  151. if (_params.n_keep > 0)
  152. {
  153. Logger.Default.Info($"static prompt based on n_keep: '");
  154. for (int i = 0; i < _params.n_keep; i++)
  155. {
  156. Logger.Default.Info($"{NativeApi.llama_token_to_str(_ctx, _embed_inp[i])}");
  157. }
  158. Logger.Default.Info("\n");
  159. }
  160. Logger.Default.Info("\n");
  161. }
  162. if (_params.interactive && verbose)
  163. {
  164. Logger.Default.Info("interactive mode on.");
  165. }
  166. if (verbose)
  167. {
  168. Logger.Default.Info($"sampling: repeat_last_n = {_params.repeat_last_n}, " +
  169. $"repeat_penalty = {_params.repeat_penalty}, presence_penalty = {_params.presence_penalty}, " +
  170. $"frequency_penalty = {_params.frequency_penalty}, top_k = {_params.top_k}, tfs_z = {_params.tfs_z}," +
  171. $" top_p = {_params.top_p}, typical_p = {_params.typical_p}, temp = {_params.temp}, mirostat = {_params.mirostat}," +
  172. $" mirostat_lr = {_params.mirostat_eta}, mirostat_ent = {_params.mirostat_tau}");
  173. Logger.Default.Info($"generate: n_ctx = {_n_ctx}, n_batch = {_params.n_batch}, n_predict = {_params.n_predict}, " +
  174. $"n_keep = {_params.n_keep}");
  175. Logger.Default.Info("\n");
  176. }
  177. _last_n_tokens = Enumerable.Repeat(0, _n_ctx).ToList();
  178. if (_params.interactive)
  179. {
  180. if (verbose)
  181. {
  182. Logger.Default.Info("== Running in interactive mode. ==");
  183. }
  184. _is_interacting = _params.interactive_first;
  185. }
  186. _is_antiprompt = false;
  187. _input_echo = echo_input;
  188. _n_past = 0;
  189. _n_remain = _params.n_predict;
  190. _n_consumed = 0;
  191. _n_session_consumed = 0;
  192. _embed = new List<llama_token>();
  193. }
  194. public LLamaModel WithPrompt(string prompt, string encoding = "UTF-8")
  195. {
  196. _params.prompt = prompt.Insert(0, " ");
  197. _embed_inp = Utils.llama_tokenize(_ctx, _params.prompt, true, encoding);
  198. if (_embed_inp.Count > _n_ctx - 4)
  199. {
  200. throw new ArgumentException($"prompt is too long ({_embed_inp.Count} tokens, max {_n_ctx - 4})");
  201. }
  202. ulong n_matching_session_tokens = 0;
  203. if (_session_tokens.Count > 0)
  204. {
  205. foreach (var id in _session_tokens)
  206. {
  207. if (n_matching_session_tokens >= (ulong)_embed_inp.Count || id != _embed_inp[(int)n_matching_session_tokens])
  208. {
  209. break;
  210. }
  211. n_matching_session_tokens++;
  212. }
  213. if (n_matching_session_tokens >= (ulong)_embed_inp.Count)
  214. {
  215. Logger.Default.Info("Session file has exact match for prompt!");
  216. }
  217. else if (n_matching_session_tokens < (ulong)(_embed_inp.Count / 2))
  218. {
  219. Logger.Default.Warn($"session file has low similarity to prompt ({n_matching_session_tokens} " +
  220. $"/ {_embed_inp.Count} tokens); will mostly be reevaluated.");
  221. }
  222. else
  223. {
  224. Logger.Default.Info($"Session file matches {n_matching_session_tokens} / {_embed_inp.Count} " +
  225. $"tokens of prompt.");
  226. }
  227. }
  228. // number of tokens to keep when resetting context
  229. if (_params.n_keep < 0 || _params.n_keep > (int)_embed_inp.Count || _params.instruct)
  230. {
  231. _params.n_keep = _embed_inp.Count;
  232. }
  233. if (_embed_inp.Count > _n_ctx - 4)
  234. {
  235. throw new ArgumentException($"prompt is too long ({_embed_inp.Count} tokens, max {_n_ctx - 4})");
  236. }
  237. _need_to_save_session = !string.IsNullOrEmpty(_path_session) && n_matching_session_tokens < (ulong)(_embed_inp.Count * 3 / 4);
  238. return this;
  239. }
  240. public LLamaModel WithPromptFile(string promptFileName)
  241. {
  242. return WithPrompt(File.ReadAllText(promptFileName));
  243. }
  244. private string ProcessTextBeforeInfer(string text, string encoding)
  245. {
  246. if (!string.IsNullOrEmpty(_params.input_prefix))
  247. {
  248. text = _params.input_prefix + text;
  249. }
  250. if (!text.EndsWith("\n"))
  251. {
  252. text += "\n";
  253. }
  254. if (text.Length > 1)
  255. {
  256. // append input suffix if any
  257. if (!string.IsNullOrEmpty(_params.input_suffix))
  258. {
  259. text += _params.input_suffix;
  260. Console.Write(_params.input_suffix);
  261. }
  262. // instruct mode: insert instruction prefix
  263. if (_params.instruct && !_is_antiprompt)
  264. {
  265. _n_consumed = _embed_inp.Count;
  266. _embed_inp.AddRange(_inp_pfx);
  267. }
  268. var line_inp = Utils.llama_tokenize(_ctx, text, false, encoding);
  269. _embed_inp.AddRange(line_inp);
  270. // instruct mode: insert response suffix
  271. if (_params.instruct)
  272. {
  273. _embed_inp.AddRange(_inp_sfx);
  274. }
  275. _n_remain -= line_inp.Count;
  276. }
  277. return text;
  278. }
  279. public void InitChatPrompt(string prompt, string encoding = "UTF-8")
  280. {
  281. WithPrompt(prompt);
  282. }
  283. public void InitChatAntiprompt(string[] antiprompt)
  284. {
  285. _params.antiprompt = antiprompt.ToList();
  286. }
  287. public IEnumerable<string> Chat(string text, string? prompt = null, string encoding = "UTF-8")
  288. {
  289. _params.interactive = true;
  290. _input_echo = false;
  291. if (!string.IsNullOrEmpty(prompt))
  292. {
  293. WithPrompt(prompt);
  294. }
  295. return Call(text, encoding);
  296. }
  297. public void SaveState(string filename)
  298. {
  299. var stateSize = NativeApi.llama_get_state_size(_ctx);
  300. byte[] stateMemory = new byte[stateSize];
  301. int nbytes = (int)NativeApi.llama_copy_state_data(_ctx, stateMemory);
  302. File.WriteAllBytes(filename, stateMemory.Take(nbytes).ToArray());
  303. }
  304. public void LoadState(string filename, bool clearPreviousEmbed = true)
  305. {
  306. var stateMemory = File.ReadAllBytes(filename);
  307. if(stateMemory.Length != (int)NativeApi.llama_get_state_size(_ctx))
  308. {
  309. throw new RuntimeError("Failed to validate state size.");
  310. }
  311. NativeApi.llama_set_state_data(_ctx, stateMemory);
  312. if (clearPreviousEmbed)
  313. {
  314. WithPrompt(_params.prompt);
  315. }
  316. }
  317. public IEnumerable<string> Call(string text, string encoding = "UTF-8")
  318. {
  319. _is_antiprompt = false;
  320. if(_n_past > 0)
  321. {
  322. _is_interacting = false;
  323. }
  324. ProcessTextBeforeInfer(text, encoding);
  325. while ((_n_remain != 0 || _params.interactive) && !_is_interacting)
  326. {
  327. if (_embed.Count > 0)
  328. {
  329. // infinite text generation via context swapping
  330. // if we run out of context:
  331. // - take the n_keep first tokens from the original prompt (via n_past)
  332. // - take half of the last (n_ctx - n_keep) tokens and recompute the logits in batches
  333. if (_n_past + _embed.Count > _n_ctx)
  334. {
  335. int n_left = _n_past - _params.n_keep;
  336. _n_past = Math.Max(1, _params.n_keep);
  337. // insert n_left/2 tokens at the start of embed from last_n_tokens
  338. _embed.InsertRange(0, _last_n_tokens.Take(_last_n_tokens.Count - _embed.Count).Skip(_n_ctx - n_left / 2 - _embed.Count));
  339. // stop saving session if we run out of context
  340. _path_session = "";
  341. // Console.WriteLine("\n---\n");
  342. // Console.Write("resetting: '");
  343. // for (int i = 0; i < embed.Count; i++) {
  344. // Console.Write(llama_token_to_str(ctx, embed[i]));
  345. // }
  346. // Console.WriteLine("'\n");
  347. // Console.WriteLine("\n---\n");
  348. }
  349. // try to reuse a matching prefix from the loaded session instead of re-eval (via n_past)
  350. // REVIEW
  351. if (_n_session_consumed < _session_tokens.Count)
  352. {
  353. int i = 0;
  354. for (; i < _embed.Count; i++)
  355. {
  356. if (_embed[i] != _session_tokens[_n_session_consumed])
  357. {
  358. _session_tokens = _session_tokens.Take(_n_session_consumed).ToList();
  359. break;
  360. }
  361. _n_past++;
  362. _n_session_consumed++;
  363. if (_n_session_consumed >= _session_tokens.Count)
  364. {
  365. i++;
  366. break;
  367. }
  368. }
  369. if (i > 0)
  370. {
  371. _embed.RemoveRange(0, i);
  372. }
  373. }
  374. // evaluate tokens in batches
  375. // embed is typically prepared beforehand to fit within a batch, but not always
  376. for (int i = 0; i < _embed.Count; i += _params.n_batch)
  377. {
  378. int n_eval = _embed.Count - i;
  379. if (n_eval > _params.n_batch)
  380. {
  381. n_eval = _params.n_batch;
  382. }
  383. var array = _embed.Skip(i).ToArray();
  384. if (NativeApi.llama_eval(_ctx, array, n_eval, _n_past, _params.n_threads) != 0)
  385. {
  386. Logger.Default.Error($"Failed to eval.");
  387. throw new RuntimeError("Failed to eval.");
  388. }
  389. _n_past += n_eval;
  390. }
  391. if (_embed.Count > 0 && !string.IsNullOrEmpty(_path_session))
  392. {
  393. _session_tokens.AddRange(_embed);
  394. _n_session_consumed = _session_tokens.Count;
  395. }
  396. }
  397. _embed.Clear();
  398. if (_embed_inp.Count <= _n_consumed && !_is_interacting)
  399. {
  400. var temp = _params.temp;
  401. var top_k = _params.top_k <= 0 ? NativeApi.llama_n_vocab(_ctx) : _params.top_k;
  402. var top_p = _params.top_p;
  403. var tfs_z = _params.tfs_z;
  404. var typical_p = _params.typical_p;
  405. var repeat_last_n = _params.repeat_last_n < 0 ? _n_ctx : _params.repeat_last_n;
  406. var repeat_penalty = _params.repeat_penalty;
  407. var alpha_presence = _params.presence_penalty;
  408. var alpha_frequency = _params.frequency_penalty;
  409. var mirostat = _params.mirostat;
  410. var mirostat_tau = _params.mirostat_tau;
  411. var mirostat_eta = _params.mirostat_eta;
  412. var penalize_nl = _params.penalize_nl;
  413. // optionally save the session on first sample (for faster prompt loading next time)
  414. if (!string.IsNullOrEmpty(_path_session) && _need_to_save_session)
  415. {
  416. _need_to_save_session = false;
  417. NativeApi.llama_save_session_file(_ctx, _path_session, _session_tokens.ToArray(), (ulong)_session_tokens.Count);
  418. }
  419. llama_token id = 0;
  420. {
  421. var n_vocab = NativeApi.llama_n_vocab(_ctx);
  422. var logits = Utils.llama_get_logits(_ctx, n_vocab);
  423. // Apply params.logit_bias map
  424. foreach (var (key, value) in _params.logit_bias)
  425. {
  426. logits[key] += value;
  427. }
  428. var candidates = new List<LLamaTokenData>();
  429. candidates.Capacity = n_vocab;
  430. for (llama_token token_id = 0; token_id < n_vocab; token_id++)
  431. {
  432. candidates.Add(new LLamaTokenData(token_id, logits[token_id], 0.0f));
  433. }
  434. LLamaTokenDataArray candidates_p = new LLamaTokenDataArray(candidates.ToArray(), (ulong)candidates.Count, false);
  435. // Apply penalties
  436. float nl_logit = logits[NativeApi.llama_token_nl()];
  437. var last_n_repeat = Math.Min(Math.Min(_last_n_tokens.Count, repeat_last_n), _n_ctx);
  438. SamplingApi.llama_sample_repetition_penalty(_ctx, candidates_p,
  439. _last_n_tokens.Skip(_last_n_tokens.Count - last_n_repeat).ToArray(),
  440. (ulong)last_n_repeat, repeat_penalty);
  441. SamplingApi.llama_sample_frequency_and_presence_penalties(_ctx, candidates_p,
  442. _last_n_tokens.Skip(_last_n_tokens.Count - last_n_repeat).ToArray(),
  443. (ulong)last_n_repeat, alpha_frequency, alpha_presence);
  444. if (!penalize_nl)
  445. {
  446. logits[NativeApi.llama_token_nl()] = nl_logit;
  447. }
  448. if (temp <= 0)
  449. {
  450. // Greedy sampling
  451. id = SamplingApi.llama_sample_token_greedy(_ctx, candidates_p);
  452. }
  453. else
  454. {
  455. if (mirostat == 1)
  456. {
  457. float mirostat_mu = 2.0f * mirostat_tau;
  458. const int mirostat_m = 100;
  459. SamplingApi.llama_sample_temperature(_ctx, candidates_p, temp);
  460. id = SamplingApi.llama_sample_token_mirostat(_ctx, candidates_p, mirostat_tau, mirostat_eta, mirostat_m, ref mirostat_mu);
  461. }
  462. else if (mirostat == 2)
  463. {
  464. float mirostat_mu = 2.0f * mirostat_tau;
  465. SamplingApi.llama_sample_temperature(_ctx, candidates_p, temp);
  466. id = SamplingApi.llama_sample_token_mirostat_v2(_ctx, candidates_p, mirostat_tau, mirostat_eta, ref mirostat_mu);
  467. }
  468. else
  469. {
  470. // Temperature sampling
  471. SamplingApi.llama_sample_top_k(_ctx, candidates_p, top_k, 1);
  472. SamplingApi.llama_sample_tail_free(_ctx, candidates_p, tfs_z, 1);
  473. SamplingApi.llama_sample_typical(_ctx, candidates_p, typical_p, 1);
  474. SamplingApi.llama_sample_top_p(_ctx, candidates_p, top_p, 1);
  475. SamplingApi.llama_sample_temperature(_ctx, candidates_p, temp);
  476. id = SamplingApi.llama_sample_token(_ctx, candidates_p);
  477. }
  478. }
  479. _last_n_tokens.RemoveAt(0);
  480. _last_n_tokens.Add(id);
  481. }
  482. // replace end of text token with newline token when in interactive mode
  483. if (id == NativeApi.llama_token_eos() && _params.interactive && !_params.instruct)
  484. {
  485. id = _llama_token_newline[0];
  486. if (_params.antiprompt.Count != 0)
  487. {
  488. // tokenize and inject first reverse prompt
  489. var first_antiprompt = Utils.llama_tokenize(_ctx, _params.antiprompt[0], false, encoding);
  490. _embed_inp.AddRange(first_antiprompt);
  491. }
  492. }
  493. // add it to the context
  494. _embed.Add(id);
  495. // echo this to console
  496. _input_echo = true;
  497. // decrement remaining sampling budget
  498. _n_remain--;
  499. }
  500. else
  501. {
  502. while (_embed_inp.Count > _n_consumed)
  503. {
  504. _embed.Add(_embed_inp[_n_consumed]);
  505. _last_n_tokens.RemoveAt(0);
  506. _last_n_tokens.Add(_embed_inp[_n_consumed]);
  507. _n_consumed++;
  508. if (_embed.Count >= _params.n_batch)
  509. {
  510. break;
  511. }
  512. }
  513. }
  514. if (_input_echo && !_is_interacting)
  515. {
  516. foreach (var id in _embed)
  517. {
  518. var res = Utils.PtrToStringUTF8(NativeApi.llama_token_to_str(_ctx, id));
  519. yield return res;
  520. }
  521. }
  522. if (_params.interactive && _embed_inp.Count <= _n_consumed)
  523. {
  524. if (_params.antiprompt.Count > 0)
  525. {
  526. string last_output = "";
  527. foreach (var id in _last_n_tokens)
  528. {
  529. last_output += Utils.PtrToStringUTF8(NativeApi.llama_token_to_str(_ctx, id));
  530. }
  531. _is_antiprompt = false;
  532. foreach (var antiprompt in _params.antiprompt)
  533. {
  534. if (last_output.EndsWith(antiprompt))
  535. {
  536. _is_interacting = true;
  537. _is_antiprompt = true;
  538. break;
  539. }
  540. }
  541. }
  542. if(_n_past > 0 && _is_interacting)
  543. {
  544. if (_params.instruct)
  545. {
  546. yield return "\n> ";
  547. }
  548. _input_echo = false;
  549. break;
  550. }
  551. if (_embed.Count > 0 && _embed.Last() == NativeApi.llama_token_eos())
  552. {
  553. if (_params.instruct) {
  554. _is_interacting = true;
  555. } else
  556. {
  557. Logger.Default.Info(" [end of text]");
  558. }
  559. }
  560. if (_params.interactive && _n_remain <= 0 && _params.n_predict != -1) {
  561. _n_remain = _params.n_predict;
  562. _is_interacting = true;
  563. }
  564. }
  565. }
  566. if(!string.IsNullOrEmpty(_path_session) && _params.prompt_cache_all)
  567. {
  568. Logger.Default.Info($"saving final output to session file {_path_session}");
  569. var session_token_array = _session_tokens.ToArray();
  570. NativeApi.llama_save_session_file(_ctx, _path_session, session_token_array, (ulong)session_token_array.Length);
  571. }
  572. }
  573. public void Dispose()
  574. {
  575. _ctx.Dispose();
  576. }
  577. }
  578. }

C#/.NET上易用的LLM高性能推理框架,支持LLaMA和LLaVA系列模型。