Skip to main content
One immutable plan describes a single agent turn — every runtime (native, plugin, CLI) reads the same plan.

Quick Start

1

Default — nothing to change

Existing Agent(...) code paths are unchanged. PreparedTurnMixin / TurnExecutionMixin on Agent can prepare context behind the scenes when you opt in explicitly.
2

Inspect what the agent will do

from praisonaiagents import Agent
from praisonaiagents.runtime import default_context_builder

agent = Agent(name="Researcher", instructions="Find facts and cite sources.")

context = default_context_builder.build_context(agent, "Who built the Eiffel Tower?")
print(context.to_dict())
3

Run with your own harness

from praisonaiagents import Agent
from praisonaiagents.runtime import default_context_builder
from praisonaiagents.runtime.example_harness import PluginHarnessRuntime

agent = Agent(name="Writer", instructions="Summarise pull requests.")
context = default_context_builder.build_context(agent, "Summarise this PR")
result = await PluginHarnessRuntime().run_turn(context)

How It Works

StageWhat happens
Builddefault_context_builder.build_context(agent, prompt, **kwargs) resolves model, tools, transcript, delivery, correlation
FreezeContext is immutable (frozen=True, MappingProxyType on dict fields)
ExecuteAny TurnRuntimeProtocol implementation runs the same context

Public imports

from praisonaiagents.runtime import (
    PreparedTurnContext,
    TurnRuntimeProtocol,
    TurnContextBuilderProtocol,
    ModelReference,
    ToolSchema,
    TranscriptWindow,
    DeliveryChannels,
    SessionCorrelation,
    RuntimeMode,
    DefaultTurnContextBuilder,
    default_context_builder,
    create_default_model_ref,
    create_empty_transcript,
    create_default_delivery,
    create_session_correlation,
)

Picking a Runtime Mode

Use caseRuntimeMode
Script, one-shot replySYNC
Inside async appASYNC
Streaming to UISTREAM (requires DeliveryChannels(enable_streaming=True, ...))
Streaming inside async appASYNC_STREAM
STREAM and ASYNC_STREAM raise ValueError if delivery.has_streaming() is false.

Configuration Options

PreparedTurnContext

FieldTypeDefaultNotes
model_refModelReferenceResolved model id, provider, capabilities
agent_runtimeAgentProtocolRuntime instance for the turn
toolsTuple[ToolSchema, ...]()Normalised tool schemas
transcriptTranscriptWindowConversation slice + system prompt
deliveryDeliveryChannelsStream emitter, callbacks, formatter
correlationSessionCorrelationsession_id / turn_id / agent_id / run_id
runtime_modeRuntimeModeSYNCsync, async, stream, async_stream
turn_metadataMapping[str, Any]{}Immutable after construction
created_atfloattime.time()Preparation timestamp
Helpers: get_tool_by_name(name), has_tools(), has_system_prompt(), get_message_count(), to_dict().

ModelReference

FieldTypeDefault
model_idstrrequired
providerstrrequired
supports_streamingboolFalse
supports_toolsboolFalse
supports_system_promptsboolTrue
max_tokensOptional[int]None
temperatureOptional[float]None
model_configMapping[str, Any]{}
Raises ValueError if model_id or provider is missing.

ToolSchema

FieldTypeDefault
namestrrequired
descriptionstrrequired
parametersdictrequired
callableOptional[Any]None
source_typestr"unknown"
metadataMapping[str, Any]{}

TranscriptWindow

FieldTypeDefault
messagesTuple[Mapping, ...]required
total_tokensint0
system_promptOptional[str]None
context_metadataMapping[str, Any]{}

DeliveryChannels

FieldTypeDefault
stream_emitterOptional[StreamEventEmitter]None
output_formatterOptional[Any]None
callbacksTuple()
async_callbacksTuple()
enable_streamingboolFalse
enable_metricsboolFalse
Helper: has_streaming().

SessionCorrelation

FieldTypeDefault
session_idOptional[str]None
turn_idOptional[str]None
agent_idOptional[str]None
run_idOptional[str]None
parent_idOptional[str]None
trace_metadataMapping[str, Any]{}

Utility constructors

  • create_default_model_ref(model_id="gpt-3.5-turbo", provider="openai")
  • create_empty_transcript(system_prompt=None)
  • create_default_delivery()
  • create_session_correlation(session_id=None, agent_id=None)

Common Patterns

Build your own harness

from typing import List
from praisonaiagents.runtime import TurnRuntimeProtocol, PreparedTurnContext, RuntimeMode

class EchoHarness:
    async def run_turn(self, context: PreparedTurnContext) -> str:
        return f"Echo: {context.get_message_count()} messages"

    def supports_runtime_mode(self, mode: RuntimeMode) -> bool:
        return mode in (RuntimeMode.SYNC, RuntimeMode.ASYNC)

    def get_supported_modes(self) -> List[RuntimeMode]:
        return [RuntimeMode.SYNC, RuntimeMode.ASYNC]
Implement TurnRuntimeProtocol: async run_turn(context), supports_runtime_mode(mode), get_supported_modes().

Stream to a UI

from praisonaiagents.runtime import DeliveryChannels, RuntimeMode, PreparedTurnContext

delivery = DeliveryChannels(enable_streaming=True, stream_emitter=my_emitter)
# Pass delivery into build_context kwargs; set runtime_mode=RuntimeMode.STREAM

Track turns across services

from praisonaiagents.runtime import create_session_correlation, default_context_builder

correlation = create_session_correlation(session_id="prod-chat-42", agent_id="researcher")
context = default_context_builder.build_context(agent, prompt, session_id=correlation.session_id)
print(context.correlation.turn_id)

Example harness templates

Reference implementations in praisonaiagents.runtime.example_harness:
  • PluginHarnessRuntime — plugin pattern, streaming-aware
  • CLIBackendHarness — CLI backend pattern
  • demonstrate_harness_integration() — run with python -m praisonaiagents.runtime.example_harness

Best Practices

Fields are frozen. Mutations must go through hooks, not field assignment.
The plan is per-turn for multi-agent safety. Do not cache it across turns.
It is a module-level singleton — no need to instantiate DefaultTurnContextBuilder yourself.
STREAM / ASYNC_STREAM require DeliveryChannels.has_streaming() — otherwise construction raises ValueError.

Runtime Selection

Model-scoped runtime configuration for each turn

Streaming

Stream agent output token-by-token