agnt5 0.2.2__cp39-abi3-macosx_11_0_arm64.whl → 0.2.4__cp39-abi3-macosx_11_0_arm64.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 +12 -12
- agnt5/_core.abi3.so +0 -0
- agnt5/_retry_utils.py +169 -0
- agnt5/_schema_utils.py +312 -0
- agnt5/_telemetry.py +28 -7
- agnt5/agent.py +153 -140
- agnt5/client.py +50 -12
- agnt5/context.py +36 -756
- agnt5/entity.py +368 -1160
- agnt5/function.py +208 -235
- agnt5/lm.py +71 -12
- agnt5/tool.py +25 -11
- agnt5/tracing.py +196 -0
- agnt5/worker.py +205 -173
- agnt5/workflow.py +444 -20
- {agnt5-0.2.2.dist-info → agnt5-0.2.4.dist-info}/METADATA +2 -1
- agnt5-0.2.4.dist-info/RECORD +22 -0
- agnt5-0.2.2.dist-info/RECORD +0 -19
- {agnt5-0.2.2.dist-info → agnt5-0.2.4.dist-info}/WHEEL +0 -0
agnt5/worker.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import contextvars
|
|
6
7
|
import logging
|
|
7
8
|
from typing import Any, Dict, Optional
|
|
8
9
|
|
|
@@ -12,6 +13,12 @@ from ._telemetry import setup_module_logger
|
|
|
12
13
|
|
|
13
14
|
logger = setup_module_logger(__name__)
|
|
14
15
|
|
|
16
|
+
# Context variable to store trace metadata for propagation to LM calls
|
|
17
|
+
# This allows Rust LM layer to access traceparent without explicit parameter passing
|
|
18
|
+
_trace_metadata: contextvars.ContextVar[Dict[str, str]] = contextvars.ContextVar(
|
|
19
|
+
'_trace_metadata', default={}
|
|
20
|
+
)
|
|
21
|
+
|
|
15
22
|
|
|
16
23
|
class Worker:
|
|
17
24
|
"""AGNT5 Worker for registering and running functions/workflows with the coordinator.
|
|
@@ -88,6 +95,10 @@ class Worker:
|
|
|
88
95
|
# Create Rust worker instance
|
|
89
96
|
self._rust_worker = self._PyWorker(self._rust_config)
|
|
90
97
|
|
|
98
|
+
# Create worker-scoped entity state manager
|
|
99
|
+
from .entity import EntityStateManager
|
|
100
|
+
self._entity_state_manager = EntityStateManager()
|
|
101
|
+
|
|
91
102
|
logger.info(
|
|
92
103
|
f"Worker initialized: {service_name} v{service_version} (runtime: {runtime})"
|
|
93
104
|
)
|
|
@@ -191,7 +202,7 @@ class Worker:
|
|
|
191
202
|
|
|
192
203
|
# Build metadata dict with methods list and schemas
|
|
193
204
|
metadata_dict = {
|
|
194
|
-
"methods": json.dumps(list(entity_type.
|
|
205
|
+
"methods": json.dumps(list(entity_type._method_schemas.keys())),
|
|
195
206
|
"method_schemas": json.dumps(method_schemas)
|
|
196
207
|
}
|
|
197
208
|
|
|
@@ -205,7 +216,7 @@ class Worker:
|
|
|
205
216
|
definition=None,
|
|
206
217
|
)
|
|
207
218
|
components.append(component_info)
|
|
208
|
-
logger.debug(f"Discovered entity: {name} with methods: {list(entity_type.
|
|
219
|
+
logger.debug(f"Discovered entity: {name} with methods: {list(entity_type._method_schemas.keys())}")
|
|
209
220
|
|
|
210
221
|
# Discover agents
|
|
211
222
|
for name, agent in AgentRegistry.all().items():
|
|
@@ -236,68 +247,6 @@ class Worker:
|
|
|
236
247
|
components.append(component_info)
|
|
237
248
|
logger.debug(f"Discovered agent: {name}")
|
|
238
249
|
|
|
239
|
-
# Discover tools
|
|
240
|
-
for name, tool in ToolRegistry.all().items():
|
|
241
|
-
# Serialize schemas to JSON strings
|
|
242
|
-
input_schema_str = None
|
|
243
|
-
if hasattr(tool, 'input_schema') and tool.input_schema:
|
|
244
|
-
input_schema_str = json.dumps(tool.input_schema)
|
|
245
|
-
|
|
246
|
-
output_schema_str = None
|
|
247
|
-
if hasattr(tool, 'output_schema') and tool.output_schema:
|
|
248
|
-
output_schema_str = json.dumps(tool.output_schema)
|
|
249
|
-
|
|
250
|
-
component_info = self._PyComponentInfo(
|
|
251
|
-
name=name,
|
|
252
|
-
component_type="tool",
|
|
253
|
-
metadata={},
|
|
254
|
-
config={},
|
|
255
|
-
input_schema=input_schema_str,
|
|
256
|
-
output_schema=output_schema_str,
|
|
257
|
-
definition=None,
|
|
258
|
-
)
|
|
259
|
-
components.append(component_info)
|
|
260
|
-
logger.debug(f"Discovered tool: {name}")
|
|
261
|
-
|
|
262
|
-
# Discover entities
|
|
263
|
-
for name, entity_type in EntityRegistry.all().items():
|
|
264
|
-
# Build metadata dict with methods list as JSON string
|
|
265
|
-
metadata_dict = {
|
|
266
|
-
"methods": json.dumps(list(entity_type._methods.keys()))
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
component_info = self._PyComponentInfo(
|
|
270
|
-
name=name,
|
|
271
|
-
component_type="entity",
|
|
272
|
-
metadata=metadata_dict,
|
|
273
|
-
config={},
|
|
274
|
-
input_schema=None,
|
|
275
|
-
output_schema=None,
|
|
276
|
-
definition=None,
|
|
277
|
-
)
|
|
278
|
-
components.append(component_info)
|
|
279
|
-
logger.debug(f"Discovered entity: {name} with methods: {list(entity_type._methods.keys())}")
|
|
280
|
-
|
|
281
|
-
# Discover agents
|
|
282
|
-
for name, agent in AgentRegistry.all().items():
|
|
283
|
-
# Build metadata dict with agent info
|
|
284
|
-
metadata_dict = {
|
|
285
|
-
"model": agent.model_name,
|
|
286
|
-
"tools": json.dumps(list(agent.tools.keys()) if hasattr(agent, 'tools') else [])
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
component_info = self._PyComponentInfo(
|
|
290
|
-
name=name,
|
|
291
|
-
component_type="agent",
|
|
292
|
-
metadata=metadata_dict,
|
|
293
|
-
config={},
|
|
294
|
-
input_schema=None,
|
|
295
|
-
output_schema=None,
|
|
296
|
-
definition=None,
|
|
297
|
-
)
|
|
298
|
-
components.append(component_info)
|
|
299
|
-
logger.debug(f"Discovered agent: {name}")
|
|
300
|
-
|
|
301
250
|
logger.info(f"Discovered {len(components)} components")
|
|
302
251
|
return components
|
|
303
252
|
|
|
@@ -305,67 +254,66 @@ class Worker:
|
|
|
305
254
|
"""Create the message handler that will be called by Rust worker."""
|
|
306
255
|
|
|
307
256
|
def handle_message(request):
|
|
308
|
-
"""Handle incoming execution requests."""
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
)
|
|
257
|
+
"""Handle incoming execution requests - returns coroutine for Rust to await."""
|
|
258
|
+
# Extract request details
|
|
259
|
+
component_name = request.component_name
|
|
260
|
+
component_type = request.component_type
|
|
261
|
+
input_data = request.input_data
|
|
262
|
+
|
|
263
|
+
logger.debug(
|
|
264
|
+
f"Handling {component_type} request: {component_name}, input size: {len(input_data)} bytes"
|
|
265
|
+
)
|
|
318
266
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
267
|
+
# Import all registries
|
|
268
|
+
from .tool import ToolRegistry
|
|
269
|
+
from .entity import EntityRegistry
|
|
270
|
+
from .agent import AgentRegistry
|
|
271
|
+
|
|
272
|
+
# Route based on component type and return coroutines
|
|
273
|
+
if component_type == "tool":
|
|
274
|
+
tool = ToolRegistry.get(component_name)
|
|
275
|
+
if tool:
|
|
276
|
+
logger.debug(f"Found tool: {component_name}")
|
|
277
|
+
# Return coroutine, don't await it
|
|
278
|
+
return self._execute_tool(tool, input_data, request)
|
|
279
|
+
|
|
280
|
+
elif component_type == "entity":
|
|
281
|
+
entity_type = EntityRegistry.get(component_name)
|
|
282
|
+
if entity_type:
|
|
283
|
+
logger.debug(f"Found entity: {component_name}")
|
|
284
|
+
# Return coroutine, don't await it
|
|
285
|
+
return self._execute_entity(entity_type, input_data, request)
|
|
286
|
+
|
|
287
|
+
elif component_type == "agent":
|
|
288
|
+
agent = AgentRegistry.get(component_name)
|
|
289
|
+
if agent:
|
|
290
|
+
logger.debug(f"Found agent: {component_name}")
|
|
291
|
+
# Return coroutine, don't await it
|
|
292
|
+
return self._execute_agent(agent, input_data, request)
|
|
293
|
+
|
|
294
|
+
elif component_type == "workflow":
|
|
295
|
+
workflow_config = WorkflowRegistry.get(component_name)
|
|
296
|
+
if workflow_config:
|
|
297
|
+
logger.debug(f"Found workflow: {component_name}")
|
|
298
|
+
# Return coroutine, don't await it
|
|
299
|
+
return self._execute_workflow(workflow_config, input_data, request)
|
|
300
|
+
|
|
301
|
+
elif component_type == "function":
|
|
302
|
+
function_config = FunctionRegistry.get(component_name)
|
|
303
|
+
if function_config:
|
|
304
|
+
logger.info(f"🔥 WORKER: Received request for function: {component_name}")
|
|
305
|
+
# Return coroutine, don't await it
|
|
306
|
+
return self._execute_function(function_config, input_data, request)
|
|
307
|
+
|
|
308
|
+
# Not found - need to return an async error response
|
|
309
|
+
error_msg = f"Component '{component_name}' of type '{component_type}' not found"
|
|
310
|
+
logger.error(error_msg)
|
|
311
|
+
|
|
312
|
+
# Create async wrapper for error response
|
|
313
|
+
async def error_response():
|
|
363
314
|
return self._create_error_response(request, error_msg)
|
|
364
315
|
|
|
365
|
-
|
|
366
|
-
error_msg = f"Handler error: {e}"
|
|
367
|
-
logger.error(error_msg, exc_info=True)
|
|
368
|
-
return self._create_error_response(request, error_msg)
|
|
316
|
+
return error_response()
|
|
369
317
|
|
|
370
318
|
return handle_message
|
|
371
319
|
|
|
@@ -382,20 +330,45 @@ class Worker:
|
|
|
382
330
|
# Parse input data
|
|
383
331
|
input_dict = json.loads(input_data.decode("utf-8")) if input_data else {}
|
|
384
332
|
|
|
385
|
-
#
|
|
333
|
+
# Store trace metadata in contextvar for LM calls to access
|
|
334
|
+
# The Rust worker injects traceparent into request.metadata for trace propagation
|
|
335
|
+
if hasattr(request, 'metadata') and request.metadata:
|
|
336
|
+
_trace_metadata.set(dict(request.metadata))
|
|
337
|
+
logger.debug(f"Trace metadata stored: traceparent={request.metadata.get('traceparent', 'N/A')}")
|
|
338
|
+
|
|
339
|
+
# Create context with runtime_context for trace correlation
|
|
386
340
|
ctx = Context(
|
|
387
341
|
run_id=f"{self.service_name}:{config.name}",
|
|
388
|
-
|
|
342
|
+
runtime_context=request.runtime_context,
|
|
389
343
|
)
|
|
390
344
|
|
|
391
|
-
#
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
345
|
+
# Create span for function execution with trace linking
|
|
346
|
+
from ._core import create_span, flush_telemetry_py
|
|
347
|
+
|
|
348
|
+
with create_span(
|
|
349
|
+
config.name,
|
|
350
|
+
"function",
|
|
351
|
+
request.runtime_context,
|
|
352
|
+
{
|
|
353
|
+
"function.name": config.name,
|
|
354
|
+
"service.name": self.service_name,
|
|
355
|
+
},
|
|
356
|
+
) as span:
|
|
357
|
+
# Execute function
|
|
358
|
+
if input_dict:
|
|
359
|
+
result = config.handler(ctx, **input_dict)
|
|
360
|
+
else:
|
|
361
|
+
result = config.handler(ctx)
|
|
362
|
+
|
|
363
|
+
# Debug: Log what type result is
|
|
364
|
+
logger.info(f"🔥 WORKER: Function result type: {type(result).__name__}, isasyncgen: {inspect.isasyncgen(result)}, iscoroutine: {inspect.iscoroutine(result)}")
|
|
365
|
+
|
|
366
|
+
# Flush telemetry after span ends to ensure it's exported
|
|
367
|
+
try:
|
|
368
|
+
flush_telemetry_py()
|
|
369
|
+
logger.debug("Telemetry flushed after function execution")
|
|
370
|
+
except Exception as e:
|
|
371
|
+
logger.warning(f"Failed to flush telemetry: {e}")
|
|
399
372
|
|
|
400
373
|
# Check if result is an async generator (streaming function)
|
|
401
374
|
if inspect.isasyncgen(result):
|
|
@@ -473,16 +446,17 @@ class Worker:
|
|
|
473
446
|
)
|
|
474
447
|
|
|
475
448
|
async def _execute_workflow(self, config, input_data: bytes, request):
|
|
476
|
-
"""Execute a workflow handler with replay support
|
|
449
|
+
"""Execute a workflow handler with automatic replay support."""
|
|
477
450
|
import json
|
|
478
|
-
from .
|
|
451
|
+
from .workflow import WorkflowEntity, WorkflowContext
|
|
452
|
+
from .entity import _get_state_manager
|
|
479
453
|
from ._core import PyExecuteComponentResponse
|
|
480
454
|
|
|
481
455
|
try:
|
|
482
456
|
# Parse input data
|
|
483
457
|
input_dict = json.loads(input_data.decode("utf-8")) if input_data else {}
|
|
484
458
|
|
|
485
|
-
#
|
|
459
|
+
# Parse replay data from request metadata for crash recovery
|
|
486
460
|
completed_steps = {}
|
|
487
461
|
initial_state = {}
|
|
488
462
|
|
|
@@ -507,38 +481,74 @@ class Worker:
|
|
|
507
481
|
except json.JSONDecodeError:
|
|
508
482
|
logger.warning("Failed to parse workflow_state from metadata")
|
|
509
483
|
|
|
510
|
-
# Create
|
|
511
|
-
|
|
484
|
+
# Create WorkflowEntity for state management
|
|
485
|
+
workflow_entity = WorkflowEntity(run_id=f"{self.service_name}:{config.name}")
|
|
486
|
+
|
|
487
|
+
# Load replay data into entity if provided
|
|
488
|
+
if completed_steps:
|
|
489
|
+
workflow_entity._completed_steps = completed_steps
|
|
490
|
+
logger.debug(f"Loaded {len(completed_steps)} completed steps into workflow entity")
|
|
491
|
+
|
|
492
|
+
if initial_state:
|
|
493
|
+
# Load initial state into entity's state manager
|
|
494
|
+
state_manager = _get_state_manager()
|
|
495
|
+
state_manager._states[workflow_entity._state_key] = initial_state
|
|
496
|
+
logger.debug(f"Loaded initial state with {len(initial_state)} keys into workflow entity")
|
|
497
|
+
|
|
498
|
+
# Create WorkflowContext with entity and runtime_context for trace correlation
|
|
499
|
+
ctx = WorkflowContext(
|
|
500
|
+
workflow_entity=workflow_entity,
|
|
512
501
|
run_id=f"{self.service_name}:{config.name}",
|
|
513
|
-
|
|
514
|
-
completed_steps=completed_steps if completed_steps else None,
|
|
515
|
-
initial_state=initial_state if initial_state else None,
|
|
502
|
+
runtime_context=request.runtime_context,
|
|
516
503
|
)
|
|
517
504
|
|
|
518
|
-
#
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
505
|
+
# Create span for workflow execution with trace linking
|
|
506
|
+
from ._core import create_span, flush_telemetry_py
|
|
507
|
+
|
|
508
|
+
with create_span(
|
|
509
|
+
config.name,
|
|
510
|
+
"workflow",
|
|
511
|
+
request.runtime_context,
|
|
512
|
+
{
|
|
513
|
+
"workflow.name": config.name,
|
|
514
|
+
"service.name": self.service_name,
|
|
515
|
+
},
|
|
516
|
+
) as span:
|
|
517
|
+
# Execute workflow
|
|
518
|
+
if input_dict:
|
|
519
|
+
result = await config.handler(ctx, **input_dict)
|
|
520
|
+
else:
|
|
521
|
+
result = await config.handler(ctx)
|
|
522
|
+
|
|
523
|
+
# Flush telemetry after span ends to ensure it's exported
|
|
524
|
+
try:
|
|
525
|
+
flush_telemetry_py()
|
|
526
|
+
logger.debug("Telemetry flushed after workflow execution")
|
|
527
|
+
except Exception as e:
|
|
528
|
+
logger.warning(f"Failed to flush telemetry: {e}")
|
|
523
529
|
|
|
524
530
|
# Serialize result
|
|
525
531
|
output_data = json.dumps(result).encode("utf-8")
|
|
526
532
|
|
|
527
|
-
#
|
|
533
|
+
# Collect workflow execution metadata for durability
|
|
528
534
|
metadata = {}
|
|
529
535
|
|
|
530
536
|
# Add step events to metadata (for workflow durability)
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
537
|
+
# Access _step_events from the workflow entity, not the context
|
|
538
|
+
step_events = ctx._workflow_entity._step_events
|
|
539
|
+
if step_events:
|
|
540
|
+
metadata["step_events"] = json.dumps(step_events)
|
|
541
|
+
logger.debug(f"Workflow has {len(step_events)} recorded steps")
|
|
534
542
|
|
|
535
543
|
# Add final state snapshot to metadata (if state was used)
|
|
536
|
-
if
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
544
|
+
# Check if _state was initialized without triggering property getter
|
|
545
|
+
if hasattr(ctx, '_workflow_entity') and ctx._workflow_entity._state is not None:
|
|
546
|
+
if ctx._workflow_entity._state.has_changes():
|
|
547
|
+
state_snapshot = ctx._workflow_entity._state.get_state_snapshot()
|
|
548
|
+
metadata["workflow_state"] = json.dumps(state_snapshot)
|
|
549
|
+
logger.debug(f"Workflow state snapshot: {state_snapshot}")
|
|
540
550
|
|
|
541
|
-
logger.info(f"Workflow completed successfully with {len(
|
|
551
|
+
logger.info(f"Workflow completed successfully with {len(step_events)} steps")
|
|
542
552
|
|
|
543
553
|
return PyExecuteComponentResponse(
|
|
544
554
|
invocation_id=request.invocation_id,
|
|
@@ -578,10 +588,10 @@ class Worker:
|
|
|
578
588
|
# Parse input data
|
|
579
589
|
input_dict = json.loads(input_data.decode("utf-8")) if input_data else {}
|
|
580
590
|
|
|
581
|
-
# Create context
|
|
591
|
+
# Create context with runtime_context for trace correlation
|
|
582
592
|
ctx = Context(
|
|
583
593
|
run_id=f"{self.service_name}:{tool.name}",
|
|
584
|
-
|
|
594
|
+
runtime_context=request.runtime_context,
|
|
585
595
|
)
|
|
586
596
|
|
|
587
597
|
# Execute tool
|
|
@@ -622,9 +632,12 @@ class Worker:
|
|
|
622
632
|
"""Execute an entity method."""
|
|
623
633
|
import json
|
|
624
634
|
from .context import Context
|
|
625
|
-
from .entity import EntityType,
|
|
635
|
+
from .entity import EntityType, Entity, _entity_state_manager_ctx
|
|
626
636
|
from ._core import PyExecuteComponentResponse
|
|
627
637
|
|
|
638
|
+
# Set entity state manager in context for Entity instances to access
|
|
639
|
+
_entity_state_manager_ctx.set(self._entity_state_manager)
|
|
640
|
+
|
|
628
641
|
try:
|
|
629
642
|
# Parse input data
|
|
630
643
|
input_dict = json.loads(input_data.decode("utf-8")) if input_data else {}
|
|
@@ -638,13 +651,26 @@ class Worker:
|
|
|
638
651
|
if not method_name:
|
|
639
652
|
raise ValueError("Entity invocation requires 'method' parameter")
|
|
640
653
|
|
|
641
|
-
#
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
654
|
+
# Load state from platform if provided in request metadata
|
|
655
|
+
state_key = (entity_type.name, entity_key)
|
|
656
|
+
if hasattr(request, 'metadata') and request.metadata:
|
|
657
|
+
if "entity_state" in request.metadata:
|
|
658
|
+
platform_state_json = request.metadata["entity_state"]
|
|
659
|
+
platform_version = int(request.metadata.get("state_version", "0"))
|
|
660
|
+
|
|
661
|
+
# Load platform state into state manager
|
|
662
|
+
self._entity_state_manager.load_state_from_platform(
|
|
663
|
+
state_key,
|
|
664
|
+
platform_state_json,
|
|
665
|
+
platform_version
|
|
666
|
+
)
|
|
667
|
+
logger.info(
|
|
668
|
+
f"Loaded entity state from platform: {entity_type.name}/{entity_key} "
|
|
669
|
+
f"(version {platform_version})"
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
# Create entity instance using the stored class reference
|
|
673
|
+
entity_instance = entity_type.entity_class(key=entity_key)
|
|
648
674
|
|
|
649
675
|
# Get method
|
|
650
676
|
if not hasattr(entity_instance, method_name):
|
|
@@ -658,26 +684,32 @@ class Worker:
|
|
|
658
684
|
# Serialize result
|
|
659
685
|
output_data = json.dumps(result).encode("utf-8")
|
|
660
686
|
|
|
661
|
-
#
|
|
662
|
-
|
|
687
|
+
# Capture entity state after execution with version tracking
|
|
688
|
+
state_dict, expected_version, new_version = \
|
|
689
|
+
self._entity_state_manager.get_state_for_persistence(state_key)
|
|
690
|
+
|
|
663
691
|
metadata = {}
|
|
664
|
-
if
|
|
665
|
-
entity_state = _entity_states[state_key]
|
|
692
|
+
if state_dict:
|
|
666
693
|
# Serialize state as JSON string for platform persistence
|
|
667
|
-
state_json = json.dumps(
|
|
694
|
+
state_json = json.dumps(state_dict)
|
|
668
695
|
# Pass in metadata for Worker Coordinator to publish
|
|
669
696
|
metadata = {
|
|
670
697
|
"entity_state": state_json,
|
|
671
698
|
"entity_type": entity_type.name,
|
|
672
699
|
"entity_key": entity_key,
|
|
700
|
+
"expected_version": str(expected_version),
|
|
701
|
+
"new_version": str(new_version),
|
|
673
702
|
}
|
|
674
|
-
logger.
|
|
703
|
+
logger.info(
|
|
704
|
+
f"Captured entity state: {entity_type.name}/{entity_key} "
|
|
705
|
+
f"(version {expected_version} → {new_version})"
|
|
706
|
+
)
|
|
675
707
|
|
|
676
708
|
return PyExecuteComponentResponse(
|
|
677
709
|
invocation_id=request.invocation_id,
|
|
678
710
|
success=True,
|
|
679
711
|
output_data=output_data,
|
|
680
|
-
state_update=None, # TODO:
|
|
712
|
+
state_update=None, # TODO: Use structured StateUpdate object
|
|
681
713
|
error_message=None,
|
|
682
714
|
metadata=metadata, # Include state in metadata for Worker Coordinator
|
|
683
715
|
is_chunk=False,
|
|
@@ -716,10 +748,10 @@ class Worker:
|
|
|
716
748
|
if not user_message:
|
|
717
749
|
raise ValueError("Agent invocation requires 'message' parameter")
|
|
718
750
|
|
|
719
|
-
# Create context
|
|
751
|
+
# Create context with runtime_context for trace correlation
|
|
720
752
|
ctx = Context(
|
|
721
753
|
run_id=f"{self.service_name}:{agent.name}",
|
|
722
|
-
|
|
754
|
+
runtime_context=request.runtime_context,
|
|
723
755
|
)
|
|
724
756
|
|
|
725
757
|
# Execute agent
|