Guest User

rough draft

a guest
Aug 6th, 2025
16
0
122 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 16.27 KB | None | 0 0
  1. diff --git a/tools/server/server.cpp b/tools/server/server.cpp
  2. index b23e35d3..8a240ea8 100644
  3. --- a/tools/server/server.cpp
  4. +++ b/tools/server/server.cpp
  5. @@ -4228,57 +4228,20 @@ int main(int argc, char ** argv) {
  6.              // TODO: this log can become very long, put it behind a flag or think about a more compact format
  7.              //SRV_DBG("Prompt: %s\n", prompt.is_string() ? prompt.get<std::string>().c_str() : prompt.dump(2).c_str());
  8.  
  9. -            // process files
  10. -            mtmd::bitmaps bitmaps;
  11. -            const bool has_mtmd = ctx_server.mctx != nullptr;
  12. -            {
  13. -                if (!has_mtmd && !files.empty()) {
  14. -                    throw std::runtime_error("This server does not support multimodal");
  15. -                }
  16. -                for (auto & file : files) {
  17. -                    mtmd::bitmap bmp(mtmd_helper_bitmap_init_from_buf(ctx_server.mctx, file.data(), file.size()));
  18. -                    if (!bmp.ptr) {
  19. -                        throw std::runtime_error("Failed to load image or audio file");
  20. -                    }
  21. -                    // calculate bitmap hash (for KV caching)
  22. -                    std::string hash = fnv_hash(bmp.data(), bmp.n_bytes());
  23. -                    bmp.set_id(hash.c_str());
  24. -                    bitmaps.entries.push_back(std::move(bmp));
  25. -                }
  26. -            }
  27. -
  28.              // process prompt
  29.              std::vector<server_tokens> inputs;
  30.  
  31. -            if (has_mtmd) {
  32. -                // multimodal
  33. -                std::string prompt_str = prompt.get<std::string>();
  34. -                mtmd_input_text inp_txt = {
  35. -                    prompt_str.c_str(),
  36. -                    /* add_special */   true,
  37. -                    /* parse_special */ true,
  38. -                };
  39. -                mtmd::input_chunks chunks(mtmd_input_chunks_init());
  40. -                auto bitmaps_c_ptr = bitmaps.c_ptr();
  41. -                int32_t tokenized = mtmd_tokenize(ctx_server.mctx,
  42. -                                                    chunks.ptr.get(),
  43. -                                                    &inp_txt,
  44. -                                                    bitmaps_c_ptr.data(),
  45. -                                                    bitmaps_c_ptr.size());
  46. -                if (tokenized != 0) {
  47. -                    throw std::runtime_error("Failed to tokenize prompt");
  48. -                }
  49. -
  50. -                server_tokens tmp(chunks, true);
  51. -                inputs.push_back(std::move(tmp));
  52. -            } else {
  53. -                // non-multimodal version
  54. -                auto tokenized_prompts = tokenize_input_prompts(ctx_server.vocab, prompt, true, true);
  55. -                for (auto & p : tokenized_prompts) {
  56. -                    auto tmp = server_tokens(p, ctx_server.mctx != nullptr);
  57. -                    inputs.push_back(std::move(tmp));
  58. -                }
  59. -            }
  60. +           const bool has_mtmd = ctx_server.mctx != nullptr;
  61. +            if (has_mtmd && oaicompat) {
  62. +                   // This is the case used by OAI compatible chat path with MTMD.
  63. +                   inputs.push_back(std::move(process_mtmd_prompt(ctx_server.mctx, prompt.get<std::string>(), files)));
  64. +           } else {
  65. +                   // Everything else, including multimodal completions.
  66. +                   auto prompts = tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, prompt, true, true);
  67. +                   for (auto& prompt : prompts) {
  68. +                           inputs.push_back(std::move(prompt));
  69. +                   }
  70. +           }
  71.  
  72.              tasks.reserve(inputs.size());
  73.              for (size_t i = 0; i < inputs.size(); i++) {
  74. @@ -4451,7 +4414,7 @@ int main(int argc, char ** argv) {
  75.          data["input_extra"] = input_extra; // default to empty array if it's not exist
  76.  
  77.          std::string prompt = json_value(data, "prompt", std::string());
  78. -        std::vector<llama_tokens> tokenized_prompts = tokenize_input_prompts(ctx_server.vocab, prompt, false, true);
  79. +        std::vector<server_tokens> tokenized_prompts = tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, prompt, false, true);
  80.          SRV_DBG("creating infill tasks, n_prompts = %d\n", (int) tokenized_prompts.size());
  81.          data["prompt"] = format_infill(
  82.              ctx_server.vocab,
  83. @@ -4462,7 +4425,7 @@ int main(int argc, char ** argv) {
  84.              ctx_server.params_base.n_predict,
  85.              ctx_server.slots[0].n_ctx, // TODO: there should be a better way
  86.              ctx_server.params_base.spm_infill,
  87. -            tokenized_prompts[0]
  88. +            tokenized_prompts[0].get_text_tokens()
  89.          );
  90.  
  91.          std::vector<raw_buffer> files; // dummy
  92. @@ -4640,7 +4603,7 @@ int main(int argc, char ** argv) {
  93.              }
  94.          }
  95.  
  96. -        auto tokenized_prompts = tokenize_input_prompts(ctx_server.vocab, prompt, true, true);
  97. +        auto tokenized_prompts = tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, prompt, true, true);
  98.          for (const auto & tokens : tokenized_prompts) {
  99.              // this check is necessary for models that do not add BOS token to the input
  100.              if (tokens.empty()) {
  101. @@ -4668,7 +4631,7 @@ int main(int argc, char ** argv) {
  102.  
  103.                  task.id            = ctx_server.queue_tasks.get_new_id();
  104.                  task.index         = i;
  105. -                task.prompt_tokens = server_tokens(tokenized_prompts[i], ctx_server.mctx != nullptr);
  106. +                task.prompt_tokens = std::move(tokenized_prompts[i]);
  107.  
  108.                  // OAI-compat
  109.                  task.params.oaicompat = oaicompat;
  110. @@ -4755,7 +4718,7 @@ int main(int argc, char ** argv) {
  111.              return;
  112.          }
  113.  
  114. -        llama_tokens tokenized_query = tokenize_input_prompts(ctx_server.vocab, query, /* add_special */ false, true)[0];
  115. +        server_tokens tokenized_query = std::move(tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, query, /* add_special */ false, true)[0]);
  116.  
  117.          // create and queue the task
  118.          json responses = json::array();
  119. @@ -4763,14 +4726,14 @@ int main(int argc, char ** argv) {
  120.          std::unordered_set<int> task_ids;
  121.          {
  122.              std::vector<server_task> tasks;
  123. -            auto tokenized_docs = tokenize_input_prompts(ctx_server.vocab, documents, /* add_special */ false, true);
  124. +            auto tokenized_docs = tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, documents, /* add_special */ false, true);
  125.              tasks.reserve(tokenized_docs.size());
  126.              for (size_t i = 0; i < tokenized_docs.size(); i++) {
  127.                  auto tmp = format_rerank(ctx_server.vocab, tokenized_query, tokenized_docs[i]);
  128.                  server_task task   = server_task(SERVER_TASK_TYPE_RERANK);
  129.                  task.id            = ctx_server.queue_tasks.get_new_id();
  130.                  task.index         = i;
  131. -                task.prompt_tokens = server_tokens(tmp, ctx_server.mctx != nullptr);
  132. +                task.prompt_tokens = std::move(tmp);
  133.                  tasks.push_back(std::move(task));
  134.              }
  135.  
  136. diff --git a/tools/server/utils.hpp b/tools/server/utils.hpp
  137. index f3dfc822..30a41038 100644
  138. --- a/tools/server/utils.hpp
  139. +++ b/tools/server/utils.hpp
  140. @@ -186,48 +186,6 @@ static llama_tokens tokenize_mixed(const llama_vocab * vocab, const json & json_
  141.      return prompt_tokens;
  142.  }
  143.  
  144. -/**
  145. - * break the input "prompt" object into multiple prompt if needed, then tokenize them
  146. - * this supports these cases:
  147. - * - "prompt": "string"
  148. - * - "prompt": [12, 34, 56]
  149. - * - "prompt": [12, 34, "string", 56, 78]
  150. - * and multiple prompts (multi-tasks):
  151. - * - "prompt": ["string1", "string2"]
  152. - * - "prompt": ["string1", [12, 34, 56]]
  153. - * - "prompt": [[12, 34, 56], [78, 90, 12]]
  154. - * - "prompt": [[12, 34, "string", 56, 78], [12, 34, 56]]
  155. - */
  156. -static std::vector<llama_tokens> tokenize_input_prompts(const llama_vocab * vocab, const json & json_prompt, bool add_special, bool parse_special) {
  157. -    std::vector<llama_tokens> result;
  158. -    if (json_prompt.is_string() || json_is_array_of_mixed_numbers_strings(json_prompt)) {
  159. -        // string or mixed
  160. -        result.push_back(tokenize_mixed(vocab, json_prompt, add_special, parse_special));
  161. -    } else if (json_is_array_of_numbers(json_prompt)) {
  162. -        // array of tokens
  163. -        result.push_back(json_prompt.get<llama_tokens>());
  164. -    } else if (json_prompt.is_array()) {
  165. -        // array of prompts
  166. -        result.reserve(json_prompt.size());
  167. -        for (const auto & p : json_prompt) {
  168. -            if (p.is_string() || json_is_array_of_mixed_numbers_strings(p)) {
  169. -                result.push_back(tokenize_mixed(vocab, p, add_special, parse_special));
  170. -            } else if (json_is_array_of_numbers(p)) {
  171. -                // array of tokens
  172. -                result.push_back(p.get<llama_tokens>());
  173. -            } else {
  174. -                throw std::runtime_error("element of \"prompt\" must be a string, an list of tokens, or a list of mixed strings & tokens");
  175. -            }
  176. -        }
  177. -    } else {
  178. -        throw std::runtime_error("\"prompt\" must be a string, an list of tokens, a list of mixed strings & tokens, or a list of prompts");
  179. -    }
  180. -    if (result.empty()) {
  181. -        throw std::runtime_error("\"prompt\" must not be empty");
  182. -    }
  183. -    return result;
  184. -}
  185. -
  186.  // return the last index of character that can form a valid string
  187.  // if the last character is potentially cut in half, return the index before the cut
  188.  // if validate_utf8(text) == text.size(), then the whole text is valid utf8
  189. @@ -262,35 +220,6 @@ static size_t validate_utf8(const std::string& text) {
  190.  // template utils
  191.  //
  192.  
  193. -// format rerank task: [BOS]query[EOS][SEP]doc[EOS]
  194. -static llama_tokens format_rerank(const struct llama_vocab * vocab, const llama_tokens & query, const llama_tokens & doc) {
  195. -    llama_tokens result;
  196. -
  197. -    // Get EOS token - use SEP token as fallback if EOS is not available
  198. -    llama_token eos_token = llama_vocab_eos(vocab);
  199. -    if (eos_token == LLAMA_TOKEN_NULL) {
  200. -        eos_token = llama_vocab_sep(vocab);
  201. -    }
  202. -
  203. -    result.reserve(doc.size() + query.size() + 4);
  204. -    if (llama_vocab_get_add_bos(vocab)) {
  205. -        result.push_back(llama_vocab_bos(vocab));
  206. -    }
  207. -    result.insert(result.end(), query.begin(), query.end());
  208. -    if (llama_vocab_get_add_eos(vocab)) {
  209. -        result.push_back(eos_token);
  210. -    }
  211. -    if (llama_vocab_get_add_sep(vocab)) {
  212. -        result.push_back(llama_vocab_sep(vocab));
  213. -    }
  214. -    result.insert(result.end(), doc.begin(), doc.end());
  215. -    if (llama_vocab_get_add_eos(vocab)) {
  216. -        result.push_back(eos_token);
  217. -    }
  218. -
  219. -    return result;
  220. -}
  221. -
  222.  // format infill task
  223.  static llama_tokens format_infill(
  224.          const llama_vocab * vocab,
  225. @@ -1186,6 +1115,17 @@ public:
  226.          }
  227.      }
  228.  
  229. +    // appends server tokens, updates the media map. destroys server tokens.
  230. +    void push_back(const server_tokens & tokens) {
  231. +           size_t start_size = tokens.size();
  232. +           for (size_t i = 0; i < start_size; i++) {
  233. +                   push_back(tokens[i]);
  234. +           }
  235. +           //for (auto it = tokens.map_pos_to_media.begin(); it != tokens.map_pos_to_media.end(); ) {
  236. +           //      map_pos_to_media[start_size+it->first]=std::move(it->second);
  237. +           //}
  238. +    }
  239. +
  240.      // for compatibility with context shift and prompt truncation
  241.      void insert(const llama_tokens & inp_tokens) {
  242.          GGML_ASSERT(!has_mtmd); // only allow this if mtmd is disabled
  243. @@ -1356,3 +1296,129 @@ static std::string fnv_hash(const uint8_t * data, size_t len) {
  244.      }
  245.      return std::to_string(hash);
  246.  }
  247. +
  248. +
  249. +// format rerank task: [BOS]query[EOS][SEP]doc[EOS]
  250. +static server_tokens format_rerank(const struct llama_vocab * vocab, const server_tokens & query, const server_tokens & doc) {
  251. +       server_tokens result = {};
  252. +
  253. +       // Get EOS token - use SEP token as fallback if EOS is not available
  254. +       llama_token eos_token = llama_vocab_eos(vocab);
  255. +       if (eos_token == LLAMA_TOKEN_NULL) {
  256. +               eos_token = llama_vocab_sep(vocab);
  257. +       }
  258. +       if (llama_vocab_get_add_bos(vocab)) {
  259. +               result.push_back(llama_vocab_bos(vocab));
  260. +       }
  261. +       result.push_back(query);
  262. +       if (llama_vocab_get_add_eos(vocab)) {
  263. +               result.push_back(eos_token);
  264. +       }
  265. +       if (llama_vocab_get_add_sep(vocab)) {
  266. +               result.push_back(llama_vocab_sep(vocab));
  267. +       }
  268. +       result.push_back(doc);
  269. +       if (llama_vocab_get_add_eos(vocab)) {
  270. +               result.push_back(eos_token);
  271. +       }
  272. +       return result;
  273. +}
  274. +
  275. +
  276. +static server_tokens process_mtmd_prompt(mtmd_context * mctx, std::string prompt, std::vector<raw_buffer> files) {
  277. +       mtmd::bitmaps bitmaps;
  278. +       for (auto & file : files) {
  279. +               mtmd::bitmap bmp(mtmd_helper_bitmap_init_from_buf(mctx, file.data(), file.size()));
  280. +               if (!bmp.ptr) {
  281. +                       throw std::runtime_error("Failed to load image or audio file");
  282. +               }
  283. +               // calculate bitmap hash (for KV caching)
  284. +               std::string hash = fnv_hash(bmp.data(), bmp.n_bytes());
  285. +               bmp.set_id(hash.c_str());
  286. +               bitmaps.entries.push_back(std::move(bmp));
  287. +       }
  288. +       // process prompt
  289. +       std::vector<server_tokens> inputs;
  290. +       // multimodal
  291. +       mtmd_input_text inp_txt = {
  292. +               prompt.c_str(),
  293. +               /* add_special */   true,
  294. +               /* parse_special */ true,
  295. +       };
  296. +       mtmd::input_chunks chunks(mtmd_input_chunks_init());
  297. +       auto bitmaps_c_ptr = bitmaps.c_ptr();
  298. +       int32_t tokenized = mtmd_tokenize(mctx,
  299. +                                         chunks.ptr.get(),
  300. +                                         &inp_txt,
  301. +                                         bitmaps_c_ptr.data(),
  302. +                                         bitmaps_c_ptr.size());
  303. +       if (tokenized != 0) {
  304. +               throw std::runtime_error("Failed to tokenize prompt");
  305. +       }
  306. +       return server_tokens(chunks,true);
  307. +}
  308. +
  309. +/**
  310. + * break the input "prompt" object into multiple prompt if needed, then tokenize them
  311. + * this supports these cases:
  312. + * - "prompt": "string"
  313. + * - "prompt": [12, 34, 56]
  314. + * - "prompt": [12, 34, "string", 56, 78]
  315. + * and multiple prompts (multi-tasks):
  316. + * - "prompt": ["string1", "string2"]
  317. + * - "prompt": ["string1", [12, 34, 56]]
  318. + * - "prompt": [[12, 34, 56], [78, 90, 12]]
  319. + * - "prompt": [[12, 34, "string", 56, 78], [12, 34, 56]]
  320. + */
  321. +static std::vector<server_tokens> tokenize_input_prompts(const llama_vocab* vocab, mtmd_context * mctx, const json & json_prompt, bool add_special, bool parse_special) {
  322. +       std::vector<server_tokens> result;
  323. +       bool has_mtmd = mctx == nullptr;
  324. +       if (json_prompt.is_string() || json_is_array_of_mixed_numbers_strings(json_prompt)) {
  325. +               // string or mixed
  326. +               llama_tokens toks = tokenize_mixed(vocab, json_prompt, add_special, parse_special);
  327. +               auto tmp = server_tokens(toks, false);
  328. +               result.push_back(std::move(tmp));
  329. +       } else if (json_is_array_of_numbers(json_prompt)) {
  330. +               // array of tokens
  331. +               llama_tokens toks = json_prompt.get<llama_tokens>();
  332. +               auto tmp = server_tokens(toks, false);
  333. +               result.push_back(std::move(tmp));
  334. +       } else if (json_prompt.find("prompt") != json_prompt.end()) {
  335. +               if (has_mtmd && json_prompt.find("multimodal_data") != json_prompt.end()) {
  336. +                       std::vector<raw_buffer> files;
  337. +                       for (const auto& entry : json_prompt.at("multimodal_data")) {
  338. +                               files.push_back(base64_decode(entry));
  339. +                       }
  340. +                       result.push_back(process_mtmd_prompt(mctx, json_prompt.at("prompt"), files));
  341. +               } else {
  342. +                       // Not multimodal, but contains a subobject.
  343. +                       llama_tokens toks = tokenize_mixed(vocab, json_prompt.at("prompt"), add_special, parse_special);
  344. +                       auto tmp = server_tokens(toks, false);
  345. +                       result.push_back(std::move(tmp));
  346. +               }
  347. +       } else if (json_prompt.is_array()) {
  348. +               // array of prompts
  349. +               result.reserve(json_prompt.size());
  350. +               for (const auto & p : json_prompt) {
  351. +                       if (p.is_string() || json_is_array_of_mixed_numbers_strings(p)) {
  352. +                               llama_tokens toks = tokenize_mixed(vocab, p, add_special, parse_special);
  353. +                               auto tmp = server_tokens(toks, false);
  354. +                               result.push_back(std::move(tmp));
  355. +                       } else if (json_is_array_of_numbers(p)) {
  356. +                               // array of tokens
  357. +                               llama_tokens toks = p.get<llama_tokens>();
  358. +                               auto tmp = server_tokens(toks,false);
  359. +                               result.push_back(std::move(tmp));
  360. +                       } else {
  361. +                               throw std::runtime_error("element of \"prompt\" must be a string, an list of tokens, or a list of mixed strings & tokens");
  362. +                       }
  363. +               }
  364. +       } else {
  365. +               throw std::runtime_error("\"prompt\" must be a string, an list of tokens, a list of mixed strings & tokens, or a list of prompts");
  366. +       }
  367. +       if (result.empty()) {
  368. +               throw std::runtime_error("\"prompt\" must not be empty");
  369. +       }
  370. +       return result;
  371. +}
  372. +
  373.  
Add Comment
Please, Sign In to add comment