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

# Configuration

> Complete MobileAgent configuration guide - all parameters, minimal examples

## Quick Start

```python theme={null}
from mobilerun import MobileAgent, MobileConfig

# Minimal (uses defaults)
agent = MobileAgent(goal="Open settings")
result = await agent.run()

# Load from config.yaml
config = MobileConfig.from_yaml("config.yaml")
agent = MobileAgent(goal="Open settings", config=config)
result = await agent.run()
```

***

## MobileAgent Parameters

### Required

```python theme={null}
MobileAgent(
    goal="Your task",  # REQUIRED: Task description
)
```

### Optional Parameters

| Parameter        | Type                                   | Default | Description                                                                  |
| ---------------- | -------------------------------------- | ------- | ---------------------------------------------------------------------------- |
| `config`         | `MobileConfig \| None`                 | `None`  | Full config object (loads LLMs from profiles if `llms` not provided)         |
| `llms`           | `dict[str, LLM] \| LLM \| None`        | `None`  | LLM(s) - dict for per-agent, single LLM for all, or None to load from config |
| `custom_tools`   | `dict`                                 | `None`  | Custom tool definitions                                                      |
| `credentials`    | `Union[dict, CredentialManager, None]` | `None`  | Dict of credential secrets or CredentialManager instance                     |
| `variables`      | `dict \| None`                         | `None`  | Custom variables accessible during execution                                 |
| `output_model`   | `Type[BaseModel] \| None`              | `None`  | Pydantic model for structured output extraction                              |
| `prompts`        | `dict[str, str] \| None`               | `None`  | Custom Jinja2 prompt templates (NOT file paths)                              |
| `driver`         | `DeviceDriver \| None`                 | `None`  | Pre-configured device driver instance (AndroidDriver or IOSDriver)           |
| `state_provider` | `StateProvider \| None`                | `None`  | Pre-configured state provider instance                                       |
| `timeout`        | `int`                                  | `1000`  | Workflow timeout in seconds                                                  |

***

## Configuration Classes

### AgentConfig

```python theme={null}
from mobilerun import AgentConfig, FastAgentConfig, ManagerConfig, ExecutorConfig, AppCardConfig

AgentConfig(
    # Core settings
    name="mobilerun",                 # Agent name (use "mobilerun" for default agent)
    max_steps=15,                    # Max execution steps
    reasoning=False,                 # Enable Manager/Executor workflow
    streaming=True,                  # Stream LLM responses
    after_sleep_action=1.0,          # Wait after actions (seconds)
    wait_for_stable_ui=0.3,          # Wait for UI to stabilize (seconds)
    use_normalized_coordinates=False, # Use normalized coordinates instead of absolute pixels
    model_screenshot_max_side=None,  # Optional cap (px, long edge) on the model-facing screenshot

    # Sub-configs
    fast_agent=FastAgentConfig(...),
    manager=ManagerConfig(...),
    executor=ExecutorConfig(...),
    app_cards=AppCardConfig(...),
)
```

**FastAgentConfig**

```python theme={null}
FastAgentConfig(
    vision=False,                                              # Enable screenshots
    parallel_tools=True,                                       # Encourage multiple tool calls in a single response
    system_prompt="config/prompts/fast_agent/system.jinja2",   # Path to system prompt template
    user_prompt="config/prompts/fast_agent/user.jinja2",       # Path to user prompt template
)
```

**ManagerConfig**

```python theme={null}
ManagerConfig(
    vision=False,                                            # Enable screenshots
    system_prompt="config/prompts/manager/system.jinja2",    # Path to system prompt template
    stateless=False,                                         # Use StatelessManagerAgent (no conversation history)
)
```

**ExecutorConfig**

```python theme={null}
ExecutorConfig(
    vision=False,                                            # Enable screenshots
    system_prompt="config/prompts/executor/system.jinja2",   # Path to system prompt template
)
```

**AppCardConfig**

```python theme={null}
AppCardConfig(
    enabled=True,                    # Enable app-specific instructions
    mode="local",                    # "local" | "server" | "composite"
    app_cards_dir="config/app_cards",  # Directory for app card files
    server_url=None,                 # Server URL (for server/composite modes)
    server_timeout=2.0,              # Server request timeout (seconds)
    server_max_retries=2,            # Server retry attempts
)
```

***

### DeviceConfig

```python theme={null}
from mobilerun import DeviceConfig

DeviceConfig(
    serial=None,          # Device serial/IP (None = auto-detect)
    use_tcp=False,        # TCP vs content provider communication
    platform="android",   # "android" or "ios"
    auto_setup=True,      # Auto-install/fix Portal APK before each run
)
```

