Move base_url and model into HarnessConfig (issue 186)

- Added base_url and model fields to HarnessConfig in json_types.hpp
- Removed base_url and model from OllamaConfig in ollama_client.hpp
- Updated OllamaClient constructor to accept base_url and model as separate parameters
- Updated main.cpp to use HarnessConfig for base_url and model
- Updated agent_tool.cpp to use new OllamaClient constructor
- Added test_harness_config.sh to verify the new config fields
- Updated README.md to document the config fields
This commit is contained in:
2026-08-04 15:13:37 -05:00
parent 975069c9d0
commit 3e99b37683
8 changed files with 91 additions and 18 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ to it)
* **`Client Layer`**: * **`Client Layer`**:
* `OllamaClient`: Handles the actual communication with an Ollama-compatible API server via HTTP (using `libcurl`). * `OllamaClient`: Handles the actual communication with an Ollama-compatible API server via HTTP (using `libcurl`).
* `DebugClient`: A CLI-friendly mock client that reads a mock JSON response from `stdin`, allowing for deterministic testing of harness logic without relying on an external LLM. * `DebugClient`: A CLI-friendly mock client that reads a mock JSON response from `stdin`, allowing for deterministic testing of harness logic without relying on an external LLM.
* **State Management**: Conversations are serialized to and deserialized from JSON files. This includes the full message history (`history`), the active tool registry (`tools`), and harness configuration (`config`). * **State Management**: Conversations are serialized to and deserialized from JSON files. This includes the full message history (`history`), the active tool registry (`tools`), and harness configuration (`config` including `base_url`, `model`, `max_tool_iterations`, `verbose`, and `depth`).
## 🛠️ How to Build ## 🛠️ How to Build
+3 -1
View File
@@ -362,8 +362,10 @@ struct HarnessConfig {
size_t max_tool_iterations = 16; size_t max_tool_iterations = 16;
bool verbose = false; bool verbose = false;
size_t depth = 0; size_t depth = 0;
std::string base_url = "https://192.168.8.63/ollama";
std::string model = "qwen3.6:35b-a3b-128k";
}; };
DRH_DEFINE_TYPE_OPTIONAL(HarnessConfig, max_tool_iterations, verbose, depth) DRH_DEFINE_TYPE_OPTIONAL(HarnessConfig, max_tool_iterations, verbose, depth, base_url, model)
struct FunctionCall { struct FunctionCall {
std::string name = ""; std::string name = "";
+3 -3
View File
@@ -9,15 +9,13 @@
using http_req_cb_t = std::function<std::string(std::string const& old, std::string const& new_str)>; using http_req_cb_t = std::function<std::string(std::string const& old, std::string const& new_str)>;
struct OllamaConfig { struct OllamaConfig {
std::string base_url = "https://192.168.8.63/ollama";
std::string model = "qwen3.6:35b-a3b-128k";
std::string auth_token = ""; std::string auth_token = "";
int timeout_s = 60*10; int timeout_s = 60*10;
}; };
class OllamaClient : public Client { class OllamaClient : public Client {
public: public:
explicit OllamaClient(OllamaConfig config = {}); explicit OllamaClient(std::string base_url, std::string model, OllamaConfig config = {});
~OllamaClient() override; ~OllamaClient() override;
std::expected<Message, std::string> chat(const std::vector<Message>& messages, std::expected<Message, std::string> chat(const std::vector<Message>& messages,
@@ -26,6 +24,8 @@ public:
std::expected<std::vector<std::string>, std::string> list_models() const override; std::expected<std::vector<std::string>, std::string> list_models() const override;
private: private:
std::string base_url_;
std::string model_;
OllamaConfig config_; OllamaConfig config_;
nlohmann::json build_body(const std::vector<Message>& messages, nlohmann::json build_body(const std::vector<Message>& messages,
const std::vector<Tool>& tools) const; const std::vector<Tool>& tools) const;
+10 -4
View File
@@ -97,10 +97,11 @@ static HarnessState read_harness_state_or_exit(const char* file_path) {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
OllamaConfig cfg; HarnessConfig cfg;
OllamaConfig ollama_cfg;
if (const char* m = std::getenv("OLLAMA_MODEL")) cfg.model = m; if (const char* m = std::getenv("OLLAMA_MODEL")) cfg.model = m;
if (const char* u = std::getenv("OLLAMA_URL")) cfg.base_url = u; if (const char* u = std::getenv("OLLAMA_URL")) cfg.base_url = u;
if (const char* a = std::getenv("OLLAMA_AUTHORIZATION_TOKEN")) cfg.auth_token = a; if (const char* a = std::getenv("OLLAMA_AUTHORIZATION_TOKEN")) ollama_cfg.auth_token = a;
bool use_debug = false; bool use_debug = false;
bool debug_paths = false; bool debug_paths = false;
@@ -417,7 +418,7 @@ int main(int argc, char** argv) {
auto create_client = [&](bool debug) -> std::unique_ptr<Client> { auto create_client = [&](bool debug) -> std::unique_ptr<Client> {
if (debug) if (debug)
return std::make_unique<DebugClient>(); return std::make_unique<DebugClient>();
return std::make_unique<OllamaClient>(cfg); return std::make_unique<OllamaClient>(cfg.base_url, cfg.model, ollama_cfg);
}; };
// Scan for skills // Scan for skills
@@ -647,7 +648,12 @@ int main(int argc, char** argv) {
} }
} }
Harness harness(create_client(use_debug), agent_tools, {.max_tool_iterations = 1000, .verbose = true}); Harness harness(create_client(use_debug), agent_tools, HarnessConfig{
.max_tool_iterations = 1000,
.verbose = true,
.base_url = cfg.base_url,
.model = cfg.model
});
if (!start_new) if (!start_new)
{ {
+5 -4
View File
@@ -7,7 +7,8 @@
static int ollama_clients = 0; static int ollama_clients = 0;
OllamaClient::OllamaClient(OllamaConfig config) : config_(std::move(config)) { OllamaClient::OllamaClient(std::string base_url, std::string model, OllamaConfig config)
: base_url_(std::move(base_url)), model_(std::move(model)), config_(std::move(config)) {
if (ollama_clients == 0) if (ollama_clients == 0)
curl_global_init(CURL_GLOBAL_DEFAULT); curl_global_init(CURL_GLOBAL_DEFAULT);
@@ -98,7 +99,7 @@ std::expected<std::string, std::string> OllamaClient::http_request(const std::st
nlohmann::json OllamaClient::build_body(const std::vector<Message>& messages, nlohmann::json OllamaClient::build_body(const std::vector<Message>& messages,
const std::vector<Tool>& tools) const { const std::vector<Tool>& tools) const {
nlohmann::json body = { nlohmann::json body = {
{"model", config_.model}, {"model", model_},
{"stream", true}, {"stream", true},
{"messages", messages}, {"messages", messages},
{"stream_options", { {"stream_options", {
@@ -119,7 +120,7 @@ std::expected<Message, std::string> OllamaClient::chat(const std::vector<Message
const std::vector<Tool>& tools) const { const std::vector<Tool>& tools) const {
nlohmann::json body = build_body(messages, tools); nlohmann::json body = build_body(messages, tools);
std::string body_str = body.dump(); std::string body_str = body.dump();
std::string url = config_.base_url + "/v1/chat/completions"; std::string url = base_url_ + "/v1/chat/completions";
Message total{}; Message total{};
Usage usage{}; Usage usage{};
@@ -227,7 +228,7 @@ std::expected<Message, std::string> OllamaClient::chat(const std::vector<Message
} }
std::expected<std::vector<std::string>, std::string> OllamaClient::list_models() const { std::expected<std::vector<std::string>, std::string> OllamaClient::list_models() const {
std::string url = config_.base_url + "/api/tags"; std::string url = base_url_ + "/api/tags";
auto response_result = http_request(url, "GET", ""); auto response_result = http_request(url, "GET", "");
if (!response_result) { if (!response_result) {
return std::unexpected(response_result.error()); return std::unexpected(response_result.error());
+3
View File
@@ -160,3 +160,6 @@ add_dr_test(test_print_history_multimodal)
# Add encoding tests # Add encoding tests
add_dr_test(test_encoding) add_dr_test(test_encoding)
# Add harness config tests (base_url and model in HarnessConfig)
add_dr_test(test_harness_config)
+61
View File
@@ -0,0 +1,61 @@
#!/bin/bash
# test_harness_config.sh - Tests for HarnessConfig with base_url and model fields
# Test 1: Verify config is saved with base_url and model in new conversations
echo '{"choices":[{"delta":{"content":"Response","role":"assistant"},"finish_reason":"stop"}],"usage":{"completion_tokens":10,"prompt_tokens":5,"total_tokens":15}}' | "${DRHARNESS}" \
-F "${TEST_TEMP_DIR}/chat.json" \
-n \
-d \
-m "test" 2>/dev/null
# Verify config contains base_url and model
BASE_URL=$(cat "${TEST_TEMP_DIR}/chat.json" | jq -r '.config.base_url')
MODEL=$(cat "${TEST_TEMP_DIR}/chat.json" | jq -r '.config.model')
if [ -z "$BASE_URL" ] || [ "$BASE_URL" = "null" ]; then
echo "FAIL: Expected config.base_url to be set"
exit 1
fi
if [ -z "$MODEL" ] || [ "$MODEL" = "null" ]; then
echo "FAIL: Expected config.model to be set"
exit 1
fi
# Test 2: Verify -M flag sets model in config
echo '{"choices":[{"delta":{"content":"Response","role":"assistant"},"finish_reason":"stop"}],"usage":{"completion_tokens":10,"prompt_tokens":5,"total_tokens":15}}' | "${DRHARNESS}" \
-F "${TEST_TEMP_DIR}/chat2.json" \
-n \
-d \
-M "custom-model" \
-m "test" 2>/dev/null
MODEL2=$(cat "${TEST_TEMP_DIR}/chat2.json" | jq -r '.config.model')
if [ "$MODEL2" != "custom-model" ]; then
echo "FAIL: Expected config.model to be 'custom-model', got '${MODEL2}'"
exit 1
fi
# Test 3: Verify backward compatibility - loading file without base_url and model
echo '{"history":[{"role":"user","content":"hello"}],"tools":[],"config":{"max_tool_iterations":16,"verbose":false,"depth":0}}' > "${TEST_TEMP_DIR}/old_format.json"
echo '{"choices":[{"delta":{"content":"Response","role":"assistant"},"finish_reason":"stop"}],"usage":{"completion_tokens":10,"prompt_tokens":5,"total_tokens":15}}' | "${DRHARNESS}" \
-F "${TEST_TEMP_DIR}/old_format.json" \
-d \
-m "test" 2>/dev/null
# Verify the saved file now has base_url and model with defaults
SAVED_BASE_URL=$(cat "${TEST_TEMP_DIR}/old_format.json" | jq -r '.config.base_url')
SAVED_MODEL=$(cat "${TEST_TEMP_DIR}/old_format.json" | jq -r '.config.model')
if [ -z "$SAVED_BASE_URL" ] || [ "$SAVED_BASE_URL" = "null" ]; then
echo "FAIL: Expected saved config.base_url to be set"
exit 1
fi
if [ -z "$SAVED_MODEL" ] || [ "$SAVED_MODEL" = "null" ]; then
echo "FAIL: Expected saved config.model to be set"
exit 1
fi
echo "All harness config tests passed"
+5 -5
View File
@@ -48,14 +48,14 @@ Tool make_agent_tool(const AgentProfile& profile,
} }
ToolRegistry sub_registry = std::move(sub_reg_result.value()); ToolRegistry sub_registry = std::move(sub_reg_result.value());
Harness harness(std::make_unique<OllamaClient>(OllamaConfig{ Harness harness(std::make_unique<OllamaClient>(base_url, profile.model, OllamaConfig{
.base_url = base_url,
.model = profile.model,
.timeout_s = 120 .timeout_s = 120
}), sub_registry, { }), sub_registry, HarnessConfig{
.max_tool_iterations = 16, .max_tool_iterations = 16,
.verbose = true, .verbose = true,
.depth = depth .depth = depth,
.base_url = base_url,
.model = profile.model
}); });
auto result = harness.chat(task); auto result = harness.chat(task);