Files
mini-openclaw-v2/backend/api/sessions.py
T
qiaoqichaoandClaude Code d56c699ce6 chore(v2): 从兴邦预审v2白名单抽取独立可运行基座
按 docs/v2/05-implementation-plan.md §1 候选迁移表与 claude_use/T00 白名单,
从 55e2cdb 抽取 224 个文件(2.48 MB)到独立目录 F:/mini-OpenClaw-v2。

- 排除:凭据(.env/server-deploy.conf/旧DELIVERY.md)、运行数据(xingbang_data 等
  4 个 SQLite)、依赖(venv/node_modules 符号链接)、归档(_backup*/test-evidence/
  ori_file)、Office 临时文件
- 目标目录独立 git 仓库,初始恢复点;Gitea 远程与分支待 OQ-06 Q2 定案
- 未部署、未启动服务、未调用模型

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-29 12:58:26 +08:00

124 lines
4.4 KiB
Python

"""Session CRUD API — list / create / rename / delete / raw messages / generate title."""
import os
import uuid
from pathlib import Path
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from graph.session_manager import session_manager
from graph.prompt_builder import build_system_prompt
router = APIRouter()
BASE_DIR = Path(__file__).resolve().parent.parent
# ── Request models ──────────────────────────────────────────
class RenameRequest(BaseModel):
title: str
# ── Endpoints ───────────────────────────────────────────────
@router.get("/sessions")
async def list_sessions():
"""List all sessions with title and metadata."""
sessions = session_manager.list_sessions()
return {"sessions": sessions}
@router.post("/sessions")
async def create_session():
"""Create a new empty session."""
session_id = f"session-{uuid.uuid4().hex[:12]}"
meta = session_manager.create_session(session_id)
return meta
@router.put("/sessions/{session_id}")
async def rename_session(session_id: str, req: RenameRequest):
"""Rename an existing session."""
try:
session_manager.rename_session(session_id, req.title)
except FileNotFoundError:
raise HTTPException(status_code=404, detail="Session not found")
return {"id": session_id, "title": req.title}
@router.delete("/sessions/{session_id}")
async def delete_session(session_id: str):
"""Delete a session."""
session_manager.delete_session(session_id)
return {"status": "deleted", "id": session_id}
@router.get("/sessions/{session_id}/messages")
async def get_raw_messages(session_id: str):
"""Get complete raw messages including system prompt."""
data = session_manager.get_raw_messages(session_id)
system_prompt = build_system_prompt(BASE_DIR)
# Prepend system prompt as the first message
all_messages = [{"role": "system", "content": system_prompt}] + data.get("messages", [])
return {"session_id": session_id, "title": data.get("title", ""), "messages": all_messages}
@router.get("/sessions/{session_id}/history")
async def get_session_history(session_id: str):
"""Get conversation history for display (no system prompt, includes tool_calls)."""
messages = session_manager.load_session(session_id)
return {"session_id": session_id, "messages": messages}
@router.post("/sessions/{session_id}/generate-title")
async def generate_title(session_id: str):
"""Use DeepSeek to generate a short title from the first conversation turn."""
messages = session_manager.load_session(session_id)
if not messages:
raise HTTPException(status_code=400, detail="No messages to generate title from")
# Get the first user message and first assistant reply
first_user = ""
first_assistant = ""
for msg in messages:
if msg["role"] == "user" and not first_user:
first_user = msg["content"][:200]
elif msg["role"] == "assistant" and not first_assistant:
first_assistant = msg["content"][:200]
if first_user and first_assistant:
break
if not first_user:
raise HTTPException(status_code=400, detail="No user message found")
try:
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import HumanMessage as HM
llm = ChatDeepSeek(
model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
api_key=os.getenv("DEEPSEEK_API_KEY"),
api_base=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
temperature=0.3,
)
prompt = (
f"根据以下对话内容,生成一个不超过10个字的中文标题,只输出标题文本,不要加引号或标点。\n\n"
f"用户: {first_user}\n"
f"助手: {first_assistant}"
)
result = await llm.ainvoke([HM(content=prompt)])
title = result.content.strip().strip('"\'""''')[:20]
session_manager.update_title(session_id, title)
return {"session_id": session_id, "title": title}
except Exception as e:
# Fallback: use first few chars of user message
fallback_title = first_user[:10].strip()
session_manager.update_title(session_id, fallback_title)
return {"session_id": session_id, "title": fallback_title}