***

### LoggingConfig

```python theme={null}
from mobilerun import LoggingConfig

LoggingConfig(
    debug=False,                   # Enable debug logs
    save_trajectory="none",        # "none" | "step" | "action"
    trajectory_path="trajectories", # Directory for trajectory files
    rich_text=False,               # Rich text formatting in logs
    trajectory_gifs=True,          # Save trajectory as animated GIFs
)
```

***

### TracingConfig

```python theme={null}
from mobilerun import TracingConfig

TracingConfig(
    enabled=False,                      # Enable tracing
    provider="phoenix",                 # "phoenix" or "langfuse"
    langfuse_screenshots=False,         # Upload screenshots to Langfuse (if enabled)

    # Langfuse settings (only used if provider="langfuse")
    langfuse_secret_key="",             # LANGFUSE_SECRET_KEY env var
    langfuse_public_key="",             # LANGFUSE_PUBLIC_KEY env var
    langfuse_host="",                   # LANGFUSE_HOST env var (e.g., "https://cloud.langfuse.com")
    langfuse_user_id="anonymous",       # User ID for Langfuse tracing
    langfuse_session_id="",             # Empty = auto-generate UUID; custom value to persist across runs
)
```

***

### TelemetryConfig

```python theme={null}
from mobilerun import TelemetryConfig

TelemetryConfig(
    enabled=True,  # Enable anonymous telemetry
)
```

***

### ToolsConfig

```python theme={null}
from mobilerun import ToolsConfig

ToolsConfig(
    disabled_tools=["click_at", "click_area", "long_press_at"],  # Tools to disable (default disables coordinate-based tools)
    stealth=False,                                                # Enable stealth mode (human-like timing + randomized coordinates)
)
```

**Example - Disable specific tools:**

```yaml theme={null}
tools:
  disabled_tools:
    - long_press
    - wait
  stealth: false
```

Disabled tools will not be available to agents during execution. The default `disabled_tools` list disables `click_at`, `click_area`, and `long_press_at`. Enabling vision auto-unmasks `click_at` on both Android and iOS (non-normalized coordinates only); `vision_only` mode auto-unmasks all three. To enable `click_area` and `long_press_at` under standard vision, set `disabled_tools: []`. See [Vision Mode](/framework/features/vision) for details.

***

### CredentialsConfig

```python theme={null}
from mobilerun import CredentialsConfig

CredentialsConfig(
    enabled=True,                        # Enable credential manager
    file_path="config/credentials.yaml",  # Path to credentials file
)
```

***

## LLM Configuration

### Single LLM (All Agents)

```python theme={null}
from llama_index.llms.google_genai import GoogleGenAI

llm = GoogleGenAI(model="gemini-3.1-flash-lite-preview", temperature=0.2)
agent = MobileAgent(goal="...", llms=llm)
```

### Per-Agent LLMs

```python theme={null}
from llama_index.llms.openai import OpenAI
from llama_index.llms.google_genai import GoogleGenAI

agent = MobileAgent(
    goal="...",
    llms={
        "manager": OpenAI(model="gpt-4o"),                    # Planning
        "executor": GoogleGenAI(model="gemini-3.1-flash-lite-preview"),  # Action selection
        "fast_agent": GoogleGenAI(model="gemini-3.1-flash-lite-preview"),   # Fast Agent: Direct execution (XML tool-calling)
        "app_opener": OpenAI(model="gpt-4o-mini"),            # App launching
        "structured_output": GoogleGenAI(model="gemini-3.1-flash-lite-preview"),  # Output extraction
    }
)
```

**LLM Keys:**

* `manager` - Planning (reasoning mode only)
* `executor` - Action selection (reasoning mode only)
* `fast_agent` - Fast Agent: Direct execution (XML tool-calling)
* `app_opener` - App launching helper
* `structured_output` - Final output extraction

***

## Custom Tools

```python theme={null}
def my_tool(param: str, **kwargs) -> str:
    """Tool description."""
    return f"Result: {param}"

agent = MobileAgent(
    goal="...",
    custom_tools={
        "my_tool": {
            "parameters": {
                "param": {"type": "string", "required": True},
            },
            "description": "Tool description with usage example",
            "function": my_tool
        }
    }
)
```

***

## Credentials

### Dict Format (Recommended)

```python theme={null}
agent = MobileAgent(
    goal="...",
    credentials={
        "USERNAME": "alice@example.com",
        "PASSWORD": "secret123"
    }
)
# Agent can call type_secret("USERNAME", index) and type_secret("PASSWORD", index)
```

