Skip to main content

Generation loop, router, and agentic engine for persona-core.

Project description

persona-runtime

Conversation loop, prompt builder, router, and agentic engine for Persona. Source-available; noncommercial use only.

Status: PolyForm Noncommercial 1.0.0 · Source Available (Noncommercial Use Only)

What it is

persona-runtime is the orchestration layer that turns a persona-core persona into a running conversational agent. It owns six things and nothing else:

  • ConversationLoop — the one-turn keystone: retrieve typed-memory context, manage history (summarise-and-compact at K=10, keep last 5 verbatim), build the prompt, route, stream-generate with a tool-call sub-loop, write the turn back to the episodic store.
  • PromptBuilder + RetrievedContext — assembles the system prompt from identity + constraints + retrieved chunks + skill index, with a context-window budget reducer.
  • RoutingRouter (@runtime_checkable Protocol) with two concrete implementations: HeuristicRouter (rule-based, per-turn, per-persona-overridable) and UnifiedRouter (two-layer: hard-filter via apply_constraint_filter then sweet-spot scoring, with bounded fallback and per-tier metadata).
  • TierRegistry — lazy-cached backend registry per tier (frontier / mid / small); configured via PERSONA_{TIER}_* env triples; small→mid→frontier fallback; cross-provider multi-model per tier (Spec 20).
  • AgenticLoop — the plan-act-reflect cycle in persona_runtime.agentic: one model decides at each step whether to call a tool, ask the user, or produce a final answer; [ASK_USER] / [FINAL] markers as the primary classification signal; step-history compaction at the tier budget; cancel-token boundary; terminal status (completed / max_steps_reached / cancelled / error) authoritative.
  • TurnLog + JSONLTurnLogWriter / MemoryTurnLogWriter — per-turn telemetry record (model, tokens, cost, routing decision, latency, fallback) durable to JSONL or held in memory for tests.

The runtime depends only on persona-core; it does not depend on the API or web app. The composition root (the API in production, the CLI for local use, the tests in CI) owns the Conversation object and the TierRegistry lifecycle — the loop itself is stateless per request.

Install

From PyPI (planned):

pip install persona-runtime

Workspace development:

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

Run

persona-runtime is a library — 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, TurnLog, 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())

Env vars (per tier; 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 pair is the fallback when no per-tier vars are set.

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

Architecture role

persona-runtime is layer 3 of the Open Persona stack. It 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 client, zero secrets — every collaborator is injected by the composition root.

Contribute

Contributions welcome under the same PolyForm Noncommercial 1.0.0 license. The package is source-available for noncommercial use; commercial use requires a separate license — contact the rights holder. Issues and pull requests welcome at github.com/yasinhessnawi1/Open-Persona. See CHANGELOG.md for the spec-by-spec history.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

persona_runtime-0.1.0.tar.gz (190.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

persona_runtime-0.1.0-py3-none-any.whl (111.9 kB view details)

Uploaded Python 3

File details

Details for the file persona_runtime-0.1.0.tar.gz.

File metadata

  • Download URL: persona_runtime-0.1.0.tar.gz
  • Upload date:
  • Size: 190.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.9

File hashes

Hashes for persona_runtime-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f7fdff87f0b6e77fbe04458d7848549d4ec4fa25a1d335c72d8f8306238da427
MD5 d15dec2a064d92fcedbce943b41c4da0
BLAKE2b-256 0cc4dafbef8dbb06aca00bdd61ae46adf1bafbdab988058cd72c303af4fa045f

See more details on using hashes here.

File details

Details for the file persona_runtime-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for persona_runtime-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c40034e5289f44e4f2be449fc6d224de17c36c75a9bf9d2f8fcf41d7f75945ab
MD5 7a5b13cefdb0affccb52d34e9ba6ff6b
BLAKE2b-256 44e03a857202c3abb0e4f065b34b97ea752a8b2abdf6fa0beace01c9e102ab95

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page