feat: add prompt history management
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.pagination import page_result
|
||||
from app.core.database import get_db
|
||||
from app.core.dependencies import get_current_admin
|
||||
from app.core.responses import api_success
|
||||
@@ -32,8 +33,13 @@ DEFAULT_PROMPT = "你是大本营答疑助手。你只能基于提供的知识
|
||||
|
||||
@router.get("/prompt")
|
||||
def get_prompt(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
|
||||
prompt = db.scalar(select(Prompt).order_by(Prompt.updated_at.desc(), Prompt.id.desc()).limit(1))
|
||||
return api_success({"promptContent": prompt.prompt_content if prompt else DEFAULT_PROMPT})
|
||||
row = db.execute(
|
||||
select(Prompt, Admin)
|
||||
.outerjoin(Admin, Admin.id == Prompt.updated_by)
|
||||
.order_by(Prompt.updated_at.desc(), Prompt.id.desc())
|
||||
.limit(1)
|
||||
).first()
|
||||
return api_success(_prompt_detail(row[0], row[1]) if row else _default_prompt_detail())
|
||||
|
||||
|
||||
@router.put("/prompt")
|
||||
@@ -42,22 +48,91 @@ def save_prompt(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: Admin = Depends(get_current_admin),
|
||||
) -> dict:
|
||||
prompt = Prompt(prompt_content=payload.promptContent, updated_by=current_admin.id)
|
||||
prompt = Prompt(prompt_content=payload.promptContent, change_type="save", updated_by=current_admin.id)
|
||||
db.add(prompt)
|
||||
db.flush()
|
||||
OperationLogService.write(db, admin_id=current_admin.id, module="prompt", action="save", target_id=prompt.id)
|
||||
db.commit()
|
||||
return api_success({"promptContent": prompt.prompt_content})
|
||||
db.refresh(prompt)
|
||||
return api_success(_prompt_detail(prompt, current_admin))
|
||||
|
||||
|
||||
@router.post("/prompt/reset")
|
||||
def reset_prompt(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
|
||||
prompt = Prompt(prompt_content=DEFAULT_PROMPT, updated_by=current_admin.id)
|
||||
prompt = Prompt(prompt_content=DEFAULT_PROMPT, change_type="reset", updated_by=current_admin.id)
|
||||
db.add(prompt)
|
||||
db.flush()
|
||||
OperationLogService.write(db, admin_id=current_admin.id, module="prompt", action="reset", target_id=prompt.id)
|
||||
db.commit()
|
||||
return api_success({"promptContent": prompt.prompt_content})
|
||||
db.refresh(prompt)
|
||||
return api_success(_prompt_detail(prompt, current_admin))
|
||||
|
||||
|
||||
@router.get("/prompt/history")
|
||||
def prompt_history(
|
||||
page: int = Query(default=1, ge=1),
|
||||
pageSize: int = Query(default=10, ge=10, le=100),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: Admin = Depends(get_current_admin),
|
||||
) -> dict:
|
||||
total = db.scalar(select(func.count(Prompt.id))) or 0
|
||||
rows = db.execute(
|
||||
select(Prompt, Admin)
|
||||
.outerjoin(Admin, Admin.id == Prompt.updated_by)
|
||||
.order_by(Prompt.updated_at.desc(), Prompt.id.desc())
|
||||
.offset((page - 1) * pageSize)
|
||||
.limit(pageSize)
|
||||
).all()
|
||||
items = [
|
||||
_prompt_summary(prompt, admin, is_current=index == 0 and page == 1)
|
||||
for index, (prompt, admin) in enumerate(rows)
|
||||
]
|
||||
return api_success(page_result(items, total=total, page=page, page_size=pageSize))
|
||||
|
||||
|
||||
@router.get("/prompt/history/{prompt_id}")
|
||||
def prompt_history_detail(
|
||||
prompt_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: Admin = Depends(get_current_admin),
|
||||
) -> dict:
|
||||
row = db.execute(
|
||||
select(Prompt, Admin)
|
||||
.outerjoin(Admin, Admin.id == Prompt.updated_by)
|
||||
.where(Prompt.id == prompt_id)
|
||||
).first()
|
||||
if row is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="提示词版本不存在")
|
||||
return api_success(_prompt_detail(row[0], row[1]))
|
||||
|
||||
|
||||
@router.post("/prompt/history/{prompt_id}/restore")
|
||||
def restore_prompt(
|
||||
prompt_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: Admin = Depends(get_current_admin),
|
||||
) -> dict:
|
||||
source = db.get(Prompt, prompt_id)
|
||||
if source is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="提示词版本不存在")
|
||||
restored = Prompt(
|
||||
prompt_content=source.prompt_content,
|
||||
change_type="restore",
|
||||
source_prompt_id=source.id,
|
||||
updated_by=current_admin.id,
|
||||
)
|
||||
db.add(restored)
|
||||
db.flush()
|
||||
OperationLogService.write(
|
||||
db,
|
||||
admin_id=current_admin.id,
|
||||
module="prompt",
|
||||
action="restore",
|
||||
target_id=restored.id,
|
||||
)
|
||||
db.commit()
|
||||
db.refresh(restored)
|
||||
return api_success(_prompt_detail(restored, current_admin))
|
||||
|
||||
|
||||
@router.post("/agent/debug")
|
||||
@@ -296,6 +371,44 @@ def _model_dict(model: ModelConfig) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _default_prompt_detail() -> dict:
|
||||
return {
|
||||
"id": None,
|
||||
"promptContent": DEFAULT_PROMPT,
|
||||
"changeType": "default",
|
||||
"sourcePromptId": None,
|
||||
"updatedByName": "系统默认",
|
||||
"updatedAt": None,
|
||||
"charCount": len(DEFAULT_PROMPT),
|
||||
}
|
||||
|
||||
|
||||
def _prompt_summary(prompt: Prompt, admin: Admin | None, *, is_current: bool) -> dict:
|
||||
compact = " ".join(prompt.prompt_content.split())
|
||||
return {
|
||||
"id": prompt.id,
|
||||
"preview": compact[:140],
|
||||
"charCount": len(prompt.prompt_content),
|
||||
"changeType": prompt.change_type,
|
||||
"sourcePromptId": prompt.source_prompt_id,
|
||||
"updatedByName": admin.name if admin else "未知管理员",
|
||||
"updatedAt": prompt.updated_at,
|
||||
"isCurrent": is_current,
|
||||
}
|
||||
|
||||
|
||||
def _prompt_detail(prompt: Prompt, admin: Admin | None = None) -> dict:
|
||||
return {
|
||||
"id": prompt.id,
|
||||
"promptContent": prompt.prompt_content,
|
||||
"changeType": prompt.change_type,
|
||||
"sourcePromptId": prompt.source_prompt_id,
|
||||
"updatedByName": admin.name if admin else None,
|
||||
"updatedAt": prompt.updated_at,
|
||||
"charCount": len(prompt.prompt_content),
|
||||
}
|
||||
|
||||
|
||||
def _agent_knowledge_scopes(db: Session, knowledge_ids: list[int]) -> list[KnowledgeScope]:
|
||||
query = select(Knowledge).where(Knowledge.status == 1)
|
||||
if knowledge_ids:
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import BigInteger, DateTime, Integer, Numeric, String, Text, func
|
||||
from sqlalchemy import BigInteger, DateTime, Index, Integer, Numeric, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base
|
||||
@@ -11,9 +11,14 @@ from app.models.base import Base
|
||||
|
||||
class Prompt(Base):
|
||||
__tablename__ = "sys_prompt"
|
||||
__table_args__ = (Index("ix_sys_prompt_updated_at_id", "updated_at", "id"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(
|
||||
BigInteger().with_variant(Integer, "sqlite"), primary_key=True, autoincrement=True
|
||||
)
|
||||
prompt_content: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
change_type: Mapped[str] = mapped_column(String(20), default="save", nullable=False)
|
||||
source_prompt_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||||
updated_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user