Refactor assistant configuration and database seeding
- Update Makefile to include new database seed commands for assistants and credentials. - Refactor assistant model to use explicit fields instead of a config dictionary, improving data integrity and clarity. - Implement new seeding SQL script for assistants, ensuring dependencies on credentials are respected. - Modify backend routes and frontend components to accommodate the new assistant structure, including direct field access for prompt, API URL, and keys. - Enhance the AssistantPage component to handle the new data structure and streamline the save process for different assistant types.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""助手 CRUD。前端「助手列表 / 创建 / 编辑」对接这里。
|
||||
|
||||
模型/KB 以 FK 引用注册表;外部类型(dify/fastgpt/opencode)的 config.apiKey 是私有密钥,
|
||||
读时打码、写时哨兵(复用 services/masking)。
|
||||
模型/KB 以 FK 引用注册表;瘦类型字段直接是真列。外部类型(dify/fastgpt/opencode)的
|
||||
api_key 是私有密钥,读时打码、写时哨兵(列级,复用 services/masking,与凭证表一致)。
|
||||
"""
|
||||
|
||||
import uuid
|
||||
@@ -9,7 +9,7 @@ import uuid
|
||||
from db.models import Assistant
|
||||
from db.session import get_session
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from schemas import EXTERNAL_TYPES, AssistantOut, AssistantUpsert
|
||||
from schemas import AssistantOut, AssistantUpsert
|
||||
from services.masking import mask, resolve_incoming_key
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -17,25 +17,6 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
router = APIRouter(prefix="/api/assistants", tags=["assistants"])
|
||||
|
||||
|
||||
def _mask_config(type_: str, config: dict) -> dict:
|
||||
"""读取返回前:外部类型的 apiKey 打码,其余原样。"""
|
||||
if type_ in EXTERNAL_TYPES and config.get("apiKey"):
|
||||
return {**config, "apiKey": mask(config["apiKey"])}
|
||||
return config
|
||||
|
||||
|
||||
def _merge_config(type_: str, incoming: dict, stored: dict) -> dict:
|
||||
"""写入时:外部类型若回传打码占位符/空 apiKey → 保留旧 key。"""
|
||||
if type_ in EXTERNAL_TYPES and "apiKey" in incoming:
|
||||
incoming = {
|
||||
**incoming,
|
||||
"apiKey": resolve_incoming_key(
|
||||
incoming.get("apiKey"), stored.get("apiKey", "")
|
||||
),
|
||||
}
|
||||
return incoming
|
||||
|
||||
|
||||
def _to_out(a: Assistant) -> AssistantOut:
|
||||
return AssistantOut(
|
||||
id=a.id,
|
||||
@@ -49,7 +30,11 @@ def _to_out(a: Assistant) -> AssistantOut:
|
||||
tts_credential_id=a.tts_credential_id,
|
||||
realtime_credential_id=a.realtime_credential_id,
|
||||
knowledge_base_id=a.knowledge_base_id,
|
||||
config=_mask_config(a.type, a.config or {}),
|
||||
prompt=a.prompt,
|
||||
api_url=a.api_url,
|
||||
api_key=mask(a.api_key), # 仅外部类型有值;空串 mask 仍是空串
|
||||
app_id=a.app_id,
|
||||
graph=a.graph or {},
|
||||
updated_at=a.updated_at.isoformat() if a.updated_at else None,
|
||||
)
|
||||
|
||||
@@ -87,7 +72,7 @@ async def get_assistant(
|
||||
async def duplicate_assistant(
|
||||
assistant_id: str, session: AsyncSession = Depends(get_session)
|
||||
):
|
||||
"""服务端整行复制:含真实 config(真 key),DB→DB,密钥不经过浏览器,副本可直接用。"""
|
||||
"""服务端整行复制:含真实 api_key,DB→DB,密钥不经过浏览器,副本可直接用。"""
|
||||
src = await session.get(Assistant, assistant_id)
|
||||
if not src:
|
||||
raise HTTPException(404, "助手不存在")
|
||||
@@ -103,7 +88,11 @@ async def duplicate_assistant(
|
||||
tts_credential_id=src.tts_credential_id,
|
||||
realtime_credential_id=src.realtime_credential_id,
|
||||
knowledge_base_id=src.knowledge_base_id,
|
||||
config=dict(src.config or {}), # 浅拷贝,避免与源行共享同一 dict
|
||||
prompt=src.prompt,
|
||||
api_url=src.api_url,
|
||||
api_key=src.api_key, # 真 key,DB→DB
|
||||
app_id=src.app_id,
|
||||
graph=dict(src.graph or {}), # 浅拷贝,避免与源行共享同一 dict
|
||||
)
|
||||
session.add(a)
|
||||
await session.commit()
|
||||
@@ -121,8 +110,8 @@ async def update_assistant(
|
||||
if not a:
|
||||
raise HTTPException(404, "助手不存在")
|
||||
data = body.model_dump()
|
||||
# 外部类型 apiKey 写时哨兵:打码占位符 → 保留旧 key(在改 a.config 前用旧值)
|
||||
data["config"] = _merge_config(body.type, data["config"], a.config or {})
|
||||
# 写时哨兵(列级):回传打码/空 api_key → 保留旧 key
|
||||
data["api_key"] = resolve_incoming_key(data["api_key"], a.api_key)
|
||||
for k, v in data.items():
|
||||
setattr(a, k, v)
|
||||
await session.commit()
|
||||
|
||||
Reference in New Issue
Block a user