Skip to content

Conversation

@johnny0120
Copy link
Collaborator

@johnny0120 johnny0120 commented Dec 31, 2025

Implement frontend control for web search behavior in Chat mode with three selectable modes:

  • Auto: AI decides whether to search (default behavior)
  • On: Force search with specified engine
  • Off: Disable search completely

Changes include:

  • Extended SearchEngineSelector component with three-state mode
  • Added web search state management with localStorage persistence
  • Updated WebSocket payload to support force/disable search flags
  • Added force web search prompt injection in backend
  • Implemented search tool registration logic based on mode
  • Added i18n translations for search modes (zh-CN and en)

The feature is only visible for Chat Shell bots when WEB_SEARCH_ENABLED is true in backend configuration.

Summary by CodeRabbit

  • New Features
    • Added web search mode selector with three options: auto (AI-determined), on (force search), and off (disable search)
    • Implemented search engine selection interface for configurable web search providers
    • Added UI controls and localization for web search configuration in English and Chinese

✏️ Tip: You can customize this high-level summary in your review settings.

qdaxb and others added 2 commits December 30, 2025 14:35
Implement frontend control for web search behavior in Chat mode with
three selectable modes:

- Auto: AI decides whether to search (default behavior)
- On: Force search with specified engine
- Off: Disable search completely

Changes include:
- Extended SearchEngineSelector component with three-state mode
- Added web search state management with localStorage persistence
- Updated WebSocket payload to support force/disable search flags
- Added force web search prompt injection in backend
- Implemented search tool registration logic based on mode
- Added i18n translations for search modes (zh-CN and en)

The feature is only visible for Chat Shell bots when WEB_SEARCH_ENABLED
is true in backend configuration.
@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

📝 Walkthrough

Walkthrough

This PR implements granular web search configuration controls across the full stack. It adds two new boolean flags (force_web_search and disable_web_search) to message payloads and agent configuration, redesigns the frontend web search mode selector from a toggle to a three-state model (auto/on/off), and integrates web search preferences with localStorage persistence throughout the chat workflow.

Changes

Cohort / File(s) Summary
Backend WebSocket/Payload Models
backend/app/api/ws/events.py, backend/app/services/chat/config/stream_config.py, frontend/src/types/socket.ts
Added force_web_search and disable_web_search boolean fields to ChatSendPayload and WebSocketStreamConfig to enable granular web search control per message.
Backend Agent/Config Integration
backend/app/chat_shell/agent.py, backend/app/services/chat/streaming/ws_handler.py
Extended AgentConfig with force_web_search field and updated ws_handler to conditionally add WebSearchTool (respecting disable_web_search) and forward config flags.
Backend Prompt Building
backend/app/chat_shell/prompts/builder.py
Added FORCE_WEB_SEARCH_PROMPT constant and append_force_web_search_prompt() function; updated build_system_prompt() signature to accept and conditionally apply force web search instructions. ⚠️ Note: Duplicate definitions detected in module.
Backend Streaming Pipeline
backend/app/services/chat/trigger/core.py
Propagated web search flags (force_web_search, disable_web_search) from ChatSendPayload into WebSocketStreamConfig during streaming initialization.
Frontend State Management
frontend/src/features/tasks/components/chat/useChatAreaState.ts, frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
Introduced WebSearchMode type and integrated web search state with localStorage persistence; threaded web search configuration (mode and engine selection) through message-sending handlers.
Frontend Chat Components
frontend/src/features/tasks/components/chat/ChatArea.tsx, frontend/src/features/tasks/components/input/ChatInputCard.tsx
Added web search state initialization (fetching enabled status and available engines) and propagated web search props through component hierarchy.
Frontend Web Search UI
frontend/src/features/tasks/components/input/ChatInputControls.tsx, frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx
Integrated SearchEngineSelector component into input controls; redesigned selector from boolean toggle to WebSearchMode dropdown (auto/on/off) with dynamic engine selection and mode-specific UI presentation.
Frontend Types & Context
frontend/src/features/tasks/contexts/chatStreamContext.tsx
Extended ChatMessageRequest and ChatSendPayload with force_web_search and disable_web_search optional fields; propagated flags through sendMessage flow.
Localization
frontend/src/i18n/locales/en/chat.json, frontend/src/i18n/locales/zh-CN/chat.json
Added translations for new web search mode options: select_mode, mode_auto (with description), mode_on (with description), mode_off (with description) in English and Chinese.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • qdaxb
  • feifei325

