Add Agent capabilities and configurations across the application
- Extend the AssistantConfig model to include agent_interface_type, agent_values, and agent_secrets for enhanced agent management. - Update ModelType to include "Agent" for broader capability support. - Seed new agent models and configurations in the database for Dify, FastGPT, and OpenCode applications. - Modify the Assistant routes to validate and handle agent capabilities. - Implement agent resource resolution in the config resolver for runtime configuration. - Enhance the interface catalog with agent-specific fields and definitions. - Update frontend components to manage agent configurations, including form handling and state management for agent-related inputs.
This commit is contained in:
@@ -131,9 +131,7 @@ type AssistantForm = {
|
||||
|
||||
type FastGptForm = {
|
||||
name: string;
|
||||
appId: string;
|
||||
apiUrl: string;
|
||||
apiKey: string;
|
||||
agent: string;
|
||||
asr: string;
|
||||
voice: string;
|
||||
enableInterrupt: boolean;
|
||||
@@ -141,8 +139,7 @@ type FastGptForm = {
|
||||
|
||||
type DifyForm = {
|
||||
name: string;
|
||||
apiUrl: string;
|
||||
apiKey: string;
|
||||
agent: string;
|
||||
asr: string;
|
||||
voice: string;
|
||||
enableInterrupt: boolean;
|
||||
@@ -151,8 +148,7 @@ type DifyForm = {
|
||||
type OpenCodeForm = {
|
||||
name: string;
|
||||
prompt: string;
|
||||
apiUrl: string;
|
||||
apiKey: string;
|
||||
agent: string;
|
||||
model: string;
|
||||
asr: string;
|
||||
voice: string;
|
||||
@@ -226,9 +222,7 @@ function blankPromptForm(name: string): AssistantForm {
|
||||
function blankFastGptForm(name: string): FastGptForm {
|
||||
return {
|
||||
name,
|
||||
appId: "",
|
||||
apiUrl: "",
|
||||
apiKey: "",
|
||||
agent: "",
|
||||
asr: "",
|
||||
voice: "",
|
||||
enableInterrupt: true,
|
||||
@@ -238,8 +232,7 @@ function blankFastGptForm(name: string): FastGptForm {
|
||||
function blankDifyForm(name: string): DifyForm {
|
||||
return {
|
||||
name,
|
||||
apiUrl: "",
|
||||
apiKey: "",
|
||||
agent: "",
|
||||
asr: "",
|
||||
voice: "",
|
||||
enableInterrupt: true,
|
||||
@@ -250,8 +243,7 @@ function blankOpenCodeForm(name: string): OpenCodeForm {
|
||||
return {
|
||||
name,
|
||||
prompt: "",
|
||||
apiUrl: "",
|
||||
apiKey: "",
|
||||
agent: "",
|
||||
model: "",
|
||||
asr: "",
|
||||
voice: "",
|
||||
@@ -434,6 +426,14 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
modelResources
|
||||
.filter((c) => c.capability === type)
|
||||
.map((c) => ({ value: c.id, label: c.name }));
|
||||
const agentOptions = (interfaceType: "dify" | "fastgpt" | "opencode") =>
|
||||
modelResources
|
||||
.filter(
|
||||
(resource) =>
|
||||
resource.capability === "Agent" &&
|
||||
resource.interfaceType === interfaceType,
|
||||
)
|
||||
.map((resource) => ({ value: resource.id, label: resource.name }));
|
||||
const visionModelOptionsFor = (currentModelId: string) => modelResources
|
||||
.filter(
|
||||
(c) =>
|
||||
@@ -573,15 +573,15 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
if (view === "create") {
|
||||
setSavedSnapshot(JSON.stringify(form));
|
||||
} else if (view === "create-dify") {
|
||||
const next = { ...difyForm, apiKey: "" };
|
||||
const next = { ...difyForm };
|
||||
setDifyForm(next);
|
||||
setSavedSnapshot(JSON.stringify(next));
|
||||
} else if (view === "create-fastgpt") {
|
||||
const next = { ...fastGptForm, apiKey: "" };
|
||||
const next = { ...fastGptForm };
|
||||
setFastGptForm(next);
|
||||
setSavedSnapshot(JSON.stringify(next));
|
||||
} else if (view === "create-opencode") {
|
||||
const next = { ...openCodeForm, apiKey: "" };
|
||||
const next = { ...openCodeForm };
|
||||
setOpenCodeForm(next);
|
||||
setSavedSnapshot(JSON.stringify(next));
|
||||
} else if (view === "workflow") {
|
||||
@@ -626,9 +626,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
function fillDifyForm(a: Assistant): DifyForm {
|
||||
const next: DifyForm = {
|
||||
name: a.name,
|
||||
apiUrl: a.apiUrl,
|
||||
// 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key
|
||||
apiKey: "",
|
||||
agent: a.modelResourceIds.Agent ?? "",
|
||||
asr: a.modelResourceIds.ASR ?? "",
|
||||
voice: a.modelResourceIds.TTS ?? "",
|
||||
enableInterrupt: a.enableInterrupt,
|
||||
@@ -644,11 +642,10 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
type: "dify",
|
||||
enableInterrupt: difyForm.enableInterrupt,
|
||||
modelResourceIds: {
|
||||
...(difyForm.agent ? { Agent: difyForm.agent } : {}),
|
||||
...(difyForm.asr ? { ASR: difyForm.asr } : {}),
|
||||
...(difyForm.voice ? { TTS: difyForm.voice } : {}),
|
||||
},
|
||||
apiUrl: difyForm.apiUrl,
|
||||
apiKey: difyForm.apiKey,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -657,10 +654,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
function fillFastGptForm(a: Assistant): FastGptForm {
|
||||
const next: FastGptForm = {
|
||||
name: a.name,
|
||||
appId: a.appId,
|
||||
apiUrl: a.apiUrl,
|
||||
// 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key
|
||||
apiKey: "",
|
||||
agent: a.modelResourceIds.Agent ?? "",
|
||||
asr: a.modelResourceIds.ASR ?? "",
|
||||
voice: a.modelResourceIds.TTS ?? "",
|
||||
enableInterrupt: a.enableInterrupt,
|
||||
@@ -676,12 +670,10 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
type: "fastgpt",
|
||||
enableInterrupt: fastGptForm.enableInterrupt,
|
||||
modelResourceIds: {
|
||||
...(fastGptForm.agent ? { Agent: fastGptForm.agent } : {}),
|
||||
...(fastGptForm.asr ? { ASR: fastGptForm.asr } : {}),
|
||||
...(fastGptForm.voice ? { TTS: fastGptForm.voice } : {}),
|
||||
},
|
||||
appId: fastGptForm.appId,
|
||||
apiUrl: fastGptForm.apiUrl,
|
||||
apiKey: fastGptForm.apiKey,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -691,9 +683,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
const next: OpenCodeForm = {
|
||||
name: a.name,
|
||||
prompt: a.prompt,
|
||||
apiUrl: a.apiUrl,
|
||||
// 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key
|
||||
apiKey: "",
|
||||
agent: a.modelResourceIds.Agent ?? "",
|
||||
model: a.modelResourceIds.LLM ?? "",
|
||||
asr: a.modelResourceIds.ASR ?? "",
|
||||
voice: a.modelResourceIds.TTS ?? "",
|
||||
@@ -772,13 +762,12 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
visionEnabled: openCodeForm.visionEnabled,
|
||||
visionModelResourceId: openCodeForm.visionModelResourceId || null,
|
||||
modelResourceIds: {
|
||||
...(openCodeForm.agent ? { Agent: openCodeForm.agent } : {}),
|
||||
...(openCodeForm.model ? { LLM: openCodeForm.model } : {}),
|
||||
...(openCodeForm.asr ? { ASR: openCodeForm.asr } : {}),
|
||||
...(openCodeForm.voice ? { TTS: openCodeForm.voice } : {}),
|
||||
},
|
||||
prompt: openCodeForm.prompt,
|
||||
apiUrl: openCodeForm.apiUrl,
|
||||
apiKey: openCodeForm.apiKey,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -1355,7 +1344,9 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
)}
|
||||
<Button
|
||||
className="gap-2"
|
||||
disabled={saving || !dirty || !difyForm.name.trim()}
|
||||
disabled={
|
||||
saving || !dirty || !difyForm.name.trim() || !difyForm.agent
|
||||
}
|
||||
onClick={() => void handleSaveDify()}
|
||||
>
|
||||
{saving ? (
|
||||
@@ -1373,20 +1364,14 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
<SectionCard
|
||||
icon={<Boxes size={18} />}
|
||||
title="Dify 应用配置"
|
||||
description="填写 API URL 与 API Key 以对接 Dify 应用。开场白、知识库、提示词等对话编排请在 Dify 平台配置,本页不重复设置。"
|
||||
description="从「模型资源」中选择 Dify 应用。开场白、知识库、提示词等对话编排请在 Dify 平台配置,本页不重复设置。"
|
||||
>
|
||||
<InputField
|
||||
label="API URL"
|
||||
value={difyForm.apiUrl}
|
||||
onChange={(value) => updateDifyForm("apiUrl", value)}
|
||||
placeholder="https://api.dify.ai/v1/chat-messages"
|
||||
/>
|
||||
<SecretInputField
|
||||
label="API Key"
|
||||
value={difyForm.apiKey}
|
||||
onChange={(value) => updateDifyForm("apiKey", value)}
|
||||
placeholder="请输入 Dify API Key"
|
||||
storedValueMask={storedApiKeyMask}
|
||||
<ResourceSelectField
|
||||
label="Dify 应用"
|
||||
value={difyForm.agent}
|
||||
onChange={(value) => updateDifyForm("agent", value)}
|
||||
options={agentOptions("dify")}
|
||||
noneLabel="请选择"
|
||||
/>
|
||||
</SectionCard>
|
||||
|
||||
@@ -1454,7 +1439,12 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
)}
|
||||
<Button
|
||||
className="gap-2"
|
||||
disabled={saving || !dirty || !fastGptForm.name.trim()}
|
||||
disabled={
|
||||
saving ||
|
||||
!dirty ||
|
||||
!fastGptForm.name.trim() ||
|
||||
!fastGptForm.agent
|
||||
}
|
||||
onClick={() => void handleSaveFastGpt()}
|
||||
>
|
||||
{saving ? (
|
||||
@@ -1472,35 +1462,14 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
<SectionCard
|
||||
icon={<Database size={18} />}
|
||||
title="FastGPT 应用配置"
|
||||
description="填写 App ID、API URL 与 API Key 以对接 FastGPT 应用。开场白、知识库、提示词等对话编排请在 FastGPT 平台配置,本页不重复设置。"
|
||||
description="从「模型资源」中选择 FastGPT 应用。开场白、知识库、提示词等对话编排请在 FastGPT 平台配置,本页不重复设置。"
|
||||
>
|
||||
<InputField
|
||||
label="App ID"
|
||||
hint={
|
||||
"FastGPT 应用的 appId,用于拉取应用预设开场白等信息。\n在 FastGPT「应用详情 → 发布渠道 / API 访问」中获取。\n留空则不展示应用开场白,回退使用本助手自身的开场白。"
|
||||
}
|
||||
value={fastGptForm.appId}
|
||||
onChange={(value) => updateFastGptForm("appId", value)}
|
||||
placeholder="请输入 FastGPT 应用 ID"
|
||||
/>
|
||||
<InputField
|
||||
label="API URL"
|
||||
hint={
|
||||
"填 FastGPT 服务的主机根地址即可,例如 http://localhost:3000。\n系统会自动拼接 /api/v1/chat/completions,无需手动带上该路径。\n(即使粘贴了完整接口地址也会自动归一,但建议只填根地址。)"
|
||||
}
|
||||
value={fastGptForm.apiUrl}
|
||||
onChange={(value) => updateFastGptForm("apiUrl", value)}
|
||||
placeholder="http://localhost:3000"
|
||||
/>
|
||||
<SecretInputField
|
||||
label="API Key"
|
||||
hint={
|
||||
"FastGPT 为该应用生成的 API Key(形如 fastgpt-xxxxx)。\n在 FastGPT「应用详情 → API 访问」中创建。每个应用的 Key 独立。"
|
||||
}
|
||||
value={fastGptForm.apiKey}
|
||||
onChange={(value) => updateFastGptForm("apiKey", value)}
|
||||
placeholder="请输入 FastGPT API Key"
|
||||
storedValueMask={storedApiKeyMask}
|
||||
<ResourceSelectField
|
||||
label="FastGPT 应用"
|
||||
value={fastGptForm.agent}
|
||||
onChange={(value) => updateFastGptForm("agent", value)}
|
||||
options={agentOptions("fastgpt")}
|
||||
noneLabel="请选择"
|
||||
/>
|
||||
</SectionCard>
|
||||
|
||||
@@ -1572,7 +1541,12 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
)}
|
||||
<Button
|
||||
className="gap-2"
|
||||
disabled={saving || !dirty || !openCodeForm.name.trim()}
|
||||
disabled={
|
||||
saving ||
|
||||
!dirty ||
|
||||
!openCodeForm.name.trim() ||
|
||||
!openCodeForm.agent
|
||||
}
|
||||
onClick={() => void handleSaveOpenCode()}
|
||||
>
|
||||
{saving ? (
|
||||
@@ -1590,20 +1564,14 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
<SectionCard
|
||||
icon={<Terminal size={18} />}
|
||||
title="OpenCode 服务配置"
|
||||
description="填写 OpenCode 服务地址与 API Key 以对接代码助手。"
|
||||
description="从「模型资源」中选择 OpenCode 服务资源。"
|
||||
>
|
||||
<InputField
|
||||
label="OpenCode URL"
|
||||
value={openCodeForm.apiUrl}
|
||||
onChange={(value) => updateOpenCodeForm("apiUrl", value)}
|
||||
placeholder="http://localhost:4096"
|
||||
/>
|
||||
<SecretInputField
|
||||
label="API Key"
|
||||
value={openCodeForm.apiKey}
|
||||
onChange={(value) => updateOpenCodeForm("apiKey", value)}
|
||||
placeholder="请输入 OpenCode API Key"
|
||||
storedValueMask={storedApiKeyMask}
|
||||
<ResourceSelectField
|
||||
label="OpenCode 服务"
|
||||
value={openCodeForm.agent}
|
||||
onChange={(value) => updateOpenCodeForm("agent", value)}
|
||||
options={agentOptions("opencode")}
|
||||
noneLabel="请选择"
|
||||
/>
|
||||
</SectionCard>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user