refactor: use config workbench for AI agent creation
This commit is contained in:
@@ -26,6 +26,7 @@ import { Input } from "@/components/ui/input"
|
|||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { Textarea } from "@/components/ui/textarea"
|
import { Textarea } from "@/components/ui/textarea"
|
||||||
import {
|
import {
|
||||||
|
createAIAgent,
|
||||||
fetchAIAgent,
|
fetchAIAgent,
|
||||||
fetchAIAgentWorkflow,
|
fetchAIAgentWorkflow,
|
||||||
fetchAIConfigsAll,
|
fetchAIConfigsAll,
|
||||||
@@ -114,10 +115,13 @@ function uniqueNumbers(input: number[]) {
|
|||||||
export function AIAgentConfigWorkbench({
|
export function AIAgentConfigWorkbench({
|
||||||
agentId,
|
agentId,
|
||||||
onAgentSaved,
|
onAgentSaved,
|
||||||
|
onAgentCreated,
|
||||||
}: {
|
}: {
|
||||||
agentId: number
|
agentId?: number | null
|
||||||
onAgentSaved?: () => void
|
onAgentSaved?: () => void
|
||||||
|
onAgentCreated?: (agent: AIAgent) => void
|
||||||
}) {
|
}) {
|
||||||
|
const [currentAgentId, setCurrentAgentId] = useState(agentId ?? null)
|
||||||
const [activeSection, setActiveSection] = useState<SectionKey>("basic")
|
const [activeSection, setActiveSection] = useState<SectionKey>("basic")
|
||||||
const [agent, setAgent] = useState<AIAgent | null>(null)
|
const [agent, setAgent] = useState<AIAgent | null>(null)
|
||||||
const [workflow, setWorkflow] = useState<AIWorkflow | null>(null)
|
const [workflow, setWorkflow] = useState<AIWorkflow | null>(null)
|
||||||
@@ -162,16 +166,14 @@ export function AIAgentConfigWorkbench({
|
|||||||
[workflow?.id, workflow?.updatedAt]
|
[workflow?.id, workflow?.updatedAt]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentAgentId(agentId ?? null)
|
||||||
|
}, [agentId])
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
if (!Number.isFinite(agentId) || agentId <= 0) {
|
|
||||||
setLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const [
|
const [
|
||||||
agentDetail,
|
|
||||||
workflowDetail,
|
|
||||||
specs,
|
specs,
|
||||||
configs,
|
configs,
|
||||||
bases,
|
bases,
|
||||||
@@ -179,8 +181,6 @@ export function AIAgentConfigWorkbench({
|
|||||||
skillList,
|
skillList,
|
||||||
catalog,
|
catalog,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
fetchAIAgent(agentId),
|
|
||||||
fetchAIAgentWorkflow(agentId),
|
|
||||||
fetchAIWorkflowNodeSpecs(),
|
fetchAIWorkflowNodeSpecs(),
|
||||||
fetchAIConfigsAll({ modelType: AIModelType.LLM }),
|
fetchAIConfigsAll({ modelType: AIModelType.LLM }),
|
||||||
fetchKnowledgeBasesAll({ status: Status.Ok }),
|
fetchKnowledgeBasesAll({ status: Status.Ok }),
|
||||||
@@ -189,14 +189,44 @@ export function AIAgentConfigWorkbench({
|
|||||||
fetchMCPCatalog(),
|
fetchMCPCatalog(),
|
||||||
])
|
])
|
||||||
|
|
||||||
setAgent(agentDetail)
|
|
||||||
setWorkflow(workflowDetail)
|
|
||||||
setNodeSpecs(specs ?? [])
|
setNodeSpecs(specs ?? [])
|
||||||
setAIConfigs(configs ?? [])
|
setAIConfigs(configs ?? [])
|
||||||
setKnowledgeBases(bases ?? [])
|
setKnowledgeBases(bases ?? [])
|
||||||
setAgentTeams(teams ?? [])
|
setAgentTeams(teams ?? [])
|
||||||
setSkills(skillList ?? [])
|
setSkills(skillList ?? [])
|
||||||
setToolCatalog(catalog ?? [])
|
setToolCatalog(catalog ?? [])
|
||||||
|
|
||||||
|
if (!currentAgentId || currentAgentId <= 0) {
|
||||||
|
setAgent(null)
|
||||||
|
setWorkflow(null)
|
||||||
|
setName("")
|
||||||
|
setDescription("")
|
||||||
|
setAIConfigId("")
|
||||||
|
setServiceMode(String(IMConversationServiceMode.AIFirst))
|
||||||
|
setSystemPrompt("")
|
||||||
|
setWelcomeMessage("")
|
||||||
|
setReplyTimeoutSeconds("180")
|
||||||
|
setHandoffMode(String(AIAgentHandoffMode.WaitPool))
|
||||||
|
setFallbackMode(String(AIAgentFallbackMode.NoAnswer))
|
||||||
|
setFallbackMessage("")
|
||||||
|
setSelectedKnowledgeIds([])
|
||||||
|
setSelectedTeamIds([])
|
||||||
|
setSelectedSkillIds([])
|
||||||
|
setDirectTools([])
|
||||||
|
setWorkflowName("新建 Agent 会话流程")
|
||||||
|
setWorkflowDescription("")
|
||||||
|
setDefinition(emptyDefinition)
|
||||||
|
setValidation(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const [agentDetail, workflowDetail] = await Promise.all([
|
||||||
|
fetchAIAgent(currentAgentId),
|
||||||
|
fetchAIAgentWorkflow(currentAgentId),
|
||||||
|
])
|
||||||
|
|
||||||
|
setAgent(agentDetail)
|
||||||
|
setWorkflow(workflowDetail)
|
||||||
setName(agentDetail.name)
|
setName(agentDetail.name)
|
||||||
setDescription(agentDetail.description || "")
|
setDescription(agentDetail.description || "")
|
||||||
setAIConfigId(toText(agentDetail.aiConfigId))
|
setAIConfigId(toText(agentDetail.aiConfigId))
|
||||||
@@ -220,7 +250,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [agentId])
|
}, [currentAgentId])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void loadData()
|
void loadData()
|
||||||
@@ -361,13 +391,20 @@ export function AIAgentConfigWorkbench({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveAgentSettings() {
|
async function saveAgentSettings() {
|
||||||
if (!agent) return
|
|
||||||
setSavingAgent(true)
|
setSavingAgent(true)
|
||||||
try {
|
try {
|
||||||
const payload = buildPayload()
|
const payload = buildPayload()
|
||||||
await updateAIAgent({ id: agent.id, ...payload })
|
if (agent) {
|
||||||
toast.success("Agent config saved")
|
await updateAIAgent({ id: agent.id, ...payload })
|
||||||
await loadData()
|
toast.success("Agent config saved")
|
||||||
|
await loadData()
|
||||||
|
} else {
|
||||||
|
const created = await createAIAgent(payload)
|
||||||
|
setCurrentAgentId(created.id)
|
||||||
|
setAgent(created)
|
||||||
|
toast.success("Agent created")
|
||||||
|
onAgentCreated?.(created)
|
||||||
|
}
|
||||||
onAgentSaved?.()
|
onAgentSaved?.()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error instanceof Error ? error.message : "Failed to save Agent config")
|
toast.error(error instanceof Error ? error.message : "Failed to save Agent config")
|
||||||
@@ -377,10 +414,11 @@ export function AIAgentConfigWorkbench({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveWorkflowDraft() {
|
async function saveWorkflowDraft() {
|
||||||
|
if (!currentAgentId) return
|
||||||
setSavingWorkflow(true)
|
setSavingWorkflow(true)
|
||||||
try {
|
try {
|
||||||
const saved = await saveAIAgentWorkflow({
|
const saved = await saveAIAgentWorkflow({
|
||||||
agentId,
|
agentId: currentAgentId,
|
||||||
name: workflowName,
|
name: workflowName,
|
||||||
description: workflowDescription,
|
description: workflowDescription,
|
||||||
definition,
|
definition,
|
||||||
@@ -410,15 +448,16 @@ export function AIAgentConfigWorkbench({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function publishWorkflow() {
|
async function publishWorkflow() {
|
||||||
|
if (!currentAgentId) return
|
||||||
setSavingWorkflow(true)
|
setSavingWorkflow(true)
|
||||||
try {
|
try {
|
||||||
await saveAIAgentWorkflow({
|
await saveAIAgentWorkflow({
|
||||||
agentId,
|
agentId: currentAgentId,
|
||||||
name: workflowName,
|
name: workflowName,
|
||||||
description: workflowDescription,
|
description: workflowDescription,
|
||||||
definition,
|
definition,
|
||||||
})
|
})
|
||||||
const version = await publishAIAgentWorkflow(agentId, definition)
|
const version = await publishAIAgentWorkflow(currentAgentId, definition)
|
||||||
toast.success(`Published version ${version.version}`)
|
toast.success(`Published version ${version.version}`)
|
||||||
await loadData()
|
await loadData()
|
||||||
onAgentSaved?.()
|
onAgentSaved?.()
|
||||||
@@ -452,7 +491,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
<BotMessageSquareIcon className="size-5" />
|
<BotMessageSquareIcon className="size-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<h1 className="truncate text-base font-semibold">{agent?.name ?? "AI Agent 配置"}</h1>
|
<h1 className="truncate text-base font-semibold">{agent?.name ?? "新建 AI Agent"}</h1>
|
||||||
<div className="mt-1 flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="mt-1 flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
{agent?.statusName ? <Badge variant="secondary">{agent.statusName}</Badge> : null}
|
{agent?.statusName ? <Badge variant="secondary">{agent.statusName}</Badge> : null}
|
||||||
{agent?.workflowVersionId ? <Badge>已发布流程</Badge> : <Badge variant="outline">草稿流程</Badge>}
|
{agent?.workflowVersionId ? <Badge>已发布流程</Badge> : <Badge variant="outline">草稿流程</Badge>}
|
||||||
@@ -464,7 +503,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
<SaveIcon className="size-4" />
|
<SaveIcon className="size-4" />
|
||||||
保存
|
保存
|
||||||
</Button>
|
</Button>
|
||||||
<Button disabled={savingWorkflow || loading} onClick={publishWorkflow}>
|
<Button disabled={savingWorkflow || loading || !currentAgentId} onClick={publishWorkflow}>
|
||||||
<SendIcon className="size-4" />
|
<SendIcon className="size-4" />
|
||||||
发布流程
|
发布流程
|
||||||
</Button>
|
</Button>
|
||||||
@@ -690,15 +729,15 @@ export function AIAgentConfigWorkbench({
|
|||||||
{validation.valid ? "校验通过" : `${validation.errors.length} 个问题`}
|
{validation.valid ? "校验通过" : `${validation.errors.length} 个问题`}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
<Button variant="outline" disabled={savingWorkflow} onClick={validateWorkflowDraft}>
|
<Button variant="outline" disabled={savingWorkflow || !currentAgentId} onClick={validateWorkflowDraft}>
|
||||||
<CheckCircle2Icon className="size-4" />
|
<CheckCircle2Icon className="size-4" />
|
||||||
校验
|
校验
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" disabled={savingWorkflow} onClick={saveWorkflowDraft}>
|
<Button variant="outline" disabled={savingWorkflow || !currentAgentId} onClick={saveWorkflowDraft}>
|
||||||
<SaveIcon className="size-4" />
|
<SaveIcon className="size-4" />
|
||||||
保存草稿
|
保存草稿
|
||||||
</Button>
|
</Button>
|
||||||
<Button disabled={savingWorkflow} onClick={publishWorkflow}>
|
<Button disabled={savingWorkflow || !currentAgentId} onClick={publishWorkflow}>
|
||||||
<SendIcon className="size-4" />
|
<SendIcon className="size-4" />
|
||||||
发布
|
发布
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,6 @@ import {
|
|||||||
import { IMConversationServiceMode, Status } from "@/lib/generated/enums";
|
import { IMConversationServiceMode, Status } from "@/lib/generated/enums";
|
||||||
import { useI18n } from "@/i18n/provider";
|
import { useI18n } from "@/i18n/provider";
|
||||||
import { AIAgentConfigWorkbench } from "./_components/config-workbench";
|
import { AIAgentConfigWorkbench } from "./_components/config-workbench";
|
||||||
import { EditDialog } from "./_components/edit";
|
|
||||||
|
|
||||||
type TFunction = (key: string, values?: Record<string, string | number>) => string;
|
type TFunction = (key: string, values?: Record<string, string | number>) => string;
|
||||||
|
|
||||||
@@ -67,6 +66,7 @@ export default function DashboardAIAgentsPage() {
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const statusOptions = useMemo(() => getStatusOptions(t), [t]);
|
const statusOptions = useMemo(() => getStatusOptions(t), [t]);
|
||||||
const [configAgentId, setConfigAgentId] = useState<number | null>(null);
|
const [configAgentId, setConfigAgentId] = useState<number | null>(null);
|
||||||
|
const [configOpen, setConfigOpen] = useState(false);
|
||||||
const [crudActions, setCrudActions] = useState<DashboardCrudActionState | null>(null);
|
const [crudActions, setCrudActions] = useState<DashboardCrudActionState | null>(null);
|
||||||
|
|
||||||
const filters = useMemo<DashboardCrudFilter[]>(
|
const filters = useMemo<DashboardCrudFilter[]>(
|
||||||
@@ -239,7 +239,14 @@ export default function DashboardAIAgentsPage() {
|
|||||||
getItemId={(item) => item.id}
|
getItemId={(item) => item.id}
|
||||||
createItem={createAIAgent}
|
createItem={createAIAgent}
|
||||||
updateItem={(item, payload) => updateAIAgent({ id: item.id, ...payload })}
|
updateItem={(item, payload) => updateAIAgent({ id: item.id, ...payload })}
|
||||||
onEditItem={(item) => setConfigAgentId(item.id)}
|
onCreateItem={() => {
|
||||||
|
setConfigAgentId(null);
|
||||||
|
setConfigOpen(true);
|
||||||
|
}}
|
||||||
|
onEditItem={(item) => {
|
||||||
|
setConfigAgentId(item.id);
|
||||||
|
setConfigOpen(true);
|
||||||
|
}}
|
||||||
deleteItem={(item) => deleteAIAgent(item.id)}
|
deleteItem={(item) => deleteAIAgent(item.id)}
|
||||||
rowActions={[
|
rowActions={[
|
||||||
{
|
{
|
||||||
@@ -248,6 +255,7 @@ export default function DashboardAIAgentsPage() {
|
|||||||
label: t("aiAgent.configure"),
|
label: t("aiAgent.configure"),
|
||||||
run: ({ item }) => {
|
run: ({ item }) => {
|
||||||
setConfigAgentId(item.id);
|
setConfigAgentId(item.id);
|
||||||
|
setConfigOpen(true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
createDashboardStatusToggleAction<AIAgent, number>({
|
createDashboardStatusToggleAction<AIAgent, number>({
|
||||||
@@ -275,15 +283,6 @@ export default function DashboardAIAgentsPage() {
|
|||||||
errorMessage: t("aiAgent.sortUpdateFailed"),
|
errorMessage: t("aiAgent.sortUpdateFailed"),
|
||||||
handleLabel: t("aiAgent.dragSort", { name: "" }),
|
handleLabel: t("aiAgent.dragSort", { name: "" }),
|
||||||
}}
|
}}
|
||||||
renderEditDialog={({ open, saving, itemId, onOpenChange, onSubmit }) => (
|
|
||||||
<EditDialog
|
|
||||||
open={open}
|
|
||||||
saving={saving}
|
|
||||||
itemId={itemId}
|
|
||||||
onOpenChange={onOpenChange}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
onActionStateChange={setCrudActions}
|
onActionStateChange={setCrudActions}
|
||||||
labels={{
|
labels={{
|
||||||
refresh: t("aiAgent.refresh"),
|
refresh: t("aiAgent.refresh"),
|
||||||
@@ -305,8 +304,9 @@ export default function DashboardAIAgentsPage() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ProjectDialog
|
<ProjectDialog
|
||||||
open={configAgentId !== null}
|
open={configOpen}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
|
setConfigOpen(open);
|
||||||
if (!open) setConfigAgentId(null);
|
if (!open) setConfigAgentId(null);
|
||||||
}}
|
}}
|
||||||
title={t("aiAgent.configure")}
|
title={t("aiAgent.configure")}
|
||||||
@@ -315,9 +315,10 @@ export default function DashboardAIAgentsPage() {
|
|||||||
contentClassName="top-5 left-5 h-[calc(100vh-40px)] max-h-[calc(100vh-40px)] w-[calc(100vw-40px)] max-w-[calc(100vw-40px)] translate-x-0 translate-y-0 sm:max-w-[calc(100vw-40px)]"
|
contentClassName="top-5 left-5 h-[calc(100vh-40px)] max-h-[calc(100vh-40px)] w-[calc(100vw-40px)] max-w-[calc(100vw-40px)] translate-x-0 translate-y-0 sm:max-w-[calc(100vw-40px)]"
|
||||||
headerClassName="sr-only"
|
headerClassName="sr-only"
|
||||||
>
|
>
|
||||||
{configAgentId ? (
|
{configOpen ? (
|
||||||
<AIAgentConfigWorkbench
|
<AIAgentConfigWorkbench
|
||||||
agentId={configAgentId}
|
agentId={configAgentId}
|
||||||
|
onAgentCreated={(agent) => setConfigAgentId(agent.id)}
|
||||||
onAgentSaved={() => crudActions?.onRefresh()}
|
onAgentSaved={() => crudActions?.onRefresh()}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ export type DashboardCrudPageProps<TItem, TPayload> = {
|
|||||||
getItemId: (item: TItem) => number
|
getItemId: (item: TItem) => number
|
||||||
createItem: (payload: TPayload) => Promise<unknown>
|
createItem: (payload: TPayload) => Promise<unknown>
|
||||||
updateItem: (item: TItem, payload: TPayload) => Promise<unknown>
|
updateItem: (item: TItem, payload: TPayload) => Promise<unknown>
|
||||||
|
onCreateItem?: () => void
|
||||||
canEdit?: (item: TItem) => boolean
|
canEdit?: (item: TItem) => boolean
|
||||||
onEditItem?: (item: TItem) => void
|
onEditItem?: (item: TItem) => void
|
||||||
deleteItem?: (item: TItem) => Promise<unknown>
|
deleteItem?: (item: TItem) => Promise<unknown>
|
||||||
@@ -199,6 +200,7 @@ export function DashboardCrudPage<TItem, TPayload>({
|
|||||||
getItemId,
|
getItemId,
|
||||||
createItem,
|
createItem,
|
||||||
updateItem,
|
updateItem,
|
||||||
|
onCreateItem,
|
||||||
canEdit,
|
canEdit,
|
||||||
onEditItem,
|
onEditItem,
|
||||||
deleteItem,
|
deleteItem,
|
||||||
@@ -255,9 +257,13 @@ export function DashboardCrudPage<TItem, TPayload>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openCreateDialog = useCallback(() => {
|
const openCreateDialog = useCallback(() => {
|
||||||
|
if (onCreateItem) {
|
||||||
|
onCreateItem()
|
||||||
|
return
|
||||||
|
}
|
||||||
setEditingItem(null)
|
setEditingItem(null)
|
||||||
setDialogOpen(true)
|
setDialogOpen(true)
|
||||||
}, [])
|
}, [onCreateItem])
|
||||||
|
|
||||||
function openEditDialog(item: TItem) {
|
function openEditDialog(item: TItem) {
|
||||||
setEditingItem(item)
|
setEditingItem(item)
|
||||||
|
|||||||
Reference in New Issue
Block a user