Merge pull request 'feature/save-model-between-chats' (#52) from feature/save-model-between-chats into master
Reviewed-on: https://192.168.8.63/git/darthrubik/drharness/pulls/52
This commit was merged in pull request #52.
This commit is contained in:
@@ -17,7 +17,7 @@ to it)
|
||||
* **`Client Layer`**:
|
||||
* `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.
|
||||
* **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
|
||||
|
||||
|
||||
@@ -362,8 +362,10 @@ struct HarnessConfig {
|
||||
size_t max_tool_iterations = 16;
|
||||
bool verbose = false;
|
||||
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 {
|
||||
std::string name = "";
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
using http_req_cb_t = std::function<std::string(std::string const& old, std::string const& new_str)>;
|
||||
|
||||
struct OllamaConfig {
|
||||
std::string base_url = "https://192.168.8.63/ollama";
|
||||
std::string model = "qwen3.6:35b-a3b-128k";
|
||||
std::string auth_token = "";
|
||||
int timeout_s = 60*10;
|
||||
};
|
||||
|
||||
class OllamaClient : public Client {
|
||||
public:
|
||||
explicit OllamaClient(OllamaConfig config = {});
|
||||
explicit OllamaClient(std::string base_url, std::string model, OllamaConfig config = {});
|
||||
~OllamaClient() override;
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
std::string base_url_;
|
||||
std::string model_;
|
||||
OllamaConfig config_;
|
||||
nlohmann::json build_body(const std::vector<Message>& messages,
|
||||
const std::vector<Tool>& tools) const;
|
||||
|
||||
+13
-8
@@ -140,9 +140,13 @@ static HarnessState read_harness_state_or_exit(const char* file_path) {
|
||||
int main(int argc, char** argv) {
|
||||
setup_signal_handler();
|
||||
|
||||
HarnessConfig harnessConfig = HarnessConfig{
|
||||
.max_tool_iterations = 1000,
|
||||
.verbose = true,
|
||||
};
|
||||
OllamaConfig cfg;
|
||||
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* m = std::getenv("OLLAMA_MODEL")) harnessConfig.model = m;
|
||||
if (const char* u = std::getenv("OLLAMA_URL")) harnessConfig.base_url = u;
|
||||
if (const char* a = std::getenv("OLLAMA_AUTHORIZATION_TOKEN")) cfg.auth_token = a;
|
||||
|
||||
bool use_debug = false;
|
||||
@@ -183,7 +187,7 @@ int main(int argc, char** argv) {
|
||||
// Add more base tools here:
|
||||
|
||||
for (const auto& profile : profiles)
|
||||
expect_or_exit(master_registery.register_tool(make_agent_tool(profile, master_registery, cfg.base_url)));
|
||||
expect_or_exit(master_registery.register_tool(make_agent_tool(profile, master_registery, harnessConfig.base_url)));
|
||||
|
||||
auto consume_arg = [&](){
|
||||
argc--;
|
||||
@@ -247,7 +251,7 @@ int main(int argc, char** argv) {
|
||||
else if (argv[0] == std::string("-M"))
|
||||
{
|
||||
consume_arg();
|
||||
cfg.model = consume_arg();
|
||||
harnessConfig.model = consume_arg();
|
||||
}
|
||||
else if (argv[0] == std::string("-d"))
|
||||
{
|
||||
@@ -460,7 +464,7 @@ int main(int argc, char** argv) {
|
||||
auto create_client = [&](bool debug) -> std::unique_ptr<Client> {
|
||||
if (debug)
|
||||
return std::make_unique<DebugClient>();
|
||||
return std::make_unique<OllamaClient>(cfg);
|
||||
return std::make_unique<OllamaClient>(harnessConfig.base_url, harnessConfig.model, cfg);
|
||||
};
|
||||
|
||||
// Scan for skills
|
||||
@@ -690,16 +694,17 @@ 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);
|
||||
|
||||
if (!start_new)
|
||||
{
|
||||
HarnessState state_data = read_harness_state_or_exit(file_to_open);
|
||||
harnessConfig = state_data.config;
|
||||
harness = Harness(create_client(use_debug), master_registery, state_data);
|
||||
}
|
||||
|
||||
std::cerr << "drharness v0.3 | model: " << cfg.model
|
||||
<< " | " << cfg.base_url << "\n\n";
|
||||
std::cerr << "drharness v0.3 | model: " << harnessConfig.model
|
||||
<< " | " << harnessConfig.base_url << "\n\n";
|
||||
|
||||
std::string final_prompt;
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
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)
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
|
||||
@@ -121,7 +122,7 @@ std::expected<std::string, std::string> OllamaClient::http_request(const std::st
|
||||
nlohmann::json OllamaClient::build_body(const std::vector<Message>& messages,
|
||||
const std::vector<Tool>& tools) const {
|
||||
nlohmann::json body = {
|
||||
{"model", config_.model},
|
||||
{"model", model_},
|
||||
{"stream", true},
|
||||
{"messages", messages},
|
||||
{"stream_options", {
|
||||
@@ -142,7 +143,7 @@ std::expected<Message, std::string> OllamaClient::chat(const std::vector<Message
|
||||
const std::vector<Tool>& tools) const {
|
||||
nlohmann::json body = build_body(messages, tools);
|
||||
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{};
|
||||
Usage usage{};
|
||||
@@ -250,7 +251,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::string url = config_.base_url + "/api/tags";
|
||||
std::string url = base_url_ + "/api/tags";
|
||||
auto response_result = http_request(url, "GET", "");
|
||||
if (!response_result) {
|
||||
return std::unexpected(response_result.error());
|
||||
|
||||
@@ -162,3 +162,6 @@ add_dr_test(test_print_history_multimodal)
|
||||
|
||||
# Add encoding tests
|
||||
add_dr_test(test_encoding)
|
||||
|
||||
# Add harness config tests (base_url and model in HarnessConfig)
|
||||
add_dr_test(test_harness_config)
|
||||
|
||||
@@ -455,6 +455,20 @@ function dr_content_text() {
|
||||
}
|
||||
export -f dr_content_text
|
||||
|
||||
# Get base_url from config in chat file
|
||||
function dr_config_base_url() {
|
||||
local chat_file="$1"
|
||||
jq -r '.config.base_url' "$chat_file"
|
||||
}
|
||||
export -f dr_config_base_url
|
||||
|
||||
# Get model from config in chat file
|
||||
function dr_config_model() {
|
||||
local chat_file="$1"
|
||||
jq -r '.config.model' "$chat_file"
|
||||
}
|
||||
export -f dr_config_model
|
||||
|
||||
# Resolve paths relative to this script
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
PROJECT_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
|
||||
|
||||
Executable
+61
@@ -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 "$(be_done "Response")" | "${DRHARNESS}" \
|
||||
-F "${TEST_TEMP_DIR}/chat.json" \
|
||||
-n \
|
||||
-d \
|
||||
-m "test" 2>/dev/null
|
||||
|
||||
# Verify config contains base_url and model
|
||||
BASE_URL=$(dr_config_base_url "${TEST_TEMP_DIR}/chat.json")
|
||||
MODEL=$(dr_config_model "${TEST_TEMP_DIR}/chat.json")
|
||||
|
||||
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 "$(be_done "Response")" | "${DRHARNESS}" \
|
||||
-F "${TEST_TEMP_DIR}/chat2.json" \
|
||||
-n \
|
||||
-d \
|
||||
-M "custom-model" \
|
||||
-m "test" 2>/dev/null
|
||||
|
||||
MODEL2=$(dr_config_model "${TEST_TEMP_DIR}/chat2.json")
|
||||
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
|
||||
create_state "${TEST_TEMP_DIR}/old_format.json" user "hello"
|
||||
|
||||
echo "$(be_done "Response")" | "${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=$(dr_config_base_url "${TEST_TEMP_DIR}/old_format.json")
|
||||
SAVED_MODEL=$(dr_config_model "${TEST_TEMP_DIR}/old_format.json")
|
||||
|
||||
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"
|
||||
@@ -48,14 +48,14 @@ Tool make_agent_tool(const AgentProfile& profile,
|
||||
}
|
||||
ToolRegistry sub_registry = std::move(sub_reg_result.value());
|
||||
|
||||
Harness harness(std::make_unique<OllamaClient>(OllamaConfig{
|
||||
.base_url = base_url,
|
||||
.model = profile.model,
|
||||
Harness harness(std::make_unique<OllamaClient>(base_url, profile.model, OllamaConfig{
|
||||
.timeout_s = 120
|
||||
}), sub_registry, {
|
||||
}), sub_registry, HarnessConfig{
|
||||
.max_tool_iterations = 16,
|
||||
.verbose = true,
|
||||
.depth = depth
|
||||
.depth = depth,
|
||||
.base_url = base_url,
|
||||
.model = profile.model
|
||||
});
|
||||
|
||||
auto result = harness.chat(task);
|
||||
|
||||
Reference in New Issue
Block a user