diff --git a/common/chat-auto-parser-generator.cpp b/common/chat-auto-parser-generator.cpp index 81a4ef0aee..d7e117e4d9 100644 --- a/common/chat-auto-parser-generator.cpp +++ b/common/chat-auto-parser-generator.cpp @@ -311,7 +311,7 @@ common_peg_parser analyze_tools::build_tool_parser_tag_json(parser_build_context foreach_function(inputs.tools, [&](const json & tool) { const auto & func = tool.at("function"); - std::string name = func.at("name").get(); + std::string name = func.at("name"); const auto & schema = func.contains("parameters") ? func.at("parameters") : json::object(); // Build call_id parser based on position (if supported) @@ -384,7 +384,7 @@ common_peg_parser analyze_tools::build_tool_parser_tag_tagged(parser_build_conte foreach_function(inputs.tools, [&](const json & tool) { const auto & func = tool.at("function"); - std::string name = func.at("name").get(); + std::string name = func.at("name"); auto params = func.contains("parameters") ? func.at("parameters") : json::object(); const auto & properties = params.contains("properties") ? params.at("properties") : json::object(); diff --git a/common/chat-peg-parser.cpp b/common/chat-peg-parser.cpp index 6f41daa403..e17134b6c2 100644 --- a/common/chat-peg-parser.cpp +++ b/common/chat-peg-parser.cpp @@ -488,7 +488,7 @@ common_peg_parser common_chat_peg_builder::standard_constructed_tools( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); // Build argument parsers @@ -565,7 +565,7 @@ common_peg_parser common_chat_peg_builder::python_style_tool_calls( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); auto args = eps(); @@ -640,7 +640,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); // Build inner object fields @@ -726,7 +726,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); auto nested_name = literal("\"" + nested_name_field + "\"") + space() + literal(":") + space() + @@ -795,7 +795,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); auto tool_name_ = name_key_parser + space() + literal(":") + space() + diff --git a/common/chat.cpp b/common/chat.cpp index 4409a2b2e7..50358729ba 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -968,7 +968,7 @@ static std::string common_chat_template_direct_apply_impl( jinja::caps_apply_preserve_reasoning(ctx, enabled); } if (inp.contains("reasoning_effort") && inp["reasoning_effort"].is_string() && !inp["reasoning_effort"].empty()) { - std::string reasoning_effort = inp["reasoning_effort"].get(); + std::string reasoning_effort = inp["reasoning_effort"]; jinja::caps_apply_reasoning_effort(ctx, reasoning_effort); } @@ -1113,7 +1113,7 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_ auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); const auto & schema = function.at("parameters"); tool_choice |= @@ -1221,7 +1221,7 @@ static common_chat_params common_chat_params_init_qwen3_coder(const common_chat_ // starting . The model may hallucinate a tool name, but it is preferable over // constraining on foreach_function(inputs.tools, [&](const json & tool) { - const std::string name = tool.at("function").at("name").get(); + const std::string name = tool.at("function").at("name"); tool_call_starts.push_back(""); }); @@ -1249,7 +1249,7 @@ static common_chat_params common_chat_params_init_qwen3_coder(const common_chat_ auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); auto parameters = function.contains("parameters") ? function.at("parameters") : json::object(); auto schema_info = common_schema_info(); @@ -1440,7 +1440,7 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); const auto & params = function.at("parameters"); auto func_name = p.literal(" to=functions.") + p.tool_name(p.literal(name)); @@ -1606,7 +1606,7 @@ static common_chat_params common_chat_params_init_gemma4(const common_chat_templ foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); // TODO @aldehir : need to extend json-schema-to-grammar to produce more than JSON rules // const auto & params = function.at("parameters"); @@ -1705,7 +1705,7 @@ static common_chat_params common_chat_params_init_functionary_v3_2(const common_ auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); const auto & schema = function.at("parameters"); // Tool format: >>>function_name\n{json_args} @@ -1842,7 +1842,7 @@ static common_chat_params common_chat_params_init_kimi_k2(const common_chat_temp auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); const auto & schema = function.at("parameters"); // Match: functions.: @@ -2036,7 +2036,7 @@ static common_chat_params common_chat_params_init_gigachat_v3( auto tool_choice = p.choice(); for (const auto & tool : inputs.tools) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); const auto & schema = function.at("parameters"); auto tool_name = p.json_member("name", "\"" + p.tool_name(p.literal(name)) + "\""); @@ -2232,7 +2232,7 @@ static common_chat_params common_chat_params_init_deepseek_v3_2(const common_cha if (has_tool_calls) { foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); const auto & props = params.contains("properties") ? params.at("properties") : json::object(); @@ -2467,7 +2467,7 @@ static common_chat_params common_chat_params_init_kimi_k3(const common_chat_temp auto tool_choices = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); const json schema = function.contains("parameters") ? function.at("parameters") : json::object(); // arguments come one tag per key, with the JSON type in a type="..." @@ -2788,7 +2788,7 @@ static common_chat_params common_chat_params_init_minimax_m3(const common_chat_t auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); auto schema_info = common_schema_info(); @@ -3242,7 +3242,7 @@ static common_chat_params common_chat_params_init_minicpm5(const common_chat_tem auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - const std::string name = function.at("name").get(); + const std::string name = function.at("name"); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); auto args = p.eps(); @@ -3388,7 +3388,7 @@ static common_chat_params common_chat_params_init_muse_glimmer(const common_chat auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - const std::string name = function.at("name").get(); + const std::string name = function.at("name"); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); auto args = p.eps(); diff --git a/common/download.cpp b/common/download.cpp index 4b28a708c8..f091ec5112 100644 --- a/common/download.cpp +++ b/common/download.cpp @@ -921,7 +921,7 @@ std::string common_docker_resolve_model(const std::string & docker) { if (manifest.contains("layers")) { for (const auto & layer : manifest["layers"]) { if (layer.contains("mediaType")) { - std::string media_type = layer["mediaType"].get(); + std::string media_type = layer["mediaType"]; if (media_type == "application/vnd.docker.ai.gguf.v3" || media_type.find("gguf") != std::string::npos) { gguf_digest = layer["digest"].get(); diff --git a/common/hf-cache.cpp b/common/hf-cache.cpp index 50d6dd6105..ce727594ee 100644 --- a/common/hf-cache.cpp +++ b/common/hf-cache.cpp @@ -244,8 +244,8 @@ static std::string get_repo_commit(const std::string & repo_id, !branch.contains("targetCommit") || !branch["targetCommit"].is_string()) { continue; } - std::string _name = branch["name"].get(); - std::string _commit = branch["targetCommit"].get(); + std::string _name = branch["name"]; + std::string _commit = branch["targetCommit"]; if (!is_valid_subpath(refs_path, _name)) { LOG_WRN("%s: skip invalid branch: %s\n", __func__, _name.c_str()); diff --git a/common/json-schema-to-grammar.cpp b/common/json-schema-to-grammar.cpp index 1d06768440..bb1119cd7a 100644 --- a/common/json-schema-to-grammar.cpp +++ b/common/json-schema-to-grammar.cpp @@ -845,7 +845,7 @@ public: } } else if (n.is_object()) { if (n.contains("$ref")) { - std::string ref = n["$ref"].get(); + std::string ref = n["$ref"]; if (_refs.find(ref) == _refs.end()) { json target; if (ref.find("https://") == 0) { @@ -947,7 +947,7 @@ public: } if ((schema_type.is_null() || schema_type == "object") && (schema.contains("properties") || - (schema.contains("additionalProperties") && schema["additionalProperties"] != true))) { + (schema.contains("additionalProperties") && !schema["additionalProperties"].get()))) { std::unordered_set required; if (schema.contains("required") && schema["required"].is_array()) { for (const auto & item : schema["required"]) { @@ -1135,7 +1135,7 @@ bool common_schema_info::resolves_to_string(const common_json & schema) { // Handle $ref if (s.contains("$ref")) { - const std::string ref = s["$ref"].get(); + const std::string ref = s["$ref"]; if (visited_refs.find(ref) != visited_refs.end()) { // Circular reference, assume not a string to be safe return false; @@ -1218,7 +1218,7 @@ bool common_schema_info::resolves_to_string(const common_json & schema) { // Check format - many formats imply string if (s.contains("format")) { - const std::string fmt = s["format"].get(); + const std::string fmt = s["format"]; if (fmt == "date" || fmt == "time" || fmt == "date-time" || fmt == "uri" || fmt == "email" || fmt == "hostname" || fmt == "ipv4" || fmt == "ipv6" || fmt == "uuid" || diff --git a/common/json.h b/common/json.h index 4a9f5a82f7..9415d57b36 100644 --- a/common/json.h +++ b/common/json.h @@ -148,6 +148,10 @@ class common_json { common_json & operator[](const std::string & key); const common_json & operator[](const std::string & key) const; + common_json & operator[](const char * key) { return (*this)[std::string(key)]; } + const common_json & operator[](const char * key) const { return (*this)[std::string(key)]; } + common_json & operator[](int idx) { return (*this)[(size_t) idx]; } + const common_json & operator[](int idx) const { return (*this)[(size_t) idx]; } common_json & operator[](size_t idx); const common_json & operator[](size_t idx) const; @@ -164,6 +168,15 @@ class common_json { // only for the types instantiated in json.cpp, the rest fails at link time template T get() const; + // implicit get() for plain values, so they can be assigned to their C++ type directly + // note: kept to this short list on purpose, a wider one makes j["key"] ambiguous + operator std::string() const { return get(); } + operator bool() const { return get(); } + operator int() const { return get(); } + operator int64_t() const { return get(); } + operator float() const { return get(); } + operator double() const { return get(); } + template T value(const std::string & key, T def) const { return contains(key) ? at(key).get() : def; diff --git a/tests/gguf-model-data.cpp b/tests/gguf-model-data.cpp index fe8b4ca76e..18e2955dfa 100644 --- a/tests/gguf-model-data.cpp +++ b/tests/gguf-model-data.cpp @@ -475,7 +475,7 @@ static std::string detect_gguf_filename(const std::string & repo, const std::str for (const auto & sibling : j["siblings"]) { if (!sibling.contains("rfilename")) { continue; } - std::string fname = sibling["rfilename"].get(); + std::string fname = sibling["rfilename"]; if (fname.size() < 5 || fname.substr(fname.size() - 5) != ".gguf") { continue; } diff --git a/tests/test-chat-auto-parser.cpp b/tests/test-chat-auto-parser.cpp index f98e281086..2209dcac84 100644 --- a/tests/test-chat-auto-parser.cpp +++ b/tests/test-chat-auto-parser.cpp @@ -2157,7 +2157,7 @@ static void test_tagged_args_with_embedded_quotes(testing & t) { for (const auto & tool_def : tools) { if (!tool_def.contains("function")) { continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); const auto & params = function.at("parameters"); if (!params.contains("properties") || !params.at("properties").is_object()) { continue; } diff --git a/tests/test-chat-peg-parser.cpp b/tests/test-chat-peg-parser.cpp index 432b67870c..cede556cf3 100644 --- a/tests/test-chat-peg-parser.cpp +++ b/tests/test-chat-peg-parser.cpp @@ -114,9 +114,9 @@ static json create_tools() { { "default", 5 } } }, { "category", { { "type", "string" }, - { "enum", { "api", "troubleshooting", "billing", "general" } }, + { "enum", json::array({ "api", "troubleshooting", "billing", "general" }) }, { "description", "Filter search by specific category." } } } } }, - { "required", { "query", "category" } }, + { "required", json::array({ "query", "category" }) }, { "additionalProperties", false } } }, { "strict", true } } } }; @@ -400,13 +400,13 @@ static void test_example_qwen3_coder(testing & t) { std::vector tool_parsers; for (const auto & def : tools) { auto function = def.at("function"); - std::string name = function.at("name").get(); + std::string name = function.at("name"); auto parameters = function.at("parameters"); auto properties = parameters.at("properties"); std::set required_properties; if (function.contains("required")) { - required_properties = function.at("required").get>(); + required_properties = function.at("required").get>(); } std::vector arg_parsers; diff --git a/tests/test-chat.cpp b/tests/test-chat.cpp index 7918f0ffcf..d43c14ffa3 100644 --- a/tests/test-chat.cpp +++ b/tests/test-chat.cpp @@ -7052,7 +7052,7 @@ static void test_reasoning_budget_message_per_request() { if (!llama_params.contains("reasoning_budget_message")) { throw std::runtime_error("reasoning_budget_message missing from llama_params (thinking_end_tag may be empty for this template)"); } - std::string got = llama_params["reasoning_budget_message"].get(); + std::string got = llama_params["reasoning_budget_message"]; if (got != per_request_message) { throw std::runtime_error("Expected reasoning_budget_message='" + per_request_message + "', got '" + got + "'"); } diff --git a/tools/cli/cli-context.cpp b/tools/cli/cli-context.cpp index f6a9c74ac6..a1dffc5776 100644 --- a/tools/cli/cli-context.cpp +++ b/tools/cli/cli-context.cpp @@ -218,7 +218,7 @@ bool cli_context::list_and_ask_models() { if (!m.contains("id") || !m.at("id").is_string()) { continue; } - std::string name = m.at("id").get(); + std::string name = m.at("id"); std::string display = name; if (m.contains("aliases") && m.at("aliases").is_array()) { std::vector aliases; @@ -387,14 +387,14 @@ bool cli_context::generate_completion(generated_content & content_out, cli_timin } const auto & delta = choice.at("delta"); if (delta.contains("reasoning_content") && delta.at("reasoning_content").is_string()) { - const std::string text = delta.at("reasoning_content").get(); + const std::string text = delta.at("reasoning_content"); if (!text.empty()) { content_out.reasoning += text; a.push(ui::ASSISTANT_DISPLAY_MODE_REASONING, text); } } if (delta.contains("content") && delta.at("content").is_string()) { - const std::string text = delta.at("content").get(); + const std::string text = delta.at("content"); if (!text.empty()) { content_out.content += text; a.push(ui::ASSISTANT_DISPLAY_MODE_CONTENT, text); diff --git a/tools/server/server-chat.cpp b/tools/server/server-chat.cpp index a6fe3c6ba6..2411ec7089 100644 --- a/tools/server/server-chat.cpp +++ b/tools/server/server-chat.cpp @@ -679,12 +679,12 @@ json convert_transcriptions_to_chatcmpl( chatcmpl_body["stream"] = stream == "true"; if (inp_body.contains("max_tokens")) { - std::string inp = inp_body["max_tokens"].get(); + std::string inp = inp_body["max_tokens"]; chatcmpl_body["max_tokens"] = std::stoul(inp); } if (inp_body.contains("temperature")) { - std::string inp = inp_body["temperature"].get(); + std::string inp = inp_body["temperature"]; chatcmpl_body["temperature"] = std::stof(inp); } diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp index 1df9e4a1af..b78cbf07f7 100644 --- a/tools/server/server-context.cpp +++ b/tools/server/server-context.cpp @@ -5207,7 +5207,7 @@ void server_routes::init_routes() { std::unique_ptr server_routes::handle_slots_save(const server_http_req & req, int id_slot) { auto res = create_response(); const json request_data = json::parse(req.body); - std::string filename = request_data.at("filename").get(); + std::string filename = request_data.at("filename"); if (!fs_validate_filename(filename)) { res->error(format_error_response("Invalid filename", ERROR_TYPE_INVALID_REQUEST)); return res; @@ -5243,7 +5243,7 @@ std::unique_ptr server_routes::handle_slots_save(const ser std::unique_ptr server_routes::handle_slots_restore(const server_http_req & req, int id_slot) { auto res = create_response(); const json request_data = json::parse(req.body); - std::string filename = request_data.at("filename").get(); + std::string filename = request_data.at("filename"); if (!fs_validate_filename(filename)) { res->error(format_error_response("Invalid filename", ERROR_TYPE_INVALID_REQUEST)); return res; @@ -5332,7 +5332,7 @@ std::unique_ptr server_routes::handle_embeddings_impl(cons bool use_base64 = false; if (body.count("encoding_format") != 0) { - const std::string format = body.at("encoding_format").get(); + const std::string format = body.at("encoding_format"); if (format == "base64") { use_base64 = true; } else if (format != "float") { diff --git a/tools/server/server-http.cpp b/tools/server/server-http.cpp index 2ec137aa07..249e71b416 100644 --- a/tools/server/server-http.cpp +++ b/tools/server/server-http.cpp @@ -785,7 +785,7 @@ void server_http_context::register_gcp_compat() const { try { json payload = instance; - const std::string format = payload.at("@requestFormat").get(); + const std::string format = payload.at("@requestFormat"); payload.erase("@requestFormat"); if (payload.contains("stream")) { diff --git a/tools/server/server-schema.cpp b/tools/server/server-schema.cpp index 7b368ef788..914c2b7021 100644 --- a/tools/server/server-schema.cpp +++ b/tools/server/server-schema.cpp @@ -306,7 +306,7 @@ std::vector> make_llama_cmpl_schema(const common_params & add((new field_str("generation_prompt")) ->set_desc("Generation prompt appended to the chat template output") ->set_handler([&](field_eval_context & ctx, const json & data) { - std::string s = data.at("generation_prompt").get(); + std::string s = data.at("generation_prompt"); ctx.params.chat_parser_params.generation_prompt = s; ctx.params.sampling.generation_prompt = s; })); @@ -399,13 +399,13 @@ std::vector> make_llama_cmpl_schema(const common_params & ctx.params.sampling.reasoning_budget_end.clear(); if (data.contains("reasoning_budget_end_tags")) { for (const auto & t : data.at("reasoning_budget_end_tags")) { - std::string tag = t.get(); + std::string tag = t; if (!tag.empty()) { ctx.params.sampling.reasoning_budget_end.push_back(common_tokenize(ctx.vocab, tag, false, true)); } } } else if (data.contains("reasoning_budget_end_tag")) { - std::string tag = data.at("reasoning_budget_end_tag").get(); + std::string tag = data.at("reasoning_budget_end_tag"); if (!tag.empty()) { ctx.params.sampling.reasoning_budget_end.push_back(common_tokenize(ctx.vocab, tag, false, true)); } diff --git a/tools/server/server-stream.cpp b/tools/server/server-stream.cpp index f6b9b8a9f4..a591fa27f5 100644 --- a/tools/server/server-stream.cpp +++ b/tools/server/server-stream.cpp @@ -510,7 +510,7 @@ server_http_context::handler_t server_stream_make_lookup_handler() { if (body.contains("conversation_ids") && body["conversation_ids"].is_array()) { for (const auto & v : body["conversation_ids"]) { if (v.is_string()) { - std::string id = v.get(); + std::string id = v; if (!id.empty()) { requested.push_back(std::move(id)); } diff --git a/tools/server/server-tools.cpp b/tools/server/server-tools.cpp index 12e9dbb8cf..82553fe01d 100644 --- a/tools/server/server-tools.cpp +++ b/tools/server/server-tools.cpp @@ -896,7 +896,7 @@ struct server_tool_read_file : server_tool { } json invoke(json params, server_tool::stream *) const override { - std::string path = params.at("path").get(); + std::string path = params.at("path"); int start_line = json_value(params, "start_line", 1); int end_line = json_value(params, "end_line", -1); // -1 = no limit bool append_loc = json_value(params, "append_loc", false); @@ -1015,7 +1015,7 @@ struct server_tool_file_glob_search : server_tool { json invoke(json params, server_tool::stream *) const override { auto io = make_tools_io(params); - const std::string path = params.at("path").get(); + const std::string path = params.at("path"); std::string base = io->resolve(path); std::string include = json_value(params, "include", std::string("**")); @@ -1128,8 +1128,8 @@ struct server_tool_grep_search : server_tool { } json invoke(json params, server_tool::stream *) const override { - std::string path = params.at("path").get(); - std::string pat_str = params.at("pattern").get(); + std::string path = params.at("path"); + std::string pat_str = params.at("pattern"); std::string include = json_value(params, "include", std::string("**")); std::string exclude = json_value(params, "exclude", std::string("")); bool show_lineno = json_value(params, "return_line_numbers", false); @@ -1271,7 +1271,7 @@ struct server_tool_exec_shell_command : server_tool { } json invoke(json params, server_tool::stream * st) const override { - std::string command = params.at("command").get(); + std::string command = params.at("command"); int timeout = json_value(params, "timeout", 10); size_t max_output = (size_t) json_value(params, "max_output_size", (int) SERVER_TOOL_EXEC_SHELL_COMMAND_MAX_OUTPUT_SIZE); @@ -1348,8 +1348,8 @@ struct server_tool_write_file : server_tool { } json invoke(json params, server_tool::stream *) const override { - std::string path = params.at("path").get(); - std::string content = params.at("content").get(); + std::string path = params.at("path"); + std::string content = params.at("content"); auto io = make_tools_io(params); if (!io->write_file(path, content)) { @@ -1405,7 +1405,7 @@ struct server_tool_edit_file : server_tool { } json invoke(json params, server_tool::stream *) const override { - std::string path = params.at("path").get(); + std::string path = params.at("path"); const json & edits_json = params.at("edits"); if (!edits_json.is_array() || edits_json.empty()) { @@ -2078,7 +2078,7 @@ void server_tools::setup(const std::vector & enabled_tools, auto res = std::make_unique(); try { json body = json::parse(req.body); - std::string tool_name = body.at("tool").get(); + std::string tool_name = body.at("tool"); json params = body.value("params", json::object()); bool stream = body.value("stream", false);