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

# Run History

> Read back persisted run and trace data from your state store

Query the runs and traces your agents have written to the state store.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Agent[🧠 Agent] --> StateStore[💾 State Store]
    StateStore --> GetRuns["📋 db.get_runs()"]
    StateStore --> GetTraces["🔍 db.get_traces()"]
    
    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef store fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef query fill:#10B981,stroke:#7C90A0,color:#fff
    
    class Agent agent
    class StateStore store
    class GetRuns,GetTraces query
```

## Quick Start

<Steps>
  <Step title="Configure State Store">
    Set `PRAISONAI_STATE_URL` or pass `state_url=` when creating agents. See [Database Persistence](/docs/features/database-persistence) for configuration options.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import os
    os.environ["PRAISONAI_STATE_URL"] = "sqlite:///agent_state.db"
    ```
  </Step>

  <Step title="Query Runs">
    Run an agent, then read its runs back using `get_runs(session_id)`:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.db import PraisonAIDB

    # Run an agent
    agent = Agent(name="Research Agent", instructions="Research the topic")
    result = agent.start("What is quantum computing?")

    # Query runs for this session
    db = PraisonAIDB(state_url="sqlite:///agent_state.db")
    runs = db.get_runs(session_id=agent.session_id)

    for run in runs:
        print(f"Run {run['run_id']}: {run['status']} at {run['started_at']}")
    ```
  </Step>

  <Step title="Query Traces with Filters">
    Use `get_traces()` with optional filters for `session_id`, `user_id`, and `limit`:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Get traces for a specific session
    traces = db.get_traces(session_id=agent.session_id, limit=10)

    # Get traces for a specific user
    traces = db.get_traces(user_id="user123", limit=5)

    # Get all traces (up to limit)
    all_traces = db.get_traces(limit=100)
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant StateStore as State Store
    participant DB as PraisonAIDB
    
    User->>Agent: Start task
    Agent->>StateStore: Write run data
    Agent->>StateStore: Write trace data
    User->>DB: get_runs(session_id)
    StateStore->>DB: Scan run:{session_id}:*
    DB->>User: List of run records
    User->>DB: get_traces(filters)
    StateStore->>DB: Scan trace:*
    DB->>User: Filtered trace records
```

Both methods read persisted data from the configured state store:

| Method       | Source keys          | Filters                                         | Sort              |
| ------------ | -------------------- | ----------------------------------------------- | ----------------- |
| `get_runs`   | `run:{session_id}:*` | `session_id` (required), `limit` (optional)     | `started_at` desc |
| `get_traces` | `trace:*`            | `session_id`, `user_id`, `limit` (all optional) | `started_at` desc |

***

## Configuration Options

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
def get_runs(session_id: str, limit: Optional[int] = None) -> List[dict]:
    """
    Get runs for a session.
    
    Args:
        session_id: Session ID to filter by (required)
        limit: Maximum number of runs to return (optional)
        
    Returns:
        List of run dictionaries sorted by started_at desc
    """

def get_traces(
    session_id: Optional[str] = None, 
    user_id: Optional[str] = None, 
    limit: Optional[int] = None
) -> List[dict]:
    """
    Get traces with optional filters.
    
    Args:
        session_id: Filter by session ID (optional)
        user_id: Filter by user ID (optional)
        limit: Maximum number of traces to return (optional)
        
    Returns:
        List of trace dictionaries sorted by started_at desc
    """
```

**Behavior:**

* If `state_store` is not configured, returns `[]` and logs a warning
* `limit=0` returns `[]`, `limit=None` returns everything matching
* Bad/non-dict entries are skipped with a warning
* Uses `state_store.scan_prefix()` when available, falls back to `state_store.keys()`

***

## Common Patterns

**Show last 10 runs for a user:**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Get user's recent sessions first, then query runs
recent_sessions = get_user_sessions(user_id="user123")  # Your implementation
for session in recent_sessions[:5]:  # Last 5 sessions
    runs = db.get_runs(session_id=session['session_id'], limit=2)
    print(f"Session {session['session_id']}: {len(runs)} runs")
```

**Export traces for a session:**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import json

# Export complete session trace data
traces = db.get_traces(session_id="session_abc123")
with open("session_traces.json", "w") as f:
    json.dump(traces, f, indent=2, default=str)
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Always set state_url before calling these methods">
    Otherwise you get `[]` and a warning: `"get_runs() called but no state_url configured; returning []"`.

    Configure via environment variable or constructor:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Environment variable (recommended)
    os.environ["PRAISONAI_STATE_URL"] = "postgresql://user:pass@host/db"

    # Or via constructor
    db = PraisonAIDB(state_url="sqlite:///data.db")
    ```
  </Accordion>

  <Accordion title="Pick a small limit for UIs">
    These methods scan the store, so use small limits for responsive UIs:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Good for UI pagination
    recent_runs = db.get_runs(session_id, limit=20)

    # Avoid large scans in UI code
    all_runs = db.get_runs(session_id)  # Could be thousands
    ```
  </Accordion>

  <Accordion title="Use these methods outside hot paths">
    Both methods perform store scans which can be expensive for large datasets. Cache results when possible:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Cache for dashboard views
    @lru_cache(maxsize=100)
    def get_cached_runs(session_id: str, limit: int = 10):
        return tuple(db.get_runs(session_id, limit))  # Tuple for hashability
    ```
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Database Persistence" icon="database" href="/docs/features/database-persistence">
    Configure state stores for persistence
  </Card>

  <Card title="Session Management" icon="users" href="/docs/features/persistence">
    Manage agent sessions and state
  </Card>

  <Card title="Langfuse Observability" icon="chart-line" href="/docs/observability/langfuse">
    Advanced trace analysis and monitoring
  </Card>
</CardGroup>