### Config Format

```python theme={null}
from mobilerun import MobileConfig, CredentialsConfig

config = MobileConfig(
    credentials=CredentialsConfig(
        enabled=True,
        file_path="config/credentials.yaml"
    )
)

agent = MobileAgent(
    goal="...",
    config=config
)
```

***

## Custom Variables

```python theme={null}
agent = MobileAgent(
    goal="...",
    variables={
        "api_url": "https://api.example.com",
        "user_id": "12345",
        "custom_data": {"key": "value"}
    }
)
# Access in shared_state.custom_variables
```

***

## Structured Output

```python theme={null}
from pydantic import BaseModel

class FlightInfo(BaseModel):
    airline: str
    flight_number: str
    confirmation_code: str

agent = MobileAgent(
    goal="Book a flight and extract details",
    output_model=FlightInfo
)
result = await agent.run()
print(result.structured_output.airline)  # Typed output
```

***

## Custom Prompts

```python theme={null}
custom_prompt = """
You are an expert mobile agent.
Goal: {{ instruction }}
Be precise and efficient.
"""

agent = MobileAgent(
    goal="...",
    prompts={
        "fast_agent_system": custom_prompt,
        "fast_agent_user": "...",
        "manager_system": "...",
        "executor_system": "...",
    }
)
```

**Template Variables:**

* `{{ instruction }}` - User's goal
* `{{ device_date }}` - Device date/time
* `{{ app_card }}` - App-specific instructions
* `{{ state }}` - Device state
* `{{ history }}` - Action history

***

## Complete Example

```python theme={null}
from mobilerun import (
    MobileAgent, MobileConfig,
    AgentConfig, FastAgentConfig, DeviceConfig, LoggingConfig, TracingConfig
)
from llama_index.llms.openai import OpenAI
from llama_index.llms.google_genai import GoogleGenAI
from pydantic import BaseModel

# Structured output
class Output(BaseModel):
    name: str
    value: int

# Custom tool
def send_email(to: str, subject: str, **kwargs) -> str:
    """Send email."""
    return f"Sent to {to}"

# Build configuration
config = MobileConfig(
    agent=AgentConfig(
        max_steps=30,
        reasoning=True,
        after_sleep_action=1.5,
        fast_agent=FastAgentConfig(vision=True)
    ),
    device=DeviceConfig(
        serial="emulator-5554",
        platform="android",
        use_tcp=False
    ),
    logging=LoggingConfig(
        debug=True,
        save_trajectory="step",
        trajectory_gifs=True
    ),
    tracing=TracingConfig(enabled=True),
)

agent = MobileAgent(
    goal="Complex task",
    config=config,

    # LLMs
    llms={
        "manager": OpenAI(model="gpt-4o"),                    # Planning
        "executor": GoogleGenAI(model="gemini-3.1-flash-lite-preview"),  # Action selection
        "fast_agent": GoogleGenAI(model="gemini-3.1-flash-lite-preview"),   # Fast Agent: Direct execution (XML tool-calling)
        "app_opener": OpenAI(model="gpt-4o-mini"),            # App launching
        "structured_output": GoogleGenAI(model="gemini-3.1-flash-lite-preview"),  # Output extraction
    },

    # Custom tools
    custom_tools={
        "send_email": {
            "parameters": {
                "to": {"type": "string", "required": True},
                "subject": {"type": "string", "required": True},
            },
            "description": "Send email to recipient with subject",
            "function": send_email
        }
    },

    # Credentials
    credentials={"USERNAME": "alice", "PASSWORD": "secret"},

    # Variables
    variables={"api_url": "https://api.example.com"},

    # Structured output
    output_model=Output,

    # Timeout
    timeout=600
)

result = await agent.run()
```

***

## YAML Config (CLI)

For CLI usage, create `config.yaml`:

