# Python SDK reference

> Exported public API from `hermes_okf`: `OKFBundle` CRUD/log/graph methods, `Concept` fields, `SearchIndex` query methods, `GraphExtractor` traversal, `HermesMemory` recall APIs, `HermesAgent` plan/snapshot/tool methods, and `get_provider()`.

- Repository: EliaszDev/hermes-okf
- GitHub: https://github.com/EliaszDev/hermes-okf
- Human docs: https://grok-wiki.com/public/docs/eliaszdev-hermes-okf-b71befaafe02
- Complete Markdown: https://grok-wiki.com/public/docs/eliaszdev-hermes-okf-b71befaafe02/llms-full.txt

## Source Files

- `src/hermes_okf/__init__.py`
- `src/hermes_okf/bundle.py`
- `src/hermes_okf/search.py`
- `src/hermes_okf/graph.py`
- `src/hermes_okf/memory.py`
- `src/hermes_okf/hermes.py`
- `src/hermes_okf/hermes_integration.py`

---

---
title: "Python SDK reference"
description: "Exported public API from `hermes_okf`: `OKFBundle` CRUD/log/graph methods, `Concept` fields, `SearchIndex` query methods, `GraphExtractor` traversal, `HermesMemory` recall APIs, `HermesAgent` plan/snapshot/tool methods, and `get_provider()`."
---

The `hermes_okf` package (v0.4.6) exposes a filesystem-backed OKF bundle API, in-memory search and graph utilities, agent memory helpers, and a Hermes-native provider entry point. Import the public surface from the package root:

```python
from hermes_okf import (
    OKFBundle,
    Concept,
    SearchIndex,
    GraphExtractor,
    HermesMemory,
    HermesAgent,
    HermesOKFProvider,
    get_provider,
    OKFValidator,
)
```

```mermaid
classDiagram
    class OKFBundle {
        +Path root
        +read_concept(id)
        +write_concept(id, body, **frontmatter)
        +delete_concept(id)
        +list_concepts(subdir)
        +append_log(entry, category)
        +get_graph_edges()
    }
    class Concept {
        +str id
        +str type
        +str title
        +list tags
        +str body
        +dict metadata
    }
    class SearchIndex {
        +search(query, top_k)
        +search_concepts(query, top_k)
        +fuzzy_search(query, threshold)
    }
    class GraphExtractor {
        +traverse(start_id, max_depth)
        +get_backlinks(id)
        +to_networkx()
    }
    class HermesMemory {
        +recall(query, top_k)
        +record_decision(decision)
        +register_project(id, title)
    }
    class HermesAgent {
        +create_plan(task, steps)
        +snapshot(note)
        +build_context(query)
    }
    class HermesOKFProvider {
        +on_session_start(id)
        +on_memory_write(target, content)
        +search(query, top_k)
    }
    OKFBundle --> Concept : parses/writes
    SearchIndex --> OKFBundle
    GraphExtractor --> OKFBundle
    HermesMemory --> OKFBundle
    HermesMemory --> SearchIndex
    HermesAgent --> HermesMemory : .memory
    HermesOKFProvider --> HermesAgent : .agent
```

<Info>
`HermesMemoryMixin` and decorator helpers (`wrap_decision`, `wrap_tool`, `wrap_observation`, `with_context`) live in `hermes_okf.agent` and are used by `HermesAgent`. They are not re-exported from `__all__` but are the integration surface for custom agent classes.
</Info>

## Package exports

| Symbol | Module | Role |
|--------|--------|------|
| `OKFBundle` | `bundle` | Filesystem CRUD, log, tag search, link edges |
| `Concept` | `concept` | Dataclass for one `.md` concept file |
| `SearchIndex` | `search` | Inverted-index full-text search |
| `GraphExtractor` | `graph` | Link graph traversal, tag clusters, NetworkX export |
| `HermesMemory` | `memory` | Agent-oriented recall, decisions, projects |
| `HermesAgent` | `hermes` | Session, tool, plan, snapshot lifecycle |
| `HermesOKFProvider` | `hermes_integration` | Hermes session/memory/tool hooks |
| `get_provider()` | `hermes_integration` | Singleton `HermesOKFProvider` |
| `OKFValidator` | `validators` | Bundle and file conformance checks |

