mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-10 07:46:02 +00:00
后端没修完版
This commit is contained in:
278
src/langbot/pkg/workflow/entities.py
Normal file
278
src/langbot/pkg/workflow/entities.py
Normal file
@@ -0,0 +1,278 @@
|
||||
"""Workflow entities and data models"""
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
import pydantic
|
||||
|
||||
|
||||
class Position(pydantic.BaseModel):
|
||||
"""Node position on canvas"""
|
||||
x: float = 0
|
||||
y: float = 0
|
||||
|
||||
|
||||
class PortDefinition(pydantic.BaseModel):
|
||||
"""Node port definition"""
|
||||
name: str
|
||||
type: str = "any" # any, string, number, boolean, object, array
|
||||
description: str = ""
|
||||
required: bool = True
|
||||
|
||||
|
||||
class NodeDefinition(pydantic.BaseModel):
|
||||
"""Workflow node definition"""
|
||||
id: str
|
||||
type: str
|
||||
name: str = ""
|
||||
position: Position = Position()
|
||||
config: dict[str, Any] = {}
|
||||
inputs: list[PortDefinition] = []
|
||||
outputs: list[PortDefinition] = []
|
||||
|
||||
# UI metadata
|
||||
description: str = ""
|
||||
comment: str = "" # User comment/annotation
|
||||
|
||||
|
||||
class EdgeDefinition(pydantic.BaseModel):
|
||||
"""Workflow edge definition (connection between nodes)"""
|
||||
id: str
|
||||
source_node: str
|
||||
source_port: str = "output"
|
||||
target_node: str
|
||||
target_port: str = "input"
|
||||
condition: Optional[str] = None # Optional condition expression
|
||||
|
||||
|
||||
class TriggerDefinition(pydantic.BaseModel):
|
||||
"""Workflow trigger definition"""
|
||||
id: str
|
||||
type: str # message, cron, event, webhook
|
||||
config: dict[str, Any] = {}
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
class WorkflowSettings(pydantic.BaseModel):
|
||||
"""Workflow settings"""
|
||||
# Execution settings
|
||||
max_execution_time: int = 300 # seconds
|
||||
max_retries: int = 3
|
||||
retry_delay: int = 5 # seconds
|
||||
|
||||
# Error handling
|
||||
error_handling: str = "stop" # stop, continue, retry
|
||||
|
||||
# Logging
|
||||
log_level: str = "info"
|
||||
save_execution_history: bool = True
|
||||
|
||||
# Concurrency
|
||||
max_concurrent_executions: int = 10
|
||||
|
||||
|
||||
class SafetyConfig(pydantic.BaseModel):
|
||||
"""Safety configuration (inherited from Pipeline)"""
|
||||
content_filter: dict[str, Any] = {
|
||||
"enable": False,
|
||||
"sensitive_words": [],
|
||||
"replace_with": "***"
|
||||
}
|
||||
rate_limit: dict[str, Any] = {
|
||||
"enable": False,
|
||||
"requests_per_minute": 60,
|
||||
"burst_limit": 10
|
||||
}
|
||||
|
||||
|
||||
class OutputConfig(pydantic.BaseModel):
|
||||
"""Output configuration (inherited from Pipeline)"""
|
||||
long_text_processing: dict[str, Any] = {
|
||||
"strategy": "split", # split, truncate, file
|
||||
"max_length": 4000,
|
||||
"split_separator": "\n\n"
|
||||
}
|
||||
force_delay: dict[str, Any] = {
|
||||
"enable": False,
|
||||
"min_delay_ms": 0,
|
||||
"max_delay_ms": 0
|
||||
}
|
||||
misc: dict[str, Any] = {}
|
||||
|
||||
|
||||
class WorkflowGlobalConfig(pydantic.BaseModel):
|
||||
"""Workflow global configuration (inherited from Pipeline capabilities)"""
|
||||
safety: SafetyConfig = SafetyConfig()
|
||||
output: OutputConfig = OutputConfig()
|
||||
|
||||
|
||||
class ExtensionsPreferences(pydantic.BaseModel):
|
||||
"""Extensions preferences (same as Pipeline)"""
|
||||
enable_all_plugins: bool = True
|
||||
enable_all_mcp_servers: bool = True
|
||||
plugins: list[str] = []
|
||||
mcp_servers: list[str] = []
|
||||
|
||||
|
||||
class ConversationVariable(pydantic.BaseModel):
|
||||
"""Conversation-level variable definition"""
|
||||
name: str
|
||||
type: str = "string" # string, number, boolean, object, array
|
||||
description: str = ""
|
||||
default_value: Any = None
|
||||
max_length: Optional[int] = None # For strings
|
||||
|
||||
|
||||
class WorkflowDefinition(pydantic.BaseModel):
|
||||
"""Complete workflow definition"""
|
||||
uuid: str
|
||||
name: str
|
||||
description: str = ""
|
||||
emoji: str = "🔄"
|
||||
version: int = 1
|
||||
|
||||
# Workflow graph
|
||||
nodes: list[NodeDefinition] = []
|
||||
edges: list[EdgeDefinition] = []
|
||||
|
||||
# Variables
|
||||
variables: dict[str, Any] = {} # Global variables
|
||||
conversation_variables: list[ConversationVariable] = [] # Session-level variables
|
||||
|
||||
# Settings
|
||||
settings: WorkflowSettings = WorkflowSettings()
|
||||
|
||||
# Triggers (for automation)
|
||||
triggers: list[TriggerDefinition] = []
|
||||
|
||||
# Global configuration (inherited from Pipeline)
|
||||
global_config: WorkflowGlobalConfig = WorkflowGlobalConfig()
|
||||
|
||||
# Extensions
|
||||
extensions_preferences: ExtensionsPreferences = ExtensionsPreferences()
|
||||
|
||||
# Metadata
|
||||
is_enabled: bool = True
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
# Source tracking (for imported workflows)
|
||||
source: Optional[str] = None # dify, n8n, langflow, etc.
|
||||
source_id: Optional[str] = None
|
||||
|
||||
|
||||
class ExecutionStatus(enum.Enum):
|
||||
"""Workflow execution status"""
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
WAITING = "waiting"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class NodeStatus(enum.Enum):
|
||||
"""Node execution status"""
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
SKIPPED = "skipped"
|
||||
|
||||
|
||||
class NodeState(pydantic.BaseModel):
|
||||
"""Runtime state of a node during execution"""
|
||||
node_id: str
|
||||
status: NodeStatus = NodeStatus.PENDING
|
||||
inputs: dict[str, Any] = {}
|
||||
outputs: dict[str, Any] = {}
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
error: Optional[str] = None
|
||||
retry_count: int = 0
|
||||
|
||||
|
||||
class MessageContext(pydantic.BaseModel):
|
||||
"""Message context for message-triggered workflows"""
|
||||
message_id: str
|
||||
message_content: str
|
||||
sender_id: str
|
||||
sender_name: str = ""
|
||||
platform: str = ""
|
||||
conversation_id: str = ""
|
||||
is_group: bool = False
|
||||
group_id: Optional[str] = None
|
||||
mentions: list[str] = []
|
||||
reply_to: Optional[str] = None
|
||||
raw_message: dict[str, Any] = {}
|
||||
|
||||
|
||||
class ExecutionStep(pydantic.BaseModel):
|
||||
"""Execution history step"""
|
||||
timestamp: datetime
|
||||
node_id: str
|
||||
node_type: str
|
||||
status: str
|
||||
inputs: dict[str, Any] = {}
|
||||
outputs: dict[str, Any] = {}
|
||||
duration_ms: int = 0
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
class ExecutionContext(pydantic.BaseModel):
|
||||
"""Workflow execution context"""
|
||||
execution_id: str
|
||||
workflow_id: str
|
||||
workflow_version: int = 1
|
||||
status: ExecutionStatus = ExecutionStatus.PENDING
|
||||
|
||||
# Runtime data
|
||||
variables: dict[str, Any] = {}
|
||||
conversation_variables: dict[str, Any] = {} # Session-level persistent variables
|
||||
node_states: dict[str, NodeState] = {}
|
||||
memory: dict[str, Any] = {} # Workflow memory for storing/retrieving data
|
||||
|
||||
# Timing
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
|
||||
# Error
|
||||
error: Optional[str] = None
|
||||
|
||||
# Message context (if triggered by message)
|
||||
message_context: Optional[MessageContext] = None
|
||||
|
||||
# Trigger info
|
||||
trigger_type: Optional[str] = None
|
||||
trigger_data: dict[str, Any] = {}
|
||||
|
||||
# Execution history
|
||||
history: list[ExecutionStep] = []
|
||||
|
||||
# Session info
|
||||
session_id: Optional[str] = None
|
||||
user_id: Optional[str] = None
|
||||
bot_id: Optional[str] = None
|
||||
|
||||
def get_node_output(self, node_id: str, output_name: str = "output") -> Any:
|
||||
"""Get output from a specific node"""
|
||||
if node_id in self.node_states:
|
||||
return self.node_states[node_id].outputs.get(output_name)
|
||||
return None
|
||||
|
||||
def set_variable(self, name: str, value: Any):
|
||||
"""Set a workflow variable"""
|
||||
self.variables[name] = value
|
||||
|
||||
def get_variable(self, name: str, default: Any = None) -> Any:
|
||||
"""Get a workflow variable"""
|
||||
return self.variables.get(name, default)
|
||||
|
||||
def set_conversation_variable(self, name: str, value: Any):
|
||||
"""Set a conversation-level variable (persisted across executions)"""
|
||||
self.conversation_variables[name] = value
|
||||
|
||||
def get_conversation_variable(self, name: str, default: Any = None) -> Any:
|
||||
"""Get a conversation-level variable"""
|
||||
return self.conversation_variables.get(name, default)
|
||||
Reference in New Issue
Block a user