Merge remote-tracking branch 'origin/main' into feat/queue-based-graph-engine

This commit is contained in:
-LAN-
2025-09-06 16:05:13 +08:00
295 changed files with 769 additions and 793 deletions

View File

@ -20,7 +20,7 @@ class ConversationVariableUpdater(Protocol):
"""
@abc.abstractmethod
def update(self, conversation_id: str, variable: "Variable") -> None:
def update(self, conversation_id: str, variable: "Variable"):
"""
Updates the value of the specified conversation variable in the underlying storage.

View File

@ -47,7 +47,7 @@ class VariablePool(BaseModel):
default_factory=list[VariableUnion],
)
def model_post_init(self, context: Any, /) -> None:
def model_post_init(self, context: Any, /):
# Create a mapping from field names to SystemVariableKey enum values
self._add_system_variables(self.system_variables)
# Add environment variables to the variable pool
@ -57,7 +57,7 @@ class VariablePool(BaseModel):
for var in self.conversation_variables:
self.add((CONVERSATION_VARIABLE_NODE_ID, var.name), var)
def add(self, selector: Sequence[str], value: Any, /) -> None:
def add(self, selector: Sequence[str], value: Any, /):
"""
Add a variable to the variable pool.
@ -161,11 +161,11 @@ class VariablePool(BaseModel):
# Return result as Segment
return result if isinstance(result, Segment) else variable_factory.build_segment(result)
def _extract_value(self, obj: Any) -> Any:
def _extract_value(self, obj: Any):
"""Extract the actual value from an ObjectSegment."""
return obj.value if isinstance(obj, ObjectSegment) else obj
def _get_nested_attribute(self, obj: Mapping[str, Any], attr: str) -> Any:
def _get_nested_attribute(self, obj: Mapping[str, Any], attr: str):
"""Get a nested attribute from a dictionary-like object."""
if not isinstance(obj, dict):
return None

View File

@ -74,7 +74,7 @@ class WorkflowNodeExecution(BaseModel):
process_data: Optional[Mapping[str, Any]] = None,
outputs: Optional[Mapping[str, Any]] = None,
metadata: Optional[Mapping[WorkflowNodeExecutionMetadataKey, Any]] = None,
) -> None:
):
"""
Update the model from mappings.

View File

@ -68,7 +68,7 @@ class AgentNode(Node):
node_type = NodeType.AGENT
_node_data: AgentNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = AgentNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -17,7 +17,7 @@ class AnswerNode(Node):
_node_data: AnswerNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = AnswerNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -50,7 +50,7 @@ class DefaultValue(BaseModel):
key: str
@staticmethod
def _parse_json(value: str) -> Any:
def _parse_json(value: str):
"""Unified JSON parsing handler"""
try:
return json.loads(value)

View File

@ -57,7 +57,7 @@ class VariableTemplateParser:
self.template = template
self.variable_keys = self.extract()
def extract(self) -> list:
def extract(self):
"""
Extracts all the template variable keys from the template string.

View File

@ -27,7 +27,7 @@ class CodeNode(Node):
_node_data: CodeNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = CodeNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -49,7 +49,7 @@ class CodeNode(Node):
return self._node_data
@classmethod
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
def get_default_config(cls, filters: Optional[dict] = None):
"""
Get default config of node.
:param filters: filter by node config parameters.

View File