## `Concept`

Each concept maps to one `.md` file with YAML frontmatter and a markdown body. `OKFBundle._parse_concept` populates the dataclass; unknown frontmatter keys are preserved in `metadata`.

<ResponseField name="id" type="string">
Relative path without `.md` (e.g. `projects/my_project`).
</ResponseField>

<ResponseField name="type" type="string">
OKF `type` frontmatter field. Defaults to `"Unknown"` when absent.
</ResponseField>

<ResponseField name="title" type="string">
Human-readable title. Defaults to `id` when absent.
</ResponseField>

<ResponseField name="description" type="string">
Short summary from frontmatter. Defaults to `""`.
</ResponseField>

<ResponseField name="tags" type="list[string]">
Tag list for filtering and clustering. Defaults to `[]`.
</ResponseField>

<ResponseField name="resource" type="string | None">
Optional external URL reference.
</ResponseField>

<ResponseField name="timestamp" type="string | None">
ISO 8601 UTC timestamp (`YYYY-MM-DDTHH:MM:SSZ`). Auto-added by `write_concept` when omitted.
</ResponseField>

<ResponseField name="body" type="string">
Markdown body after frontmatter.
</ResponseField>

<ResponseField name="metadata" type="dict">
Full parsed frontmatter, including custom keys (e.g. `model`, `status`, `steps` on agent concepts).
</ResponseField>

## `OKFBundle`

<ParamField body="root_path" type="str | Path" required>
Bundle root directory. Created if missing; seeds `index.md`, `log.md`, and stub `projects/`, `decisions/`, `context/` indexes when `index.md` is absent.
</ParamField>

### Concept CRUD

| Method | Returns | Behavior |
|--------|---------|----------|
| `read_concept(concept_id)` | `Concept \| None` | Reads by relative ID; `None` if file missing |
| `write_concept(concept_id, body, **frontmatter)` | `Concept` | Creates parent dirs; auto-adds `timestamp` if not in frontmatter |
| `delete_concept(concept_id)` | `bool` | `True` if file existed and was removed |
| `list_concepts(subdir=None)` | `list[str]` | All concept IDs; skips `index.md` and `log.md` |
| `to_dict(concept_id)` | `dict \| None` | `dataclasses.asdict(Concept)` for JSON serialization |

`concept_id` uses forward slashes; internally normalized to OS path separators. `type` is not enforced at write time — use `OKFValidator` for conformance.

<RequestExample>

```python title="basic_usage.py pattern"
from hermes_okf import OKFBundle

bundle = OKFBundle("./my_knowledge")
bundle.write_concept(
    "projects/my_project",
    body="# My Project\n\nDescribe your project here.",
    type="Project",
    title="My Project",
    tags=["ml", "data", "gpu"],
    resource="https://github.com/YOUR_USERNAME/my-project",
)
concept = bundle.read_concept("projects/my_project")
```

</RequestExample>

### Log

| Method | Returns | Behavior |
|--------|---------|----------|
| `append_log(entry, category="Update")` | `None` | Appends `* **{category}**: {entry}` under today's `## YYYY-MM-DD` heading |
| `read_log()` | `str` | Full `log.md` contents, or `""` if missing |

### Graph and tags

| Method | Returns | Behavior |
|--------|---------|----------|
| `search_by_tag(tag)` | `list[Concept]` | Linear scan of all concepts |
| `get_graph_edges()` | `list[dict]` | Directed edges from markdown links: `source`, `target`, `context` |
| `get_neighbors(concept_id)` | `list[dict]` | Outgoing edges where `source == concept_id` |

Non-HTTP link targets ending in `.md` have the suffix stripped. External URLs are excluded.

## `SearchIndex`

Wraps an `OKFBundle` with a lazy-built inverted index (`token → [concept_id]`). Tokenization is lowercase alphanumeric (`re.findall(r"[a-z0-9]+", text.lower())`).

<ParamField body="bundle" type="OKFBundle" required>
Bundle to index.
</ParamField>