```yaml theme={null}
agent:
  name: mobilerun
  max_steps: 15
  reasoning: false
  streaming: true
  after_sleep_action: 1.0
  wait_for_stable_ui: 0.3
  use_normalized_coordinates: false
  # model_screenshot_max_side: 1280  # Optional cap for local vision models

  fast_agent:
    vision: false
    parallel_tools: true
    system_prompt: config/prompts/fast_agent/system.jinja2
    user_prompt: config/prompts/fast_agent/user.jinja2

  manager:
    vision: false
    system_prompt: config/prompts/manager/system.jinja2
    stateless: false

  executor:
    vision: false
    system_prompt: config/prompts/executor/system.jinja2

  app_cards:
    enabled: true
    mode: local
    app_cards_dir: config/app_cards
    server_url: null
    server_timeout: 2.0
    server_max_retries: 2

llm_profiles:
  manager:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.2
    kwargs:
      max_tokens: 8192

  executor:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.1
    kwargs:
      max_tokens: 4096

  fast_agent:  # Fast Agent: Direct execution (XML tool-calling)
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.2
    kwargs:
      max_tokens: 8192

  app_opener:
    provider: OpenAI
    model: gpt-4o-mini
    temperature: 0.0

  structured_output:
    provider: GoogleGenAI
    model: gemini-3.1-flash-lite-preview
    temperature: 0.0

device:
  serial: null
  platform: android
  use_tcp: false
  auto_setup: true  # Auto-install/fix Portal APK before each run

tools:
  disabled_tools:
    - click_at
    - click_area
    - long_press_at
  stealth: false

telemetry:
  enabled: true

tracing:
  enabled: false
  provider: phoenix  # "phoenix" or "langfuse"
  langfuse_screenshots: false  # Upload screenshots to Langfuse
  langfuse_secret_key: ""
  langfuse_public_key: ""
  langfuse_host: ""
  langfuse_user_id: anonymous
  langfuse_session_id: ""

logging:
  debug: false
  save_trajectory: none
  trajectory_path: trajectories
  rich_text: false
  trajectory_gifs: true

credentials:
  enabled: false
  file_path: config/credentials.yaml
```

### Ollama profiles

Ollama profiles accept the same portable `kwargs` as other providers — `max_tokens` and `context_window` work consistently across providers, and Mobilerun translates them to Ollama's native parameters at runtime.

```yaml theme={null}
llm_profiles:
  manager:
    provider: Ollama
    model: qwen3:8b
    base_url: http://localhost:11434
    kwargs:
      max_tokens: 2048       # Caps output length (mapped to Ollama's num_predict)
      context_window: 32768  # Controls KV cache size (mapped to num_ctx)
```

| Key              | Default for Ollama | Notes                                                                                                                                                                                                                      |
| ---------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `max_tokens`     | unset (no cap)     | Caps generated tokens. If you set `additional_kwargs.num_predict`, that explicit value wins.                                                                                                                               |
| `context_window` | `32768`            | KV cache size. Set `-1` to use the model's maximum (preallocates the full KV cache and can spill to CPU on large-context models). An explicit `additional_kwargs.num_ctx` is mirrored into `context_window` automatically. |

<Tip>
  The 32K default keeps Ollama on the GPU for most models. If you need the model's full context, set `context_window: -1` explicitly.
</Tip>

***

## CLI Overrides

```bash theme={null}
# Override agent settings
mobilerun run "Task" --steps 30 --reasoning --vision

# Override device
mobilerun run "Task" --device emulator-5554 --tcp

# Override LLM (applies to ALL agents)
mobilerun run "Task" --provider GoogleGenAI --model gemini-3.1-flash-lite-preview

# Override logging
mobilerun run "Task" --debug --save-trajectory action --tracing

# Custom config file
mobilerun run "Task" --config /path/to/config.yaml
```

**All CLI Flags:**

* `--config PATH` - Custom config file
* `--device SERIAL` - Device serial/IP
* `--agent NAME` - External agent to use. Not yet supported — reserved for future use.
* `--provider PROVIDER` - LLM provider (OpenAI, Ollama, Anthropic, GoogleGenAI, DeepSeek)
* `--model MODEL` - LLM model name
* `--temperature FLOAT` - LLM temperature
* `--steps INT` - Max steps
* `--base_url URL` - API base URL (for Ollama/OpenRouter)
* `--api_base URL` - API base URL (for OpenAI-like)
* `--vision/--no-vision` - Enable/disable vision for all agents
* `--reasoning/--no-reasoning` - Enable/disable reasoning mode
* `--tracing/--no-tracing` - Enable/disable tracing
* `--debug/--no-debug` - Enable/disable debug logs
* `--tcp/--no-tcp` - Enable/disable TCP communication
* `--save-trajectory none|step|action` - Trajectory saving level
* `--ios` - Run on iOS device

***

## Environment Variables

Set API keys via environment variables:

```bash theme={null}
export GOOGLE_API_KEY=your-key
export OPENAI_API_KEY=your-key
export ANTHROPIC_API_KEY=your-key
export DEEPSEEK_API_KEY=your-key
export MOBILERUN_CONFIG=/path/to/config.yaml  # Custom config path
```
