kbot cpp testing - classifiers

This commit is contained in:
lovebird 2026-03-30 13:57:06 +02:00
parent 8cfa1b7c89
commit 2f2d507baa
9 changed files with 335 additions and 39 deletions

View File

@ -130,9 +130,14 @@ else()
endif()
# Install
# Library + headers: see packages/kbot/CMakeLists.txt and packages/ipc/CMakeLists.txt
# Optional DLL/so: configure with -DIPC_BUILD_SHARED=ON -DPOLYMECH_KBOT_SHARED=ON
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/cmd_kbot.h
DESTINATION include/polymech
)
# Tests
enable_testing()

View File

@ -21,6 +21,16 @@
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "dev-dll",
"displayName": "Dev (Debug, ipc + kbot as DLL)",
"binaryDir": "${sourceDir}/build/dev-dll",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"IPC_BUILD_SHARED": "ON",
"POLYMECH_KBOT_SHARED": "ON"
}
}
],
"buildPresets": [
@ -31,6 +41,10 @@
{
"name": "release",
"configurePreset": "release"
},
{
"name": "dev-dll",
"configurePreset": "dev-dll"
}
]
}

View File

@ -1,40 +1,251 @@
# polymech-cli
# kbot (C++)
Cross-platform C++ CLI built with CMake.
CMake-based C++ toolchain for **kbot**: HTML/HTTP/JSON utilities, **length-prefixed JSON IPC**, optional **UDS/TCP worker** for Node orchestrators, and **LLM chat** via liboai (OpenRouter, OpenAI, Ollama-compatible servers, etc.). The main binary is **`kbot`** (`kbot.exe` on Windows).
## Prerequisites
| Tool | Version |
|------|---------|
| Requirement | Notes |
|-------------|--------|
| CMake | ≥ 3.20 |
| C++ compiler | C++17 (MSVC, GCC, or Clang) |
| C++ compiler | C++17 (MSVC, GCC, Clang) |
| Git | For `FetchContent` dependencies |
| Node.js | Optional; for `orchestrator/` IPC integration tests (`npm run test:ipc`) |
## Build
On Windows, use a **Developer Command Prompt** or **PowerShell** with MSVC in `PATH`. **Git Bash** helps if you use shell scripts under `scripts/`.
## Quick start (build)
From this directory (`packages/kbot/cpp`):
```bash
npm install # optional; only needed if you use npm scripts
npm run build
```
Artifacts go to **`dist/`** (e.g. `dist/kbot.exe`, test tools).
Equivalent CMake:
```bash
# Debug
cmake --preset dev
cmake --build --preset dev
# Release
cmake --preset release
cmake --build --preset release
```
## Usage
### Presets
| Preset | Role |
|--------|------|
| `dev` | Debug, static `ipc` + `kbot` libraries (default) |
| `release` | Release build |
| `dev-dll` | Debug with **`ipc.dll`** and **`kbot.dll`** (`IPC_BUILD_SHARED=ON`, `POLYMECH_KBOT_SHARED=ON`) |
```bash
polymech-cli --help
polymech-cli --version
cmake --preset dev-dll
cmake --build --preset dev-dll --config Debug
```
Place **`ipc.dll`** and **`kbot.dll`** next to **`kbot.exe`** (or on `PATH`) when using the DLL configuration.
### npm scripts (reference)
| Script | Purpose |
|--------|---------|
| `npm run build` | Configure `dev` + build |
| `npm run build:release` | Release preset |
| `npm run test` | `ctest` in `build/dev` |
| `npm run clean` | Remove `build/` and `dist/` |
| `npm run test:ipc` | Node UDS IPC integration test |
| `npm run worker` | Run worker (stdio IPC) |
## Installation
Install the CLI and headers into a prefix (e.g. local tree or system root):
```bash
cmake --install build/dev --prefix "C:/path/to/install"
```
This installs:
- **`bin/kbot`** (runtime)
- **`include/polymech/`** — `kbot.h`, `llm_client.h`, `polymech_export.h`, `cmd_kbot.h`
- **`include/ipc/`** — `ipc.h`, `ipc_export.h`
- **`lib/`** — import libraries / archives (depending on static vs shared)
Library layout is defined in `packages/kbot/CMakeLists.txt` and `packages/ipc/CMakeLists.txt`.
### CMake options (libraries)
| Cache variable | Effect |
|----------------|--------|
| `IPC_BUILD_SHARED` | Build **`ipc`** as a shared library (`OFF` default) |
| `POLYMECH_KBOT_SHARED` | Build **`kbot`** as a shared library (`OFF` default) |
Static builds define `IPC_STATIC_BUILD` / `POLYMECH_STATIC_BUILD` for consumers via `INTERFACE` compile definitions. Shared builds export **`IPC_API`** / **`POLYMECH_API`** (see `ipc_export.h`, `polymech_export.h`).
## CLI overview
Top-level:
```bash
kbot --help
kbot -v,--version
kbot --log-level debug|info|warn|error
```
### Subcommands
| Command | Description |
|---------|-------------|
| `parse <html>` | Parse HTML and list elements |
| `select <html> <selector>` | CSS-select elements |
| `config <file>` | Load and print a TOML file |
| `fetch <url>` | HTTP GET |
| `json <input>` | Prettify JSON |
| `db [-c config] [table] [-l limit]` | Supabase / DB helper (uses `config/postgres.toml` by default) |
| `worker [--uds <arg>]` | IPC worker (see below) |
| `kbot ai ...` / `kbot run ...` | AI and run pipelines (`setup_cmd_kbot` — use `kbot kbot ai --help`) |
### Worker mode (`kbot worker`)
Used by orchestrators and tests.
- **Stdio IPC** (length-prefixed JSON frames on stdin/stdout):
```bash
kbot worker
```
- **UDS / TCP** (Windows: TCP port string, e.g. `4001`; Unix: socket path):
```bash
kbot worker --uds 4001
```
Framing: `[uint32 LE length][UTF-8 JSON object with id, type, payload]`. Message types include `ping`, `job`, `kbot-ai`, `kbot-run`, `shutdown`, etc. See `src/main.cpp` and `orchestrator/test-ipc.mjs`.
### `kbot kbot` (nested)
CLI for AI tasks and run configurations:
```bash
kbot kbot ai --help
kbot kbot run --help
```
Example:
```bash
kbot kbot ai --prompt "Hello" --config config/postgres.toml
```
API keys are typically resolved from **`config/postgres.toml`** (`[services]`).
## Using in other CMake projects
There is no single `find_package(kbot)` config yet. Practical options:
### 1. Same repository / superbuild (recommended)
Add this repos `cpp` tree as a subdirectory from a parent `CMakeLists.txt` so `FetchContent` and internal targets (`logger`, `json`, `ipc`, `oai`, `kbot`, …) resolve once. Then:
```cmake
target_link_libraries(your_app PRIVATE ipc kbot)
```
`kbot` pulls in `logger`, `json`, `liboai` (`oai`) per `packages/kbot/CMakeLists.txt`.
### 2. Install prefix + explicit `IMPORTED` libraries
After `cmake --install`, link import libraries under `lib/` and add `include/` for **`ipc`** and **`polymech`**. You must still satisfy **transitive** dependencies (`oai`, `logger`, `json`, …) from the **same** build/install of this project, or duplicate their build—usually easier to use option 1.
### 3. Minimal example: IPC framing only
If you only need **`ipc::encode` / `ipc::decode`** (and can build `logger` + `json` the same way this project does), mirror `packages/ipc/CMakeLists.txt`:
```cmake
cmake_minimum_required(VERSION 3.20)
project(myapp CXX)
set(CMAKE_CXX_STANDARD 17)
add_subdirectory(path/to/polymech-mono/packages/kbot/cpp/packages/logger)
add_subdirectory(path/to/polymech-mono/packages/kbot/cpp/packages/json)
add_subdirectory(path/to/polymech-mono/packages/kbot/cpp/packages/ipc)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE ipc)
```
**`main.cpp`** (stdio-style framing helpers):
```cpp
#include <iostream>
#include <ipc/ipc.h>
int main() {
ipc::Message msg{"1", "ping", "{}"};
auto frame = ipc::encode(msg);
// frame: 4-byte LE length + JSON object bytes
ipc::Message roundtrip;
if (frame.size() > 4 &&
ipc::decode(frame.data() + 4, frame.size() - 4, roundtrip)) {
std::cout << roundtrip.type << "\n"; // ping
}
return 0;
}
```
### 4. Example: LLM pipeline API (`kbot` library)
Headers: `kbot.h`, `llm_client.h`, `polymech_export.h`. You need a valid API key and options (see `KBotOptions` in `kbot.h`).
```cpp
#include <iostream>
#include "kbot.h"
#include "llm_client.h"
int main() {
polymech::kbot::KBotOptions opts;
opts.prompt = "Say hello in one sentence.";
opts.api_key = "YOUR_KEY";
opts.router = "openrouter";
opts.model = "openai/gpt-4o-mini";
polymech::kbot::LLMClient client(opts);
polymech::kbot::LLMResponse r = client.execute_chat(opts.prompt);
if (r.success) {
std::cout << r.text << "\n";
} else {
std::cerr << r.error << "\n";
return 1;
}
return 0;
}
```
Or use the callback-based pipeline:
```cpp
polymech::kbot::KBotCallbacks cb;
cb.onEvent = [](const std::string& type, const std::string& json) {
std::cout << type << ": " << json << "\n";
};
return polymech::kbot::run_kbot_ai_pipeline(opts, cb);
```
Link **`kbot`** (and its public dependencies). **`cmd_kbot.h`** entry points (`run_kbot_ai_ipc`, `run_cmd_kbot_uds`, …) are implemented in **`src/cmd_kbot*.cpp`** in this project; to reuse them, compile those sources into your binary or vendor the logic.
## Node / IPC tests
Integration tests live under **`orchestrator/`** (see comments in `orchestrator/test-ipc.mjs`). Typical run from `cpp/`:
```bash
npm run test:ipc
```
Requires a built **`dist/kbot.exe`** (or `kbot` on Unix).
## License
BSD-3-Clause
## Requirements
- [https://github.com/taskflow/taskflow](https://github.com/taskflow/taskflow)
- [https://github.com/cameron314/concurrentqueue](https://github.com/cameron314/concurrentqueue)
- [https://github.com/chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
See [LICENSE](LICENSE) in this directory.

View File

@ -1,11 +1,45 @@
add_library(ipc STATIC
src/ipc.cpp
)
cmake_minimum_required(VERSION 3.20)
project(ipc CXX)
option(IPC_BUILD_SHARED "Build ipc as a shared library (DLL/so)" OFF)
set(_ipc_sources src/ipc.cpp)
if(IPC_BUILD_SHARED)
add_library(ipc SHARED ${_ipc_sources})
target_compile_definitions(ipc PRIVATE IPC_BUILDING_LIBRARY)
else()
add_library(ipc STATIC ${_ipc_sources})
target_compile_definitions(ipc PRIVATE IPC_STATIC_BUILD=1)
target_compile_definitions(ipc INTERFACE IPC_STATIC_BUILD=1)
endif()
target_include_directories(ipc
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(ipc
PUBLIC json logger
PUBLIC json logger
)
if(IPC_BUILD_SHARED)
set_target_properties(ipc PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/dist"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_SOURCE_DIR}/dist"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_SOURCE_DIR}/dist"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/dist"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/dist"
)
endif()
install(TARGETS ipc
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/include/ipc/ipc.h
${CMAKE_CURRENT_SOURCE_DIR}/include/ipc/ipc_export.h
DESTINATION include/ipc
)

View File

@ -1,5 +1,6 @@
#pragma once
#include "ipc/ipc_export.h"
#include <cstdint>
#include <cstdio>
#include <string>
@ -16,19 +17,19 @@ struct Message {
/// Encode a Message into a length-prefixed binary frame.
/// Layout: [4-byte LE uint32 length][JSON bytes]
std::vector<uint8_t> encode(const Message &msg);
IPC_API std::vector<uint8_t> encode(const Message &msg);
/// Decode a binary frame (without the 4-byte length prefix) into a Message.
/// Returns false if the JSON is invalid or missing required fields.
bool decode(const uint8_t *data, size_t len, Message &out);
bool decode(const std::vector<uint8_t> &frame, Message &out);
IPC_API bool decode(const uint8_t *data, size_t len, Message &out);
IPC_API bool decode(const std::vector<uint8_t> &frame, Message &out);
/// Blocking: read exactly one length-prefixed message from a FILE*.
/// Returns false on EOF or read error.
bool read_message(Message &out, FILE *in = stdin);
IPC_API bool read_message(Message &out, FILE *in = stdin);
/// Write one length-prefixed message to a FILE*. Flushes after write.
/// Returns false on write error.
bool write_message(const Message &msg, FILE *out = stdout);
IPC_API bool write_message(const Message &msg, FILE *out = stdout);
} // namespace ipc

View File

@ -2,10 +2,18 @@ cmake_minimum_required(VERSION 3.20)
project(kbot CXX)
add_library(kbot STATIC
kbot.cpp
llm_client.cpp
)
option(POLYMECH_KBOT_SHARED "Build kbot as a shared library (DLL/so)" OFF)
set(_kbot_sources kbot.cpp llm_client.cpp)
if(POLYMECH_KBOT_SHARED)
add_library(kbot SHARED ${_kbot_sources})
target_compile_definitions(kbot PRIVATE POLYMECH_BUILDING_LIBRARY)
else()
add_library(kbot STATIC ${_kbot_sources})
target_compile_definitions(kbot PRIVATE POLYMECH_STATIC_BUILD=1)
target_compile_definitions(kbot INTERFACE POLYMECH_STATIC_BUILD=1)
endif()
target_include_directories(kbot PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
@ -17,3 +25,25 @@ target_link_libraries(kbot PUBLIC
json
oai
)
if(POLYMECH_KBOT_SHARED)
set_target_properties(kbot PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/dist"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_SOURCE_DIR}/dist"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_SOURCE_DIR}/dist"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/dist"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/dist"
)
endif()
install(TARGETS kbot
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/kbot.h
${CMAKE_CURRENT_SOURCE_DIR}/llm_client.h
${CMAKE_CURRENT_SOURCE_DIR}/polymech_export.h
DESTINATION include/polymech
)

View File

@ -1,5 +1,6 @@
#pragma once
#include "polymech_export.h"
#include <string>
#include <vector>
#include <memory>
@ -65,8 +66,8 @@ struct KBotCallbacks {
std::function<void(const std::string& type, const std::string& json)> onEvent;
};
int run_kbot_ai_pipeline(const KBotOptions& opts, const KBotCallbacks& cb);
int run_kbot_run_pipeline(const KBotRunOptions& opts, const KBotCallbacks& cb);
POLYMECH_API int run_kbot_ai_pipeline(const KBotOptions& opts, const KBotCallbacks& cb);
POLYMECH_API int run_kbot_run_pipeline(const KBotRunOptions& opts, const KBotCallbacks& cb);
} // namespace kbot
} // namespace polymech

View File

@ -14,7 +14,7 @@ struct LLMResponse {
std::string provider_meta_json;
};
class LLMClient {
class POLYMECH_API LLMClient {
public:
// Initialize the client with the options (api_key, model, router).
explicit LLMClient(const KBotOptions& opts);

View File

@ -14,7 +14,7 @@ CLI::App* setup_cmd_kbot(CLI::App& app);
int run_cmd_kbot_ai();
int run_cmd_kbot_run();
/// IPC / UDS Entry points
/// IPC / UDS Entry points (implemented in src/cmd_kbot*.cpp — not in libkbot; compile those TU into your binary).
int run_kbot_ai_ipc(const std::string& payload, const std::string& jobId, const kbot::KBotCallbacks& cb);
int run_kbot_run_ipc(const std::string& payload, const std::string& jobId, const kbot::KBotCallbacks& cb);