| Method | Returns | Defaults | Behavior |
|--------|---------|----------|----------|
| `search(query, top_k=10)` | `list[tuple[str, float]]` | `top_k=10` | TF-like score: matching token count / query token count |
| `search_concepts(query, top_k=10)` | `list[Concept]` | `top_k=10` | Hydrates `search()` hits |
| `filter(predicate)` | `list[Concept]` | — | All concepts where `predicate(concept)` is true |
| `fuzzy_search(query, threshold=0.6)` | `list[tuple[str, float]]` | `threshold=0.6` | Uses `rapidfuzz.fuzz.partial_ratio` when installed; otherwise token-overlap fallback |
| `invalidate()` | `None` | — | Clears cache; rebuilds on next query |

<Warning>
Call `invalidate()` after bundle writes when using a long-lived `SearchIndex`. `HermesMemory.recall` and `HermesOKFProvider.search` invalidate automatically.
</Warning>

## `GraphExtractor`

Higher-level graph navigation over `OKFBundle.get_graph_edges()` plus directory and tag structure.

| Method | Returns | Behavior |
|--------|---------|----------|
| `get_edges()` | `list[dict]` | Delegates to `bundle.get_graph_edges()` |
| `get_neighbors(concept_id)` | `list[str]` | Outgoing target IDs |
| `get_backlinks(concept_id)` | `list[str]` | Incoming source IDs |
| `get_children(concept_id)` | `list[str]` | Sibling concepts in the same directory (excludes `index.md` and self) |
| `get_tag_clusters()` | `dict[str, list[str]]` | `tag → [concept_id]` |
| `traverse(start_id, max_depth=3)` | `dict` | BFS link traversal; nested tree with `id`, `title`, `type`, `depth`, optional `children` |
| `to_networkx()` | `networkx.DiGraph` | Nodes carry `concept.metadata`; edges carry `context`. Raises `ImportError` if `networkx` is not installed |

## `HermesMemory`

Agent-facing memory layer over `OKFBundle` with a bundled `SearchIndex`.

<ParamField body="bundle_path" type="str" required>
OKF bundle root path.
</ParamField>

<ParamField body="agent_id" type="str">
Identifier used in log entries. Default: `"hermes"`.
</ParamField>

### Session and logging

| Method | Returns | Behavior |
|--------|---------|----------|
| `start_session(session_id=None)` | `str` | Logs session start; generates timestamp ID if `session_id` is `None` |
| `end_session(session_id)` | `None` | Logs session end |
| `record_decision(decision, rationale=None, tags=None)` | `Concept` | Writes under `decisions/{slug}_{date}` with `type="Decision"` |
| `record_observation(observation, category="Observation", tags=None)` | `None` | Appends to `log.md` |
| `record_tool_call(tool_name, result_summary)` | `None` | Appends with category `Tool-Call` |

### Recall

| Method | Returns | Defaults | Behavior |
|--------|---------|----------|----------|
| `recall(query, top_k=5)` | `list[Concept]` | `top_k=5` | Invalidates index, then `search_concepts` |
| `recall_by_tag(tag)` | `list[Concept]` | — | Delegates to `bundle.search_by_tag` |
| `recall_project(project_name)` | `Concept \| None` | — | Exact title match or ID suffix match under `projects/` |
| `get_recent_log(n_lines=50)` | `str` | `n_lines=50` | Last *n* lines of `log.md` |
| `get_decisions()` | `list[Concept]` | — | Concepts tagged `decision` |

### Projects

| Method | Returns | Behavior |
|--------|---------|----------|
| `register_project(project_id, title, description="", tags=None, resource=None)` | `Concept` | Writes `projects/{project_id}` with `type="Project"` |
| `update_project(project_id, body, **metadata)` | `Concept` | Merges existing metadata, overwrites body |

## `HermesAgent`

Extends `HermesMemoryMixin`; stores full agent state in the OKF bundle. On construction: ensures `config/`, `tools/`, `sessions/`, `plans/`, `plans/archive/` directories, loads `config/agent`, and calls `start_session()`.

<ParamField body="bundle_path" type="str" required>
Agent OKF bundle root.
</ParamField>

