From 5d4eb57110a73bb604343082b88b1c5c704ad1ed Mon Sep 17 00:00:00 2001 From: Fantasy lee <129943055+Fantasylee21@users.noreply.github.com> Date: Mon, 26 May 2025 17:46:08 +0800 Subject: [PATCH 1/2] =?UTF-8?q?[feat]:=20=E5=A2=9E=E5=8A=A0ai=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- app/api/v1/endpoints/aichat.py | 84 +++++++++++++++++++++++++++++++++- app/curd/note.py | 10 +++- app/utils/aichat.py | 18 ++++++-- app/utils/readPDF.py | 17 +++++++ 5 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 app/utils/readPDF.py diff --git a/.env b/.env index 4121775..b8ec1b0 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ SECRET_KEY=bN3hZ6LbHG7nH9YXWULCr-crcS3GAaRELbNBdAyHBuiHH5TRctd0Zbd6OuLRHHa4Fbs SENDER_PASSWORD=TXVU2unpCAE2EtEX -KIMI_API_KEY=sk-icdiHIiv6x8XjJCaN6J6Un7uoVxm6df5WPhflq10ZVFo03D9 +KIMI_API_KEY=sk-WFAukbN3TVJKhkGLF55a5aF702Ec435b8c36A580E8E4D92d FERNET_SECRET_KEY=6WssEkvinI_YqwKXdokii2yI6iBiLO_Cjoyq0bBBC5o= \ No newline at end of file diff --git a/app/api/v1/endpoints/aichat.py b/app/api/v1/endpoints/aichat.py index f4748ff..e9dd4a6 100644 --- a/app/api/v1/endpoints/aichat.py +++ b/app/api/v1/endpoints/aichat.py @@ -1,11 +1,15 @@ from fastapi import Depends, Request from fastapi.responses import StreamingResponse -from app.utils.aichat import kimi_chat_stream +from app.utils.aichat import kimi_chat_stream, kimi_chat from app.utils.redis import get_redis_client from app.utils.auth import get_current_user import json from fastapi import APIRouter from app.schemas.aichat import NoteInput +from sqlalchemy.ext.asyncio import AsyncSession +from app.utils.get_db import get_db +from app.utils.readPDF import read_pdf +from fastapi import HTTPException router = APIRouter() @@ -47,4 +51,80 @@ async def clear_notes( user_id = current_user["id"] redis_key = f"aichat:{user_id}" redis_client.delete(redis_key) - return {"msg": "clear successfully"} \ No newline at end of file + return {"msg": "clear successfully"} + +@router.get("/review", response_model=dict) +async def review_notes( + article_id: int, +): + path = f"/lhcos-data/{article_id}.pdf" + text = await read_pdf(path) + text += "\n\n请根据以上内容生成文章综述。" + async def ai_stream(): + full_reply = "" + try: + async for content in kimi_chat_stream([{"role": "user", "content": text}]): + full_reply += content + yield f"data: {json.dumps({'content': content}, ensure_ascii=False)}\n\n" + except Exception as e: + error_str = str(e) + if "exceeded model token limit" in error_str: + raise HTTPException( + status_code=413, + detail="输入内容过长,超出了模型的token限制" + ) + # 其他类型的错误重新抛出 + raise + return StreamingResponse(ai_stream(), media_type="text/event-stream") + +@router.get("/graph", response_model=dict) +async def generate_graph( + note_id: int, + db : AsyncSession = Depends(get_db), +): + # 读取数据库获取笔记内容 + from app.curd.note import get_note_by_id + note = await get_note_by_id(db, note_id) + if not note: + raise HTTPException(status_code=404, detail="Note not found") + text = note.content + text += """ + 我需要你对于上面的内容生成思维导图,请仅给我返回mermaid代码,不要有其他内容,下面是生成样例, + graph TD + A[Natural Language Navigation for Service Robots] --> B[Task Definition] + A --> C[Challenges] + A --> D[Proposed Solution] + A --> E[Experimental Results] + + B --> B1["- Predict action sequence from NL instructions"] + B --> B2["- Example: 'Walk out of bathroom to right stairs'"] + + C --> C1["- Environment exploration"] + C --> C2["- Accurate path following"] + C --> C3["- Language-vision relationship modeling"] + + D --> D1[CrossMap Transformer Network] + D --> D2[Transformer-based Speaker] + D --> D3[Double Back-Translation Model] + + D1 --> D11["- Encodes linguistic/visual features"] + D1 --> D12["- Sequentially generates paths"] + + D2 --> D21["- Generates navigation instructions"] + + D3 --> D31["- Paths → Instructions"] + D3 --> D32["- Instructions → Paths"] + D3 --> D33["- Shared latent features"] + + E --> E1["- Improved instruction understanding"] + E --> E2["- Enhanced instruction generation" + """ + try: + ans = await kimi_chat([{"role": "user", "content": text}], model="moonshot-v1-32k") + except Exception as e: + raise HTTPException( + status_code=500, + detail=f"AI服务异常: {str(e)}" + ) + return {"mermaid_code": ans.strip().replace("```mermaid", "").replace("```", "").strip()} + \ No newline at end of file diff --git a/app/curd/note.py b/app/curd/note.py index aa48945..ac4b8ee 100644 --- a/app/curd/note.py +++ b/app/curd/note.py @@ -179,4 +179,12 @@ async def find_self_notes_count_in_db(db: AsyncSession, user_id: int): ) result = await db.execute(stmt) count = result.scalar_one_or_none() - return count \ No newline at end of file + return count + +async def get_note_by_id(db: AsyncSession, note_id: int): + """ + 根据 ID 获取笔记 + """ + stmt = select(Note).where(Note.id == note_id, Note.visible == True) + result = await db.execute(stmt) + return result.scalar_one_or_none() # 返回单个笔记或 None \ No newline at end of file diff --git a/app/utils/aichat.py b/app/utils/aichat.py index c3e7f0c..778a0f5 100644 --- a/app/utils/aichat.py +++ b/app/utils/aichat.py @@ -3,10 +3,10 @@ client = AsyncOpenAI( api_key=settings.KIMI_API_KEY, - base_url="https://api.moonshot.cn/v1", + base_url="http://47.93.172.156:3001/v1", ) -async def kimi_chat_stream(messages, model="moonshot-v1-8k", temperature=0.3): +async def kimi_chat_stream(messages, model="moonshot-v1-32k", temperature=0.3): """ 异步AI流式对话工具方法,传入消息列表,流式返回AI回复内容。 :param messages: List[dict] @@ -21,4 +21,16 @@ async def kimi_chat_stream(messages, model="moonshot-v1-8k", temperature=0.3): async for chunk in stream: content = getattr(chunk.choices[0].delta, "content", None) if content: - yield content \ No newline at end of file + yield content + + +async def kimi_chat(messages, model="moonshot-v1-32k", temperature=0): + """ + 异步但不流式AI对话工具方法,传入消息列表,返回AI回复内容。 + """ + response = await client.chat.completions.create( + model=model, + messages=messages, + temperature=temperature + ) + return response.choices[0].message.content if response.choices else "" \ No newline at end of file diff --git a/app/utils/readPDF.py b/app/utils/readPDF.py new file mode 100644 index 0000000..e12a3dd --- /dev/null +++ b/app/utils/readPDF.py @@ -0,0 +1,17 @@ +import fitz +import asyncio + +def extract_text_from_pdf(pdf_path): + # 打开PDF文件 + doc = fitz.open(pdf_path) + text = "" + # 遍历每一页 + for page_num in range(len(doc) - 2): + page = doc.load_page(page_num) # 加载页面 + page_text = page.get_text("text") + text += page_text + doc.close() # 关闭PDF文件 + return text + +async def read_pdf(pdf_path: str): + return await asyncio.to_thread(extract_text_from_pdf, pdf_path) \ No newline at end of file From 2d493f84f97d63155f36c51e23791dfc065be7a3 Mon Sep 17 00:00:00 2001 From: Fantasy lee <129943055+Fantasylee21@users.noreply.github.com> Date: Mon, 26 May 2025 17:46:53 +0800 Subject: [PATCH 2/2] =?UTF-8?q?[docs]:=20=E6=9B=B4=E6=96=B0requirements.tx?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index ccc5d03..65d9e22 100644 --- a/requirements.txt +++ b/requirements.txt @@ -63,6 +63,7 @@ pycparser==2.22 pydantic==2.11.2 pydantic_core==2.33.1 PyJWT==2.10.1 +PyMuPDF==1.26.0 PyMySQL==1.1.1 pytest==8.3.5 python-dateutil==2.9.0.post0