@ -46,7 +46,7 @@ class DocumentExtractorNode(Node):
_node_data: DocumentExtractorNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = DocumentExtractorNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -15,7 +15,7 @@ class EndNode(Node):
_node_data: EndNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = EndNodeData(**data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -36,7 +36,7 @@ class HttpRequestNode(Node):
_node_data: HttpRequestNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = HttpRequestNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -58,7 +58,7 @@ class HttpRequestNode(Node):
return self._node_data
@classmethod
def get_default_config(cls, filters: Optional[dict[str, Any]] = None) -> dict:
def get_default_config(cls, filters: Optional[dict[str, Any]] = None):
return {
"type": "http-request",
"config": {

View File

@ -19,7 +19,7 @@ class IfElseNode(Node):
_node_data: IfElseNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = IfElseNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -55,7 +55,7 @@ class IterationNode(Node):
execution_type = NodeExecutionType.CONTAINER
_node_data: IterationNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = IterationNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -77,7 +77,7 @@ class IterationNode(Node):
return self._node_data
@classmethod
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
def get_default_config(cls, filters: Optional[dict] = None):
return {
"type": "iteration",
"config": {

View File

@ -17,7 +17,7 @@ class IterationStartNode(Node):
_node_data: IterationStartNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = IterationStartNodeData(**data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -99,7 +99,7 @@ class KnowledgeRetrievalNode(Node):
graph_runtime_state: "GraphRuntimeState",
*,
llm_file_saver: LLMFileSaver | None = None,
) -> None:
):
super().__init__(
id=id,
config=config,
@ -116,7 +116,7 @@ class KnowledgeRetrievalNode(Node):
)
self._llm_file_saver = llm_file_saver
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = KnowledgeRetrievalNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -40,7 +40,7 @@ class ListOperatorNode(Node):
_node_data: ListOperatorNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = ListOperatorNodeData(**data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -41,5 +41,5 @@ class FileTypeNotSupportError(LLMNodeError):
class UnsupportedPromptContentTypeError(LLMNodeError):
def __init__(self, *, type_name: str) -> None:
def __init__(self, *, type_name: str):
super().__init__(f"Prompt content type {type_name} is not supported.")

View File

@ -107,7 +107,7 @@ def fetch_memory(
return memory
def deduct_llm_quota(tenant_id: str, model_instance: ModelInstance, usage: LLMUsage) -> None:
def deduct_llm_quota(tenant_id: str, model_instance: ModelInstance, usage: LLMUsage):
provider_model_bundle = model_instance.provider_model_bundle
provider_configuration = provider_model_bundle.configuration

View File

@ -119,7 +119,7 @@ class LLMNode(Node):
graph_runtime_state: "GraphRuntimeState",
*,
llm_file_saver: LLMFileSaver | None = None,
) -> None:
):
super().__init__(
id=id,
config=config,
@ -136,7 +136,7 @@ class LLMNode(Node):
)
self._llm_file_saver = llm_file_saver
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = LLMNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -959,7 +959,7 @@ class LLMNode(Node):
return variable_mapping
@classmethod
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
def get_default_config(cls, filters: Optional[dict] = None):
return {
"type": "llm",
"config": {

View File

@ -17,7 +17,7 @@ class LoopEndNode(Node):
_node_data: LoopEndNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = LoopEndNodeData(**data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -49,7 +49,7 @@ class LoopNode(Node):
_node_data: LoopNodeData
execution_type = NodeExecutionType.CONTAINER
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = LoopNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -17,7 +17,7 @@ class LoopStartNode(Node):
_node_data: LoopStartNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = LoopStartNodeData(**data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -96,7 +96,7 @@ class ParameterExtractorNodeData(BaseNodeData):
def set_reasoning_mode(cls, v) -> str:
return v or "function_call"
def get_parameter_json_schema(self) -> dict:
def get_parameter_json_schema(self):
"""
Get parameter json schema.

View File

@ -63,7 +63,7 @@ class InvalidValueTypeError(ParameterExtractorNodeError):
expected_type: SegmentType,
actual_type: SegmentType | None,
value: Any,
) -> None:
):
message = (
f"Invalid value for parameter {parameter_name}, expected segment type: {expected_type}, "
f"actual_type: {actual_type}, python_type: {type(value)}, value: {value}"

View File

@ -92,7 +92,7 @@ class ParameterExtractorNode(Node):
_node_data: ParameterExtractorNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = ParameterExtractorNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -117,7 +117,7 @@ class ParameterExtractorNode(Node):
_model_config: Optional[ModelConfigWithCredentialsEntity] = None
@classmethod
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
def get_default_config(cls, filters: Optional[dict] = None):
return {
"model": {
"prompt_templates": {
@ -538,7 +538,7 @@ class ParameterExtractorNode(Node):
return prompt_messages
def _validate_result(self, data: ParameterExtractorNodeData, result: dict) -> dict:
def _validate_result(self, data: ParameterExtractorNodeData, result: dict):
if len(data.parameters) != len(result):
raise InvalidNumberOfParametersError("Invalid number of parameters")
@ -591,7 +591,7 @@ class ParameterExtractorNode(Node):
else:
return None
def _transform_result(self, data: ParameterExtractorNodeData, result: dict) -> dict:
def _transform_result(self, data: ParameterExtractorNodeData, result: dict):
"""
Transform result into standard format.
"""
@ -684,7 +684,7 @@ class ParameterExtractorNode(Node):
logger.info("extra error: %s", result)
return None
def _generate_default_result(self, data: ParameterExtractorNodeData) -> dict:
def _generate_default_result(self, data: ParameterExtractorNodeData):
"""
Generate default result.
"""

View File

@ -60,7 +60,7 @@ class QuestionClassifierNode(Node):
graph_runtime_state: "GraphRuntimeState",
*,
llm_file_saver: LLMFileSaver | None = None,
) -> None:
):
super().__init__(
id=id,
config=config,
@ -77,7 +77,7 @@ class QuestionClassifierNode(Node):
)
self._llm_file_saver = llm_file_saver
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = QuestionClassifierNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -271,7 +271,7 @@ class QuestionClassifierNode(Node):
return variable_mapping
@classmethod
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
def get_default_config(cls, filters: Optional[dict] = None):
"""
Get default config of node.
:param filters: filter by node config parameters (not used in this implementation).

View File

@ -15,7 +15,7 @@ class StartNode(Node):
_node_data: StartNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = StartNodeData(**data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -17,7 +17,7 @@ class TemplateTransformNode(Node):
_node_data: TemplateTransformNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = TemplateTransformNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -39,7 +39,7 @@ class TemplateTransformNode(Node):
return self._node_data
@classmethod
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
def get_default_config(cls, filters: Optional[dict] = None):
"""
Get default config of node.
:param filters: filter by node config parameters.

View File

@ -48,7 +48,7 @@ class ToolNode(Node):
_node_data: ToolNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = ToolNodeData.model_validate(data)
@classmethod

View File

@ -14,7 +14,7 @@ class VariableAggregatorNode(Node):
_node_data: VariableAssignerNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = VariableAssignerNodeData(**data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -30,7 +30,7 @@ class VariableAssignerNode(Node):
_node_data: VariableAssignerData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = VariableAssignerData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:
@ -58,7 +58,7 @@ class VariableAssignerNode(Node):
graph_init_params: "GraphInitParams",
graph_runtime_state: "GraphRuntimeState",
conv_var_updater_factory: _CONV_VAR_UPDATER_FACTORY = conversation_variable_updater_factory,
) -> None:
):
super().__init__(
id=id,
config=config,

View File

@ -32,5 +32,5 @@ class ConversationIDNotFoundError(VariableOperatorNodeError):
class InvalidDataError(VariableOperatorNodeError):
def __init__(self, message: str) -> None:
def __init__(self, message: str):
super().__init__(message)

View File

@ -57,7 +57,7 @@ class VariableAssignerNode(Node):
_node_data: VariableAssignerNodeData
def init_node_data(self, data: Mapping[str, Any]) -> None:
def init_node_data(self, data: Mapping[str, Any]):
self._node_data = VariableAssignerNodeData.model_validate(data)
def _get_error_strategy(self) -> Optional[ErrorStrategy]:

View File

@ -16,7 +16,7 @@ class WorkflowExecutionRepository(Protocol):
application domains or deployment scenarios.
"""
def save(self, execution: WorkflowExecution) -> None:
def save(self, execution: WorkflowExecution):
"""
Save or update a WorkflowExecution instance.

View File

@ -26,7 +26,7 @@ class WorkflowNodeExecutionRepository(Protocol):
application domains or deployment scenarios.
"""
def save(self, execution: WorkflowNodeExecution) -> None:
def save(self, execution: WorkflowNodeExecution):
"""
Save or update a NodeExecution instance.

View File

@ -50,7 +50,7 @@ class WorkflowCycleManager:
workflow_info: CycleManagerWorkflowInfo,
workflow_execution_repository: WorkflowExecutionRepository,
workflow_node_execution_repository: WorkflowNodeExecutionRepository,
) -> None:
):
self._application_generate_entity = application_generate_entity
self._workflow_system_variables = workflow_system_variables
self._workflow_info = workflow_info
@ -298,7 +298,7 @@ class WorkflowCycleManager:
error_message: Optional[str] = None,
exceptions_count: int = 0,
finished_at: Optional[datetime] = None,
) -> None:
):
"""Update workflow execution with completion data."""
execution.status = status
execution.outputs = outputs or {}
@ -315,7 +315,7 @@ class WorkflowCycleManager:
workflow_execution: WorkflowExecution,
conversation_id: Optional[str],
external_trace_id: Optional[str],
) -> None:
):
"""Add trace task if trace manager is provided."""
if trace_manager:
trace_manager.add_trace_task(
@ -333,7 +333,7 @@ class WorkflowCycleManager:
workflow_execution_id: str,
error_message: str,
now: datetime,
) -> None:
):
"""Fail all running node executions for a workflow."""
running_node_executions = [
node_exec
@ -403,7 +403,7 @@ class WorkflowCycleManager:
status: WorkflowNodeExecutionStatus,
error: Optional[str] = None,
handle_special_values: bool = False,
) -> None:
):
"""Update node execution with completion data."""
finished_at = naive_utc_now()
elapsed_time = (finished_at - event.start_at).total_seconds()

View File

@ -41,6 +41,7 @@ class WorkflowEntry:
user_from: UserFrom,
invoke_from: InvokeFrom,
call_depth: int,
variable_pool: VariablePool,
graph_runtime_state: GraphRuntimeState,
command_channel: Optional[CommandChannel] = None,
) -> None:
@ -351,7 +352,7 @@ class WorkflowEntry:
return result if isinstance(result, Mapping) or result is None else dict(result)
@staticmethod
def _handle_special_values(value: Any) -> Any:
def _handle_special_values(value: Any):
if value is None:
return value
if isinstance(value, dict):
@ -376,7 +377,7 @@ class WorkflowEntry:
user_inputs: Mapping[str, Any],
variable_pool: VariablePool,
tenant_id: str,
) -> None:
):
# NOTE(QuantumGhost): This logic should remain synchronized with
# the implementation of `load_into_variable_pool`, specifically the logic about
# variable existence checking.

View File

@ -13,7 +13,7 @@ class WorkflowRuntimeTypeConverter:
result = self._to_json_encodable_recursive(value)
return result if isinstance(result, Mapping) or result is None else dict(result)
def _to_json_encodable_recursive(self, value: Any) -> Any:
def _to_json_encodable_recursive(self, value: Any):
if value is None:
return value
if isinstance(value, (bool, int, str, float)):