Resolve path to share/status.html at startup.
| 892 | if (::stat(path.c_str(), &st) == 0) { |
| 893 | file_size = (int64_t)st.st_size; |
| 894 | file_mtime = (int64_t)st.st_mtime; |
| 895 | } else { |
| 896 | std::fprintf(stderr, "[disk-cache] salt: stat(%s) failed — path-only fingerprint\n", |
| 897 | path.c_str()); |
| 898 | } |
| 899 | |
| 900 | // Hash chat_template_src separately (can be large; fold as digest). |
| 901 | uint8_t tmpl_digest[20] = {}; |
| 902 | sha1_hash(cfg.chat_template_src.data(), cfg.chat_template_src.size(), tmpl_digest); |
| 903 | |
| 904 | // Serialization: path_len(4) + path + file_size(8) + file_mtime(8) + max_ctx(4) + tmpl_digest(20). |
| 905 | std::vector<uint8_t> buf; |
| 906 | uint32_t plen = (uint32_t)path.size(); |
| 907 | buf.insert(buf.end(), (uint8_t *)&plen, (uint8_t *)&plen + 4); |
| 908 | buf.insert(buf.end(), (uint8_t *)path.data(), (uint8_t *)path.data() + path.size()); |
| 909 | buf.insert(buf.end(), (uint8_t *)&file_size, (uint8_t *)&file_size + 8); |
| 910 | buf.insert(buf.end(), (uint8_t *)&file_mtime, (uint8_t *)&file_mtime + 8); |
| 911 | int32_t mc = (int32_t)cfg.max_ctx; |
| 912 | buf.insert(buf.end(), (uint8_t *)&mc, (uint8_t *)&mc + 4); |
| 913 | buf.insert(buf.end(), tmpl_digest, tmpl_digest + 20); |
| 914 | |
| 915 | uint8_t digest[20]; |
| 916 | sha1_hash(buf.data(), buf.size(), digest); |
| 917 | std::memcpy(salt.data(), digest, 16); |
| 918 | return salt; |
| 919 | } |
| 920 | |
| 921 | // ─── HttpServer ───────────────────────────────────────────────────────── |
| 922 | |
| 923 | HttpServer::HttpServer(ModelBackend & backend, |
| 924 | Tokenizer & tokenizer, |
| 925 | const ServerConfig & config) |
| 926 | : backend_(backend) |
| 927 | , tokenizer_(tokenizer) |
| 928 | , config_(config) |
| 929 | , chat_format_(ChatFormat::QWEN3) // default, overridden by arch |
| 930 | , prefix_cache_(config.prefix_cache_cap, tokenizer) |
| 931 | , disk_cache_({config.disk_cache_dir, |
| 932 | config.disk_cache_budget_mb * (size_t)(1024 * 1024), |
| 933 | config.disk_cache_min_tokens, |
| 934 | config.disk_cache_continued_interval, |
| 935 | config.disk_cache_cold_max_tokens}, backend) |
| 936 | { |
| 937 | #ifdef DFLASH_HAS_CURL |
| 938 | curl_global_init(CURL_GLOBAL_DEFAULT); |
| 939 | #endif |
| 940 | prefix_cache_.init_full_cache(config.prefill_cache_cap); |
| 941 | // Fold model+config identity into the layout fingerprint BEFORE init() |
| 942 | // so compute_layout_id sees it on every learn/verify call. Prevents stale |
| 943 | // KV hits when the server restarts over the same --kv-cache-dir with a |
| 944 | // different model, max_ctx, or chat_template (gemma4 ↔ qwen3.6, etc.). |
| 945 | if (!disk_cache_.disabled()) { |
| 946 | disk_cache_.set_identity_salt(compute_disk_cache_salt(config)); |