spanforge 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spanforge/__init__.py +815 -0
- spanforge/_ansi.py +93 -0
- spanforge/_batch_exporter.py +409 -0
- spanforge/_cli.py +2094 -0
- spanforge/_cli_audit.py +639 -0
- spanforge/_cli_compliance.py +711 -0
- spanforge/_cli_cost.py +243 -0
- spanforge/_cli_ops.py +791 -0
- spanforge/_cli_phase11.py +356 -0
- spanforge/_hooks.py +337 -0
- spanforge/_server.py +1708 -0
- spanforge/_span.py +1036 -0
- spanforge/_store.py +288 -0
- spanforge/_stream.py +664 -0
- spanforge/_trace.py +335 -0
- spanforge/_tracer.py +254 -0
- spanforge/actor.py +141 -0
- spanforge/alerts.py +469 -0
- spanforge/auto.py +464 -0
- spanforge/baseline.py +335 -0
- spanforge/cache.py +635 -0
- spanforge/compliance.py +325 -0
- spanforge/config.py +532 -0
- spanforge/consent.py +228 -0
- spanforge/consumer.py +377 -0
- spanforge/core/__init__.py +5 -0
- spanforge/core/compliance_mapping.py +1254 -0
- spanforge/cost.py +600 -0
- spanforge/debug.py +548 -0
- spanforge/deprecations.py +205 -0
- spanforge/drift.py +482 -0
- spanforge/egress.py +58 -0
- spanforge/eval.py +648 -0
- spanforge/event.py +1064 -0
- spanforge/exceptions.py +240 -0
- spanforge/explain.py +178 -0
- spanforge/export/__init__.py +69 -0
- spanforge/export/append_only.py +337 -0
- spanforge/export/cloud.py +357 -0
- spanforge/export/datadog.py +497 -0
- spanforge/export/grafana.py +320 -0
- spanforge/export/jsonl.py +195 -0
- spanforge/export/openinference.py +158 -0
- spanforge/export/otel_bridge.py +294 -0
- spanforge/export/otlp.py +811 -0
- spanforge/export/otlp_bridge.py +233 -0
- spanforge/export/redis_backend.py +282 -0
- spanforge/export/siem_schema.py +98 -0
- spanforge/export/siem_splunk.py +264 -0
- spanforge/export/siem_syslog.py +212 -0
- spanforge/export/webhook.py +299 -0
- spanforge/exporters/__init__.py +30 -0
- spanforge/exporters/console.py +271 -0
- spanforge/exporters/jsonl.py +144 -0
- spanforge/exporters/sqlite.py +142 -0
- spanforge/gate.py +1150 -0
- spanforge/governance.py +181 -0
- spanforge/hitl.py +295 -0
- spanforge/http.py +187 -0
- spanforge/inspect.py +427 -0
- spanforge/integrations/__init__.py +45 -0
- spanforge/integrations/_pricing.py +280 -0
- spanforge/integrations/anthropic.py +388 -0
- spanforge/integrations/azure_openai.py +133 -0
- spanforge/integrations/bedrock.py +292 -0
- spanforge/integrations/crewai.py +251 -0
- spanforge/integrations/gemini.py +351 -0
- spanforge/integrations/groq.py +442 -0
- spanforge/integrations/langchain.py +349 -0
- spanforge/integrations/langgraph.py +306 -0
- spanforge/integrations/llamaindex.py +373 -0
- spanforge/integrations/ollama.py +287 -0
- spanforge/integrations/openai.py +368 -0
- spanforge/integrations/together.py +483 -0
- spanforge/io.py +214 -0
- spanforge/lint.py +322 -0
- spanforge/metrics.py +417 -0
- spanforge/metrics_export.py +343 -0
- spanforge/migrate.py +402 -0
- spanforge/model_registry.py +278 -0
- spanforge/models.py +389 -0
- spanforge/namespaces/__init__.py +254 -0
- spanforge/namespaces/audit.py +256 -0
- spanforge/namespaces/cache.py +237 -0
- spanforge/namespaces/chain.py +77 -0
- spanforge/namespaces/confidence.py +72 -0
- spanforge/namespaces/consent.py +92 -0
- spanforge/namespaces/cost.py +179 -0
- spanforge/namespaces/decision.py +143 -0
- spanforge/namespaces/diff.py +157 -0
- spanforge/namespaces/drift.py +80 -0
- spanforge/namespaces/eval_.py +251 -0
- spanforge/namespaces/feedback.py +241 -0
- spanforge/namespaces/fence.py +193 -0
- spanforge/namespaces/guard.py +105 -0
- spanforge/namespaces/hitl.py +91 -0
- spanforge/namespaces/latency.py +72 -0
- spanforge/namespaces/prompt.py +190 -0
- spanforge/namespaces/redact.py +173 -0
- spanforge/namespaces/retrieval.py +379 -0
- spanforge/namespaces/runtime_governance.py +494 -0
- spanforge/namespaces/template.py +208 -0
- spanforge/namespaces/tool_call.py +77 -0
- spanforge/namespaces/trace.py +1029 -0
- spanforge/normalizer.py +171 -0
- spanforge/plugins.py +82 -0
- spanforge/presidio_backend.py +349 -0
- spanforge/processor.py +258 -0
- spanforge/prompt_registry.py +418 -0
- spanforge/py.typed +0 -0
- spanforge/redact.py +914 -0
- spanforge/regression.py +192 -0
- spanforge/runtime_policy.py +159 -0
- spanforge/sampling.py +511 -0
- spanforge/schema.py +183 -0
- spanforge/schemas/v1.0/schema.json +170 -0
- spanforge/schemas/v2.0/schema.json +536 -0
- spanforge/sdk/__init__.py +625 -0
- spanforge/sdk/_base.py +584 -0
- spanforge/sdk/_base.pyi +71 -0
- spanforge/sdk/_exceptions.py +1096 -0
- spanforge/sdk/_types.py +2184 -0
- spanforge/sdk/alert.py +1514 -0
- spanforge/sdk/alert.pyi +56 -0
- spanforge/sdk/audit.py +1196 -0
- spanforge/sdk/audit.pyi +67 -0
- spanforge/sdk/cec.py +1215 -0
- spanforge/sdk/cec.pyi +37 -0
- spanforge/sdk/config.py +641 -0
- spanforge/sdk/config.pyi +55 -0
- spanforge/sdk/enterprise.py +714 -0
- spanforge/sdk/enterprise.pyi +79 -0
- spanforge/sdk/explain.py +170 -0
- spanforge/sdk/fallback.py +432 -0
- spanforge/sdk/feedback.py +351 -0
- spanforge/sdk/gate.py +874 -0
- spanforge/sdk/gate.pyi +51 -0
- spanforge/sdk/identity.py +2114 -0
- spanforge/sdk/identity.pyi +47 -0
- spanforge/sdk/lineage.py +175 -0
- spanforge/sdk/observe.py +1065 -0
- spanforge/sdk/observe.pyi +50 -0
- spanforge/sdk/operator.py +338 -0
- spanforge/sdk/pii.py +1473 -0
- spanforge/sdk/pii.pyi +119 -0
- spanforge/sdk/pipelines.py +458 -0
- spanforge/sdk/pipelines.pyi +39 -0
- spanforge/sdk/policy.py +930 -0
- spanforge/sdk/rag.py +594 -0
- spanforge/sdk/rbac.py +280 -0
- spanforge/sdk/registry.py +430 -0
- spanforge/sdk/registry.pyi +46 -0
- spanforge/sdk/scope.py +279 -0
- spanforge/sdk/secrets.py +293 -0
- spanforge/sdk/secrets.pyi +25 -0
- spanforge/sdk/security.py +560 -0
- spanforge/sdk/security.pyi +57 -0
- spanforge/sdk/trust.py +472 -0
- spanforge/sdk/trust.pyi +41 -0
- spanforge/secrets.py +799 -0
- spanforge/signing.py +1179 -0
- spanforge/stats.py +100 -0
- spanforge/stream.py +560 -0
- spanforge/testing.py +378 -0
- spanforge/testing_mocks.py +1052 -0
- spanforge/trace.py +199 -0
- spanforge/types.py +696 -0
- spanforge/ulid.py +300 -0
- spanforge/validate.py +379 -0
- spanforge-1.0.0.dist-info/METADATA +1509 -0
- spanforge-1.0.0.dist-info/RECORD +174 -0
- spanforge-1.0.0.dist-info/WHEEL +4 -0
- spanforge-1.0.0.dist-info/entry_points.txt +5 -0
- spanforge-1.0.0.dist-info/licenses/LICENSE +128 -0
spanforge/trace.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""spanforge.trace — @trace() function decorator and trace engine (Tool 1 / llm-trace).
|
|
2
|
+
|
|
3
|
+
Single decorator that instruments any Python function as an SpanForge span::
|
|
4
|
+
|
|
5
|
+
from spanforge import trace
|
|
6
|
+
|
|
7
|
+
@trace(name="search", model="gpt-4o")
|
|
8
|
+
def call_llm(prompt: str) -> str: ...
|
|
9
|
+
|
|
10
|
+
@trace(name="async-step")
|
|
11
|
+
async def async_step(x: int) -> dict: ...
|
|
12
|
+
|
|
13
|
+
@trace(name="web-search", tool=True)
|
|
14
|
+
def search_web(query: str) -> list[str]: ...
|
|
15
|
+
|
|
16
|
+
Supports:
|
|
17
|
+
- Sync and async functions/methods
|
|
18
|
+
- Auto-capture of call arguments and return values (opt-in)
|
|
19
|
+
- Parent-child span relationships via contextvars
|
|
20
|
+
- ``tool=True`` to emit spans with ``operation="execute_tool"``
|
|
21
|
+
- Pytest fixture integration via :func:`~spanforge.testing.captured_spans`
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import functools
|
|
27
|
+
import inspect
|
|
28
|
+
from typing import Any, Callable, TypeVar
|
|
29
|
+
|
|
30
|
+
from spanforge._span import SpanContextManager
|
|
31
|
+
|
|
32
|
+
__all__ = ["trace"]
|
|
33
|
+
|
|
34
|
+
_F = TypeVar("_F", bound=Callable[..., Any])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _safe_repr(value: Any, max_len: int = 200) -> str:
|
|
38
|
+
"""Return a repr of *value* truncated to *max_len* characters."""
|
|
39
|
+
try:
|
|
40
|
+
r = repr(value)
|
|
41
|
+
except Exception:
|
|
42
|
+
r = "<unrepresentable>"
|
|
43
|
+
return r[:max_len] + "..." if len(r) > max_len else r
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _TraceDecorator:
|
|
47
|
+
"""Wraps a callable so every invocation is recorded as an SpanForge span.
|
|
48
|
+
|
|
49
|
+
Created by :func:`trace`; use :func:`trace` rather than instantiating
|
|
50
|
+
this class directly.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
fn: Callable[..., Any],
|
|
56
|
+
name: str | None,
|
|
57
|
+
model: str | None,
|
|
58
|
+
operation: str,
|
|
59
|
+
tool: bool,
|
|
60
|
+
capture_args: bool,
|
|
61
|
+
capture_return: bool,
|
|
62
|
+
attributes: dict[str, Any] | None,
|
|
63
|
+
) -> None:
|
|
64
|
+
self._fn = fn
|
|
65
|
+
self._name = name or fn.__qualname__
|
|
66
|
+
self._model = model
|
|
67
|
+
self._operation = operation
|
|
68
|
+
self._tool = tool
|
|
69
|
+
self._capture_args = capture_args
|
|
70
|
+
self._capture_return = capture_return
|
|
71
|
+
self._attributes = dict(attributes or {})
|
|
72
|
+
# Preserve __name__, __doc__, __module__, etc. on the wrapper.
|
|
73
|
+
functools.update_wrapper(self, fn)
|
|
74
|
+
|
|
75
|
+
# ------------------------------------------------------------------
|
|
76
|
+
# Internal helpers
|
|
77
|
+
# ------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
def _build_attrs(self, args: tuple[Any, ...], kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
80
|
+
"""Build the initial span attributes dict from static attrs + captured args."""
|
|
81
|
+
attrs: dict[str, Any] = dict(self._attributes)
|
|
82
|
+
if self._tool:
|
|
83
|
+
# Mark span so InspectorSession can detect it even without checking operation.
|
|
84
|
+
attrs["tool"] = True
|
|
85
|
+
if self._capture_args or self._tool:
|
|
86
|
+
try:
|
|
87
|
+
sig = inspect.signature(self._fn)
|
|
88
|
+
bound = sig.bind(*args, **kwargs)
|
|
89
|
+
bound.apply_defaults()
|
|
90
|
+
for k, v in bound.arguments.items():
|
|
91
|
+
attrs[f"arg.{k}"] = _safe_repr(v)
|
|
92
|
+
except (TypeError, ValueError):
|
|
93
|
+
pass
|
|
94
|
+
return attrs
|
|
95
|
+
|
|
96
|
+
def _record_return(self, span: Any, result: Any) -> None:
|
|
97
|
+
if self._capture_return or self._tool:
|
|
98
|
+
span.set_attribute("return_value", _safe_repr(result))
|
|
99
|
+
|
|
100
|
+
# ------------------------------------------------------------------
|
|
101
|
+
# Sync call
|
|
102
|
+
# ------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
105
|
+
attrs = self._build_attrs(args, kwargs)
|
|
106
|
+
cm = SpanContextManager(
|
|
107
|
+
name=self._name,
|
|
108
|
+
model=self._model,
|
|
109
|
+
operation=self._operation,
|
|
110
|
+
attributes=attrs,
|
|
111
|
+
)
|
|
112
|
+
with cm as span:
|
|
113
|
+
result = self._fn(*args, **kwargs)
|
|
114
|
+
self._record_return(span, result)
|
|
115
|
+
return result
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _make_async_wrapper(decorator: _TraceDecorator, fn: Callable[..., Any]) -> Callable[..., Any]:
|
|
119
|
+
"""Return an async wrapper that runs *fn* inside a span context."""
|
|
120
|
+
|
|
121
|
+
@functools.wraps(fn)
|
|
122
|
+
async def _async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
123
|
+
attrs = decorator._build_attrs(args, kwargs)
|
|
124
|
+
cm = SpanContextManager(
|
|
125
|
+
name=decorator._name,
|
|
126
|
+
model=decorator._model,
|
|
127
|
+
operation=decorator._operation,
|
|
128
|
+
attributes=attrs,
|
|
129
|
+
)
|
|
130
|
+
async with cm as span:
|
|
131
|
+
result = await fn(*args, **kwargs)
|
|
132
|
+
decorator._record_return(span, result)
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
return _async_wrapper
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def trace(
|
|
139
|
+
fn: Callable[..., Any] | None = None,
|
|
140
|
+
*,
|
|
141
|
+
name: str | None = None,
|
|
142
|
+
model: str | None = None,
|
|
143
|
+
operation: str = "chat",
|
|
144
|
+
tool: bool = False,
|
|
145
|
+
capture_args: bool = False,
|
|
146
|
+
capture_return: bool = False,
|
|
147
|
+
attributes: dict[str, Any] | None = None,
|
|
148
|
+
) -> Any:
|
|
149
|
+
"""Decorator that instruments a function as an SpanForge span.
|
|
150
|
+
|
|
151
|
+
Works with or without parentheses::
|
|
152
|
+
|
|
153
|
+
@trace
|
|
154
|
+
def my_fn(): ...
|
|
155
|
+
|
|
156
|
+
@trace(name="custom-name", model="gpt-4o")
|
|
157
|
+
def my_fn(): ...
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
fn: The function to wrap (only when used bare as ``@trace``).
|
|
161
|
+
name: Span name. Defaults to ``fn.__qualname__``.
|
|
162
|
+
model: Model identifier string forwarded to ``SpanPayload``.
|
|
163
|
+
operation: GenAI operation name (default ``"chat"``). Any
|
|
164
|
+
:class:`~spanforge.namespaces.trace.GenAIOperationName`
|
|
165
|
+
value or a custom string.
|
|
166
|
+
tool: When ``True``, marks this as a tool call; sets
|
|
167
|
+
``operation="execute_tool"`` regardless of *operation*.
|
|
168
|
+
capture_args: When ``True``, records call arguments as ``arg.<name>``
|
|
169
|
+
span attributes (values truncated to 200 chars).
|
|
170
|
+
capture_return: When ``True``, records the return value as a
|
|
171
|
+
``return_value`` span attribute (truncated to 200 chars).
|
|
172
|
+
attributes: Static key-value attributes added to every span.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Decorated callable (sync or async), or a single-argument decorator
|
|
176
|
+
when keyword arguments are supplied.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def _decorate(f: Callable[..., Any]) -> Callable[..., Any]:
|
|
180
|
+
effective_op = "execute_tool" if tool else operation
|
|
181
|
+
dec = _TraceDecorator(
|
|
182
|
+
fn=f,
|
|
183
|
+
name=name,
|
|
184
|
+
model=model,
|
|
185
|
+
operation=effective_op,
|
|
186
|
+
tool=tool,
|
|
187
|
+
capture_args=capture_args,
|
|
188
|
+
capture_return=capture_return,
|
|
189
|
+
attributes=attributes,
|
|
190
|
+
)
|
|
191
|
+
if inspect.iscoroutinefunction(f):
|
|
192
|
+
return _make_async_wrapper(dec, f)
|
|
193
|
+
return dec
|
|
194
|
+
|
|
195
|
+
if fn is not None:
|
|
196
|
+
# @trace — bare decorator, no parentheses
|
|
197
|
+
return _decorate(fn)
|
|
198
|
+
# @trace(...) — decorator factory
|
|
199
|
+
return _decorate
|