CoolFace
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 2d agoView on Hugging Face
0likes1.1kdownloads
server-common.cpp1987 linesDownload Raw Back to server
1#include "common.h"2#include "download.h"3#include "log.h"4#include "llama.h"5#include "mtmd.h"6#include "mtmd-helper.h"7#include "chat.h"8#include "base64.hpp"9 10#include "server-common.h"11 12#include <random>13#include <sstream>14#include <fstream>15#include <limits>16#include <cstring>17#include <type_traits>18#include <chrono>19#include <thread>20 21#ifdef _WIN3222// windows.h defines min and max as macros, which breaks std::min and std::max23#define WIN32_LEAN_AND_MEAN24#ifndef NOMINMAX25#   define NOMINMAX26#endif27#include <windows.h>28#include <io.h>29#else30#include <errno.h>31#include <fcntl.h>32#include <poll.h>33#include <unistd.h>34#endif35 36json format_error_response(const std::string & message, const enum error_type type) {37    std::string type_str;38    int code = 500;39    switch (type) {40        case ERROR_TYPE_INVALID_REQUEST:41            type_str = "invalid_request_error";42            code = 400;43            break;44        case ERROR_TYPE_AUTHENTICATION:45            type_str = "authentication_error";46            code = 401;47            break;48        case ERROR_TYPE_NOT_FOUND:49            type_str = "not_found_error";50            code = 404;51            break;52        case ERROR_TYPE_SERVER:53            type_str = "server_error";54            code = 500;55            break;56        case ERROR_TYPE_PERMISSION:57            type_str = "permission_error";58            code = 403;59            break;60        case ERROR_TYPE_NOT_SUPPORTED:61            type_str = "not_supported_error";62            code = 501;63            break;64        case ERROR_TYPE_UNAVAILABLE:65            type_str = "unavailable_error";66            code = 503;67            break;68        case ERROR_TYPE_EXCEED_CONTEXT_SIZE:69            type_str = "exceed_context_size_error";70            code = 400;71            break;72    }73    return json {74        {"code", code},75        {"message", message},76        {"type", type_str},77    };78}79 80//81// server_slot_stats82//83 84json server_slot_stats::to_json() const {85    json base = {86        {"cache_n",                n_prompt_cached},87 88        {"prompt_n",               n_prompt_processed},89        {"prompt_ms",              t_prompt_ms()},90        {"prompt_per_token_ms",    t_prompt_per_token_ms()},91        {"prompt_per_second",      n_prompt_tps()},92 93        {"predicted_n",            n_gen},94        {"predicted_ms",           t_gen_ms()},95        {"predicted_per_token_ms", t_gen_per_token_ms()},96        {"predicted_per_second",   n_gen_tps()},97    };98 99    if (n_draft_tokens > 0) {100        base["draft_n"]          = n_draft_tokens;101        base["draft_n_accepted"] = n_draft_accepted;102    }103 104    return base;105}106 107//108// random string / id109//110 111std::string random_string() {112    static const std::string str("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");113 114    std::random_device rd;115    std::mt19937 generator(rd());116 117    std::string result(32, ' ');118 119    for (int i = 0; i < 32; ++i) {120        result[i] = str[generator() % str.size()];121    }122 123    return result;124}125 126std::string gen_chatcmplid() {127    return "chatcmpl-" + random_string();128}129 130std::string gen_tool_call_id() {131    return random_string();132}133 134const char * get_media_marker() {135    static const std::string marker = []() {136        // allow user to pin a reproducible marker via env var137        const char * env = getenv("LLAMA_MEDIA_MARKER");138        if (env && env[0] != '\0') {139            return std::string(env);140        }141        return std::string("<__media_") + random_string() + "__>";142    }();143    return marker.c_str();144}145 146//147// lora utils148//149 150bool lora_all_alora(const std::vector<common_adapter_lora_info> & loras) {151    bool found_alora = false;152    for (const auto & lora : loras) {153        if (lora.scale != 0) {154            if (llama_adapter_get_alora_n_invocation_tokens(lora.ptr) == 0) {155                return false;156            }157            found_alora = true;158        }159    }160    return found_alora;161}162 163bool lora_should_clear_cache(164        const std::vector<common_adapter_lora_info> & current,165        const std::vector<common_adapter_lora_info> & next) {166 167    // This should always be called after determining that the two sets are168    // _not_ equal. This assert is therefore some slightly wasted work and169    // should be safe to remove as long as this method is called correctly.170    GGML_ASSERT(!are_lora_equal(current, next));171 172    return (173        !(lora_get_enabled_ids(current).empty() || lora_all_alora(current)) ||174        !lora_all_alora(next));175}176 177std::map<int, float> parse_lora_request(const json & data) {178    std::map<int, float> lora;179 180    // set value181    for (const auto & entry : data) {182        int id      = json_value(entry, "id", -1);183        float scale = json_value(entry, "scale", 0.0f);184        lora[id] = scale;185    }186 187    return lora;188}189 190bool are_lora_equal(191        const std::vector<common_adapter_lora_info> & l1,192        const std::vector<common_adapter_lora_info> & l2) {193    if (l1.size() != l2.size()) {194        return false;195    }196    for (size_t i = 0; i < l1.size(); ++i) {197        // we don't check lora.path to reduce the time complexity198        if (l1[i].scale != l2[i].scale || l1[i].ptr != l2[i].ptr) {199            return false;200        }201    }202    return true;203}204 205std::vector<size_t> lora_get_enabled_ids(const std::vector<common_adapter_lora_info> & loras) {206    std::vector<size_t> enabled_ids;207    for (size_t i = 0; i < loras.size(); ++i) {208        if (loras[i].scale > 0) {209            enabled_ids.push_back(i);210        }211    }212    return enabled_ids;213}214 215//216// base64 utils (TODO: use the base64::decode from base64.hpp)217//218 219static const std::string base64_chars =220             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"221             "abcdefghijklmnopqrstuvwxyz"222             "0123456789+/";223 224static inline bool is_base64(uint8_t c) {225    return (isalnum(c) || (c == '+') || (c == '/'));226}227 228static inline raw_buffer base64_decode(const std::string & encoded_string) {229    int i = 0;230    int j = 0;231    int in_ = 0;232 233    int in_len = encoded_string.size();234 235    uint8_t char_array_4[4];236    uint8_t char_array_3[3];237 238    raw_buffer ret;239 240    while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {241        char_array_4[i++] = encoded_string[in_]; in_++;242        if (i == 4) {243            for (i = 0; i < 4; i++) {244                char_array_4[i] = base64_chars.find(char_array_4[i]);245            }246 247            char_array_3[0] = ((char_array_4[0]      ) << 2) + ((char_array_4[1] & 0x30) >> 4);248            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);249            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) +   char_array_4[3];250 251            for (i = 0; (i < 3); i++) {252                ret.push_back(char_array_3[i]);253            }254 255            i = 0;256        }257    }258 259    if (i) {260        for (j = i; j < 4; j++) {261            char_array_4[j] = 0;262        }263 264        for (j = 0; j < 4; j++) {265            char_array_4[j] = base64_chars.find(char_array_4[j]);266        }267 268        char_array_3[0] = ((char_array_4[0]      ) << 2) + ((char_array_4[1] & 0x30) >> 4);269        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);270        char_array_3[2] = ((char_array_4[2] & 0x3) << 6) +   char_array_4[3];271 272        for (j = 0; j < i - 1; j++) {273            ret.push_back(char_array_3[j]);274        }275    }276 277    return ret;278}279 280//281// server_tokens implementation282//283 284namespace {285 286constexpr uint32_t SERVER_TOKENS_STATE_VERSION = 1;287 288uint32_t server_tokens_state_u32(size_t value) {289    if (value > std::numeric_limits<uint32_t>::max()) {290        throw std::runtime_error("Server tokens state is too large");291    }292    return value;293}294 295class server_tokens_state_writer {296public:297    template <typename T>298    void write(T value) {299        static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");300        const auto * ptr = reinterpret_cast<const char *>(&value);301        data.insert(data.end(), ptr, ptr + sizeof(value));302    }303 304    template <typename T>305    void write(const std::vector<T> & values) {306        static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");307        write(server_tokens_state_u32(values.size()));308        if (values.empty()) {309            return;310        }311        const auto * ptr = reinterpret_cast<const char *>(values.data());312        data.insert(data.end(), ptr, ptr + values.size() * sizeof(T));313    }314 315    void write_media_chunk(const mtmd_input_chunk * chunk) {316        size_t chunk_size = 0;317        if (mtmd_input_chunk_save(chunk, nullptr, 0, &chunk_size) != 0 || chunk_size == 0) {318            throw std::runtime_error("Cannot serialize media chunk in server tokens");319        }320        std::vector<char> chunk_data(server_tokens_state_u32(chunk_size));321        if (mtmd_input_chunk_save(chunk, chunk_data.data(), chunk_data.size(), nullptr) != 0) {322            throw std::runtime_error("Cannot serialize media chunk in server tokens");323        }324        write(chunk_data);325    }326 327    std::vector<char> take() {328        data.resize((data.size() + sizeof(llama_token) - 1) / sizeof(llama_token) * sizeof(llama_token), 0);329        return std::move(data);330    }331 332private:333    std::vector<char> data;334};335 336class server_tokens_state_reader {337public:338    server_tokens_state_reader(const char * data, size_t size) : data(data), size(size) {}339 340    template <typename T>341    T read() {342        static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");343        if (size - pos < sizeof(T)) {344            throw std::runtime_error("Unexpected end of server tokens state");345        }346        T value;347        std::memcpy(&value, data + pos, sizeof(value));348        pos += sizeof(value);349        return value;350    }351 352    template <typename T>353    std::vector<T> read_vector() {354        static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");355        const uint32_t n_values = read<uint32_t>();356        // reject before resizing, so that a small corrupted payload cannot request a huge allocation357        if (n_values > remaining() / sizeof(T)) {358            throw std::runtime_error("Unexpected end of server tokens state");359        }360        std::vector<T> values(n_values);361        if (n_values > 0) {362            std::memcpy(values.data(), data + pos, values.size() * sizeof(T));363            pos += values.size() * sizeof(T);364        }365        return values;366    }367 368    size_t remaining() const {369        return size - pos;370    }371 372private:373    const char * data;374    size_t size;375    size_t pos = 0;376};377 378} // namespace379 380server_tokens::server_tokens(mtmd::input_chunks & mtmd_chunks, bool has_mtmd) : has_mtmd(has_mtmd) {381    for (size_t i = 0; i < mtmd_chunks.size(); ++i) {382        push_back(mtmd_chunks[i]);383    }384}385 386server_tokens::server_tokens(const llama_tokens & tokens, bool has_mtmd) : has_mtmd(has_mtmd), tokens(tokens) {387}388 389llama_pos server_tokens::pos_next(int64_t n_tokens) const {390    if (!has_mtmd) {391        if (n_tokens < 0) {392            return tokens.size();393        }394 395        return n_tokens;396    }397 398    if (n_tokens < 0) {399        llama_pos res = tokens.size();400 401        for (auto it = map_idx_to_media.begin(); it != map_idx_to_media.end(); ++it) {402            const auto & chunk = it->second;403            res += mtmd_input_chunk_get_n_pos(chunk.get()) - mtmd_input_chunk_get_n_tokens(chunk.get());404        }405 406        return res;407    }408 409    int64_t idx = 0;410    llama_pos pos = 0;411 412    GGML_ASSERT(n_tokens <= (int64_t)tokens.size());413 414    while (idx < n_tokens) {415        const auto media_it = map_idx_to_media.find(idx);416        if (media_it != map_idx_to_media.end()) {417            const auto & chunk = media_it->second;418            const llama_pos n_pos = mtmd_input_chunk_get_n_pos(chunk.get());419            const size_t n_tok = mtmd_input_chunk_get_n_tokens(chunk.get());420 421            pos += n_pos;422            idx += n_tok;423        } else {424            pos++;425            idx++;426        }427    }428 429    return pos;430}431 432size_t server_tokens::size_up_to_pos(llama_pos max_pos) const {433    if (!has_mtmd) {434        return std::min((size_t)max_pos, tokens.size());435    }436 437    size_t idx = 0;438    llama_pos pos = 0;439 440    while (idx < tokens.size()) {441        const auto media_it = map_idx_to_media.find(idx);442        if (media_it != map_idx_to_media.end()) {443            const auto & chunk = media_it->second;444            const llama_pos n_pos = mtmd_input_chunk_get_n_pos(chunk.get());445            const size_t n_tok = mtmd_input_chunk_get_n_tokens(chunk.get());446 447            pos += n_pos;448            idx += n_tok;449        } else {450            pos++;451            idx++;452        }453 454        if (pos >= max_pos) {455            break;456        }457    }458 459    return idx;460}461 462std::string server_tokens::str() const {463    std::ostringstream oss;464    oss << "tokens: ";465    for (size_t idx = 0; idx < tokens.size(); ++idx) {466        llama_token t = tokens[idx];467        oss << "idx:" << idx << " ";468        if (t == LLAMA_TOKEN_NULL) {469            oss << "<embd> ";470        } else {471            oss << t << " ";472        }473    }474    oss << "\n";475    oss << "image idx: ";476    for (const auto & it : map_idx_to_media) {477        oss << it.first << ", ";478    }479    return oss.str();480}481 482const mtmd::input_chunk_ptr & server_tokens::find_chunk(size_t idx) const {483    auto it = map_idx_to_media.find(idx);484    if (it != map_idx_to_media.end()) {485        return it->second;486    }487    throw std::runtime_error("Chunk not found");488}489 490std::pair<const mtmd::input_chunk_ptr *, size_t> server_tokens::find_next_media_chunk(size_t idx) const {491    auto it = map_idx_to_media.upper_bound(idx);492    if (it != map_idx_to_media.end()) {493        return { &it->second, it->first };494    }495    return { nullptr, 0 };496}497 498void server_tokens::push_back(llama_token tok) {499    if (tok == LLAMA_TOKEN_NULL) {500        throw std::runtime_error("Invalid token");501    }502    tokens.emplace_back(tok);503}504 505void server_tokens::push_back(const mtmd_input_chunk * chunk) {506    auto type = mtmd_input_chunk_get_type(chunk);507    if (type == MTMD_INPUT_CHUNK_TYPE_IMAGE || type == MTMD_INPUT_CHUNK_TYPE_AUDIO) {508        GGML_ASSERT(has_mtmd);509        const size_t n_tokens = mtmd_input_chunk_get_n_tokens(chunk);510        size_t start_idx = tokens.size();511        for (size_t i = 0; i < n_tokens; ++i) {512            tokens.emplace_back(LLAMA_TOKEN_NULL);513        }514        mtmd::input_chunk_ptr new_chunk(mtmd_input_chunk_copy(chunk));515        map_idx_to_media[start_idx] = std::move(new_chunk);516    } else if (type == MTMD_INPUT_CHUNK_TYPE_TEXT) {517        size_t n_tokens;518        const auto * text_tokens = mtmd_input_chunk_get_tokens_text(chunk, &n_tokens);519        for (size_t i = 0; i < n_tokens; ++i) {520            push_back(text_tokens[i]);521        }522    } else {523        GGML_ABORT("Invalid chunk type");524    }525}526 527void server_tokens::push_back_placeholder(const mtmd_input_chunk * chunk) {528    auto type = mtmd_input_chunk_get_type(chunk);529    if (type == MTMD_INPUT_CHUNK_TYPE_IMAGE || type == MTMD_INPUT_CHUNK_TYPE_AUDIO) {530        GGML_ASSERT(has_mtmd);531        mtmd::input_chunk_ptr new_chunk(mtmd_input_chunk_get_placeholder(chunk));532        GGML_ASSERT(new_chunk != nullptr && "failed to create placeholder chunk");533        const size_t n_tokens = mtmd_input_chunk_get_n_tokens(chunk);534        size_t start_idx = tokens.size();535        for (size_t i = 0; i < n_tokens; ++i) {536            tokens.emplace_back(LLAMA_TOKEN_NULL);537        }538        map_idx_to_media[start_idx] = std::move(new_chunk);539    } else {540        push_back(chunk);541    }542}543 544void server_tokens::push_back(server_tokens & tokens) {545    size_t start_idx = size();546    for (size_t i = 0; i < tokens.size(); i++) {547        push_back(tokens[i]);548    }549    if (tokens.has_mtmd) {550        // Assert if we are copying MTMD chunks to a server_tokens that does not have mtmd.551        // We could also just check, but this will prevent silently dropping MTMD data.552        GGML_ASSERT(has_mtmd);553        for (auto it = tokens.map_idx_to_media.begin(); it != tokens.map_idx_to_media.end(); ) {554            auto * chunk = tokens.map_idx_to_media[it->first].get();555            mtmd::input_chunk_ptr new_chunk(mtmd_input_chunk_copy(chunk));556            map_idx_to_media[start_idx + it->first] = std::move(new_chunk);557        }558    }559}560 561void server_tokens::insert(const llama_tokens & inp_tokens) {562    tokens.insert(tokens.end(), inp_tokens.begin(), inp_tokens.end());563}564 565const llama_tokens & server_tokens::get_tokens() const {566    GGML_ASSERT(!has_mtmd);567    return tokens;568}569 570std::vector<char> server_tokens::serialize() const {571    static_assert(sizeof(llama_token) == sizeof(uint32_t), "unexpected llama_token size");572 573    server_tokens_state_writer writer;574    writer.write((llama_token) LLAMA_TOKEN_NULL);575    writer.write(SERVER_TOKENS_STATE_VERSION);576    writer.write(tokens);577 578    std::vector<uint32_t> media_keys;579    media_keys.reserve(map_idx_to_media.size());580    for (const auto & item : map_idx_to_media) {581        media_keys.push_back(server_tokens_state_u32(item.first));582    }583    writer.write(media_keys);584 585    for (const auto & item : map_idx_to_media) {586        writer.write_media_chunk(item.second.get());587    }588 589    return writer.take();590}591 592server_tokens server_tokens::deserialize(const llama_tokens & packed, bool has_mtmd) {593    static_assert(sizeof(llama_token) == sizeof(uint32_t), "unexpected llama_token size");594 595    if (packed.empty() || packed[0] != LLAMA_TOKEN_NULL) {596        // plain token list, as written by older versions597        return server_tokens(packed, has_mtmd);598    }599 600    server_tokens_state_reader reader(reinterpret_cast<const char *>(packed.data()), packed.size() * sizeof(llama_token));601    reader.read<llama_token>(); // format marker602    if (reader.read<uint32_t>() != SERVER_TOKENS_STATE_VERSION) {603        throw std::runtime_error("Unsupported server tokens state version");604    }605 606    const llama_tokens tokens = reader.read_vector<llama_token>();607 608    // the media start indices, followed by the media chunks in the same order609    const std::vector<uint32_t> media_keys = reader.read_vector<uint32_t>();610    if (!media_keys.empty() && !has_mtmd) {611        throw std::runtime_error("Cannot restore media tokens without an mmproj");612    }613 614    server_tokens result(tokens, has_mtmd);615 616    for (const uint32_t key : media_keys) {617        const size_t start_idx = key;618        const std::vector<char> chunk_data = reader.read_vector<char>();619        if (chunk_data.empty()) {620            throw std::runtime_error("Cannot load media chunk from server tokens state");621        }622 623        mtmd::input_chunk_ptr chunk(mtmd_input_chunk_load(chunk_data.data(), chunk_data.size()));624        if (!chunk) {625            throw std::runtime_error("Cannot load media chunk from server tokens state");626        }627        result.map_idx_to_media[start_idx] = std::move(chunk);628    }629 630    if (reader.remaining() >= sizeof(llama_token)) {631        throw std::runtime_error("Trailing data in server tokens state");632    }633 634    return result;635}636 637llama_tokens server_tokens::get_text_tokens() const {638    llama_tokens res;639    res.reserve(tokens.size());640    for (llama_token t : tokens) {641        if (t != LLAMA_TOKEN_NULL) {642            res.push_back(t);643        }644    }645    return res;646}647 648void server_tokens::set_token(llama_pos pos, llama_token id) {649    GGML_ASSERT(!has_mtmd); // only allow this if mtmd is disabled650    tokens[pos] = id;651}652 653void server_tokens::keep_first(size_t n) {654    GGML_ASSERT(n <= tokens.size());655    if (has_mtmd) {656        if (n == tokens.size()) {657            return; // nothing to do658        }659        // we throw an error if we try to remove a token in the middle of an image660        // for ex. with input of 5 text tokens and 2 images:661        //    [0] [1] [2] [3] [4] [img0] [img0] [img0] [img1] [img1]662        // n  1   2   3   4   5   6      7      8      9      10663        // allowed to resize      ^                    ^664        // disallowed to resize          ^      ^             ^665        if (n > 0) {666            // make sure we never remove tokens in the middle of an image667            // note that the case where we keep a full image at the end is allowed:668            //   tokens[n - 1] == LLAMA_TOKEN_NULL && tokens[n] != LLAMA_TOKEN_NULL669            if (tokens[n - 1] == LLAMA_TOKEN_NULL && tokens[n] == LLAMA_TOKEN_NULL) {670                find_chunk(n - 1); // will throw an error if the token is not begin-of-chunk671            }672        }673        // remove all image chunks that are not used anymore674        for (auto it = map_idx_to_media.begin(); it != map_idx_to_media.end(); ) {675            size_t idx = it->first;676            if (idx >= n) {677                it = map_idx_to_media.erase(it);678            } else {679                ++it;680            }681        }682    }683    tokens.resize(n);684}685 686std::string server_tokens::detokenize(const llama_context * ctx, bool special) const {687    llama_tokens text_tokens;688    text_tokens.reserve(tokens.size());689    for (const auto & t : tokens) {690        if (t != LLAMA_TOKEN_NULL) {691            text_tokens.push_back(t);692        }693    }694    return common_detokenize(ctx, text_tokens, special);695}696 697size_t server_tokens::get_common_prefix(const server_tokens & b) const {698    const size_t max_idx = std::min(tokens.size(), b.tokens.size());699 700    if (!has_mtmd) {701        for (size_t i = 0; i < max_idx; ++i) {702            if (tokens[i] == b.tokens[i]) {703                continue;704            }705 706            return i;707        }708 709        return max_idx;710    }711 712    for (size_t i = 0; i < max_idx; ++i) {713        const llama_token ai =   tokens[i];714        const llama_token bi = b.tokens[i];715 716        if (ai == LLAMA_TOKEN_NULL && bi == LLAMA_TOKEN_NULL) {717            const auto & a_chunk =   find_chunk(i);718            const auto & b_chunk = b.find_chunk(i);719 720            GGML_ASSERT(a_chunk && b_chunk);721 722            const std::string id_ai = mtmd_input_chunk_get_id(a_chunk.get());723            const std::string id_bi = mtmd_input_chunk_get_id(b_chunk.get());724 725            const size_t n_tok_a = mtmd_input_chunk_get_n_tokens(a_chunk.get());726            const size_t n_tok_b = mtmd_input_chunk_get_n_tokens(b_chunk.get());727 728            if (id_ai == id_bi && n_tok_a == n_tok_b) {729                GGML_ASSERT(n_tok_a > 0 && "Invalid media chunk"); // should never happen730                i += n_tok_a - 1; // will be +1 by the for loop731                continue;732            }733 734            return i;735        }736 737        if (ai == bi) {738            continue;739        }740 741        return i;742    }743 744    return max_idx; // all tokens are equal745}746 747common_chat_msg_spans server_tokens::find_message_spans(const common_chat_msg_delimiters & delims) const {748    std::map<size_t, size_t> skips;749    for (const auto & it : map_idx_to_media) {750        skips[it.first] = mtmd_input_chunk_get_n_tokens(it.second.get());751    }752    return delims.split(tokens, skips);753}754 755bool server_tokens::validate(const struct llama_context * ctx) const {756    const llama_model * model = llama_get_model(ctx);757    const llama_vocab * vocab = llama_model_get_vocab(model);758    const int32_t n_vocab = llama_vocab_n_tokens(vocab);759    size_t n_media = 0;760 761    for (size_t i = 0; i < tokens.size(); ++i) {762        const auto & t = tokens[i];763        if (t == LLAMA_TOKEN_NULL) {764            try {765                const auto & chunk = find_chunk(i);766                if (mtmd_input_chunk_get_type(chunk.get()) == MTMD_INPUT_CHUNK_TYPE_TEXT) {767                    return false;768                }769                const size_t n_tokens = mtmd_input_chunk_get_n_tokens(chunk.get());770                const llama_pos n_pos = mtmd_input_chunk_get_n_pos(chunk.get());771                if (n_tokens == 0 || n_pos <= 0 || n_tokens > tokens.size() - i) {772                    return false;773                }774                for (size_t j = i; j < i + n_tokens; ++j) {775                    if (tokens[j] != LLAMA_TOKEN_NULL) {776                        return false;777                    }778                }779                ++n_media;780                i += n_tokens - 1;781            } catch (const std::exception & e) {782                return false;783            }784        } else if (t < 0 || t >= n_vocab) {785            return false;786        }787    }788    return n_media == map_idx_to_media.size();789}790 791server_tokens server_tokens::clone() const {792    server_tokens res;793    res.has_mtmd = has_mtmd;794    res.tokens   = tokens;795    for (auto it = map_idx_to_media.begin(); it != map_idx_to_media.end(); ++it) {796        size_t idx = it->first;797        const mtmd::input_chunk_ptr & chunk = it->second;798        res.map_idx_to_media[idx] = mtmd::input_chunk_ptr(mtmd_input_chunk_copy(chunk.get()));799    }800    return res;801}802 803//804// tokenizer and input processing utils805//806 807bool json_is_array_of_numbers(const json & data) {808    if (data.is_array()) {809        for (const auto & e : data) {810            if (!e.is_number_integer()) {811                return false;812            }813        }814        return true;815    }816    return false;817}818 819bool json_is_array_of_mixed_numbers_strings(const json & data) {820    bool seen_string = false;821    bool seen_number = false;822    if (data.is_array()) {823        for (const auto & e : data) {824            seen_string |= e.is_string();825            seen_number |= e.is_number_integer();826            if (seen_number && seen_string) {827                return true;828            }829        }830    }831    return false;832}833 834bool json_is_array_and_contains_numbers(const json & data) {835    if (data.is_array()) {836        for (const auto & e : data) {837            if (e.is_number_integer()) {838                return true;839            }840        }841        return false;842    }843    return false;844}845 846json json_get_nested_values(const std::vector<std::string> & paths, const json & js) {847    json result = json::object();848 849    for (const std::string & path : paths) {850        json current = js;851        const auto keys = string_split<std::string>(path, /*separator*/ '/');852        bool valid_path = true;853        for (const std::string & k : keys) {854            if (valid_path && current.is_object() && current.contains(k)) {855                current = current[k];856            } else {857                valid_path = false;858            }859        }860        if (valid_path) {861            result[path] = current;862        }863    }864    return result;865}866 867llama_tokens tokenize_mixed(const llama_vocab * vocab, const json & json_prompt, bool add_special, bool parse_special) {868    // If `add_bos` is true, we only add BOS, when json_prompt is a string,869    // or the first element of the json_prompt array is a string.870    llama_tokens prompt_tokens;871 872    if (json_prompt.is_array()) {873        bool first = true;874        for (const auto & p : json_prompt) {875            if (p.is_string()) {876                auto s = p.template get<std::string>();877 878                llama_tokens p;879                if (first) {880                    p = common_tokenize(vocab, s, add_special, parse_special);881                    first = false;882                } else {883                    p = common_tokenize(vocab, s, false, parse_special);884                }885 886                prompt_tokens.insert(prompt_tokens.end(), p.begin(), p.end());887            } else {888                if (first) {889                    first = false;890                }891 892                prompt_tokens.push_back(p.template get<llama_token>());893            }894        }895    } else {896        auto s = json_prompt.template get<std::string>();897        prompt_tokens = common_tokenize(vocab, s, add_special, parse_special);898    }899 900    return prompt_tokens;901}902 903size_t validate_utf8(const std::string& text) {904    size_t len = text.size();905    if (len == 0) return 0;906 907    // Check the last few bytes to see if a multi-byte character is cut off908    for (size_t i = 1; i <= 4 && i <= len; ++i) {909        unsigned char c = text[len - i];910        // Check for start of a multi-byte sequence from the end911        if ((c & 0xE0) == 0xC0) {912            // 2-byte character start: 110xxxxx913            // Needs at least 2 bytes914            if (i < 2) return len - i;915        } else if ((c & 0xF0) == 0xE0) {916            // 3-byte character start: 1110xxxx917            // Needs at least 3 bytes918            if (i < 3) return len - i;919        } else if ((c & 0xF8) == 0xF0) {920            // 4-byte character start: 11110xxx921            // Needs at least 4 bytes922            if (i < 4) return len - i;923        }924    }925 926    // If no cut-off multi-byte character is found, return full length927    return len;928}929 930server_tokens process_mtmd_prompt(931        mtmd_context * mctx,932        const std::string & prompt,933        const std::vector<raw_buffer> & files,934        const mtmd_helper_init_opt & init_opt,935        bool is_placeholder) {936    // these will be freed upon going out of scope937    mtmd::bitmaps bitmaps;938    std::vector<mtmd_helper::video_ptr> videos;939    for (auto & file : files) {940        auto out = mtmd_helper_bitmap_init_from_buf(mctx, file.data(), file.size(), is_placeholder, init_opt);941        if (!out.bitmap) {942            throw std::runtime_error("Failed to load image or audio file");943        }944        bitmaps.entries.emplace_back(out.bitmap);945        if (out.video_ctx) {946            videos.emplace_back(out.video_ctx);947        }948    }949    // process prompt950    std::vector<server_tokens> inputs;951    // multimodal952    mtmd_input_text inp_txt = {953        prompt.data(),954        prompt.size(),955        /* add_special */   true,956        /* parse_special */ true,957    };958    mtmd::input_chunks chunks(mtmd_input_chunks_init());959    auto bitmaps_c_ptr = bitmaps.c_ptr();960    int32_t tokenized = mtmd_tokenize(mctx,961                                      chunks.ptr.get(),962                                      &inp_txt,963                                      bitmaps_c_ptr.data(),964                                      bitmaps_c_ptr.size());965    if (tokenized != 0) {966        throw std::runtime_error("Failed to tokenize prompt");967    }968    auto result = server_tokens(chunks, true);969    return result;970}971 972/**973 * break the input "prompt" object into multiple prompt if needed, then tokenize them974 * use tokenize_input_prompts() if the input could be an array.975 * this supports these cases:976 * - "prompt": "string"977 * - "prompt": [12, 34, 56]978 * - "prompt": [12, 34, "string", 56, 78]979 * - "prompt": { "prompt_string": "string", "multimodal_data": [ "base64" ] }980 */981static server_tokens tokenize_input_subprompt(const llama_vocab * vocab, mtmd_context * mctx, const json & json_prompt, bool add_special, bool parse_special, const mtmd_helper_init_opt & init_opt) {982    constexpr char JSON_STRING_PROMPT_KEY[] = "prompt_string";983    constexpr char JSON_MTMD_DATA_KEY[] = "multimodal_data";984    const bool has_mtmd = mctx != nullptr;985    if (json_prompt.is_string() || json_is_array_of_mixed_numbers_strings(json_prompt)) {986        // string or mixed987        llama_tokens tmp = tokenize_mixed(vocab, json_prompt, add_special, parse_special);988        return server_tokens(tmp, false);989    } else if (json_is_array_of_numbers(json_prompt)) {990        // array of tokens991        llama_tokens tmp = json_prompt.get<llama_tokens>();992        return server_tokens(tmp, false);993    } else if (json_prompt.contains(JSON_STRING_PROMPT_KEY)) {994        // JSON object with prompt key.995        if (json_prompt.contains(JSON_MTMD_DATA_KEY)) {996            if (!has_mtmd)997                throw std::runtime_error("Multimodal data provided, but model does not support multimodal requests.");998 999            // JSON object with prompt and multimodal key.1000            std::vector<raw_buffer> files;1001            for (const auto & entry : json_prompt.at(JSON_MTMD_DATA_KEY)) {1002                files.push_back(base64_decode(entry));1003            }1004            return process_mtmd_prompt(mctx, json_prompt.at(JSON_STRING_PROMPT_KEY), files, init_opt);1005        } else {1006            // Not multimodal, but contains a subobject.1007            llama_tokens tmp = tokenize_mixed(vocab, json_prompt.at(JSON_STRING_PROMPT_KEY), add_special, parse_special);1008            return server_tokens(tmp, false);1009        }1010   } else {1011       throw std::runtime_error("\"prompt\" elements must be a string, a list of tokens, a JSON object containing a prompt string, or a list of mixed strings & tokens.");1012   }1013}1014 1015std::vector<server_tokens> tokenize_input_prompts(const llama_vocab * vocab, mtmd_context * mctx, const json & json_prompt, bool add_special, bool parse_special, const mtmd_helper_init_opt & init_opt) {1016    std::vector<server_tokens> result;1017    if (json_prompt.is_array() && !json_is_array_and_contains_numbers(json_prompt)) {1018        result.reserve(json_prompt.size());1019        for (const auto & p : json_prompt) {1020            result.push_back(tokenize_input_subprompt(vocab, mctx, p, add_special, parse_special, init_opt));1021        }1022    } else {1023        result.push_back(tokenize_input_subprompt(vocab, mctx, json_prompt, add_special, parse_special, init_opt));1024    }1025    if (result.empty()) {1026        throw std::runtime_error("\"prompt\" must not be empty");1027    }1028    return result;1029}1030 1031//1032// OAI utils1033//1034 1035// used by /completions endpoint1036json oaicompat_completion_params_parse(const json & body) {1037    json llama_params;1038 1039    if (!body.contains("prompt")) {1040        throw std::runtime_error("\"prompt\" is required");1041    }1042 1043    // Handle "stop" field1044    if (body.contains("stop") && body.at("stop").is_string()) {1045        llama_params["stop"] = json::array({body.at("stop").get<std::string>()});1046    } else {1047        llama_params["stop"] = json_value(body, "stop", json::array());1048    }1049 1050    // Handle "echo" field1051    if (json_value(body, "echo", false)) {1052        throw std::runtime_error("Only no echo is supported");1053    }1054 1055    // Params supported by OAI but unsupported by llama.cpp1056    static const std::vector<std::string> unsupported_params { "best_of", "suffix" };1057    for (const auto & param : unsupported_params) {1058        if (body.contains(param)) {1059            throw std::runtime_error("Unsupported param: " + param);1060        }1061    }1062 1063    // Copy remaining properties to llama_params1064    for (const auto & item : body.items()) {1065        // Exception: if "n_predict" is present, we overwrite the value specified earlier by "max_tokens"1066        if (!llama_params.contains(item.key()) || item.key() == "n_predict") {1067            llama_params[item.key()] = item.value();1068        }1069    }1070 1071    return llama_params;1072}1073 1074// url can be1075// - http(s):// for remote files1076// - file:// for local files (only allowed if media_path is set)1077// - data: for base64 encoded data with uri scheme (e.g. data:image/png;base64,...)1078// - raw base64 encoded data1079static void handle_media(1080        std::vector<raw_buffer> & out_files,1081        const std::string & url,1082        const std::string & media_path) {1083    if (!media_path.empty()) {1084        // should already be enforced by arg.cpp, but checking just in case1085        GGML_ASSERT(media_path.back() == DIRECTORY_SEPARATOR);1086    }1087 1088    if (string_starts_with(url, "http")) {1089        // download remote image1090        // TODO @ngxson : maybe make these params configurable1091        common_remote_params params;1092        params.max_size = 1024 * 1024 * 10; // 10MB1093        params.timeout  = 10; // seconds1094        SRV_INF("downloading image from '%s'\n", url.c_str());1095        auto res = common_remote_get_content(url, params);1096        if (200 <= res.first && res.first < 300) {1097            SRV_INF("downloaded %zu bytes\n", res.second.size());1098            raw_buffer data;1099            data.insert(data.end(), res.second.begin(), res.second.end());1100            out_files.push_back(data);1101        } else {1102            throw std::runtime_error("Failed to download image");1103        }1104 1105    } else if (string_starts_with(url, "file://")) {1106        if (media_path.empty()) {1107            throw std::invalid_argument("file:// URLs are not allowed unless --media-path is specified");1108        }1109        // load local image file1110        std::string file_path = url.substr(7); // remove "file://"1111        raw_buffer data;1112        if (!fs_validate_filename(file_path, true)) {1113            throw std::invalid_argument("file path is not allowed: " + file_path);1114        }1115        SRV_INF("loading image from local file '%s'\n", (media_path + file_path).c_str());1116        std::ifstream file(media_path + file_path, std::ios::binary);1117        if (!file) {1118            throw std::invalid_argument("file does not exist or cannot be opened: " + file_path);1119        }1120        data.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());1121        out_files.push_back(data);1122 1123    } else if (string_starts_with(url, "data:")) {1124        // try to decode base64 image, video, or audio1125        std::vector<std::string> parts = string_split<std::string>(url, /*separator*/ ',');1126        if (parts.size() != 2) {1127            throw std::invalid_argument("Invalid uri-encoded base64 value");1128        } else if (!string_starts_with(parts[0], "data:image/")1129                && !string_starts_with(parts[0], "data:video/")1130                && !string_starts_with(parts[0], "data:audio/")) {1131            throw std::invalid_argument("Invalid uri format: " + parts[0]);1132        } else if (!string_ends_with(parts[0], "base64")) {1133            throw std::invalid_argument("uri must be base64 encoded");1134        } else {1135            auto base64_data = parts[1];1136            auto decoded_data = base64_decode(base64_data);1137            out_files.push_back(decoded_data);1138        }1139 1140    } else {1141        // try as raw base64 string1142        auto decoded_data = base64_decode(url);1143        if (decoded_data.empty()) {1144            throw std::runtime_error("Invalid base64 value");1145        }1146        out_files.push_back(decoded_data);1147    }1148}1149 1150// used by /chat/completions endpoint1151json oaicompat_chat_params_parse(1152    json & body, /* openai api json semantics */1153    const server_chat_params & opt,1154    std::vector<raw_buffer> & out_files)1155{1156    json llama_params;1157 1158    auto tools = json_value(body, "tools", json());1159    auto has_tools = tools.is_array() && !tools.empty();1160    auto stream = json_value(body, "stream", false);1161    auto tool_choice = json_value(body, "tool_choice", std::string("auto"));1162 1163    if (!opt.use_jinja) {1164        if (has_tools) {1165            throw std::runtime_error("tools param requires --jinja flag");1166        }1167        if (tool_choice != "auto") {1168            throw std::runtime_error("tool_choice param requires --jinja flag");1169        }1170    }1171 1172    // Handle "stop" field1173    if (body.contains("stop") && body.at("stop").is_string()) {1174        llama_params["stop"] = json::array({body.at("stop").get<std::string>()});1175    } else {1176        llama_params["stop"] = json_value(body, "stop", json::array());1177    }1178 1179    auto json_schema = json_value(body, "json_schema", json());1180    auto grammar = json_value(body, "grammar", std::string());1181    if (!json_schema.is_null() && !grammar.empty()) {1182        throw std::runtime_error("Cannot use both json_schema and grammar");1183    }1184 1185    // Handle "response_format" field1186    if (body.contains("response_format")) {1187        json response_format      = json_value(body, "response_format", json::object());1188        std::string response_type = json_value(response_format, "type", std::string());1189        if (response_type == "json_object") {1190            if (response_format.contains("schema") || json_schema.empty()) {1191                json_schema = json_value(response_format, "schema", json::object());1192            }1193        } else if (response_type == "json_schema") {1194            auto schema_wrapper = json_value(response_format, "json_schema", json::object());1195            json_schema = json_value(schema_wrapper, "schema", json::object());1196        } else if (!response_type.empty() && response_type != "text") {1197            throw std::invalid_argument("response_format type must be one of \"text\" or \"json_object\", but got: " + response_type);1198        }1199    }1200 

Showing the first 1,200 of 1987 lines. Download the file for the rest.