Changes
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index d5b5028..0000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,113 +0,0 @@
-cmake_minimum_required(VERSION 3.24)
-project(AgentSmith)
-
-set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
-set(CMAKE_CXX_STANDARD 23)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS FALSE)
-
-include(FetchContent)
-
-FetchContent_Declare(
- libmw
- GIT_REPOSITORY https://github.com/MetroWind/libmw.git
-)
-
-FetchContent_Declare(
- spdlog
- GIT_REPOSITORY https://github.com/gabime/spdlog.git
- GIT_TAG v1.12.0
-)
-
-FetchContent_Declare(
- json
- GIT_REPOSITORY https://github.com/nlohmann/json.git
- GIT_TAG v3.11.3
-)
-
-FetchContent_Declare(
- cxxopts
- GIT_REPOSITORY https://github.com/jarro2783/cxxopts.git
- GIT_TAG v3.1.1
-)
-
-set(SPDLOG_USE_STD_FORMAT ON)
-set(LIBMW_BUILD_URL ON)
-set(LIBMW_BUILD_SQLITE ON)
-set(LIBMW_BUILD_HTTP_SERVER OFF)
-set(LIBMW_BUILD_CRYPTO OFF)
-
-FetchContent_MakeAvailable(libmw spdlog json cxxopts)
-
-set(LIB_SOURCE_FILES
- src/agent.cpp
- src/llm_client.cpp
- src/memory.cpp
- src/tool.cpp
- src/message.cpp
-)
-
-set(LIBS
- mw::mw
- mw::url
- mw::sqlite
- spdlog::spdlog
- nlohmann_json::nlohmann_json
-)
-
-set(INCLUDES
- ${libmw_SOURCE_DIR}/includes
- ${json_SOURCE_DIR}/single_include
-)
-
-# 1. Build the Agent Smith Library
-add_library(agent_smith ${LIB_SOURCE_FILES})
-set_property(TARGET agent_smith PROPERTY CXX_STANDARD 23)
-target_compile_options(agent_smith PRIVATE -Wall -Wextra)
-target_include_directories(agent_smith PUBLIC ${INCLUDES} src)
-target_link_libraries(agent_smith PUBLIC ${LIBS})
-
-# 2. Build the Example CLI Executable
-add_executable(agent_smith_cli src/main.cpp)
-set_property(TARGET agent_smith_cli PROPERTY CXX_STANDARD 23)
-target_compile_options(agent_smith_cli PRIVATE -Wall -Wextra)
-target_include_directories(agent_smith_cli PRIVATE ${cxxopts_SOURCE_DIR}/include)
-target_link_libraries(agent_smith_cli PRIVATE agent_smith cxxopts::cxxopts)
-
-option(AGENT_SMITH_BUILD_TESTS "Build unit tests" ON)
-
-if(AGENT_SMITH_BUILD_TESTS)
- FetchContent_Declare(
- googletest
- URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz
- )
- FetchContent_MakeAvailable(googletest)
-
- set(TEST_FILES
- src/task_test.cpp
- src/memory_test.cpp
- src/tool_test.cpp
- src/agent_test.cpp
- src/llm_client_test.cpp
- src/message_test.cpp
- )
-
- add_executable(agent_smith_test ${TEST_FILES})
-
- set_property(TARGET agent_smith_test PROPERTY CXX_STANDARD 23)
- target_compile_options(agent_smith_test PRIVATE -Wall -Wextra)
- target_include_directories(agent_smith_test PRIVATE
- ${googletest_SOURCE_DIR}/googletest/include
- ${googletest_SOURCE_DIR}/googlemock/include
- )
-
- target_link_libraries(agent_smith_test PRIVATE
- agent_smith
- GTest::gtest_main
- GTest::gmock_main
- )
-
- enable_testing()
- include(GoogleTest)
- gtest_discover_tests(agent_smith_test)
-endif()
diff --git a/README.md b/README.md
deleted file mode 100644
index 896ae4d..0000000
--- a/README.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Agent Smith 🕶️
-
-**Agent Smith** is a high-performance, modular C++23 framework for building LLM-powered agents.
-
-Designed with composability and safety in mind, it provides a clean, dependency-injectable architecture for orchestrating LLM interactions, managing conversation history, and natively executing tools/functions.
-
-## Key Features
-
-* **Modular Architecture:** Swap out LLM clients, memory storage backends, and tool registries effortlessly.
-* **Native Tool Calling:** Fully supports structured JSON tool calling, paving the way for seamless Model Context Protocol (MCP) integrations.
-* **Exception-Free Error Handling:** Built heavily around C++23 `std::expected` (via `mw::E<T>`), ensuring robust network and execution error propagation without crashing your application.
-* **Async-Ready:** Core loops and LLM executions return `Task<T>` coroutine wrappers, ready for integration into modern async event loops.
-* **Lightweight Foundation:** Powered by `libmw` for everyday HTTP networking and data handling, alongside `nlohmann/json` and `spdlog`.
-
-## Integration
-
-Agent Smith is designed to be consumed as a CMake library. The easiest way to add it to your project is via `FetchContent`.
-
-### CMakeLists.txt
-
-```cmake
-include(FetchContent)
-
-FetchContent_Declare(
- agent_smith
- GIT_REPOSITORY https://github.com/YourOrg/agent-smith.git
- # GIT_TAG main
-)
-
-FetchContent_MakeAvailable(agent_smith)
-
-add_executable(my_agent src/main.cpp)
-target_link_libraries(my_agent PRIVATE agent_smith)
-```
-
-## Quick Start Example
-
-Here is a minimal example of how to configure an OpenAI-compatible client, define a custom tool, and run an agent loop.
-
-```cpp
-#include <iostream>
-#include <memory>
-#include <agent.hpp>
-#include <llm_client.hpp>
-#include <memory.hpp>
-#include <tool.hpp>
-
-// 1. Define a custom tool
-class MyCalculator : public Tool {
-public:
- std::string name() const override { return "calculator"; }
- std::string description() const override { return "Performs basic addition."; }
-
- nlohmann::json parametersSchema() const override {
- return R"({
- "type": "object",
- "properties": {
- "a": {"type": "number"},
- "b": {"type": "number"}
- },
- "required": ["a", "b"]
- })"_json;
- }
-
- Task<mw::E<std::string>> execute(const nlohmann::json& args) override {
- double a = args["a"];
- double b = args["b"];
- co_return std::to_string(a + b);
- }
-};
-
-// Simple blocking runner for coroutines
-void runTask(Task<mw::E<std::string>> task) {
- auto res = task.get();
- if(res.has_value()) std::cout << "Agent: " << res.value() << "
-";
- else std::cout << "Error: " << mw::errorMsg(res.error()) << "
-";
-}
-
-int main() {
- // 2. Setup the infrastructure
- auto client = std::make_unique<OpenAiClient>("your-api-key");
- auto memory = std::make_unique<InMemoryMemory>();
- ToolRegistry registry;
-
- registry.registerTool(std::make_unique<MyCalculator>());
-
- // 3. Instantiate the agent
- Agent agent(std::move(client), std::move(memory), registry);
- agent.allowTool("calculator");
-
- Skill math_skill{
- "math_bot",
- "You are a helpful math bot. Use your tools to answer questions.",
- {"calculator"}
- };
- agent.activateSkill(math_skill);
-
- // 4. Run the conversational loop
- runTask(agent.run("What is 42 + 8?"));
-
- return 0;
-}
-```
-
-## Built-in CLI
-
-The repository also builds an interactive CLI executable (`agent_smith_cli`) out of the box, which serves as a great reference implementation.
-
-```bash
-# Build the CLI and tests
-cmake -S . -B build
-cmake --build build -j
-
-# Run the CLI using a custom local endpoint
-./build/agent_smith_cli --api-key "dummy" --endpoint "http://localhost:8080/v1" --model "local-model"
-```
-
-## Dependencies
-
-* **C++ Standard:** C++23
-* **Build System:** CMake >= 3.24
-* [libmw](https://github.com/MetroWind/libmw) (HTTP client, SQLite, core utils)
-* [nlohmann/json](https://github.com/nlohmann/json)
-* [spdlog](https://github.com/gabime/spdlog)
-* [cxxopts](https://github.com/jarro2783/cxxopts) (CLI only)
-* [GoogleTest](https://github.com/google/googletest) (Tests only)
diff --git a/design.md b/design.md
deleted file mode 100644
index 61a57ec..0000000
--- a/design.md
+++ /dev/null
@@ -1,311 +0,0 @@
-# Technical Design Document: Agent Smith Framework
-
-## 1. Introduction
-This document outlines the technical design for **Agent Smith**, a modular C++23 framework for building LLM-powered agents. It is designed to be highly efficient and leverages `libmw` for everyday system tasks, networking, and data handling.
-
-This guide is structured to help a new graduate software engineer understand the architecture and implement the system systematically.
-
----
-
-## 2. High-Level Architecture
-The framework relies on a decoupled architecture centered around the `Agent` class.
-
-* **`Agent`**: The orchestrator. It manages the conversational loop, maintains state via `Memory`, interacts with the `LlmClient`, and dynamically executes `Tools` and `Skills`.
-* **`LlmClient`**: Handles network communication with LLM APIs (OpenAI and Gemini) using `mw::url`.
-* **`Memory`**: Stores the conversation history. Can be in-memory or backed by `mw::sqlite` for persistence.
-* **`Tool`**: An interface representing a callable action. Includes native tools and integrations via the Model Context Protocol (MCP).
-* **`Skill`**: A configuration that bundles a specific system prompt and a subset of tools to focus the agent on a specific workflow.
-* **CLI**: A command-line interface implemented using `cxxopts` to allow users to configure the agent's connection parameters (API key and endpoint) at runtime.
-
----
-
-## 3. Data Models
-
-We use `nlohmann/json` (provided via `libmw`) for all dynamic data structures and serialization.
-
-```cpp
-#include <string>
-#include <optional>
-#include <vector>
-#include <variant>
-#include <nlohmann/json.hpp>
-
-// 1. System Message: Defines the agent's persona and overarching rules.
-struct SystemMessage
-{
- std::string content;
-};
-
-// 2. User Message: Input provided by the human user.
-struct UserMessage
-{
- std::string content;
-};
-
-// Represents a single tool call requested by the Assistant
-struct ToolCall
-{
- std::string id;
- std::string name;
- nlohmann::json arguments;
-};
-
-// 3. Assistant Message: The LLM's response. Can contain text or tool calls.
-struct AssistantMessage
-{
- std::optional<std::string> content;
- std::vector<ToolCall> tool_calls;
-};
-
-// 4. Tool Result Message: The result of executing a tool, sent back to LLM.
-struct ToolResultMessage
-{
- std::string tool_call_id;
- std::string content;
-};
-
-// The unified Message type using std::variant for compile-time type safety.
-using Message = std::variant<
- SystemMessage,
- UserMessage,
- AssistantMessage,
- ToolResultMessage>;
-
-// Standalone serialization functions for each message type
-nlohmann::json toJson(const SystemMessage& msg);
-nlohmann::json toJson(const UserMessage& msg);
-nlohmann::json toJson(const ToolCall& msg);
-nlohmann::json toJson(const AssistantMessage& msg);
-nlohmann::json toJson(const ToolResultMessage& msg);
-nlohmann::json toJson(const Message& msg);
-```
-
----
-
-## 4. Component Design
-
-### 4.1 LlmClient Interface
-The `LlmClient` abstracts the underlying LLM provider. This component uses `mw::E<>` for explicit error handling and C++23 coroutines for asynchronous task management.
-
-```cpp
-#include <mw/error.hpp>
-
-// A placeholder for a C++23 coroutine Task type.
-template<typename T> class Task;
-
-class LlmClient
-{
-public:
- virtual ~LlmClient() = default;
-
- // Submits the history and tools, returning the Assistant's response or an error
- virtual Task<mw::E<Message>> generateResponse(
- const std::vector<Message>& history,
- const nlohmann::json& available_tools_schema
- ) = 0;
-};
-```
-* **Implementation Steps:**
- * Implement `OpenAiClient`. Allow dependency injection of `mw::HTTPSessionInterface` (falling back to `mw::HTTPSession`) to enable rigorous mocked network testing.
- * Use `mw::URL` to safely parse custom endpoints and append standard paths (like `chat/completions`).
- * Deserialize the response body into the `Message` struct, utilizing `std::unexpected(mw::runtimeError(...))` on JSON parsing failures.
-
-### 4.2 Tool and MCP System
-A `Tool` must expose its JSON Schema so the LLM understands its parameters, and an execution method.
-
-```cpp
-class Tool
-{
-public:
- virtual ~Tool() = default;
- virtual std::string name() const = 0;
- virtual std::string description() const = 0;
-
- // Returns the JSON Schema describing the arguments the tool accepts
- virtual nlohmann::json parametersSchema() const = 0;
-
- // Executes the tool with the JSON arguments provided by the LLM
- virtual Task<mw::E<std::string>> execute(const nlohmann::json& arguments) = 0;
-};
-```
-
-**Tool Registry:**
-To support multiple agents and centralized tool management, tools are owned by a global or application-level `ToolRegistry`. Methods returning `mw::E<void>` signal success or report duplication errors.
-
-```cpp
-#include <unordered_map>
-#include <memory>
-#include <stdexcept>
-#include <format>
-
-class ToolRegistry
-{
-public:
- // Registers a tool. Throws an exception if the tool name already exists
- // to prevent silent collisions.
- void registerTool(std::unique_ptr<Tool> tool)
- {
- if(tools_.contains(tool->name()))
- {
- throw std::runtime_error(
- std::format("Tool '{}' is already registered.", tool->name()));
- }
- tools_[tool->name()] = std::move(tool);
- }
-
- // Registers a tool with a namespace prefix to avoid collisions
- // (e.g., from different MCP servers).
- void registerToolWithNamespace(
- const std::string& ns,
- std::unique_ptr<Tool> tool)
- {
- // Implementation detail: The Tool interface or a wrapper class
- // would need to return the new name when name() is called.
- std::string prefixed_name = std::format("{}_{}", ns, tool->name());
- if(tools_.contains(prefixed_name))
- {
- throw std::runtime_error(
- std::format("Tool '{}' is already registered.", prefixed_name));
- }
- // ... wrap tool and store with prefixed_name
- }
-
- Tool* getTool(const std::string& name) const
- {
- auto it = tools_.find(name);
- return it != tools_.end() ? it->second.get() : nullptr;
- }
-
- std::vector<Tool*> getAllTools() const
- {
- std::vector<Tool*> result;
- for(const auto& [name, tool] : tools_)
- {
- result.push_back(tool.get());
- }
- return result;
- }
-
-private:
- std::unordered_map<std::string, std::unique_ptr<Tool>> tools_;
-};
-```
-
-* **Handling Collisions & Namespacing:** LLMs require unique tool names. If an MCP server provides a tool named `search` and another server also provides `search`, the `ToolRegistry` will reject the second registration. To resolve this, external tools (like those from MCP) should be registered using `registerToolWithNamespace` (e.g., `github_search` vs `local_search`).
-* **MCP Integration:**
- * Implement an `McpClient` that can parse MCP server manifests.
- * The `McpClient` will act as a factory, dynamically generating `Tool` objects that map to remote MCP functions and registering them with the `ToolRegistry` using the server's name as a namespace.
-
-### 4.3 Agent Skills
-A `Skill` overrides the agent's default behavior, giving it a new persona and restricted capabilities.
-
-```cpp
-struct Skill
-{
- std::string name;
- std::string system_prompt;
- // Names of the tools allowed for this skill
- std::vector<std::string> allowed_tools;
-};
-```
-
-### 4.4 Memory Management
-Handles the context window.
-
-```cpp
-class Memory
-{
-public:
- virtual ~Memory() = default;
- virtual void addMessage(const Message& msg) = 0;
- virtual std::vector<Message> getHistory() const = 0;
- virtual void clear() = 0;
-};
-```
-* **Implementation Steps:**
- * Create `InMemoryMemory` (a simple `std::vector` wrapper).
- * Create `SqliteMemory` using `mw::sqlite` to persist `Message` structs to a database file.
-
-### 4.5 The Agent Core
-The `Agent` ties the components together in its main conversational loop.
-
-```cpp
-#include <memory>
-#include <vector>
-
-class Agent
-{
-public:
- Agent(
- std::unique_ptr<LlmClient> client,
- std::unique_ptr<Memory> memory,
- ToolRegistry& tool_registry);
-
- // Grants the agent permission to use a specific tool from the registry
- mw::E<void> allowTool(const std::string& tool_name);
- void activateSkill(const Skill& skill);
-
- // The main entry point for user interaction
- Task<mw::E<std::string>> run(const std::string& user_input);
-
-private:
- std::unique_ptr<LlmClient> client_;
- std::unique_ptr<Memory> memory_;
- ToolRegistry& tool_registry_;
-
- // List of tool names this agent is currently allowed to use
- std::vector<std::string> allowed_tools_;
- std::optional<Skill> current_skill_;
-};
-```
-
-**The `run` Loop Logic (Pseudo-code):**
-1. Append `user_input` as a `UserMessage` to `memory_`.
-2. `loop`:
- 1. Fetch `history` from `memory_`.
- 2. Resolve `allowed_tools_` against the `tool_registry_` and convert them into a single JSON Schema array.
- 3. `response_msg = co_await client_->generateResponse(history, tools_schema)`.
- 4. Append `response_msg` (which is an `AssistantMessage`) to `memory_`.
- 5. `if response_msg contains tool_calls`:
- 1. For each `call` in `tool_calls`:
- 1. Find matching `Tool* tool` in `tool_registry_`.
- 2. `result = co_await tool->execute(call.arguments)`.
- 3. Append a `ToolResultMessage` (containing `result` and `call.id`) to `memory_`.
- 6. `else`:
- 1. Break loop.
-3. Return `response_msg.content` (or extract content from the variant).
-
----
-
-## 5. Build System & Dependencies
-* **CMake:** The project will be built using CMake (`CMakeLists.txt`).
-* **C++23:** Ensure compiler flags support standard C++23 (`-std=c++23`).
-* **libmw:** Include `libmw` via `FetchContent` or as a Git submodule.
- * Link against `mw::core`, `mw::url`, `mw::sqlite`.
-* **Logging:** Use `spdlog` (bundled with `libmw`) for detailed debug and execution logging.
-
----
-
-## 6. Implementation Plan (For the Developer)
-
-To build this systematically, follow these phases:
-
-1. **Phase 1: Foundations & Build Setup**
- * Create the `CMakeLists.txt`. Integrate `libmw`.
- * Define the core structs (`Message`, `Role`, `Skill`).
-2. **Phase 2: Networking & LLM Client**
- * Implement the `LlmClient` interface.
- * Build `OpenAiClient` using `mw::url`. Test it with a hardcoded prompt to ensure you can parse the JSON responses.
-3. **Phase 3: The Core Loop**
- * Implement the `Agent` class and a simple `InMemoryMemory` class.
- * Get a basic back-and-forth chat working in the terminal *without* tools.
-4. **Phase 4: Tool Calling**
- * Implement the `Tool` interface.
- * Create a simple mock tool (e.g., `CalculatorTool`).
- * Implement the `Agent::run` loop logic to parse tool calls, execute them, and feed the results back to the LLM.
-5. **Phase 5: Persistence & Skills**
- * Implement `SqliteMemory` utilizing `mw::sqlite`.
- * Add the ability to load and switch between `Skill` profiles.
-6. **Phase 6: Async Polish & MCP**
- * Finalize C++23 coroutine integration for all network and execution paths.
- * Research and implement an `McpClient` to communicate with external MCP servers over stdio.
\ No newline at end of file
diff --git a/src/agent.cpp b/src/agent.cpp
deleted file mode 100644
index 54060db..0000000
--- a/src/agent.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include "agent.hpp"
-
-#include <spdlog/spdlog.h>
-
-Agent::Agent(std::unique_ptr<LlmClient> client, std::unique_ptr<Memory> memory,
- ToolRegistry& tool_registry)
- : client_(std::move(client)), memory_(std::move(memory)),
- tool_registry_(tool_registry)
-{
-}
-
-mw::E<void> Agent::allowTool(const std::string& tool_name)
-{
- if(tool_registry_.getTool(tool_name) == nullptr)
- {
- return std::unexpected(mw::runtimeError("Tool not found in registry"));
- }
- allowed_tools_.push_back(tool_name);
- return {};
-}
-
-void Agent::activateSkill(const Skill& skill)
-{
- current_skill_ = skill;
- allowed_tools_ = skill.allowed_tools;
- memory_->addMessage(SystemMessage{skill.system_prompt});
-}
-
-Task<mw::E<std::string>> Agent::run(const std::string& user_input)
-{
- memory_->addMessage(UserMessage{user_input});
-
- while(true)
- {
- auto history = memory_->getHistory();
- nlohmann::json tools_schema = nlohmann::json::array();
-
- for(const auto& tool_name : allowed_tools_)
- {
- if(Tool* tool = tool_registry_.getTool(tool_name))
- {
- nlohmann::json tool_json = {
- {"type", "function"},
- {"function",
- {{"name", tool->name()},
- {"description", tool->description()},
- {"parameters", tool->parametersSchema()}}}};
- tools_schema.push_back(tool_json);
- }
- }
-
- auto response_res =
- co_await client_->generateResponse(history, tools_schema);
- if(!response_res.has_value())
- {
- co_return std::unexpected(response_res.error());
- }
-
- Message response_msg = response_res.value();
- memory_->addMessage(response_msg);
-
- auto& assistant_msg = std::get<AssistantMessage>(response_msg);
-
- if(!assistant_msg.tool_calls.empty())
- {
- for(const auto& call : assistant_msg.tool_calls)
- {
- if(Tool* tool = tool_registry_.getTool(call.name))
- {
- auto result = co_await tool->execute(call.arguments);
- if(result.has_value())
- {
- memory_->addMessage(
- ToolResultMessage{call.id, result.value()});
- }
- else
- {
- memory_->addMessage(ToolResultMessage{
- call.id, "Error: " + mw::errorMsg(result.error())});
- }
- }
- else
- {
- memory_->addMessage(
- ToolResultMessage{call.id, "Error: Tool not found"});
- }
- }
- }
- else
- {
- if(assistant_msg.content)
- {
- co_return *assistant_msg.content;
- }
- co_return "";
- }
- }
-}
diff --git a/src/agent.hpp b/src/agent.hpp
deleted file mode 100644
index 6a6622e..0000000
--- a/src/agent.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <mw/error.hpp>
-
-#include "message.hpp"
-#include "llm_client.hpp"
-#include "memory.hpp"
-#include "task.hpp"
-#include "tool.hpp"
-
-class Agent
-{
-public:
- Agent(std::unique_ptr<LlmClient> client, std::unique_ptr<Memory> memory,
- ToolRegistry& tool_registry);
-
- mw::E<void> allowTool(const std::string& tool_name);
- void activateSkill(const Skill& skill);
-
- Task<mw::E<std::string>> run(const std::string& user_input);
-
-private:
- std::unique_ptr<LlmClient> client_;
- std::unique_ptr<Memory> memory_;
- ToolRegistry& tool_registry_;
-
- std::vector<std::string> allowed_tools_;
- std::optional<Skill> current_skill_;
-};
diff --git a/src/agent_test.cpp b/src/agent_test.cpp
deleted file mode 100644
index 02de426..0000000
--- a/src/agent_test.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#include <gtest/gtest.h>
-
-#include "agent.hpp"
-#include "llm_client.hpp"
-#include "memory.hpp"
-#include "tool.hpp"
-
-class MockLlmClient : public LlmClient
-{
-public:
- std::vector<Message> responses;
- size_t call_count = 0;
-
- Task<mw::E<Message>> generateResponse(const std::vector<Message>&,
- const nlohmann::json&) override
- {
- if(call_count < responses.size())
- {
- auto res = responses[call_count++];
- co_return res;
- }
- co_return AssistantMessage{"default_mock_end", {}};
- }
-};
-
-class DummyTool : public Tool
-{
-public:
- std::string name() const override
- {
- return "dummy";
- }
- std::string description() const override
- {
- return "dummy desc";
- }
- nlohmann::json parametersSchema() const override
- {
- return nlohmann::json::object();
- }
- Task<mw::E<std::string>> execute(const nlohmann::json&) override
- {
- co_return "dummy_result";
- }
-};
-
-TEST(AgentTest, BasicRunAndReturn)
-{
- auto client = std::make_unique<MockLlmClient>();
- client->responses.push_back(AssistantMessage{"Hello from Agent", {}});
-
- auto memory = std::make_unique<InMemoryMemory>();
- ToolRegistry registry;
-
- Agent agent(std::move(client), std::move(memory), registry);
-
- auto result = agent.run("Hi").get();
- ASSERT_TRUE(result.has_value());
- EXPECT_EQ(result.value(), "Hello from Agent");
-}
-
-TEST(AgentTest, ToolCallingFlow)
-{
- auto client = std::make_unique<MockLlmClient>();
-
- AssistantMessage tool_call_msg;
- tool_call_msg.tool_calls.push_back(
- {"call_1", "dummy", nlohmann::json::object()});
-
- client->responses.push_back(tool_call_msg);
- client->responses.push_back(AssistantMessage{"Final Result", {}});
-
- auto memory = std::make_unique<InMemoryMemory>();
- ToolRegistry registry;
- registry.registerTool(std::make_unique<DummyTool>());
-
- Agent agent(std::move(client), std::move(memory), registry);
- agent.allowTool("dummy");
-
- auto result = agent.run("Hi").get();
- ASSERT_TRUE(result.has_value());
- EXPECT_EQ(result.value(), "Final Result");
-}
-
-TEST(AgentTest, ActivateSkill)
-{
- auto client = std::make_unique<MockLlmClient>();
- client->responses.push_back(AssistantMessage{"Skill Active", {}});
-
- auto memory = std::make_unique<InMemoryMemory>();
- auto* mem_ptr = memory.get();
- ToolRegistry registry;
-
- Agent agent(std::move(client), std::move(memory), registry);
-
- Skill test_skill{"test_skill", "You are a tester", {}};
- agent.activateSkill(test_skill);
-
- auto history = mem_ptr->getHistory();
- ASSERT_EQ(history.size(), 1);
- EXPECT_TRUE(std::holds_alternative<SystemMessage>(history[0]));
- EXPECT_EQ(std::get<SystemMessage>(history[0]).content, "You are a tester");
-}
diff --git a/src/calculator_tool.hpp b/src/calculator_tool.hpp
deleted file mode 100644
index 37921ed..0000000
--- a/src/calculator_tool.hpp
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-#include <string>
-
-#include "tool.hpp"
-
-class CalculatorTool : public Tool
-{
-public:
- std::string name() const override
- {
- return "calculator";
- }
-
- std::string description() const override
- {
- return "Performs basic arithmetic operations.";
- }
-
- nlohmann::json parametersSchema() const override
- {
- return R"({
- "type": "object",
- "properties": {
- "operation": {
- "type": "string",
- "enum": ["add", "subtract", "multiply", "divide"],
- "description": "The operation to perform"
- },
- "a": {
- "type": "number"
- },
- "b": {
- "type": "number"
- }
- },
- "required": ["operation", "a", "b"]
- })"_json;
- }
-
- Task<mw::E<std::string>> execute(const nlohmann::json& arguments) override
- {
- if(!arguments.contains("operation") || !arguments.contains("a") ||
- !arguments.contains("b"))
- {
- co_return std::unexpected(mw::runtimeError("Missing arguments"));
- }
-
- std::string op = arguments["operation"];
- double a = arguments["a"];
- double b = arguments["b"];
- double result = 0.0;
-
- if(op == "add")
- {
- result = a + b;
- }
- else if(op == "subtract")
- {
- result = a - b;
- }
- else if(op == "multiply")
- {
- result = a * b;
- }
- else if(op == "divide")
- {
- if(b == 0)
- {
- co_return std::unexpected(mw::runtimeError("Division by zero"));
- }
- result = a / b;
- }
- else
- {
- co_return std::unexpected(mw::runtimeError("Unknown operation"));
- }
-
- co_return std::to_string(result);
- }
-};
diff --git a/src/llm_client.cpp b/src/llm_client.cpp
deleted file mode 100644
index 16a0c79..0000000
--- a/src/llm_client.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "llm_client.hpp"
-
-#include <mw/http_client.hpp>
-#include <mw/url.hpp>
-#include <spdlog/spdlog.h>
-
-OpenAiClient::OpenAiClient(std::string api_key, std::string model,
- std::string endpoint,
- std::unique_ptr<mw::HTTPSessionInterface> session)
- : api_key_(std::move(api_key)), model_(std::move(model)),
- endpoint_(std::move(endpoint)),
- session_(session ? std::move(session)
- : std::make_unique<mw::HTTPSession>())
-{
-}
-
-mw::E<AssistantMessage>
-OpenAiClient::parseResponse(std::string_view response_body) const
-{
- try
- {
- auto response_json = nlohmann::json::parse(response_body);
- if(!response_json.contains("choices") || response_json["choices"].empty())
- {
- return std::unexpected(mw::runtimeError(
- std::string("Invalid response format: missing choices. Body: ") + std::string(response_body)));
- }
- auto& choice = response_json["choices"][0]["message"];
-
- AssistantMessage assistant_msg;
- if(choice.contains("content") && !choice["content"].is_null())
- {
- assistant_msg.content = static_cast<std::string>(choice["content"]);
- }
-
- if(choice.contains("tool_calls"))
- {
- for(const auto& tc : choice["tool_calls"])
- {
- ToolCall call;
- call.id = static_cast<std::string>(tc["id"]);
- call.name = static_cast<std::string>(tc["function"]["name"]);
- call.arguments = nlohmann::json::parse(
- static_cast<std::string>(tc["function"]["arguments"]));
- assistant_msg.tool_calls.push_back(call);
- }
- }
-
- return assistant_msg;
- }
- catch(const std::exception& e)
- {
- return std::unexpected(
- mw::runtimeError(std::string("JSON parse error: ") + e.what()));
- }
-}
-
-Task<mw::E<Message>>
-OpenAiClient::generateResponse(const std::vector<Message>& history,
- const nlohmann::json& available_tools_schema)
-{
- nlohmann::json request_body;
- request_body["model"] = model_;
-
- nlohmann::json messages = nlohmann::json::array();
- for(const auto& msg : history)
- {
- messages.push_back(toJson(msg));
- }
- request_body["messages"] = messages;
-
- if(!available_tools_schema.empty() && available_tools_schema.is_array())
- {
- request_body["tools"] = available_tools_schema;
- }
-
- auto url_res = mw::URL::fromStr(endpoint_);
- if(!url_res.has_value())
- {
- co_return std::unexpected(mw::runtimeError("Invalid endpoint URL"));
- }
- mw::URL request_url = std::move(url_res.value());
- request_url.appendPath("chat/completions");
-
- mw::HTTPRequest request(request_url.str());
- request.addHeader("Content-Type", "application/json");
- request.addHeader("Authorization", "Bearer " + api_key_);
- request.setPayload(request_body.dump());
-
- auto response = session_->post(request);
- if(!response.has_value())
- {
- spdlog::error("OpenAI request failed: {}",
- mw::errorMsg(response.error()));
- co_return std::unexpected(response.error());
- }
-
- auto parsed = parseResponse(response.value()->payloadAsStr());
- if(!parsed.has_value())
- {
- co_return std::unexpected(parsed.error());
- }
- co_return parsed.value();
-}
diff --git a/src/llm_client.hpp b/src/llm_client.hpp
deleted file mode 100644
index c2234c2..0000000
--- a/src/llm_client.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <string_view>
-#include <vector>
-
-#include <mw/error.hpp>
-#include <mw/http_client.hpp>
-#include <nlohmann/json.hpp>
-
-#include "message.hpp"
-#include "task.hpp"
-
-class LlmClient
-{
-public:
- virtual ~LlmClient() = default;
-
- virtual Task<mw::E<Message>>
- generateResponse(const std::vector<Message>& history,
- const nlohmann::json& available_tools_schema) = 0;
-};
-
-class OpenAiClient : public LlmClient
-{
-public:
- OpenAiClient(
- std::string api_key, std::string model = "gpt-4o",
- std::string endpoint = "https://api.openai.com/v1",
- std::unique_ptr<mw::HTTPSessionInterface> session = nullptr);
-
- Task<mw::E<Message>>
- generateResponse(const std::vector<Message>& history,
- const nlohmann::json& available_tools_schema) override;
-
-private:
- std::string api_key_;
- std::string model_;
- std::string endpoint_;
- std::unique_ptr<mw::HTTPSessionInterface> session_;
-
- mw::E<AssistantMessage> parseResponse(std::string_view response_body) const;
-};
diff --git a/src/llm_client_test.cpp b/src/llm_client_test.cpp
deleted file mode 100644
index 22881f9..0000000
--- a/src/llm_client_test.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <mw/http_client_mock.hpp>
-
-#include "llm_client.hpp"
-
-using ::testing::_;
-using ::testing::Return;
-
-TEST(OpenAiClientTest, SuccessfulGeneration)
-{
- auto mock_session = std::make_unique<mw::HTTPSessionMock>();
-
- std::string mock_json_response = R"({
- "choices": [
- {
- "message": {
- "role": "assistant",
- "content": "Hello! How can I help?"
- }
- }
- ]
- })";
-
- mw::HTTPResponse http_response(200, mock_json_response);
-
- EXPECT_CALL(*mock_session, post(_)).WillOnce(Return(&http_response));
-
- OpenAiClient client("fake_key", "gpt-4o", "https://fake.endpoint.com/v1", std::move(mock_session));
-
- std::vector<Message> history = {UserMessage{"Hi"}};
- nlohmann::json tools = nlohmann::json::array();
-
- auto result = client.generateResponse(history, tools).get();
- ASSERT_TRUE(result.has_value());
-
- auto& assistant_msg = std::get<AssistantMessage>(result.value());
- ASSERT_TRUE(assistant_msg.content.has_value());
- EXPECT_EQ(*assistant_msg.content, "Hello! How can I help?");
- EXPECT_TRUE(assistant_msg.tool_calls.empty());
-}
-
-TEST(OpenAiClientTest, ToolCallGeneration)
-{
- auto mock_session = std::make_unique<mw::HTTPSessionMock>();
-
- std::string mock_json_response = R"({
- "choices": [
- {
- "message": {
- "role": "assistant",
- "content": null,
- "tool_calls": [
- {
- "id": "call_123",
- "type": "function",
- "function": {
- "name": "calculator",
- "arguments": "{\"a\": 2, \"b\": 2, \"operation\": \"add\"}"
- }
- }
- ]
- }
- }
- ]
- })";
-
- mw::HTTPResponse http_response(200, mock_json_response);
-
- EXPECT_CALL(*mock_session, post(_)).WillOnce(Return(&http_response));
-
- OpenAiClient client("fake_key", "gpt-4o", "https://fake.endpoint.com/v1", std::move(mock_session));
-
- std::vector<Message> history = {UserMessage{"Calculate 2+2"}};
- nlohmann::json tools = nlohmann::json::array();
-
- auto result = client.generateResponse(history, tools).get();
- ASSERT_TRUE(result.has_value());
-
- auto& assistant_msg = std::get<AssistantMessage>(result.value());
- EXPECT_FALSE(assistant_msg.content.has_value());
- ASSERT_EQ(assistant_msg.tool_calls.size(), 1);
- EXPECT_EQ(assistant_msg.tool_calls[0].name, "calculator");
- EXPECT_EQ(assistant_msg.tool_calls[0].arguments["a"], 2);
-}
-
-TEST(OpenAiClientTest, NetworkFailureReturnsError)
-{
- auto mock_session = std::make_unique<mw::HTTPSessionMock>();
-
- EXPECT_CALL(*mock_session, post(_))
- .WillOnce(Return(std::unexpected(mw::runtimeError("Network Error"))));
-
- OpenAiClient client("fake_key", "gpt-4o", "https://fake.endpoint.com/v1", std::move(mock_session));
-
- std::vector<Message> history = {UserMessage{"Hi"}};
- nlohmann::json tools = nlohmann::json::array();
-
- auto result = client.generateResponse(history, tools).get();
- EXPECT_FALSE(result.has_value());
- EXPECT_EQ(mw::errorMsg(result.error()), "Network Error");
-}
diff --git a/src/main.cpp b/src/main.cpp
deleted file mode 100644
index b51feba..0000000
--- a/src/main.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-#include <cstdlib>
-#include <iostream>
-#include <memory>
-#include <string>
-
-#include <cxxopts.hpp>
-
-#include "agent.hpp"
-#include "calculator_tool.hpp"
-#include "llm_client.hpp"
-#include "memory.hpp"
-
-// A simple coroutine runner since our Task is mostly synchronous right now
-void runTask(Task<mw::E<std::string>> task)
-{
- auto res = task.get();
- if(res.has_value())
- {
- std::cout << "Agent: " << res.value() << "\n";
- }
- else
- {
- std::cout << "Agent Error: " << mw::errorMsg(res.error()) << "\n";
- }
-}
-
-int main(int argc, char** argv)
-{
- cxxopts::Options options("agent_smith", "C++ LLM Agent Framework");
-
- options.add_options()
- ("k,api-key", "LLM API Key", cxxopts::value<std::string>())
- ("e,endpoint", "LLM API Endpoint (Base URL)",
- cxxopts::value<std::string>()->default_value(
- "https://api.openai.com/v1"))
- ("m,model", "LLM Model Name",
- cxxopts::value<std::string>()->default_value("gpt-4o"))
- ("h,help", "Print usage")
- ;
-
- auto result = options.parse(argc, argv);
-
- if(result.count("help"))
- {
- std::cout << options.help() << "\n";
- return 0;
- }
-
- std::string api_key;
- if(result.count("api-key"))
- {
- api_key = result["api-key"].as<std::string>();
- }
- else if(const char* env_key = std::getenv("OPENAI_API_KEY"))
- {
- api_key = env_key;
- }
- else
- {
- std::cerr << "Error: API key must be provided via --api-key or "
- << "OPENAI_API_KEY environment variable.\n";
- return 1;
- }
-
- std::string endpoint = result["endpoint"].as<std::string>();
- std::string model = result["model"].as<std::string>();
-
- auto client = std::make_unique<OpenAiClient>(api_key, model, endpoint);
- auto memory = std::make_unique<InMemoryMemory>();
- ToolRegistry tool_registry;
-
- auto reg_res =
- tool_registry.registerTool(std::make_unique<CalculatorTool>());
- if(!reg_res.has_value())
- {
- std::cerr << "Failed to register tool: "
- << mw::errorMsg(reg_res.error()) << "\n";
- return 1;
- }
-
- Agent agent(std::move(client), std::move(memory), tool_registry);
- auto allow_res = agent.allowTool("calculator");
- if(!allow_res.has_value())
- {
- std::cerr << "Failed to allow tool: " << mw::errorMsg(allow_res.error())
- << "\n";
- return 1;
- }
-
- Skill default_skill{
- "calculator_assistant",
- "You are a helpful assistant that can perform calculations. "
- "Use the calculator tool when needed.",
- {"calculator"}};
-
- agent.activateSkill(default_skill);
-
- std::cout << "Agent Smith initialized.\n"
- << "Endpoint: " << endpoint << "\n"
- << "Model: " << model << "\n"
- << "Type 'exit' to quit.\n";
-
- std::string user_input;
- while(true)
- {
- std::cout << "You: ";
- if(!std::getline(std::cin, user_input) || user_input == "exit")
- {
- break;
- }
-
- try
- {
- runTask(agent.run(user_input));
- }
- catch(const std::exception& e)
- {
- std::cerr << "Exception: " << e.what() << "\n";
- }
- }
-
- return 0;
-}
diff --git a/src/memory.cpp b/src/memory.cpp
deleted file mode 100644
index bbf9419..0000000
--- a/src/memory.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "memory.hpp"
-
-void InMemoryMemory::addMessage(const Message& msg)
-{
- history_.push_back(msg);
-}
-
-std::vector<Message> InMemoryMemory::getHistory() const
-{
- return history_;
-}
-
-void InMemoryMemory::clear()
-{
- history_.clear();
-}
diff --git a/src/memory.hpp b/src/memory.hpp
deleted file mode 100644
index 4d50c9b..0000000
--- a/src/memory.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include <vector>
-
-#include "message.hpp"
-
-class Memory
-{
-public:
- virtual ~Memory() = default;
- virtual void addMessage(const Message& msg) = 0;
- virtual std::vector<Message> getHistory() const = 0;
- virtual void clear() = 0;
-};
-
-class InMemoryMemory : public Memory
-{
-public:
- void addMessage(const Message& msg) override;
- std::vector<Message> getHistory() const override;
- void clear() override;
-
-private:
- std::vector<Message> history_;
-};
diff --git a/src/memory_test.cpp b/src/memory_test.cpp
deleted file mode 100644
index 25e0524..0000000
--- a/src/memory_test.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <gtest/gtest.h>
-
-#include "memory.hpp"
-
-TEST(InMemoryMemoryTest, AddAndGetHistory)
-{
- InMemoryMemory memory;
- memory.addMessage(SystemMessage{"sys"});
- memory.addMessage(UserMessage{"user"});
-
- auto history = memory.getHistory();
- ASSERT_EQ(history.size(), 2);
- EXPECT_TRUE(std::holds_alternative<SystemMessage>(history[0]));
- EXPECT_TRUE(std::holds_alternative<UserMessage>(history[1]));
-}
-
-TEST(InMemoryMemoryTest, ClearHistory)
-{
- InMemoryMemory memory;
- memory.addMessage(UserMessage{"hi"});
- memory.clear();
- EXPECT_TRUE(memory.getHistory().empty());
-}
diff --git a/src/message.cpp b/src/message.cpp
deleted file mode 100644
index 7a53ff7..0000000
--- a/src/message.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "message.hpp"
-
-nlohmann::json toJson(const SystemMessage& msg)
-{
- return {{"role", "system"}, {"content", msg.content}};
-}
-
-nlohmann::json toJson(const UserMessage& msg)
-{
- return {{"role", "user"}, {"content", msg.content}};
-}
-
-nlohmann::json toJson(const ToolCall& msg)
-{
- return {{"id", msg.id},
- {"type", "function"},
- {"function",
- {{"name", msg.name}, {"arguments", msg.arguments.dump()}}}};
-}
-
-nlohmann::json toJson(const AssistantMessage& msg)
-{
- nlohmann::json j = {{"role", "assistant"}};
- if(msg.content)
- {
- j["content"] = *msg.content;
- }
- if(!msg.tool_calls.empty())
- {
- nlohmann::json calls_json = nlohmann::json::array();
- for(const auto& call : msg.tool_calls)
- {
- calls_json.push_back(toJson(call));
- }
- j["tool_calls"] = calls_json;
- }
- return j;
-}
-
-nlohmann::json toJson(const ToolResultMessage& msg)
-{
- return {{"role", "tool"},
- {"tool_call_id", msg.tool_call_id},
- {"content", msg.content}};
-}
-
-nlohmann::json toJson(const Message& msg)
-{
- return std::visit([](const auto& m) { return toJson(m); }, msg);
-}
diff --git a/src/message.hpp b/src/message.hpp
deleted file mode 100644
index 61b1f0a..0000000
--- a/src/message.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include <optional>
-#include <string>
-#include <variant>
-#include <vector>
-
-#include <nlohmann/json.hpp>
-
-struct SystemMessage
-{
- std::string content;
-};
-
-struct UserMessage
-{
- std::string content;
-};
-
-struct ToolCall
-{
- std::string id;
- std::string name;
- nlohmann::json arguments;
-};
-
-struct AssistantMessage
-{
- std::optional<std::string> content;
- std::vector<ToolCall> tool_calls;
-};
-
-struct ToolResultMessage
-{
- std::string tool_call_id;
- std::string content;
-};
-
-using Message = std::variant<SystemMessage, UserMessage, AssistantMessage,
- ToolResultMessage>;
-
-nlohmann::json toJson(const SystemMessage& msg);
-nlohmann::json toJson(const UserMessage& msg);
-nlohmann::json toJson(const ToolCall& msg);
-nlohmann::json toJson(const AssistantMessage& msg);
-nlohmann::json toJson(const ToolResultMessage& msg);
-nlohmann::json toJson(const Message& msg);
-
-struct Skill
-{
- std::string name;
- std::string system_prompt;
- std::vector<std::string> allowed_tools;
-};
diff --git a/src/message_test.cpp b/src/message_test.cpp
deleted file mode 100644
index 54ad36e..0000000
--- a/src/message_test.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#include <gtest/gtest.h>
-
-#include "message.hpp"
-
-TEST(MessageTest, SystemMessageToJson)
-{
- SystemMessage msg{"You are a helpful assistant."};
- nlohmann::json j = toJson(msg);
- EXPECT_EQ(j["role"], "system");
- EXPECT_EQ(j["content"], "You are a helpful assistant.");
-}
-
-TEST(MessageTest, UserMessageToJson)
-{
- UserMessage msg{"What is 2+2?"};
- nlohmann::json j = toJson(msg);
- EXPECT_EQ(j["role"], "user");
- EXPECT_EQ(j["content"], "What is 2+2?");
-}
-
-TEST(MessageTest, ToolCallToJson)
-{
- ToolCall call{"call_1", "calc", {{"a", 2}, {"b", 2}}};
- nlohmann::json j = toJson(call);
- EXPECT_EQ(j["id"], "call_1");
- EXPECT_EQ(j["type"], "function");
- EXPECT_EQ(j["function"]["name"], "calc");
- EXPECT_EQ(j["function"]["arguments"], "{\"a\":2,\"b\":2}");
-}
-
-TEST(MessageTest, AssistantMessageToJson_ContentOnly)
-{
- AssistantMessage msg{"The answer is 4.", {}};
- nlohmann::json j = toJson(msg);
- EXPECT_EQ(j["role"], "assistant");
- EXPECT_EQ(j["content"], "The answer is 4.");
- EXPECT_FALSE(j.contains("tool_calls"));
-}
-
-TEST(MessageTest, AssistantMessageToJson_ToolCallsOnly)
-{
- AssistantMessage msg{std::nullopt,
- {{"call_1", "calc", {{"a", 2}, {"b", 2}}}}};
- nlohmann::json j = toJson(msg);
- EXPECT_EQ(j["role"], "assistant");
- EXPECT_FALSE(j.contains("content"));
- ASSERT_TRUE(j.contains("tool_calls"));
- EXPECT_EQ(j["tool_calls"].size(), 1);
- EXPECT_EQ(j["tool_calls"][0]["id"], "call_1");
-}
-
-TEST(MessageTest, ToolResultMessageToJson)
-{
- ToolResultMessage msg{"call_1", "4"};
- nlohmann::json j = toJson(msg);
- EXPECT_EQ(j["role"], "tool");
- EXPECT_EQ(j["tool_call_id"], "call_1");
- EXPECT_EQ(j["content"], "4");
-}
-
-TEST(MessageTest, MessageVariantToJson)
-{
- Message msg = UserMessage{"Hello"};
- nlohmann::json j = toJson(msg);
- EXPECT_EQ(j["role"], "user");
- EXPECT_EQ(j["content"], "Hello");
-}
diff --git a/src/task.hpp b/src/task.hpp
deleted file mode 100644
index c8671a3..0000000
--- a/src/task.hpp
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-#include <coroutine>
-#include <exception>
-#include <utility>
-
-template <typename T> class Task
-{
-public:
- struct promise_type;
- using handle_type = std::coroutine_handle<promise_type>;
-
- struct promise_type
- {
- T value;
- std::exception_ptr exception;
-
- Task get_return_object()
- {
- return Task(handle_type::from_promise(*this));
- }
-
- std::suspend_never initial_suspend()
- {
- return {};
- }
- std::suspend_always final_suspend() noexcept
- {
- return {};
- }
-
- template <typename U> void return_value(U&& val)
- {
- value = std::forward<U>(val);
- }
-
- void unhandled_exception()
- {
- exception = std::current_exception();
- }
- };
-
- Task(handle_type h) : coro(h) {}
- Task(Task&& t) noexcept : coro(t.coro)
- {
- t.coro = nullptr;
- }
- ~Task()
- {
- if(coro)
- {
- coro.destroy();
- }
- }
-
- T get()
- {
- if(coro.promise().exception)
- {
- std::rethrow_exception(coro.promise().exception);
- }
- return coro.promise().value;
- }
-
- // Awaiter to allow co_await Task<T>
- bool await_ready() const noexcept
- {
- return coro.done();
- }
- void await_suspend(std::coroutine_handle<> awaiting_coro)
- {
- // Simple synchronous execution for this placeholder
- }
- T await_resume()
- {
- return get();
- }
-
-private:
- handle_type coro;
-};
diff --git a/src/task_test.cpp b/src/task_test.cpp
deleted file mode 100644
index 4c0fcea..0000000
--- a/src/task_test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <stdexcept>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include "task.hpp"
-
-Task<int> asyncCompute()
-{
- co_return 42;
-}
-
-Task<int> asyncThrow()
-{
- throw std::runtime_error("Task Error");
- co_return 0; // Unreachable
-}
-
-TEST(TaskTest, ReturnsValue)
-{
- auto task = asyncCompute();
- EXPECT_EQ(task.get(), 42);
-}
-
-TEST(TaskTest, ThrowsException)
-{
- auto task = asyncThrow();
- EXPECT_THROW(task.get(), std::runtime_error);
-}
diff --git a/src/tool.cpp b/src/tool.cpp
deleted file mode 100644
index 2519430..0000000
--- a/src/tool.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "tool.hpp"
-
-mw::E<void> ToolRegistry::registerTool(std::unique_ptr<Tool> tool)
-{
- if(tools_.contains(tool->name()))
- {
- return std::unexpected(mw::runtimeError(
- std::format("Tool '{}' is already registered.", tool->name())));
- }
- tools_[tool->name()] = std::move(tool);
- return {};
-}
-
-mw::E<void> ToolRegistry::registerToolWithNamespace(const std::string& ns,
- std::unique_ptr<Tool> tool)
-{
- std::string prefixed_name = std::format("{}_{}", ns, tool->name());
- if(tools_.contains(prefixed_name))
- {
- return std::unexpected(mw::runtimeError(
- std::format("Tool '{}' is already registered.", prefixed_name)));
- }
-
- tools_[prefixed_name] = std::move(tool);
- return {};
-}
-
-Tool* ToolRegistry::getTool(const std::string& name) const
-{
- auto it = tools_.find(name);
- return it != tools_.end() ? it->second.get() : nullptr;
-}
-
-std::vector<Tool*> ToolRegistry::getAllTools() const
-{
- std::vector<Tool*> result;
- for(const auto& [name, tool] : tools_)
- {
- result.push_back(tool.get());
- }
- return result;
-}
diff --git a/src/tool.hpp b/src/tool.hpp
deleted file mode 100644
index 4511e29..0000000
--- a/src/tool.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include <format>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include <mw/error.hpp>
-#include <nlohmann/json.hpp>
-
-#include "task.hpp"
-
-class Tool
-{
-public:
- virtual ~Tool() = default;
- virtual std::string name() const = 0;
- virtual std::string description() const = 0;
- virtual nlohmann::json parametersSchema() const = 0;
- virtual Task<mw::E<std::string>>
- execute(const nlohmann::json& arguments) = 0;
-};
-
-class ToolRegistry
-{
-public:
- mw::E<void> registerTool(std::unique_ptr<Tool> tool);
- mw::E<void> registerToolWithNamespace(const std::string& ns,
- std::unique_ptr<Tool> tool);
- Tool* getTool(const std::string& name) const;
- std::vector<Tool*> getAllTools() const;
-
-private:
- std::unordered_map<std::string, std::unique_ptr<Tool>> tools_;
-};
diff --git a/src/tool_test.cpp b/src/tool_test.cpp
deleted file mode 100644
index f6f938a..0000000
--- a/src/tool_test.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#include <gtest/gtest.h>
-#include <nlohmann/json.hpp>
-
-#include "tool.hpp"
-
-class MockTool : public Tool
-{
-public:
- std::string name() const override
- {
- return "mock_tool";
- }
- std::string description() const override
- {
- return "Mock Tool";
- }
- nlohmann::json parametersSchema() const override
- {
- return nlohmann::json::object();
- }
- Task<mw::E<std::string>> execute(const nlohmann::json&) override
- {
- co_return "mock_result";
- }
-};
-
-TEST(ToolRegistryTest, RegisterAndGetTool)
-{
- ToolRegistry registry;
- auto res = registry.registerTool(std::make_unique<MockTool>());
- ASSERT_TRUE(res.has_value());
-
- Tool* tool = registry.getTool("mock_tool");
- ASSERT_NE(tool, nullptr);
- EXPECT_EQ(tool->name(), "mock_tool");
-}
-
-TEST(ToolRegistryTest, DuplicateRegistrationFails)
-{
- ToolRegistry registry;
- ASSERT_TRUE(
- registry.registerTool(std::make_unique<MockTool>()).has_value());
- EXPECT_FALSE(
- registry.registerTool(std::make_unique<MockTool>()).has_value());
-}
-
-TEST(ToolRegistryTest, RegisterWithNamespace)
-{
- ToolRegistry registry;
- auto res =
- registry.registerToolWithNamespace("ns", std::make_unique<MockTool>());
- ASSERT_TRUE(res.has_value());
-
- Tool* tool = registry.getTool("ns_mock_tool");
- ASSERT_NE(tool, nullptr);
- EXPECT_EQ(tool->name(), "mock_tool");
-}
-
-TEST(ToolRegistryTest, GetAllTools)
-{
- ToolRegistry registry;
- registry.registerTool(std::make_unique<MockTool>());
-
- auto tools = registry.getAllTools();
- ASSERT_EQ(tools.size(), 1);
- EXPECT_EQ(tools[0]->name(), "mock_tool");
-}