diff --git a/src/cai/sdk/agents/models/openai_chatcompletions.py b/src/cai/sdk/agents/models/openai_chatcompletions.py index 0df96012..9d9bc01a 100644 --- a/src/cai/sdk/agents/models/openai_chatcompletions.py +++ b/src/cai/sdk/agents/models/openai_chatcompletions.py @@ -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"] = "high" 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"] = "high" 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 diff --git a/src/cai/util.py b/src/cai/util.py index ffa87576..04e54f5e 100644 --- a/src/cai/util.py +++ b/src/cai/util.py @@ -3165,8 +3165,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 @@ -3198,10 +3198,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") @@ -3211,7 +3220,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), @@ -3230,6 +3239,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, @@ -3242,12 +3252,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 @@ -3283,6 +3293,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( @@ -3290,7 +3303,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), @@ -3304,7 +3317,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 @@ -3315,12 +3329,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 @@ -3339,10 +3354,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") @@ -3368,7 +3386,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), @@ -3390,13 +3408,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 @@ -3412,7 +3431,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 @@ -3425,11 +3444,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: @@ -3440,16 +3471,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