refactor(minimax): restore tool-call loop in _send_minimax
The previous refactor (commit 344a66fc) dropped the tool-call loop
in _send_minimax. The original function executed tool calls when the
response had tool_calls; the refactor was single-shot. This is a real
behavior regression (tools stop working) even though the existing
tests don't catch it.
Restore the tool loop:
- For each round (up to MAX_TOOL_ROUNDS + 2), call send_openai_compatible
with tools=_get_deepseek_tools() and tool_choice='auto'
- If response has tool_calls: dispatch each via
_execute_tool_calls_concurrently (handles both async context and
sync via run_coroutine_threadsafe / asyncio.run), append each
result to _minimax_history with role='tool' and tool_call_id
- If no tool_calls: return the response text (with thinking tags for
reasoning models)
- The lock is acquired/released per iteration to avoid holding it
during the API call (which can take seconds)
Preserved:
- 10-arg signature
- _minimax_history_lock (now acquired per iteration)
- _repair_minimax_history
- discussion_history handling
- System + context message wrapping
- Reasoning content extraction (response.raw_response.choices[0].message
.reasoning_details[0].get('text', ''))
- <thinking> tags wrap on the final response
Dropped (still):
- extra_body={reasoning_split: True} (not supported by send_openai_compatible;
would be a Phase 5 adapter addition if minimax-reasoner models need it)
New line count: 75 lines (vs 41 single-shot, vs 231 pre-refactor).
Net effect: 231 -> 75 = 68% reduction; tool loop preserved.
Verification: 38/38 tests pass (no regressions).
This commit is contained in:
+51
-28
@@ -2233,40 +2233,63 @@ def _send_minimax(md_content: str, user_message: str, base_dir: str,
|
||||
_ensure_minimax_client()
|
||||
from src.openai_compatible import OpenAICompatibleRequest, send_openai_compatible
|
||||
from src.vendor_capabilities import get_capabilities
|
||||
tools: list[dict[str, Any]] | None = _get_deepseek_tools() or None
|
||||
with _minimax_history_lock:
|
||||
_repair_minimax_history(_minimax_history)
|
||||
if discussion_history and not _minimax_history:
|
||||
_minimax_history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"})
|
||||
else:
|
||||
_minimax_history.append({"role": "user", "content": user_message})
|
||||
messages = [{"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}]
|
||||
messages.extend(_minimax_history)
|
||||
request = OpenAICompatibleRequest(
|
||||
messages=messages,
|
||||
model=_model,
|
||||
temperature=_temperature,
|
||||
top_p=_top_p,
|
||||
max_tokens=min(_max_tokens, 8192),
|
||||
stream=stream,
|
||||
stream_callback=stream_callback,
|
||||
)
|
||||
caps = get_capabilities("minimax", _model)
|
||||
response = send_openai_compatible(_minimax_client, request, capabilities=caps)
|
||||
reasoning_content = ""
|
||||
if response.raw_response and hasattr(response.raw_response, "choices"):
|
||||
choice = response.raw_response.choices[0]
|
||||
if hasattr(choice.message, "reasoning_details") and choice.message.reasoning_details:
|
||||
reasoning_content = choice.message.reasoning_details[0].get("text", "") if choice.message.reasoning_details else ""
|
||||
thinking_tags = ""
|
||||
if reasoning_content:
|
||||
thinking_tags = f"<thinking>\n{reasoning_content}\n</thinking>\n"
|
||||
full_text = thinking_tags + response.text
|
||||
with _minimax_history_lock:
|
||||
msg_to_store: dict[str, Any] = {"role": "assistant", "content": response.text or None}
|
||||
if reasoning_content:
|
||||
msg_to_store["reasoning_content"] = reasoning_content
|
||||
_minimax_history.append(msg_to_store)
|
||||
return full_text
|
||||
response_text: str = ""
|
||||
reasoning_content: str = ""
|
||||
for round_idx in range(MAX_TOOL_ROUNDS + 2):
|
||||
with _minimax_history_lock:
|
||||
messages = [{"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}]
|
||||
messages.extend(_minimax_history)
|
||||
request = OpenAICompatibleRequest(
|
||||
messages=messages,
|
||||
model=_model,
|
||||
temperature=_temperature,
|
||||
top_p=_top_p,
|
||||
max_tokens=min(_max_tokens, 8192),
|
||||
stream=stream,
|
||||
stream_callback=stream_callback,
|
||||
tools=tools,
|
||||
tool_choice="auto" if tools else "auto",
|
||||
)
|
||||
caps = get_capabilities("minimax", _model)
|
||||
response = send_openai_compatible(_minimax_client, request, capabilities=caps)
|
||||
reasoning_content = ""
|
||||
if response.raw_response and hasattr(response.raw_response, "choices"):
|
||||
choice = response.raw_response.choices[0]
|
||||
if hasattr(choice.message, "reasoning_details") and choice.message.reasoning_details:
|
||||
reasoning_content = choice.message.reasoning_details[0].get("text", "") if choice.message.reasoning_details else ""
|
||||
with _minimax_history_lock:
|
||||
msg_to_store: dict[str, Any] = {"role": "assistant", "content": response.text or None}
|
||||
if reasoning_content:
|
||||
msg_to_store["reasoning_content"] = reasoning_content
|
||||
if response.tool_calls:
|
||||
msg_to_store["tool_calls"] = response.tool_calls
|
||||
_minimax_history.append(msg_to_store)
|
||||
if not response.tool_calls:
|
||||
response_text = (f"<thinking>\n{reasoning_content}\n</thinking>\n" if reasoning_content else "") + response.text
|
||||
break
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
results = asyncio.run_coroutine_threadsafe(
|
||||
_execute_tool_calls_concurrently(response.tool_calls, base_dir, pre_tool_callback, qa_callback, round_idx, "minimax", patch_callback),
|
||||
loop,
|
||||
).result()
|
||||
except RuntimeError:
|
||||
results = asyncio.run(_execute_tool_calls_concurrently(response.tool_calls, base_dir, pre_tool_callback, qa_callback, round_idx, "minimax", patch_callback))
|
||||
with _minimax_history_lock:
|
||||
for _i, (name, call_id, out, _) in enumerate(results):
|
||||
_minimax_history.append({
|
||||
"role": "tool",
|
||||
"tool_call_id": call_id,
|
||||
"content": str(out) if out else "",
|
||||
})
|
||||
return response_text
|
||||
|
||||
#endregion: MiniMax Provider
|
||||
|
||||
|
||||
Reference in New Issue
Block a user