<ParamField body="agent_id" type="str" required>
Unique agent identifier.
</ParamField>

<ParamField body="model" type="str">
Default LLM model. Default: `"gpt-4o"`. Overridden by `config/agent` metadata when present.
</ParamField>

### Session lifecycle

| Method | Returns | Behavior |
|--------|---------|----------|
| `start_session(session_id=None)` | `str` | Creates `sessions/{id}` concept with `status="active"` |
| `end_session()` | `None` | Sets current session `status="completed"`, clears `current_session_id` |
| `list_sessions()` | `list[str]` | Sorted session concept IDs |
| `recall_session(session_id=None)` | `Concept \| None` | Specific session or most recent |

### Tool registry

| Method | Returns | Behavior |
|--------|---------|----------|
| `register_tool(name, description, schema=None, example="")` | `None` | Writes `tools/{name}` with `type="Tool"`; `schema` stored as JSON string |
| `list_tools()` | `list[str]` | Concept IDs under `tools/` |
| `get_tool(name)` | `Concept \| None` | Reads `tools/{name}` |

### Plans

| Method | Returns | Behavior |
|--------|---------|----------|
| `create_plan(task, steps)` | `str` | Plan ID under `plans/`; sets `current_plan_id`; logs observation |
| `complete_step(step_index, result="")` | `None` | Checks off step in body; updates `progress` and `status` |
| `archive_plan(plan_id=None)` | `None` | Copies to `plans/archive/`, deletes original |

`complete_step` is a no-op when `current_plan_id` is unset or `step_index` is out of range.

### Snapshots and context

| Method | Returns | Behavior |
|--------|---------|----------|
| `snapshot(note="")` | `None` | Writes `snapshots/{timestamp}` with JSON state: `agent_id`, `model`, `current_session`, `current_plan`, `system_prompt`, `note` |
| `restore(snapshot_id=None)` | `dict[str, Any]` | Restores from given ID or latest snapshot; returns metadata dict (empty if none) |
| `build_context(query, top_k=5)` | `str` | Markdown context: system prompt, active plan, recalled memory, recent log (20 lines), tool list |

Access underlying memory via `agent.memory` (`HermesMemory`).

## `HermesOKFProvider` and `get_provider()`

Hermes-native memory provider that auto-loads `HermesOKFConfig` from environment (`HERMES_OKF_*`), `~/.hermes/hermes-okf.yaml`, and `~/.hermes/config.yaml` → `plugins.hermes_okf`.

```python
from hermes_okf import get_provider

provider = get_provider()  # singleton; first call constructs HermesOKFProvider()
provider.on_session_start("session-123")
provider.on_memory_write("memory", "User prefers Python")
provider.search("Python preferences", top_k=5)
```

### Session and memory hooks

| Method | Behavior |
|--------|----------|
| `on_session_start(session_id)` | Starts agent session; optional auto-snapshot |
| `on_session_end(session_id)` | Flushes hot buffer, ends session; optional auto-snapshot |
| `on_memory_write(target, content)` | `target="memory"` → hot observation; `target="user"` → `UserProfile` concept |
| `on_tool_call(tool_name, args, result)` | Hot-buffered tool call; lazy tool registration |
| `on_decision(decision, rationale="", tags=None)` | Hot-buffered decision when `log_decisions` is true |

### Plan hooks

| Method | Returns | Behavior |
|--------|---------|----------|
| `on_plan_create(plan_name, steps)` | `str` | Delegates to `agent.create_plan` |
| `on_plan_step_complete(plan_id, step_index, result="")` | `None` | Delegates to `agent.complete_step` |
| `on_plan_complete(plan_id)` | `None` | Archives plan, flushes hot buffer, optional snapshot |

### Search, state, and RAG

| Method | Returns | Behavior |
|--------|---------|----------|
| `search(query, top_k=10)` | `list[tuple[str, float]]` | Full-text search over bundle |
| `recall_by_tag(tag)` | `list` | Tag recall via `agent.memory` |
| `build_context(query, top_k=5)` | `str` | Delegates to `agent.build_context` |
| `snapshot(note="")` | `None` | Flushes hot buffer, then `agent.snapshot` |
| `restore()` | `dict` | Flushes hot buffer, restores latest snapshot |
| `resume()` | `None` | `restore()` plus resume observation |
| `rag_search(query, top_k=5)` | `list[dict]` | Chroma semantic search; requires `hermes-okf[rag]` |

