Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions agentops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ def end_trace(
"task",
"workflow",
"operation",
"guardrail",
"tracer",
"tool",
# Trace state enums
Expand Down
12 changes: 11 additions & 1 deletion agentops/sdk/decorators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
trace = create_entity_decorator(SpanKind.SESSION)
tool = create_entity_decorator(SpanKind.TOOL)
operation = task
guardrail = create_entity_decorator(SpanKind.GUARDRAIL)


# For backward compatibility: @session decorator calls @trace decorator
Expand All @@ -37,4 +38,13 @@ def session(*args, **kwargs): # noqa: F811
# For now, keeping the alias as it was, assuming it was intentional for `operation` to be `task`.
operation = task

__all__ = ["agent", "task", "workflow", "trace", "session", "operation", "tool"]
__all__ = [
"agent",
"task",
"workflow",
"trace",
"session",
"operation",
"tool",
"guardrail",
] # "in_guardrail", "out_guardrail"]
31 changes: 24 additions & 7 deletions agentops/sdk/decorators/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@
version: Optional[Any] = None,
tags: Optional[Union[list, dict]] = None,
cost=None,
spec=None,
) -> Callable[..., Any]:
if wrapped is None:
return functools.partial(decorator, name=name, version=version, tags=tags, cost=cost)
return functools.partial(decorator, name=name, version=version, tags=tags, cost=cost, spec=spec)

if inspect.isclass(wrapped):
# Class decoration wraps __init__ and aenter/aexit for context management.
Expand Down Expand Up @@ -168,10 +169,13 @@
attributes={CoreAttributes.TAGS: tags} if tags else None,
)
try:
_record_entity_input(span, args, kwargs)
_record_entity_input(span, args, kwargs, entity_kind=entity_kind)
# Set cost attribute if tool
if entity_kind == "tool" and cost is not None:
span.set_attribute(SpanAttributes.LLM_USAGE_TOOL_COST, cost)
# Set spec attribute if guardrail
if entity_kind == "guardrail" and (spec == "input" or spec == "output"):
span.set_attribute(SpanAttributes.AGENTOPS_DECORATOR_SPEC.format(entity_kind=entity_kind), spec)

Check warning on line 178 in agentops/sdk/decorators/factory.py

View check run for this annotation

Codecov / codecov/patch

agentops/sdk/decorators/factory.py#L178

Added line #L178 was not covered by tests
except Exception as e:
logger.warning(f"Input recording failed for '{operation_name}': {e}")
result = wrapped_func(*args, **kwargs)
Expand All @@ -184,10 +188,13 @@
attributes={CoreAttributes.TAGS: tags} if tags else None,
)
try:
_record_entity_input(span, args, kwargs)
_record_entity_input(span, args, kwargs, entity_kind=entity_kind)
# Set cost attribute if tool
if entity_kind == "tool" and cost is not None:
span.set_attribute(SpanAttributes.LLM_USAGE_TOOL_COST, cost)
# Set spec attribute if guardrail
if entity_kind == "guardrail" and (spec == "input" or spec == "output"):
span.set_attribute(SpanAttributes.AGENTOPS_DECORATOR_SPEC.format(entity_kind=entity_kind), spec)

Check warning on line 197 in agentops/sdk/decorators/factory.py

View check run for this annotation

Codecov / codecov/patch

agentops/sdk/decorators/factory.py#L197

