Metadata-Version: 2.4
Name: persona-runtime
Version: 1.0.0
Summary: Generation loop, router, and agentic engine for persona-core.
Project-URL: Homepage, https://github.com/yasinhessnawi1/Open-Persona
Project-URL: Repository, https://github.com/yasinhessnawi1/Open-Persona
Project-URL: Issues, https://github.com/yasinhessnawi1/Open-Persona/issues
Project-URL: Changelog, https://github.com/yasinhessnawi1/Open-Persona/blob/main/CHANGELOG.md
Author-email: Yasin Hessnawi <yasinhessnawi1@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agent,agentic,ai,llm,persona,router,tool-use
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: persona-core
Requires-Dist: tiktoken<1,>=0.7
Description-Content-Type: text/markdown

# persona-runtime

> The MIT-licensed conversation and agentic engine for Open Persona — the loop,
> the prompt builder, the router, and the agentic plan-act-reflect cycle.

**License:** [MIT](LICENSE) — free for any use, including commercial.

`persona-runtime` is the orchestration layer of [Open Persona](../../README.md).
It turns a [`persona-core`](../core/README.md) persona into a running
conversational agent and depends only on `persona-core` — no HTTP, no database,
no secrets.

## What it is

The runtime owns the per-turn lifecycle and the agentic loop, and nothing else.
Every collaborator (the persona registry, the model tiers, the toolbox, the
conversation object) is injected by the composition root — the API in production,
the CLI for local use, the tests in CI. The loop itself is stateless per request.

- **`ConversationLoop`** — the one-turn keystone: retrieve typed-memory context,
  manage history (summarise-and-compact at K=10, keep the last 5 turns verbatim),
  build the prompt, route, stream-generate with a tool-call sub-loop, and write the
  turn back to the episodic store.
- **`PromptBuilder`** + `RetrievedContext` — assembles the system prompt from
  identity + constraints + retrieved chunks + the skill index, with a
  context-window budget reducer.
- **`retrieve_context`** — the per-turn conditioning retrieval (identity via
  `get_all`, the rest via `query`), extracted so the voice trunk shares the *same*
  conditioning rather than reimplementing it.
- **Routing** — `Router` (a `@runtime_checkable` Protocol) with `HeuristicRouter`
  (rule-based, per-turn, per-persona-overridable) and `UnifiedRouter` (two-layer:
  hard constraint-filter then sweet-spot scoring with bounded fallback). Plus the
  opt-in **`IntelligentRouter`**: after the rules pick a tier, it scores the
  candidate models in that tier on cost / quality / latency (with a hard capability
  gate) using published metadata — deterministic, no ML, off by default, and
  degrading to the slot-0 model on any metadata miss.
- **`TierRegistry`** — a lazy-cached backend registry per tier (`frontier` / `mid`
  / `small`), configured via `PERSONA_{TIER}_*` env triples, with
  small→mid→frontier fallback and cross-provider multi-model per tier.
- **`AgenticLoop`** — the plan-act-reflect cycle: one model decides at each step
  whether to call a tool, ask the user, or produce a final answer, with
  step-history compaction at the tier budget, a cancel-token boundary, and an
  authoritative terminal status (`completed` / `max_steps_reached` / `cancelled` /
  `error`).
- **`TurnLog`** + `JSONLTurnLogWriter` / `MemoryTurnLogWriter` — per-turn telemetry
  (model, tokens, cost, routing decision, latency, fallback), durable to JSONL or
  held in memory for tests.

## Install

```bash
pip install persona-runtime          # pulls in persona-core
```

Python ≥ 3.11. For workspace development from the monorepo:

```bash
git clone https://github.com/yasinhessnawi1/Open-Persona.git
cd Open-Persona
uv sync --all-packages
```

## Quickstart

`persona-runtime` is a library with no CLI of its own; compose it on top of
`persona-core`:

```python
import asyncio
from pathlib import Path

from persona.schema.persona import Persona
from persona.schema.conversation import Conversation, ConversationMessage
from persona.registry import PersonaRegistry
from persona.stores.chroma import ChromaMemoryStore
from persona.tools.toolbox import Toolbox
from persona_runtime import (
    ConversationLoop, PromptBuilder, Router, tier_registry_from_env,
)


async def main() -> None:
    persona = Persona.from_yaml(Path("examples/astrid_tenancy_law.yaml"))
    registry = PersonaRegistry(store=ChromaMemoryStore.local("./.persona-data"))
    registry.load(persona)
    tiers = tier_registry_from_env()

    loop = ConversationLoop(
        registry=registry,
        tiers=tiers,
        router=Router(),
        prompt_builder=PromptBuilder(),
        toolbox=Toolbox.empty(),
    )

    conversation = Conversation.new(persona_id=persona.id)
    user = ConversationMessage(role="user", content="Hva sier husleieloven om mugg?", created_at=None)
    async for chunk in loop.turn(conversation, user):
        print(chunk.delta, end="", flush=True)
    await tiers.aclose()


asyncio.run(main())
```

## Configuration

Each tier is configured by an env triple (see `.env.example` at the repo root):

```
PERSONA_FRONTIER_PROVIDER=anthropic   PERSONA_FRONTIER_MODEL=claude-opus-...
PERSONA_MID_PROVIDER=deepseek         PERSONA_MID_MODEL=deepseek-chat
PERSONA_SMALL_PROVIDER=groq           PERSONA_SMALL_MODEL=llama-...
```

A single `PERSONA_PROVIDER` + `PERSONA_MODEL` + `PERSONA_API_KEY` triple is the
fallback when no per-tier vars are set.

## Architecture role

`persona-runtime` sits directly above [`persona-core`](../core/README.md) and
below `persona-api`. The API composes the runtime, attaches it to HTTP routes, and
persists the per-request state (conversation, run, turn-log, event bus); the
runtime contains zero HTTP, zero database client, zero secrets. The voice trunk
([`persona-voice`](../voice/README.md)) reuses the runtime's reply producer so a
voice turn is conditioned and routed exactly like a text turn.

## Test

```bash
uv run pytest packages/runtime                 # unit (default)
uv run pytest packages/runtime -m integration  # integration
uv run mypy packages/runtime/src
uv run ruff check packages/runtime
```

## License

`persona-runtime` is licensed under the **MIT License** — free for any use,
including commercial. See [LICENSE](LICENSE). The application layer of Open Persona
(`persona-api`, `persona-web`) is separately licensed PolyForm Noncommercial
1.0.0; see the [root README](../../README.md) for the full per-package table.

## Links

- [Open Persona — root README](../../README.md)
- [`persona-core`](../core/README.md) — the schema, memory stores, backends, tools
- [`persona-voice`](../voice/README.md) — the real-time voice trunk
- [CHANGELOG](CHANGELOG.md)
