Skip to main content

persona-runtime

The conversation and agentic engine for Open Persona: the turn loop, the prompt builder, the router, and the plan, act, reflect cycle.

License: MIT. Free for any use, including commercial.

persona-runtime is the orchestration layer of Open Persona. It turns a persona-core persona into a running conversational agent, and it depends only on persona-core. No HTTP, no database, no secrets.

What it is

One turn through the runtime: the message arrives, memory is recalled, a model is picked by tier, tools run, the reply streams, and the writes and metering happen afterwards.

The one turn the runtime owns, from arrival to the writes that happen after the reply.

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 and RetrievedContext. Assembles the system prompt from identity, constraints, retrieved chunks, and the skill index, with a context window budget reducer. It also renders the graph knowledge block: an additive, independent source of what is known about the user, drawn from the shared knowledge graph, relevance gated and budgeted alongside the persona's own memory, with a versioned usage guidance artifact and a wellbeing care slot.
  • retrieve_context, the per turn conditioning retrieval (identity via get_all, the rest via query). It was extracted so the voice trunk shares the same conditioning instead of reimplementing it. Optionally enriched with an owner scoped graph retrieval (graph_selection.make_graph_retrieval), queried independently of the persona stores.
  • Routing, a deliberate surface to tier policy (routing/policy.py). PolicyRouter sits behind the Router Protocol and resolves each surface's stated tier: chat, authoring, and agentic get frontier, voice gets the latency tier, background gets small, recognition gets mid. A persona's pinned tier_for_generation still wins, because a deliberate override is honored. Layer-1 capability constraints such as vision still filter first. The earlier machinery is retained dormant: HeuristicRouter (the original rules), UnifiedRouter (constraint filter plus sweet spot scoring), and the IntelligentRouter model within tier scorer, the last of which is gated globally by PERSONA_ROUTING_INTELLIGENT_ENABLED (default off; repopulate model metadata before re-enabling).
  • TierRegistry, a lazy cached backend registry per tier (frontier / mid / small), configured via PERSONA_{TIER}_* env triples, with small to mid to 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 / awaiting_user). Two deterministic guards keep a long run from paying twice for the same answer: a per run call ledger refuses a repeat of a call that failed (handing the model the original error and telling it to change something) and serves a repeat of an allowlisted read from what the run already has, and a cost keyed tool result pruner trims output older than the last two steps to a readable head once a step's context passes a ceiling. Both report themselves on the step trace.
  • persona_runtime.legs, the leg executor for the autonomous task model. One leg is one bounded run of the unmodified AgenticLoop, book-ended by context reconstruction and a checkpoint write. It enforces the leg box (a wall clock trip at a step boundary, never mid step), writes the checkpoint through a sink port (the api's compare and set append), and distils episodic memory at milestone granularity rather than spamming per leg. The checkpoint writer is async and comes in two: the token bounded CompactingCheckpointWriter, which cannot reason and so writes no plan, and the model backed SemanticCheckpointWriter, which reads the leg and returns merged conclusions, lessons, a plan and one next action. The deterministic one is always underneath: any timeout, refusal, malformed answer or over budget distillation delegates to it, so a leg never loses its checkpoint. A leg also carries what it already asked (the queries run and sources read), reads its own recent legs and memory before working, and records its measured shape (steps, tool calls, distinct questions, tokens, wall clock) beside its spend.
  • Safety, in the loop itself. Character adherence is a never break rule with researched carve-outs: the persona never claims to be human when sincerely asked, and never roleplays through a wellbeing signal. The turn time crisis gate takes the persona out of the loop entirely on an acute, explicit signal, backed by a trained encoder for euphemistic and non English phrasing, with documented limits. Explicit acute is the reliability claim; subtle phrasing is a named residual, not a solved problem.
  • TurnLog plus JSONLTurnLogWriter and MemoryTurnLogWriter, the per turn telemetry (model, tokens, cost, routing decision, latency, fallback), durable to JSONL or held in memory for tests.
  • persona_runtime.extraction, the LLM half of the knowledge graph write paths: the grounded extraction pipeline (versioned prompt, one model call, grounded and restrained candidates), entity resolution with the AMBIGUOUS band judge, the Synthesizer (the off critical path reflection assembly), and the on by default record_user_fact direct write tool. It feeds the core graph's one merge.

Install

pip install persona-runtime          # pulls in persona-core

Python 3.11 or newer. For workspace development from the monorepo:

git clone https://github.com/yasinhessnawi1/Open-Personas-ai.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:

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 plus PERSONA_MODEL plus PERSONA_API_KEY triple is the fallback when no per tier vars are set.

Architecture role

persona-runtime sits directly above persona-core 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 clients, zero secrets. The voice trunk (persona-voice) reuses the runtime's reply producer, so a voice turn is conditioned and routed exactly like a text turn.

Test

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. The application layer of Open Persona (persona-api, persona-web) is separately licensed PolyForm Noncommercial 1.0.0; see the root README for the full per package table.

Release files for persona-runtime 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for persona-runtime 1.2.0
File Size Uploaded
persona_runtime-1.2.0.tar.gz 745.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for persona-runtime 1.2.0
File Interpreter ABI Platform
persona_runtime-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / persona_runtime-1.2.0.tar.gz

Download URL persona_runtime-1.2.0.tar.gz
Size 745.5 kB
Tags Source
SHA-256 checksum
How to use checksums
d5258f166a6c79a37b93a0634a4f24053330932a8ad579c0d560222c3847082a
BLAKE2b-256 checksum
How to use checksums
a870ca45c66828d88bda1194172ec5b6bd7c158644e6c3997d423d664bdf683d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.9

Release files / persona_runtime-1.2.0-py3-none-any.whl

Download URL persona_runtime-1.2.0-py3-none-any.whl
Size 401.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6c00f4ed98ba39828a0fdd2411791cfb0c20b828e8798f053e5ced390b161324
BLAKE2b-256 checksum
How to use checksums
716e99e304d7609f9bbdeb779fd6dc20a571eb710751ea6c72aee61fdf5d77d2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.9

Release history Release notifications | RSS feed

1.2.1

2 release files

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page