feat(web): 项目级配置 + 会话命名/历史 + 设计文档纳入影响调查

- 会话支持 name/project 字段,上传要件定义后自动命名;前端侧边栏会话历史 + localStorage 恢复,顶部只显示会话名
- 新增 ProjectsStore(SQLite)与 /api/projects CRUD;绑定项目后 _rebuild_source 合并模板/规则/代码库/设计文档,上传区仅要件定义
- StructuredSource.design_docs 与 ImpactReport.design_references;影响调查新增既有设计文档确定性交叉引用(无 LLM)
- 同步更新 docs/design.md §12.7、README、_AI_USAGE_LOG.md;全量测试 558 通过,覆盖率 99.10%
This commit is contained in:
lhl
2026-08-27 12:13:28 +08:00
parent 838a93720e
commit 916c5beed7
18 changed files with 1174 additions and 65 deletions
+57 -5
View File
@@ -20,7 +20,9 @@ from pydantic import BaseModel
from genesis.inference.factory import build_inference_engine
from genesis.server.service import FileTypeError, GenesisService, ServiceStepError
from genesis.server.store import SessionNotFoundError, SessionStore
from genesis.server.store import (
ProjectConfigError, ProjectsStore, SessionNotFoundError, SessionStore,
)
VERSION = "0.1.0"
@@ -47,6 +49,18 @@ class ChatMessageReq(BaseModel):
class SessionCreate(BaseModel):
user_id: str = "default"
name: str | None = None
project: str | None = None
class ProjectCreate(BaseModel):
name: str
display_name: str = ""
template: str = ""
write_instruction: str = ""
rules: list[str] = []
existing_system_code_dir: str = ""
design_docs_dir: str = ""
class FileUploadResp(BaseModel):
@@ -75,7 +89,8 @@ def create_app(
elif engine is None:
engine = None # 真实模式:generate/qa 时按需 build(避免未配置 key 直接 503)
service = GenesisService(store=store, data_root=data_root, engine=engine)
projects = ProjectsStore(db_path=str(store._db)) if store else None
service = GenesisService(store=store, data_root=data_root, engine=engine, projects=projects)
from genesis.chat.agent import ChatAgent
chat_agent = ChatAgent(service=service, fake=is_fake, engine=engine)
@@ -95,17 +110,54 @@ def create_app(
def index():
return chat_html
# ---------- 项目配置 ----------
@app.post("/api/projects")
def create_project(body: ProjectCreate):
try:
cfg = service.projects.upsert(
name=body.name, display_name=body.display_name, template=body.template,
write_instruction=body.write_instruction, rules=body.rules,
existing_system_code_dir=body.existing_system_code_dir,
design_docs_dir=body.design_docs_dir,
)
except ProjectConfigError as e:
raise _error(400, "PROJECT_CONFIG_INVALID", str(e))
return cfg.to_dict
@app.get("/api/projects")
def list_projects():
if not service.projects:
return []
return [c.to_dict for c in service.projects.list()]
@app.get("/api/projects/{name}")
def get_project(name: str):
if not service.projects:
raise _error(404, "PROJECT_NOT_FOUND", f"项目不存在: {name}")
cfg = service.projects.get(name)
if cfg is None:
raise _error(404, "PROJECT_NOT_FOUND", f"项目不存在: {name}")
return cfg.to_dict
@app.delete("/api/projects/{name}")
def delete_project(name: str):
if not service.projects:
return {"deleted": False}
return {"deleted": service.projects.delete(name)}
# ---------- 会话 ----------
@app.post("/api/sessions")
def create_session(body: SessionCreate):
rec = service.create_session(body.user_id)
return {"session_id": rec.session_id, "status": rec.status}
rec = service.create_session(body.user_id, name=body.name, project=body.project)
return {"session_id": rec.session_id, "status": rec.status, "name": rec.name}
@app.get("/api/sessions")
def list_sessions(user_id: str = "default"):
return [
{"session_id": r.session_id, "status": r.status, "updated_at": r.updated_at}
{"session_id": r.session_id, "name": r.name, "project": r.project,
"status": r.status, "updated_at": r.updated_at}
for r in service.store.list_sessions(user_id)
]