agnt5 0.3.2a1__cp310-abi3-manylinux_2_34_aarch64.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.
Potentially problematic release.
This version of agnt5 might be problematic. Click here for more details.
- agnt5/__init__.py +119 -0
- agnt5/_compat.py +16 -0
- agnt5/_core.abi3.so +0 -0
- agnt5/_retry_utils.py +196 -0
- agnt5/_schema_utils.py +312 -0
- agnt5/_sentry.py +515 -0
- agnt5/_telemetry.py +279 -0
- agnt5/agent/__init__.py +48 -0
- agnt5/agent/context.py +581 -0
- agnt5/agent/core.py +1782 -0
- agnt5/agent/decorator.py +112 -0
- agnt5/agent/handoff.py +105 -0
- agnt5/agent/registry.py +68 -0
- agnt5/agent/result.py +39 -0
- agnt5/checkpoint.py +246 -0
- agnt5/client.py +1556 -0
- agnt5/context.py +288 -0
- agnt5/emit.py +197 -0
- agnt5/entity.py +1230 -0
- agnt5/events.py +567 -0
- agnt5/exceptions.py +110 -0
- agnt5/function.py +330 -0
- agnt5/journal.py +212 -0
- agnt5/lm.py +1266 -0
- agnt5/memoization.py +379 -0
- agnt5/memory.py +521 -0
- agnt5/tool.py +721 -0
- agnt5/tracing.py +300 -0
- agnt5/types.py +111 -0
- agnt5/version.py +19 -0
- agnt5/worker.py +2094 -0
- agnt5/workflow.py +1632 -0
- agnt5-0.3.2a1.dist-info/METADATA +26 -0
- agnt5-0.3.2a1.dist-info/RECORD +35 -0
- agnt5-0.3.2a1.dist-info/WHEEL +4 -0
agnt5/tracing.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User-facing tracing API for AGNT5 SDK.
|
|
3
|
+
|
|
4
|
+
Provides decorators and context managers for instrumenting Python code with
|
|
5
|
+
OpenTelemetry spans. All spans are created via Rust FFI and exported through
|
|
6
|
+
the centralized Rust OpenTelemetry system.
|
|
7
|
+
|
|
8
|
+
This module uses Python's contextvars for async-safe span context propagation,
|
|
9
|
+
ensuring proper parent-child relationships even with asyncio.gather() and
|
|
10
|
+
other parallel async operations.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
```python
|
|
14
|
+
from agnt5.tracing import span
|
|
15
|
+
|
|
16
|
+
@span("my_operation")
|
|
17
|
+
async def my_function(ctx, data):
|
|
18
|
+
# Your code here
|
|
19
|
+
return result
|
|
20
|
+
|
|
21
|
+
# Or use context manager
|
|
22
|
+
from agnt5.tracing import span_context
|
|
23
|
+
|
|
24
|
+
async def process():
|
|
25
|
+
with span_context("processing", user_id="123") as s:
|
|
26
|
+
data = await fetch_data()
|
|
27
|
+
s.set_attribute("records", str(len(data)))
|
|
28
|
+
return data
|
|
29
|
+
```
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import functools
|
|
33
|
+
import inspect
|
|
34
|
+
from contextvars import ContextVar
|
|
35
|
+
from contextlib import contextmanager
|
|
36
|
+
from dataclasses import dataclass
|
|
37
|
+
from typing import Any, Callable, Dict, Optional, Tuple
|
|
38
|
+
|
|
39
|
+
from ._core import create_span as _rust_create_span
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class SpanInfo:
|
|
44
|
+
"""Information about the current span for context propagation."""
|
|
45
|
+
trace_id: str
|
|
46
|
+
span_id: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Async-safe contextvar for tracking the current span
|
|
50
|
+
# This is task-local in asyncio, ensuring proper isolation for parallel operations
|
|
51
|
+
_current_span: ContextVar[Optional[SpanInfo]] = ContextVar('current_span', default=None)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_current_span_info() -> Optional[SpanInfo]:
|
|
55
|
+
"""Get the current span info from the contextvar (if any)."""
|
|
56
|
+
return _current_span.get()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class SpanContextManager:
|
|
60
|
+
"""
|
|
61
|
+
Wrapper around PySpan that manages the contextvar for proper async-safe
|
|
62
|
+
parent-child span linking.
|
|
63
|
+
|
|
64
|
+
When entering this context manager:
|
|
65
|
+
1. Saves the previous span info
|
|
66
|
+
2. Sets this span as the current span in the contextvar
|
|
67
|
+
|
|
68
|
+
When exiting:
|
|
69
|
+
1. Restores the previous span info
|
|
70
|
+
2. Calls PySpan.__exit__ to end the span
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, py_span):
|
|
74
|
+
self._py_span = py_span
|
|
75
|
+
self._token = None
|
|
76
|
+
|
|
77
|
+
def __enter__(self):
|
|
78
|
+
# Enter the underlying PySpan first
|
|
79
|
+
self._py_span.__enter__()
|
|
80
|
+
|
|
81
|
+
# Set this span as the current span in the contextvar
|
|
82
|
+
# The token allows us to restore the previous value on exit
|
|
83
|
+
span_info = SpanInfo(
|
|
84
|
+
trace_id=self._py_span.trace_id,
|
|
85
|
+
span_id=self._py_span.span_id
|
|
86
|
+
)
|
|
87
|
+
self._token = _current_span.set(span_info)
|
|
88
|
+
|
|
89
|
+
return self._py_span
|
|
90
|
+
|
|
91
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
92
|
+
# Restore the previous span context first
|
|
93
|
+
if self._token is not None:
|
|
94
|
+
_current_span.reset(self._token)
|
|
95
|
+
self._token = None
|
|
96
|
+
|
|
97
|
+
# Then exit the underlying PySpan (ends the span, handles exceptions)
|
|
98
|
+
return self._py_span.__exit__(exc_type, exc_val, exc_tb)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def create_span(
|
|
102
|
+
name: str,
|
|
103
|
+
component_type: str = "operation",
|
|
104
|
+
runtime_context: Optional[Any] = None,
|
|
105
|
+
attributes: Optional[Dict[str, str]] = None,
|
|
106
|
+
) -> SpanContextManager:
|
|
107
|
+
"""
|
|
108
|
+
Create a span with proper async-safe context propagation.
|
|
109
|
+
|
|
110
|
+
This function checks the contextvar for the current span and passes
|
|
111
|
+
the parent trace_id/span_id to Rust for proper parent-child linking.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
name: Span name
|
|
115
|
+
component_type: Component type (e.g., "function", "task", "agent")
|
|
116
|
+
runtime_context: Optional RuntimeContext for initial trace context
|
|
117
|
+
attributes: Optional span attributes
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
SpanContextManager that can be used as a context manager
|
|
121
|
+
"""
|
|
122
|
+
# Get the current span from contextvar (async-safe parent lookup)
|
|
123
|
+
current_span = _current_span.get()
|
|
124
|
+
|
|
125
|
+
parent_trace_id = None
|
|
126
|
+
parent_span_id = None
|
|
127
|
+
if current_span is not None:
|
|
128
|
+
parent_trace_id = current_span.trace_id
|
|
129
|
+
parent_span_id = current_span.span_id
|
|
130
|
+
|
|
131
|
+
# Create the Rust span with parent IDs for proper linking
|
|
132
|
+
py_span = _rust_create_span(
|
|
133
|
+
name,
|
|
134
|
+
component_type,
|
|
135
|
+
runtime_context,
|
|
136
|
+
attributes or {},
|
|
137
|
+
parent_trace_id,
|
|
138
|
+
parent_span_id,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Wrap in our context manager for contextvar management
|
|
142
|
+
return SpanContextManager(py_span)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def span(
|
|
146
|
+
name: Optional[str] = None,
|
|
147
|
+
component_type: str = "function",
|
|
148
|
+
runtime_context: Optional[Any] = None,
|
|
149
|
+
**attributes: str
|
|
150
|
+
):
|
|
151
|
+
"""
|
|
152
|
+
Decorator to automatically create spans for functions.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
name: Span name (defaults to function name)
|
|
156
|
+
component_type: Component type (default: "function")
|
|
157
|
+
runtime_context: Optional RuntimeContext for trace linking
|
|
158
|
+
**attributes: Additional span attributes
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
```python
|
|
162
|
+
@span("fetch_user_data", user_type="premium")
|
|
163
|
+
async def fetch_user(user_id: str):
|
|
164
|
+
return await db.get_user(user_id)
|
|
165
|
+
```
|
|
166
|
+
"""
|
|
167
|
+
def decorator(func: Callable) -> Callable:
|
|
168
|
+
span_name = name or func.__name__
|
|
169
|
+
|
|
170
|
+
if inspect.iscoroutinefunction(func):
|
|
171
|
+
@functools.wraps(func)
|
|
172
|
+
async def async_wrapper(*args, **kwargs):
|
|
173
|
+
# Try to extract runtime_context from first arg if it's a Context
|
|
174
|
+
ctx = runtime_context
|
|
175
|
+
if ctx is None and args:
|
|
176
|
+
from .context import Context
|
|
177
|
+
if isinstance(args[0], Context):
|
|
178
|
+
ctx = args[0]._runtime_context
|
|
179
|
+
|
|
180
|
+
with create_span(span_name, component_type, ctx, attributes) as s:
|
|
181
|
+
try:
|
|
182
|
+
result = await func(*args, **kwargs)
|
|
183
|
+
# Span automatically marked as OK on success
|
|
184
|
+
return result
|
|
185
|
+
except Exception as e:
|
|
186
|
+
# Exception automatically recorded by PySpan.__exit__
|
|
187
|
+
raise
|
|
188
|
+
return async_wrapper
|
|
189
|
+
else:
|
|
190
|
+
@functools.wraps(func)
|
|
191
|
+
def sync_wrapper(*args, **kwargs):
|
|
192
|
+
# Try to extract runtime_context from first arg if it's a Context
|
|
193
|
+
ctx = runtime_context
|
|
194
|
+
if ctx is None and args:
|
|
195
|
+
from .context import Context
|
|
196
|
+
if isinstance(args[0], Context):
|
|
197
|
+
ctx = args[0]._runtime_context
|
|
198
|
+
|
|
199
|
+
with create_span(span_name, component_type, ctx, attributes) as s:
|
|
200
|
+
try:
|
|
201
|
+
result = func(*args, **kwargs)
|
|
202
|
+
return result
|
|
203
|
+
except Exception as e:
|
|
204
|
+
raise
|
|
205
|
+
return sync_wrapper
|
|
206
|
+
|
|
207
|
+
return decorator
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@contextmanager
|
|
211
|
+
def span_context(
|
|
212
|
+
name: str,
|
|
213
|
+
component_type: str = "operation",
|
|
214
|
+
runtime_context: Optional[Any] = None,
|
|
215
|
+
**attributes: str
|
|
216
|
+
):
|
|
217
|
+
"""
|
|
218
|
+
Context manager for creating spans around code blocks.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
name: Span name
|
|
222
|
+
component_type: Component type (default: "operation")
|
|
223
|
+
runtime_context: Optional RuntimeContext for trace linking
|
|
224
|
+
**attributes: Span attributes
|
|
225
|
+
|
|
226
|
+
Yields:
|
|
227
|
+
PySpan object with set_attribute() and record_exception() methods
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
```python
|
|
231
|
+
with span_context("db_query", runtime_context=ctx._runtime_context, table="users") as s:
|
|
232
|
+
results = query_database()
|
|
233
|
+
s.set_attribute("result_count", str(len(results)))
|
|
234
|
+
```
|
|
235
|
+
"""
|
|
236
|
+
with create_span(name, component_type, runtime_context, attributes) as s:
|
|
237
|
+
yield s
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def create_task_span(name: str, runtime_context: Optional[Any] = None, **attributes: str):
|
|
241
|
+
"""
|
|
242
|
+
Create a span for task execution.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
name: Task name
|
|
246
|
+
runtime_context: Optional RuntimeContext for trace linking
|
|
247
|
+
**attributes: Task attributes
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
SpanContextManager to use as context manager
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
```python
|
|
254
|
+
with create_task_span("process_data", runtime_context=ctx._runtime_context, batch_size="100") as s:
|
|
255
|
+
result = await process()
|
|
256
|
+
```
|
|
257
|
+
"""
|
|
258
|
+
return create_span(name, "task", runtime_context, attributes)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def create_workflow_span(name: str, runtime_context: Optional[Any] = None, **attributes: str):
|
|
262
|
+
"""
|
|
263
|
+
Create a span for workflow execution.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
name: Workflow name
|
|
267
|
+
runtime_context: Optional RuntimeContext for trace linking
|
|
268
|
+
**attributes: Workflow attributes
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
SpanContextManager to use as context manager
|
|
272
|
+
"""
|
|
273
|
+
return create_span(name, "workflow", runtime_context, attributes)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def create_agent_span(name: str, runtime_context: Optional[Any] = None, **attributes: str):
|
|
277
|
+
"""
|
|
278
|
+
Create a span for agent execution.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
name: Agent name
|
|
282
|
+
runtime_context: Optional RuntimeContext for trace linking
|
|
283
|
+
**attributes: Agent attributes
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
SpanContextManager to use as context manager
|
|
287
|
+
"""
|
|
288
|
+
return create_span(name, "agent", runtime_context, attributes)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
__all__ = [
|
|
292
|
+
"span",
|
|
293
|
+
"span_context",
|
|
294
|
+
"create_span",
|
|
295
|
+
"create_task_span",
|
|
296
|
+
"create_workflow_span",
|
|
297
|
+
"create_agent_span",
|
|
298
|
+
"get_current_span_info",
|
|
299
|
+
"SpanInfo",
|
|
300
|
+
]
|
agnt5/types.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Type definitions and protocols for AGNT5 SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Awaitable, Callable, Dict, List, Optional, Protocol, TypeVar, Union
|
|
8
|
+
|
|
9
|
+
# Type aliases
|
|
10
|
+
JSON = Union[Dict[str, Any], List[Any], str, int, float, bool, None]
|
|
11
|
+
HandlerFunc = Callable[..., Awaitable[Any]]
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BackoffType(str, Enum):
|
|
17
|
+
"""Backoff strategy for retry policies."""
|
|
18
|
+
|
|
19
|
+
CONSTANT = "constant"
|
|
20
|
+
LINEAR = "linear"
|
|
21
|
+
EXPONENTIAL = "exponential"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class RetryPolicy:
|
|
26
|
+
"""Configuration for function retry behavior."""
|
|
27
|
+
|
|
28
|
+
max_attempts: int = 3
|
|
29
|
+
initial_interval_ms: int = 1000
|
|
30
|
+
max_interval_ms: int = 60000
|
|
31
|
+
|
|
32
|
+
def __post_init__(self) -> None:
|
|
33
|
+
if self.max_attempts < 1:
|
|
34
|
+
raise ValueError("max_attempts must be at least 1")
|
|
35
|
+
if self.initial_interval_ms < 0:
|
|
36
|
+
raise ValueError("initial_interval_ms must be non-negative")
|
|
37
|
+
if self.max_interval_ms < self.initial_interval_ms:
|
|
38
|
+
raise ValueError("max_interval_ms must be >= initial_interval_ms")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class BackoffPolicy:
|
|
43
|
+
"""Configuration for retry backoff strategy."""
|
|
44
|
+
|
|
45
|
+
type: BackoffType = BackoffType.EXPONENTIAL
|
|
46
|
+
multiplier: float = 2.0
|
|
47
|
+
|
|
48
|
+
def __post_init__(self) -> None:
|
|
49
|
+
if self.multiplier <= 0:
|
|
50
|
+
raise ValueError("multiplier must be positive")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class FunctionConfig:
|
|
55
|
+
"""Configuration for a function handler."""
|
|
56
|
+
|
|
57
|
+
name: str
|
|
58
|
+
handler: HandlerFunc
|
|
59
|
+
retries: Optional[RetryPolicy] = None
|
|
60
|
+
backoff: Optional[BackoffPolicy] = None
|
|
61
|
+
timeout_ms: Optional[int] = None
|
|
62
|
+
input_schema: Optional[Dict[str, Any]] = None
|
|
63
|
+
output_schema: Optional[Dict[str, Any]] = None
|
|
64
|
+
metadata: Optional[Dict[str, str]] = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class WorkflowConfig:
|
|
69
|
+
"""Configuration for a workflow handler."""
|
|
70
|
+
|
|
71
|
+
name: str
|
|
72
|
+
handler: HandlerFunc
|
|
73
|
+
input_schema: Optional[Dict[str, Any]] = None
|
|
74
|
+
output_schema: Optional[Dict[str, Any]] = None
|
|
75
|
+
metadata: Optional[Dict[str, str]] = None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ContextProtocol(Protocol):
|
|
79
|
+
"""Protocol defining the Context interface."""
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def run_id(self) -> str:
|
|
83
|
+
"""Workflow/run identifier."""
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def step_id(self) -> Optional[str]:
|
|
88
|
+
"""Current step identifier."""
|
|
89
|
+
...
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def attempt(self) -> int:
|
|
93
|
+
"""Retry attempt number."""
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def component_type(self) -> str:
|
|
98
|
+
"""Component type: 'function', 'entity', 'workflow'."""
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
async def get(self, key: str, default: Any = None) -> Any:
|
|
102
|
+
"""Get value from state."""
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
def set(self, key: str, value: Any) -> None:
|
|
106
|
+
"""Set value in state."""
|
|
107
|
+
...
|
|
108
|
+
|
|
109
|
+
def delete(self, key: str) -> None:
|
|
110
|
+
"""Delete key from state."""
|
|
111
|
+
...
|
agnt5/version.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Version information for agnt5 SDK."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _get_version() -> str:
|
|
5
|
+
"""Get package version from installed metadata.
|
|
6
|
+
|
|
7
|
+
This uses importlib.metadata (Python 3.8+) to read the version from
|
|
8
|
+
the installed package metadata, maintaining pyproject.toml as the
|
|
9
|
+
single source of truth.
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
Package version string, or "0.0.0+dev" for development installs.
|
|
13
|
+
"""
|
|
14
|
+
try:
|
|
15
|
+
from importlib.metadata import version
|
|
16
|
+
return version("agnt5")
|
|
17
|
+
except Exception:
|
|
18
|
+
# Development/editable install fallback
|
|
19
|
+
return "0.0.0+dev"
|