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

# Simulate Conversation

> Run a full conversation simulation between your agent and a simulated user using the Galtea SDK.

## Returns

It returns a result object from the [Conversation Simulator](/concepts/product/dataset/test-case/conversation-simulator#simulation-result-structure) containing the complete conversation history, status, and metadata. This is used to analyze how your agent performed in the test scenario.

## Agent Options

<Tabs>
  <Tab title="Simple">
    The quickest way to get started. Your function receives just the latest user message as a string.

    ```python theme={"system"}
    def my_agent(user_message: str) -> str:
        # In a real scenario, call your model here
        return f"Your model output to: {user_message}"
    ```
  </Tab>

  <Tab title="Chat History">
    Use this when your agent needs the full conversation context. Your function receives the message list in the OpenAI format (`{"role": "...", "content": "..."}`).

    ```python theme={"system"}
    def my_agent(messages: list[dict]) -> str:
        # messages follows the standard chat format:
        # [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}, ...]
        user_message = messages[-1]["content"]
        return f"Your model output to: {user_message}"
    ```
  </Tab>

  <Tab title="Structured">
    For full control over input and output — including optional usage tracking, cost tracking, and retrieval context for RAG evaluations.

    ```python theme={"system"}
    def my_agent(input_data: AgentInput) -> AgentResponse:
        user_message = input_data.last_user_message_str()

        # Access structured input fields from the first user message's metadata
        # (e.g. when test case input is {"user_message": "hello", "chat_type": "support"})
        first_msg = input_data.messages[0] if input_data.messages else None
        chat_type = first_msg.metadata.get("chat_type") if first_msg and first_msg.metadata else None

        # In a real scenario, call your model here
        model_output = f"Your model output to: {user_message}"
        # Return AgentResponse with optional usage/cost tracking
        return AgentResponse(
            content=model_output,
            usage_info={"input_tokens": 100, "output_tokens": 50},
        )
    ```
  </Tab>
</Tabs>

<Info>
  All three signatures work with `evaluations.run()`, `traces.generate()`, and `simulator.simulate()`. Both sync and async functions are supported. The SDK auto-detects which signature you're using from the type hint on the first parameter.

  Only the **Structured** signature receives the files a test case attaches, as `input_data.input_files`. The other two receive the text alone and fail the trace for a file-only test case. See [Reading attached files](/sdk/api/agent/input#reading-attached-files).

  For the full list of fields available on `AgentInput` (including structured input access via message metadata), see the [AgentInput reference](/sdk/api/agent/input).
</Info>

## AgentInput Fields

With the **Structured** signature (`(input_data: AgentInput) -> AgentResponse`), your function receives an [`AgentInput`](/sdk/api/agent/input). That page lists every field and helper method, including `input_files` for [reading attached files](/sdk/api/agent/input#reading-attached-files).

## Example

```python theme={"system"}
# Define your agent function
def my_agent(user_message: str) -> str:
    return f"Response to: {user_message}"


simulation_result = galtea.simulator.simulate(
    session_id=behavior_session.id,
    agent=my_agent,
    max_turns=3,
)
```

## Parameters

<ResponseField name="session_id" type="str" required>
  The session identifier for this simulation.
</ResponseField>

<ResponseField name="agent" type="AgentType" required>
  The agent function that generates responses. Supported signatures:

  * **Simple:** `(user_message: str) -> str` — receives last user message
  * **Chat History:** `(messages: list[dict]) -> str` — receives full chat history (OpenAI format)
  * **Structured:** `(input_data: AgentInput) -> AgentResponse` — structured input/output

  Both sync and async functions are supported. Use the structured signature when you need `usage_info`, `cost_info`, or `retrieval_context`. Only the structured signature receives the files a test case attaches; the other two fail the trace for a file-only test case.
</ResponseField>

<ResponseField name="max_turns" type="int">
  Maximum number of conversation turns. Defaults to the Test Case's max turns if not provided.
</ResponseField>

<ResponseField name="log_inference_results" type="bool">
  <Warning>**Deprecated** — Traces are now automatically tracked by the API. The parameter keeps its old name and no longer has any effect.</Warning>
</ResponseField>

<ResponseField name="enable_last_inference" type="bool">
  Whether to call your agent one final time after the simulated user sends their last message. Default: True.
  When enabled, your agent will have the opportunity to respond to the conversation's final user message, ensuring complete dialogue coverage for evaluation purposes.

  <Warning>
    Setting this to `False` needs Galtea SDK 5.2.0 or later. A turn stays pending until something answers it, and the platform skips every evaluation on a session that holds a pending turn. From 5.2.0 the SDK closes that unanswered last turn for you. An earlier release leaves it pending, so the session gets no scores.

    Closing it is best effort. If that call fails, the simulation still returns normally and the session stays unscored, so check `result.metadata["last_turn_finalize_error"]` in any script or CI job.
  </Warning>
</ResponseField>

<ResponseField name="include_metadata" type="bool">
  Whether to include metadata in the simulation result. Default: False
</ResponseField>

<ResponseField name="agent_goes_first" type="bool">
  If True, the agent will generate the first message before any user messages are generated. Default: False

  <Note>
    When `agent_goes_first` is set to `True`:

    * Any `input` (first user message) defined on the associated `TestCase` will be ignored.
    * The first call to your agent will have an empty `messages` list in the `AgentInput`. Ensure your agent function can handle cases where `input_data.last_user_message_str()` returns `None` (or the messages list is empty).
    * The opening turn is recorded as pending and only your agent's reply completes it. If the run stops during that first call, the turn stays pending and the session is not evaluated.
  </Note>
</ResponseField>

<ResponseField name="stop_when_agent_returns_empty_response" type="bool">
  If True, the simulation will stop when the agent returns an empty (`""` / `None`) response. Default: True

  <Note>
    When `stop_when_agent_returns_empty_response` is set to `False`:

    * The simulation will continue even if the agent returns a response that is empty (`""`) or `None`.
    * This allows for more flexible conversation flows where you want to capture all agent interactions regardless of content.
    * Be careful when using this option, as it may lead to further credit consumption if the agent keeps returning empty responses. To mitigate this, you can also make use of the `max_turns` parameter to set an upper limit on the number of turns.
  </Note>
</ResponseField>
