langray 1.0.2
X-ray vision into your LangGraph agents
Requires Python: >=3.10
Keywords: agents, ai, debugging, langchain, langgraph, llm, visualization

# LangRay

X-ray vision into your LangGraph agents.

LangRay is a real-time debugging and visualization tool for [LangGraph](https://github.com/langchain-ai/langgraph). See exactly what your agent is doing as it executes—inspect state, track tokens, replay runs, and debug with confidence.

![LangRay Screenshot](https://raw.githubusercontent.com/vascogaspar/langray/main/docs/screenshot.png)

## Features

- **One-line integration** — Wrap your graph with `visualize()` and you're done
- **Real-time execution flow** — Watch nodes light up as your agent thinks
- **State inspector** — Click any node to see its full state, messages, and diffs
- **Run history** — Automatically saves last 50 runs, replay anytime
- **Token tracking** — Monitor LLM input/output tokens per run
- **Export runs** — Download execution traces as JSON for debugging or sharing
- **Keyboard-first** — Vim-style shortcuts for power users
- **Zero config** — Works out of the box with any LangGraph agent
- **Dark theme** — Easy on the eyes during those late-night debug sessions

## Installation

```bash
pip install langray
```

Or install from source:

```bash
git clone https://github.com/vascogaspar/langray.git
cd langray
pip install -e .
```

## Quick Start

```python
from langray import visualize
from my_agent import graph  # Your compiled LangGraph

# Wrap your graph — that's it!
viz_graph = visualize(graph)

# Use exactly like before
result = viz_graph.invoke({"messages": [HumanMessage(content="Hello!")]})
```

Your browser opens automatically to `http://localhost:8080` showing real-time execution.

### Async Support

```python
# Works with async too
result = await viz_graph.ainvoke({"messages": [...]})

# And streaming
async for chunk in viz_graph.astream({"messages": [...]}):
    print(chunk)
```

### Configuration

```python
viz_graph = visualize(
    graph,
    port=8080,           # Port for the UI (default: 8080)
    host="127.0.0.1",    # Host to bind (default: 127.0.0.1)
    open_browser=True,   # Auto-open browser (default: True)
)
```

## UI Overview

```
┌────────────┬──────────────────┬─────────────────┬──────────────────┐
│   INPUT    │   ARCHITECTURE   │  EXECUTION FLOW │  STATE INSPECTOR │
│            │                  │                 │                  │
│  Message   │    ┌───────┐     │     START       │  Step 5: agent   │
│  User ID   │    │ agent │     │       │         │  ┌────┬────┬────┐│
│            │    └───┬───┘     │     AGENT #1    │  │State│Msgs│Diff││
│  [Run]     │        │         │       │         │  └────┴────┴────┘│
│            │    ┌───┴───┐     │     TOOLS       │  messages: [     │
│            │    │ tools │     │   get_balance   │    HumanMessage  │
│            │    └───┬───┘     │       │         │    AIMessage     │
│            │        │         │     AGENT #2    │  ]               │
│            │    ┌───┴───┐     │       │         │  user_id: "123"  │
│            │    │format │     │     FORMAT      │                  │
│            │    └───────┘     │       │         │                  │
│            │                  │      END        │                  │
├────────────┴──────────────────┴─────────────────┴──────────────────┤
│                              TOOLS (17)                            │
│  get_balance │ get_transactions │ create_reminder │ analyze_trends │
└────────────────────────────────────────────────────────────────────┘
```

### Panels

| Panel               | Description                                   |
| ------------------- | --------------------------------------------- |
| **Input**           | Enter messages and parameters, run your agent |
| **Architecture**    | Static graph structure with zoom/pan          |
| **Execution Flow**  | Live execution trace with timing              |
| **State Inspector** | Deep dive into state at any step              |
| **Tools**           | All available tools with their schemas        |

### State Inspector Tabs

- **State** — Collapsible tree view of the full state object
- **Messages** — Pretty-printed conversation with role icons
- **Diff** — Side-by-side comparison of state changes between steps

## Keyboard Shortcuts

| Key          | Action                      |
| ------------ | --------------------------- |
| `/`          | Focus message input         |
| `r`          | Run agent                   |
| `Ctrl+Enter` | Run (when in input)         |
| `Esc`        | Clear input / close dialogs |
| `i`          | Toggle inspector panel      |
| `h`          | Focus history dropdown      |
| `t`          | Toggle tools footer         |
| `↑` `↓`      | Navigate flow nodes         |
| `[` `]`      | Navigate state snapshots    |
| `?`          | Show all shortcuts          |

## Run History

LangRay automatically persists your last 50 runs in the browser's IndexedDB:

- **Replay** — Select any past run from the dropdown to replay it
- **Compare** — Switch between runs to compare behavior
- **Export** — Download any run as JSON for sharing or analysis
- **Clear** — Wipe history with the trash button

Runs persist across browser refreshes and include full state snapshots.

## Token Tracking

The header displays token usage during and after execution:

```
↓ 3.5K  ↑ 62  Σ 3.6K
```

- `↓` Input tokens sent to the LLM
- `↑` Output tokens received
- `Σ` Total tokens for the run

## Exported Run Format

```json
{
  "run_id": "abc-123",
  "timestamp": 1706284800000,
  "message": "What's my balance?",
  "duration_ms": 3243,
  "status": "completed",
  "tools_called": ["get_balance"],
  "steps": [
    {
      "type": "node_start",
      "node": "agent",
      "timestamp": 1706284800100,
      "state": { "messages": [...], "user_id": "..." }
    }
  ],
  "response": "Your balance is $15.00"
}
```

## API Reference

### `visualize(graph, **options) -> VisualizedGraph`

Wrap a LangGraph for visualization.

**Arguments:**

- `graph` — A compiled LangGraph `StateGraph`
- `port` — Port for UI server (default: `8080`)
- `host` — Host to bind (default: `"127.0.0.1"`)
- `open_browser` — Auto-open browser (default: `True`)

**Returns:** `VisualizedGraph` wrapper

### `VisualizedGraph`

Drop-in replacement for your graph with identical API:

```python
# All standard methods work
result = viz_graph.invoke(input)
result = await viz_graph.ainvoke(input)

for chunk in viz_graph.stream(input):
    ...

async for chunk in viz_graph.astream(input):
    ...

# Additional properties
viz_graph.url          # Server URL
viz_graph.graph        # Underlying graph
viz_graph.get_graph_structure()  # Introspected structure
```

### Introspection Utilities

```python
from langray import introspect_graph, graph_to_mermaid

# Get graph structure as dict
structure = introspect_graph(graph)

# Generate Mermaid diagram
mermaid = graph_to_mermaid(graph)
print(mermaid)
# graph TD
#     __start__ --> agent
#     agent --> tools
#     tools --> agent
#     agent --> __end__
```

## Event Types

Events streamed via Server-Sent Events (SSE):

| Event        | Description           | Data                                  |
| ------------ | --------------------- | ------------------------------------- |
| `run_start`  | Execution began       | `run_id`, initial state               |
| `node_start` | Node began executing  | `node`, `step`, state snapshot        |
| `node_end`   | Node finished         | `node`, `duration_ms`, state snapshot |
| `tool_start` | Tool invocation began | `tool`, `inputs`                      |
| `tool_end`   | Tool completed        | `tool`, `output`                      |
| `llm_usage`  | Token usage update    | `input_tokens`, `output_tokens`       |
| `run_end`    | Execution completed   | `duration_ms`, final state            |
| `error`      | Error occurred        | `message`, `traceback`                |

## Requirements

- Python 3.10+
- LangGraph 0.2+
- Modern browser (Chrome, Firefox, Safari, Edge)

## Development

```bash
# Clone the repo
git clone https://github.com/vascogaspar/langray.git
cd langray

# Install in dev mode
pip install -e ".[dev]"

# Run example
python examples/tool_agent.py
```

### Project Structure

```
langray/
├── __init__.py      # Public API
├── callback.py      # LangGraph callback handler
├── introspect.py    # Graph structure extraction
├── server.py        # FastAPI + SSE server
├── static/
│   ├── index.html   # Main page
│   ├── styles.css   # VOID dark theme
│   ├── app.js       # Core application
│   ├── graph.js     # D3 + Dagre rendering
│   ├── flow.js      # Execution flow
│   ├── inspector.js # State inspector
│   └── history.js   # IndexedDB persistence
└── examples/
    ├── simple_chain.py
    └── tool_agent.py
```

### Tech Stack

- **Backend:** FastAPI, Server-Sent Events
- **Frontend:** Vanilla JS, D3.js, Dagre
- **Storage:** IndexedDB (browser-side)
- **Styling:** Custom CSS (VOID dark theme)

## Troubleshooting

### Port already in use

```python
viz_graph = visualize(graph, port=8081)  # Use different port
```

### Browser doesn't open

```python
viz_graph = visualize(graph, open_browser=False)
print(f"Open {viz_graph.url} manually")
```

### State not showing in inspector

Ensure your graph nodes return state updates. LangRay captures state from LangGraph's built-in checkpointing.

### Old runs not loading properly

Runs saved before certain updates may have incomplete data. Run a new query to save with the latest format.

## Contributing

Contributions welcome! Please read our contributing guidelines before submitting PRs.

1. Fork the repo
2. Create a feature branch (`git checkout -b feature/amazing`)
3. Commit changes (`git commit -m 'Add amazing feature'`)
4. Push to branch (`git push origin feature/amazing`)
5. Open a Pull Request

## License

MIT License — see [LICENSE](LICENSE) for details.

## Acknowledgments

- [LangGraph](https://github.com/langchain-ai/langgraph) — The amazing agent framework this tool visualizes
- [D3.js](https://d3js.org/) — Powerful visualization library
- [Dagre](https://github.com/dagrejs/dagre) — Graph layout algorithm

---

<p align="center">
  <b>LangRay</b> — See what your agents are thinking
</p>