feat: add icon property to workflow node specifications and update related components
This commit is contained in:
+1
-1
Submodule docs updated: 97dc51ae52...fe29cfb3cc
@@ -25,6 +25,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeStart,
|
Type: NodeTypeStart,
|
||||||
Title: "Start",
|
Title: "Start",
|
||||||
Description: "Conversation workflow entry.",
|
Description: "Conversation workflow entry.",
|
||||||
|
Icon: "PlayCircleIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
OutputSchema: []VariableSpec{
|
OutputSchema: []VariableSpec{
|
||||||
output("conversationId", VariableTypeInteger, "Conversation ID."),
|
output("conversationId", VariableTypeInteger, "Conversation ID."),
|
||||||
@@ -38,6 +39,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeConversationUnderstanding,
|
Type: NodeTypeConversationUnderstanding,
|
||||||
Title: "Conversation Understanding",
|
Title: "Conversation Understanding",
|
||||||
Description: "Classify customer message intent and answer scope before retrieval.",
|
Description: "Classify customer message intent and answer scope before retrieval.",
|
||||||
|
Icon: "MessageCircleIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
||||||
@@ -75,6 +77,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeReplyPolicy,
|
Type: NodeTypeReplyPolicy,
|
||||||
Title: "Reply Policy",
|
Title: "Reply Policy",
|
||||||
Description: "Decide the next customer-service action from understanding output and agent policy.",
|
Description: "Decide the next customer-service action from understanding output and agent policy.",
|
||||||
|
Icon: "ShieldCheckIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("messageIntent", VariableTypeString, "Detected customer message intent."),
|
requiredInput("messageIntent", VariableTypeString, "Detected customer message intent."),
|
||||||
@@ -115,6 +118,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeKnowledgeRetrieve,
|
Type: NodeTypeKnowledgeRetrieve,
|
||||||
Title: "Knowledge Retrieve",
|
Title: "Knowledge Retrieve",
|
||||||
Description: "Retrieve knowledge for the current user message.",
|
Description: "Retrieve knowledge for the current user message.",
|
||||||
|
Icon: "BookOpenIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("query", VariableTypeString, "Search query."),
|
requiredInput("query", VariableTypeString, "Search query."),
|
||||||
@@ -131,6 +135,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeAnswerabilityGate,
|
Type: NodeTypeAnswerabilityGate,
|
||||||
Title: "Answerability Gate",
|
Title: "Answerability Gate",
|
||||||
Description: "Decide whether retrieved knowledge is enough to answer.",
|
Description: "Decide whether retrieved knowledge is enough to answer.",
|
||||||
|
Icon: "HelpCircleIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
||||||
@@ -148,6 +153,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeLLMReply,
|
Type: NodeTypeLLMReply,
|
||||||
Title: "LLM Reply",
|
Title: "LLM Reply",
|
||||||
Description: "Generate a reply or structured analysis with the configured model.",
|
Description: "Generate a reply or structured analysis with the configured model.",
|
||||||
|
Icon: "BotIcon",
|
||||||
RiskLevel: NodeRiskLevelMedium,
|
RiskLevel: NodeRiskLevelMedium,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
||||||
@@ -161,6 +167,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeCondition,
|
Type: NodeTypeCondition,
|
||||||
Title: "Condition",
|
Title: "Condition",
|
||||||
Description: "Route by controlled workflow variables.",
|
Description: "Route by controlled workflow variables.",
|
||||||
|
Icon: "GitBranchIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
OutputSchema: []VariableSpec{
|
OutputSchema: []VariableSpec{
|
||||||
output("matched", VariableTypeBoolean, "Whether the condition matched."),
|
output("matched", VariableTypeBoolean, "Whether the condition matched."),
|
||||||
@@ -170,6 +177,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeAnalyzeConversation,
|
Type: NodeTypeAnalyzeConversation,
|
||||||
Title: "Analyze Conversation",
|
Title: "Analyze Conversation",
|
||||||
Description: "Analyze intent, risk, and recommended next action.",
|
Description: "Analyze intent, risk, and recommended next action.",
|
||||||
|
Icon: "SearchIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
requiredInput("userMessage", VariableTypeString, "Current user message content."),
|
||||||
@@ -185,6 +193,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypePrepareTicketDraft,
|
Type: NodeTypePrepareTicketDraft,
|
||||||
Title: "Prepare Ticket Draft",
|
Title: "Prepare Ticket Draft",
|
||||||
Description: "Build a ticket draft from conversation context.",
|
Description: "Build a ticket draft from conversation context.",
|
||||||
|
Icon: "ClipboardListIcon",
|
||||||
RiskLevel: NodeRiskLevelMedium,
|
RiskLevel: NodeRiskLevelMedium,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("issue", VariableTypeString, "Issue summary."),
|
requiredInput("issue", VariableTypeString, "Issue summary."),
|
||||||
@@ -197,6 +206,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeHumanConfirm,
|
Type: NodeTypeHumanConfirm,
|
||||||
Title: "Human Confirm",
|
Title: "Human Confirm",
|
||||||
Description: "Interrupt and wait for explicit user confirmation.",
|
Description: "Interrupt and wait for explicit user confirmation.",
|
||||||
|
Icon: "UserCheckIcon",
|
||||||
RiskLevel: NodeRiskLevelMedium,
|
RiskLevel: NodeRiskLevelMedium,
|
||||||
Interruptible: true,
|
Interruptible: true,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
@@ -211,6 +221,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeCreateTicket,
|
Type: NodeTypeCreateTicket,
|
||||||
Title: "Create Ticket",
|
Title: "Create Ticket",
|
||||||
Description: "Create a ticket from a confirmed draft.",
|
Description: "Create a ticket from a confirmed draft.",
|
||||||
|
Icon: "TicketIcon",
|
||||||
RiskLevel: NodeRiskLevelHigh,
|
RiskLevel: NodeRiskLevelHigh,
|
||||||
RequiresConfirmationPredecessor: true,
|
RequiresConfirmationPredecessor: true,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
@@ -228,6 +239,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeHandoffToHuman,
|
Type: NodeTypeHandoffToHuman,
|
||||||
Title: "Handoff To Human",
|
Title: "Handoff To Human",
|
||||||
Description: "Transfer the conversation to human support.",
|
Description: "Transfer the conversation to human support.",
|
||||||
|
Icon: "HeadphonesIcon",
|
||||||
RiskLevel: NodeRiskLevelHigh,
|
RiskLevel: NodeRiskLevelHigh,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("reason", VariableTypeString, "Handoff reason."),
|
requiredInput("reason", VariableTypeString, "Handoff reason."),
|
||||||
@@ -252,6 +264,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeSendReply,
|
Type: NodeTypeSendReply,
|
||||||
Title: "Send Reply",
|
Title: "Send Reply",
|
||||||
Description: "Return or commit customer-visible reply text.",
|
Description: "Return or commit customer-visible reply text.",
|
||||||
|
Icon: "SendIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
InputSchema: []VariableSpec{
|
InputSchema: []VariableSpec{
|
||||||
requiredInput("replyText", VariableTypeString, "Customer-visible reply text."),
|
requiredInput("replyText", VariableTypeString, "Customer-visible reply text."),
|
||||||
@@ -265,6 +278,7 @@ func DefaultRegistry() *Registry {
|
|||||||
Type: NodeTypeEnd,
|
Type: NodeTypeEnd,
|
||||||
Title: "End",
|
Title: "End",
|
||||||
Description: "End workflow execution.",
|
Description: "End workflow execution.",
|
||||||
|
Icon: "FlagIcon",
|
||||||
RiskLevel: NodeRiskLevelLow,
|
RiskLevel: NodeRiskLevelLow,
|
||||||
OutputSchema: []VariableSpec{
|
OutputSchema: []VariableSpec{
|
||||||
output("status", VariableTypeString, "Workflow terminal status."),
|
output("status", VariableTypeString, "Workflow terminal status."),
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ type NodeSpec struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
RiskLevel NodeRiskLevel `json:"riskLevel"`
|
RiskLevel NodeRiskLevel `json:"riskLevel"`
|
||||||
Interruptible bool `json:"interruptible"`
|
Interruptible bool `json:"interruptible"`
|
||||||
RequiresConfirmationPredecessor bool `json:"requiresConfirmationPredecessor"`
|
RequiresConfirmationPredecessor bool `json:"requiresConfirmationPredecessor"`
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ func BuildAIWorkflowNodeSpecs(list []workflowregistry.NodeSpec) []response.AIWor
|
|||||||
Type: item.Type,
|
Type: item.Type,
|
||||||
Title: item.Title,
|
Title: item.Title,
|
||||||
Description: item.Description,
|
Description: item.Description,
|
||||||
|
Icon: item.Icon,
|
||||||
RiskLevel: item.RiskLevel,
|
RiskLevel: item.RiskLevel,
|
||||||
Interruptible: item.Interruptible,
|
Interruptible: item.Interruptible,
|
||||||
RequiresConfirmationPredecessor: item.RequiresConfirmationPredecessor,
|
RequiresConfirmationPredecessor: item.RequiresConfirmationPredecessor,
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ func TestBuildAIWorkflowNodeSpecsIncludesVariableContracts(t *testing.T) {
|
|||||||
switch spec.Type {
|
switch spec.Type {
|
||||||
case workflowregistry.NodeTypeStart:
|
case workflowregistry.NodeTypeStart:
|
||||||
startFound = true
|
startFound = true
|
||||||
|
if spec.Icon != "PlayCircleIcon" {
|
||||||
|
t.Fatalf("expected start icon PlayCircleIcon, got %q", spec.Icon)
|
||||||
|
}
|
||||||
if !hasResponseVariable(spec.OutputSchema, "userMessage") {
|
if !hasResponseVariable(spec.OutputSchema, "userMessage") {
|
||||||
t.Fatalf("expected start output userMessage, got %#v", spec.OutputSchema)
|
t.Fatalf("expected start output userMessage, got %#v", spec.OutputSchema)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type AIWorkflowNodeSpecResponse struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
RiskLevel workflowregistry.NodeRiskLevel `json:"riskLevel"`
|
RiskLevel workflowregistry.NodeRiskLevel `json:"riskLevel"`
|
||||||
Interruptible bool `json:"interruptible"`
|
Interruptible bool `json:"interruptible"`
|
||||||
RequiresConfirmationPredecessor bool `json:"requiresConfirmationPredecessor"`
|
RequiresConfirmationPredecessor bool `json:"requiresConfirmationPredecessor"`
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export function buildFlowgramNodeRegistries(nodeSpecs: AIWorkflowNodeSpec[]): Wo
|
|||||||
type: "start",
|
type: "start",
|
||||||
title: "开始",
|
title: "开始",
|
||||||
description: "流程入口",
|
description: "流程入口",
|
||||||
|
icon: "PlayCircleIcon",
|
||||||
riskLevel: "low" as const,
|
riskLevel: "low" as const,
|
||||||
interruptible: false,
|
interruptible: false,
|
||||||
requiresConfirmationPredecessor: false,
|
requiresConfirmationPredecessor: false,
|
||||||
@@ -21,6 +22,7 @@ export function buildFlowgramNodeRegistries(nodeSpecs: AIWorkflowNodeSpec[]): Wo
|
|||||||
type: "end",
|
type: "end",
|
||||||
title: "结束",
|
title: "结束",
|
||||||
description: "流程结束",
|
description: "流程结束",
|
||||||
|
icon: "FlagIcon",
|
||||||
riskLevel: "low" as const,
|
riskLevel: "low" as const,
|
||||||
interruptible: false,
|
interruptible: false,
|
||||||
requiresConfirmationPredecessor: false,
|
requiresConfirmationPredecessor: false,
|
||||||
@@ -47,9 +49,10 @@ export function buildFlowgramNodeRegistries(nodeSpecs: AIWorkflowNodeSpec[]): Wo
|
|||||||
formMeta: {
|
formMeta: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<FlowgramNodeForm
|
<FlowgramNodeForm
|
||||||
nodeType={spec.type}
|
nodeType={spec.type}
|
||||||
fallbackTitle={spec.title || spec.type}
|
fallbackTitle={spec.title || spec.type}
|
||||||
/>
|
icon={spec.icon}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
@@ -58,9 +61,11 @@ export function buildFlowgramNodeRegistries(nodeSpecs: AIWorkflowNodeSpec[]): Wo
|
|||||||
function FlowgramNodeForm({
|
function FlowgramNodeForm({
|
||||||
nodeType,
|
nodeType,
|
||||||
fallbackTitle,
|
fallbackTitle,
|
||||||
|
icon,
|
||||||
}: {
|
}: {
|
||||||
nodeType: string
|
nodeType: string
|
||||||
fallbackTitle: string
|
fallbackTitle: string
|
||||||
|
icon: string
|
||||||
}) {
|
}) {
|
||||||
const { node, selected } = useNodeRender()
|
const { node, selected } = useNodeRender()
|
||||||
const nodeId = String(node.id ?? "")
|
const nodeId = String(node.id ?? "")
|
||||||
@@ -69,8 +74,8 @@ function FlowgramNodeForm({
|
|||||||
<Field<string> name="title">
|
<Field<string> name="title">
|
||||||
{({ field }) => (
|
{({ field }) => (
|
||||||
<WorkflowNodeCard
|
<WorkflowNodeCard
|
||||||
nodeType={nodeType}
|
|
||||||
title={field.value || fallbackTitle}
|
title={field.value || fallbackTitle}
|
||||||
|
icon={icon}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
width={nodeType === "condition" ? "wide" : "normal"}
|
width={nodeType === "condition" ? "wide" : "normal"}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { cn } from "@/lib/utils"
|
|||||||
import { WorkflowNodeIcon } from "./workflow-node-icon"
|
import { WorkflowNodeIcon } from "./workflow-node-icon"
|
||||||
|
|
||||||
export function WorkflowNodeCard({
|
export function WorkflowNodeCard({
|
||||||
nodeType,
|
|
||||||
title,
|
title,
|
||||||
|
icon,
|
||||||
selected,
|
selected,
|
||||||
width = "normal",
|
width = "normal",
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
nodeType: string
|
|
||||||
title: string
|
title: string
|
||||||
|
icon: string
|
||||||
selected: boolean
|
selected: boolean
|
||||||
width?: "normal" | "wide"
|
width?: "normal" | "wide"
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
@@ -28,7 +28,7 @@ export function WorkflowNodeCard({
|
|||||||
>
|
>
|
||||||
<div className="overflow-hidden rounded-lg border border-transparent bg-background">
|
<div className="overflow-hidden rounded-lg border border-transparent bg-background">
|
||||||
<div className="flex items-center gap-2 px-3 pb-2 pt-3">
|
<div className="flex items-center gap-2 px-3 pb-2 pt-3">
|
||||||
<WorkflowNodeIcon type={nodeType} />
|
<WorkflowNodeIcon icon={icon} />
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="truncate text-sm font-semibold leading-5 text-foreground">
|
<div className="truncate text-sm font-semibold leading-5 text-foreground">
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { getWorkflowNodeMeta } from "./workflow-node-meta"
|
import { FileTextIcon, type LucideIcon } from "lucide-react"
|
||||||
|
import * as LucideIcons from "lucide-react"
|
||||||
|
|
||||||
|
const lucideIconComponents = LucideIcons as unknown as Record<string, LucideIcon>
|
||||||
|
|
||||||
export function WorkflowNodeIcon({
|
export function WorkflowNodeIcon({
|
||||||
type,
|
icon,
|
||||||
size = "md",
|
size = "md",
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
type: string
|
icon?: string
|
||||||
size?: "sm" | "md"
|
size?: "sm" | "md"
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const meta = getWorkflowNodeMeta(type)
|
const Icon = icon ? lucideIconComponents[icon] ?? FileTextIcon : FileTextIcon
|
||||||
const Icon = meta.icon
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import {
|
|
||||||
BookOpenIcon,
|
|
||||||
BotIcon,
|
|
||||||
ClipboardListIcon,
|
|
||||||
FileTextIcon,
|
|
||||||
FlagIcon,
|
|
||||||
GitBranchIcon,
|
|
||||||
HeadphonesIcon,
|
|
||||||
HelpCircleIcon,
|
|
||||||
MessageCircleIcon,
|
|
||||||
PlayCircleIcon,
|
|
||||||
SearchIcon,
|
|
||||||
SendIcon,
|
|
||||||
ShieldCheckIcon,
|
|
||||||
TicketIcon,
|
|
||||||
UserCheckIcon,
|
|
||||||
type LucideIcon,
|
|
||||||
} from "lucide-react"
|
|
||||||
|
|
||||||
export type WorkflowNodeMeta = {
|
|
||||||
icon: LucideIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
const workflowNodeMetaByType: Record<string, WorkflowNodeMeta> = {
|
|
||||||
start: { icon: PlayCircleIcon },
|
|
||||||
conversation_understanding: { icon: MessageCircleIcon },
|
|
||||||
reply_policy: { icon: ShieldCheckIcon },
|
|
||||||
knowledge_retrieve: { icon: BookOpenIcon },
|
|
||||||
answerability_gate: { icon: HelpCircleIcon },
|
|
||||||
llm_reply: { icon: BotIcon },
|
|
||||||
condition: { icon: GitBranchIcon },
|
|
||||||
analyze_conversation: { icon: SearchIcon },
|
|
||||||
prepare_ticket_draft: { icon: ClipboardListIcon },
|
|
||||||
human_confirm: { icon: UserCheckIcon },
|
|
||||||
create_ticket: { icon: TicketIcon },
|
|
||||||
handoff_to_human: { icon: HeadphonesIcon },
|
|
||||||
send_reply: { icon: SendIcon },
|
|
||||||
end: { icon: FlagIcon },
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWorkflowNodeMeta(type: string): WorkflowNodeMeta {
|
|
||||||
return workflowNodeMetaByType[type] ?? { icon: FileTextIcon }
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ export function WorkflowNodePalette({
|
|||||||
className="flex w-full items-start gap-2 rounded-md px-2 py-2 text-left text-sm transition-colors hover:bg-muted"
|
className="flex w-full items-start gap-2 rounded-md px-2 py-2 text-left text-sm transition-colors hover:bg-muted"
|
||||||
onClick={() => onAddNode(spec)}
|
onClick={() => onAddNode(spec)}
|
||||||
>
|
>
|
||||||
<WorkflowNodeIcon type={spec.type} size="sm" className="mt-0.5" />
|
<WorkflowNodeIcon icon={spec.icon} size="sm" className="mt-0.5" />
|
||||||
<span className="min-w-0">
|
<span className="min-w-0">
|
||||||
<span className="block truncate font-medium">{spec.title || spec.type}</span>
|
<span className="block truncate font-medium">{spec.title || spec.type}</span>
|
||||||
<span className="line-clamp-2 text-xs text-muted-foreground">{spec.description}</span>
|
<span className="line-clamp-2 text-xs text-muted-foreground">{spec.description}</span>
|
||||||
|
|||||||
@@ -380,6 +380,7 @@ export type AIWorkflowNodeSpec = {
|
|||||||
type: string
|
type: string
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
|
icon: string
|
||||||
riskLevel: "low" | "medium" | "high"
|
riskLevel: "low" | "medium" | "high"
|
||||||
interruptible: boolean
|
interruptible: boolean
|
||||||
requiresConfirmationPredecessor: boolean
|
requiresConfirmationPredecessor: boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user