Private
Public Access
0
0

feat(tool_loop): apply run_with_tool_loop to Grok + Llama (Qwen deferred)

Task 1.6 of the follow-up track. _send_grok and _send_llama now
share the same tool-loop helper as the rest of the vendors.

Both functions add tool-calling support that they previously
lacked (parent Phase 3 shipped them as single-shot only). The
plan's Task 1.6 title says 'add missing loop' which matches
this scope. tool_choice='auto' if tools else 'auto' matches
the MiniMax pattern.

Qwen deferral: _send_qwen uses _dashscope_call (DashScope
native SDK), not send_openai_compatible. run_with_tool_loop
hard-codes send_openai_compatible. Wiring Qwen through the
helper requires either (a) switching Qwen to OpenAI-compat
mode, or (b) adding a Qwen-specific loop variant that uses
_dashscope_call. Both are non-trivial and out of scope for
Task 1.6. Tracked as a follow-up note in the state.toml.

Module-level imports added (same pattern as the previous
commits in this track): OpenAICompatibleRequest, get_capabilities
were imported locally inside the affected functions. Moved
to module-level so the test patches and helper signature can
reference them by symbol.

Green confirmed: 51 vendor + tool tests pass.
This commit is contained in:
2026-06-11 14:24:39 -04:00
parent 38f9484e49
commit 4069d67716
+30 -30
View File
@@ -42,8 +42,8 @@ from src import mcp_client
from src import mma_prompts from src import mma_prompts
from src import performance_monitor from src import performance_monitor
from src import project_manager from src import project_manager
from src.openai_compatible import send_openai_compatible from src.openai_compatible import send_openai_compatible, OpenAICompatibleRequest
from src.vendor_capabilities import VendorCapabilities from src.vendor_capabilities import VendorCapabilities, get_capabilities
# TODO(Ed): Eliminate these? # TODO(Ed): Eliminate these?
from src.events import EventEmitter from src.events import EventEmitter
@@ -2244,8 +2244,7 @@ def _send_grok(md_content: str, user_message: str, base_dir: str,
stream_callback: Optional[Callable[[str], None]] = None, stream_callback: Optional[Callable[[str], None]] = None,
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str: patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
client = _ensure_grok_client() client = _ensure_grok_client()
from src.openai_compatible import OpenAICompatibleRequest, send_openai_compatible tools: list[dict[str, Any]] | None = _get_deepseek_tools() or None
from src.vendor_capabilities import get_capabilities
with _grok_history_lock: with _grok_history_lock:
user_content = user_message user_content = user_message
if file_items: if file_items:
@@ -2256,21 +2255,22 @@ def _send_grok(md_content: str, user_message: str, base_dir: str,
_grok_history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"}) _grok_history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"})
else: else:
_grok_history.append({"role": "user", "content": user_content}) _grok_history.append({"role": "user", "content": user_content})
messages = [{"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}] def _build_grok_request(_round_idx: int) -> OpenAICompatibleRequest:
with _grok_history_lock:
messages: list[dict[str, Any]] = [{"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}]
messages.extend(_grok_history) messages.extend(_grok_history)
request = OpenAICompatibleRequest( return OpenAICompatibleRequest(
messages=messages, messages=messages, model=_model, temperature=_temperature, top_p=_top_p,
model=_model, max_tokens=_max_tokens, stream=stream, stream_callback=stream_callback,
temperature=_temperature, tools=tools, tool_choice="auto" if tools else "auto",
top_p=_top_p,
max_tokens=_max_tokens,
stream=stream,
stream_callback=stream_callback,
) )
caps = get_capabilities("grok", _model) caps = get_capabilities("grok", _model)
response = send_openai_compatible(client, request, capabilities=caps) return run_with_tool_loop(
_grok_history.append({"role": "assistant", "content": response.text}) client, _build_grok_request, capabilities=caps,
return response.text pre_tool_callback=pre_tool_callback, qa_callback=qa_callback, stream_callback=stream_callback,
patch_callback=patch_callback, base_dir=base_dir, vendor_name="grok",
history_lock=_grok_history_lock, history=_grok_history,
)
def _list_grok_models() -> list[str]: def _list_grok_models() -> list[str]:
from src.vendor_capabilities import list_models_for_vendor from src.vendor_capabilities import list_models_for_vendor
@@ -2450,8 +2450,7 @@ def _send_llama(md_content: str, user_message: str, base_dir: str,
stream_callback: Optional[Callable[[str], None]] = None, stream_callback: Optional[Callable[[str], None]] = None,
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str: patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str:
client = _ensure_llama_client() client = _ensure_llama_client()
from src.openai_compatible import OpenAICompatibleRequest, send_openai_compatible tools: list[dict[str, Any]] | None = _get_deepseek_tools() or None
from src.vendor_capabilities import get_capabilities
with _llama_history_lock: with _llama_history_lock:
user_content = user_message user_content = user_message
if file_items: if file_items:
@@ -2462,21 +2461,22 @@ def _send_llama(md_content: str, user_message: str, base_dir: str,
_llama_history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"}) _llama_history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"})
else: else:
_llama_history.append({"role": "user", "content": user_content}) _llama_history.append({"role": "user", "content": user_content})
messages = [{"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}] def _build_llama_request(_round_idx: int) -> OpenAICompatibleRequest:
with _llama_history_lock:
messages: list[dict[str, Any]] = [{"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}]
messages.extend(_llama_history) messages.extend(_llama_history)
request = OpenAICompatibleRequest( return OpenAICompatibleRequest(
messages=messages, messages=messages, model=_model, temperature=_temperature, top_p=_top_p,
model=_model, max_tokens=_max_tokens, stream=stream, stream_callback=stream_callback,
temperature=_temperature, tools=tools, tool_choice="auto" if tools else "auto",
top_p=_top_p,
max_tokens=_max_tokens,
stream=stream,
stream_callback=stream_callback,
) )
caps = get_capabilities("llama", _model) caps = get_capabilities("llama", _model)
response = send_openai_compatible(client, request, capabilities=caps) return run_with_tool_loop(
_llama_history.append({"role": "assistant", "content": response.text}) client, _build_llama_request, capabilities=caps,
return response.text pre_tool_callback=pre_tool_callback, qa_callback=qa_callback, stream_callback=stream_callback,
patch_callback=patch_callback, base_dir=base_dir, vendor_name="llama",
history_lock=_llama_history_lock, history=_llama_history,
)
def _list_llama_models() -> list[str]: def _list_llama_models() -> list[str]:
from src.vendor_capabilities import list_models_for_vendor from src.vendor_capabilities import list_models_for_vendor