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/_telemetry.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenTelemetry integration for Python logging.
|
|
3
|
+
|
|
4
|
+
This module bridges Python's standard logging to Rust's tracing/OpenTelemetry system,
|
|
5
|
+
ensuring all logs from ctx.logger are sent to both the console and OTLP exporters.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Dict, MutableMapping, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Standard logging kwargs that should NOT be treated as custom attributes
|
|
14
|
+
_STANDARD_LOGGING_KWARGS = frozenset({
|
|
15
|
+
'exc_info', 'stack_info', 'stacklevel', 'extra'
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ContextLogger(logging.LoggerAdapter):
|
|
20
|
+
"""
|
|
21
|
+
Logger adapter that allows passing arbitrary keyword arguments as log attributes.
|
|
22
|
+
|
|
23
|
+
This enables the convenient API:
|
|
24
|
+
ctx.logger.info("message", attr1="value1", attr2="value2")
|
|
25
|
+
|
|
26
|
+
Instead of the verbose standard logging approach:
|
|
27
|
+
ctx.logger.info("message", extra={"attr1": "value1", "attr2": "value2"})
|
|
28
|
+
|
|
29
|
+
The custom attributes are passed through to the Rust OpenTelemetry system
|
|
30
|
+
for structured logging and will appear as log record attributes in OTLP exports.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> ctx.logger.info("Processing request", request_id="abc123", user_id="user456")
|
|
34
|
+
>>> ctx.logger.error("Failed to connect", host="example.com", port=8080, retries=3)
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, logger: logging.Logger, extra: Optional[Dict[str, Any]] = None):
|
|
38
|
+
"""Initialize the ContextLogger.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
logger: The underlying logger to wrap
|
|
42
|
+
extra: Optional default extra attributes to include in all log records
|
|
43
|
+
"""
|
|
44
|
+
super().__init__(logger, extra or {})
|
|
45
|
+
|
|
46
|
+
def process(
|
|
47
|
+
self, msg: str, kwargs: MutableMapping[str, Any]
|
|
48
|
+
) -> tuple[str, MutableMapping[str, Any]]:
|
|
49
|
+
"""Process the logging call to extract custom attributes.
|
|
50
|
+
|
|
51
|
+
This method intercepts logging calls and extracts any keyword arguments
|
|
52
|
+
that are not standard logging parameters, storing them in the 'extra'
|
|
53
|
+
dict under the key 'agnt5_attrs'.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
msg: The log message
|
|
57
|
+
kwargs: Keyword arguments passed to the logging method
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Tuple of (message, updated_kwargs)
|
|
61
|
+
"""
|
|
62
|
+
# Extract custom attributes (any kwargs that aren't standard logging params)
|
|
63
|
+
custom_attrs = {}
|
|
64
|
+
standard_kwargs = {}
|
|
65
|
+
|
|
66
|
+
for key, value in kwargs.items():
|
|
67
|
+
if key in _STANDARD_LOGGING_KWARGS:
|
|
68
|
+
standard_kwargs[key] = value
|
|
69
|
+
else:
|
|
70
|
+
# Convert non-string values to their string representation
|
|
71
|
+
if isinstance(value, (dict, list)):
|
|
72
|
+
custom_attrs[key] = json.dumps(value)
|
|
73
|
+
elif not isinstance(value, str):
|
|
74
|
+
custom_attrs[key] = str(value)
|
|
75
|
+
else:
|
|
76
|
+
custom_attrs[key] = value
|
|
77
|
+
|
|
78
|
+
# Merge with any existing extra dict
|
|
79
|
+
extra = standard_kwargs.get('extra', {})
|
|
80
|
+
if isinstance(extra, dict):
|
|
81
|
+
extra = dict(extra) # Make a copy
|
|
82
|
+
else:
|
|
83
|
+
extra = {}
|
|
84
|
+
|
|
85
|
+
# Add custom attributes and any default extra from adapter
|
|
86
|
+
if custom_attrs:
|
|
87
|
+
extra['agnt5_attrs'] = custom_attrs
|
|
88
|
+
if self.extra:
|
|
89
|
+
extra.update(self.extra)
|
|
90
|
+
|
|
91
|
+
standard_kwargs['extra'] = extra
|
|
92
|
+
return msg, standard_kwargs
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class OpenTelemetryHandler(logging.Handler):
|
|
96
|
+
"""
|
|
97
|
+
Custom logging handler that forwards Python logs to Rust OpenTelemetry system.
|
|
98
|
+
|
|
99
|
+
This handler routes all Python log records through the Rust `log_from_python()`
|
|
100
|
+
function, which integrates with the tracing ecosystem. This ensures:
|
|
101
|
+
|
|
102
|
+
1. Logs are sent to OpenTelemetry OTLP exporter
|
|
103
|
+
2. Logs appear in console output (via Rust's fmt layer)
|
|
104
|
+
3. Logs inherit span context (invocation.id, trace_id, etc.)
|
|
105
|
+
4. Structured logging with proper attributes
|
|
106
|
+
|
|
107
|
+
The Rust side handles both console output and OTLP export, so we only
|
|
108
|
+
need one handler on the Python side.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self, level=logging.NOTSET):
|
|
112
|
+
"""Initialize the OpenTelemetry handler.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
level: Minimum log level to process (default: NOTSET processes all)
|
|
116
|
+
"""
|
|
117
|
+
super().__init__(level)
|
|
118
|
+
|
|
119
|
+
# Import Rust bridge function
|
|
120
|
+
try:
|
|
121
|
+
from ._core import log_from_python
|
|
122
|
+
self._log_from_python = log_from_python
|
|
123
|
+
except ImportError as e:
|
|
124
|
+
# Fallback if Rust core not available (development/testing)
|
|
125
|
+
import warnings
|
|
126
|
+
warnings.warn(
|
|
127
|
+
f"Failed to import Rust telemetry bridge: {e}. "
|
|
128
|
+
"Logs will not be sent to OpenTelemetry.",
|
|
129
|
+
RuntimeWarning
|
|
130
|
+
)
|
|
131
|
+
self._log_from_python = None
|
|
132
|
+
|
|
133
|
+
def emit(self, record: logging.LogRecord):
|
|
134
|
+
"""
|
|
135
|
+
Process a log record and forward to Rust telemetry.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
record: Python logging record to process
|
|
139
|
+
"""
|
|
140
|
+
if self._log_from_python is None:
|
|
141
|
+
# No Rust bridge available, silently skip
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Filter out gRPC internal logs to avoid noise
|
|
145
|
+
# These are low-level HTTP/2 protocol logs that aren't useful for application debugging
|
|
146
|
+
if record.name.startswith(('grpc.', 'h2.', '_grpc_', 'h2-')):
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
# Format the message (applies any formatters)
|
|
151
|
+
message = self.format(record)
|
|
152
|
+
|
|
153
|
+
# Include exception traceback if present (from logger.exception() or exc_info=True)
|
|
154
|
+
if record.exc_info:
|
|
155
|
+
# Use formatter to format the exception, or fall back to basic formatting
|
|
156
|
+
if self.formatter:
|
|
157
|
+
exc_text = self.formatter.formatException(record.exc_info)
|
|
158
|
+
else:
|
|
159
|
+
# Fallback: use basic traceback formatting
|
|
160
|
+
import traceback
|
|
161
|
+
exc_text = ''.join(traceback.format_exception(*record.exc_info))
|
|
162
|
+
message = f"{message}\n{exc_text}"
|
|
163
|
+
|
|
164
|
+
# Extract correlation IDs from LogRecord attributes (added by _CorrelationFilter)
|
|
165
|
+
# These ensure logs can be correlated with distributed traces in observability backends
|
|
166
|
+
trace_id = getattr(record, 'trace_id', None)
|
|
167
|
+
span_id = getattr(record, 'span_id', None)
|
|
168
|
+
run_id = getattr(record, 'run_id', None)
|
|
169
|
+
|
|
170
|
+
# Extract streaming context for real-time SSE delivery
|
|
171
|
+
is_streaming = getattr(record, 'is_streaming', None)
|
|
172
|
+
tenant_id = getattr(record, 'tenant_id', None)
|
|
173
|
+
deployment_id = getattr(record, 'deployment_id', None)
|
|
174
|
+
|
|
175
|
+
# Extract custom attributes from ContextLogger
|
|
176
|
+
# These are stored in the 'agnt5_attrs' key of the extra dict
|
|
177
|
+
attributes = getattr(record, 'agnt5_attrs', None)
|
|
178
|
+
|
|
179
|
+
# Forward to Rust tracing system
|
|
180
|
+
# Rust side will:
|
|
181
|
+
# - Add to current span context (inherits invocation.id)
|
|
182
|
+
# - Attach correlation IDs as span attributes for OTLP export
|
|
183
|
+
# - Send to OTLP exporter with trace context
|
|
184
|
+
# - Print to console via fmt layer
|
|
185
|
+
# - Export to journal for SSE streaming if is_streaming=True
|
|
186
|
+
self._log_from_python(
|
|
187
|
+
level=record.levelname,
|
|
188
|
+
message=message,
|
|
189
|
+
target=record.name,
|
|
190
|
+
module_path=record.module,
|
|
191
|
+
filename=record.pathname,
|
|
192
|
+
line=record.lineno,
|
|
193
|
+
trace_id=trace_id,
|
|
194
|
+
span_id=span_id,
|
|
195
|
+
run_id=run_id,
|
|
196
|
+
is_streaming=is_streaming,
|
|
197
|
+
tenant_id=tenant_id,
|
|
198
|
+
deployment_id=deployment_id,
|
|
199
|
+
attributes=attributes,
|
|
200
|
+
)
|
|
201
|
+
except Exception:
|
|
202
|
+
# Don't let logging errors crash the application
|
|
203
|
+
# Use handleError to report the issue via logging system
|
|
204
|
+
self.handleError(record)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def setup_context_logger(logger: logging.Logger, log_level: Optional[int] = None) -> None:
|
|
208
|
+
"""
|
|
209
|
+
Configure a Context logger with OpenTelemetry integration.
|
|
210
|
+
|
|
211
|
+
This function:
|
|
212
|
+
1. Removes any existing handlers (avoid duplicates)
|
|
213
|
+
2. Adds OpenTelemetry handler for OTLP + console output (when Worker is running)
|
|
214
|
+
3. Adds console handler for local testing (fallback)
|
|
215
|
+
4. Sets appropriate log level
|
|
216
|
+
5. Disables propagation to avoid duplicate logs
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
logger: Logger instance to configure
|
|
220
|
+
log_level: Optional log level (default: DEBUG)
|
|
221
|
+
"""
|
|
222
|
+
# Remove existing handlers to avoid duplicate logs
|
|
223
|
+
logger.handlers.clear()
|
|
224
|
+
|
|
225
|
+
# Add OpenTelemetry handler (for Worker/platform execution)
|
|
226
|
+
otel_handler = OpenTelemetryHandler()
|
|
227
|
+
otel_handler.setLevel(logging.DEBUG)
|
|
228
|
+
|
|
229
|
+
# Use simple formatter - Rust side handles structured logging
|
|
230
|
+
formatter = logging.Formatter('%(message)s')
|
|
231
|
+
otel_handler.setFormatter(formatter)
|
|
232
|
+
|
|
233
|
+
logger.addHandler(otel_handler)
|
|
234
|
+
|
|
235
|
+
# Add console handler for local testing (fallback when Rust bridge not available)
|
|
236
|
+
# This ensures logs appear when testing functions locally without Worker
|
|
237
|
+
console_handler = logging.StreamHandler()
|
|
238
|
+
console_handler.setLevel(logging.DEBUG)
|
|
239
|
+
|
|
240
|
+
# Console format includes level, message, and exception info if present
|
|
241
|
+
# exc_info=True in the format string means "include traceback if present"
|
|
242
|
+
console_formatter = logging.Formatter(
|
|
243
|
+
'[%(levelname)s] %(message)s',
|
|
244
|
+
# Python automatically appends exception traceback when exc_info is set
|
|
245
|
+
)
|
|
246
|
+
console_handler.setFormatter(console_formatter)
|
|
247
|
+
|
|
248
|
+
logger.addHandler(console_handler)
|
|
249
|
+
|
|
250
|
+
# Set log level (default to DEBUG to let handlers filter)
|
|
251
|
+
if log_level is None:
|
|
252
|
+
log_level = logging.DEBUG
|
|
253
|
+
logger.setLevel(log_level)
|
|
254
|
+
|
|
255
|
+
# Don't propagate to root logger (we handle everything ourselves)
|
|
256
|
+
logger.propagate = False
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def setup_module_logger(module_name: str, log_level: Optional[int] = None) -> logging.Logger:
|
|
260
|
+
"""
|
|
261
|
+
Create and configure a logger for a module with OpenTelemetry integration.
|
|
262
|
+
|
|
263
|
+
Convenience function for setting up loggers in SDK modules.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
module_name: Name of the module (e.g., "agnt5.worker")
|
|
267
|
+
log_level: Optional log level (default: INFO for modules)
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
Configured logger instance
|
|
271
|
+
"""
|
|
272
|
+
logger = logging.getLogger(module_name)
|
|
273
|
+
|
|
274
|
+
# For module loggers, default to INFO level
|
|
275
|
+
if log_level is None:
|
|
276
|
+
log_level = logging.INFO
|
|
277
|
+
|
|
278
|
+
setup_context_logger(logger, log_level)
|
|
279
|
+
return logger
|
agnt5/agent/__init__.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Agent module - AI agents with streaming execution.
|
|
2
|
+
|
|
3
|
+
This module provides the core agent primitives for building AI-powered
|
|
4
|
+
applications with tool orchestration and multi-agent collaboration.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
```python
|
|
8
|
+
from agnt5.agent import Agent, AgentResult, handoff
|
|
9
|
+
|
|
10
|
+
# Create an agent
|
|
11
|
+
agent = Agent(
|
|
12
|
+
name="researcher",
|
|
13
|
+
model="openai/gpt-4o",
|
|
14
|
+
instructions="You are a research assistant.",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Streaming execution (recommended)
|
|
18
|
+
async for event in agent.run("Find recent AI papers"):
|
|
19
|
+
if event.event_type == EventType.LM_MESSAGE_DELTA:
|
|
20
|
+
print(event.data, end="") # data is raw content string for deltas
|
|
21
|
+
|
|
22
|
+
# Non-streaming execution
|
|
23
|
+
result = await agent.run_sync("Find recent AI papers")
|
|
24
|
+
print(result.output)
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# Import from split modules
|
|
29
|
+
from .context import AgentContext
|
|
30
|
+
from .result import AgentResult
|
|
31
|
+
from .handoff import Handoff, handoff
|
|
32
|
+
from .registry import AgentRegistry
|
|
33
|
+
from .core import Agent
|
|
34
|
+
from .decorator import agent
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
# Core classes
|
|
38
|
+
"Agent",
|
|
39
|
+
"AgentContext",
|
|
40
|
+
"AgentResult",
|
|
41
|
+
# Handoff support
|
|
42
|
+
"Handoff",
|
|
43
|
+
"handoff",
|
|
44
|
+
# Registry
|
|
45
|
+
"AgentRegistry",
|
|
46
|
+
# Decorator
|
|
47
|
+
"agent",
|
|
48
|
+
]
|