- harness/es.py: append-only event store + проекции (образец — кооп-портал) - harness/levels.py: уровни 1с/1м/1ч/1д, контекст ∝ масштабу - harness/scheduler.py: уровневые потоки, идемпотентные тики, absorbed-стабы - harness/isolated_runner.py + _llm_worker.py: LLM-тик в сабпроцессе (глобальный asyncio-рантайм Strands не переживает 2+ create_harness) - cli.py: init/task/tasks/status/events + requeue (написан самим минутным агентом) - 9router: отдельный ключ sk-simbiot, модель auto (glm-5.3 первым)
134 lines
4.8 KiB
Python
134 lines
4.8 KiB
Python
"""Concurrent tool executor implementation."""
|
|
|
|
import asyncio
|
|
from collections.abc import AsyncGenerator
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from typing_extensions import override
|
|
|
|
from ...telemetry.metrics import Trace
|
|
from ...types._events import TypedEvent
|
|
from ...types.tools import ToolResult, ToolUse
|
|
from ._executor import ToolExecutor
|
|
|
|
if TYPE_CHECKING: # pragma: no cover
|
|
from ...agent import Agent
|
|
from ..structured_output._structured_output_context import StructuredOutputContext
|
|
|
|
|
|
class ConcurrentToolExecutor(ToolExecutor):
|
|
"""Concurrent tool executor."""
|
|
|
|
@override
|
|
async def _execute(
|
|
self,
|
|
agent: "Agent",
|
|
tool_uses: list[ToolUse],
|
|
tool_results: list[ToolResult],
|
|
cycle_trace: Trace,
|
|
cycle_span: Any,
|
|
invocation_state: dict[str, Any],
|
|
structured_output_context: "StructuredOutputContext | None" = None,
|
|
) -> AsyncGenerator[TypedEvent, None]:
|
|
"""Execute tools concurrently.
|
|
|
|
Args:
|
|
agent: The agent for which tools are being executed.
|
|
tool_uses: Metadata and inputs for the tools to be executed.
|
|
tool_results: List of tool results from each tool execution.
|
|
cycle_trace: Trace object for the current event loop cycle.
|
|
cycle_span: Span object for tracing the cycle.
|
|
invocation_state: Context for the tool invocation.
|
|
structured_output_context: Context for structured output handling.
|
|
|
|
Yields:
|
|
Events from the tool execution stream.
|
|
"""
|
|
task_queue: asyncio.Queue[tuple[int, Any]] = asyncio.Queue()
|
|
task_events = [asyncio.Event() for _ in tool_uses]
|
|
task_results: list[list[ToolResult]] = [[] for _ in tool_uses]
|
|
stop_event = object()
|
|
|
|
tasks = []
|
|
try:
|
|
for task_id, tool_use in enumerate(tool_uses):
|
|
tasks.append(
|
|
asyncio.create_task(
|
|
self._task(
|
|
agent,
|
|
tool_use,
|
|
task_results[task_id],
|
|
cycle_trace,
|
|
cycle_span,
|
|
invocation_state,
|
|
task_id,
|
|
task_queue,
|
|
task_events[task_id],
|
|
stop_event,
|
|
structured_output_context,
|
|
)
|
|
)
|
|
)
|
|
|
|
task_count = len(tasks)
|
|
while task_count:
|
|
task_id, event = await task_queue.get()
|
|
if event is stop_event:
|
|
task_count -= 1
|
|
continue
|
|
|
|
if isinstance(event, Exception):
|
|
raise event
|
|
|
|
yield event
|
|
task_events[task_id].set()
|
|
for results in task_results:
|
|
tool_results.extend(results)
|
|
finally:
|
|
for task in tasks:
|
|
task.cancel()
|
|
await asyncio.gather(*tasks, return_exceptions=True)
|
|
|
|
async def _task(
|
|
self,
|
|
agent: "Agent",
|
|
tool_use: ToolUse,
|
|
tool_results: list[ToolResult],
|
|
cycle_trace: Trace,
|
|
cycle_span: Any,
|
|
invocation_state: dict[str, Any],
|
|
task_id: int,
|
|
task_queue: asyncio.Queue,
|
|
task_event: asyncio.Event,
|
|
stop_event: object,
|
|
structured_output_context: "StructuredOutputContext | None",
|
|
) -> None:
|
|
"""Execute a single tool and put results in the task queue.
|
|
|
|
Args:
|
|
agent: The agent executing the tool.
|
|
tool_use: Tool use metadata and inputs.
|
|
tool_results: List of tool results from each tool execution.
|
|
cycle_trace: Trace object for the current event loop cycle.
|
|
cycle_span: Span object for tracing the cycle.
|
|
invocation_state: Context for tool execution.
|
|
task_id: Unique identifier for this task.
|
|
task_queue: Queue to put tool events into.
|
|
task_event: Event to signal when task can continue.
|
|
stop_event: Sentinel object to signal task completion.
|
|
structured_output_context: Context for structured output handling.
|
|
"""
|
|
try:
|
|
events = ToolExecutor._stream_with_trace(
|
|
agent, tool_use, tool_results, cycle_trace, cycle_span, invocation_state, structured_output_context
|
|
)
|
|
async for event in events:
|
|
task_queue.put_nowait((task_id, event))
|
|
await task_event.wait()
|
|
task_event.clear()
|
|
|
|
except Exception as e:
|
|
task_queue.put_nowait((task_id, e))
|
|
|
|
finally:
|
|
task_queue.put_nowait((task_id, stop_event))
|