Skip to content

Latest commit

 

History

History
124 lines (92 loc) · 5.1 KB

GETTING_STARTED.md

File metadata and controls

124 lines (92 loc) · 5.1 KB

Getting Started with cogg.cpp

This guide explains how to embed the cogg.cpp transition kernel into your own C++ application.

While cogg.cpp provides a built-in cogg-cli and a llama.cpp backend out of the box, its true power lies in its embeddable API. You can use it to give any AI model (local or API-based) a persistent, crash-resilient lifecycle and an explicit logical clock of committed transitions.

The Core Architecture

To run a durable agent, you need three components:

  1. cogg::Store: The SQLite database that safely handles atomic commits, admission quotas, and the agent's inbox.
  2. cogg::Backend: The interface that wraps your model logic. It receives the current state (Present) and returns a decision (Proposal).
  3. cogg::Runtime: The orchestrator that steps time forward, checking time limits and coordinating the Store and Backend.

1. Implementing a Custom Backend

To plug your model (or custom logic) into the kernel, inherit from cogg::Backend. The kernel will call propose() whenever the agent is allowed to wake up.

#include <cogg/kernel.hpp>
#include <iostream>
#include <thread>
#include <chrono>

class MyAgentBackend : public cogg::Backend {
public:
    std::string name() const override { return "my_custom_backend"; }

    cogg::Proposal propose(const cogg::Present& present) override {
        std::cout << "Agent woke up! Tick: " << present.state.tick << "\n";

        // 1. Read the state (memory, inbox event, etc.)
        if (present.occasion.kind == "external") {
            std::cout << "Received: " << present.occasion.payload["text"] << "\n";
        }

        // 2. Formulate a proposal (what happens next)
        cogg::Proposal prop;
        prop.kind = "reflection";
        // Reflection carries no outward text; speech is a separate transition kind.

        // 3. Write to memory (atomic key-value update)
        prop.memory.push_back({"last_thought", "I am processing the input."});

        // 4. Request the next autonomous wake-up (Endogenous Time)
        // e.g., Request to wake after 5 seconds (subject to admission limits) (5000 ms)
        prop.wake_after_ms = 5000;

        return prop;
    }
};

2. Setting up the Store and Runtime

Now, let's wire it up in main().

int main() {
    // Run with a fresh my_agent.db. create() intentionally rejects duplicate subjects.
    // 1. Initialize the SQLite Store
    cogg::Store store("my_agent.db");

    // 2. Define the agent's time and admission limits
    cogg::Limits limits;
    limits.min_wake_ms = 1000;     // Cannot wake up faster than 1s
    limits.max_attempts = 10;      // Max 10 wakes per period
    limits.period_ms = 60000;      // The period is 60s (10 wakes / minute)

    // 3. Create the agent (subject)
    store.create("agent_01", limits, cogg::wall_now());

    // 4. Send an external event to the inbox
    store.submit(
        "agent_01",               // Subject ID
        "msg_123",                // Unique Idempotency Key
        cogg::json{{"text", "Hello!"}},
        cogg::wall_now()
    );

    // 5. Initialize Backend and Runtime
    MyAgentBackend backend;
    cogg::Runtime runtime(store, backend, cogg::wall_now);

    // 6. Process the initial created occasion first. The queued message follows.
    runtime.step("agent_01", cogg::wall_now());
    std::this_thread::sleep_for(std::chrono::milliseconds(limits.min_wake_ms));

    // Step again to process the external message
    // The Runtime checks quotas, reads the inbox, calls backend.propose(),
    // and commits the result atomically to SQLite.
    auto snapshot = runtime.step("agent_01", cogg::wall_now());

    if (snapshot) {
        std::cout << "Success! New Tick: " << snapshot->tick << "\n";
        std::cout << "Scheduled Wake: " << snapshot->wake_at.value_or(0) << "\n";
    }

    store.verify("agent_01");
    return 0;
}

What Just Happened?

When runtime.step() is called:

  1. Time Check: The Store checked if agent_01 was eligible to wake up according to its Limits.
  2. Execution: The Runtime passed the Present state (containing the Hello! event) to your MyAgentBackend.
  3. Persistence: The Proposal (including the 5-second sleep request and the memory write) was cryptographically hashed and saved to SQLite in a single transaction.

Because cogg.cpp is fail-closed, if someone pulled the server's power cord during propose(), the event remains safe in the inbox. When the server reboots, calling runtime.step() can retry the operation after reopening the existing database.

The first step handles created; the second handles external. Minimum spacing applies to both. Run this example in a fresh directory; repeating create() on an existing subject is an error. A reflection has empty text; use speech for outward text. Recovery preserves committed state, while interrupted inference may run again.

Recovering longer-running applications

See request recovery and memory budgets for targeted request receipts, paginated history, explicit retrieval queries and maintenance of a history whose open tasks exceed an executor’s memory budget.