metal-kompanion/docs/18_10_25_middleware_archite...

12 KiB
Raw Permalink Blame History

🧭 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:

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.

LanguageTool Mapping Layer

Uses a registry of regular expressions and language patterns:

{
  "regex": "open (.*) in editor",
  "tool": "file.open",
  "args": { "path": "{1}" }
}

Each mapping can be exported/imported in Harmony tool schema:

{
  "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.

  1. 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.

  1. 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

// 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

<!-- 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

{
  "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

{
  "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.:
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)

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

- 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.