Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
SECRET_KEY=bN3hZ6LbHG7nH9YXWULCr-crcS3GAaRELbNBdAyHBuiHH5TRctd0Zbd6OuLRHHa4Fbs
SENDER_PASSWORD=TXVU2unpCAE2EtEX
SENDER_PASSWORD=TXVU2unpCAE2EtEX
KIMI_API_KEY=sk-icdiHIiv6x8XjJCaN6J6Un7uoVxm6df5WPhflq10ZVFo03D9
50 changes: 50 additions & 0 deletions app/api/v1/endpoints/aichat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from fastapi import Depends, Request
from fastapi.responses import StreamingResponse
from app.utils.aichat import kimi_chat_stream
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


router = APIRouter()
redis_client = get_redis_client()

@router.post("/note", response_model=dict)
async def generate_notes(
input: NoteInput,
current_user: dict = Depends(get_current_user)
):
user_id = current_user["id"]
redis_key = f"aichat:{user_id}"

# 1. 读取历史对话
history = redis_client.get(redis_key)
if history:
messages = json.loads(history)
else:
# 首轮对话可加 system prompt
messages = [{"role": "system", "content": "你是一个智能笔记助手。"}]

# 2. 追加用户输入
messages.append({"role": "user", "content": input.input})

async def ai_stream():
full_reply = ""
async for content in kimi_chat_stream(messages):
full_reply += content
yield f"data: {json.dumps({'content': content}, ensure_ascii=False)}\n\n"
messages.append({"role": "assistant", "content": full_reply})
redis_client.set(redis_key, json.dumps(messages), ex=3600)

return StreamingResponse(ai_stream(), media_type="text/event-stream")

@router.get("/clear", response_model=dict)
async def clear_notes(
current_user: dict = Depends(get_current_user)
):
user_id = current_user["id"]
redis_key = f"aichat:{user_id}"
redis_client.delete(redis_key)
return {"msg": "clear successfully"}
1 change: 1 addition & 0 deletions app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class Settings:
SMTP_PORT: int = 465 # SMTP端口
SENDER_EMAIL : str = "jienote_buaa@163.com"
SENDER_PASSWORD: str = os.getenv("SENDER_PASSWORD", "default_password") # 发件人邮箱密码
KIMI_API_KEY: str = os.getenv("KIMI_API_KEY", "default_kimi_api_key") # KIMI API密钥


settings = Settings()
7 changes: 6 additions & 1 deletion app/routers/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from app.api.v1.endpoints.auth import router as auth_router
from app.api.v1.endpoints.note import router as note_router
from app.api.v1.endpoints.user import router as user_router
from app.api.v1.endpoints.aichat import router as aichat_router

def include_auth_router(app):
app.include_router(auth_router, prefix="/public", tags=["auth"])
Expand All @@ -13,7 +14,11 @@ def include_note_router(app):
def include_user_router(app):
app.include_router(user_router, prefix="/user", tags=["user"], dependencies=[Depends(get_current_user)])

def include_aichat_router(app):
app.include_router(aichat_router, prefix="/chat", tags=["aichat"], dependencies=[Depends(get_current_user)])

def include_routers(app):
include_auth_router(app)
include_note_router(app)
include_user_router(app)
include_user_router(app)
include_aichat_router(app)
4 changes: 4 additions & 0 deletions app/schemas/aichat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from pydantic import BaseModel

class NoteInput(BaseModel):
input: str
24 changes: 24 additions & 0 deletions app/utils/aichat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from openai import AsyncOpenAI
from app.core.config import settings

client = AsyncOpenAI(
api_key=settings.KIMI_API_KEY,
base_url="https://api.moonshot.cn/v1",
)

async def kimi_chat_stream(messages, model="moonshot-v1-8k", temperature=0.3):
"""
异步AI流式对话工具方法,传入消息列表,流式返回AI回复内容。
:param messages: List[dict]
:yield: str,AI回复内容片段
"""
stream = await client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
stream=True
)
async for chunk in stream:
content = getattr(chunk.choices[0].delta, "content", None)
if content:
yield content
7 changes: 7 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@ anyio==4.9.0
async-timeout==5.0.1
asyncmy==0.2.10
bcrypt==4.3.0
certifi==2025.1.31
cffi==1.17.1
click==8.1.8
colorama==0.4.6
cryptography==44.0.2
distro==1.9.0
dnspython==2.7.0
dotenv==0.9.9
email_validator==2.2.0
fastapi==0.115.12
fastapi-pagination==0.12.34
greenlet==3.1.1
h11==0.14.0
httpcore==1.0.8
httpx==0.28.1
idna==3.10
jiter==0.9.0
jwt==1.3.1
Mako==1.3.9
MarkupSafe==3.0.2
numpy==2.2.4
openai==1.75.0
pandas==2.2.3
passlib==1.7.4
pycparser==2.22
Expand All @@ -37,6 +43,7 @@ six==1.17.0
sniffio==1.3.1
SQLAlchemy==2.0.40
starlette==0.46.1
tqdm==4.67.1
typing-inspection==0.4.0
typing_extensions==4.13.1
tzdata==2025.2
Expand Down