Skip to main content
Runtime tool result middleware converts vendor-shaped tool outputs from plugin harnesses into a standard format before hooks and memory adapters run.

Quick Start

1

Default (zero config)

The native praisonai runtime needs no middleware — results pass straight to hooks:
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful")
agent.start("What is 2 + 2?")
2

Register harness middleware

Implement normalize, register once, then set agent._runtime_id:
from typing import Any
from praisonaiagents import Agent, tool
from praisonaiagents.runtime import (
    MiddlewareContext,
    NormalizedToolResult,
    register_middleware,
)


class MyHarnessMiddleware:
    @property
    def runtime_id(self) -> str:
        return "my_plugin_harness"

    def normalize(self, result: Any, tool_name: str, ctx: MiddlewareContext) -> NormalizedToolResult:
        if isinstance(result, dict) and result.get("status") == "error":
            return NormalizedToolResult(
                content=None,
                success=False,
                error_message=result.get("message"),
                metadata={"vendor": "my_harness"},
                raw_result=result,
            )
        return NormalizedToolResult(content=result.get("data", result), raw_result=result)


register_middleware("my_plugin_harness", MyHarnessMiddleware())

@tool
def lookup(query: str) -> dict:
    """Look up data."""
    return {"status": "ok", "data": f"Results for {query}"}

agent = Agent(name="assistant", tools=[lookup])
agent._runtime_id = "my_plugin_harness"  # triggers middleware lookup
agent.start("Look up Python tutorials")

How It Works

Middleware runs in tool_execution.py before the AFTER_TOOL hook fires. When agent._runtime_id is missing or equals "praisonai", middleware is bypassed entirely (zero allocation).

Configuration Options

MiddlewareContext

FieldTypeDefaultDescription
tool_namestr— (required)Name of the tool that was executed
runtime_idstr— (required)Runtime that produced the result
agent_idOptional[str]NoneAgent name, if known
session_idOptional[str]NoneSession id, if known
execution_time_msfloat0.0Tool execution time
timestampfloattime.time()When the result was produced
metadataDict[str, Any]{}Free-form context for middleware decisions

NormalizedToolResult

FieldTypeDefaultDescription
contentAny— (required)The actual tool result
successboolTrueWhether the tool succeeded
error_messageOptional[str]NoneError text when success=False
metadataDict[str, Any]{}Vendor/exec metadata for downstream consumers
execution_time_msfloat0.0Execution time in ms
timestampfloattime.time()When normalized
raw_resultOptional[Any]NoneOriginal vendor object for debugging

Registry API

MethodSignaturePurpose
register(runtime_id, middleware) -> NoneRegister middleware for a runtime
unregister(runtime_id) -> boolRemove middleware
get_middleware(runtime_id) -> RuntimeToolResultMiddlewareLook up; returns PassThroughMiddleware if not registered
has_middleware(runtime_id) -> boolCheck registration
list_runtimes() -> list[str]List registered runtime ids
clear() -> NoneDrop all registrations (testing)
Module helpers: register_middleware, get_middleware, get_default_middleware_registry().

Common Patterns

Error handling — set success=False and fill error_message; failed tools surface as "Tool Error: <message>" to the agent. Rich metadata — attach vendor, version, cache hit, or region in metadata for observability hooks. Conditional normalization — branch on tool_name prefix (e.g. search_ vs db_) inside normalize().

User Interaction Flow

Best Practices

Middleware runs on the hot path after every tool call. Avoid heavy imports or deep copies inside normalize().
Preserve the original vendor object in raw_result so hooks and debugging can inspect the pre-normalised payload.
Middleware failures are caught and logged; tool execution continues with the raw result.
Do not register middleware for "praisonai" — the core runtime bypasses it for zero overhead.

Hooks

Hooks receive the normalised payload via AfterToolInput.

Managed Runtime Protocol

Remote agent loops on managed infrastructure — a different runtime concept.

Agent Runtime Protocol

Pluggable agent execution runtimes (turn/stream abstraction).

Memory Lifecycle Hooks

Memory backends that react to tool and session events.