mirror of https://github.com/aliasrobotics/cai.git
Merge pull request #173 from aliasrobotics/deepseek_reasoning_trace
Deepseek reasoning trace
This commit is contained in:
commit
3c650c10d3
|
|
@ -1141,7 +1141,9 @@ class OpenAIChatCompletionsModel(Model):
|
|||
else:
|
||||
# Non-streaming mode: Use simple text output
|
||||
from cai.util import print_claude_reasoning_simple, detect_claude_thinking_in_stream
|
||||
if detect_claude_thinking_in_stream(str(self.model)):
|
||||
# Check if model supports reasoning (Claude or DeepSeek)
|
||||
model_str_lower = str(self.model).lower()
|
||||
if detect_claude_thinking_in_stream(str(self.model)) or "deepseek" in model_str_lower:
|
||||
print_claude_reasoning_simple(reasoning_content, self.agent_name, str(self.model))
|
||||
|
||||
|
||||
|
|
@ -2153,6 +2155,14 @@ class OpenAIChatCompletionsModel(Model):
|
|||
# Remove tool_choice if no tools are specified
|
||||
if not converted_tools:
|
||||
kwargs.pop("tool_choice", None)
|
||||
|
||||
# Add reasoning support for DeepSeek
|
||||
# DeepSeek supports reasoning_effort parameter
|
||||
if hasattr(model_settings, "reasoning_effort") and model_settings.reasoning_effort:
|
||||
kwargs["reasoning_effort"] = model_settings.reasoning_effort
|
||||
else:
|
||||
# Default to "low" reasoning effort if model supports it
|
||||
kwargs["reasoning_effort"] = "low"
|
||||
elif provider == "claude" or "claude" in model_str:
|
||||
litellm.drop_params = True
|
||||
kwargs.pop("store", None)
|
||||
|
|
@ -2355,6 +2365,13 @@ class OpenAIChatCompletionsModel(Model):
|
|||
provider_kwargs["custom_llm_provider"] = "deepseek"
|
||||
provider_kwargs.pop("store", None) # DeepSeek doesn't support store parameter
|
||||
provider_kwargs.pop("parallel_tool_calls", None) # DeepSeek doesn't support parallel tool calls
|
||||
|
||||
# Add reasoning support for DeepSeek
|
||||
if hasattr(model_settings, "reasoning_effort") and model_settings.reasoning_effort:
|
||||
provider_kwargs["reasoning_effort"] = model_settings.reasoning_effort
|
||||
else:
|
||||
# Default to "low" reasoning effort
|
||||
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
|
||||
|
|
@ -3234,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
|
||||
|
|
@ -3354,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)
|
||||
|
|
|
|||
157
src/cai/util.py
157
src/cai/util.py
|
|
@ -797,6 +797,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
|
||||
|
|
@ -810,22 +811,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"):
|
||||
|
|
@ -833,12 +857,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
|
||||
|
|
@ -855,15 +879,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"):
|
||||
|
|
@ -871,7 +895,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 = (
|
||||
|
|
@ -883,7 +907,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", []))):
|
||||
|
|
@ -893,10 +917,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:
|
||||
|
|
@ -923,7 +947,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
|
||||
|
|
@ -945,7 +969,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
|
||||
|
|
@ -959,7 +983,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"
|
||||
|
|
@ -977,18 +1001,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
|
||||
|
|
@ -1005,9 +1029,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
|
||||
|
|
@ -1028,13 +1052,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"""
|
||||
|
|
@ -3221,8 +3245,8 @@ def setup_ctf():
|
|||
|
||||
def create_claude_thinking_context(agent_name, counter, model):
|
||||
"""
|
||||
Create a streaming context for Claude thinking/reasoning display.
|
||||
This creates a dedicated panel that shows Claude's internal reasoning process.
|
||||
Create a streaming context for AI thinking/reasoning display.
|
||||
This creates a dedicated panel that shows the model's internal reasoning process.
|
||||
|
||||
Args:
|
||||
agent_name: The name of the agent
|
||||
|
|
@ -3254,10 +3278,19 @@ def create_claude_thinking_context(agent_name, counter, model):
|
|||
terminal_width, _ = shutil.get_terminal_size((100, 24))
|
||||
panel_width = min(terminal_width - 4, 120)
|
||||
|
||||
# Determine model type for display
|
||||
model_str = str(model).lower()
|
||||
if "claude" in model_str:
|
||||
model_display = "Claude"
|
||||
elif "deepseek" in model_str:
|
||||
model_display = "DeepSeek"
|
||||
else:
|
||||
model_display = "AI"
|
||||
|
||||
# Create the thinking panel header
|
||||
header = Text()
|
||||
header.append("🧠 ", style="bold yellow")
|
||||
header.append(f"Claude Reasoning [{counter}]", style="bold yellow")
|
||||
header.append(f"{model_display} Reasoning [{counter}]", style="bold yellow")
|
||||
header.append(f" | {agent_name}", style="bold cyan")
|
||||
header.append(f" | {timestamp}", style="dim")
|
||||
|
||||
|
|
@ -3267,7 +3300,7 @@ def create_claude_thinking_context(agent_name, counter, model):
|
|||
# Create the panel for thinking
|
||||
panel = Panel(
|
||||
Group(header, Text("\n"), thinking_content),
|
||||
title="[bold yellow]🧠 Thinking Process[/bold yellow]",
|
||||
title=f"[bold yellow]🧠 {model_display} Thinking Process[/bold yellow]",
|
||||
border_style="yellow",
|
||||
box=ROUNDED,
|
||||
padding=(1, 2),
|
||||
|
|
@ -3286,6 +3319,7 @@ def create_claude_thinking_context(agent_name, counter, model):
|
|||
"thinking_content": thinking_content,
|
||||
"timestamp": timestamp,
|
||||
"model": model,
|
||||
"model_display": model_display,
|
||||
"agent_name": agent_name,
|
||||
"panel_width": panel_width,
|
||||
"is_started": False,
|
||||
|
|
@ -3298,12 +3332,12 @@ def create_claude_thinking_context(agent_name, counter, model):
|
|||
return context
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating Claude thinking context: {e}")
|
||||
print(f"Error creating {model_display} thinking context: {e}")
|
||||
return None
|
||||
|
||||
def update_claude_thinking_content(context, thinking_delta):
|
||||
"""
|
||||
Update the Claude thinking content with new reasoning text.
|
||||
Update the AI thinking content with new reasoning text.
|
||||
|
||||
Args:
|
||||
context: The thinking context created by create_claude_thinking_context
|
||||
|
|
@ -3339,6 +3373,9 @@ def update_claude_thinking_content(context, thinking_delta):
|
|||
# For short thinking, use regular text with styling
|
||||
thinking_display = Text(thinking_text, style="white")
|
||||
|
||||
# Get model display name from context
|
||||
model_display = context.get("model_display", "AI")
|
||||
|
||||
# Update the panel content
|
||||
updated_panel = Panel(
|
||||
Group(
|
||||
|
|
@ -3346,7 +3383,7 @@ def update_claude_thinking_content(context, thinking_delta):
|
|||
Text("\n"),
|
||||
thinking_display
|
||||
),
|
||||
title="[bold yellow]🧠 Thinking Process[/bold yellow]",
|
||||
title=f"[bold yellow]🧠 {model_display} Thinking Process[/bold yellow]",
|
||||
border_style="yellow",
|
||||
box=ROUNDED,
|
||||
padding=(1, 2),
|
||||
|
|
@ -3360,7 +3397,8 @@ def update_claude_thinking_content(context, thinking_delta):
|
|||
context["live"].start()
|
||||
context["is_started"] = True
|
||||
except Exception as e:
|
||||
print(f"Error starting Claude thinking display: {e}")
|
||||
model_display = context.get("model_display", "AI")
|
||||
print(f"Error starting {model_display} thinking display: {e}")
|
||||
return False
|
||||
|
||||
# Update the live display
|
||||
|
|
@ -3371,12 +3409,13 @@ def update_claude_thinking_content(context, thinking_delta):
|
|||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error updating Claude thinking content: {e}")
|
||||
model_display = context.get("model_display", "AI")
|
||||
print(f"Error updating {model_display} thinking content: {e}")
|
||||
return False
|
||||
|
||||
def finish_claude_thinking_display(context):
|
||||
"""
|
||||
Finish the Claude thinking display session.
|
||||
Finish the AI thinking display session.
|
||||
|
||||
Args:
|
||||
context: The thinking context to finish
|
||||
|
|
@ -3395,10 +3434,13 @@ def finish_claude_thinking_display(context):
|
|||
from rich.syntax import Syntax
|
||||
from rich.console import Group
|
||||
|
||||
# Get model display name
|
||||
model_display = context.get("model_display", "AI")
|
||||
|
||||
# Add final formatting to show completion
|
||||
final_header = Text()
|
||||
final_header.append("🧠 ", style="bold green")
|
||||
final_header.append(f"Claude Reasoning Complete", style="bold green")
|
||||
final_header.append(f"{model_display} Reasoning Complete", style="bold green")
|
||||
final_header.append(f" | {context['agent_name']}", style="bold cyan")
|
||||
final_header.append(f" | {context['timestamp']}", style="dim")
|
||||
|
||||
|
|
@ -3424,7 +3466,7 @@ def finish_claude_thinking_display(context):
|
|||
Text("\n"),
|
||||
final_thinking_display
|
||||
),
|
||||
title="[bold green]🧠 Thinking Complete[/bold green]",
|
||||
title=f"[bold green]🧠 {model_display} Thinking Complete[/bold green]",
|
||||
border_style="green",
|
||||
box=ROUNDED,
|
||||
padding=(1, 2),
|
||||
|
|
@ -3446,13 +3488,14 @@ def finish_claude_thinking_display(context):
|
|||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error finishing Claude thinking display: {e}")
|
||||
model_display = context.get("model_display", "AI")
|
||||
print(f"Error finishing {model_display} thinking display: {e}")
|
||||
return False
|
||||
|
||||
def detect_claude_thinking_in_stream(model_name):
|
||||
"""
|
||||
Detect if a model should show thinking/reasoning display.
|
||||
Only applies to Claude models with reasoning capability.
|
||||
Applies to Claude and DeepSeek models with reasoning capability.
|
||||
|
||||
Args:
|
||||
model_name: The model name to check
|
||||
|
|
@ -3468,7 +3511,7 @@ def detect_claude_thinking_in_stream(model_name):
|
|||
# Check for Claude models with reasoning capability
|
||||
# Claude 4 models (like claude-sonnet-4-20250514) support reasoning
|
||||
# Also check for explicit "thinking" in model name
|
||||
has_reasoning = (
|
||||
has_claude_reasoning = (
|
||||
"claude" in model_str and (
|
||||
# Claude 4 models (sonnet-4, haiku-4, opus-4)
|
||||
"-4-" in model_str or
|
||||
|
|
@ -3481,11 +3524,23 @@ def detect_claude_thinking_in_stream(model_name):
|
|||
)
|
||||
)
|
||||
|
||||
return has_reasoning
|
||||
# Check for DeepSeek models with reasoning capability
|
||||
has_deepseek_reasoning = (
|
||||
"deepseek" in model_str and (
|
||||
# DeepSeek reasoner models
|
||||
"reasoner" in model_str or
|
||||
# DeepSeek chat models also support reasoning
|
||||
"chat" in model_str or
|
||||
# Generic deepseek models likely support it
|
||||
"/" in model_str # e.g., deepseek/deepseek-chat
|
||||
)
|
||||
)
|
||||
|
||||
return has_claude_reasoning or has_deepseek_reasoning
|
||||
|
||||
def print_claude_reasoning_simple(reasoning_content, agent_name, model_name):
|
||||
"""
|
||||
Print Claude reasoning content in simple mode (no Rich panels).
|
||||
Print AI reasoning content in simple mode (no Rich panels).
|
||||
Used when CAI_STREAM=False.
|
||||
|
||||
Args:
|
||||
|
|
@ -3496,16 +3551,26 @@ def print_claude_reasoning_simple(reasoning_content, agent_name, model_name):
|
|||
if not reasoning_content or not reasoning_content.strip():
|
||||
return
|
||||
|
||||
# Determine model type for display
|
||||
model_str = str(model_name).lower()
|
||||
if "claude" in model_str:
|
||||
model_display = "Claude"
|
||||
elif "deepseek" in model_str:
|
||||
model_display = "DeepSeek"
|
||||
else:
|
||||
model_display = "AI"
|
||||
|
||||
# Simple text output without Rich formatting
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
print(f"\n🧠 Reasoning | {agent_name} | {model_name} | {timestamp}")
|
||||
print(f"\n🧠 {model_display} Reasoning | {agent_name} | {model_name} | {timestamp}")
|
||||
print("=" * 60)
|
||||
print(reasoning_content)
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
def start_claude_thinking_if_applicable(model_name, agent_name, counter):
|
||||
"""
|
||||
Start Claude thinking display if the model supports it AND streaming is enabled.
|
||||
Start AI thinking display if the model supports it AND streaming is enabled.
|
||||
Supports Claude and DeepSeek models with reasoning capabilities.
|
||||
|
||||
Args:
|
||||
model_name: The model name
|
||||
|
|
|
|||
Loading…
Reference in New Issue