> ## 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/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()`, `inference_results.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.

  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

When using the **Structured** signature (`(input_data: AgentInput) -> AgentResponse`), your function receives an [`AgentInput`](/sdk/api/agent/input) object with the following fields:

<ResponseField name="messages" type="List[ConversationMessage]">
  The full conversation history up to this turn. Each message has `role` (`"user"` or `"assistant"`), `content`, and optional `retrieval_context` and `metadata`. When the test case has structured JSON input, non-message fields (e.g. `chat_type`, `reasoning_level`) are placed in the first user message's `metadata`.
</ResponseField>

<ResponseField name="session_id" type="str">
  The current session identifier. Use this to maintain state in stateful agents.
</ResponseField>

<ResponseField name="context_data" type="Optional[dict[str, Any]]">
  Structured context data from the test case associated with the current session. Use this to access context fields (e.g. `context_data["customer_tier"]`).
</ResponseField>

<ResponseField name="inference_result_id" type="Optional[str]">
  The inference result ID for this execution. Forward this to remote agents so they can attach traces to the correct session. See [Remote Agent Tracing](/sdk/tutorials/tracing-agent-operations#remote-agent-tracing).
</ResponseField>

<ResponseField name="metadata" type="Optional[dict[str, Any]]">
  Additional metadata for the current execution.
</ResponseField>

### Helper Methods

<ResponseField name="last_user_message_str()" type="Optional[str]">
  Returns the content of the last user message. Returns `None` if no user message is found in `messages`.
</ResponseField>

<ResponseField name="last_user_message()" type="Optional[ConversationMessage]">
  Returns the last `ConversationMessage` with `role == "user"`, or `None` if not found.
</ResponseField>

## 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`.
</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** — Inference results are now automatically tracked by the API. This parameter is kept for backward compatibility but 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.
</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).
  </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>
