feat: 后端基础设施 — LLM工厂/Embedding工厂/验证客户端/会话持久化
- backend/llm.py: 支持 OpenAI 兼容 API 与 Ollama 本地模型切换 - backend/embeddings.py: 支持云端与本地嵌入模型(sentence-transformers) - backend/validation.py: FastAPI 验证服务 HTTP 客户端 - backend/session.py: JSON 文件会话管理(创建/加载/保存/列表/删除) - .env.example: 完整环境变量模板 - requirements.txt: 所有 Python 依赖声明
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"""多会话持久化管理模块。
|
||||
|
||||
每个会话对应一个独立的 JSON 文件存储在 ./sessions/ 目录下。
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
SESSIONS_DIR = Path(os.getenv("SESSIONS_DIR", "./sessions"))
|
||||
|
||||
|
||||
def _ensure_dir():
|
||||
SESSIONS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def _session_path(session_id: str) -> Path:
|
||||
return SESSIONS_DIR / f"{session_id}.json"
|
||||
|
||||
|
||||
def generate_session_id() -> str:
|
||||
return uuid.uuid4().hex[:12]
|
||||
|
||||
|
||||
def create_session(name: str = "", agent_state: Optional[dict] = None) -> dict:
|
||||
"""创建新会话,返回会话元数据。"""
|
||||
_ensure_dir()
|
||||
sid = generate_session_id()
|
||||
now = _now_iso()
|
||||
data = {
|
||||
"session_id": sid,
|
||||
"session_name": name or f"新建报表 {now[:10]}",
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
"agent_state": agent_state or {},
|
||||
}
|
||||
with open(_session_path(sid), "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
return data
|
||||
|
||||
|
||||
def load_session(session_id: str) -> Optional[dict]:
|
||||
"""按 ID 加载会话数据。未找到则返回 None。"""
|
||||
_ensure_dir()
|
||||
fp = _session_path(session_id)
|
||||
if not fp.exists():
|
||||
return None
|
||||
with open(fp, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def save_session(session_id: str, agent_state: dict, session_name: str = ""):
|
||||
"""将会话状态保存(更新)至磁盘。"""
|
||||
_ensure_dir()
|
||||
fp = _session_path(session_id)
|
||||
data = {}
|
||||
if fp.exists():
|
||||
with open(fp, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
data["session_id"] = session_id
|
||||
if session_name:
|
||||
data["session_name"] = session_name
|
||||
if not data.get("session_name"):
|
||||
data["session_name"] = f"报表 {data.get('created_at', _now_iso())[:10]}"
|
||||
data["updated_at"] = _now_iso()
|
||||
if not data.get("created_at"):
|
||||
data["created_at"] = data["updated_at"]
|
||||
data["agent_state"] = agent_state
|
||||
|
||||
with open(fp, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def list_all_sessions() -> list[dict]:
|
||||
"""列出所有历史会话(仅摘要,不含完整 agent_state)。"""
|
||||
_ensure_dir()
|
||||
sessions = []
|
||||
for fp in sorted(SESSIONS_DIR.glob("*.json"), key=os.path.getmtime, reverse=True):
|
||||
try:
|
||||
with open(fp, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
sessions.append({
|
||||
"session_id": data.get("session_id", fp.stem),
|
||||
"session_name": data.get("session_name", fp.stem),
|
||||
"created_at": data.get("created_at", ""),
|
||||
"updated_at": data.get("updated_at", ""),
|
||||
})
|
||||
except (json.JSONDecodeError, KeyError):
|
||||
continue
|
||||
return sessions
|
||||
|
||||
|
||||
def delete_session(session_id: str) -> bool:
|
||||
"""按 ID 删除会话文件。"""
|
||||
_ensure_dir()
|
||||
fp = _session_path(session_id)
|
||||
if fp.exists():
|
||||
fp.unlink()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _now_iso() -> str:
|
||||
return datetime.now(timezone.utc).isoformat()
|
||||
Reference in New Issue
Block a user