Prepared Turn Context

One immutable plan per agent turn, shared by every runtime (native, plugin, CLI).

Build one frozen turn plan from an Agent and prompt — any runtime (native, plugin, CLI) executes the same context.

python
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())

The user sends a prompt; the runtime builds one shared PreparedTurnContext for the turn.

Diagram

🤖 Agent

📋 ContextBuilder

PreparedTurnContext

Native runtime

Plugin harness

CLI backend

Quick Start

  1. Simple Usage

    Inspect what the agent will send on the next turn:

    python
    from praisonaiagents import Agent
    from praisonaiagents.runtime import default_context_builder
    
    agent = Agent(name="Writer", instructions="Summarise pull requests.")
    context = default_context_builder.build_context(agent, "Summarise this PR")
    
    print(context.has_tools(), context.get_message_count())
    print(context.to_dict())
  2. With Configuration

    Run the same context through your own runtime by implementing TurnRuntimeProtocol:

    python
    import asyncio
    from typing import List
    from praisonaiagents import Agent
    from praisonaiagents.runtime import (
        default_context_builder,
        PreparedTurnContext,
        RuntimeMode,
    )
    
    class MyHarness:
        async def run_turn(self, context: PreparedTurnContext) -> str:
            return f"Handled turn {context.correlation.turn_id} with {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]
    
    agent = Agent(name="Writer", instructions="Summarise pull requests.")
    context = default_context_builder.build_context(agent, "Summarise this PR")
    
    asyncio.run(MyHarness().run_turn(context))

How It Works

Diagram
RuntimePreparedTurnContextDefaultTurnContextBuilderAgentUserRuntimePreparedTurnContextDefaultTurnContextBuilderAgentUserpromptbuild_context(agent, prompt)freeze immutable planrun_turn(context)response
StageWhat happens
BuildResolves model, tools (kwarg → agent.tools), transcript, delivery, correlation
FreezeContext is immutable after construction
ExecuteAny TurnRuntimeProtocol runs the same plan

Key imports: PreparedTurnContext, default_context_builder, RuntimeMode, DeliveryChannels.


Tool Resolution

build_context(agent, prompt, tools=…) resolves tools from the kwarg first, then the agent:

CallResult
build_context(agent, prompt)Uses agent.tools
build_context(agent, prompt, tools=None)Uses agent.tools
build_context(agent, prompt, tools=[])No tools this turn (explicit disable)
build_context(agent, prompt, tools=[fn])Overrides agent.tools with [fn]
python
from praisonaiagents import Agent
from praisonaiagents.runtime import default_context_builder

def lookup(topic: str) -> str:
    """Return a short fact about the topic."""
    return f"fact about {topic}"

agent = Agent(name="Researcher", instructions="Cite facts.", tools=[lookup])

# 1. Uses agent.tools
ctx1 = default_context_builder.build_context(agent, "Tell me about Paris")
assert ctx1.has_tools()

# 2. tools=None also uses agent.tools (same as omitting the kwarg)
ctx2 = default_context_builder.build_context(agent, "Tell me about Paris", tools=None)
assert ctx2.has_tools()

# 3. tools=[] explicitly disables tools for this turn
ctx3 = default_context_builder.build_context(agent, "Just chat", tools=[])
assert not ctx3.has_tools()

Configuration Options

FieldTypeNotes
runtime_modeRuntimeModeSYNC, ASYNC, STREAM, ASYNC_STREAM
deliveryDeliveryChannelsRequired for streaming modes
correlationSessionCorrelationsession_id, turn_id, agent_id, run_id

STREAM and ASYNC_STREAM raise ValueError if delivery.has_streaming() is false.


Best Practices

Treat context as read-only

Fields are frozen — mutate via hooks, not assignment.

Build one context per turn

Do not cache plans across turns in multi-agent setups.

Reuse default_context_builder

It is a module-level singleton — no need to instantiate your own builder.

Match runtime_mode to delivery

Enable DeliveryChannels(enable_streaming=True, ...) before choosing stream modes.

Disable tools with an empty list, not None

tools=None (or omitting the kwarg) uses agent.tools. Pass an explicit empty list to run a turn with no tools.


Related