22#include " server-http.h"
33#include " server-models.h"
44
5+ #include " server-common.h"
56#include " arg.h"
67#include " common.h"
78#include " llama.h"
89#include " log.h"
910
1011#include < atomic>
12+ #include < cstdlib>
13+ #include < fstream>
1114#include < signal.h>
1215#include < thread> // for std::thread::hardware_concurrency
1316
1417#if defined(_WIN32)
1518#include < windows.h>
1619#endif
1720
21+ using json = nlohmann::ordered_json;
22+
1823static std::function<void (int )> shutdown_handler;
1924static std::atomic_flag is_terminating = ATOMIC_FLAG_INIT;
2025
@@ -29,6 +34,67 @@ static inline void signal_handler(int signal) {
2934 shutdown_handler (signal);
3035}
3136
37+ static bool is_valid_webui_setting_value (const json & value) {
38+ return value.is_boolean () || value.is_number () || value.is_string ();
39+ }
40+
41+ static bool merge_webui_settings (const json & overrides, json & target, const std::string & source) {
42+ if (overrides.is_null ()) {
43+ return true ;
44+ }
45+
46+ if (!overrides.is_object ()) {
47+ LOG_ERR (" %s must be a JSON object of key/value pairs\n " , source.c_str ());
48+ return false ;
49+ }
50+
51+ for (const auto & [key, value] : overrides.items ()) {
52+ if (!is_valid_webui_setting_value (value)) {
53+ LOG_WRN (" %s: ignoring '%s' because the value type is not supported (expected string/number/boolean)\n " , source.c_str (), key.c_str ());
54+ continue ;
55+ }
56+ target[key] = value;
57+ }
58+
59+ return true ;
60+ }
61+
62+ static bool load_webui_config_from_file (const std::string & path, json & target) {
63+ if (path.empty ()) {
64+ return true ;
65+ }
66+
67+ std::ifstream file (path);
68+ if (!file.is_open ()) {
69+ LOG_WRN (" failed to open webui config file '%s', continuing with defaults\n " , path.c_str ());
70+ return true ;
71+ }
72+
73+ try {
74+ json parsed = json::parse (file);
75+ return merge_webui_settings (parsed, target, string_format (" webui config file '%s'" , path.c_str ()));
76+ } catch (const std::exception & e) {
77+ LOG_ERR (" failed to parse webui config file '%s' as JSON: %s\n " , path.c_str (), e.what ());
78+ return false ;
79+ }
80+ }
81+
82+ static void merge_webui_config_from_env (json & target) {
83+ const char * env_value = std::getenv (" LLAMA_WEBUI_CONFIG" );
84+ if (env_value == nullptr ) {
85+ return ;
86+ }
87+
88+ try {
89+ json parsed = json::parse (env_value);
90+ if (!merge_webui_settings (parsed, target, " LLAMA_WEBUI_CONFIG" )) {
91+ LOG_ERR (" ignoring LLAMA_WEBUI_CONFIG because it is not a JSON object\n " );
92+ }
93+ } catch (const std::exception & e) {
94+ LOG_ERR (" failed to parse LLAMA_WEBUI_CONFIG as JSON: %s\n " , e.what ());
95+ }
96+ }
97+
3298// wrapper function that handles exceptions and logs errors
3399// this is to make sure handler_t never throws exceptions; instead, it returns an error response
34100static server_http_context::handler_t ex_wrapper (server_http_context::handler_t func) {
@@ -73,6 +139,14 @@ int main(int argc, char ** argv, char ** envp) {
73139 return 1 ;
74140 }
75141
142+ // Merge priority: WebUI built-in defaults -> config file -> LLAMA_WEBUI_CONFIG -> user localStorage
143+ json webui_settings = json::object ();
144+ if (!load_webui_config_from_file (params.webui_config_file , webui_settings)) {
145+ return 1 ;
146+ }
147+ merge_webui_config_from_env (webui_settings);
148+ params.webui_config = std::move (webui_settings);
149+
76150 // TODO: should we have a separate n_parallel parameter for the server?
77151 // https://github.com/ggml-org/llama.cpp/pull/16736#discussion_r2483763177
78152 // TODO: this is a common configuration that is suitable for most local use cases
0 commit comments