feat: add default workflow definition endpoint and related functionality
This commit is contained in:
@@ -230,6 +230,7 @@ func registerDashboardAIAgentRoutes(group *gin.RouterGroup) {
|
|||||||
|
|
||||||
func registerDashboardAIWorkflowRoutes(group *gin.RouterGroup) {
|
func registerDashboardAIWorkflowRoutes(group *gin.RouterGroup) {
|
||||||
group.GET("/node-spec/list", dashboard.AIWorkflowGetNodeSpecList)
|
group.GET("/node-spec/list", dashboard.AIWorkflowGetNodeSpecList)
|
||||||
|
group.GET("/default-definition", dashboard.AIWorkflowGetDefaultDefinition)
|
||||||
group.POST("/validate", dashboard.AIWorkflowPostValidate)
|
group.POST("/validate", dashboard.AIWorkflowPostValidate)
|
||||||
group.Any("/run/list", dashboard.AIWorkflowAnyRunList)
|
group.Any("/run/list", dashboard.AIWorkflowAnyRunList)
|
||||||
group.GET("/run/:id", dashboard.AIWorkflowGetRunBy)
|
group.GET("/run/:id", dashboard.AIWorkflowGetRunBy)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ func TestNewServerRegistersGinRoutes(t *testing.T) {
|
|||||||
http.MethodGet + " /api/dashboard/user/:id",
|
http.MethodGet + " /api/dashboard/user/:id",
|
||||||
http.MethodPost + " /api/dashboard/user/create",
|
http.MethodPost + " /api/dashboard/user/create",
|
||||||
http.MethodPost + " /api/dashboard/conversation/send_message",
|
http.MethodPost + " /api/dashboard/conversation/send_message",
|
||||||
|
http.MethodGet + " /api/dashboard/ai-workflow/default-definition",
|
||||||
http.MethodGet + " /api/dashboard/ai-workflow/run/list",
|
http.MethodGet + " /api/dashboard/ai-workflow/run/list",
|
||||||
http.MethodGet + " /api/dashboard/ai-workflow/run/:id",
|
http.MethodGet + " /api/dashboard/ai-workflow/run/:id",
|
||||||
http.MethodGet + " /api/ws/dashboard",
|
http.MethodGet + " /api/ws/dashboard",
|
||||||
|
|||||||
@@ -108,6 +108,14 @@ func AIWorkflowGetNodeSpecList(ctx *gin.Context) {
|
|||||||
httpx.WriteJSON(ctx, builders.BuildAIWorkflowNodeSpecs(services.AIWorkflowService.ListNodeSpecs()))
|
httpx.WriteJSON(ctx, builders.BuildAIWorkflowNodeSpecs(services.AIWorkflowService.ListNodeSpecs()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AIWorkflowGetDefaultDefinition(ctx *gin.Context) {
|
||||||
|
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
|
||||||
|
httpx.WriteJSON(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpx.WriteJSON(ctx, services.AIWorkflowService.DefaultAgentWorkflowDefinition())
|
||||||
|
}
|
||||||
|
|
||||||
func AIWorkflowPostValidate(ctx *gin.Context) {
|
func AIWorkflowPostValidate(ctx *gin.Context) {
|
||||||
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
|
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
|
||||||
httpx.WriteJSON(ctx, err)
|
httpx.WriteJSON(ctx, err)
|
||||||
|
|||||||
@@ -82,6 +82,26 @@ func TestAIAgentServiceCreatesDefaultWorkflow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAIWorkflowServiceDefaultAgentWorkflowDefinitionIsValid(t *testing.T) {
|
||||||
|
definition := AIWorkflowService.DefaultAgentWorkflowDefinition()
|
||||||
|
if definition.EntryNodeID == "" {
|
||||||
|
t.Fatalf("expected default workflow definition")
|
||||||
|
}
|
||||||
|
validation := workflowvalidator.ValidateDefinition(definition, workflowregistry.DefaultRegistry())
|
||||||
|
if !validation.Valid {
|
||||||
|
t.Fatalf("expected default workflow definition to be valid, got %#v", validation.Errors)
|
||||||
|
}
|
||||||
|
if nodeTypeByID(definition, "route_intent_1") != workflowregistry.NodeTypeCondition {
|
||||||
|
t.Fatalf("expected default workflow to include intent router, got nodes: %#v", definition.Nodes)
|
||||||
|
}
|
||||||
|
if !workflowHasNodeType(definition, workflowregistry.NodeTypeHandoffToHuman) {
|
||||||
|
t.Fatalf("expected default workflow to include human handoff node")
|
||||||
|
}
|
||||||
|
if !workflowHasNodeType(definition, workflowregistry.NodeTypeCreateTicket) {
|
||||||
|
t.Fatalf("expected default workflow to include ticket creation node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAIWorkflowServicePublishAgentWorkflowBindsAgentVersion(t *testing.T) {
|
func TestAIWorkflowServicePublishAgentWorkflowBindsAgentVersion(t *testing.T) {
|
||||||
setupAIAgentWorkflowTestDB(t)
|
setupAIAgentWorkflowTestDB(t)
|
||||||
operator := aiAgentWorkflowTestOperator()
|
operator := aiAgentWorkflowTestOperator()
|
||||||
|
|||||||
@@ -190,6 +190,10 @@ func (s *aiWorkflowService) ListNodeSpecs() []workflowregistry.NodeSpec {
|
|||||||
return s.registry.List()
|
return s.registry.List()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *aiWorkflowService) DefaultAgentWorkflowDefinition() dsl.Definition {
|
||||||
|
return defaultAgentWorkflowDefinition()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *aiWorkflowService) ValidateDefinition(def dsl.Definition) workflowvalidator.Result {
|
func (s *aiWorkflowService) ValidateDefinition(def dsl.Definition) workflowvalidator.Result {
|
||||||
return workflowvalidator.ValidateDefinition(def, s.registry)
|
return workflowvalidator.ValidateDefinition(def, s.registry)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import {
|
|||||||
fetchAIAgent,
|
fetchAIAgent,
|
||||||
fetchAIAgentWorkflow,
|
fetchAIAgentWorkflow,
|
||||||
fetchAIConfigsAll,
|
fetchAIConfigsAll,
|
||||||
|
fetchAIWorkflowDefaultDefinition,
|
||||||
fetchAIWorkflowNodeSpecs,
|
fetchAIWorkflowNodeSpecs,
|
||||||
fetchAIWorkflowVersions,
|
fetchAIWorkflowVersions,
|
||||||
fetchAgentTeamsAll,
|
fetchAgentTeamsAll,
|
||||||
@@ -93,7 +94,7 @@ type SectionKey =
|
|||||||
| "handoff"
|
| "handoff"
|
||||||
| "versions"
|
| "versions"
|
||||||
|
|
||||||
const emptyDefinition: AIWorkflowDefinition = {
|
const fallbackDefinition: AIWorkflowDefinition = {
|
||||||
schemaVersion: 1,
|
schemaVersion: 1,
|
||||||
entryNodeId: "start_1",
|
entryNodeId: "start_1",
|
||||||
nodes: [
|
nodes: [
|
||||||
@@ -101,256 +102,18 @@ const emptyDefinition: AIWorkflowDefinition = {
|
|||||||
id: "start_1",
|
id: "start_1",
|
||||||
type: "start",
|
type: "start",
|
||||||
name: "开始",
|
name: "开始",
|
||||||
position: { x: 0, y: 260 },
|
position: { x: 0, y: 80 },
|
||||||
config: {},
|
config: {},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "route_intent_1",
|
|
||||||
type: "condition",
|
|
||||||
name: "意图分流",
|
|
||||||
position: { x: 260, y: 260 },
|
|
||||||
config: {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "handoff_1",
|
|
||||||
type: "handoff_to_human",
|
|
||||||
name: "转人工",
|
|
||||||
position: { x: 560, y: 80 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
reason: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "handoff_end_1",
|
|
||||||
type: "end",
|
|
||||||
name: "结束",
|
|
||||||
position: { x: 860, y: 80 },
|
|
||||||
config: {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "draft_ticket_1",
|
|
||||||
type: "prepare_ticket_draft",
|
|
||||||
name: "整理工单草稿",
|
|
||||||
position: { x: 560, y: 240 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
issue: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "ticket_confirm_prompt_1",
|
|
||||||
type: "llm_reply",
|
|
||||||
name: "建单确认文案",
|
|
||||||
position: { x: 860, y: 240 },
|
|
||||||
config: {
|
|
||||||
staticReply: "我已整理工单草稿。请回复“确认”创建工单,或回复“取消”放弃。",
|
|
||||||
},
|
|
||||||
inputs: {
|
|
||||||
userMessage: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "ticket_confirm_1",
|
|
||||||
type: "human_confirm",
|
|
||||||
name: "确认建单",
|
|
||||||
position: { x: 1160, y: 240 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
prompt: { nodeId: "ticket_confirm_prompt_1", field: "replyText" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "create_ticket_1",
|
|
||||||
type: "create_ticket",
|
|
||||||
name: "创建工单",
|
|
||||||
position: { x: 1460, y: 180 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
ticketDraft: { nodeId: "draft_ticket_1", field: "ticketDraft" },
|
|
||||||
confirmed: { nodeId: "ticket_confirm_1", field: "confirmed" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "ticket_result_reply_1",
|
|
||||||
type: "send_reply",
|
|
||||||
name: "发送建单结果",
|
|
||||||
position: { x: 1760, y: 180 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
replyText: { nodeId: "create_ticket_1", field: "message" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "ticket_cancel_reply_1",
|
|
||||||
type: "llm_reply",
|
|
||||||
name: "取消建单提示",
|
|
||||||
position: { x: 1460, y: 320 },
|
|
||||||
config: {
|
|
||||||
staticReply: "已取消创建工单。你可以继续补充问题,我会继续帮你处理。",
|
|
||||||
},
|
|
||||||
inputs: {
|
|
||||||
userMessage: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "send_ticket_cancel_1",
|
|
||||||
type: "send_reply",
|
|
||||||
name: "发送取消提示",
|
|
||||||
position: { x: 1760, y: 320 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
replyText: { nodeId: "ticket_cancel_reply_1", field: "replyText" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "retrieve_1",
|
|
||||||
type: "knowledge_retrieve",
|
|
||||||
name: "知识检索",
|
|
||||||
position: { x: 560, y: 500 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
query: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "answerability_1",
|
|
||||||
type: "answerability_gate",
|
|
||||||
name: "可回答判断",
|
|
||||||
position: { x: 860, y: 500 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
userMessage: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
knowledgeItems: { nodeId: "retrieve_1", field: "items" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "reply_1",
|
|
||||||
type: "llm_reply",
|
|
||||||
name: "AI 回复",
|
|
||||||
position: { x: 1160, y: 440 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
userMessage: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
knowledgeItems: { nodeId: "retrieve_1", field: "items" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "send_1",
|
|
||||||
type: "send_reply",
|
|
||||||
name: "发送回复",
|
|
||||||
position: { x: 1460, y: 440 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
replyText: { nodeId: "reply_1", field: "replyText" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "fallback_reply_1",
|
|
||||||
type: "llm_reply",
|
|
||||||
name: "兜底追问",
|
|
||||||
position: { x: 1160, y: 600 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
userMessage: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
knowledgeItems: { nodeId: "retrieve_1", field: "items" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "send_fallback_1",
|
|
||||||
type: "send_reply",
|
|
||||||
name: "发送兜底",
|
|
||||||
position: { x: 1460, y: 600 },
|
|
||||||
config: {},
|
|
||||||
inputs: {
|
|
||||||
replyText: { nodeId: "fallback_reply_1", field: "replyText" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "end_1",
|
id: "end_1",
|
||||||
type: "end",
|
type: "end",
|
||||||
name: "结束",
|
name: "结束",
|
||||||
position: { x: 2060, y: 440 },
|
position: { x: 260, y: 80 },
|
||||||
config: {},
|
config: {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
edges: [
|
edges: [{ id: "edge_start_end", source: "start_1", target: "end_1" }],
|
||||||
{ id: "edge_start_route_intent", source: "start_1", target: "route_intent_1" },
|
|
||||||
{
|
|
||||||
id: "edge_intent_handoff",
|
|
||||||
source: "route_intent_1",
|
|
||||||
target: "handoff_1",
|
|
||||||
condition: {
|
|
||||||
left: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
operator: "contains",
|
|
||||||
right: "人工",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "edge_intent_ticket",
|
|
||||||
source: "route_intent_1",
|
|
||||||
target: "draft_ticket_1",
|
|
||||||
condition: {
|
|
||||||
left: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
operator: "contains",
|
|
||||||
right: "工单",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "edge_intent_complaint",
|
|
||||||
source: "route_intent_1",
|
|
||||||
target: "draft_ticket_1",
|
|
||||||
condition: {
|
|
||||||
left: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
operator: "contains",
|
|
||||||
right: "投诉",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "edge_intent_incident",
|
|
||||||
source: "route_intent_1",
|
|
||||||
target: "draft_ticket_1",
|
|
||||||
condition: {
|
|
||||||
left: { nodeId: "start_1", field: "userMessage" },
|
|
||||||
operator: "contains",
|
|
||||||
right: "报障",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ id: "edge_intent_knowledge_default", source: "route_intent_1", target: "retrieve_1" },
|
|
||||||
{ id: "edge_handoff_end", source: "handoff_1", target: "handoff_end_1" },
|
|
||||||
{ id: "edge_draft_ticket_confirm_prompt", source: "draft_ticket_1", target: "ticket_confirm_prompt_1" },
|
|
||||||
{ id: "edge_ticket_prompt_confirm", source: "ticket_confirm_prompt_1", target: "ticket_confirm_1" },
|
|
||||||
{
|
|
||||||
id: "edge_ticket_confirm_create",
|
|
||||||
source: "ticket_confirm_1",
|
|
||||||
target: "create_ticket_1",
|
|
||||||
condition: {
|
|
||||||
left: { nodeId: "ticket_confirm_1", field: "confirmed" },
|
|
||||||
operator: "is_true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ id: "edge_ticket_confirm_cancel", source: "ticket_confirm_1", target: "ticket_cancel_reply_1" },
|
|
||||||
{ id: "edge_create_ticket_result", source: "create_ticket_1", target: "ticket_result_reply_1" },
|
|
||||||
{ id: "edge_ticket_result_end", source: "ticket_result_reply_1", target: "end_1" },
|
|
||||||
{ id: "edge_ticket_cancel_send", source: "ticket_cancel_reply_1", target: "send_ticket_cancel_1" },
|
|
||||||
{ id: "edge_ticket_cancel_end", source: "send_ticket_cancel_1", target: "end_1" },
|
|
||||||
{ id: "edge_retrieve_answerability", source: "retrieve_1", target: "answerability_1" },
|
|
||||||
{
|
|
||||||
id: "edge_answerability_reply",
|
|
||||||
source: "answerability_1",
|
|
||||||
target: "reply_1",
|
|
||||||
condition: {
|
|
||||||
left: { nodeId: "answerability_1", field: "answerability" },
|
|
||||||
operator: "eq",
|
|
||||||
right: "answerable",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ id: "edge_answerability_fallback", source: "answerability_1", target: "fallback_reply_1" },
|
|
||||||
{ id: "edge_reply_send", source: "reply_1", target: "send_1" },
|
|
||||||
{ id: "edge_fallback_send", source: "fallback_reply_1", target: "send_fallback_1" },
|
|
||||||
{ id: "edge_send_end", source: "send_1", target: "end_1" },
|
|
||||||
{ id: "edge_send_fallback_end", source: "send_fallback_1", target: "end_1" },
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toText(value: string | number | undefined | null) {
|
function toText(value: string | number | undefined | null) {
|
||||||
@@ -401,7 +164,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
const [selectedSkillIds, setSelectedSkillIds] = useState<number[]>([])
|
const [selectedSkillIds, setSelectedSkillIds] = useState<number[]>([])
|
||||||
const [directTools, setDirectTools] = useState<DirectToolItem[]>([])
|
const [directTools, setDirectTools] = useState<DirectToolItem[]>([])
|
||||||
|
|
||||||
const [definition, setDefinition] = useState<AIWorkflowDefinition>(emptyDefinition)
|
const [definition, setDefinition] = useState<AIWorkflowDefinition>(fallbackDefinition)
|
||||||
|
|
||||||
const [aiConfigs, setAIConfigs] = useState<AIConfig[]>([])
|
const [aiConfigs, setAIConfigs] = useState<AIConfig[]>([])
|
||||||
const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>([])
|
const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>([])
|
||||||
@@ -423,6 +186,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
try {
|
try {
|
||||||
const [
|
const [
|
||||||
specs,
|
specs,
|
||||||
|
defaultDefinition,
|
||||||
configs,
|
configs,
|
||||||
bases,
|
bases,
|
||||||
teams,
|
teams,
|
||||||
@@ -430,6 +194,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
catalog,
|
catalog,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
fetchAIWorkflowNodeSpecs(),
|
fetchAIWorkflowNodeSpecs(),
|
||||||
|
fetchAIWorkflowDefaultDefinition().catch(() => fallbackDefinition),
|
||||||
fetchAIConfigsAll({ modelType: AIModelType.LLM }),
|
fetchAIConfigsAll({ modelType: AIModelType.LLM }),
|
||||||
fetchKnowledgeBasesAll({ status: Status.Ok }),
|
fetchKnowledgeBasesAll({ status: Status.Ok }),
|
||||||
fetchAgentTeamsAll(),
|
fetchAgentTeamsAll(),
|
||||||
@@ -462,7 +227,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
setSelectedTeamIds([])
|
setSelectedTeamIds([])
|
||||||
setSelectedSkillIds([])
|
setSelectedSkillIds([])
|
||||||
setDirectTools([])
|
setDirectTools([])
|
||||||
setDefinition(emptyDefinition)
|
setDefinition(defaultDefinition ?? fallbackDefinition)
|
||||||
setValidation(null)
|
setValidation(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -494,7 +259,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
setSelectedTeamIds((agentDetail.teams ?? []).map((team) => team.id))
|
setSelectedTeamIds((agentDetail.teams ?? []).map((team) => team.id))
|
||||||
setSelectedSkillIds(agentDetail.skillIds ?? [])
|
setSelectedSkillIds(agentDetail.skillIds ?? [])
|
||||||
setDirectTools(agentDetail.directTools ?? [])
|
setDirectTools(agentDetail.directTools ?? [])
|
||||||
setDefinition(workflowDetail.draftDefinition ?? emptyDefinition)
|
setDefinition(workflowDetail.draftDefinition ?? defaultDefinition ?? fallbackDefinition)
|
||||||
setValidation(null)
|
setValidation(null)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error instanceof Error ? error.message : "Failed to load Agent config")
|
toast.error(error instanceof Error ? error.message : "Failed to load Agent config")
|
||||||
|
|||||||
@@ -821,6 +821,10 @@ export function fetchAIWorkflowNodeSpecs() {
|
|||||||
return request<AIWorkflowNodeSpec[]>("/api/dashboard/ai-workflow/node-spec/list")
|
return request<AIWorkflowNodeSpec[]>("/api/dashboard/ai-workflow/node-spec/list")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fetchAIWorkflowDefaultDefinition() {
|
||||||
|
return request<AIWorkflowDefinition>("/api/dashboard/ai-workflow/default-definition")
|
||||||
|
}
|
||||||
|
|
||||||
export function fetchAIWorkflowVersions(query?: Record<string, string | number | undefined>) {
|
export function fetchAIWorkflowVersions(query?: Record<string, string | number | undefined>) {
|
||||||
return request<PageResult<AIWorkflowVersion>>(
|
return request<PageResult<AIWorkflowVersion>>(
|
||||||
`/api/dashboard/ai-workflow/version/list${toQueryString(query)}`
|
`/api/dashboard/ai-workflow/version/list${toQueryString(query)}`
|
||||||
|
|||||||
Reference in New Issue
Block a user