> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# scripted • AI Agent SDK

> A scriptable model double for offline agent tests.

# scripted

<Badge color="blue">AI Agent</Badge>

A scriptable model double for offline agent tests.

:class:`ScriptedModel` stands in for a real provider so a unit test can assert
what an agent *does* -- "given this user turn, it calls `refund()` and then
stops" -- in milliseconds, with no network, no API key and no `unittest.mock`
patching of provider internals.

It is a real :class:`~praisonaiagents.llm.llm.LLM` subclass that replaces the
methods which talk to a provider and nothing else, so everything around the
call is the production code path: the system prompt is assembled normally,
tools are serialised to real schemas, scripted tool calls are dispatched
through the agent's own executor, and results are fed back as real tool
messages. Replies are built as genuine litellm response objects, so they are
parsed by the same code a live response would be.

Basic use::

from praisonaiagents import Agent
from praisonaiagents.model\_harness import ScriptedModel

model = ScriptedModel(\["Paris."])
agent = Agent(instructions="You are a geography bot.", llm=model)

assert agent.start("What is the capital of France?") == "Paris."
assert model.requests\[0].last\_user\_message == "What is the capital of France?"

Scripting a tool call followed by a final answer::

model = ScriptedModel(\[
ScriptedModel.tool\_call("refund", \{"order\_id": "A1"}),
"Refunded order A1.",
])
agent = Agent(instructions="Support bot.", llm=model, tools=\[refund])

assert agent.start("refund order A1") == "Refunded order A1."
assert model.request\_count == 2  # tool turn, then the follow-up

A script entry may also be a callable, which receives the
:class:`RecordedRequest` and returns a reply -- useful for replies that depend
on what the agent actually sent::

model = ScriptedModel(\[lambda req: f"You said: \{req.last\_user\_message}"])

When the agent asks for one more reply than the script holds, the double raises
:class:`ScriptExhausted` rather than hanging, looping or inventing an answer.

## Import

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents.model_harness import scripted
```

## Classes

<CardGroup cols={2}>
  <Card title="ScriptedModelError" icon="brackets-curly" href="../classes/ScriptedModelError">
    Base class for failures of the double itself.
  </Card>

  <Card title="ScriptExhausted" icon="brackets-curly" href="../classes/ScriptExhausted">
    Raised when an agent asks a :class:`ScriptedModel` for an unscripted reply.
  </Card>

  <Card title="ScriptedToolCall" icon="brackets-curly" href="../classes/ScriptedToolCall">
    One tool call the double should emit.
  </Card>

  <Card title="ScriptedReply" icon="brackets-curly" href="../classes/ScriptedReply">
    One assistant turn the double should return.
  </Card>

  <Card title="RecordedRequest" icon="brackets-curly" href="../classes/RecordedRequest">
    What the agent actually sent to the model on one turn.
  </Card>

  <Card title="ScriptedModel" icon="brackets-curly" href="../classes/ScriptedModel">
    A model double that returns scripted replies in order and records requests.
  </Card>
</CardGroup>
