Skip to content

Commit 6f641ad

Browse files
cursoragentalex
andcommitted
Add session diagnostics and improved connectivity debugging tools
Co-authored-by: alex <alex@agentops.ai>
1 parent 6e129e1 commit 6f641ad

7 files changed

Lines changed: 691 additions & 24 deletions

File tree

agentops/__init__.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,141 @@ def extract_key_from_attr(attr_value: str) -> str:
447447
return False
448448

449449

450+
def diagnose_session() -> Dict[str, Any]:
451+
"""
452+
Diagnose the current session status and connectivity.
453+
454+
Returns:
455+
Dictionary containing diagnostic information about the session status.
456+
"""
457+
diagnosis = {
458+
"sdk_initialized": False,
459+
"client_initialized": False,
460+
"has_api_key": False,
461+
"has_auth_token": False,
462+
"active_traces": 0,
463+
"exporter_healthy": False,
464+
"export_stats": {},
465+
"issues": [],
466+
"recommendations": []
467+
}
468+
469+
try:
470+
# Check SDK initialization
471+
diagnosis["sdk_initialized"] = tracer.initialized
472+
if not tracer.initialized:
473+
diagnosis["issues"].append("AgentOps SDK not initialized")
474+
diagnosis["recommendations"].append("Call agentops.init() to initialize the SDK")
475+
return diagnosis
476+
477+
# Check client
478+
client = get_client()
479+
diagnosis["client_initialized"] = client.initialized
480+
481+
# Check API key
482+
diagnosis["has_api_key"] = bool(client.config.api_key)
483+
if not client.config.api_key:
484+
diagnosis["issues"].append("No API key provided")
485+
diagnosis["recommendations"].append("Set AGENTOPS_API_KEY environment variable or pass api_key to init()")
486+
487+
# Check auth token
488+
auth_token = client.get_current_jwt()
489+
diagnosis["has_auth_token"] = bool(auth_token)
490+
if client.config.api_key and not auth_token:
491+
diagnosis["issues"].append("Authentication failed - no JWT token available")
492+
diagnosis["recommendations"].append("Check if API key is valid and network connectivity is working")
493+
494+
# Check active traces
495+
active_traces = tracer.get_active_traces()
496+
diagnosis["active_traces"] = len(active_traces)
497+
498+
# Check exporter health
499+
try:
500+
# Access the exporter from the tracer's span processors
501+
span_processors = tracer._provider._active_span_processor._span_processors
502+
for processor in span_processors:
503+
if hasattr(processor, '_exporter') and hasattr(processor._exporter, 'is_healthy'):
504+
diagnosis["exporter_healthy"] = processor._exporter.is_healthy()
505+
diagnosis["export_stats"] = processor._exporter.get_export_stats()
506+
break
507+
except Exception:
508+
pass
509+
510+
# Analyze issues
511+
if diagnosis["export_stats"].get("failed_exports", 0) > 0:
512+
total_attempts = diagnosis["export_stats"].get("total_attempts", 0)
513+
failed_exports = diagnosis["export_stats"].get("failed_exports", 0)
514+
if total_attempts > 0 and failed_exports / total_attempts > 0.5:
515+
diagnosis["issues"].append(f"High export failure rate: {failed_exports}/{total_attempts} attempts failed")
516+
diagnosis["recommendations"].append("Check network connectivity and API key validity")
517+
518+
if diagnosis["has_api_key"] and not diagnosis["has_auth_token"]:
519+
diagnosis["issues"].append("API key provided but authentication failed")
520+
diagnosis["recommendations"].append("Verify API key is correct and check network connectivity")
521+
522+
if not diagnosis["issues"]:
523+
diagnosis["recommendations"].append("Session appears healthy - data should be reaching backend")
524+
525+
except Exception as e:
526+
diagnosis["issues"].append(f"Error during diagnosis: {e}")
527+
528+
return diagnosis
529+
530+
531+
def print_session_status():
532+
"""
533+
Print a user-friendly diagnostic report of the current session status.
534+
This is helpful for debugging when sessions aren't reaching the backend.
535+
"""
536+
from termcolor import colored
537+
538+
diagnosis = diagnose_session()
539+
540+
print("\n" + "="*50)
541+
print(colored("AgentOps Session Diagnostic Report", "cyan", attrs=["bold"]))
542+
print("="*50)
543+
544+
# Status indicators
545+
status_items = [
546+
("SDK Initialized", diagnosis["sdk_initialized"]),
547+
("Client Initialized", diagnosis["client_initialized"]),
548+
("API Key Present", diagnosis["has_api_key"]),
549+
("Authenticated", diagnosis["has_auth_token"]),
550+
("Exporter Healthy", diagnosis["exporter_healthy"]),
551+
]
552+
553+
print("\nStatus:")
554+
for item, status in status_items:
555+
color = "green" if status else "red"
556+
symbol = "✓" if status else "✗"
557+
print(f" {colored(symbol, color)} {item}: {colored(str(status), color)}")
558+
559+
print(f"\nActive Traces: {diagnosis['active_traces']}")
560+
561+
# Export statistics
562+
if diagnosis["export_stats"]:
563+
stats = diagnosis["export_stats"]
564+
print(f"\nExport Statistics:")
565+
print(f" Total Attempts: {stats.get('total_attempts', 0)}")
566+
print(f" Successful: {stats.get('successful_exports', 0)}")
567+
print(f" Failed: {stats.get('failed_exports', 0)}")
568+
print(f" Success Rate: {stats.get('success_rate', 0)}%")
569+
570+
# Issues
571+
if diagnosis["issues"]:
572+
print(colored("\nIssues Found:", "red", attrs=["bold"]))
573+
for issue in diagnosis["issues"]:
574+
print(f" • {colored(issue, 'red')}")
575+
576+
# Recommendations
577+
if diagnosis["recommendations"]:
578+
print(colored("\nRecommendations:", "yellow", attrs=["bold"]))
579+
for rec in diagnosis["recommendations"]:
580+
print(f" • {colored(rec, 'yellow')}")
581+
582+
print("\n" + "="*50)
583+
584+
450585
__all__ = [
451586
# Legacy exports
452587
"start_session",
@@ -466,6 +601,9 @@ def extract_key_from_attr(attr_value: str) -> str:
466601
"update_trace_metadata",
467602
"Client",
468603
"get_client",
604+
# Diagnostics
605+
"diagnose_session",
606+
"print_session_status",
469607
# Decorators
470608
"trace",
471609
"session",

agentops/client/client.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import asyncio
33
import threading
44
from typing import Optional, Any
5+
import time
56

67
from agentops.client.api import ApiClient
78
from agentops.config import Config
@@ -110,9 +111,10 @@ async def _fetch_auth_async(self, api_key: str) -> Optional[dict]:
110111
logger.debug("Successfully fetched authentication token asynchronously")
111112
return response
112113
else:
113-
logger.debug("Authentication failed - will continue without authentication")
114+
logger.warning("Authentication failed - invalid API key or network issue. Session data will not reach backend.")
114115
return None
115-
except Exception:
116+
except Exception as e:
117+
logger.warning(f"Authentication error: {e}. Session data will not reach backend.")
116118
return None
117119

118120
def _start_auth_task(self, api_key: str):
@@ -143,6 +145,33 @@ def run_async_auth():
143145

144146
auth_thread = threading.Thread(target=run_async_auth, daemon=True)
145147
auth_thread.start()
148+
149+
def wait_for_auth(self, timeout_seconds: int = 10) -> bool:
150+
"""
151+
Wait for authentication to complete.
152+
153+
Args:
154+
timeout_seconds: Maximum time to wait for authentication
155+
156+
Returns:
157+
True if authenticated successfully, False otherwise
158+
"""
159+
if not self.config.api_key:
160+
return False
161+
162+
# If we already have a token, return immediately
163+
if self.get_current_jwt():
164+
return True
165+
166+
# Wait for auth task to complete
167+
start_time = time.time()
168+
while time.time() - start_time < timeout_seconds:
169+
if self.get_current_jwt():
170+
return True
171+
time.sleep(0.1)
172+
173+
logger.warning(f"Authentication timeout after {timeout_seconds}s. Session data may not reach backend.")
174+
return False
146175

147176
def init(self, **kwargs: Any) -> None: # Return type updated to None
148177
# Recreate the Config object to parse environment variables at the time of initialization

agentops/helpers/README_DEBUG.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# AgentOps Session Debugging Tools
2+
3+
This document describes the debugging tools available to help diagnose issues where users see session URLs but no data reaches the AgentOps backend.
4+
5+
## The Problem
6+
7+
Some users experience an issue where:
8+
1. They call `agentops.init()` successfully
9+
2. They see a session URL printed to the console
10+
3. However, no session data actually reaches the AgentOps backend
11+
12+
This happens due to a race condition between URL generation and authentication, plus silent export failures.
13+
14+
## Root Causes
15+
16+
1. **Race Condition**: Session URLs are generated immediately when a trace starts, but authentication happens asynchronously in the background
17+
2. **Silent Authentication Failures**: If authentication fails, the JWT token remains `None` and exports fail silently
18+
3. **Export Failures**: The span exporter fails to send data but this doesn't prevent URL generation
19+
4. **Poor Error Visibility**: Export failures are logged as warnings that users might miss
20+
21+
## Debugging Tools
22+
23+
### 1. `agentops.diagnose_session()`
24+
25+
Returns a dictionary with detailed diagnostic information:
26+
27+
```python
28+
import agentops
29+
30+
agentops.init()
31+
diagnosis = agentops.diagnose_session()
32+
print(diagnosis)
33+
```
34+
35+
Returns:
36+
```python
37+
{
38+
"sdk_initialized": True,
39+
"client_initialized": True,
40+
"has_api_key": True,
41+
"has_auth_token": False, # This indicates the issue!
42+
"active_traces": 1,
43+
"exporter_healthy": False,
44+
"export_stats": {
45+
"total_attempts": 5,
46+
"successful_exports": 0,
47+
"failed_exports": 5,
48+
"success_rate": 0.0
49+
},
50+
"issues": ["Authentication failed - no JWT token available"],
51+
"recommendations": ["Check if API key is valid and network connectivity is working"]
52+
}
53+
```
54+
55+
### 2. `agentops.print_session_status()`
56+
57+
Prints a user-friendly diagnostic report:
58+
59+
```python
60+
import agentops
61+
62+
agentops.init()
63+
agentops.print_session_status()
64+
```
65+
66+
Output:
67+
```
68+
==================================================
69+
AgentOps Session Diagnostic Report
70+
==================================================
71+
72+
Status:
73+
✓ SDK Initialized: True
74+
✓ Client Initialized: True
75+
✓ API Key Present: True
76+
✗ Authenticated: False
77+
✗ Exporter Healthy: False
78+
79+
Active Traces: 1
80+
81+
Export Statistics:
82+
Total Attempts: 3
83+
Successful: 0
84+
Failed: 3
85+
Success Rate: 0.0%
86+
87+
Issues Found:
88+
• Authentication failed - no JWT token available
89+
90+
Recommendations:
91+
• Check if API key is valid and network connectivity is working
92+
==================================================
93+
```
94+
95+
### 3. Full Connectivity Test
96+
97+
Use the debug helper module for comprehensive testing:
98+
99+
```python
100+
from agentops.helpers.debug_session import test_session_connectivity, print_connectivity_test_results
101+
102+
# Test with your API key
103+
results = test_session_connectivity(api_key="your-api-key-here")
104+
print_connectivity_test_results(results)
105+
```
106+
107+
Or run the example script:
108+
```bash
109+
python examples/debug_session_connectivity.py your-api-key-here
110+
```
111+
112+
## Enhanced Error Messages
113+
114+
The updated code now provides better error messages:
115+
116+
1. **Session URL Generation**: Now includes status indicators
117+
- 🟢 Normal URL (authenticated)
118+
- 🟡 Local only URL (no API key)
119+
- 🔴 Auth failed URL (invalid API key)
120+
121+
2. **Export Failures**: More explicit error messages
122+
- "Session data will not reach backend"
123+
- "Session data not sent to backend"
124+
125+
3. **Authentication Issues**: Clearer warnings
126+
- "Authentication failed - invalid API key or network issue"
127+
- "Authentication timeout after Xs. Session data may not reach backend"
128+
129+
## Usage in Support
130+
131+
When users report this issue, ask them to run:
132+
133+
```python
134+
import agentops
135+
agentops.init() # With their normal setup
136+
agentops.print_session_status()
137+
```
138+
139+
This will immediately show:
140+
- Whether they have an API key
141+
- Whether authentication succeeded
142+
- Whether the exporter is healthy
143+
- Export success/failure statistics
144+
- Specific recommendations
145+
146+
## Prevention
147+
148+
The enhanced code also prevents the issue by:
149+
1. Checking authentication status before showing URLs
150+
2. Color-coding URLs based on connectivity status
151+
3. Providing immediate feedback on authentication failures
152+
4. Tracking export statistics for ongoing monitoring

0 commit comments

Comments
 (0)