376 lines
12 KiB
Markdown
376 lines
12 KiB
Markdown
🧭 Kompanion Architecture Overview
|
||
1. System Composition
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ Kompanion GUI │
|
||
│ - Chat & Prompt Window (bare-bones interactive shell) │
|
||
│ - Database Inspector & Settings │
|
||
│ - “Under-the-hood” Repair / Diagnostics │
|
||
└──────────────────────┬───────────────────────────────────────┘
|
||
│ Qt signals / slots
|
||
▼
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ Kompanion Management Layer / Interactive App │
|
||
│ Session context, user state, identity.json, guardrails │
|
||
│ Event dispatch to middleware │
|
||
└──────────────────────┬───────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ Middleware / Integration Bus │
|
||
│ (MCP Server + D-Bus bridge + Harmony adapter) │
|
||
│ │
|
||
│ • Receives prompts & structured messages from GUI │
|
||
│ • Parses intents / actions │
|
||
│ • Maps to available tool APIs via libKI │
|
||
│ • Emits Qt-style signals (or D-Bus signals) for: │
|
||
│ → text_output, tool_call, file_request, etc. │
|
||
│ • Converts internal tool descriptions to OpenAI Harmony JSON│
|
||
│ for external compatibility │
|
||
│ • Acts as security sandbox & audit logger │
|
||
└──────────────────────┬───────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ libKI Layer │
|
||
│ - Executes validated tool actions │
|
||
│ - Provides adapters for system utilities, MCP tools, etc. │
|
||
│ - Returns results via structured JSON events │
|
||
│ - No direct LLM exposure │
|
||
└──────────────────────────────────────────────────────────────┘
|
||
|
||
Public API Surface
|
||
Component Interface Purpose
|
||
MCP Server WebSocket / JSON-RPC Integrations and external agents
|
||
D-Bus Bridge org.kde.kompanion Desktop IPC for local tools
|
||
libKI C / C++ / Python API Tool execution, capability registration
|
||
Harmony Adapter JSON Schema Compatibility with OpenAI-style tool descriptors
|
||
2. Middleware Responsibilities
|
||
|
||
Prompt Routing & Intent Recognition
|
||
|
||
Receive structured prompt events (PromptReceived, ToolRequest, ContextUpdate).
|
||
|
||
Apply regex / template matching to map natural-language requests → tool actions.
|
||
|
||
Generate Harmony-compliant tool calls when needed.
|
||
|
||
Signal-Based Event Model
|
||
|
||
Expose agent state as Qt signals:
|
||
|
||
```cpp
|
||
signals:
|
||
void textOutput(const QString &text);
|
||
void toolRequested(const QString &toolName, const QVariantMap &args);
|
||
void fileAccessRequested(const QString &path);
|
||
void actionComplete(const QString &resultJson);
|
||
```
|
||
|
||
The GUI subscribes to these, while libKI listens for action triggers.
|
||
|
||
Language–Tool Mapping Layer
|
||
|
||
Uses a registry of regular expressions and language patterns:
|
||
|
||
```json
|
||
{
|
||
"regex": "open (.*) in editor",
|
||
"tool": "file.open",
|
||
"args": { "path": "{1}" }
|
||
}
|
||
```
|
||
|
||
Each mapping can be exported/imported in Harmony tool schema:
|
||
|
||
```json
|
||
{
|
||
"name": "file.open",
|
||
"description": "Open a file in the editor",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": { "path": { "type": "string" } }
|
||
}
|
||
}
|
||
```
|
||
|
||
Security & Guardrails
|
||
|
||
Middleware verifies that tool calls comply with the active identity.json guardrails.
|
||
|
||
D-Bus and MCP servers expose only whitelisted methods.
|
||
|
||
All tool invocations are logged with timestamp, user, and hash.
|
||
|
||
Interoperability
|
||
|
||
The Harmony adapter serializes Kompanion tool metadata to the OpenAI format, so external LLMs can call Kompanion tools safely.
|
||
|
||
Conversely, Harmony JSON from OpenAI APIs can be wrapped into libKI calls for local execution.
|
||
|
||
3. Data Flow Example
|
||
|
||
User Prompt → GUI → Middleware → libKI → Middleware → GUI
|
||
|
||
1. Prompt: "List running containers."
|
||
2. Middleware regex matches → tool `docker.list`
|
||
3. Emits `toolRequested("docker.list", {})`
|
||
4. libKI executes, returns JSON result
|
||
5. Middleware emits `textOutput()` with formatted result
|
||
|
||
If the same request comes from an OpenAI API:
|
||
|
||
Harmony JSON tool call → parsed by Middleware → identical libKI action executed.
|
||
|
||
4. Key Design Goals
|
||
|
||
- Human-grade transparency: every action is signalized; nothing hidden.
|
||
- Replaceable backend: libKI can wrap any execution layer (Python, Rust, C++).
|
||
- Unified schema: one tool description format (Harmony) across OpenAI and Kompanion.
|
||
- Extensibility: new tools register dynamically via D-Bus or MCP messages.
|
||
- Auditability: all interactions logged to structured database.
|
||
|
||
---
|
||
|
||
## 5. Interface Diagrams & Example Code
|
||
|
||
### 5.1 Component Classes & Signals (Qt-style)
|
||
|
||
```
|
||
┌──────────────────────┐
|
||
| KompanionGui |
|
||
|-----------------------|
|
||
| + promptUser() |
|
||
| + showText(QString) |
|
||
| + showError(QString) |
|
||
└────────┬──────────────┘
|
||
|
|
||
| signal: userPrompted(QString prompt)
|
||
|
|
||
┌────────▼──────────────┐
|
||
| KompanionController |
|
||
| (Middleware layer) |
|
||
|------------------------|
|
||
| + handlePrompt(QString)|
|
||
| + requestTool(...) |
|
||
| + outputText(...) |
|
||
└────────┬───────────────┘
|
||
|
|
||
| signal: toolRequested(QString toolName, QVariantMap args)
|
||
| signal: textOutput(QString text)
|
||
|
|
||
┌────────▼───────────────┐
|
||
| libKIExecutor |
|
||
| (Tool execution) |
|
||
|-------------------------|
|
||
| + executeTool(...) |
|
||
| + returnResult(...) |
|
||
└─────────────────────────┘
|
||
```
|
||
|
||
**Signal / slot examples**
|
||
|
||
```cpp
|
||
// KompanionGui emits when user types:
|
||
emit userPrompted(promptText);
|
||
|
||
// KompanionController connects:
|
||
connect(gui, &KompanionGui::userPrompted,
|
||
controller, &KompanionController::handlePrompt);
|
||
|
||
// Within handlePrompt():
|
||
void KompanionController::handlePrompt(const QString &prompt) {
|
||
// parse intent → determine which tool to call
|
||
QString tool = "file.open";
|
||
QVariantMap args;
|
||
args["path"] = "/home/user/file.txt";
|
||
emit toolRequested(tool, args);
|
||
}
|
||
|
||
// libKIExecutor listens:
|
||
connect(controller, &KompanionController::toolRequested,
|
||
executor, &libKIExecutor::executeTool);
|
||
|
||
void libKIExecutor::executeTool(const QString &toolName,
|
||
const QVariantMap &args) {
|
||
// call actual tool, then:
|
||
QString result = runTool(toolName, args);
|
||
emit toolResult(toolName, args, result);
|
||
}
|
||
|
||
// Controller then forwards:
|
||
connect(executor, &libKIExecutor::toolResult,
|
||
controller, &KompanionController::onToolResult);
|
||
|
||
void KompanionController::onToolResult(...) {
|
||
emit textOutput(formattedResult);
|
||
}
|
||
|
||
// GUI shows:
|
||
connect(controller, &KompanionController::textOutput,
|
||
gui, &KompanionGui::showText);
|
||
```
|
||
|
||
### 5.2 D-Bus Interface Definition (KDE / Doxygen Style)
|
||
|
||
The canonical D-Bus interface lives at: `docs/dbus/org.kde.kompanion.xml`
|
||
|
||
```xml
|
||
<!-- org.kde.kompanion.xml -->
|
||
<node>
|
||
<interface name="org.kde.kompanion.Controller">
|
||
<method name="SendPrompt">
|
||
<arg direction="in" name="prompt" type="s"/>
|
||
<arg direction="out" name="accepted" type="b"/>
|
||
</method>
|
||
<method name="CancelRequest">
|
||
<arg direction="in" name="requestId" type="s"/>
|
||
<arg direction="out" name="cancelled" type="b"/>
|
||
</method>
|
||
<signal name="TextOutput">
|
||
<arg name="text" type="s"/>
|
||
</signal>
|
||
<signal name="ToolRequested">
|
||
<arg name="toolName" type="s"/>
|
||
<arg name="args" type="a{sv}"/>
|
||
<arg name="requestId" type="s"/>
|
||
</signal>
|
||
<signal name="ToolResult">
|
||
<arg name="requestId" type="s"/>
|
||
<arg name="result" type="s"/>
|
||
<arg name="success" type="b"/>
|
||
</signal>
|
||
<property name="SessionId" type="s" access="read"/>
|
||
<property name="IdentityPath" type="s" access="read"/>
|
||
</interface>
|
||
<interface name="org.kde.kompanion.Executor">
|
||
<method name="ExecuteTool">
|
||
<arg direction="in" name="toolName" type="s"/>
|
||
<arg direction="in" name="args" type="a{sv}"/>
|
||
<arg direction="out" name="requestId" type="s"/>
|
||
</method>
|
||
<method name="Cancel">
|
||
<arg direction="in" name="requestId" type="s"/>
|
||
</method>
|
||
<signal name="Progress">
|
||
<arg name="requestId" type="s"/>
|
||
<arg name="message" type="s"/>
|
||
<arg name="percent" type="d"/>
|
||
</signal>
|
||
</interface>
|
||
</node>
|
||
```
|
||
|
||
### 5.3 Object Paths / Service Names
|
||
|
||
- Service: `org.kde.kompanion`
|
||
- Root path: `/org/kde/kompanion`
|
||
- Controller object: `/org/kde/kompanion/Controller`
|
||
- Executor object: `/org/kde/kompanion/Executor`
|
||
|
||
---
|
||
|
||
## 6. Harmony Adapter (OpenAI Compatibility)
|
||
|
||
**Goal:** translate native libKI tool metadata to/from OpenAI Harmony JSON so Kompanion tools work via OpenAI interfaces.
|
||
|
||
### 6.1 Native → Harmony
|
||
|
||
```json
|
||
{
|
||
"name": "file.open",
|
||
"description": "Open a file in the editor",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"path": { "type": "string", "description": "Absolute or relative path" }
|
||
},
|
||
"required": ["path"]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.2 Harmony → Native
|
||
|
||
```json
|
||
{
|
||
"tool_call": {
|
||
"name": "file.open",
|
||
"arguments": { "path": "/home/user/notes.md" }
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.3 Adapter Rules
|
||
- Enforce guardrails (identity.json) before registering tools.
|
||
- Redact secret-like args per redaction patterns.
|
||
- Map Harmony types ↔ Qt/QDBus types: `string↔s`, `number↔d/x`, `boolean↔b`, `object↔a{sv}`, `array↔av`.
|
||
|
||
---
|
||
|
||
## 7. CMake & Codegen Hooks
|
||
|
||
- Place D-Bus XML at `docs/dbus/org.kde.kompanion.xml`.
|
||
- In `CMakeLists.txt`, add Qt DBus codegen targets, e.g.:
|
||
|
||
```cmake
|
||
find_package(Qt6 REQUIRED COMPONENTS Core DBus)
|
||
|
||
qt_add_dbus_adaptor(
|
||
DBUS_SRCS
|
||
${CMAKE_CURRENT_SOURCE_DIR}/docs/dbus/org.kde.kompanion.xml
|
||
src/middleware/kompanioncontroller.h KompanionController
|
||
/org/kde/kompanion/Controller org.kde.kompanion.Controller
|
||
)
|
||
|
||
qt_add_dbus_interface(
|
||
DBUS_IFACES
|
||
${CMAKE_CURRENT_SOURCE_DIR}/docs/dbus/org.kde.kompanion.xml
|
||
OrgKdeKompanion
|
||
)
|
||
|
||
add_library(dbus_gen ${DBUS_SRCS} ${DBUS_IFACES})
|
||
target_link_libraries(dbus_gen Qt6::Core Qt6::DBus)
|
||
```
|
||
|
||
(Adjust paths and targets to your tree.)
|
||
|
||
---
|
||
|
||
## 8. libKI Execution Contract (minimal)
|
||
|
||
```cpp
|
||
struct KiArg { QString key; QVariant value; };
|
||
struct KiResult { bool ok; QString mime; QByteArray data; QString json; };
|
||
|
||
class ILibKiExecutor : public QObject {
|
||
Q_OBJECT
|
||
public slots:
|
||
virtual QString execute(const QString &toolName, const QVariantMap &args) = 0; // returns requestId
|
||
virtual void cancel(const QString &requestId) = 0;
|
||
signals:
|
||
void resultReady(const QString &requestId, const KiResult &result);
|
||
void progress(const QString &requestId, const QString &message, double percent);
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Example Regex Mapping Registry
|
||
|
||
```yaml
|
||
- regex: "open (.*) in editor"
|
||
tool: file.open
|
||
args: { path: "{1}" }
|
||
- regex: "list containers"
|
||
tool: docker.list
|
||
- regex: "compose up (.*)"
|
||
tool: docker.compose.up
|
||
args: { service: "{1}" }
|
||
```
|
||
|
||
At runtime, the controller compiles these and emits `toolRequested()` on match.
|
||
|
||
---
|
||
|
||
_End of document._
|