docs(ai_client): add SQLite-granularity docstrings to tool execution functions
Also fixes return-type discrepancy in tests/test_ai_client_tool_loop.py mock by wrapping NormalizedResponse inside Result.
This commit is contained in:
+98
-4
@@ -671,10 +671,33 @@ async def _execute_tool_calls_concurrently(
|
||||
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None
|
||||
) -> list[tuple[str, str, str, str]]: # tool_name, call_id, output, original_name
|
||||
"""
|
||||
|
||||
|
||||
Executes multiple tool calls concurrently using asyncio.gather.
|
||||
Returns a list of (tool_name, call_id, output, original_name).
|
||||
Executes tool calls concurrently using asyncio.gather.
|
||||
|
||||
Functional Purpose:
|
||||
Concurrently dispatches tool calls to _execute_single_tool_call_async.
|
||||
|
||||
Parameters & Inputs:
|
||||
calls (list[Any]): List of tool calls.
|
||||
base_dir (str): Workspace path.
|
||||
pre_tool_callback (Optional[Callable]): HITL/approval callback.
|
||||
qa_callback (Optional[Callable]): QA verification callback.
|
||||
r_idx (int): Round index.
|
||||
provider (str): LLM provider.
|
||||
patch_callback (Optional[Callable]): Patch verification callback.
|
||||
|
||||
Returns:
|
||||
list[tuple[str, str, str, str]]: List of (tool_name, call_id, output, original_name).
|
||||
|
||||
Immediate-Mode DAG / Thread Context:
|
||||
Called by: run_with_tool_loop
|
||||
Calls: _execute_single_tool_call_async
|
||||
|
||||
SSDL:
|
||||
`[I:gather] => o-> [I:_execute_single_tool_call_async] -> [M] -> [T:tool_results]`
|
||||
|
||||
Thread Boundaries:
|
||||
Runs in the active asyncio event loop thread.
|
||||
|
||||
[C: tests/test_async_tools.py:test_execute_tool_calls_concurrently_exception_handling, tests/test_async_tools.py:test_execute_tool_calls_concurrently_timing]
|
||||
"""
|
||||
monitor = performance_monitor.get_monitor()
|
||||
@@ -729,6 +752,44 @@ def run_with_tool_loop(
|
||||
send_func: Optional[Callable[[int], NormalizedResponse]] = None,
|
||||
on_pre_dispatch: Optional[Callable[[int, list[dict[str, Any]]], list[dict[str, Any]]]] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Orchestrates the LLM conversation loop, executing tool calls and updating history.
|
||||
|
||||
Functional Purpose:
|
||||
Runs a multi-round tool loop (up to MAX_TOOL_ROUNDS + 2). It dispatches client requests,
|
||||
executes any generated tool calls concurrently, updates history, and repeats until completion.
|
||||
|
||||
Parameters & Inputs:
|
||||
client (Any): Active client instance.
|
||||
request (Union[OpenAICompatibleRequest, Callable]): Initial request or builder callback.
|
||||
capabilities (Optional[VendorCapabilities]): Capabilities config.
|
||||
pre_tool_callback (Optional[Callable]): Human-in-the-loop validation callback.
|
||||
qa_callback (Optional[Callable]): QA verification callback.
|
||||
stream_callback (Optional[Callable]): Streaming callback.
|
||||
patch_callback (Optional[Callable]): Verification callback for code patches.
|
||||
base_dir (str): Base workspace directory.
|
||||
vendor_name (str): The vendor name.
|
||||
history_lock (Optional[threading.Lock]): Lock for thread safety on history.
|
||||
history (Optional[list[dict[str, Any]]]): Conversation history.
|
||||
trim_func (Optional[Callable]): Trimming callback for history.
|
||||
reasoning_extractor (Optional[Callable]): Callback to extract reasoning content.
|
||||
send_func (Optional[Callable]): Dispatch sender callback.
|
||||
on_pre_dispatch (Optional[Callable]): Callback to adjust tools.
|
||||
|
||||
Returns:
|
||||
str: The final text response returned by the LLM.
|
||||
|
||||
Immediate-Mode DAG / Thread Context:
|
||||
Called by: _send_anthropic, _send_deepseek, _send_minimax, _send_qwen, _send_llama,
|
||||
_send_grok, _send_llama_native
|
||||
Calls: dispatch_send, _execute_tool_calls_concurrently
|
||||
|
||||
SSDL:
|
||||
`o-> [I:dispatch_send] -> [B:tool_calls?] => [I:_execute_tool_calls_concurrently] -> [T:response_text]`
|
||||
|
||||
Thread Boundaries:
|
||||
Runs synchronously in caller thread; synchronizes history modifications using history_lock.
|
||||
"""
|
||||
def _default_send(_round_idx: int) -> NormalizedResponse:
|
||||
from src.openai_compatible import send_openai_compatible as _send_oc
|
||||
assert capabilities is not None, "capabilities required when send_func is not provided"
|
||||
@@ -794,6 +855,39 @@ async def _execute_single_tool_call_async(
|
||||
tier: str | None = None,
|
||||
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None
|
||||
) -> tuple[str, str, str, str]:
|
||||
"""
|
||||
Executes a single tool call asynchronously, checking the approval clutch.
|
||||
|
||||
Functional Purpose:
|
||||
Executes a tool call (either PowerShell script or MCP tool) based on tool approval clutch settings.
|
||||
Uses pre_tool_callback for human approval when required.
|
||||
|
||||
Parameters & Inputs:
|
||||
name (str): The name of the tool to execute.
|
||||
args (dict[str, Any]): Arguments passed to the tool.
|
||||
call_id (str): Unique call identifier.
|
||||
base_dir (str): Workspace root directory.
|
||||
pre_tool_callback (Optional[Callable]): Hook for HITL validation.
|
||||
qa_callback (Optional[Callable]): QA verification callback.
|
||||
r_idx (int): Current tool loop round index.
|
||||
tier (str | None): Active MMA orchestration tier.
|
||||
patch_callback (Optional[Callable]): Verification callback for code patches.
|
||||
|
||||
Returns:
|
||||
tuple[str, str, str, str]: A tuple containing (tool_name, call_id, output, original_name).
|
||||
|
||||
Immediate-Mode DAG / Thread Context:
|
||||
Called by: _execute_tool_calls_concurrently
|
||||
Calls: set_current_tier, events.emit, _append_comms, _run_script,
|
||||
pre_tool_callback, mcp_client.async_dispatch
|
||||
|
||||
SSDL:
|
||||
`[I:CheckClutch] -> [B:Approved?] -> [I:run_powershell] -> [T:output]`
|
||||
|
||||
Thread Boundaries:
|
||||
Runs in the active asyncio event loop thread; offloads blocking synchronous calls
|
||||
(like pre_tool_callback and _run_script) to separate worker threads using asyncio.to_thread.
|
||||
"""
|
||||
set_current_tier(tier)
|
||||
out = ""
|
||||
tool_executed = False
|
||||
|
||||
@@ -16,6 +16,7 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
from src.result_types import Result
|
||||
from src.openai_compatible import NormalizedResponse, OpenAICompatibleRequest
|
||||
from src.ai_client import run_with_tool_loop
|
||||
from src.vendor_capabilities import VendorCapabilities
|
||||
@@ -24,13 +25,13 @@ from src.vendor_capabilities import VendorCapabilities
|
||||
def caps() -> VendorCapabilities:
|
||||
return VendorCapabilities(vendor="test", model="test-model", tool_calling=True, context_window=8192)
|
||||
|
||||
def _make_normalized_response(text: str = "ok", tool_calls: list[dict[str, Any]] | None = None) -> NormalizedResponse:
|
||||
return NormalizedResponse(
|
||||
def _make_normalized_response(text: str = "ok", tool_calls: list[dict[str, Any]] | None = None) -> Result[NormalizedResponse]:
|
||||
return Result(data=NormalizedResponse(
|
||||
text=text, tool_calls=tool_calls or [],
|
||||
usage_input_tokens=10, usage_output_tokens=5,
|
||||
usage_cache_read_tokens=0, usage_cache_creation_tokens=0,
|
||||
raw_response=None,
|
||||
)
|
||||
))
|
||||
|
||||
def test_run_with_tool_loop_no_tool_calls_returns_immediately(caps: VendorCapabilities) -> None:
|
||||
client = MagicMock()
|
||||
|
||||
Reference in New Issue
Block a user