diff --git a/agentops/__init__.py b/agentops/__init__.py index 816e77443..ffcbe29d3 100755 --- a/agentops/__init__.py +++ b/agentops/__init__.py @@ -447,6 +447,141 @@ def extract_key_from_attr(attr_value: str) -> str: return False +def diagnose_session() -> Dict[str, Any]: + """ + Diagnose the current session status and connectivity. + + Returns: + Dictionary containing diagnostic information about the session status. + """ + diagnosis = { + "sdk_initialized": False, + "client_initialized": False, + "has_api_key": False, + "has_auth_token": False, + "active_traces": 0, + "exporter_healthy": False, + "export_stats": {}, + "issues": [], + "recommendations": [] + } + + try: + # Check SDK initialization + diagnosis["sdk_initialized"] = tracer.initialized + if not tracer.initialized: + diagnosis["issues"].append("AgentOps SDK not initialized") + diagnosis["recommendations"].append("Call agentops.init() to initialize the SDK") + return diagnosis + + # Check client + client = get_client() + diagnosis["client_initialized"] = client.initialized + + # Check API key + diagnosis["has_api_key"] = bool(client.config.api_key) + if not client.config.api_key: + diagnosis["issues"].append("No API key provided") + diagnosis["recommendations"].append("Set AGENTOPS_API_KEY environment variable or pass api_key to init()") + + # Check auth token + auth_token = client.get_current_jwt() + diagnosis["has_auth_token"] = bool(auth_token) + if client.config.api_key and not auth_token: + diagnosis["issues"].append("Authentication failed - no JWT token available") + diagnosis["recommendations"].append("Check if API key is valid and network connectivity is working") + + # Check active traces + active_traces = tracer.get_active_traces() + diagnosis["active_traces"] = len(active_traces) + + # Check exporter health + try: + # Access the exporter from the tracer's span processors + span_processors = tracer._provider._active_span_processor._span_processors + for processor in span_processors: + if hasattr(processor, '_exporter') and hasattr(processor._exporter, 'is_healthy'): + diagnosis["exporter_healthy"] = processor._exporter.is_healthy() + diagnosis["export_stats"] = processor._exporter.get_export_stats() + break + except Exception: + pass + + # Analyze issues + if diagnosis["export_stats"].get("failed_exports", 0) > 0: + total_attempts = diagnosis["export_stats"].get("total_attempts", 0) + failed_exports = diagnosis["export_stats"].get("failed_exports", 0) + if total_attempts > 0 and failed_exports / total_attempts > 0.5: + diagnosis["issues"].append(f"High export failure rate: {failed_exports}/{total_attempts} attempts failed") + diagnosis["recommendations"].append("Check network connectivity and API key validity") + + if diagnosis["has_api_key"] and not diagnosis["has_auth_token"]: + diagnosis["issues"].append("API key provided but authentication failed") + diagnosis["recommendations"].append("Verify API key is correct and check network connectivity") + + if not diagnosis["issues"]: + diagnosis["recommendations"].append("Session appears healthy - data should be reaching backend") + + except Exception as e: + diagnosis["issues"].append(f"Error during diagnosis: {e}") + + return diagnosis + + +def print_session_status(): + """ + Print a user-friendly diagnostic report of the current session status. + This is helpful for debugging when sessions aren't reaching the backend. + """ + from termcolor import colored + + diagnosis = diagnose_session() + + print("\n" + "="*50) + print(colored("AgentOps Session Diagnostic Report", "cyan", attrs=["bold"])) + print("="*50) + + # Status indicators + status_items = [ + ("SDK Initialized", diagnosis["sdk_initialized"]), + ("Client Initialized", diagnosis["client_initialized"]), + ("API Key Present", diagnosis["has_api_key"]), + ("Authenticated", diagnosis["has_auth_token"]), + ("Exporter Healthy", diagnosis["exporter_healthy"]), + ] + + print("\nStatus:") + for item, status in status_items: + color = "green" if status else "red" + symbol = "✓" if status else "✗" + print(f" {colored(symbol, color)} {item}: {colored(str(status), color)}") + + print(f"\nActive Traces: {diagnosis['active_traces']}") + + # Export statistics + if diagnosis["export_stats"]: + stats = diagnosis["export_stats"] + print(f"\nExport Statistics:") + print(f" Total Attempts: {stats.get('total_attempts', 0)}") + print(f" Successful: {stats.get('successful_exports', 0)}") + print(f" Failed: {stats.get('failed_exports', 0)}") + print(f" Success Rate: {stats.get('success_rate', 0)}%") + + # Issues + if diagnosis["issues"]: + print(colored("\nIssues Found:", "red", attrs=["bold"])) + for issue in diagnosis["issues"]: + print(f" • {colored(issue, 'red')}") + + # Recommendations + if diagnosis["recommendations"]: + print(colored("\nRecommendations:", "yellow", attrs=["bold"])) + for rec in diagnosis["recommendations"]: + print(f" • {colored(rec, 'yellow')}") + + print("\n" + "="*50) + + __all__ = [ # Legacy exports "start_session", @@ -466,6 +601,9 @@ def extract_key_from_attr(attr_value: str) -> str: "update_trace_metadata", "Client", "get_client", + # Diagnostics + "diagnose_session", + "print_session_status", # Decorators "trace", "session", diff --git a/agentops/client/client.py b/agentops/client/client.py index d66e898c7..0759854e0 100644 --- a/agentops/client/client.py +++ b/agentops/client/client.py @@ -2,6 +2,7 @@ import asyncio import threading from typing import Optional, Any +import time from agentops.client.api import ApiClient from agentops.config import Config @@ -110,9 +111,10 @@ async def _fetch_auth_async(self, api_key: str) -> Optional[dict]: logger.debug("Successfully fetched authentication token asynchronously") return response else: - logger.debug("Authentication failed - will continue without authentication") + logger.warning("Authentication failed - invalid API key or network issue. Session data will not reach backend.") return None - except Exception: + except Exception as e: + logger.warning(f"Authentication error: {e}. Session data will not reach backend.") return None def _start_auth_task(self, api_key: str): @@ -143,6 +145,33 @@ def run_async_auth(): auth_thread = threading.Thread(target=run_async_auth, daemon=True) auth_thread.start() + + def wait_for_auth(self, timeout_seconds: int = 10) -> bool: + """ + Wait for authentication to complete. + + Args: + timeout_seconds: Maximum time to wait for authentication + + Returns: + True if authenticated successfully, False otherwise + """ + if not self.config.api_key: + return False + + # If we already have a token, return immediately + if self.get_current_jwt(): + return True + + # Wait for auth task to complete + start_time = time.time() + while time.time() - start_time < timeout_seconds: + if self.get_current_jwt(): + return True + time.sleep(0.1) + + logger.warning(f"Authentication timeout after {timeout_seconds}s. Session data may not reach backend.") + return False def init(self, **kwargs: Any) -> None: # Return type updated to None # Recreate the Config object to parse environment variables at the time of initialization diff --git a/agentops/helpers/README_DEBUG.md b/agentops/helpers/README_DEBUG.md new file mode 100644 index 000000000..b7a1c8f27 --- /dev/null +++ b/agentops/helpers/README_DEBUG.md @@ -0,0 +1,152 @@ +# AgentOps Session Debugging Tools + +This document describes the debugging tools available to help diagnose issues where users see session URLs but no data reaches the AgentOps backend. + +## The Problem + +Some users experience an issue where: +1. They call `agentops.init()` successfully +2. They see a session URL printed to the console +3. However, no session data actually reaches the AgentOps backend + +This happens due to a race condition between URL generation and authentication, plus silent export failures. + +## Root Causes + +1. **Race Condition**: Session URLs are generated immediately when a trace starts, but authentication happens asynchronously in the background +2. **Silent Authentication Failures**: If authentication fails, the JWT token remains `None` and exports fail silently +3. **Export Failures**: The span exporter fails to send data but this doesn't prevent URL generation +4. **Poor Error Visibility**: Export failures are logged as warnings that users might miss + +## Debugging Tools + +### 1. `agentops.diagnose_session()` + +Returns a dictionary with detailed diagnostic information: + +```python +import agentops + +agentops.init() +diagnosis = agentops.diagnose_session() +print(diagnosis) +``` + +Returns: +```python +{ + "sdk_initialized": True, + "client_initialized": True, + "has_api_key": True, + "has_auth_token": False, # This indicates the issue! + "active_traces": 1, + "exporter_healthy": False, + "export_stats": { + "total_attempts": 5, + "successful_exports": 0, + "failed_exports": 5, + "success_rate": 0.0 + }, + "issues": ["Authentication failed - no JWT token available"], + "recommendations": ["Check if API key is valid and network connectivity is working"] +} +``` + +### 2. `agentops.print_session_status()` + +Prints a user-friendly diagnostic report: + +```python +import agentops + +agentops.init() +agentops.print_session_status() +``` + +Output: +``` +================================================== +AgentOps Session Diagnostic Report +================================================== + +Status: + ✓ SDK Initialized: True + ✓ Client Initialized: True + ✓ API Key Present: True + ✗ Authenticated: False + ✗ Exporter Healthy: False + +Active Traces: 1 + +Export Statistics: + Total Attempts: 3 + Successful: 0 + Failed: 3 + Success Rate: 0.0% + +Issues Found: + • Authentication failed - no JWT token available + +Recommendations: + • Check if API key is valid and network connectivity is working +================================================== +``` + +### 3. Full Connectivity Test + +Use the debug helper module for comprehensive testing: + +```python +from agentops.helpers.debug_session import test_session_connectivity, print_connectivity_test_results + +# Test with your API key +results = test_session_connectivity(api_key="your-api-key-here") +print_connectivity_test_results(results) +``` + +Or run the example script: +```bash +python examples/debug_session_connectivity.py your-api-key-here +``` + +## Enhanced Error Messages + +The updated code now provides better error messages: + +1. **Session URL Generation**: Now includes status indicators + - 🟢 Normal URL (authenticated) + - 🟡 Local only URL (no API key) + - 🔴 Auth failed URL (invalid API key) + +2. **Export Failures**: More explicit error messages + - "Session data will not reach backend" + - "Session data not sent to backend" + +3. **Authentication Issues**: Clearer warnings + - "Authentication failed - invalid API key or network issue" + - "Authentication timeout after Xs. Session data may not reach backend" + +## Usage in Support + +When users report this issue, ask them to run: + +```python +import agentops +agentops.init() # With their normal setup +agentops.print_session_status() +``` + +This will immediately show: +- Whether they have an API key +- Whether authentication succeeded +- Whether the exporter is healthy +- Export success/failure statistics +- Specific recommendations + +## Prevention + +The enhanced code also prevents the issue by: +1. Checking authentication status before showing URLs +2. Color-coding URLs based on connectivity status +3. Providing immediate feedback on authentication failures +4. Tracking export statistics for ongoing monitoring \ No newline at end of file diff --git a/agentops/helpers/debug_session.py b/agentops/helpers/debug_session.py new file mode 100644 index 000000000..bdd27247c --- /dev/null +++ b/agentops/helpers/debug_session.py @@ -0,0 +1,168 @@ +""" +Debug helper for AgentOps session connectivity issues. + +This module provides utilities to help users diagnose why their sessions +might not be reaching the AgentOps backend. +""" + +import time +from typing import Dict, Any +from agentops.logging import logger + + +def test_session_connectivity(api_key: str = None, timeout: int = 15) -> Dict[str, Any]: + """ + Test session connectivity end-to-end. + + Args: + api_key: Optional API key to test with + timeout: Maximum time to wait for authentication + + Returns: + Dictionary with test results + """ + import agentops + + results = { + "test_passed": False, + "steps": [], + "issues": [], + "session_url": None + } + + try: + # Step 1: Initialize AgentOps + results["steps"].append("Initializing AgentOps...") + if api_key: + session = agentops.init(api_key=api_key, log_level="DEBUG") + else: + session = agentops.init(log_level="DEBUG") + results["steps"].append("✓ AgentOps initialized") + + # Step 2: Check client status + client = agentops.get_client() + if not client.config.api_key: + results["issues"].append("No API key provided") + results["steps"].append("✗ No API key found") + return results + results["steps"].append("✓ API key found") + + # Step 3: Wait for authentication + results["steps"].append("Waiting for authentication...") + auth_success = client.wait_for_auth(timeout) + if auth_success: + results["steps"].append("✓ Authentication successful") + else: + results["issues"].append("Authentication failed or timed out") + results["steps"].append("✗ Authentication failed") + + # Step 4: Check session status + diagnosis = agentops.diagnose_session() + if diagnosis["has_auth_token"] and diagnosis["active_traces"] > 0: + results["steps"].append("✓ Session active and authenticated") + results["test_passed"] = True + else: + results["issues"].extend(diagnosis["issues"]) + results["steps"].append("✗ Session issues detected") + + # Get session URL if available + if diagnosis["active_traces"] > 0: + active_traces = agentops.tracer.get_active_traces() + if active_traces: + trace_context = list(active_traces.values())[0] + from agentops.helpers.dashboard import get_trace_url + results["session_url"] = get_trace_url(trace_context.span) + + # Step 5: Test a simple operation + if results["test_passed"]: + results["steps"].append("Testing span creation...") + try: + # Create a test span to trigger export + with agentops.trace("connectivity_test"): + time.sleep(0.1) # Brief operation + results["steps"].append("✓ Test span created") + + # Wait a moment for export to attempt + time.sleep(2) + + # Check export stats + for processor in client.api._provider._active_span_processor._span_processors: + if hasattr(processor, '_exporter') and hasattr(processor._exporter, 'get_export_stats'): + stats = processor._exporter.get_export_stats() + if stats.get("successful_exports", 0) > 0: + results["steps"].append("✓ Span successfully exported to backend") + elif stats.get("failed_exports", 0) > 0: + results["issues"].append("Span export failed - check network and API key") + results["steps"].append("✗ Span export failed") + results["test_passed"] = False + break + + except Exception as e: + results["issues"].append(f"Error during span test: {e}") + results["steps"].append("✗ Span test failed") + results["test_passed"] = False + + except Exception as e: + results["issues"].append(f"Test failed with error: {e}") + results["steps"].append(f"✗ Test error: {e}") + + finally: + # Clean up + try: + agentops.end_trace() + except: + pass + + return results + + +def print_connectivity_test_results(results: Dict[str, Any]): + """Print formatted connectivity test results.""" + from termcolor import colored + + print("\n" + "="*60) + print(colored("AgentOps Connectivity Test Results", "cyan", attrs=["bold"])) + print("="*60) + + # Overall result + if results["test_passed"]: + print(colored("\n✓ CONNECTIVITY TEST PASSED", "green", attrs=["bold"])) + print("Your AgentOps session should be working correctly!") + else: + print(colored("\n✗ CONNECTIVITY TEST FAILED", "red", attrs=["bold"])) + print("Your session data is likely not reaching the AgentOps backend.") + + # Test steps + print(colored("\nTest Steps:", "cyan", attrs=["bold"])) + for step in results["steps"]: + if step.startswith("✓"): + print(colored(f" {step}", "green")) + elif step.startswith("✗"): + print(colored(f" {step}", "red")) + else: + print(f" {step}") + + # Session URL + if results["session_url"]: + print(f"\nSession URL: {results['session_url']}") + + # Issues + if results["issues"]: + print(colored("\nIssues Found:", "red", attrs=["bold"])) + for issue in results["issues"]: + print(f" • {colored(issue, 'red')}") + + print("\n" + "="*60) + + +if __name__ == "__main__": + """Run connectivity test when script is executed directly.""" + import sys + + api_key = None + if len(sys.argv) > 1: + api_key = sys.argv[1] + + print("Running AgentOps connectivity test...") + results = test_session_connectivity(api_key) + print_connectivity_test_results(results) \ No newline at end of file diff --git a/agentops/sdk/core.py b/agentops/sdk/core.py index f42285ec6..65f3de1d9 100644 --- a/agentops/sdk/core.py +++ b/agentops/sdk/core.py @@ -4,30 +4,30 @@ import threading from typing import Optional, Any, Dict, Union, Callable -from opentelemetry import metrics, trace -from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter +from opentelemetry.context import context_api +from opentelemetry.sdk.trace import TracerProvider, Span +from opentelemetry.sdk.trace.export import BatchSpanProcessor +from opentelemetry.trace import Tracer, get_current_span, get_tracer +from opentelemetry import trace, metrics from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader +from opentelemetry.trace.status import StatusCode from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider, Span -from opentelemetry.sdk.trace.export import BatchSpanProcessor -from opentelemetry import context as context_api -from agentops.exceptions import AgentOpsClientNotInitializedException -from agentops.logging import logger, setup_print_logger -from agentops.sdk.processors import InternalSpanProcessor -from agentops.sdk.types import TracingConfig from agentops.sdk.exporters import AuthenticatedOTLPExporter +from agentops.sdk.processors import InternalSpanProcessor from agentops.sdk.attributes import ( get_global_resource_attributes, get_trace_attributes, - get_span_attributes, - get_session_end_attributes, get_system_resource_attributes, + get_session_end_attributes, ) +from agentops.sdk.types import TracingConfig +from agentops.logging import logger, setup_print_logger +from agentops.helpers.dashboard import log_trace_url, get_trace_url +from agentops.exceptions import AgentOpsClientNotInitializedException from agentops.semconv import SpanKind -from agentops.helpers.dashboard import log_trace_url -from opentelemetry.trace.status import StatusCode # No need to create shortcuts since we're using our own ResourceAttributes class now @@ -315,7 +315,7 @@ def _flush_span_processors(self) -> None: except Exception as e: logger.warning(f"Failed to force flush provider's span processors: {e}", exc_info=True) - def get_tracer(self, name: str = "agentops") -> trace.Tracer: + def get_tracer(self, name: str = "agentops") -> Tracer: """ Get a tracer with the given name. @@ -328,7 +328,7 @@ def get_tracer(self, name: str = "agentops") -> trace.Tracer: if not self._initialized: raise AgentOpsClientNotInitializedException - return trace.get_tracer(name) + return get_tracer(name) @classmethod def initialize_from_config( @@ -409,7 +409,28 @@ def start_trace( # Log the session replay URL for this new trace try: - log_trace_url(span, title=trace_name) + # Check if we have authentication before showing the URL + # Import here to avoid circular imports + from agentops.client.client import Client + + # Get client instance without using agentops.get_client() to avoid circular import + client = Client() + + # Only log URL if we have a valid auth token or if auth is still pending + auth_token = client.get_current_jwt() + if auth_token or (client.config.api_key and hasattr(client, '_auth_task') and client._auth_task and not client._auth_task.done()): + log_trace_url(span, title=trace_name) + elif not client.config.api_key: + logger.warning(f"Session URL for '{trace_name}' trace: {get_trace_url(span)} - Note: No API key provided, session data will not be sent to backend") + # Still log the URL but with a warning + session_url = get_trace_url(span) + from termcolor import colored + logger.info(colored(f"\x1b[33mSession URL (local only - no API key): {session_url}\x1b[0m", "yellow")) + else: + logger.warning(f"Authentication failed for API key. Session URL: {get_trace_url(span)} - Session data may not reach backend") + session_url = get_trace_url(span) + from termcolor import colored + logger.info(colored(f"\x1b[31mSession URL (auth failed): {session_url}\x1b[0m", "red")) except Exception as e: logger.warning(f"Failed to log trace URL for '{trace_name}': {e}") diff --git a/agentops/sdk/exporters.py b/agentops/sdk/exporters.py index 268217c97..5636b0661 100644 --- a/agentops/sdk/exporters.py +++ b/agentops/sdk/exporters.py @@ -50,6 +50,9 @@ def __init__( self._lock = threading.Lock() self._last_auth_failure = 0 self._auth_failure_threshold = 60 # Don't retry auth failures more than once per minute + self._export_attempts = 0 + self._successful_exports = 0 + self._failed_exports = 0 # Store any additional kwargs for potential future use self._custom_kwargs = kwargs @@ -125,16 +128,28 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: This method overrides the parent's export to ensure we always use the latest JWT token and handle authentication failures gracefully. """ + with self._lock: + self._export_attempts += 1 + # Check if we should skip due to recent auth failure with self._lock: current_time = time.time() if self._last_auth_failure > 0 and current_time - self._last_auth_failure < self._auth_failure_threshold: logger.debug("Skipping export due to recent authentication failure") + self._failed_exports += 1 return SpanExportResult.FAILURE try: # Get current JWT and prepare headers current_headers = self._prepare_headers() + + # Check if we have authentication + jwt_token = self._get_current_jwt() + if not jwt_token: + logger.warning("No JWT token available for span export. Session data will not reach backend.") + with self._lock: + self._failed_exports += 1 + return SpanExportResult.FAILURE # Temporarily update the session headers for this request original_headers = dict(self._session.headers) @@ -148,6 +163,15 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: if result == SpanExportResult.SUCCESS: with self._lock: self._last_auth_failure = 0 + self._successful_exports += 1 + + # Log success for first few exports to confirm connectivity + if self._successful_exports <= 3: + logger.debug(f"Successfully exported spans to backend (attempt #{self._successful_exports})") + else: + with self._lock: + self._failed_exports += 1 + logger.warning(f"Span export failed with result: {result}") return result @@ -157,6 +181,9 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: self._session.headers.update(original_headers) except requests.exceptions.HTTPError as e: + with self._lock: + self._failed_exports += 1 + if e.response and e.response.status_code in (401, 403): # Authentication error - record timestamp and warn with self._lock: @@ -164,36 +191,46 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: logger.warning( f"Authentication failed during span export: {e}. " - f"Will retry in {self._auth_failure_threshold} seconds." + f"Will retry in {self._auth_failure_threshold} seconds. " + f"Session data is not reaching backend." ) return SpanExportResult.FAILURE else: - logger.error(f"HTTP error during span export: {e}") + logger.error(f"HTTP error during span export: {e}. Session data not sent to backend.") return SpanExportResult.FAILURE except AgentOpsApiJwtExpiredException as e: # JWT expired - record timestamp and warn with self._lock: self._last_auth_failure = time.time() + self._failed_exports += 1 logger.warning( - f"JWT token expired during span export: {e}. " f"Will retry in {self._auth_failure_threshold} seconds." + f"JWT token expired during span export: {e}. " + f"Will retry in {self._auth_failure_threshold} seconds. " + f"Session data is not reaching backend." ) return SpanExportResult.FAILURE except ApiServerException as e: # Server-side error - logger.error(f"API server error during span export: {e}") + with self._lock: + self._failed_exports += 1 + logger.error(f"API server error during span export: {e}. Session data not sent to backend.") return SpanExportResult.FAILURE except requests.RequestException as e: # Network or HTTP error - logger.error(f"Network error during span export: {e}") + with self._lock: + self._failed_exports += 1 + logger.error(f"Network error during span export: {e}. Session data not sent to backend.") return SpanExportResult.FAILURE except Exception as e: # Any other error - logger.error(f"Unexpected error during span export: {e}") + with self._lock: + self._failed_exports += 1 + logger.error(f"Unexpected error during span export: {e}. Session data not sent to backend.") return SpanExportResult.FAILURE def clear(self): @@ -204,3 +241,33 @@ def clear(self): The OTLP exporter doesn't store spans, so this is a no-op. """ pass + + def get_export_stats(self) -> Dict[str, int]: + """Get export statistics for debugging.""" + with self._lock: + return { + "total_attempts": self._export_attempts, + "successful_exports": self._successful_exports, + "failed_exports": self._failed_exports, + "success_rate": round(self._successful_exports / max(self._export_attempts, 1) * 100, 2) + } + + def is_healthy(self) -> bool: + """Check if the exporter is healthy (has authentication and recent successful exports).""" + jwt_token = self._get_current_jwt() + if not jwt_token: + return False + + with self._lock: + # Consider healthy if we have recent successful exports or haven't tried yet + if self._export_attempts == 0: + return True # Haven't tried yet, assume healthy + + # Check if we have recent auth failures + current_time = time.time() + if self._last_auth_failure > 0 and current_time - self._last_auth_failure < self._auth_failure_threshold: + return False + + # Consider healthy if success rate is above 50% + success_rate = self._successful_exports / max(self._export_attempts, 1) + return success_rate > 0.5 diff --git a/examples/debug_session_connectivity.py b/examples/debug_session_connectivity.py new file mode 100644 index 000000000..e6876c7e4 --- /dev/null +++ b/examples/debug_session_connectivity.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +Example script to debug AgentOps session connectivity issues. + +This script demonstrates how to use the new diagnostic tools to identify +why some users might see session URLs but have no data reaching the backend. +""" + +import agentops +import time +import sys + + +def main(): + """Main function to demonstrate session debugging.""" + print("AgentOps Session Connectivity Debug Example") + print("=" * 50) + + # Example 1: Initialize without API key (will show the issue) + print("\n1. Testing without API key (should show connectivity issues):") + try: + # This will show a session URL but won't send data to backend + agentops.init(api_key=None, log_level="INFO") + + # Use the diagnostic function + agentops.print_session_status() + + # Clean up + agentops.end_trace() + + except Exception as e: + print(f"Error in test 1: {e}") + + # Example 2: Initialize with API key (if provided) + if len(sys.argv) > 1: + api_key = sys.argv[1] + print(f"\n2. Testing with provided API key:") + + try: + # Initialize with API key + agentops.init(api_key=api_key, log_level="INFO") + + # Wait for authentication + client = agentops.get_client() + print("Waiting for authentication...") + auth_success = client.wait_for_auth(timeout_seconds=10) + + if auth_success: + print("✓ Authentication successful!") + else: + print("✗ Authentication failed or timed out") + + # Show diagnostic report + agentops.print_session_status() + + # Test with actual operations + print("\n3. Testing with actual operations:") + with agentops.trace("test_operation") as trace: + # Simulate some work + time.sleep(1) + + # Add some metadata + agentops.update_trace_metadata({ + "test_type": "connectivity_debug", + "user_agent": "debug_script" + }) + + # Wait for export to complete + time.sleep(3) + + # Final diagnostic + print("\nFinal diagnostic after operations:") + agentops.print_session_status() + + except Exception as e: + print(f"Error in test 2: {e}") + finally: + agentops.end_trace() + else: + print("\n2. To test with API key, run: python debug_session_connectivity.py YOUR_API_KEY") + + # Example 3: Run the full connectivity test + print("\n3. Running full connectivity test:") + from agentops.helpers.debug_session import test_session_connectivity, print_connectivity_test_results + + api_key = sys.argv[1] if len(sys.argv) > 1 else None + results = test_session_connectivity(api_key) + print_connectivity_test_results(results) + + +if __name__ == "__main__": + main() \ No newline at end of file