Unrecognized attribute access on `HermesOKFProvider` delegates to the wrapped `HermesAgent` via `__getattr__`.

### Hot memory buffer

When `use_hot_memory` is true (default), `HotMemoryBuffer` batches writes (`observation`, `decision`, `tool_call`) and flushes to the cold OKF archive on session end, buffer max (`hot_memory_max`, default 50), explicit `snapshot`/`restore`, or plan completion.

## `OKFValidator`

| Method | Returns | Behavior |
|--------|---------|----------|
| `validate()` | `list[OKFValidationError]` | Checks reserved files (`index.md`, `log.md`) and all concepts for frontmatter + required `type` |
| `is_valid()` | `bool` | `len(validate()) == 0` |
| `validate_file(path)` (static) | `list[OKFValidationError]` | Single-file check without bundle context |
| `quick_check(path)` (static) | `bool` | `validate_file` with no errors |

`OKFValidationError` exposes `file`, `message`, and optional `line`.

## `HermesMemoryMixin` (integration helper)

Import from `hermes_okf.agent` for custom agent classes:

| Method | Behavior |
|--------|----------|
| `wrap_decision(fn)` | Decorates method to persist return value as `Decision` |
| `wrap_observation(fn)` | Logs each call as observation |
| `wrap_tool(fn)` | Logs each call as tool call |
| `with_context(query, top_k=3)` | Returns `memory.recall(query, top_k)` |

Apply wrappers in `__init__` after `super().__init__(bundle_path, agent_id=...)`:

```python
from hermes_okf.agent import HermesMemoryMixin

class MyAgent(HermesMemoryMixin):
    def __init__(self):
        super().__init__("./knowledge", agent_id="my-agent")
        self.choose_model = self.wrap_decision(self.choose_model)
```

## Error and constraint summary

| Surface | Condition | Outcome |
|---------|-----------|---------|
| `read_concept` | Missing file | `None` |
| `delete_concept` | Missing file | `False` |
| `write_concept` | Missing `type` in frontmatter | Writes succeed; `OKFValidator` reports error |
| `SearchIndex.search` | Empty query tokens | `[]` |
| `GraphExtractor.to_networkx` | `networkx` not installed | `ImportError` with install hint |
| `SearchIndex.fuzzy_search` | `rapidfuzz` not installed | Token-overlap fallback |
| `HermesAgent.complete_step` | No active plan or bad index | Silent no-op |
| `HermesAgent.restore` | No snapshots | `{}` |
| `HermesOKFProvider.rag_search` | Missing `[rag]` extras | `ImportError` |
| `get_provider` | First call | Constructs singleton from resolved config |

<Note>
Core SDK depends only on `pyyaml`. Optional capabilities: `rapidfuzz` (fuzzy search), `networkx` (graph export), `hermes-okf[rag]` (vector retrieval via LangChain + Chroma).
</Note>

## Related pages

<CardGroup>
<Card title="OKF bundle model" href="/okf-bundle-model">
Filesystem layout, frontmatter rules, and Hermes-native directories that `OKFBundle` and `HermesAgent` create.
</Card>
<Card title="Python agent integration" href="/python-agent-integration">
`HermesMemoryMixin`, decorator wrappers, and wiring memory into custom agent classes.
</Card>
<Card title="Hermes provider integration" href="/hermes-provider-integration">
`HermesOKFProvider` session hooks, hot/cold memory, and Hermes `MemoryProvider` plugin surface.
</Card>
<Card title="Example: OKF bundle basics" href="/example-okf-bundle-basics">
Copy-paste `OKFBundle` workflow from `examples/basic_usage.py`.
</Card>
<Card title="Example: full agent" href="/example-full-agent">
End-to-end `HermesAgent` session, plan, and snapshot lifecycle.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
`HermesOKFConfig` fields and `HERMES_OKF_*` environment variables used by `get_provider()`.
</Card>
</CardGroup>
