agnt5 0.1.3__cp39-abi3-win_amd64.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/worker.py ADDED
@@ -0,0 +1,261 @@
1
+ """
2
+ High-level Worker manager that integrates function decorators with the Rust core.
3
+ """
4
+
5
+ import asyncio
6
+ import logging
7
+ import time
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from ._compat import _rust_available, _import_error
11
+ from .decorators import get_registered_functions, get_function_metadata, invoke_function
12
+ from .runtimes import WorkerRuntime, ASGIRuntime
13
+ from .logging import install_opentelemetry_logging
14
+
15
+ # Core functionality import from Rust extension
16
+ from ._compat import _rust_available
17
+
18
+ if _rust_available:
19
+ from ._core import PyWorker, PyWorkerConfig, PyInvokeFunctionRequest, PyInvokeFunctionResponse, PyComponentInfo
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class Worker:
25
+ """
26
+ High-level AGNT5 Worker that automatically registers decorated functions.
27
+
28
+ This class wraps the low-level Rust PyWorker and provides automatic
29
+ registration of @function decorated handlers.
30
+ """
31
+
32
+ def __init__(self,
33
+ service_name: str,
34
+ service_version: str = "1.0.0",
35
+ coordinator_endpoint: str = "http://localhost:9091",
36
+ runtime: str = "standalone"):
37
+ """
38
+ Initialize the worker.
39
+
40
+ Args:
41
+ service_name: Name of the service
42
+ service_version: Version of the service
43
+ coordinator_endpoint: Endpoint of the coordinator service
44
+ runtime: Runtime mode - "standalone" or "asgi"
45
+ """
46
+ if not _rust_available:
47
+ raise RuntimeError(f"Rust core is required but not available: {_import_error}")
48
+
49
+ self.service_name = service_name
50
+ self.service_version = service_version
51
+ self.coordinator_endpoint = coordinator_endpoint
52
+ self.runtime_mode = runtime
53
+
54
+ # Create runtime adapter
55
+ if runtime == "asgi":
56
+ self.runtime_adapter = ASGIRuntime(worker=self)
57
+ elif runtime == "standalone":
58
+ self.runtime_adapter = WorkerRuntime()
59
+ else:
60
+ raise ValueError(f"Unknown runtime: {runtime}. Supported: 'standalone', 'asgi'")
61
+
62
+ # Import and create Rust worker
63
+ config = PyWorkerConfig(service_name, service_version, "python")
64
+ self._rust_worker = PyWorker(config)
65
+
66
+ # Note: Telemetry initialization deferred to run() method due to Tokio runtime requirement
67
+
68
+ # Set up OpenTelemetry logging integration (handler is resilient to timing issues)
69
+ try:
70
+ self._otel_handler = install_opentelemetry_logging(
71
+ logger=None, # Install on root logger to capture all Python logs
72
+ level=logging.INFO
73
+ )
74
+ logger.info("OpenTelemetry logging integration enabled")
75
+ except Exception as e:
76
+ logger.warning(f"Failed to initialize OpenTelemetry logging: {e}")
77
+ self._otel_handler = None
78
+
79
+ # Set the message handler - this is the simple FFI boundary
80
+ self._rust_worker.set_message_handler(self._handle_message)
81
+
82
+ self._running = False
83
+
84
+ logger.info(f"Worker created: {service_name} v{service_version} (runtime: {runtime})")
85
+
86
+ async def run(self):
87
+ """
88
+ Run the worker and handle decorated function invocations.
89
+
90
+ This will:
91
+ 1. Register all decorated functions
92
+ 2. Start the underlying Rust worker
93
+ 3. Handle incoming invocations
94
+ """
95
+ logger.info(f"Starting worker {self.service_name}...")
96
+
97
+ # Register all decorated functions first
98
+ self._register_functions()
99
+
100
+ # Run the Rust worker (this will block until shutdown)
101
+ try:
102
+ self._running = True
103
+ await self._rust_worker.run()
104
+ except Exception as e:
105
+ logger.error(f"Worker {self.service_name} failed: {e}")
106
+ raise
107
+ finally:
108
+ self._running = False
109
+ logger.info(f"Worker {self.service_name} stopped")
110
+
111
+ # Clean up OpenTelemetry logging handler
112
+ if hasattr(self, '_otel_handler') and self._otel_handler:
113
+ try:
114
+ from .logging import remove_opentelemetry_logging
115
+ remove_opentelemetry_logging()
116
+ logger.info("OpenTelemetry logging integration cleaned up")
117
+ except Exception as e:
118
+ logger.warning(f"Failed to cleanup OpenTelemetry logging: {e}")
119
+
120
+ def is_running(self) -> bool:
121
+ """Check if the worker is running."""
122
+ return self._running
123
+
124
+ def _register_functions(self):
125
+ """Register all decorated functions with the Worker Coordinator."""
126
+ functions = get_registered_functions()
127
+
128
+ if not functions:
129
+ logger.warning("No @function decorated handlers found")
130
+ return
131
+
132
+ logger.info(f"Registering {len(functions)} function handlers: {list(functions.keys())}")
133
+
134
+ # Build component list for registration
135
+ py_components = []
136
+ for handler_name, func in functions.items():
137
+ metadata = get_function_metadata(func)
138
+ if metadata:
139
+ # Create PyComponentInfo for the Rust worker
140
+ component_metadata = {
141
+ 'handler_name': handler_name,
142
+ 'return_type': metadata.get('return_type', 'any'),
143
+ 'parameters': str(len(metadata.get('parameters', [])))
144
+ }
145
+
146
+ py_component = PyComponentInfo(
147
+ name=handler_name,
148
+ component_type='function',
149
+ metadata=component_metadata
150
+ )
151
+ py_components.append(py_component)
152
+
153
+ # Set components on the Rust worker
154
+ if py_components:
155
+ self._rust_worker.set_components(py_components)
156
+ logger.info(f"Registered {len(py_components)} components with Rust worker")
157
+
158
+ # Function invocations are now handled through the message handler
159
+
160
+ def _handle_message(self, request: 'PyInvokeFunctionRequest') -> 'PyInvokeFunctionResponse':
161
+ """Handle incoming function invocation requests."""
162
+ try:
163
+ # Extract request data
164
+ invocation_id = request.invocation_id
165
+ handler_name = request.component_name
166
+ input_data = bytes(request.input_data)
167
+
168
+ logger.info(f"Processing function invocation - Handler: {handler_name}, ID: {invocation_id}, Data size: {len(input_data)} bytes")
169
+ if request.metadata:
170
+ logger.debug(f"Request metadata: {dict(request.metadata)}")
171
+
172
+ # Log input data preview for debugging
173
+ if input_data:
174
+ try:
175
+ # Try to show a preview of the input data
176
+ if len(input_data) > 100:
177
+ preview = input_data[:100].hex() + "..."
178
+ else:
179
+ preview = input_data.hex()
180
+ logger.debug(f"Input data hex preview: {preview}")
181
+ except Exception:
182
+ logger.debug(f"Input data (raw bytes): {len(input_data)} bytes")
183
+
184
+ # Create context for the function
185
+ context = {
186
+ 'invocation_id': invocation_id,
187
+ 'service_name': request.service_name,
188
+ 'handler_name': handler_name,
189
+ 'metadata': request.metadata
190
+ }
191
+
192
+ # Call the function through the decorator system
193
+ # RuntimeAdapter is used internally by invoke_function if needed
194
+ try:
195
+ result_data = invoke_function(
196
+ handler_name=handler_name,
197
+ input_data=input_data,
198
+ context=context
199
+ )
200
+
201
+ logger.info(f"Function {handler_name} completed successfully")
202
+
203
+ # Return successful response
204
+ return PyInvokeFunctionResponse(
205
+ invocation_id=invocation_id,
206
+ success=True,
207
+ output_data=list(result_data), # Convert bytes to list for PyO3
208
+ error_message=None,
209
+ metadata={}
210
+ )
211
+
212
+ except Exception as e:
213
+ error_msg = f"Function {handler_name} failed: {str(e)}"
214
+ logger.error(error_msg)
215
+
216
+ # Return error response
217
+ return PyInvokeFunctionResponse(
218
+ invocation_id=invocation_id,
219
+ success=False,
220
+ output_data=[],
221
+ error_message=error_msg,
222
+ metadata={}
223
+ )
224
+
225
+ except Exception as e:
226
+ error_msg = f"Message handling failed: {str(e)}"
227
+ logger.error(error_msg)
228
+
229
+ # Return error response with fallback invocation_id
230
+ return PyInvokeFunctionResponse(
231
+ invocation_id=getattr(request, 'invocation_id', 'unknown'),
232
+ success=False,
233
+ output_data=[],
234
+ error_message=error_msg,
235
+ metadata={}
236
+ )
237
+
238
+ async def __call__(self, scope, receive, send):
239
+ """
240
+ ASGI application interface.
241
+
242
+ This makes the Worker itself callable as an ASGI app when using ASGI runtime.
243
+ """
244
+ if self.runtime_mode != "asgi":
245
+ raise RuntimeError("ASGI interface only available when runtime='asgi'")
246
+
247
+ return await self.runtime_adapter(scope, receive, send)
248
+
249
+ def enable_cors(self, origins: List[str] = None):
250
+ """Enable CORS for ASGI runtime."""
251
+ if self.runtime_mode == "asgi" and hasattr(self.runtime_adapter, 'enable_cors'):
252
+ self.runtime_adapter.enable_cors(origins)
253
+ else:
254
+ logger.warning("CORS can only be enabled for ASGI runtime")
255
+
256
+ def disable_cors(self):
257
+ """Disable CORS for ASGI runtime."""
258
+ if self.runtime_mode == "asgi" and hasattr(self.runtime_adapter, 'disable_cors'):
259
+ self.runtime_adapter.disable_cors()
260
+ else:
261
+ logger.warning("CORS can only be disabled for ASGI runtime")
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: agnt5
3
+ Version: 0.1.3
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.9
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Requires-Dist: maturin>=1.9.3
15
+ Summary: AGNT5 Python SDK - Build durable, resilient agent-first applications
16
+ Author-email: AGNT5 Team <team@agnt5.com>
17
+ License: Apache-2.0
18
+ Requires-Python: >=3.9
19
+ Project-URL: Homepage, https://agnt5.dev
20
+ Project-URL: Documentation, https://docs.agnt5.dev
@@ -0,0 +1,15 @@
1
+ agnt5-0.1.3.dist-info/METADATA,sha256=nlya4S-aLN5AkJj6yac8TLo6UIbqkketHy4aHuPj_m4,811
2
+ agnt5-0.1.3.dist-info/WHEEL,sha256=-M5O7l5EczTA8VFaBQsg2Fpg0dKz0WOuvpt3nEh86bo,94
3
+ agnt5/__init__.py,sha256=P7bDXLjr8a8OsNhbfQ74E1NHMxgJPo6jdj-HUKfdd5I,677
4
+ agnt5/_compat.py,sha256=t6IdWQxNsPZfa3kChcaQGIn5ivKfmlVyehY0IDf8rSA,381
5
+ agnt5/_core.pyd,sha256=j6BcJUXOYQ2DY6ZlgLA0rfrYs8Qn8o-ahqBtZTwqxyU,5286400
6
+ agnt5/components.py,sha256=wm8QegtdYrd-Uka7FPV8DtAjQKB7hAjk9VeuNctxpZs,9779
7
+ agnt5/decorators.py,sha256=8eD07dhleMnzTkHMiwe6_m7uB6jGTfFCLKq8qOB2s0U,9031
8
+ agnt5/logging.py,sha256=vl7sbQ6mjM2PREqJ90Hpm_-IdOMLkKyWxmBnNwa6qj8,4799
9
+ agnt5/runtimes/__init__.py,sha256=mdo-72N0wzrzeTIP9JW0q1UEn6OLwDCAQ-9yo6n6Tc0,250
10
+ agnt5/runtimes/asgi.py,sha256=sk_lpJVJIJhOrcbaMXFJsLD7A1cfFg20QU5oUbOe2-I,10183
11
+ agnt5/runtimes/base.py,sha256=T6IdXexl3KCmNK6gNtN1B_LSTetthapP2eSnYdcAUlo,2199
12
+ agnt5/runtimes/worker.py,sha256=CyyoROoQqNeHu12xhkY5qWq_LFKnLQQuQhxQy1YAkx8,2613
13
+ agnt5/version.py,sha256=j2K8ccbO4oNJlSg7yY7l7-ZkvEMjtjjimkCXisZIyo8,725
14
+ agnt5/worker.py,sha256=JwBZprNX2ADu6eWxCV7a-9oHu8iEgX4mIuCwudWuiFk,10716
15
+ agnt5-0.1.3.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.9.4)
3
+ Root-Is-Purelib: false
4
+ Tag: cp39-abi3-win_amd64