-
Notifications
You must be signed in to change notification settings - Fork 440
Description
Decorators not working properly when imported from agentops.sdk.decorators
namespace
Summary
There is a critical issue with AgentOps decorators that affects both import paths:
- SDK Namespace Import: Decorators imported from
agentops.sdk.decorators
fail silently when applied beforeagentops.init()
- Main Namespace Import: Decorators imported from
agentops
work but have incorrect span timing that breaks parent-child relationships in traces
Problem Description
SDK Namespace Import (Silent Failure)
from agentops.sdk.decorators import trace
from agentops import init
@trace # Applied before init - becomes a no-op
def my_function():
return "success"
init(api_key="your-key") # Too late - decorator already disabled
my_function() # No span created
Main Namespace Import (Timing Issues)
import agentops
from agentops import trace
agentops.init(api_key="your-key")
@trace # Works but has timing problems
def parent_function():
return child_function()
@trace
def child_function():
return "success"
# Spans created but parent-child timing is incorrect
Root Cause Analysis
Issue 1: SDK Namespace Silent Failure
The primary issue stems from the decorator factory implementation in agentops/sdk/decorators/factory.py
at line 84:
@wrapt.decorator
def wrapper(wrapped_func: Callable[..., Any], instance: Optional[Any], args: tuple, kwargs: Dict[str, Any]) -> Any:
if not tracer.initialized: # ← This is the problem
return wrapped_func(*args, **kwargs)
When decorators are imported directly from agentops.sdk.decorators
, the tracer.initialized
check happens at decoration time (import time), not at function execution time. If AgentOps hasn't been initialized yet when the decorator is applied, the decorator essentially becomes a no-op and never gets a chance to instrument the function properly, even after agentops.init()
is called later.
Issue 2: Main Namespace Span Timing Problems
Even when using the main namespace workaround (from agentops import trace
), there's a secondary issue with span timing that breaks parent-child relationships:
Problem: Span start times are set when tracer.start_span()
is called in _create_as_current_span()
(utility.py:122-123), not when the decorated function actually begins execution. This causes:
- Incorrect span hierarchy: Child spans may appear to start before their parents
- Broken trace visualization: Timeline views show incorrect execution order
- Context propagation issues: Parent-child relationships are not properly maintained
Technical Details:
_create_as_current_span()
usesstart_as_current_span()
with current timestamp- Span timing reflects decorator execution time, not actual function execution time
- OpenTelemetry context management works but timing metadata is incorrect
Impact
SDK Namespace Issues:
- Silent Failures: Functions decorated with
@trace
,@agent
,@tool
, etc. imported from the SDK namespace don't get instrumented - Missing Telemetry Data: No spans are created, leading to complete loss of observability
- No Error Messages: Failures are completely silent, making debugging difficult
Main Namespace Issues:
- Broken Trace Hierarchy: Parent-child relationships appear incorrect in trace visualizations
- Timing Inaccuracies: Span start/end times don't reflect actual function execution
- Context Propagation Problems: Nested function calls may not maintain proper trace context
- Misleading Performance Data: Timing metrics are unreliable for performance analysis
Overall Impact:
- Inconsistent Behavior: Same code works differently depending on import path
- Data Quality Issues: Telemetry data is either missing or incorrect
- Developer Experience: Confusing behavior leads to lost development time
Reproduction Steps
- Create a Python script that imports decorators from
agentops.sdk.decorators
- Apply decorators to functions BEFORE calling
agentops.init()
- Initialize AgentOps after decoration
- Call the decorated functions
- Observe that no spans are created and no telemetry is captured
Expected vs Actual Behavior
SDK Namespace
Expected: Decorators should work regardless of import path and should check tracer initialization at function execution time, not decoration time.
Actual: Decorators imported from agentops.sdk.decorators
become no-ops if applied before agentops.init()
is called.
Main Namespace
Expected: Decorators should create spans with accurate timing that reflects actual function execution and maintains proper parent-child relationships.
Actual: Decorators create spans but with incorrect timing metadata that breaks trace hierarchy visualization and context propagation.
Proposed Solutions
For SDK Namespace Issue:
- Move initialization check to execution time: The
tracer.initialized
check infactory.py:84
should happen when the function is called, not when the decorator is applied - Lazy initialization: Store a reference to check initialization when the decorated function is actually invoked
- Decorator state management: Allow decorators to "activate" retroactively when the tracer becomes initialized
For Main Namespace Timing Issue:
- Defer span creation: Create spans at function entry time, not decorator application time
- Proper timing capture: Ensure span start/end times accurately reflect function execution boundaries
- Context preservation: Maintain proper parent-child relationships with correct timing metadata
- OpenTelemetry best practices: Follow OTel guidelines for span lifecycle management in decorators
Files Affected
agentops/sdk/decorators/factory.py
(line 84)agentops/__init__.py
(import/export structure)- All decorator implementations that use
create_entity_decorator
Test Cases Needed
SDK Namespace Tests:
- Test decorators imported from
agentops.sdk.decorators
with various initialization orders - Test that late initialization properly enables previously "no-op" decorators
- Test error handling when decorators fail silently
Main Namespace Tests:
- Test span timing accuracy for nested function calls
- Test parent-child relationship preservation in trace hierarchy
- Test context propagation with correct timing metadata
- Test performance metric accuracy
Cross-Namespace Tests:
- Test mixed import sources in the same application
- Test initialization order dependencies
- Test behavior consistency across import paths
Workarounds
Current Partial Workaround:
Import decorators from the main agentops
namespace and ensure initialization happens before decoration:
import agentops
# Initialize FIRST
agentops.init(api_key="your-key")
# Then import and use decorators
from agentops import trace, agent, tool
@trace
def my_function():
return "success"
Limitations of Workaround:
- Still has span timing issues that affect trace visualization
- Parent-child relationships may appear incorrect in dashboards
- Performance metrics may be inaccurate
- Not a complete solution - just reduces the severity of the problem
Priority
Critical - This affects core SDK functionality in multiple ways:
- Data Loss: Complete telemetry loss with SDK namespace imports
- Data Quality: Incorrect timing and hierarchy with main namespace imports
- Silent Failures: No error messages make debugging extremely difficult
- User Experience: Inconsistent behavior confuses developers and breaks observability workflows
Both issues need to be addressed to ensure reliable telemetry collection and accurate trace visualization.
Activity
bboynton97 commentedon Jun 30, 2025
code fix in, waiting for token to test
devin-ai-integration commentedon Jul 6, 2025
This issue was resolved by PR #1123 (fix instrumentation)