Poem

🐰 A rabbit hops through web search modes
Auto, on, and off it goes
Force a search or disable it flat
Backend config, frontend chat
Search preferences, localStorage'd and free
Web knowledge at the click, you see! 🌐

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a web search mode toggle feature for Chat Shell, which aligns with the core functionality introduced across frontend and backend.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
backend/app/services/chat/config/stream_config.py (1)

58-59: Add validation for mutually exclusive flags.

Similar to the frontend, force_web_search and disable_web_search are mutually exclusive but not enforced. When both are True, the behavior is ambiguous. Consider adding a validation method to the dataclass:

🔎 Proposed validation
 @dataclass
 class WebSocketStreamConfig:
     """Configuration for WebSocket streaming.
 
     Attributes:
         task_id: Task ID for the chat session
         subtask_id: Assistant subtask ID
         task_room: WebSocket room name for broadcasting
 
         user_id: User ID for permission checks and history loading
         user_name: User name for group chat message prefix
         is_group_chat: Whether this is a group chat (affects message prefix and history truncation)
 
         message_id: Assistant's message_id for frontend ordering
         user_message_id: User's message_id for history exclusion (prevents duplicate messages)
 
         enable_web_search: Enable web search tool
         search_engine: Specific search engine to use
 
         bot_name: Bot name for MCP server loading
         bot_namespace: Bot namespace
         shell_type: Shell type (Chat, ClaudeCode, Agno) for frontend display
         extra_tools: Additional tools (e.g., KnowledgeBaseTool)
     """
 
     # Task identification
     task_id: int
     subtask_id: int
     task_room: str
 
     # User context
     user_id: int
     user_name: str
     is_group_chat: bool = False
 
     # Message ordering context
     message_id: int | None = None  # Assistant's message_id for ordering in frontend
     user_message_id: int | None = None  # User's message_id for history exclusion
 
     # Feature flags
     enable_tools: bool = True  # Enable tools (MCP, web search, skills, etc.)
     enable_web_search: bool = False
     search_engine: str | None = None
     force_web_search: bool = False  # Force AI to perform web search at least once
     disable_web_search: bool = False  # Completely disable web search tool
 
     # Prompt enhancement options
     enable_clarification: bool = False
     enable_deep_thinking: bool = True
     skills: list[dict] = field(
         default_factory=list
     )  # Skill metadata for prompt injection
 
     # Bot configuration
     bot_name: str = ""
     bot_namespace: str = "default"
     shell_type: str = "Chat"  # Shell type for frontend display
     extra_tools: list[BaseTool] = field(default_factory=list)
 
+    def __post_init__(self):
+        """Validate that mutually exclusive flags are not both set."""
+        if self.force_web_search and self.disable_web_search:
+            raise ValueError(
+                "force_web_search and disable_web_search cannot both be True"
+            )
+
     def get_username_for_message(self) -> str | None:
         """Get username for message prefix in group chat mode."""
         return self.user_name if self.is_group_chat else None
backend/app/api/ws/events.py (1)

106-113: Consider adding validation for mutually exclusive flags.

The force_web_search and disable_web_search flags are semantically mutually exclusive—enabling both simultaneously would be contradictory. While the frontend appears to handle this correctly, consider adding a Pydantic model_validator to enforce this constraint at the API boundary.

🔎 Optional: Add model validator
+from pydantic import BaseModel, Field, model_validator
+
 class ChatSendPayload(BaseModel):
     """Payload for chat:send event."""
     # ... existing fields ...
     force_web_search: bool = Field(
         False, description="Force AI to perform web search at least once"
     )
     disable_web_search: bool = Field(
         False, description="Completely disable web search tool"
     )
+
+    @model_validator(mode='after')
+    def validate_web_search_flags(self) -> 'ChatSendPayload':
+        if self.force_web_search and self.disable_web_search:
+            raise ValueError("Cannot both force and disable web search")
+        return self
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38f4c24 and da7bae1.

