diff --git a/api/app/controllers/public_share_controller.py b/api/app/controllers/public_share_controller.py index 9435fc9b..9f5f8075 100644 --- a/api/app/controllers/public_share_controller.py +++ b/api/app/controllers/public_share_controller.py @@ -580,6 +580,7 @@ async def chat( conversation_id=conversation.id, # 使用已创建的会话 ID user_id=end_user_id, # 转换为字符串 variables=payload.variables, + files=payload.files, config=config, web_search=payload.web_search, memory=payload.memory, diff --git a/api/app/controllers/service/app_api_controller.py b/api/app/controllers/service/app_api_controller.py index 27a929ba..bb71d831 100644 --- a/api/app/controllers/service/app_api_controller.py +++ b/api/app/controllers/service/app_api_controller.py @@ -12,7 +12,6 @@ from app.core.exceptions import BusinessException from app.core.logging_config import get_business_logger from app.core.response_utils import success from app.db import get_db -from app.dependencies import get_app_or_workspace from app.models.app_model import App from app.models.app_model import AppType from app.repositories import knowledge_repository @@ -21,9 +20,10 @@ from app.schemas import AppChatRequest, conversation_schema from app.schemas.api_key_schema import ApiKeyAuth from app.services import workspace_service from app.services.app_chat_service import AppChatService, get_app_chat_service -from app.services.conversation_service import ConversationService, get_conversation_service -from app.utils.app_config_utils import dict_to_multi_agent_config, workflow_config_4_app_release, agent_config_4_app_release, multi_agent_config_4_app_release from app.services.app_service import get_app_service, AppService +from app.services.conversation_service import ConversationService, get_conversation_service +from app.utils.app_config_utils import workflow_config_4_app_release, \ + agent_config_4_app_release, multi_agent_config_4_app_release router = APIRouter(prefix="/app", tags=["V1 - App API"]) logger = get_business_logger() @@ -34,6 +34,7 @@ async def list_apps(): """列出可访问的应用(占位)""" return success(data=[], msg="App API - Coming Soon") + # /v1/app/chat # @router.post("/chat") @@ -73,16 +74,17 @@ def _checkAppConfig(app: App): else: raise BusinessException("不支持的应用类型", BizCode.AGENT_CONFIG_MISSING) + @router.post("/chat") @require_api_key(scopes=["app"]) async def chat( - request:Request, - api_key_auth: ApiKeyAuth = None, - db: Session = Depends(get_db), - conversation_service: Annotated[ConversationService, Depends(get_conversation_service)] = None, - app_chat_service: Annotated[AppChatService, Depends(get_app_chat_service)] = None, - app_service: Annotated[AppService, Depends(get_app_service)] = None, - message: str = Body(..., description="聊天消息内容"), + request: Request, + api_key_auth: ApiKeyAuth = None, + db: Session = Depends(get_db), + conversation_service: Annotated[ConversationService, Depends(get_conversation_service)] = None, + app_chat_service: Annotated[AppChatService, Depends(get_app_chat_service)] = None, + app_service: Annotated[AppService, Depends(get_app_service)] = None, + message: str = Body(..., description="聊天消息内容"), ): body = await request.json() payload = AppChatRequest(**body) @@ -98,8 +100,8 @@ async def chat( original_user_id=other_id # Save original user_id to other_id ) end_user_id = str(new_end_user.id) - web_search=True - memory=True + web_search = True + memory = True # 提前验证和准备(在流式响应开始前完成) storage_type = workspace_service.get_workspace_storage_type_without_auth( db=db, @@ -146,17 +148,17 @@ async def chat( if payload.stream: async def event_generator(): async for event in app_chat_service.agnet_chat_stream( - message=payload.message, - conversation_id=conversation.id, # 使用已创建的会话 ID - user_id= end_user_id, # 转换为字符串 - variables=payload.variables, - web_search=web_search, - config=agent_config, - memory=memory, - storage_type=storage_type, - user_rag_memory_id=user_rag_memory_id, - workspace_id=workspace_id, - files=payload.files # 传递多模态文件 + message=payload.message, + conversation_id=conversation.id, # 使用已创建的会话 ID + user_id=end_user_id, # 转换为字符串 + variables=payload.variables, + web_search=web_search, + config=agent_config, + memory=memory, + storage_type=storage_type, + user_rag_memory_id=user_rag_memory_id, + workspace_id=workspace_id, + files=payload.files # 传递多模态文件 ): yield event @@ -176,7 +178,7 @@ async def chat( conversation_id=conversation.id, # 使用已创建的会话 ID user_id=end_user_id, # 转换为字符串 variables=payload.variables, - config= agent_config, + config=agent_config, web_search=web_search, memory=memory, storage_type=storage_type, @@ -192,15 +194,15 @@ async def chat( async def event_generator(): async for event in app_chat_service.multi_agent_chat_stream( - message=payload.message, - conversation_id=conversation.id, # 使用已创建的会话 ID - user_id=end_user_id, # 转换为字符串 - variables=payload.variables, - config=config, - web_search=web_search, - memory=memory, - storage_type=storage_type, - user_rag_memory_id=user_rag_memory_id + message=payload.message, + conversation_id=conversation.id, # 使用已创建的会话 ID + user_id=end_user_id, # 转换为字符串 + variables=payload.variables, + config=config, + web_search=web_search, + memory=memory, + storage_type=storage_type, + user_rag_memory_id=user_rag_memory_id ): yield event @@ -234,19 +236,19 @@ async def chat( if payload.stream: async def event_generator(): async for event in app_chat_service.workflow_chat_stream( - - message=payload.message, - conversation_id=conversation.id, # 使用已创建的会话 ID - user_id=end_user_id, # 转换为字符串 - variables=payload.variables, - config=config, - web_search=web_search, - memory=memory, - storage_type=storage_type, - user_rag_memory_id=user_rag_memory_id, - app_id=app.id, - workspace_id=workspace_id, - release_id=app.current_release.id, + message=payload.message, + conversation_id=conversation.id, # 使用已创建的会话 ID + user_id=end_user_id, # 转换为字符串 + variables=payload.variables, + files=payload.files, + config=config, + web_search=web_search, + memory=memory, + storage_type=storage_type, + user_rag_memory_id=user_rag_memory_id, + app_id=app.id, + workspace_id=workspace_id, + release_id=app.current_release.id, ): event_type = event.get("event", "message") event_data = event.get("data", {}) @@ -296,4 +298,3 @@ async def chat( from app.core.exceptions import BusinessException from app.core.error_codes import BizCode raise BusinessException(f"不支持的应用类型: {app_type}", BizCode.APP_TYPE_NOT_SUPPORTED) - diff --git a/api/app/schemas/app_schema.py b/api/app/schemas/app_schema.py index 2f94b69d..480179cf 100644 --- a/api/app/schemas/app_schema.py +++ b/api/app/schemas/app_schema.py @@ -15,6 +15,13 @@ class FileType(StrEnum): AUDIO = "audio" VIDEO = "video" + @classmethod + def trans(cls, value: str) -> 'FileType': + if value.startswith("image"): + return cls.IMAGE + # TODO: other file type support + raise RuntimeError("Unsupport file type") + class TransferMethod(str, Enum): """文件传输方式枚举""" @@ -28,7 +35,16 @@ class FileInput(BaseModel): transfer_method: TransferMethod = Field(..., description="传输方式: local_file/remote_url") upload_file_id: Optional[uuid.UUID] = Field(None, description="已上传文件ID(local_file时必填)") url: Optional[str] = Field(None, description="远程URL(remote_url时必填)") - + + @field_validator("type", mode="before") + @classmethod + def validate_type(cls, v): + """验证文件类型""" + try: + return FileType.trans(v) + except ValueError: + raise ValueError(f"无效的文件类型: {v}") + @field_validator("upload_file_id") @classmethod def validate_local_file(cls, v, info): @@ -36,7 +52,7 @@ class FileInput(BaseModel): if info.data.get("transfer_method") == TransferMethod.LOCAL_FILE and not v: raise ValueError("transfer_method 为 local_file 时,upload_file_id 不能为空") return v - + @field_validator("url") @classmethod def validate_remote_url(cls, v, info): @@ -82,6 +98,7 @@ class ToolConfig(BaseModel): tool_id: Optional[str] = Field(default=None, description="工具ID") operation: Optional[str] = Field(default=None, description="工具特定配置") + class SkillConfig(BaseModel): """技能配置""" enabled: bool = Field(default=True, description="是否启用该技能") @@ -216,7 +233,7 @@ class AgentConfigUpdate(BaseModel): # 工具配置 tools: Optional[List[ToolConfig]] = Field(default_factory=list, description="工具列表") - + # 技能配置 skills: Optional[SkillConfig] = Field(default=dict, description="关联的技能列表") diff --git a/api/app/services/app_chat_service.py b/api/app/services/app_chat_service.py index 5e989150..9723121d 100644 --- a/api/app/services/app_chat_service.py +++ b/api/app/services/app_chat_service.py @@ -655,6 +655,7 @@ class AppChatService: workspace_id: uuid.UUID, user_id: str = None, variables: Optional[Dict[str, Any]] = None, + files: Optional[List[FileInput]] = None, web_search: bool = False, memory: bool = True, storage_type: Optional[str] = None, @@ -669,7 +670,8 @@ class AppChatService: variables=variables, conversation_id=str(conversation_id), stream=True, - user_id=user_id + user_id=user_id, + files=files ) async for event in workflow_service.run_stream( app_id=app_id,