Files
deer-flow/src/server/chat_request.py

131 lines
4.9 KiB
Python
Raw Normal View History

2025-04-17 11:34:42 +08:00
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
# SPDX-License-Identifier: MIT
2025-04-19 22:11:41 +08:00
from typing import List, Optional, Union
2025-04-13 21:14:31 +08:00
from pydantic import BaseModel, Field
from src.config.report_style import ReportStyle
from src.rag.retriever import Resource
2025-04-13 21:14:31 +08:00
class ContentItem(BaseModel):
type: str = Field(..., description="The type of content (text, image, etc.)")
text: Optional[str] = Field(None, description="The text content if type is 'text'")
image_url: Optional[str] = Field(
None, description="The image URL if type is 'image'"
)
class ChatMessage(BaseModel):
role: str = Field(
..., description="The role of the message sender (user or assistant)"
)
content: Union[str, List[ContentItem]] = Field(
...,
description="The content of the message, either a string or a list of content items",
)
class ChatRequest(BaseModel):
2025-04-15 16:36:02 +08:00
messages: Optional[List[ChatMessage]] = Field(
[], description="History of messages between the user and the assistant"
2025-04-13 21:14:31 +08:00
)
resources: Optional[List[Resource]] = Field(
[], description="Resources to be used for the research"
)
2025-04-13 21:14:31 +08:00
debug: Optional[bool] = Field(False, description="Whether to enable debug logging")
thread_id: Optional[str] = Field(
"__default__", description="A specific conversation identifier"
)
locale: Optional[str] = Field(
"en-US", description="Language locale for the conversation (e.g., en-US, zh-CN)"
)
2025-04-13 21:14:31 +08:00
max_plan_iterations: Optional[int] = Field(
1, description="The maximum number of plan iterations"
)
max_step_num: Optional[int] = Field(
3, description="The maximum number of steps in a plan"
)
max_search_results: Optional[int] = Field(
3, description="The maximum number of search results"
)
2025-04-14 18:01:50 +08:00
auto_accepted_plan: Optional[bool] = Field(
False, description="Whether to automatically accept the plan"
)
2025-04-15 16:36:02 +08:00
interrupt_feedback: Optional[str] = Field(
None, description="Interrupt feedback from the user on the plan"
2025-04-14 18:01:50 +08:00
)
2025-04-23 16:00:01 +08:00
mcp_settings: Optional[dict] = Field(
None, description="MCP settings for the chat request"
)
enable_background_investigation: Optional[bool] = Field(
True, description="Whether to get background investigation before plan"
)
enable_web_search: Optional[bool] = Field(
True, description="Whether to enable web search, set to False to use only local RAG"
)
report_style: Optional[ReportStyle] = Field(
ReportStyle.ACADEMIC, description="The style of the report"
)
enable_deep_thinking: Optional[bool] = Field(
False, description="Whether to enable deep thinking"
)
feat: Add intelligent clarification feature in coordinate step for research queries (#613) * fix: support local models by making thought field optional in Plan model - Make thought field optional in Plan model to fix Pydantic validation errors with local models - Add Ollama configuration example to conf.yaml.example - Update documentation to include local model support - Improve planner prompt with better JSON format requirements Fixes local model integration issues where models like qwen3:14b would fail due to missing thought field in JSON output. * feat: Add intelligent clarification feature for research queries - Add multi-turn clarification process to refine vague research questions - Implement three-dimension clarification standard (Tech/App, Focus, Scope) - Add clarification state management in coordinator node - Update coordinator prompt with detailed clarification guidelines - Add UI settings to enable/disable clarification feature (disabled by default) - Update workflow to handle clarification rounds recursively - Add comprehensive test coverage for clarification functionality - Update documentation with clarification feature usage guide Key components: - src/graph/nodes.py: Core clarification logic and state management - src/prompts/coordinator.md: Detailed clarification guidelines - src/workflow.py: Recursive clarification handling - web/: UI settings integration - tests/: Comprehensive test coverage - docs/: Updated configuration guide * fix: Improve clarification conversation continuity - Add comprehensive conversation history to clarification context - Include previous exchanges summary in system messages - Add explicit guidelines for continuing rounds in coordinator prompt - Prevent LLM from starting new topics during clarification - Ensure topic continuity across clarification rounds Fixes issue where LLM would restart clarification instead of building upon previous exchanges. * fix: Add conversation history to clarification context * fix: resolve clarification feature message to planer, prompt, test issues - Optimize coordinator.md prompt template for better clarification flow - Simplify final message sent to planner after clarification - Fix API key assertion issues in test_search.py * fix: Add configurable max_clarification_rounds and comprehensive tests - Add max_clarification_rounds parameter for external configuration - Add comprehensive test cases for clarification feature in test_app.py - Fixes issues found during interactive mode testing where: - Recursive call failed due to missing initial_state parameter - Clarification exited prematurely at max rounds - Incorrect logging of max rounds reached * Move clarification tests to test_nodes.py and add max_clarification_rounds to zh.json
2025-10-13 22:35:57 -07:00
enable_clarification: Optional[bool] = Field(
None,
description="Whether to enable multi-turn clarification (default: None, uses State default=False)",
)
max_clarification_rounds: Optional[int] = Field(
None,
description="Maximum number of clarification rounds (default: None, uses State default=3)",
)
feat: implement tool-specific interrupts for create_react_agent (#572) (#659) * feat: implement tool-specific interrupts for create_react_agent (#572) Add selective tool interrupt capability allowing interrupts before specific tools rather than all tools. Users can now configure which tools trigger interrupts via the interrupt_before_tools parameter. Changes: - Create ToolInterceptor class to handle tool-specific interrupt logic - Add interrupt_before_tools parameter to create_agent() function - Extend Configuration with interrupt_before_tools field - Add interrupt_before_tools to ChatRequest API - Update nodes.py to pass interrupt configuration to agents - Update app.py workflow to support tool interrupt configuration - Add comprehensive unit tests for tool interceptor Features: - Selective tool interrupts: interrupt only specific tools by name - Approval keywords: recognize user approval (approved, proceed, accept, etc.) - Backward compatible: optional parameter, existing code unaffected - Flexible: works with default tools and MCP-powered tools - Works with existing resume mechanism for seamless workflow Example usage: request = ChatRequest( messages=[...], interrupt_before_tools=['db_tool', 'sensitive_api'] ) * test: add comprehensive integration tests for tool-specific interrupts (#572) Add 24 integration tests covering all aspects of the tool interceptor feature: Test Coverage: - Agent creation with tool interrupts - Configuration support (with/without interrupts) - ChatRequest API integration - Multiple tools with selective interrupts - User approval/rejection flows - Tool wrapping and functionality preservation - Error handling and edge cases - Approval keyword recognition - Complex tool inputs - Logging and monitoring All tests pass with 100% coverage of tool interceptor functionality. Tests verify: ✓ Selective tool interrupts work correctly ✓ Only specified tools trigger interrupts ✓ Non-matching tools execute normally ✓ User feedback is properly parsed ✓ Tool functionality is preserved after wrapping ✓ Error handling works as expected ✓ Configuration options are properly respected ✓ Logging provides useful debugging info * fix: mock get_llm_by_type in agent creation test Fix test_agent_creation_with_tool_interrupts which was failing because get_llm_by_type() was being called before create_react_agent was mocked. Changes: - Add mock for get_llm_by_type in test - Use context manager composition for multiple patches - Test now passes and validates tool wrapping correctly All 24 integration tests now pass successfully. * refactor: use mock assertion methods for consistent and clearer error messages Update integration tests to use mock assertion methods instead of direct attribute checking for consistency and clearer error messages: Changes: - Replace 'assert mock_interrupt.called' with 'mock_interrupt.assert_called()' - Replace 'assert not mock_interrupt.called' with 'mock_interrupt.assert_not_called()' Benefits: - Consistent with pytest-mock and unittest.mock best practices - Clearer error messages when assertions fail - Better IDE autocompletion support - More professional test code All 42 tests pass with improved assertion patterns. * refactor: use default_factory for interrupt_before_tools consistency Improve consistency between ChatRequest and Configuration implementations: Changes: - ChatRequest.interrupt_before_tools: Use Field(default_factory=list) instead of Optional[None] - Remove unnecessary 'or []' conversion in app.py line 505 - Aligns with Configuration.interrupt_before_tools implementation pattern - No functional changes - all tests still pass Benefits: - Consistent field definition across codebase - Simpler and cleaner code - Reduced chance of None/empty list bugs - Better alignment with Pydantic best practices All 42 tests passing. * refactor: improve tool input formatting in interrupt messages Enhance tool input representation for better readability in interrupt messages: Changes: - Add json import for better formatting - Create _format_tool_input() static method with JSON serialization - Use JSON formatting for dicts, lists, tuples with indent=2 - Fall back to str() for non-serializable types - Handle None input specially (returns 'No input') - Improve interrupt message formatting with better spacing Benefits: - Complex tool inputs now display as readable JSON - Nested structures are properly indented and visible - Better user experience when reviewing tool inputs before approval - Handles edge cases gracefully with fallbacks - Improved logging output for debugging Example improvements: Before: {'query': 'SELECT...', 'limit': 10, 'nested': {'key': 'value'}} After: { "query": "SELECT...", "limit": 10, "nested": { "key": "value" } } All 42 tests still passing. * test: add comprehensive unit tests for tool input formatting
2025-10-26 09:47:03 +08:00
interrupt_before_tools: List[str] = Field(
default_factory=list,
description="List of tool names to interrupt before execution (e.g., ['db_tool', 'api_tool'])",
)
class TTSRequest(BaseModel):
text: str = Field(..., description="The text to convert to speech")
voice_type: Optional[str] = Field(
"BV700_V2_streaming", description="The voice type to use"
)
encoding: Optional[str] = Field("mp3", description="The audio encoding format")
speed_ratio: Optional[float] = Field(1.0, description="Speech speed ratio")
volume_ratio: Optional[float] = Field(1.0, description="Speech volume ratio")
pitch_ratio: Optional[float] = Field(1.0, description="Speech pitch ratio")
text_type: Optional[str] = Field("plain", description="Text type (plain or ssml)")
with_frontend: Optional[int] = Field(
1, description="Whether to use frontend processing"
)
frontend_type: Optional[str] = Field("unitTson", description="Frontend type")
2025-04-19 22:11:41 +08:00
class GeneratePodcastRequest(BaseModel):
content: str = Field(..., description="The content of the podcast")
2025-04-21 16:43:06 +08:00
class GeneratePPTRequest(BaseModel):
content: str = Field(..., description="The content of the ppt")
locale: str = Field(
"en-US", description="Language locale for the conversation (e.g., en-US, zh-CN)"
)
2025-04-26 23:12:13 +08:00
class GenerateProseRequest(BaseModel):
prompt: str = Field(..., description="The content of the prose")
option: str = Field(..., description="The option of the prose writer")
command: Optional[str] = Field(
"", description="The user custom command of the prose writer"
)
class EnhancePromptRequest(BaseModel):
prompt: str = Field(..., description="The original prompt to enhance")
context: Optional[str] = Field(
"", description="Additional context about the intended use"
)
report_style: Optional[str] = Field(
"academic", description="The style of the report"
)