📒 Files selected for processing (16)
  • backend/app/api/ws/events.py
  • backend/app/chat_shell/agent.py
  • backend/app/chat_shell/prompts/builder.py
  • backend/app/services/chat/config/stream_config.py
  • backend/app/services/chat/streaming/ws_handler.py
  • backend/app/services/chat/trigger/core.py
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/chat/useChatAreaState.ts
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • frontend/src/features/tasks/components/input/ChatInputControls.tsx
  • frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx
  • frontend/src/features/tasks/contexts/chatStreamContext.tsx
  • frontend/src/i18n/locales/en/chat.json
  • frontend/src/i18n/locales/zh-CN/chat.json
  • frontend/src/types/socket.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{py,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{py,ts,tsx,js,jsx}: All code comments MUST be written in English
File size limit: If a file exceeds 1000 lines, split it into multiple sub-modules
Max 50 lines per function (preferred)
Extract common logic into shared utilities to avoid duplication

Files:

  • frontend/src/features/tasks/contexts/chatStreamContext.tsx
  • frontend/src/types/socket.ts
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • backend/app/services/chat/config/stream_config.py
  • backend/app/services/chat/streaming/ws_handler.py
  • frontend/src/features/tasks/components/chat/useChatAreaState.ts
  • backend/app/api/ws/events.py
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
  • frontend/src/features/tasks/components/input/ChatInputControls.tsx
  • frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx
  • backend/app/chat_shell/prompts/builder.py
  • backend/app/services/chat/trigger/core.py
  • backend/app/chat_shell/agent.py
frontend/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

frontend/src/**/*.{ts,tsx,js,jsx}: TypeScript/React frontend code must use TypeScript strict mode, functional components, Prettier formatting, ESLint, single quotes, and no semicolons
Use const over let, never var in TypeScript/React code

Files:

  • frontend/src/features/tasks/contexts/chatStreamContext.tsx
  • frontend/src/types/socket.ts
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • frontend/src/features/tasks/components/chat/useChatAreaState.ts
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
  • frontend/src/features/tasks/components/input/ChatInputControls.tsx
  • frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx
frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

frontend/src/**/*.{ts,tsx}: Extract reusable logic when implementing similar UI patterns multiple times; avoid duplication through composition over copy-paste
Use messages from useUnifiedMessages as the single source of truth for displayed messages; never use selectedTaskDetail.subtasks for display/export
Always import i18n translations from @/hooks/useTranslation, not from react-i18next
Use single namespace matching your feature when calling useTranslation() (e.g., useTranslation('groups') for groups feature); never use array with common first
Within current i18n namespace use t('key.subkey') format; from other namespace use t('namespace:key.subkey') format
Use responsive breakpoints with useIsMobile() for max-width 767px and useIsDesktop() for min-width 1024px

Files:

  • frontend/src/features/tasks/contexts/chatStreamContext.tsx
  • frontend/src/types/socket.ts
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • frontend/src/features/tasks/components/chat/useChatAreaState.ts
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
  • frontend/src/features/tasks/components/input/ChatInputControls.tsx
  • frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx
frontend/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Frontend: Only use NEXT_PUBLIC_* environment variables for client-safe values

Files:

  • frontend/src/features/tasks/contexts/chatStreamContext.tsx
  • frontend/src/types/socket.ts
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • frontend/src/features/tasks/components/chat/useChatAreaState.ts
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
  • frontend/src/features/tasks/components/input/ChatInputControls.tsx
  • frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx
frontend/src/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

frontend/src/**/*.{tsx,jsx}: Tailwind CSS color system usage: use bg-base for page background, text-text-primary for primary text, bg-surface for cards/panels
Tailwind CSS typography: Use text-xl font-semibold for H1, text-lg font-semibold for H2, text-sm for body text, text-xs text-text-muted for small text

Files:

  • frontend/src/features/tasks/contexts/chatStreamContext.tsx
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
  • frontend/src/features/tasks/components/input/ChatInputControls.tsx
  • frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx
frontend/src/types/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript types should be organized in src/types/

Files:

  • frontend/src/types/socket.ts
frontend/src/i18n/locales/**/*.{json,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Add new i18n translation keys to the appropriate namespace file in src/i18n/locales/{lang}/

Files:

  • frontend/src/i18n/locales/en/chat.json
  • frontend/src/i18n/locales/zh-CN/chat.json
backend/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

backend/**/*.py: Python backend code must follow PEP 8 standard with Black formatter (line length: 88) and isort
For Task/Workspace CRDs use TaskResource model from app.models.task; for other CRDs (Ghost, Model, Shell, Bot, Team, Skill) use Kind model from app.models.kind
Backend encrypts Git tokens and API keys using AES-256-CBC
Web search feature: Configure with WEB_SEARCH_ENABLED (default false), WEB_SEARCH_ENGINES JSON config, and WEB_SEARCH_DEFAULT_MAX_RESULTS (default 100)
MCP server configuration: Use CHAT_MCP_ENABLED to enable/disable MCP tools in Chat Shell mode, configure with CHAT_MCP_SERVERS JSON supporting ${{path}} variable substitution (user.name, user.id)

Files:

  • backend/app/services/chat/config/stream_config.py
  • backend/app/services/chat/streaming/ws_handler.py
  • backend/app/api/ws/events.py
  • backend/app/chat_shell/prompts/builder.py
  • backend/app/services/chat/trigger/core.py
  • backend/app/chat_shell/agent.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Python code requires type hints
Python code must include descriptive names and docstrings for public functions/classes
Extract magic numbers to constants in Python code

Files:

  • backend/app/services/chat/config/stream_config.py
  • backend/app/services/chat/streaming/ws_handler.py
  • backend/app/api/ws/events.py
  • backend/app/chat_shell/prompts/builder.py
  • backend/app/services/chat/trigger/core.py
  • backend/app/chat_shell/agent.py
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T10:42:55.732Z
Learning: Applies to backend/**/*.py : Web search feature: Configure with `WEB_SEARCH_ENABLED` (default false), `WEB_SEARCH_ENGINES` JSON config, and `WEB_SEARCH_DEFAULT_MAX_RESULTS` (default 100)
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T02:09:09.776Z
Learning: Applies to backend/app/services/search/**/*.py : Backend MUST support web search feature configuration via `WEB_SEARCH_*` environment variables with adapter configuration
📚 Learning: 2025-12-29T10:42:55.732Z
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T10:42:55.732Z
Learning: Applies to frontend/src/**/*.{ts,tsx} : Use `messages` from `useUnifiedMessages` as the single source of truth for displayed messages; never use `selectedTaskDetail.subtasks` for display/export

Applied to files:

  • frontend/src/features/tasks/contexts/chatStreamContext.tsx
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/chat/useChatAreaState.ts
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
📚 Learning: 2025-12-29T10:42:55.732Z
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T10:42:55.732Z
Learning: Applies to backend/**/*.py : Web search feature: Configure with `WEB_SEARCH_ENABLED` (default false), `WEB_SEARCH_ENGINES` JSON config, and `WEB_SEARCH_DEFAULT_MAX_RESULTS` (default 100)

Applied to files:

  • frontend/src/types/socket.ts
  • frontend/src/i18n/locales/en/chat.json
  • backend/app/services/chat/config/stream_config.py
  • backend/app/services/chat/streaming/ws_handler.py
  • backend/app/api/ws/events.py
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
📚 Learning: 2025-12-18T02:09:09.776Z
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T02:09:09.776Z
Learning: Applies to frontend/src/i18n/zh-CN.json : Frontend i18n Chinese values (zh-CN) MUST use UI terms: `'智能体列表'`, `'机器人配置'`

Applied to files:

  • frontend/src/i18n/locales/en/chat.json
  • frontend/src/i18n/locales/zh-CN/chat.json
📚 Learning: 2025-12-18T02:09:09.776Z
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T02:09:09.776Z
Learning: Applies to frontend/src/**/*.tsx : Frontend component files named with CRD terms: team-list.tsx, bot-form.tsx, model-selector.tsx

Applied to files:

  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/input/ChatInputCard.tsx
  • frontend/src/features/tasks/components/input/ChatInputControls.tsx
📚 Learning: 2025-12-29T10:42:55.732Z
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T10:42:55.732Z
Learning: Applies to frontend/src/**/*.{ts,tsx} : Always import i18n translations from `@/hooks/useTranslation`, not from `react-i18next`

Applied to files:

  • frontend/src/features/tasks/components/chat/ChatArea.tsx
📚 Learning: 2025-12-18T02:09:09.776Z
Learnt from: CR
Repo: wecode-ai/Wegent PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T02:09:09.776Z
Learning: Applies to backend/app/services/search/**/*.py : Backend MUST support web search feature configuration via `WEB_SEARCH_*` environment variables with adapter configuration

Applied to files:

  • backend/app/services/chat/config/stream_config.py
  • backend/app/services/chat/streaming/ws_handler.py
🧬 Code graph analysis (5)
frontend/src/features/tasks/contexts/chatStreamContext.tsx (1)
frontend/src/apis/client.ts (1)
  • request (57-121)
frontend/src/features/tasks/components/chat/ChatArea.tsx (1)
frontend/src/apis/chat.ts (2)
  • SearchEngine (295-298)
  • getSearchEngines (313-339)
frontend/src/features/tasks/components/chat/useChatAreaState.ts (1)
frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx (1)
  • WebSearchMode (29-29)
frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx (1)
frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx (1)
  • WebSearchMode (29-29)
frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx (5)
frontend/src/hooks/useTranslation.ts (1)
  • useTranslation (8-30)
frontend/src/components/ui/tooltip.tsx (4)
  • TooltipProvider (34-34)
  • Tooltip (34-34)
  • TooltipTrigger (34-34)
  • TooltipContent (34-34)
frontend/src/components/ui/button.tsx (1)
  • Button (56-56)
frontend/src/lib/utils.ts (1)
  • cn (8-10)
frontend/src/components/ui/dropdown.tsx (6)
  • DropdownMenu (178-178)
  • DropdownMenuTrigger (179-179)
  • DropdownMenuContent (180-180)
  • DropdownMenuLabel (184-184)
  • DropdownMenuItem (181-181)
  • DropdownMenuSeparator (185-185)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: E2E Tests (Shard 1/3)
  • GitHub Check: Test wegent CLI Integration
  • GitHub Check: E2E Tests (Shard 3/3)
  • GitHub Check: E2E Tests (Shard 2/3)
  • GitHub Check: Test Backend (3.11)
  • GitHub Check: Test Backend (3.10)
  • GitHub Check: Test Frontend
🔇 Additional comments (24)
frontend/src/i18n/locales/zh-CN/chat.json (1)

235-242: LGTM! Clear and consistent i18n additions.

The web search mode translations are well-structured with clear descriptions for each mode. The naming pattern is consistent, and the Chinese translations accurately convey the intended meaning.

backend/app/chat_shell/agent.py (2)

317-317: Correct propagation of force_web_search to prompt builder.

The flag is correctly passed to build_system_prompt for prompt enhancement.


53-53: Prompt builder correctly handles force_web_search flag.

The flag is properly passed from AgentConfig to build_system_prompt(), which uses it to conditionally append FORCE_WEB_SEARCH_PROMPT via the append_force_web_search_prompt() helper. The injected prompt text contains meaningful instructions ("MANDATORY Web Search Requirement") that direct the AI to use the web_search tool at least once before responding. Implementation is complete and correct.

backend/app/services/chat/streaming/ws_handler.py (2)

144-156: LGTM! Correct three-state web search logic.

The implementation correctly handles the three web search modes:

  1. Off (disable_web_search=True): Web search tool is not registered (line 145)
  2. Auto (disable_web_search=False, force_web_search=False): Web search tool is available for AI to use
  3. On (disable_web_search=False, force_web_search=True): Web search tool is available AND prompt forces usage

The condition settings.WEB_SEARCH_ENABLED and not config.disable_web_search correctly implements the tool registration logic for modes 2 and 3.


190-190: Correct propagation of force_web_search to AgentConfig.

The flag is properly forwarded from the WebSocket stream config to the agent configuration, enabling prompt-level control of web search behavior.

frontend/src/i18n/locales/en/chat.json (1)

235-242: LGTM! Clear and user-friendly English translations.

The web search mode translations are well-written with concise descriptions that clearly explain each mode's behavior. The translations align well with the Chinese versions and maintain consistency with the existing i18n patterns.

frontend/src/features/tasks/contexts/chatStreamContext.tsx (2)

185-188: Correct interface extension for web search control.

The ChatMessageRequest interface is properly extended with the two new optional boolean fields, maintaining consistency with the existing optional parameters pattern.

Note: The mutual exclusivity concern for these flags was already raised in the review of frontend/src/types/socket.ts. Consider addressing it at the interface level rather than in every usage site.


1161-1162: Correct payload construction with web search flags.

The implementation correctly extracts the web search control flags from the request and includes them in the WebSocket payload, following the established pattern for other optional fields.

backend/app/services/chat/trigger/core.py (1)

441-442: Correct flag propagation from payload to config.

The implementation correctly extracts and forwards the web search control flags from the validated payload to the WebSocket stream configuration. The ChatSendPayload Pydantic model validates both force_web_search and disable_web_search fields before the handler executes (via the @auto_task_context decorator), ensuring type safety and proper defaults. The flags are then correctly propagated to WebSocketStreamConfig which declares both fields.

frontend/src/features/tasks/components/input/ChatInputCard.tsx (1)

109-114: LGTM!

Web search props are correctly threaded through to ChatInputControls. The prop forwarding pattern is consistent with the existing code structure.

Also applies to: 223-228

frontend/src/features/tasks/components/chat/ChatArea.tsx (2)

70-88: LGTM!

The search engines configuration fetch is well-implemented with proper error handling that gracefully falls back to safe defaults. The separation between backend configuration (webSearchEnabled, searchEngines) and user preferences (webSearchMode, selectedSearchEngine from chatState) is clean.


361-367: LGTM!

Web search props are correctly assembled and passed to ChatInputCard. The comment clarifies the prop grouping.

frontend/src/features/tasks/components/chat/useChatAreaState.ts (2)

224-235: LGTM!

The localStorage persistence implementation is well-designed with proper SSR safety checks and validation of the saved mode value against the allowed types.


237-251: LGTM!

The setter callbacks correctly persist to localStorage with SSR guards. Using useCallback ensures stable function references.

frontend/src/features/tasks/components/input/ChatInputControls.tsx (1)

264-274: LGTM!

The SearchEngineSelector integration follows the established pattern for chat shell controls, with appropriate conditional rendering and disabled state handling.

backend/app/chat_shell/prompts/builder.py (2)

251-280: LGTM!

The FORCE_WEB_SEARCH_PROMPT and append_force_web_search_prompt function follow the established patterns for other prompt modifiers in this module. The prompt text is clear and imperative about the mandatory search requirement.


303-344: LGTM!

The build_system_prompt function is cleanly extended with the force_web_search parameter, maintaining consistency with other optional prompt enhancements.

frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx (2)

536-567: LGTM!

Dependencies are correctly updated to include webSearchMode and selectedSearchEngine, ensuring the callback refreshes when web search preferences change.


470-477: Remove this concern—the flags serve different purposes.

enable_web_search and force_web_search are not redundant. Backend analysis shows they control different aspects: enable_web_search determines whether the WebSearchTool is registered at all (line 106 of backend/app/chat_shell/agent.py), while force_web_search instructs the AI to perform a search by injecting special instructions into the system prompt (lines 267–280 of backend/app/chat_shell/prompts/builder.py). Both being true when webSearchMode === 'on' is correct—the tool must be available and the AI must be explicitly told to use it.

frontend/src/features/tasks/components/selector/SearchEngineSelector.tsx (5)

23-29: LGTM!

The WebSearchMode type is well-documented and exported for use across the codebase. The three-state model (auto/on/off) provides clear semantic meaning.


53-57: Verify: potential re-render loop in useEffect.

This useEffect calls onSelectEngine when selectedEngine is null and engines exist. If onSelectEngine is not memoized in the parent component, this could cause re-renders. The parent (useChatAreaState) uses useCallback for setSelectedSearchEngine, so this should be safe, but worth noting.


63-88: LGTM!

The getModeInfo helper cleanly encapsulates mode-specific UI configuration. The icon, label, description, and color mappings are consistent with the design intent.


96-102: LGTM!

Auto-setting mode to 'on' when an engine is selected is good UX—it avoids the confusing state where an engine is selected but mode is still 'auto' or 'off'.


131-203: LGTM!

The dropdown UI is well-structured with clear visual hierarchy. Mode selection is separated from engine selection, and the check marks provide clear feedback on current selections.

Comment on lines +86 to +87
force_web_search?: boolean; // Force AI to perform web search
disable_web_search?: boolean; // Completely disable web search tool
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider documenting precedence or adding validation for mutually exclusive flags.

The force_web_search and disable_web_search flags are logically mutually exclusive (you cannot both force and disable web search). However, the type system allows both to be true simultaneously. Consider either:

  1. Adding JSDoc comments to document which flag takes precedence if both are set
  2. Adding runtime validation in the frontend before sending the payload
  3. Using a discriminated union type to enforce mutual exclusivity at compile time

For example, a more type-safe approach could use a single webSearchMode field:

web_search_mode?: 'auto' | 'force' | 'disable';
🔎 Alternative type-safe approach
-  force_web_search?: boolean; // Force AI to perform web search
-  disable_web_search?: boolean; // Completely disable web search tool
+  /** Web search mode: 'auto' (default), 'force', or 'disable' */
+  web_search_mode?: 'auto' | 'force' | 'disable';

This would require updating the backend to accept this field instead of two separate booleans.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants