agno 2.2.8__py3-none-any.whl → 2.2.10__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.
- agno/agent/agent.py +37 -19
- agno/db/base.py +23 -0
- agno/db/dynamo/dynamo.py +20 -25
- agno/db/dynamo/schemas.py +1 -0
- agno/db/firestore/firestore.py +11 -0
- agno/db/gcs_json/gcs_json_db.py +4 -0
- agno/db/in_memory/in_memory_db.py +4 -0
- agno/db/json/json_db.py +4 -0
- agno/db/mongo/async_mongo.py +27 -0
- agno/db/mongo/mongo.py +25 -0
- agno/db/mysql/mysql.py +26 -1
- agno/db/postgres/async_postgres.py +26 -1
- agno/db/postgres/postgres.py +26 -1
- agno/db/redis/redis.py +4 -0
- agno/db/singlestore/singlestore.py +24 -0
- agno/db/sqlite/async_sqlite.py +25 -1
- agno/db/sqlite/sqlite.py +25 -1
- agno/db/surrealdb/surrealdb.py +13 -1
- agno/knowledge/reader/docx_reader.py +0 -1
- agno/models/azure/ai_foundry.py +2 -1
- agno/models/cerebras/cerebras.py +3 -2
- agno/models/openai/chat.py +2 -1
- agno/models/openai/responses.py +2 -1
- agno/os/app.py +127 -65
- agno/os/config.py +1 -0
- agno/os/interfaces/agui/router.py +9 -0
- agno/os/interfaces/agui/utils.py +49 -3
- agno/os/mcp.py +8 -8
- agno/os/router.py +27 -9
- agno/os/routers/evals/evals.py +12 -7
- agno/os/routers/memory/memory.py +18 -10
- agno/os/routers/metrics/metrics.py +6 -4
- agno/os/routers/session/session.py +21 -11
- agno/os/utils.py +57 -11
- agno/team/team.py +33 -23
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/redis/__init__.py +4 -0
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +26 -4
- agno/workflow/loop.py +9 -0
- agno/workflow/parallel.py +39 -16
- agno/workflow/router.py +25 -4
- agno/workflow/step.py +162 -91
- agno/workflow/steps.py +9 -0
- agno/workflow/workflow.py +26 -22
- {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/METADATA +11 -13
- {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/RECORD +50 -50
- {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/WHEEL +0 -0
- {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/top_level.txt +0 -0
agno/workflow/router.py
CHANGED
|
@@ -4,6 +4,7 @@ from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List
|
|
|
4
4
|
from uuid import uuid4
|
|
5
5
|
|
|
6
6
|
from agno.run.agent import RunOutputEvent
|
|
7
|
+
from agno.run.base import RunContext
|
|
7
8
|
from agno.run.team import TeamRunOutputEvent
|
|
8
9
|
from agno.run.workflow import (
|
|
9
10
|
RouterExecutionCompletedEvent,
|
|
@@ -171,6 +172,7 @@ class Router:
|
|
|
171
172
|
session_id: Optional[str] = None,
|
|
172
173
|
user_id: Optional[str] = None,
|
|
173
174
|
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
175
|
+
run_context: Optional[RunContext] = None,
|
|
174
176
|
session_state: Optional[Dict[str, Any]] = None,
|
|
175
177
|
store_executor_outputs: bool = True,
|
|
176
178
|
workflow_session: Optional[WorkflowSession] = None,
|
|
@@ -185,7 +187,10 @@ class Router:
|
|
|
185
187
|
self._prepare_steps()
|
|
186
188
|
|
|
187
189
|
# Route to appropriate steps
|
|
188
|
-
|
|
190
|
+
if run_context is not None and run_context.session_state is not None:
|
|
191
|
+
steps_to_execute = self._route_steps(step_input, session_state=run_context.session_state)
|
|
192
|
+
else:
|
|
193
|
+
steps_to_execute = self._route_steps(step_input, session_state=session_state)
|
|
189
194
|
log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
|
|
190
195
|
|
|
191
196
|
if not steps_to_execute:
|
|
@@ -209,6 +214,7 @@ class Router:
|
|
|
209
214
|
user_id=user_id,
|
|
210
215
|
workflow_run_response=workflow_run_response,
|
|
211
216
|
store_executor_outputs=store_executor_outputs,
|
|
217
|
+
run_context=run_context,
|
|
212
218
|
session_state=session_state,
|
|
213
219
|
workflow_session=workflow_session,
|
|
214
220
|
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
@@ -266,6 +272,7 @@ class Router:
|
|
|
266
272
|
step_input: StepInput,
|
|
267
273
|
session_id: Optional[str] = None,
|
|
268
274
|
user_id: Optional[str] = None,
|
|
275
|
+
run_context: Optional[RunContext] = None,
|
|
269
276
|
session_state: Optional[Dict[str, Any]] = None,
|
|
270
277
|
stream_events: bool = False,
|
|
271
278
|
stream_intermediate_steps: bool = False,
|
|
@@ -286,7 +293,10 @@ class Router:
|
|
|
286
293
|
router_step_id = str(uuid4())
|
|
287
294
|
|
|
288
295
|
# Route to appropriate steps
|
|
289
|
-
|
|
296
|
+
if run_context is not None and run_context.session_state is not None:
|
|
297
|
+
steps_to_execute = self._route_steps(step_input, session_state=run_context.session_state)
|
|
298
|
+
else:
|
|
299
|
+
steps_to_execute = self._route_steps(step_input, session_state=session_state)
|
|
290
300
|
log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
|
|
291
301
|
|
|
292
302
|
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
@@ -341,6 +351,7 @@ class Router:
|
|
|
341
351
|
workflow_run_response=workflow_run_response,
|
|
342
352
|
step_index=step_index,
|
|
343
353
|
store_executor_outputs=store_executor_outputs,
|
|
354
|
+
run_context=run_context,
|
|
344
355
|
session_state=session_state,
|
|
345
356
|
parent_step_id=router_step_id,
|
|
346
357
|
workflow_session=workflow_session,
|
|
@@ -425,6 +436,7 @@ class Router:
|
|
|
425
436
|
session_id: Optional[str] = None,
|
|
426
437
|
user_id: Optional[str] = None,
|
|
427
438
|
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
439
|
+
run_context: Optional[RunContext] = None,
|
|
428
440
|
session_state: Optional[Dict[str, Any]] = None,
|
|
429
441
|
store_executor_outputs: bool = True,
|
|
430
442
|
workflow_session: Optional[WorkflowSession] = None,
|
|
@@ -439,7 +451,10 @@ class Router:
|
|
|
439
451
|
self._prepare_steps()
|
|
440
452
|
|
|
441
453
|
# Route to appropriate steps
|
|
442
|
-
|
|
454
|
+
if run_context is not None and run_context.session_state is not None:
|
|
455
|
+
steps_to_execute = await self._aroute_steps(step_input, session_state=run_context.session_state)
|
|
456
|
+
else:
|
|
457
|
+
steps_to_execute = await self._aroute_steps(step_input, session_state=session_state)
|
|
443
458
|
log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
|
|
444
459
|
|
|
445
460
|
if not steps_to_execute:
|
|
@@ -464,6 +479,7 @@ class Router:
|
|
|
464
479
|
user_id=user_id,
|
|
465
480
|
workflow_run_response=workflow_run_response,
|
|
466
481
|
store_executor_outputs=store_executor_outputs,
|
|
482
|
+
run_context=run_context,
|
|
467
483
|
session_state=session_state,
|
|
468
484
|
workflow_session=workflow_session,
|
|
469
485
|
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
@@ -523,6 +539,7 @@ class Router:
|
|
|
523
539
|
step_input: StepInput,
|
|
524
540
|
session_id: Optional[str] = None,
|
|
525
541
|
user_id: Optional[str] = None,
|
|
542
|
+
run_context: Optional[RunContext] = None,
|
|
526
543
|
session_state: Optional[Dict[str, Any]] = None,
|
|
527
544
|
stream_events: bool = False,
|
|
528
545
|
stream_intermediate_steps: bool = False,
|
|
@@ -543,7 +560,10 @@ class Router:
|
|
|
543
560
|
router_step_id = str(uuid4())
|
|
544
561
|
|
|
545
562
|
# Route to appropriate steps
|
|
546
|
-
|
|
563
|
+
if run_context is not None and run_context.session_state is not None:
|
|
564
|
+
steps_to_execute = await self._aroute_steps(step_input, session_state=run_context.session_state)
|
|
565
|
+
else:
|
|
566
|
+
steps_to_execute = await self._aroute_steps(step_input, session_state=session_state)
|
|
547
567
|
log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
|
|
548
568
|
|
|
549
569
|
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
@@ -600,6 +620,7 @@ class Router:
|
|
|
600
620
|
workflow_run_response=workflow_run_response,
|
|
601
621
|
step_index=step_index,
|
|
602
622
|
store_executor_outputs=store_executor_outputs,
|
|
623
|
+
run_context=run_context,
|
|
603
624
|
session_state=session_state,
|
|
604
625
|
parent_step_id=router_step_id,
|
|
605
626
|
workflow_session=workflow_session,
|
agno/workflow/step.py
CHANGED
|
@@ -11,7 +11,9 @@ from agno.agent import Agent
|
|
|
11
11
|
from agno.media import Audio, Image, Video
|
|
12
12
|
from agno.models.metrics import Metrics
|
|
13
13
|
from agno.run import RunContext
|
|
14
|
-
from agno.run.agent import RunOutput
|
|
14
|
+
from agno.run.agent import RunCompletedEvent, RunOutput, RunContentEvent
|
|
15
|
+
from agno.run.base import BaseRunOutputEvent
|
|
16
|
+
from agno.run.team import RunCompletedEvent as TeamRunCompletedEvent, RunContentEvent as TeamRunContentEvent
|
|
15
17
|
from agno.run.team import TeamRunOutput
|
|
16
18
|
from agno.run.workflow import (
|
|
17
19
|
StepCompletedEvent,
|
|
@@ -179,31 +181,37 @@ class Step:
|
|
|
179
181
|
func: Callable,
|
|
180
182
|
step_input: StepInput,
|
|
181
183
|
session_state: Optional[Dict[str, Any]] = None,
|
|
184
|
+
run_context: Optional[RunContext] = None,
|
|
182
185
|
) -> Any:
|
|
183
186
|
"""Call custom function with session_state support if the function accepts it"""
|
|
187
|
+
|
|
188
|
+
kwargs: Dict[str, Any] = {}
|
|
189
|
+
if run_context is not None and self._function_has_run_context_param():
|
|
190
|
+
kwargs["run_context"] = run_context
|
|
184
191
|
if session_state is not None and self._function_has_session_state_param():
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
kwargs["session_state"] = session_state
|
|
193
|
+
|
|
194
|
+
return func(step_input, **kwargs)
|
|
188
195
|
|
|
189
196
|
async def _acall_custom_function(
|
|
190
197
|
self,
|
|
191
198
|
func: Callable,
|
|
192
199
|
step_input: StepInput,
|
|
193
200
|
session_state: Optional[Dict[str, Any]] = None,
|
|
201
|
+
run_context: Optional[RunContext] = None,
|
|
194
202
|
) -> Any:
|
|
195
203
|
"""Call custom async function with session_state support if the function accepts it"""
|
|
196
204
|
|
|
205
|
+
kwargs: Dict[str, Any] = {}
|
|
206
|
+
if run_context is not None and self._function_has_run_context_param():
|
|
207
|
+
kwargs["run_context"] = run_context
|
|
208
|
+
if session_state is not None and self._function_has_session_state_param():
|
|
209
|
+
kwargs["session_state"] = session_state
|
|
210
|
+
|
|
197
211
|
if _is_async_generator_function(func):
|
|
198
|
-
|
|
199
|
-
return func(step_input, session_state)
|
|
200
|
-
else:
|
|
201
|
-
return func(step_input)
|
|
212
|
+
return func(step_input, **kwargs)
|
|
202
213
|
else:
|
|
203
|
-
|
|
204
|
-
return await func(step_input, session_state)
|
|
205
|
-
else:
|
|
206
|
-
return await func(step_input)
|
|
214
|
+
return await func(step_input, **kwargs)
|
|
207
215
|
|
|
208
216
|
def execute(
|
|
209
217
|
self,
|
|
@@ -227,8 +235,10 @@ class Step:
|
|
|
227
235
|
if workflow_session:
|
|
228
236
|
step_input.workflow_session = workflow_session
|
|
229
237
|
|
|
238
|
+
# Create session_state copy once to avoid duplication.
|
|
239
|
+
# Consider both run_context.session_state and session_state.
|
|
230
240
|
if run_context is not None and run_context.session_state is not None:
|
|
231
|
-
session_state_copy =
|
|
241
|
+
session_state_copy = run_context.session_state
|
|
232
242
|
else:
|
|
233
243
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
234
244
|
|
|
@@ -247,13 +257,13 @@ class Step:
|
|
|
247
257
|
self.active_executor,
|
|
248
258
|
step_input,
|
|
249
259
|
session_state_copy, # type: ignore[arg-type]
|
|
260
|
+
run_context,
|
|
250
261
|
): # type: ignore
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
content += chunk.content
|
|
262
|
+
if isinstance(chunk, (BaseRunOutputEvent)):
|
|
263
|
+
if isinstance(chunk, (RunContentEvent, TeamRunContentEvent)):
|
|
264
|
+
content += chunk.content if chunk.content is not None else ""
|
|
265
|
+
elif isinstance(chunk, (RunCompletedEvent, TeamRunCompletedEvent)):
|
|
266
|
+
content = chunk.content if chunk.content is not None else ""
|
|
257
267
|
else:
|
|
258
268
|
content += str(chunk)
|
|
259
269
|
if isinstance(chunk, StepOutput):
|
|
@@ -264,7 +274,7 @@ class Step:
|
|
|
264
274
|
final_response = e.value
|
|
265
275
|
|
|
266
276
|
# Merge session_state changes back
|
|
267
|
-
if session_state is not None:
|
|
277
|
+
if run_context is None and session_state is not None:
|
|
268
278
|
merge_dictionaries(session_state, session_state_copy)
|
|
269
279
|
|
|
270
280
|
if final_response is not None:
|
|
@@ -273,10 +283,15 @@ class Step:
|
|
|
273
283
|
response = StepOutput(content=content)
|
|
274
284
|
else:
|
|
275
285
|
# Execute function with signature inspection for session_state support
|
|
276
|
-
result = self._call_custom_function(
|
|
286
|
+
result = self._call_custom_function(
|
|
287
|
+
self.active_executor, # type: ignore[arg-type]
|
|
288
|
+
step_input,
|
|
289
|
+
session_state_copy,
|
|
290
|
+
run_context,
|
|
291
|
+
)
|
|
277
292
|
|
|
278
293
|
# Merge session_state changes back
|
|
279
|
-
if session_state is not None:
|
|
294
|
+
if run_context is None and session_state is not None:
|
|
280
295
|
merge_dictionaries(session_state, session_state_copy)
|
|
281
296
|
|
|
282
297
|
# If function returns StepOutput, use it directly
|
|
@@ -334,12 +349,13 @@ class Step:
|
|
|
334
349
|
session_id=session_id,
|
|
335
350
|
user_id=user_id,
|
|
336
351
|
session_state=session_state_copy, # Send a copy to the executor
|
|
352
|
+
run_context=run_context,
|
|
337
353
|
**kwargs,
|
|
338
354
|
)
|
|
339
355
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
merge_dictionaries(session_state, session_state_copy)
|
|
356
|
+
# Update workflow session state
|
|
357
|
+
if run_context is None and session_state is not None:
|
|
358
|
+
merge_dictionaries(session_state, session_state_copy)
|
|
343
359
|
|
|
344
360
|
if store_executor_outputs and workflow_run_response is not None:
|
|
345
361
|
self._store_executor_response(workflow_run_response, response) # type: ignore
|
|
@@ -368,6 +384,17 @@ class Step:
|
|
|
368
384
|
|
|
369
385
|
return StepOutput(content=f"Step {self.name} failed but skipped", success=False)
|
|
370
386
|
|
|
387
|
+
def _function_has_run_context_param(self) -> bool:
|
|
388
|
+
"""Check if the custom function has a run_context parameter"""
|
|
389
|
+
if self._executor_type != "function":
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
try:
|
|
393
|
+
sig = inspect.signature(self.active_executor) # type: ignore
|
|
394
|
+
return "run_context" in sig.parameters
|
|
395
|
+
except Exception:
|
|
396
|
+
return False
|
|
397
|
+
|
|
371
398
|
def _function_has_session_state_param(self) -> bool:
|
|
372
399
|
"""Check if the custom function has a session_state parameter"""
|
|
373
400
|
if self._executor_type != "function":
|
|
@@ -430,9 +457,10 @@ class Step:
|
|
|
430
457
|
if workflow_session:
|
|
431
458
|
step_input.workflow_session = workflow_session
|
|
432
459
|
|
|
433
|
-
# Create session_state copy once to avoid duplication
|
|
460
|
+
# Create session_state copy once to avoid duplication.
|
|
461
|
+
# Consider both run_context.session_state and session_state.
|
|
434
462
|
if run_context is not None and run_context.session_state is not None:
|
|
435
|
-
session_state_copy =
|
|
463
|
+
session_state_copy = run_context.session_state
|
|
436
464
|
else:
|
|
437
465
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
438
466
|
|
|
@@ -460,22 +488,24 @@ class Step:
|
|
|
460
488
|
|
|
461
489
|
if self._executor_type == "function":
|
|
462
490
|
log_debug(f"Executing function executor for step: {self.name}")
|
|
463
|
-
|
|
464
491
|
if _is_async_callable(self.active_executor) or _is_async_generator_function(self.active_executor):
|
|
465
492
|
raise ValueError("Cannot use async function with synchronous execution")
|
|
466
|
-
|
|
467
493
|
if _is_generator_function(self.active_executor):
|
|
468
494
|
log_debug("Function returned iterable, streaming events")
|
|
469
495
|
content = ""
|
|
470
496
|
try:
|
|
471
|
-
iterator = self._call_custom_function(
|
|
497
|
+
iterator = self._call_custom_function(
|
|
498
|
+
self.active_executor,
|
|
499
|
+
step_input,
|
|
500
|
+
session_state_copy,
|
|
501
|
+
run_context,
|
|
502
|
+
)
|
|
472
503
|
for event in iterator: # type: ignore
|
|
473
|
-
if (
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
content += event.content
|
|
504
|
+
if isinstance(event, (BaseRunOutputEvent)):
|
|
505
|
+
if isinstance(event, (RunContentEvent, TeamRunContentEvent)):
|
|
506
|
+
content += event.content if event.content is not None else ""
|
|
507
|
+
elif isinstance(event, (RunCompletedEvent, TeamRunCompletedEvent)):
|
|
508
|
+
content = event.content if event.content is not None else ""
|
|
479
509
|
else:
|
|
480
510
|
content += str(event)
|
|
481
511
|
if isinstance(event, StepOutput):
|
|
@@ -491,7 +521,7 @@ class Step:
|
|
|
491
521
|
yield enriched_event # type: ignore[misc]
|
|
492
522
|
|
|
493
523
|
# Merge session_state changes back
|
|
494
|
-
if session_state is not None:
|
|
524
|
+
if run_context is None and session_state is not None:
|
|
495
525
|
merge_dictionaries(session_state, session_state_copy)
|
|
496
526
|
|
|
497
527
|
if not final_response:
|
|
@@ -501,10 +531,15 @@ class Step:
|
|
|
501
531
|
final_response = e.value
|
|
502
532
|
|
|
503
533
|
else:
|
|
504
|
-
result = self._call_custom_function(
|
|
534
|
+
result = self._call_custom_function(
|
|
535
|
+
self.active_executor, # type: ignore[arg-type]
|
|
536
|
+
step_input,
|
|
537
|
+
session_state_copy,
|
|
538
|
+
run_context,
|
|
539
|
+
)
|
|
505
540
|
|
|
506
541
|
# Merge session_state changes back
|
|
507
|
-
if session_state is not None:
|
|
542
|
+
if run_context is None and session_state is not None:
|
|
508
543
|
merge_dictionaries(session_state, session_state_copy)
|
|
509
544
|
|
|
510
545
|
if isinstance(result, StepOutput):
|
|
@@ -564,6 +599,7 @@ class Step:
|
|
|
564
599
|
stream=True,
|
|
565
600
|
stream_events=stream_events,
|
|
566
601
|
yield_run_response=True,
|
|
602
|
+
run_context=run_context,
|
|
567
603
|
**kwargs,
|
|
568
604
|
)
|
|
569
605
|
|
|
@@ -577,9 +613,9 @@ class Step:
|
|
|
577
613
|
if stream_executor_events:
|
|
578
614
|
yield enriched_event # type: ignore[misc]
|
|
579
615
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
merge_dictionaries(session_state, session_state_copy)
|
|
616
|
+
# Update workflow session state
|
|
617
|
+
if run_context is None and session_state is not None:
|
|
618
|
+
merge_dictionaries(session_state, session_state_copy)
|
|
583
619
|
|
|
584
620
|
if store_executor_outputs and workflow_run_response is not None:
|
|
585
621
|
self._store_executor_response(workflow_run_response, active_executor_run_response) # type: ignore
|
|
@@ -640,6 +676,7 @@ class Step:
|
|
|
640
676
|
session_id: Optional[str] = None,
|
|
641
677
|
user_id: Optional[str] = None,
|
|
642
678
|
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
679
|
+
run_context: Optional[RunContext] = None,
|
|
643
680
|
session_state: Optional[Dict[str, Any]] = None,
|
|
644
681
|
store_executor_outputs: bool = True,
|
|
645
682
|
workflow_session: Optional["WorkflowSession"] = None,
|
|
@@ -655,8 +692,13 @@ class Step:
|
|
|
655
692
|
|
|
656
693
|
if workflow_session:
|
|
657
694
|
step_input.workflow_session = workflow_session
|
|
658
|
-
|
|
659
|
-
|
|
695
|
+
|
|
696
|
+
# Create session_state copy once to avoid duplication.
|
|
697
|
+
# Consider both run_context.session_state and session_state.
|
|
698
|
+
if run_context is not None and run_context.session_state is not None:
|
|
699
|
+
session_state_copy = run_context.session_state
|
|
700
|
+
else:
|
|
701
|
+
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
660
702
|
|
|
661
703
|
# Execute with retries
|
|
662
704
|
for attempt in range(self.max_retries + 1):
|
|
@@ -672,15 +714,15 @@ class Step:
|
|
|
672
714
|
iterator = self._call_custom_function(
|
|
673
715
|
self.active_executor,
|
|
674
716
|
step_input,
|
|
675
|
-
session_state_copy,
|
|
676
|
-
|
|
717
|
+
session_state_copy,
|
|
718
|
+
run_context,
|
|
719
|
+
)
|
|
677
720
|
for chunk in iterator: # type: ignore
|
|
678
|
-
if (
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
content += chunk.content
|
|
721
|
+
if isinstance(chunk, (BaseRunOutputEvent)):
|
|
722
|
+
if isinstance(chunk, (RunContentEvent, TeamRunContentEvent)):
|
|
723
|
+
content += chunk.content if chunk.content is not None else ""
|
|
724
|
+
elif isinstance(chunk, (RunCompletedEvent, TeamRunCompletedEvent)):
|
|
725
|
+
content = chunk.content if chunk.content is not None else ""
|
|
684
726
|
else:
|
|
685
727
|
content += str(chunk)
|
|
686
728
|
if isinstance(chunk, StepOutput):
|
|
@@ -690,15 +732,15 @@ class Step:
|
|
|
690
732
|
iterator = await self._acall_custom_function(
|
|
691
733
|
self.active_executor,
|
|
692
734
|
step_input,
|
|
693
|
-
session_state_copy,
|
|
694
|
-
|
|
735
|
+
session_state_copy,
|
|
736
|
+
run_context,
|
|
737
|
+
)
|
|
695
738
|
async for chunk in iterator: # type: ignore
|
|
696
|
-
if (
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
content += chunk.content
|
|
739
|
+
if isinstance(chunk, (BaseRunOutputEvent)):
|
|
740
|
+
if isinstance(chunk, (RunContentEvent, TeamRunContentEvent)):
|
|
741
|
+
content += chunk.content if chunk.content is not None else ""
|
|
742
|
+
elif isinstance(chunk, (RunCompletedEvent, TeamRunCompletedEvent)):
|
|
743
|
+
content = chunk.content if chunk.content is not None else ""
|
|
702
744
|
else:
|
|
703
745
|
content += str(chunk)
|
|
704
746
|
if isinstance(chunk, StepOutput):
|
|
@@ -709,7 +751,7 @@ class Step:
|
|
|
709
751
|
final_response = e.value
|
|
710
752
|
|
|
711
753
|
# Merge session_state changes back
|
|
712
|
-
if session_state is not None:
|
|
754
|
+
if run_context is None and session_state is not None:
|
|
713
755
|
merge_dictionaries(session_state, session_state_copy)
|
|
714
756
|
|
|
715
757
|
if final_response is not None:
|
|
@@ -719,13 +761,21 @@ class Step:
|
|
|
719
761
|
else:
|
|
720
762
|
if _is_async_callable(self.active_executor):
|
|
721
763
|
result = await self._acall_custom_function(
|
|
722
|
-
self.active_executor,
|
|
723
|
-
|
|
764
|
+
self.active_executor,
|
|
765
|
+
step_input,
|
|
766
|
+
session_state_copy,
|
|
767
|
+
run_context,
|
|
768
|
+
)
|
|
724
769
|
else:
|
|
725
|
-
result = self._call_custom_function(
|
|
770
|
+
result = self._call_custom_function(
|
|
771
|
+
self.active_executor, # type: ignore[arg-type]
|
|
772
|
+
step_input,
|
|
773
|
+
session_state_copy,
|
|
774
|
+
run_context,
|
|
775
|
+
)
|
|
726
776
|
|
|
727
777
|
# Merge session_state changes back
|
|
728
|
-
if session_state is not None:
|
|
778
|
+
if run_context is None and session_state is not None:
|
|
729
779
|
merge_dictionaries(session_state, session_state_copy)
|
|
730
780
|
|
|
731
781
|
# If function returns StepOutput, use it directly
|
|
@@ -784,12 +834,13 @@ class Step:
|
|
|
784
834
|
session_id=session_id,
|
|
785
835
|
user_id=user_id,
|
|
786
836
|
session_state=session_state_copy,
|
|
837
|
+
run_context=run_context,
|
|
787
838
|
**kwargs,
|
|
788
839
|
)
|
|
789
840
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
merge_dictionaries(session_state, session_state_copy)
|
|
841
|
+
# Update workflow session state
|
|
842
|
+
if run_context is None and session_state is not None:
|
|
843
|
+
merge_dictionaries(session_state, session_state_copy)
|
|
793
844
|
|
|
794
845
|
if store_executor_outputs and workflow_run_response is not None:
|
|
795
846
|
self._store_executor_response(workflow_run_response, response) # type: ignore
|
|
@@ -827,6 +878,7 @@ class Step:
|
|
|
827
878
|
stream_intermediate_steps: bool = False,
|
|
828
879
|
stream_executor_events: bool = True,
|
|
829
880
|
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
881
|
+
run_context: Optional[RunContext] = None,
|
|
830
882
|
session_state: Optional[Dict[str, Any]] = None,
|
|
831
883
|
step_index: Optional[Union[int, tuple]] = None,
|
|
832
884
|
store_executor_outputs: bool = True,
|
|
@@ -843,8 +895,12 @@ class Step:
|
|
|
843
895
|
if workflow_session:
|
|
844
896
|
step_input.workflow_session = workflow_session
|
|
845
897
|
|
|
846
|
-
# Create session_state copy once to avoid duplication
|
|
847
|
-
|
|
898
|
+
# Create session_state copy once to avoid duplication.
|
|
899
|
+
# Consider both run_context.session_state and session_state.
|
|
900
|
+
if run_context is not None and run_context.session_state is not None:
|
|
901
|
+
session_state_copy = run_context.session_state
|
|
902
|
+
else:
|
|
903
|
+
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
848
904
|
|
|
849
905
|
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
850
906
|
stream_events = stream_events or stream_intermediate_steps
|
|
@@ -878,15 +934,15 @@ class Step:
|
|
|
878
934
|
iterator = await self._acall_custom_function(
|
|
879
935
|
self.active_executor,
|
|
880
936
|
step_input,
|
|
881
|
-
session_state_copy,
|
|
882
|
-
|
|
937
|
+
session_state_copy,
|
|
938
|
+
run_context,
|
|
939
|
+
)
|
|
883
940
|
async for event in iterator: # type: ignore
|
|
884
|
-
if (
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
content += event.content
|
|
941
|
+
if isinstance(event, (BaseRunOutputEvent)):
|
|
942
|
+
if isinstance(event, (RunContentEvent, TeamRunContentEvent)):
|
|
943
|
+
content += event.content if event.content is not None else ""
|
|
944
|
+
elif isinstance(event, (RunCompletedEvent, TeamRunCompletedEvent)):
|
|
945
|
+
content = event.content if event.content is not None else ""
|
|
890
946
|
else:
|
|
891
947
|
content += str(event)
|
|
892
948
|
if isinstance(event, StepOutput):
|
|
@@ -904,7 +960,12 @@ class Step:
|
|
|
904
960
|
final_response = StepOutput(content=content)
|
|
905
961
|
elif _is_async_callable(self.active_executor):
|
|
906
962
|
# It's a regular async function - await it
|
|
907
|
-
result = await self._acall_custom_function(
|
|
963
|
+
result = await self._acall_custom_function(
|
|
964
|
+
self.active_executor,
|
|
965
|
+
step_input,
|
|
966
|
+
session_state_copy,
|
|
967
|
+
run_context,
|
|
968
|
+
)
|
|
908
969
|
if isinstance(result, StepOutput):
|
|
909
970
|
final_response = result
|
|
910
971
|
else:
|
|
@@ -912,14 +973,18 @@ class Step:
|
|
|
912
973
|
elif _is_generator_function(self.active_executor):
|
|
913
974
|
content = ""
|
|
914
975
|
# It's a regular generator function - iterate over it
|
|
915
|
-
iterator = self._call_custom_function(
|
|
976
|
+
iterator = self._call_custom_function(
|
|
977
|
+
self.active_executor,
|
|
978
|
+
step_input,
|
|
979
|
+
session_state_copy,
|
|
980
|
+
run_context,
|
|
981
|
+
)
|
|
916
982
|
for event in iterator: # type: ignore
|
|
917
|
-
if (
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
content += event.content
|
|
983
|
+
if isinstance(event, (BaseRunOutputEvent)):
|
|
984
|
+
if isinstance(event, (RunContentEvent, TeamRunContentEvent)):
|
|
985
|
+
content += event.content if event.content is not None else ""
|
|
986
|
+
elif isinstance(event, (RunCompletedEvent, TeamRunCompletedEvent)):
|
|
987
|
+
content = event.content if event.content is not None else ""
|
|
923
988
|
else:
|
|
924
989
|
content += str(event)
|
|
925
990
|
if isinstance(event, StepOutput):
|
|
@@ -937,14 +1002,19 @@ class Step:
|
|
|
937
1002
|
final_response = StepOutput(content=content)
|
|
938
1003
|
else:
|
|
939
1004
|
# It's a regular function - call it directly
|
|
940
|
-
result = self._call_custom_function(
|
|
1005
|
+
result = self._call_custom_function(
|
|
1006
|
+
self.active_executor, # type: ignore[arg-type]
|
|
1007
|
+
step_input,
|
|
1008
|
+
session_state_copy,
|
|
1009
|
+
run_context,
|
|
1010
|
+
)
|
|
941
1011
|
if isinstance(result, StepOutput):
|
|
942
1012
|
final_response = result
|
|
943
1013
|
else:
|
|
944
1014
|
final_response = StepOutput(content=str(result))
|
|
945
1015
|
|
|
946
1016
|
# Merge session_state changes back
|
|
947
|
-
if session_state is not None:
|
|
1017
|
+
if run_context is None and session_state is not None:
|
|
948
1018
|
merge_dictionaries(session_state, session_state_copy)
|
|
949
1019
|
else:
|
|
950
1020
|
# For agents and teams, prepare message with context
|
|
@@ -997,6 +1067,7 @@ class Step:
|
|
|
997
1067
|
session_state=session_state_copy,
|
|
998
1068
|
stream=True,
|
|
999
1069
|
stream_events=stream_events,
|
|
1070
|
+
run_context=run_context,
|
|
1000
1071
|
yield_run_response=True,
|
|
1001
1072
|
**kwargs,
|
|
1002
1073
|
)
|
|
@@ -1011,9 +1082,9 @@ class Step:
|
|
|
1011
1082
|
if stream_executor_events:
|
|
1012
1083
|
yield enriched_event # type: ignore[misc]
|
|
1013
1084
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
merge_dictionaries(session_state, session_state_copy)
|
|
1085
|
+
# Update workflow session state
|
|
1086
|
+
if run_context is None and session_state is not None:
|
|
1087
|
+
merge_dictionaries(session_state, session_state_copy)
|
|
1017
1088
|
|
|
1018
1089
|
if store_executor_outputs and workflow_run_response is not None:
|
|
1019
1090
|
self._store_executor_response(workflow_run_response, active_executor_run_response) # type: ignore
|