mirror of https://github.com/aliasrobotics/cai.git
Add deepseek resilence
This commit is contained in:
parent
0c9544a76b
commit
b53547ba46
|
|
@ -2162,7 +2162,7 @@ class OpenAIChatCompletionsModel(Model):
|
|||
kwargs["reasoning_effort"] = model_settings.reasoning_effort
|
||||
else:
|
||||
# Default to "low" reasoning effort if model supports it
|
||||
kwargs["reasoning_effort"] = "high"
|
||||
kwargs["reasoning_effort"] = "low"
|
||||
elif provider == "claude" or "claude" in model_str:
|
||||
litellm.drop_params = True
|
||||
kwargs.pop("store", None)
|
||||
|
|
@ -2371,7 +2371,7 @@ class OpenAIChatCompletionsModel(Model):
|
|||
provider_kwargs["reasoning_effort"] = model_settings.reasoning_effort
|
||||
else:
|
||||
# Default to "low" reasoning effort
|
||||
provider_kwargs["reasoning_effort"] = "high"
|
||||
provider_kwargs["reasoning_effort"] = "low"
|
||||
elif provider == "claude" or "claude" in model_str:
|
||||
provider_kwargs["custom_llm_provider"] = "anthropic"
|
||||
provider_kwargs.pop("store", None) # Claude doesn't support store parameter
|
||||
|
|
@ -3251,6 +3251,9 @@ class _Converter:
|
|||
call_id = func_output["call_id"]
|
||||
output_content = func_output["output"]
|
||||
|
||||
# IMPORTANT: Truncate call_id to 40 characters for consistency
|
||||
truncated_call_id = call_id[:40] if call_id else call_id
|
||||
|
||||
# Update execution timing if we have the start time
|
||||
if hasattr(cls, 'recent_tool_calls') and call_id in cls.recent_tool_calls:
|
||||
tool_call_details = cls.recent_tool_calls[call_id] # Renamed for clarity
|
||||
|
|
@ -3371,10 +3374,10 @@ class _Converter:
|
|||
# The responsibility for ensuring a preceding assistant message
|
||||
# is now fully deferred to fix_message_list, called later.
|
||||
|
||||
# Now add the tool message
|
||||
# Now add the tool message with truncated call_id
|
||||
msg: ChatCompletionToolMessageParam = {
|
||||
"role": "tool",
|
||||
"tool_call_id": func_output["call_id"],
|
||||
"tool_call_id": truncated_call_id,
|
||||
"content": func_output["output"],
|
||||
}
|
||||
result.append(msg)
|
||||
|
|
|
|||
|
|
@ -741,6 +741,7 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
a tool_result block (tool message with matching tool_call_id).
|
||||
6. Each 'tool' message must be immediately preceded by an 'assistant' message
|
||||
with matching tool_call_id in its tool_calls.
|
||||
7. Tool call IDs are truncated to 40 characters for API compatibility.
|
||||
|
||||
Args:
|
||||
messages (List[dict]): List of message dictionaries containing
|
||||
|
|
@ -754,22 +755,45 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
# Deep-copy to ensure we don't modify the input
|
||||
sanitized_messages = []
|
||||
|
||||
# First pass - identify tool_call_ids from assistant messages and tool messages
|
||||
# First, truncate all tool call IDs to 40 characters throughout the messages
|
||||
# This ensures consistency for providers like DeepSeek that have strict ID matching
|
||||
for msg in messages:
|
||||
msg_copy = msg.copy()
|
||||
|
||||
# Truncate tool_call_id in tool messages
|
||||
if msg_copy.get("role") == "tool" and msg_copy.get("tool_call_id"):
|
||||
if len(msg_copy["tool_call_id"]) > 40:
|
||||
msg_copy["tool_call_id"] = msg_copy["tool_call_id"][:40]
|
||||
|
||||
# Truncate IDs in assistant tool_calls
|
||||
if msg_copy.get("role") == "assistant" and msg_copy.get("tool_calls"):
|
||||
tool_calls_copy = []
|
||||
for tc in msg_copy["tool_calls"]:
|
||||
tc_copy = tc.copy()
|
||||
if tc_copy.get("id") and len(tc_copy["id"]) > 40:
|
||||
tc_copy["id"] = tc_copy["id"][:40]
|
||||
tool_calls_copy.append(tc_copy)
|
||||
msg_copy["tool_calls"] = tool_calls_copy
|
||||
|
||||
sanitized_messages.append(msg_copy)
|
||||
|
||||
# Now process the messages with truncated IDs
|
||||
processed_messages = []
|
||||
tool_call_map = {} # Map from tool_call_id to (assistant_idx, tool_idx)
|
||||
|
||||
for i, msg in enumerate(messages):
|
||||
for i, msg in enumerate(sanitized_messages):
|
||||
# Skip empty messages (considered empty if 'content' is None or only whitespace)
|
||||
if msg.get("role") in ["user", "system"] and (msg.get("content") is None or not str(msg.get("content", "")).strip()):
|
||||
# Special case: if it's a system message, set content to empty string instead of skipping
|
||||
if msg.get("role") == "system":
|
||||
# Replace None with empty string
|
||||
msg["content"] = ""
|
||||
sanitized_messages.append(msg)
|
||||
processed_messages.append(msg)
|
||||
# Skip empty user messages entirely
|
||||
continue
|
||||
|
||||
# Add valid messages to our sanitized list first
|
||||
sanitized_messages.append(msg)
|
||||
# Add valid messages to our processed list first
|
||||
processed_messages.append(msg)
|
||||
|
||||
# Now track tool calls and tool messages for pairing
|
||||
if msg.get("role") == "assistant" and msg.get("tool_calls"):
|
||||
|
|
@ -777,12 +801,12 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
if tc.get("id"):
|
||||
tool_id = tc.get("id")
|
||||
if tool_id not in tool_call_map:
|
||||
tool_call_map[tool_id] = {"assistant_idx": len(sanitized_messages) - 1, "tool_idx": None}
|
||||
tool_call_map[tool_id] = {"assistant_idx": len(processed_messages) - 1, "tool_idx": None}
|
||||
|
||||
if msg.get("role") == "tool" and msg.get("tool_call_id"):
|
||||
tool_id = msg.get("tool_call_id")
|
||||
if tool_id in tool_call_map:
|
||||
tool_call_map[tool_id]["tool_idx"] = len(sanitized_messages) - 1
|
||||
tool_call_map[tool_id]["tool_idx"] = len(processed_messages) - 1
|
||||
else:
|
||||
# Tool response without a matching tool call - create a synthetic pair
|
||||
# by adding a dummy assistant message with a tool_call
|
||||
|
|
@ -799,15 +823,15 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
}]
|
||||
}
|
||||
# Insert the assistant message *before* the tool message
|
||||
sanitized_messages.insert(len(sanitized_messages) - 1, assistant_msg)
|
||||
processed_messages.insert(len(processed_messages) - 1, assistant_msg)
|
||||
# Update mapping
|
||||
tool_call_map[tool_id] = {"assistant_idx": len(sanitized_messages) - 2, "tool_idx": len(sanitized_messages) - 1}
|
||||
tool_call_map[tool_id] = {"assistant_idx": len(processed_messages) - 2, "tool_idx": len(processed_messages) - 1}
|
||||
|
||||
# Second pass - ensure correct sequence (tool messages must directly follow their assistant messages)
|
||||
# This fixes the error "messages with role 'tool' must be a response to a preceeding message with 'tool_calls'"
|
||||
i = 0
|
||||
while i < len(sanitized_messages):
|
||||
msg = sanitized_messages[i]
|
||||
while i < len(processed_messages):
|
||||
msg = processed_messages[i]
|
||||
|
||||
# Check if this is a tool message that might be out of sequence
|
||||
if msg.get("role") == "tool" and msg.get("tool_call_id"):
|
||||
|
|
@ -815,7 +839,7 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
|
||||
# If this isn't the first message, check if the previous message is a matching assistant message
|
||||
if i > 0:
|
||||
prev_msg = sanitized_messages[i-1]
|
||||
prev_msg = processed_messages[i-1]
|
||||
|
||||
# Check if the previous message is an assistant message with matching tool_call_id
|
||||
is_valid_sequence = (
|
||||
|
|
@ -827,7 +851,7 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
if not is_valid_sequence:
|
||||
# Find the assistant message with this tool_call_id
|
||||
assistant_idx = None
|
||||
for j, assistant_msg in enumerate(sanitized_messages):
|
||||
for j, assistant_msg in enumerate(processed_messages):
|
||||
if (assistant_msg.get("role") == "assistant" and
|
||||
assistant_msg.get("tool_calls") and
|
||||
any(tc.get("id") == tool_id for tc in assistant_msg.get("tool_calls", []))):
|
||||
|
|
@ -837,10 +861,10 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
# If we found a matching assistant message, move this tool message right after it
|
||||
if assistant_idx is not None:
|
||||
# Remember to save the tool message
|
||||
tool_msg = sanitized_messages.pop(i)
|
||||
tool_msg = processed_messages.pop(i)
|
||||
|
||||
# Insert right after the assistant message
|
||||
sanitized_messages.insert(assistant_idx + 1, tool_msg)
|
||||
processed_messages.insert(assistant_idx + 1, tool_msg)
|
||||
|
||||
# Adjust i to account for the move
|
||||
if assistant_idx < i:
|
||||
|
|
@ -867,7 +891,7 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
}
|
||||
|
||||
# Insert the assistant message before the tool message
|
||||
sanitized_messages.insert(i, assistant_msg)
|
||||
processed_messages.insert(i, assistant_msg)
|
||||
|
||||
# Skip past both messages
|
||||
i += 2
|
||||
|
|
@ -889,7 +913,7 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
}
|
||||
|
||||
# Insert the assistant message before the tool message
|
||||
sanitized_messages.insert(0, assistant_msg)
|
||||
processed_messages.insert(0, assistant_msg)
|
||||
|
||||
# Skip past both messages
|
||||
i += 2
|
||||
|
|
@ -903,7 +927,7 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
if indices["tool_idx"] is None:
|
||||
# Tool call without a response - create a synthetic tool message
|
||||
assistant_idx = indices["assistant_idx"]
|
||||
assistant_msg = sanitized_messages[assistant_idx]
|
||||
assistant_msg = processed_messages[assistant_idx]
|
||||
|
||||
# Find the relevant tool call
|
||||
tool_name = "unknown_function"
|
||||
|
|
@ -921,18 +945,18 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
}
|
||||
|
||||
# Insert immediately after the assistant message
|
||||
if assistant_idx + 1 < len(sanitized_messages):
|
||||
if assistant_idx + 1 < len(processed_messages):
|
||||
# Insert at the position after assistant
|
||||
sanitized_messages.insert(assistant_idx + 1, tool_msg)
|
||||
processed_messages.insert(assistant_idx + 1, tool_msg)
|
||||
else:
|
||||
# Just append if we're at the end
|
||||
sanitized_messages.append(tool_msg)
|
||||
processed_messages.append(tool_msg)
|
||||
|
||||
# Update the map to note that this tool call now has a response
|
||||
tool_call_map[tool_id]["tool_idx"] = assistant_idx + 1
|
||||
|
||||
# Ensure messages have non-null content (required by some providers)
|
||||
for msg in sanitized_messages:
|
||||
for msg in processed_messages:
|
||||
# For assistant messages with tool_calls, content can be None
|
||||
if msg.get("role") == "assistant" and msg.get("tool_calls"):
|
||||
# Assistant messages with tool calls can have None content - this is valid
|
||||
|
|
@ -949,9 +973,9 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
# Special case for Claude: ensure strict alternating pattern between assistant tool_calls and tool results
|
||||
# If multiple consecutive assistant messages with tool_calls exist, interleave them with tool responses
|
||||
i = 0
|
||||
while i < len(sanitized_messages) - 1:
|
||||
current_msg = sanitized_messages[i]
|
||||
next_msg = sanitized_messages[i + 1]
|
||||
while i < len(processed_messages) - 1:
|
||||
current_msg = processed_messages[i]
|
||||
next_msg = processed_messages[i + 1]
|
||||
|
||||
# When current message is assistant with tool_calls and next message is NOT a tool response
|
||||
if (current_msg.get("role") == "assistant" and
|
||||
|
|
@ -972,13 +996,13 @@ def fix_message_list(messages): # pylint: disable=R0914,R0915,R0912
|
|||
}
|
||||
|
||||
# Insert the tool message after the current assistant message
|
||||
sanitized_messages.insert(i + 1, tool_msg)
|
||||
processed_messages.insert(i + 1, tool_msg)
|
||||
|
||||
# Skip over the newly inserted message
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
return sanitized_messages
|
||||
return processed_messages
|
||||
|
||||
def cli_print_tool_call(tool_name="", args="", output="", prefix=" "):
|
||||
"""Print a tool call with pretty formatting"""
|
||||
|
|
|
|||
Loading…
Reference in New Issue