Added line #L197 was not covered by tests
except Exception as e:
logger.warning(f"Input recording failed for '{operation_name}': {e}")
result = wrapped_func(*args, **kwargs)
Expand All @@ -202,16 +209,21 @@
attributes={CoreAttributes.TAGS: tags} if tags else None,
) as span:
try:
_record_entity_input(span, args, kwargs)
_record_entity_input(span, args, kwargs, entity_kind=entity_kind)
# Set cost attribute if tool
if entity_kind == "tool" and cost is not None:
span.set_attribute(SpanAttributes.LLM_USAGE_TOOL_COST, cost)
# Set spec attribute if guardrail
if entity_kind == "guardrail" and (spec == "input" or spec == "output"):
span.set_attribute(

Check warning on line 218 in agentops/sdk/decorators/factory.py

View check run for this annotation

Codecov / codecov/patch

agentops/sdk/decorators/factory.py#L218

Added line #L218 was not covered by tests
SpanAttributes.AGENTOPS_DECORATOR_SPEC.format(entity_kind=entity_kind), spec
)
except Exception as e:
logger.warning(f"Input recording failed for '{operation_name}': {e}")
try:
result = await wrapped_func(*args, **kwargs)
try:
_record_entity_output(span, result)
_record_entity_output(span, result, entity_kind=entity_kind)
except Exception as e:
logger.warning(f"Output recording failed for '{operation_name}': {e}")
return result
Expand All @@ -229,16 +241,21 @@
attributes={CoreAttributes.TAGS: tags} if tags else None,
) as span:
try:
_record_entity_input(span, args, kwargs)
_record_entity_input(span, args, kwargs, entity_kind=entity_kind)
# Set cost attribute if tool
if entity_kind == "tool" and cost is not None:
span.set_attribute(SpanAttributes.LLM_USAGE_TOOL_COST, cost)
# Set spec attribute if guardrail
if entity_kind == "guardrail" and (spec == "input" or spec == "output"):
span.set_attribute(

Check warning on line 250 in agentops/sdk/decorators/factory.py

View check run for this annotation

Codecov / codecov/patch

agentops/sdk/decorators/factory.py#L250

Added line #L250 was not covered by tests
SpanAttributes.AGENTOPS_DECORATOR_SPEC.format(entity_kind=entity_kind), spec
)
except Exception as e:
logger.warning(f"Input recording failed for '{operation_name}': {e}")
try:
result = wrapped_func(*args, **kwargs)
try:
_record_entity_output(span, result)
_record_entity_output(span, result, entity_kind=entity_kind)
except Exception as e:
logger.warning(f"Output recording failed for '{operation_name}': {e}")
return result
Expand Down
8 changes: 4 additions & 4 deletions agentops/sdk/decorators/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,27 @@ def _create_as_current_span(
logger.debug(f"[DEBUG] AFTER {operation_name}.{span_kind} - Returned to context: {after_span}")


def _record_entity_input(span: trace.Span, args: tuple, kwargs: Dict[str, Any]) -> None:
def _record_entity_input(span: trace.Span, args: tuple, kwargs: Dict[str, Any], entity_kind: str = "entity") -> None:
"""Record operation input parameters to span if content tracing is enabled"""
try:
input_data = {"args": args, "kwargs": kwargs}
json_data = safe_serialize(input_data)

if _check_content_size(json_data):
span.set_attribute(SpanAttributes.AGENTOPS_ENTITY_INPUT, json_data)
span.set_attribute(SpanAttributes.AGENTOPS_DECORATOR_INPUT.format(entity_kind=entity_kind), json_data)
else:
logger.debug("Operation input exceeds size limit, not recording")
except Exception as err:
logger.warning(f"Failed to serialize operation input: {err}")


def _record_entity_output(span: trace.Span, result: Any) -> None:
def _record_entity_output(span: trace.Span, result: Any, entity_kind: str = "entity") -> None:
"""Record operation output value to span if content tracing is enabled"""
try:
json_data = safe_serialize(result)

if _check_content_size(json_data):
span.set_attribute(SpanAttributes.AGENTOPS_ENTITY_OUTPUT, json_data)
span.set_attribute(SpanAttributes.AGENTOPS_DECORATOR_OUTPUT.format(entity_kind=entity_kind), json_data)
else:
logger.debug("Operation output exceeds size limit, not recording")
except Exception as err:
Expand Down
3 changes: 3 additions & 0 deletions agentops/semconv/span_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class SpanAttributes:
AGENTOPS_ENTITY_INPUT = "agentops.entity.input"
AGENTOPS_SPAN_KIND = "agentops.span.kind"
AGENTOPS_ENTITY_NAME = "agentops.entity.name"
AGENTOPS_DECORATOR_SPEC = "agentops.{entity_kind}.spec"
AGENTOPS_DECORATOR_INPUT = "agentops.{entity_kind}.input"
AGENTOPS_DECORATOR_OUTPUT = "agentops.{entity_kind}.output"

# Operation attributes
OPERATION_NAME = "operation.name"
Expand Down
1 change: 1 addition & 0 deletions agentops/semconv/span_kinds.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SpanKind:
UNKNOWN = "unknown"
CHAIN = "chain"
TEXT = "text"
GUARDRAIL = "guardrail"


class AgentOpsSpanKindValues(Enum):
Expand Down
Loading