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:
@@ -339,7 +339,7 @@ export function AssistantPage() {
|
||||
setForm({
|
||||
name: a.name,
|
||||
greeting: a.greeting,
|
||||
prompt: typeof a.config.prompt === "string" ? a.config.prompt : "",
|
||||
prompt: a.prompt,
|
||||
runtimeMode: a.runtimeMode,
|
||||
realtimeModel: a.realtimeCredentialId ?? "",
|
||||
model: a.llmCredentialId ?? "",
|
||||
@@ -464,70 +464,32 @@ export function AssistantPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// 保存提示词助手(新建 POST / 编辑 PUT)
|
||||
async function handleSavePrompt() {
|
||||
setSaving(true);
|
||||
setSaveError(null);
|
||||
const payload: AssistantUpsert = {
|
||||
name: form.name.trim(),
|
||||
// 平铺字段的空白 upsert,各类型按需覆盖(后端会按 type 清掉无关字段)
|
||||
function baseUpsert(over: Partial<AssistantUpsert>): AssistantUpsert {
|
||||
return {
|
||||
name: "",
|
||||
type: "prompt",
|
||||
runtimeMode: form.runtimeMode,
|
||||
greeting: form.greeting,
|
||||
enableInterrupt: form.enableInterrupt,
|
||||
llmCredentialId: form.model || null,
|
||||
asrCredentialId: form.asr || null,
|
||||
ttsCredentialId: form.voice || null,
|
||||
realtimeCredentialId: form.realtimeModel || null,
|
||||
knowledgeBaseId: form.knowledgeBase || null,
|
||||
config: { prompt: form.prompt },
|
||||
};
|
||||
try {
|
||||
if (editingId) {
|
||||
await assistantsApi.update(editingId, payload);
|
||||
} else {
|
||||
await assistantsApi.create(payload);
|
||||
}
|
||||
await loadAssistants();
|
||||
setView("list");
|
||||
} catch (error) {
|
||||
setSaveError(error instanceof Error ? error.message : "保存失败");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
// config 里取字符串字段(后端回传是 Record<string, unknown>)
|
||||
const cfgStr = (a: Assistant, key: string) =>
|
||||
typeof a.config[key] === "string" ? (a.config[key] as string) : "";
|
||||
|
||||
// ---- Dify ----
|
||||
function fillDifyForm(a: Assistant) {
|
||||
setDifyForm({
|
||||
name: a.name,
|
||||
apiUrl: cfgStr(a, "apiUrl"),
|
||||
apiKey: cfgStr(a, "apiKey"), // 编辑时为打码值,不改则原样回传(后端哨兵保留旧 key)
|
||||
asr: a.asrCredentialId ?? "",
|
||||
voice: a.ttsCredentialId ?? "",
|
||||
enableInterrupt: a.enableInterrupt,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSaveDify() {
|
||||
setSaving(true);
|
||||
setSaveError(null);
|
||||
const payload: AssistantUpsert = {
|
||||
name: difyForm.name.trim(),
|
||||
type: "dify",
|
||||
runtimeMode: "pipeline",
|
||||
greeting: "",
|
||||
enableInterrupt: difyForm.enableInterrupt,
|
||||
llmCredentialId: null, // LLM 由 Dify 应用提供
|
||||
asrCredentialId: difyForm.asr || null,
|
||||
ttsCredentialId: difyForm.voice || null,
|
||||
enableInterrupt: true,
|
||||
llmCredentialId: null,
|
||||
asrCredentialId: null,
|
||||
ttsCredentialId: null,
|
||||
realtimeCredentialId: null,
|
||||
knowledgeBaseId: null,
|
||||
config: { apiUrl: difyForm.apiUrl, apiKey: difyForm.apiKey },
|
||||
prompt: "",
|
||||
apiUrl: "",
|
||||
apiKey: "",
|
||||
appId: "",
|
||||
graph: {},
|
||||
...over,
|
||||
};
|
||||
}
|
||||
|
||||
// 统一的保存:新建 POST / 编辑 PUT
|
||||
async function save(payload: AssistantUpsert) {
|
||||
setSaving(true);
|
||||
setSaveError(null);
|
||||
try {
|
||||
if (editingId) await assistantsApi.update(editingId, payload);
|
||||
else await assistantsApi.create(payload);
|
||||
@@ -540,49 +502,76 @@ export function AssistantPage() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSavePrompt() {
|
||||
void save(
|
||||
baseUpsert({
|
||||
name: form.name.trim(),
|
||||
type: "prompt",
|
||||
runtimeMode: form.runtimeMode,
|
||||
greeting: form.greeting,
|
||||
enableInterrupt: form.enableInterrupt,
|
||||
llmCredentialId: form.model || null,
|
||||
asrCredentialId: form.asr || null,
|
||||
ttsCredentialId: form.voice || null,
|
||||
realtimeCredentialId: form.realtimeModel || null,
|
||||
knowledgeBaseId: form.knowledgeBase || null,
|
||||
prompt: form.prompt,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// ---- Dify ----
|
||||
function fillDifyForm(a: Assistant) {
|
||||
setDifyForm({
|
||||
name: a.name,
|
||||
apiUrl: a.apiUrl,
|
||||
apiKey: a.apiKey, // 编辑时为打码值,不改则原样回传(后端哨兵保留旧 key)
|
||||
asr: a.asrCredentialId ?? "",
|
||||
voice: a.ttsCredentialId ?? "",
|
||||
enableInterrupt: a.enableInterrupt,
|
||||
});
|
||||
}
|
||||
|
||||
function handleSaveDify() {
|
||||
void save(
|
||||
baseUpsert({
|
||||
name: difyForm.name.trim(),
|
||||
type: "dify",
|
||||
enableInterrupt: difyForm.enableInterrupt,
|
||||
asrCredentialId: difyForm.asr || null,
|
||||
ttsCredentialId: difyForm.voice || null,
|
||||
apiUrl: difyForm.apiUrl,
|
||||
apiKey: difyForm.apiKey,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// ---- FastGPT ----
|
||||
function fillFastGptForm(a: Assistant) {
|
||||
setFastGptForm({
|
||||
name: a.name,
|
||||
appId: cfgStr(a, "appId"),
|
||||
apiUrl: cfgStr(a, "apiUrl"),
|
||||
apiKey: cfgStr(a, "apiKey"),
|
||||
appId: a.appId,
|
||||
apiUrl: a.apiUrl,
|
||||
apiKey: a.apiKey,
|
||||
asr: a.asrCredentialId ?? "",
|
||||
voice: a.ttsCredentialId ?? "",
|
||||
enableInterrupt: a.enableInterrupt,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSaveFastGpt() {
|
||||
setSaving(true);
|
||||
setSaveError(null);
|
||||
const payload: AssistantUpsert = {
|
||||
name: fastGptForm.name.trim(),
|
||||
type: "fastgpt",
|
||||
runtimeMode: "pipeline",
|
||||
greeting: "",
|
||||
enableInterrupt: fastGptForm.enableInterrupt,
|
||||
llmCredentialId: null,
|
||||
asrCredentialId: fastGptForm.asr || null,
|
||||
ttsCredentialId: fastGptForm.voice || null,
|
||||
realtimeCredentialId: null,
|
||||
knowledgeBaseId: null,
|
||||
config: {
|
||||
function handleSaveFastGpt() {
|
||||
void save(
|
||||
baseUpsert({
|
||||
name: fastGptForm.name.trim(),
|
||||
type: "fastgpt",
|
||||
enableInterrupt: fastGptForm.enableInterrupt,
|
||||
asrCredentialId: fastGptForm.asr || null,
|
||||
ttsCredentialId: fastGptForm.voice || null,
|
||||
appId: fastGptForm.appId,
|
||||
apiUrl: fastGptForm.apiUrl,
|
||||
apiKey: fastGptForm.apiKey,
|
||||
},
|
||||
};
|
||||
try {
|
||||
if (editingId) await assistantsApi.update(editingId, payload);
|
||||
else await assistantsApi.create(payload);
|
||||
await loadAssistants();
|
||||
setView("list");
|
||||
} catch (error) {
|
||||
setSaveError(error instanceof Error ? error.message : "保存失败");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const listItems: AssistantListItem[] = assistants.map((a) => ({
|
||||
|
||||
Reference in New Issue
Block a user