Debug with @xray
July 26, 2026 · View on GitHub
See what your AI agent is thinking.
Quick Start
from connectonion import xray
@xray
def my_tool(text: str) -> str:
"""Process some text."""
# Now you can see inside the agent's mind!
print(xray.agent.name) # "my_assistant"
print(xray.task) # "Process this document"
print(xray.iteration) # 1, 2, 3...
return f"Processed: {text}"
That's it! Add @xray to any tool to unlock debugging superpowers.
What You Can Access
Inside any @xray decorated function:
xray.agent # The Agent instance calling this tool
xray.task # Original request from user
xray.messages # Full conversation history
xray.iteration # Which round of tool calls (1-10)
xray.previous_tools # Tools called before this one
Real Example
@xray
def search_database(query: str) -> str:
"""Search our database."""
# See what led to this search
print(f"User asked: {xray.task}")
print(f"This is iteration {xray.iteration}")
if xray.previous_tools:
print(f"Already tried: {xray.previous_tools}")
# Adjust behavior based on context
if xray.iteration > 2:
return "No results found, please refine your search"
return f"Found 5 results for '{query}'"
Visual Execution Trace
See the complete flow of your agent's work from inside a tool:
@xray
def analyze_data(text: str) -> str:
"""Analyze data and show execution trace."""
# Show what happened so far
xray.trace()
return "Analysis complete"
Output:
Task: "Find Python tutorials and summarize them"
[1] • 89ms search_database(query="Python tutorials")
IN → query: "Python tutorials"
OUT ← "Found 5 results for 'Python tutorials'"
[2] • 234ms summarize_text(text="Found 5 results...", max_words=50)
IN → text: "Found 5 results for 'Python tutorials'"
IN → max_words: 50
OUT ← "5 Python tutorials found covering basics to advanced topics"
Total: 323ms • 2 steps • 1 iterations
Debug in Your IDE
Set a breakpoint and explore:
@xray
def analyze_sentiment(text: str) -> str:
# 🎯 Set breakpoint on next line
sentiment = "positive" # When stopped here in debugger:
# >>> xray
# <XrayContext active>
# agent: 'my_bot'
# task: 'How do people feel about Python?'
# >>> xray.messages
# [{'role': 'user', 'content': '...'}, ...]
return sentiment
Practical Use Cases
1. Understand Why a Tool Was Called
@xray
def emergency_shutdown():
"""Shutdown the system."""
# Check why this drastic action was requested
print(f"Shutdown requested because: {xray.task}")
print(f"After trying: {xray.previous_tools}")
# Maybe don't shutdown if it's the first try
if xray.iteration == 1:
return "Try restarting first"
return "System shutdown complete"
2. Adaptive Tool Behavior
@xray
def fetch_data(source: str) -> str:
"""Fetch data from a source."""
# Use cache on repeated calls
if "fetch_data" in xray.previous_tools:
return "Using cached data"
# Fresh fetch on first call
return f"Fresh data from {source}"
3. Debug Complex Flows
@xray
def process_order(order_id: str) -> str:
"""Process an order."""
# See the full context when debugging
if xray.agent:
print(f"Processing for agent: {xray.agent.name}")
print(f"Original request: {xray.task}")
print(f"Conversation length: {len(xray.messages)}")
return f"Order {order_id} processed"
Tips
- Development Only - Remove @xray in production for best performance
- Combine with IDE - Set breakpoints for interactive debugging
- Use trace() - Call
xray.trace(agent)after runs to see full flow - Check context - Always verify
xray.agentexists before using
Production Notes
Thread Safety: xray uses global state that is NOT thread-safe. This is fine for:
- Single-threaded async servers (uvicorn, asyncio) ✅
- Multi-process workers (gunicorn with workers) ✅
But NOT safe for:
- Multi-threaded servers with shared agent instances ❌
- Parallel tool execution in threads ❌
If you need multi-threading, either:
- Don't use
@xrayin production (recommended) - Use separate agent instances per thread
For production: If you need to access agent context in tools, use the Events system instead. Events provide a cleaner, safer pattern for injecting agent context without global state.
If you have a use case that requires thread-local xray context, please open a GitHub issue to discuss.
Common Patterns
Logging What Matters
@xray
def important_action(data: str) -> str:
# Log with context
if xray.agent:
logger.info(f"Agent {xray.agent.name} performing action")
logger.info(f"Original task: {xray.task}")
logger.info(f"Iteration: {xray.iteration}")
return "Action completed"
Conditional Logic
@xray
def smart_search(query: str) -> str:
# Different strategies based on context
if xray.iteration > 1:
# Broaden search on retry
query = f"{query} OR related"
if "analyze" in xray.previous_tools:
# We already analyzed, search differently
query = f"summary of {query}"
return f"Results for: {query}"
Next Steps
- Try
@replayfor re-running tools with different parameters - See Examples for complete debugging workflows
- Check API Reference for all xray properties