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:
@@ -6,6 +6,7 @@ import {
|
||||
ChevronUp,
|
||||
Copy,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Loader2,
|
||||
MoreHorizontal,
|
||||
Pencil,
|
||||
@@ -62,6 +63,7 @@ const capabilities: ModelType[] = [
|
||||
"TTS",
|
||||
"Realtime",
|
||||
"Embedding",
|
||||
"Agent",
|
||||
];
|
||||
|
||||
const capabilityFilters = ["全部", ...capabilities] as const;
|
||||
@@ -120,6 +122,15 @@ function fieldValue(draft: ResourceDraft, field: InterfaceField): unknown {
|
||||
: draft.values[field.key];
|
||||
}
|
||||
|
||||
function secretMask(
|
||||
masks: Record<string, unknown>,
|
||||
field: InterfaceField,
|
||||
): string {
|
||||
if (field.group !== "secrets") return "";
|
||||
const value = masks[field.key];
|
||||
return typeof value === "string" && value.includes("****") ? value : "";
|
||||
}
|
||||
|
||||
function extraBodyText(values: Record<string, unknown>): string {
|
||||
const value = values.extraBody;
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return "";
|
||||
@@ -171,6 +182,7 @@ export function ComponentsModelsPage() {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
const [draft, setDraft] = useState<ResourceDraft>(emptyDraft);
|
||||
const [storedSecretMasks, setStoredSecretMasks] = useState<Record<string, unknown>>({});
|
||||
const [extraBodyDraft, setExtraBodyDraft] = useState("");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState("");
|
||||
@@ -284,6 +296,7 @@ export function ComponentsModelsPage() {
|
||||
values: definition ? defaults(definition) : {},
|
||||
secrets: {},
|
||||
}));
|
||||
setStoredSecretMasks({});
|
||||
setExtraBodyDraft("");
|
||||
setTestResult(null);
|
||||
}
|
||||
@@ -301,6 +314,7 @@ export function ComponentsModelsPage() {
|
||||
supportImageInput:
|
||||
capability === "LLM" ? previous.supportImageInput : false,
|
||||
}));
|
||||
setStoredSecretMasks({});
|
||||
setExtraBodyDraft("");
|
||||
setTestResult(null);
|
||||
}
|
||||
@@ -314,8 +328,10 @@ export function ComponentsModelsPage() {
|
||||
...emptyDraft,
|
||||
interfaceType: first?.interfaceType ?? "",
|
||||
values: first ? defaults(first) : {},
|
||||
secrets: {},
|
||||
supportImageInput: false,
|
||||
});
|
||||
setStoredSecretMasks({});
|
||||
setExtraBodyDraft("");
|
||||
setSaveError("");
|
||||
setTestResult(null);
|
||||
@@ -329,9 +345,10 @@ export function ComponentsModelsPage() {
|
||||
capability: resource.capability,
|
||||
interfaceType: resource.interfaceType,
|
||||
values: resource.values,
|
||||
secrets: resource.secrets,
|
||||
secrets: {},
|
||||
supportImageInput: resource.supportImageInput,
|
||||
});
|
||||
setStoredSecretMasks(resource.secrets);
|
||||
setExtraBodyDraft(extraBodyText(resource.values));
|
||||
setSaveError("");
|
||||
setTestResult(null);
|
||||
@@ -355,7 +372,10 @@ export function ComponentsModelsPage() {
|
||||
!extraBodyError &&
|
||||
selectedDefinition.fieldSchema.fields.every((field) => {
|
||||
if (!field.required) return true;
|
||||
const value = fieldValue(draft, field);
|
||||
const value =
|
||||
field.group === "secrets" && secretMask(storedSecretMasks, field)
|
||||
? secretMask(storedSecretMasks, field)
|
||||
: fieldValue(draft, field);
|
||||
return value !== undefined && value !== null && value !== "";
|
||||
}),
|
||||
);
|
||||
@@ -369,7 +389,9 @@ export function ComponentsModelsPage() {
|
||||
draft.capability === "LLM"
|
||||
? valuesWithExtraBody(draft.values, extraBody)
|
||||
: draft.values,
|
||||
secrets: draft.secrets,
|
||||
secrets: editingId
|
||||
? { ...storedSecretMasks, ...draft.secrets }
|
||||
: draft.secrets,
|
||||
supportImageInput:
|
||||
draft.capability === "LLM" ? draft.supportImageInput : false,
|
||||
enabled: editingId
|
||||
@@ -747,6 +769,7 @@ export function ComponentsModelsPage() {
|
||||
key={`${field.group}-${field.key}`}
|
||||
field={field}
|
||||
value={fieldValue(draft, field)}
|
||||
storedValueMask={secretMask(storedSecretMasks, field)}
|
||||
onChange={(value) => updateField(field, value)}
|
||||
/>
|
||||
))}
|
||||
@@ -779,6 +802,7 @@ export function ComponentsModelsPage() {
|
||||
key={`${field.group}-${field.key}`}
|
||||
field={field}
|
||||
value={fieldValue(draft, field)}
|
||||
storedValueMask={secretMask(storedSecretMasks, field)}
|
||||
onChange={(value) => updateField(field, value)}
|
||||
/>
|
||||
))
|
||||
@@ -901,12 +925,16 @@ function FieldSection({
|
||||
function DynamicField({
|
||||
field,
|
||||
value,
|
||||
storedValueMask,
|
||||
onChange,
|
||||
}: {
|
||||
field: InterfaceField;
|
||||
value: unknown;
|
||||
storedValueMask?: string;
|
||||
onChange: (value: unknown) => void;
|
||||
}) {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
if (field.type === "boolean") {
|
||||
return (
|
||||
<div className="flex items-center justify-between rounded-xl border border-hairline p-4">
|
||||
@@ -942,17 +970,63 @@ function DynamicField({
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === "password") {
|
||||
const inputValue = String(value ?? "");
|
||||
const hasStoredValue = Boolean(storedValueMask);
|
||||
|
||||
return (
|
||||
<Field label={field.label} required={field.required}>
|
||||
{hasStoredValue && (
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>当前密钥</span>
|
||||
<code className="rounded-md bg-surface-strong px-2 py-1 font-mono text-foreground">
|
||||
{storedValueMask}
|
||||
</code>
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={inputValue}
|
||||
onChange={(event) => {
|
||||
const nextValue = event.target.value;
|
||||
if (!nextValue) setShowPassword(false);
|
||||
onChange(nextValue);
|
||||
}}
|
||||
placeholder={
|
||||
hasStoredValue ? "已配置,留空则保持不变" : undefined
|
||||
}
|
||||
autoComplete="new-password"
|
||||
className="pr-10"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!inputValue}
|
||||
onClick={() => setShowPassword((current) => !current)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-soft transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40"
|
||||
aria-label={showPassword ? "隐藏密钥" : "显示密钥"}
|
||||
>
|
||||
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
{hasStoredValue && (
|
||||
<p className="text-xs leading-5 text-muted-foreground">
|
||||
仅显示当前密钥首尾用于识别。留空可保持原密钥,输入新值将覆盖原密钥。
|
||||
</p>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Field label={field.label} required={field.required}>
|
||||
<Input
|
||||
type={
|
||||
field.type === "password"
|
||||
? "password"
|
||||
: field.type === "number"
|
||||
? "number"
|
||||
: field.type === "url"
|
||||
? "url"
|
||||
: "text"
|
||||
field.type === "number"
|
||||
? "number"
|
||||
: field.type === "url"
|
||||
? "url"
|
||||
: "text"
|
||||
}
|
||||
value={String(value ?? "")}
|
||||
onChange={(event) =>
|
||||
@@ -962,11 +1036,6 @@ function DynamicField({
|
||||
: event.target.value,
|
||||
)
|
||||
}
|
||||
placeholder={
|
||||
field.type === "password" && String(value ?? "").includes("****")
|
||||
? "已配置,留空或保留掩码表示不修改"
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user