diff --git a/deepinsight/api/app.py b/deepinsight/api/app.py new file mode 100755 index 0000000000000000000000000000000000000000..1421566e742cb508cf8965c98dbbfe6f6d3499bd --- /dev/null +++ b/deepinsight/api/app.py @@ -0,0 +1,125 @@ +# Copyright (c) 2025 Huawei Technologies Co. Ltd. +# deepinsight is licensed under Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. + +import asyncio +import os +import uuid +from datetime import datetime +from typing import Optional, Dict + +from fastapi import FastAPI, Request, APIRouter, HTTPException, Query, Body, Depends +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import StreamingResponse + +from deepinsight.service.conversation import ConversationService +from deepinsight.service.deep_research import MessageType, DeepResearchService +from deepinsight.service.schemas.conversation import (ConversationListRsp, ConversationListMsg, ConversationListItem, + AddConversationRsp, BodyAddConversation, AddConversationMsg, + ResponseData, DeleteConversationData, RenameConversationData, + BodyGetList) +from deepinsight.service.schemas.chat import GetChatHistoryData, GetChatHistoryStructure, GetChatHistoryRsp + +# 读取环境变量中的 API 前缀 +API_PREFIX = os.getenv("API_PREFIX", "") + +# 创建 FastAPI 实例 +app_instance = FastAPI( + title="DeepInsight API", + description="A streaming chat API for DeepInsight", + version="1.0.0" +) +_conversations: Dict[str, ConversationListItem] = {} +# 创建路由 +router = APIRouter() + +# 跨域中间件配置 +app_instance.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + + +@router.get("/api/conversations", response_model=ConversationListRsp, tags=["conversation"]) +async def get_conversation_list(body: BodyGetList = Depends()): + try: + conversation_list = ConversationService.get_list(user_id=body.user_id, offset=body.offset, limit=body.limit) + return ConversationListRsp( + code=0, + message="OK", + data=ConversationListMsg(conversations=conversation_list) + ) + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +# temporarily deprecated +@router.post("/api/conversation", response_model=AddConversationRsp, tags=["conversation"]) +async def add_conversation( + body: BodyAddConversation = Body(...) +): + try: + new_conversation = ConversationService.add_conversation(user_id=body.user_id, title=body.title, + conversation_id=body.conversation_id) + + return AddConversationRsp( + code=0, + message="OK", + data=AddConversationMsg(conversationId=str(new_conversation.conversation_id), + created_time=str(new_conversation.created_time)) + ) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@router.delete("/api/conversation", response_model=ResponseData, tags=["conversation"]) +async def delete_conversation(data: DeleteConversationData = Body(...)): + try: + for cid in data.conversation_list: + ConversationService.del_conversation(conversation_id=cid) + return ResponseData(code=0, message="Deleted", data={}) + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@router.put("/api/conversation", response_model=ResponseData, tags=["conversation"]) +async def rename_conversation(data: RenameConversationData = Body(...)): + try: + conversation, is_succeed = ConversationService.rename_conversation(conversation_id=data.conversation_id, + new_name=data.new_name) + if is_succeed: + return ResponseData(code=0, message="Modified", data={"new_name": data.new_name}) + else: + return ResponseData(code=100, message="Conversation Not Found", data={"new_name": data.new_name}) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@router.get("/api/conversations/{conversation_id}/messages", response_model=GetChatHistoryRsp, tags=["conversation"]) +async def get_conversation_messages(conversation_id: str): + conversation_info = ConversationService.get_conversation_info(conversation_id) + history_present = ConversationService.get_history_messages(conversation_id) + new_data = GetChatHistoryStructure( + conversation_id=conversation_id, + user_id=conversation_info.user_id, + created_time=str(conversation_info.created_time), + title=conversation_info.title, + status=conversation_info.status, + messages=history_present + ) + return GetChatHistoryRsp(code=0, message="ok", data=new_data) + + +app_instance.include_router(router, prefix=API_PREFIX) diff --git a/deepinsight/service/conversation.py b/deepinsight/service/conversation.py new file mode 100755 index 0000000000000000000000000000000000000000..ce90279295553dcc378cfa56d18357712a8711a1 --- /dev/null +++ b/deepinsight/service/conversation.py @@ -0,0 +1,93 @@ +# Copyright (c) 2025 Huawei Technologies Co. Ltd. +# deepinsight is licensed under Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. + +from typing import List + +from deepinsight.service.deep_research import DeepResearchService +from deepinsight.service.schemas.chat import ServiceMessage +from deepinsight.service.schemas.conversation import ConversationListItem +from deepinsight.stores.postgresql.database import get_database_session +from deepinsight.stores.postgresql.repositories.conversation_repository import ConversationRepository +from deepinsight.stores.postgresql.repositories.message_repository import MessageRepository +from deepinsight.stores.postgresql.repositories.report_repository import ReportRepository + + +class ConversationService: + @classmethod + def get_list(cls, user_id, offset: int = 0, limit: int = 100): + db = get_database_session() + conversation_repo = ConversationRepository(db) + conversation_list = conversation_repo.get_by_user_id(user_id=user_id, offset=offset, limit=limit) + conv_item_list = [] + for conv in conversation_list: + conv_item_list.append(ConversationListItem( + conversationId=str(conv.conversation_id), + title=conv.title, + createdTime=str(conv.created_time) + ) + ) + return conv_item_list + + @classmethod + def del_conversation(cls, conversation_id): + db = get_database_session() + conversation_repo = ConversationRepository(db) + message_repo = MessageRepository(db) + report_repo = ReportRepository(db) + message_repo.delete_by_conversation_id(conversation_id=conversation_id) + report_repo.delete_by_conversation_id(conversation_id=conversation_id) + conversation_repo.delete_conversation(conversation_id=conversation_id) + + @classmethod + def rename_conversation(cls, conversation_id, new_name): + db = get_database_session() + conversation_repo = ConversationRepository(db) + return conversation_repo.update_title(conversation_id=conversation_id, new_title=new_name) + + @classmethod + def add_conversation(cls, user_id, title, conversation_id): + db = get_database_session() + conversation_repo = ConversationRepository(db) + saved_conversation = conversation_repo.create_conversation(user_id, title, conversation_id) + return saved_conversation + + @classmethod + def get_conversation_info(cls, conversation_id_str: str): + db = get_database_session + repository = ConversationRepository(db) + return repository.get_by_id(conversation_id_str) + + @classmethod + def get_history_messages(cls, conversation_id_str: str) -> List[ServiceMessage]: + db = get_database_session() + repository = MessageRepository(db) + messages_from_db = repository.get_by_conversation_id(conversation_id_str) + + processed_messages = [] + for msg in messages_from_db: + content_to_use = msg.content + if msg.type == "report": + processed_report = DeepResearchService.get_report_and_thought_by_message_id(msg.message_id) + content_to_use = processed_report.thought.messages + [processed_report.report] + + + processed_message = ServiceMessage( + id=str(msg.message_id), + content=content_to_use, + role=msg.type, + created_at=msg.created_time.isoformat() if msg.created_time else None + ) + + processed_messages.append(processed_message) + return processed_messages + + +if __name__ == '__main__': + print(ConversationService.add_conversation(user_id="1"))