model-compose 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. {model_compose-0.2.0 → model_compose-0.2.2}/PKG-INFO +1 -1
  2. {model_compose-0.2.0 → model_compose-0.2.2}/pyproject.toml +1 -1
  3. {model_compose-0.2.0/src/mindor/core/component/engine → model_compose-0.2.2/src/mindor/core/component}/base.py +25 -3
  4. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/component.py +5 -3
  5. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/engine/__init__.py +1 -1
  6. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/engine/http_client.py +8 -9
  7. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/engine/http_server.py +6 -6
  8. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/engine/mcp_client.py +5 -6
  9. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/engine/mcp_server.py +6 -6
  10. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/engine/shell.py +5 -6
  11. model_compose-0.2.2/src/mindor/core/component/engine/workflow.py +40 -0
  12. {model_compose-0.2.0/src/mindor/core/controller/engine → model_compose-0.2.2/src/mindor/core/controller}/base.py +28 -12
  13. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/controller.py +4 -2
  14. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/engine/__init__.py +0 -1
  15. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/engine/http_server.py +5 -8
  16. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/engine/mcp_server.py +20 -26
  17. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/runner/mcp_client.py +10 -11
  18. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/runner/runner.py +3 -4
  19. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/webui/gradio.py +31 -34
  20. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/webui/webui.py +1 -3
  21. model_compose-0.2.2/src/mindor/core/gateway/__init__.py +2 -0
  22. {model_compose-0.2.0/src/mindor/core/gateway/engine → model_compose-0.2.2/src/mindor/core/gateway}/base.py +7 -1
  23. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/gateway/engine/__init__.py +1 -1
  24. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/gateway/engine/http_tunnel.py +5 -6
  25. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/gateway/engine/ssh_tunnel.py +2 -3
  26. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/gateway/gateway.py +4 -2
  27. {model_compose-0.2.0/src/mindor/core/listener/engine → model_compose-0.2.2/src/mindor/core/listener}/base.py +7 -1
  28. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/listener/engine/__init__.py +0 -1
  29. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/listener/engine/http_callback.py +2 -3
  30. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/listener/listener.py +4 -2
  31. {model_compose-0.2.0/src/mindor/core/workflow/job/impl → model_compose-0.2.2/src/mindor/core/workflow/job}/base.py +10 -3
  32. model_compose-0.2.2/src/mindor/core/workflow/job/impl/__init__.py +2 -0
  33. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/workflow/job/impl/action.py +7 -8
  34. model_compose-0.2.0/src/mindor/core/workflow/job/impl/wait.py → model_compose-0.2.2/src/mindor/core/workflow/job/impl/delay.py +11 -12
  35. model_compose-0.2.2/src/mindor/core/workflow/job/job.py +10 -0
  36. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/workflow/workflow.py +9 -9
  37. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/action.py +3 -1
  38. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/impl/__init__.py +1 -0
  39. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/impl/common.py +2 -2
  40. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/impl/http_client.py +26 -21
  41. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/impl/http_server.py +2 -2
  42. model_compose-0.2.2/src/mindor/dsl/schema/action/impl/workflow.py +8 -0
  43. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/component.py +1 -0
  44. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/__init__.py +1 -0
  45. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/common.py +4 -2
  46. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/http_client.py +2 -2
  47. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/types.py +1 -0
  48. model_compose-0.2.2/src/mindor/dsl/schema/component/impl/workflow.py +17 -0
  49. model_compose-0.2.2/src/mindor/dsl/schema/controller/impl/common.py +13 -0
  50. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/controller/impl/http_server.py +4 -4
  51. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/controller/impl/mcp_server.py +3 -3
  52. model_compose-0.2.2/src/mindor/dsl/schema/controller/impl/webui.py +11 -0
  53. model_compose-0.2.2/src/mindor/dsl/schema/gateway/impl/common.py +9 -0
  54. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/gateway/impl/http_tunnel.py +5 -1
  55. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/job/impl/__init__.py +1 -1
  56. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/job/impl/common.py +1 -1
  57. model_compose-0.2.0/src/mindor/dsl/schema/job/impl/wait.py → model_compose-0.2.2/src/mindor/dsl/schema/job/impl/delay.py +11 -11
  58. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/job/impl/types.py +1 -1
  59. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/job/job.py +1 -1
  60. model_compose-0.2.2/src/mindor/dsl/schema/listener/impl/common.py +9 -0
  61. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/listener/impl/http_callback.py +8 -8
  62. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/runtime/impl/common.py +1 -1
  63. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/runtime/runtime.py +5 -0
  64. {model_compose-0.2.0 → model_compose-0.2.2}/src/model_compose.egg-info/PKG-INFO +1 -1
  65. {model_compose-0.2.0 → model_compose-0.2.2}/src/model_compose.egg-info/SOURCES.txt +11 -8
  66. model_compose-0.2.0/src/mindor/core/gateway/__init__.py +0 -2
  67. model_compose-0.2.0/src/mindor/core/workflow/job/impl/__init__.py +0 -3
  68. model_compose-0.2.0/src/mindor/core/workflow/job/job.py +0 -7
  69. model_compose-0.2.0/src/mindor/dsl/schema/controller/impl/common.py +0 -12
  70. model_compose-0.2.0/src/mindor/dsl/schema/controller/impl/webui.py +0 -7
  71. model_compose-0.2.0/src/mindor/dsl/schema/gateway/impl/common.py +0 -8
  72. model_compose-0.2.0/src/mindor/dsl/schema/listener/impl/common.py +0 -8
  73. {model_compose-0.2.0 → model_compose-0.2.2}/LICENSE +0 -0
  74. {model_compose-0.2.0 → model_compose-0.2.2}/README.md +0 -0
  75. {model_compose-0.2.0 → model_compose-0.2.2}/setup.cfg +0 -0
  76. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/__init__.py +0 -0
  77. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/cli/__init__.py +0 -0
  78. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/cli/compose.py +0 -0
  79. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/__init__.py +0 -0
  80. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/component/__init__.py +0 -0
  81. {model_compose-0.2.0/src/mindor/core/component/engine → model_compose-0.2.2/src/mindor/core/component}/context.py +0 -0
  82. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/compose/__init__.py +0 -0
  83. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/compose/compose.py +0 -0
  84. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/compose/manager.py +0 -0
  85. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/__init__.py +0 -0
  86. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/runner/__init__.py +0 -0
  87. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/runner/client.py +0 -0
  88. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/runner/http_client.py +0 -0
  89. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/controller/webui/__init__.py +0 -0
  90. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/listener/__init__.py +0 -0
  91. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/runtime/__init__.py +0 -0
  92. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/runtime/env.py +0 -0
  93. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/services/__init__.py +0 -0
  94. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/services/async_service.py +0 -0
  95. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/__init__.py +0 -0
  96. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/expiring.py +0 -0
  97. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/http_client.py +0 -0
  98. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/http_request.py +0 -0
  99. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/http_status.py +0 -0
  100. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/image.py +0 -0
  101. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/mcp_client.py +0 -0
  102. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/renderer.py +0 -0
  103. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/ssh_client.py +0 -0
  104. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/streaming.py +0 -0
  105. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/time.py +0 -0
  106. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/utils/workqueue.py +0 -0
  107. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/workflow/__init__.py +0 -0
  108. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/workflow/context.py +0 -0
  109. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/workflow/job/__init__.py +0 -0
  110. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/core/workflow/schema.py +0 -0
  111. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/__init__.py +0 -0
  112. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/loader.py +0 -0
  113. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/__init__.py +0 -0
  114. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/__init__.py +0 -0
  115. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/impl/mcp_client.py +0 -0
  116. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/impl/mcp_server.py +0 -0
  117. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/action/impl/shell.py +0 -0
  118. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/__init__.py +0 -0
  119. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/http_server.py +0 -0
  120. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/mcp_client.py +0 -0
  121. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/mcp_server.py +0 -0
  122. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/component/impl/shell.py +0 -0
  123. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/compose.py +0 -0
  124. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/controller/__init__.py +0 -0
  125. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/controller/controller.py +0 -0
  126. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
  127. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
  128. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/gateway/__init__.py +0 -0
  129. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/gateway/gateway.py +0 -0
  130. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/gateway/impl/__init__.py +0 -0
  131. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/gateway/impl/ssh_tunnel.py +0 -0
  132. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/gateway/impl/types.py +0 -0
  133. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/job/__init__.py +0 -0
  134. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/job/impl/action.py +0 -0
  135. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/listener/__init__.py +0 -0
  136. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
  137. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
  138. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/listener/listener.py +0 -0
  139. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
  140. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
  141. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/runtime/impl/docker.py +0 -0
  142. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
  143. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/utils/__init__.py +0 -0
  144. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/utils/annotation.py +0 -0
  145. {model_compose-0.2.0 → model_compose-0.2.2}/src/mindor/dsl/schema/workflow.py +0 -0
  146. {model_compose-0.2.0 → model_compose-0.2.2}/src/model_compose.egg-info/dependency_links.txt +0 -0
  147. {model_compose-0.2.0 → model_compose-0.2.2}/src/model_compose.egg-info/entry_points.txt +0 -0
  148. {model_compose-0.2.0 → model_compose-0.2.2}/src/model_compose.egg-info/requires.txt +0 -0
  149. {model_compose-0.2.0 → model_compose-0.2.2}/src/model_compose.egg-info/top_level.txt +0 -0
  150. {model_compose-0.2.0 → model_compose-0.2.2}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: model-compose
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: model-compose: Declarative AI Model and Workflow Orchestrator
5
5
  Author-email: Hanyeol Cho <hanyeol.cho@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "model-compose"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "model-compose: Declarative AI Model and Workflow Orchestrator"
5
5
  authors = [
6
6
  { name = "Hanyeol Cho", email = "hanyeol.cho@gmail.com" }
@@ -1,8 +1,10 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable, Any
2
2
  from abc import ABC, abstractmethod
3
-
4
3
  from mindor.dsl.schema.component import ComponentConfig, ComponentType
5
4
  from mindor.dsl.schema.action import ActionConfig
5
+ from mindor.dsl.schema.listener import ListenerConfig
6
+ from mindor.dsl.schema.gateway import GatewayConfig
7
+ from mindor.dsl.schema.workflow import WorkflowConfig
6
8
  from mindor.core.services import AsyncService
7
9
  from mindor.core.utils.workqueue import WorkQueue
8
10
  from .context import ComponentActionContext
@@ -30,12 +32,26 @@ class ActionResolver:
30
32
 
31
33
  return default_ids[0] if default_ids else "__default__"
32
34
 
35
+ class ComponentGlobalConfigs:
36
+ def __init__(
37
+ self,
38
+ components: Dict[str, ComponentConfig],
39
+ listeners: List[ListenerConfig],
40
+ gateways: List[GatewayConfig],
41
+ workflows: Dict[str, WorkflowConfig]
42
+ ):
43
+ self.components: Dict[str, ComponentConfig] = components
44
+ self.listeners: List[ListenerConfig] = listeners
45
+ self.gateways: List[GatewayConfig] = gateways
46
+ self.workflows: Dict[str, WorkflowConfig] = workflows
47
+
33
48
  class ComponentEngine(AsyncService):
34
- def __init__(self, id: str, config: ComponentConfig, daemon: bool):
49
+ def __init__(self, id: str, config: ComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool):
35
50
  super().__init__(daemon)
36
51
 
37
52
  self.id: str = id
38
53
  self.config: ComponentConfig = config
54
+ self.global_configs: ComponentGlobalConfigs = global_configs
39
55
  self.queue: Optional[WorkQueue] = None
40
56
 
41
57
  if self.config.max_concurrent_count > 0:
@@ -66,4 +82,10 @@ class ComponentEngine(AsyncService):
66
82
  async def _run(self, action: ActionConfig, context: ComponentActionContext) -> Any:
67
83
  pass
68
84
 
69
- ComponentEngineMap: Dict[ComponentType, Type[ComponentEngine]] = {}
85
+ def register_component(type: ComponentType):
86
+ def decorator(cls: Type[ComponentEngine]) -> Type[ComponentEngine]:
87
+ ComponentRegistry[type] = cls
88
+ return cls
89
+ return decorator
90
+
91
+ ComponentRegistry: Dict[ComponentType, Type[ComponentEngine]] = {}
@@ -1,6 +1,6 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
2
  from mindor.dsl.schema.component import ComponentConfig
3
- from .engine import ComponentEngine, ComponentEngineMap
3
+ from .base import ComponentEngine, ComponentGlobalConfigs, ComponentRegistry
4
4
 
5
5
  ComponentInstances: Dict[str, ComponentEngine] = {}
6
6
 
@@ -27,12 +27,14 @@ class ComponentResolver:
27
27
 
28
28
  return default_ids[0] if default_ids else "__default__"
29
29
 
30
- def create_component(id: str, config: ComponentConfig, daemon: bool) -> ComponentEngine:
30
+ def create_component(id: str, config: ComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool) -> ComponentEngine:
31
31
  try:
32
32
  component = ComponentInstances[id] if id in ComponentInstances else None
33
33
 
34
34
  if not component:
35
- component = ComponentEngineMap[config.type](id, config, daemon)
35
+ if not ComponentRegistry:
36
+ from . import engine
37
+ component = ComponentRegistry[config.type](id, config, global_configs, daemon)
36
38
  ComponentInstances[id] = component
37
39
 
38
40
  return component
@@ -1,6 +1,6 @@
1
- from .base import *
2
1
  from .http_server import *
3
2
  from .http_client import *
4
3
  from .mcp_server import *
5
4
  from .mcp_client import *
5
+ from .workflow import *
6
6
  from .shell import *
@@ -1,13 +1,13 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, AsyncIterator, Any
2
2
  from abc import ABC, abstractmethod
3
3
  from mindor.dsl.schema.component import HttpClientComponentConfig
4
- from mindor.dsl.schema.action import ActionConfig, HttpClientActionConfig, HttpClientCompletionConfig
4
+ from mindor.dsl.schema.action import ActionConfig, HttpClientActionConfig, HttpClientCompletionType, HttpClientCompletionConfig
5
5
  from mindor.core.listener import HttpCallbackListener
6
6
  from mindor.core.utils.http_client import HttpClient
7
7
  from mindor.core.utils.http_status import is_status_code_matched
8
8
  from mindor.core.utils.time import parse_duration
9
- from .base import ComponentEngine, ComponentType, ComponentEngineMap
10
- from .context import ComponentActionContext
9
+ from ..base import ComponentEngine, ComponentType, ComponentGlobalConfigs, register_component
10
+ from ..context import ComponentActionContext
11
11
  from datetime import datetime, timezone
12
12
  import asyncio
13
13
 
@@ -79,11 +79,11 @@ class HttpClientAction:
79
79
  self._configure_completion()
80
80
 
81
81
  def _configure_completion(self) -> None:
82
- if self.config.completion.type == "polling":
82
+ if self.config.completion.type == HttpClientCompletionType.POLLING:
83
83
  self.completion = HttpClientPollingCompletion(self.config.completion)
84
84
  return
85
85
 
86
- if self.config.completion.type == "callback":
86
+ if self.config.completion.type == HttpClientCompletionType.CALLBACK:
87
87
  self.completion = HttpClientCallbackCompletion(self.config.completion)
88
88
  return
89
89
 
@@ -111,9 +111,10 @@ class HttpClientAction:
111
111
 
112
112
  return await context.render_variable(self.config.endpoint)
113
113
 
114
+ @register_component(ComponentType.HTTP_CLIENT)
114
115
  class HttpClientComponent(ComponentEngine):
115
- def __init__(self, id: str, config: HttpClientComponentConfig, daemon: bool):
116
- super().__init__(id, config, daemon)
116
+ def __init__(self, id: str, config: HttpClientComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool):
117
+ super().__init__(id, config, global_configs, daemon)
117
118
 
118
119
  self.client: HttpClient = None
119
120
 
@@ -126,5 +127,3 @@ class HttpClientComponent(ComponentEngine):
126
127
 
127
128
  async def _run(self, action: ActionConfig, context: ComponentActionContext) -> Any:
128
129
  return await HttpClientAction(action).run(context, self.client)
129
-
130
- ComponentEngineMap[ComponentType.HTTP_CLIENT] = HttpClientComponent
@@ -1,11 +1,13 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
2
  from mindor.dsl.schema.component import HttpServerComponentConfig
3
- from .base import ComponentEngine, ComponentType, ComponentEngineMap, ActionConfig
4
- from .context import ComponentActionContext
3
+ from mindor.dsl.schema.action import ActionConfig, HttpServerActionConfig
4
+ from ..base import ComponentEngine, ComponentType, ComponentGlobalConfigs, register_component
5
+ from ..context import ComponentActionContext
5
6
 
7
+ @register_component(ComponentType.HTTP_SERVER)
6
8
  class HttpServerComponent(ComponentEngine):
7
- def __init__(self, id: str, config: HttpServerComponentConfig, daemon: bool):
8
- super().__init__(id, config, daemon)
9
+ def __init__(self, id: str, config: HttpServerComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool):
10
+ super().__init__(id, config, global_configs, daemon)
9
11
 
10
12
  async def _serve(self) -> None:
11
13
  pass
@@ -15,5 +17,3 @@ class HttpServerComponent(ComponentEngine):
15
17
 
16
18
  async def _run(self, action: ActionConfig, context: ComponentActionContext) -> Any:
17
19
  return {}
18
-
19
- ComponentEngineMap[ComponentType.HTTP_SERVER] = HttpServerComponent
@@ -2,8 +2,8 @@ from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annot
2
2
  from mindor.dsl.schema.component import McpClientComponentConfig
3
3
  from mindor.dsl.schema.action import ActionConfig, McpClientActionConfig
4
4
  from mindor.core.utils.mcp_client import McpClient, ContentBlock, TextContent, ImageContent, AudioContent
5
- from .base import ComponentEngine, ComponentType, ComponentEngineMap, ActionConfig
6
- from .context import ComponentActionContext
5
+ from ..base import ComponentEngine, ComponentType, ComponentGlobalConfigs, register_component
6
+ from ..context import ComponentActionContext
7
7
 
8
8
  class McpClientAction():
9
9
  def __init__(self, config: McpClientActionConfig):
@@ -27,9 +27,10 @@ class McpClientAction():
27
27
 
28
28
  return None
29
29
 
30
+ @register_component(ComponentType.MCP_CLIENT)
30
31
  class McpClientComponent(ComponentEngine):
31
- def __init__(self, id: str, config: McpClientComponentConfig, daemon: bool):
32
- super().__init__(id, config, daemon)
32
+ def __init__(self, id: str, config: McpClientComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool):
33
+ super().__init__(id, config, global_configs, daemon)
33
34
 
34
35
  async def _serve(self) -> None:
35
36
  self.client = McpClient(self.config.url, self.config.headers)
@@ -40,5 +41,3 @@ class McpClientComponent(ComponentEngine):
40
41
 
41
42
  async def _run(self, action: ActionConfig, context: ComponentActionContext) -> Any:
42
43
  return await McpClientAction(action).run(context, self.client)
43
-
44
- ComponentEngineMap[ComponentType.MCP_CLIENT] = McpClientComponent
@@ -1,11 +1,13 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
2
  from mindor.dsl.schema.component import McpServerComponentConfig
3
- from .base import ComponentEngine, ComponentType, ComponentEngineMap, ActionConfig
4
- from .context import ComponentActionContext
3
+ from mindor.dsl.schema.action import ActionConfig, McpServerActionConfig
4
+ from ..base import ComponentEngine, ComponentType, ComponentGlobalConfigs, register_component
5
+ from ..context import ComponentActionContext
5
6
 
7
+ @register_component(ComponentType.MCP_SERVER)
6
8
  class McpServerComponent(ComponentEngine):
7
- def __init__(self, id: str, config: McpServerComponentConfig, daemon: bool):
8
- super().__init__(id, config, daemon)
9
+ def __init__(self, id: str, config: McpServerComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool):
10
+ super().__init__(id, config, global_configs, daemon)
9
11
 
10
12
  async def _serve(self) -> None:
11
13
  pass
@@ -15,5 +17,3 @@ class McpServerComponent(ComponentEngine):
15
17
 
16
18
  async def _run(self, action: ActionConfig, context: ComponentActionContext) -> Any:
17
19
  return {}
18
-
19
- ComponentEngineMap[ComponentType.MCP_SERVER] = McpServerComponent
@@ -1,8 +1,8 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
2
  from mindor.dsl.schema.component import ShellComponentConfig
3
3
  from mindor.dsl.schema.action import ActionConfig, ShellActionConfig
4
- from .base import ComponentEngine, ComponentType, ComponentEngineMap, ActionConfig
5
- from .context import ComponentActionContext
4
+ from ..base import ComponentEngine, ComponentType, ComponentGlobalConfigs, register_component
5
+ from ..context import ComponentActionContext
6
6
  from asyncio.subprocess import Process
7
7
  import asyncio, os
8
8
 
@@ -64,9 +64,10 @@ class ShellAction:
64
64
  working_dir = self.base_dir or os.getcwd()
65
65
  return working_dir
66
66
 
67
+ @register_component(ComponentType.SHELL)
67
68
  class ShellComponent(ComponentEngine):
68
- def __init__(self, id: str, config: ShellComponentConfig, daemon: bool):
69
- super().__init__(id, config, daemon)
69
+ def __init__(self, id: str, config: ShellComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool):
70
+ super().__init__(id, config, global_configs, daemon)
70
71
 
71
72
  async def _serve(self) -> None:
72
73
  pass
@@ -76,5 +77,3 @@ class ShellComponent(ComponentEngine):
76
77
 
77
78
  async def _run(self, action: ActionConfig, context: ComponentActionContext) -> Any:
78
79
  return await ShellAction(action, self.config.base_dir, self.config.env).run(context)
79
-
80
- ComponentEngineMap[ComponentType.SHELL] = ShellComponent
@@ -0,0 +1,40 @@
1
+ from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
+ from mindor.dsl.schema.component import ComponentConfig, WorkflowComponentConfig
3
+ from mindor.dsl.schema.action import ActionConfig, WorkflowActionConfig
4
+ from mindor.dsl.schema.workflow import WorkflowConfig
5
+ from mindor.core.workflow import Workflow, WorkflowResolver, create_workflow
6
+ from ..base import ComponentEngine, ComponentType, ComponentGlobalConfigs, register_component
7
+ from ..context import ComponentActionContext
8
+ import asyncio, os
9
+
10
+ class WorkflowAction:
11
+ def __init__(self, config: WorkflowActionConfig, global_configs: ComponentGlobalConfigs):
12
+ self.config: WorkflowActionConfig = config
13
+ self.global_configs: ComponentGlobalConfigs = global_configs
14
+
15
+ async def run(self, context: ComponentActionContext) -> Any:
16
+ workflow_id = await context.render_variable(self.config.workflow)
17
+ input = await context.render_variable(self.config.input)
18
+
19
+ workflow = self._create_workflow(workflow_id)
20
+ output = await workflow.run(context.call_id, input)
21
+ context.register_source("output", output)
22
+
23
+ return (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else output
24
+
25
+ def _create_workflow(self, workflow_id: Optional[str]) -> Workflow:
26
+ return create_workflow(*WorkflowResolver(self.global_configs.workflows).resolve(workflow_id), self.global_configs)
27
+
28
+ @register_component(ComponentType.WORKFLOW)
29
+ class WorkflowComponent(ComponentEngine):
30
+ def __init__(self, id: str, config: WorkflowComponentConfig, global_configs: ComponentGlobalConfigs, daemon: bool):
31
+ super().__init__(id, config, global_configs, daemon)
32
+
33
+ async def _serve(self) -> None:
34
+ pass
35
+
36
+ async def _shutdown(self) -> None:
37
+ pass
38
+
39
+ async def _run(self, action: ActionConfig, context: ComponentActionContext) -> Any:
40
+ return await WorkflowAction(action, self.global_configs).run(context)
@@ -1,27 +1,32 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
+ from enum import Enum
2
3
  from dataclasses import dataclass
3
-
4
4
  from mindor.dsl.schema.controller import ControllerConfig, ControllerType
5
5
  from mindor.dsl.schema.component import ComponentConfig
6
6
  from mindor.dsl.schema.listener import ListenerConfig
7
7
  from mindor.dsl.schema.gateway import GatewayConfig
8
8
  from mindor.dsl.schema.workflow import WorkflowConfig
9
9
  from mindor.core.services import AsyncService
10
- from mindor.core.component import ComponentEngine, create_component
10
+ from mindor.core.component import ComponentEngine, ComponentGlobalConfigs, create_component
11
11
  from mindor.core.listener import ListenerEngine, create_listener
12
12
  from mindor.core.gateway import GatewayEngine, create_gateway
13
13
  from mindor.core.workflow import Workflow, WorkflowResolver, create_workflow
14
14
  from mindor.core.controller.webui import ControllerWebUI
15
15
  from mindor.core.utils.workqueue import WorkQueue
16
16
  from mindor.core.utils.expiring import ExpiringDict
17
-
18
17
  from threading import Lock
19
18
  import asyncio, ulid
20
19
 
20
+ class TaskStatus(str, Enum):
21
+ PENDING = "pending"
22
+ PROCESSING = "processing"
23
+ COMPLETED = "completed"
24
+ FAILED = "failed"
25
+
21
26
  @dataclass
22
27
  class TaskState:
23
28
  task_id: str
24
- status: Literal[ "pending", "processing", "completed", "failed" ]
29
+ status: TaskStatus
25
30
  output: Optional[Any] = None
26
31
  error: Optional[Any] = None
27
32
 
@@ -51,7 +56,7 @@ class ControllerEngine(AsyncService):
51
56
 
52
57
  async def run_workflow(self, workflow_id: Optional[str], input: Dict[str, Any], wait_for_completion: bool = True) -> TaskState:
53
58
  task_id = ulid.ulid()
54
- state = TaskState(task_id=task_id, status="pending")
59
+ state = TaskState(task_id=task_id, status=TaskStatus.PENDING)
55
60
  with self.task_states_lock:
56
61
  self.task_states.set(task_id, state)
57
62
 
@@ -122,16 +127,16 @@ class ControllerEngine(AsyncService):
122
127
  await asyncio.gather(*[ self._create_webui().stop() ])
123
128
 
124
129
  async def _run_workflow(self, task_id: str, workflow_id: Optional[str], input: Dict[str, Any]) -> TaskState:
125
- state = TaskState(task_id=task_id, status="processing")
130
+ state = TaskState(task_id=task_id, status=TaskStatus.PROCESSING)
126
131
  with self.task_states_lock:
127
132
  self.task_states.set(task_id, state)
128
133
 
129
134
  try:
130
- workflow = create_workflow(*WorkflowResolver(self.workflows).resolve(workflow_id), self.components)
135
+ workflow = self._create_workflow(workflow_id)
131
136
  output = await workflow.run(task_id, input)
132
- state = TaskState(task_id=task_id, status="completed", output=output)
137
+ state = TaskState(task_id=task_id, status=TaskStatus.COMPLETED, output=output)
133
138
  except Exception as e:
134
- state = TaskState(task_id=task_id, status="failed", error=str(e))
139
+ state = TaskState(task_id=task_id, status=TaskStatus.FAILED, error=str(e))
135
140
 
136
141
  with self.task_states_lock:
137
142
  self.task_states.set(task_id, state, 1 * 3600)
@@ -139,7 +144,8 @@ class ControllerEngine(AsyncService):
139
144
  return state
140
145
 
141
146
  def _create_components(self) -> List[ComponentEngine]:
142
- return [ create_component(component_id, config, self.daemon) for component_id, config in self.components.items() ]
147
+ global_configs = self._get_component_global_configs()
148
+ return [ create_component(component_id, config, global_configs, self.daemon) for component_id, config in self.components.items() ]
143
149
 
144
150
  def _create_listeners(self) -> List[ListenerEngine]:
145
151
  return [ create_listener(f"listener-{index}", config, self.daemon) for index, config in enumerate(self.listeners) ]
@@ -151,6 +157,16 @@ class ControllerEngine(AsyncService):
151
157
  return ControllerWebUI(self.config.webui, self.config, self.components, self.workflows, self.daemon)
152
158
 
153
159
  def _create_workflow(self, workflow_id: Optional[str]) -> Workflow:
154
- return create_workflow(*WorkflowResolver(self.workflows).resolve(workflow_id), self.components)
160
+ global_configs = self._get_component_global_configs()
161
+ return create_workflow(*WorkflowResolver(self.workflows).resolve(workflow_id), global_configs)
162
+
163
+ def _get_component_global_configs(self) -> ComponentGlobalConfigs:
164
+ return ComponentGlobalConfigs(self.components, self.listeners, self.gateways, self.workflows)
165
+
166
+ def register_controller(type: ControllerType):
167
+ def decorator(cls: Type[ControllerEngine]) -> Type[ControllerEngine]:
168
+ ControllerRegistry[type] = cls
169
+ return cls
170
+ return decorator
155
171
 
156
- ControllerEngineMap: Dict[ControllerType, Type[ControllerEngine]] = {}
172
+ ControllerRegistry: Dict[ControllerType, Type[ControllerEngine]] = {}
@@ -4,7 +4,7 @@ from mindor.dsl.schema.component import ComponentConfig
4
4
  from mindor.dsl.schema.listener import ListenerConfig
5
5
  from mindor.dsl.schema.gateway import GatewayConfig
6
6
  from mindor.dsl.schema.workflow import WorkflowConfig
7
- from .engine import ControllerEngine, ControllerEngineMap
7
+ from .base import ControllerEngine, ControllerRegistry
8
8
 
9
9
  def create_controller(
10
10
  config: ControllerConfig,
@@ -15,6 +15,8 @@ def create_controller(
15
15
  daemon: bool
16
16
  ) -> ControllerEngine:
17
17
  try:
18
- return ControllerEngineMap[config.type](config, components, listeners, gateways, workflows, daemon)
18
+ if not ControllerRegistry:
19
+ from . import engine
20
+ return ControllerRegistry[config.type](config, components, listeners, gateways, workflows, daemon)
19
21
  except KeyError:
20
22
  raise ValueError(f"Unsupported controller type: {config.type}")
@@ -1,3 +1,2 @@
1
- from .base import *
2
1
  from .http_server import *
3
2
  from .mcp_server import *
@@ -1,7 +1,6 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
2
  from typing_extensions import Self
3
3
  from pydantic import BaseModel
4
-
5
4
  from mindor.dsl.schema.controller import HttpServerControllerConfig
6
5
  from mindor.dsl.schema.component import ComponentConfig
7
6
  from mindor.dsl.schema.listener import ListenerConfig
@@ -9,8 +8,7 @@ from mindor.dsl.schema.gateway import GatewayConfig
9
8
  from mindor.dsl.schema.workflow import WorkflowConfig
10
9
  from mindor.core.utils.http_request import parse_request_body, parse_options_header
11
10
  from mindor.core.utils.streaming import StreamResource
12
- from .base import ControllerEngine, ControllerType, ControllerEngineMap, TaskState
13
-
11
+ from ..base import ControllerEngine, ControllerType, TaskState, TaskStatus, register_controller
14
12
  from fastapi import FastAPI, APIRouter, Request, Body, HTTPException
15
13
  from fastapi.middleware.cors import CORSMiddleware
16
14
  from fastapi.responses import Response, JSONResponse, StreamingResponse
@@ -38,6 +36,7 @@ class TaskResult(BaseModel):
38
36
  error=instance.error
39
37
  )
40
38
 
39
+ @register_controller(ControllerType.HTTP_SERVER)
41
40
  class HttpServerController(ControllerEngine):
42
41
  def __init__(
43
42
  self,
@@ -76,7 +75,7 @@ class HttpServerController(ControllerEngine):
76
75
  state = await self.run_workflow(body.workflow_id, body.input, body.wait_for_completion)
77
76
 
78
77
  if body.output_only and not body.wait_for_completion:
79
- raise HTTPException(status_code=400, detail="output_only requires wait_for_completion=True.")
78
+ raise HTTPException(status_code=400, detail="output_only requires wait_for_completion=true.")
80
79
 
81
80
  if body.output_only:
82
81
  return self._render_task_output(state)
@@ -130,10 +129,10 @@ class HttpServerController(ControllerEngine):
130
129
  return JSONResponse(content=TaskResult.from_instance(state).model_dump(exclude_none=True))
131
130
 
132
131
  def _render_task_output(self, state: TaskState) -> Response:
133
- if state.status in [ "pending", "processing" ]:
132
+ if state.status in [ TaskStatus.PENDING, TaskStatus.PROCESSING ]:
134
133
  raise HTTPException(status_code=202, detail="Task is still in progress.")
135
134
 
136
- if state.status == "failed":
135
+ if state.status == TaskStatus.FAILED:
137
136
  raise HTTPException(status_code=500, detail=str(state.error))
138
137
 
139
138
  if isinstance(state.output, StreamResource):
@@ -160,5 +159,3 @@ class HttpServerController(ControllerEngine):
160
159
  headers["Content-Disposition"] = f'attachment; filename="{filename}"'
161
160
 
162
161
  return headers
163
-
164
- ControllerEngineMap[ControllerType.HTTP_SERVER] = HttpServerController
@@ -3,12 +3,12 @@ from mindor.dsl.schema.controller import McpServerControllerConfig
3
3
  from mindor.dsl.schema.component import ComponentConfig
4
4
  from mindor.dsl.schema.listener import ListenerConfig
5
5
  from mindor.dsl.schema.gateway import GatewayConfig
6
- from mindor.dsl.schema.workflow import WorkflowConfig, WorkflowVariableConfig
6
+ from mindor.dsl.schema.workflow import WorkflowConfig, WorkflowVariableConfig, WorkflowVariableType, WorkflowVariableFormat
7
7
  from mindor.core.workflow.schema import WorkflowSchema, create_workflow_schema
8
8
  from mindor.core.utils.streaming import StreamResource, Base64StreamResource
9
9
  from mindor.core.utils.streaming import save_stream_to_temporary_file
10
10
  from mindor.core.utils.http_client import create_stream_with_url
11
- from .base import ControllerEngine, ControllerType, ControllerEngineMap, TaskState
11
+ from ..base import ControllerEngine, ControllerType, TaskState, register_controller
12
12
  from mcp.server.fastmcp.server import FastMCP
13
13
  from mcp.types import TextContent, ImageContent, AudioContent
14
14
  import uvicorn, re, json
@@ -35,13 +35,12 @@ class WorkflowToolGenerator():
35
35
  input: Dict[str, Any] = {}
36
36
 
37
37
  for value, variable in zip(arguments, workflow.input):
38
- type, subtype, format = variable.type.value, variable.subtype, variable.format.value if variable.format else None
39
- input[variable.name or "input"] = await self._convert_input_value(value, type, subtype, format, variable.default)
38
+ input[variable.name or "input"] = await self._convert_input_value(value, variable.type, variable.subtype, variable.format, variable.default)
40
39
 
41
40
  return input
42
41
 
43
- async def _convert_input_value(self, value: Any, type: str, subtype: Optional[str], format: Optional[str], default: Optional[Any]) -> Any:
44
- if type in [ "image", "audio", "video", "file" ]:
42
+ async def _convert_input_value(self, value: Any, type: WorkflowVariableType, subtype: Optional[str], format: Optional[WorkflowVariableFormat], default: Optional[Any]) -> Any:
43
+ if type in [ WorkflowVariableType.IMAGE, WorkflowVariableType.AUDIO, WorkflowVariableType.VIDEO, WorkflowVariableType.FILE ]:
45
44
  if format and format != "path":
46
45
  pass
47
46
 
@@ -53,23 +52,21 @@ class WorkflowToolGenerator():
53
52
  if state.output:
54
53
  if len(workflow.output) == 1 and not workflow.output[0].name:
55
54
  variable = workflow.output[0]
56
- type, subtype, format = variable.type.value, variable.subtype, variable.format.value if variable.format else None
57
- output.append(await self._convert_output_value(state.output, type, subtype, format))
55
+ output.append(await self._convert_output_value(state.output, variable.type, variable.subtype, variable.format))
58
56
  else:
59
57
  for variable in workflow.output:
60
- type, subtype, format = variable.type.value, variable.subtype, variable.format.value if variable.format else None
61
- output.append(await self._convert_output_value(state.output[variable.name], type, subtype, format))
58
+ output.append(await self._convert_output_value(state.output[variable.name], variable.type, variable.subtype, variable.format))
62
59
 
63
60
  return output
64
61
 
65
- async def _convert_output_value(self, value: Any, type: str, subtype: Optional[str], format: Optional[str]) -> Union[TextContent, ImageContent, AudioContent]:
66
- if type in [ "image", "audio", "video", "file" ]:
67
- if format == "base64" and len(value) < 1024 * 1024: # at most 1MB
68
- if type == "image":
62
+ async def _convert_output_value(self, value: Any, type: WorkflowVariableType, subtype: Optional[str], format: Optional[WorkflowVariableFormat]) -> Union[TextContent, ImageContent, AudioContent]:
63
+ if type in [ WorkflowVariableType.IMAGE, WorkflowVariableType.AUDIO, WorkflowVariableType.VIDEO, WorkflowVariableType.FILE ]:
64
+ if format == WorkflowVariableFormat.BASE64 and len(value) < 1024 * 1024: # at most 1MB
65
+ if type == WorkflowVariableType.IMAGE:
69
66
  return ImageContent(type="image", data=value, mimeType=f"{type}/{subtype}")
70
- if type == "audio":
67
+ if type == WorkflowVariableType.AUDIO:
71
68
  return AudioContent(type="audio", data=value, mimeType=f"{type}/{subtype}")
72
- if not format or format not in [ "path", "url" ]:
69
+ if not format or format not in [ WorkflowVariableFormat.PATH, WorkflowVariableFormat.URL ]:
73
70
  value = await self._save_value_to_temporary_file(value, subtype, format)
74
71
  return TextContent(type="text", text=value)
75
72
 
@@ -78,11 +75,11 @@ class WorkflowToolGenerator():
78
75
 
79
76
  return TextContent(type="text", text=str(value))
80
77
 
81
- async def _save_value_to_temporary_file(self, value: Any, subtype: Optional[str], format: Optional[str]) -> Optional[str]:
82
- if format == "base64" and isinstance(value, str):
78
+ async def _save_value_to_temporary_file(self, value: Any, subtype: Optional[str], format: Optional[WorkflowVariableFormat]) -> Optional[str]:
79
+ if format == WorkflowVariableFormat.BASE64 and isinstance(value, str):
83
80
  return await save_stream_to_temporary_file(Base64StreamResource(value), subtype)
84
81
 
85
- if format == "url" and isinstance(value, str):
82
+ if format == WorkflowVariableFormat.URL and isinstance(value, str):
86
83
  return await save_stream_to_temporary_file(await create_stream_with_url(value), subtype)
87
84
 
88
85
  if isinstance(value, StreamResource):
@@ -119,19 +116,18 @@ class WorkflowToolGenerator():
119
116
  return "\n".join(lines)
120
117
 
121
118
  def _get_docstring_type(self, variable: WorkflowVariableConfig) -> str:
122
- type, subtype, format = variable.type.value, variable.subtype, variable.format.value if variable.format else None
123
-
124
- if type == "object[]":
119
+ if variable.type == WorkflowVariableType.OBJECTS:
125
120
  return "list[dict]"
126
121
 
127
- if type == "number":
122
+ if variable.type == WorkflowVariableType.NUMBER:
128
123
  return "float"
129
124
 
130
- if type == "integer":
125
+ if variable.type == WorkflowVariableType.INTEGER:
131
126
  return "int"
132
127
 
133
128
  return "str"
134
129
 
130
+ @register_controller(ControllerType.MCP_SERVER)
135
131
  class McpServerController(ControllerEngine):
136
132
  def __init__(
137
133
  self,
@@ -180,5 +176,3 @@ class McpServerController(ControllerEngine):
180
176
  async def _shutdown(self) -> None:
181
177
  if self.server:
182
178
  self.server.should_exit = True
183
-
184
- ControllerEngineMap[ControllerType.MCP_SERVER] = McpServerController
@@ -1,5 +1,6 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
2
  from mindor.dsl.schema.controller import ControllerConfig
3
+ from mindor.dsl.schema.workflow import WorkflowVariableType, WorkflowVariableFormat
3
4
  from mindor.core.workflow.schema import WorkflowSchema
4
5
  from mindor.core.utils.mcp_client import McpClient, ContentBlock, TextContent, ImageContent, AudioContent
5
6
  from mindor.core.utils.streaming import StreamResource, FileStreamResource, Base64StreamResource
@@ -29,23 +30,21 @@ class McpControllerClient(ControllerClient):
29
30
 
30
31
  if len(workflow.output) == 1 and not workflow.output[0].name:
31
32
  content, variable = contents[0], workflow.output[0]
32
- type, subtype, format = variable.type.value, variable.subtype, variable.format.value if variable.format else None
33
- return await self._convert_output_value(content, type, subtype, format)
33
+ return await self._convert_output_value(content, variable.type, variable.subtype, variable.format)
34
34
 
35
35
  output = {}
36
36
  for content, variable in zip(contents, workflow.output):
37
- type, subtype, format = variable.type.value, variable.subtype, variable.format.value if variable.format else None
38
- output[variable.name or "output"] = await self._convert_output_value(content, type, subtype, format)
37
+ output[variable.name or "output"] = await self._convert_output_value(content, variable.type, variable.subtype, variable.format)
39
38
  return output
40
39
 
41
- async def _convert_output_value(self, content: ContentBlock, type: str, subtype: Optional[str], format: Optional[str]) -> Any:
40
+ async def _convert_output_value(self, content: ContentBlock, type: WorkflowVariableType, subtype: Optional[str], format: Optional[WorkflowVariableFormat]) -> Any:
42
41
  if isinstance(content, TextContent):
43
- if type in [ "json", "object[]" ]:
42
+ if type in [ WorkflowVariableType.JSON, WorkflowVariableType.OBJECTS ]:
44
43
  return json.loads(content.text)
45
- if type in [ "image", "audio", "video", "file" ]:
44
+ if type in [ WorkflowVariableType.IMAGE, WorkflowVariableType.AUDIO, WorkflowVariableType.VIDEO, WorkflowVariableType.FILE ]:
46
45
  if not format and os.path.exists(content.text):
47
46
  return FileStreamResource(content.text)
48
- if format == "base64": # content.text is path
47
+ if format == WorkflowVariableFormat.BASE64: # content.text is path
49
48
  return await encode_stream_to_base64(FileStreamResource(content.text))
50
49
  return content.text
51
50
 
@@ -54,11 +53,11 @@ class McpControllerClient(ControllerClient):
54
53
 
55
54
  return None
56
55
 
57
- async def _load_image_from_value(self, value: Any, subtype: Optional[str], format: Optional[str]) -> Optional[str]:
58
- if format == "base64" and isinstance(value, str):
56
+ async def _load_image_from_value(self, value: Any, subtype: Optional[str], format: Optional[WorkflowVariableFormat]) -> Optional[str]:
57
+ if format == WorkflowVariableFormat.BASE64 and isinstance(value, str):
59
58
  return await load_image_from_stream(Base64StreamResource(value), subtype)
60
59
 
61
- if format == "url" and isinstance(value, str):
60
+ if format == WorkflowVariableFormat.URL and isinstance(value, str):
62
61
  return await load_image_from_stream(await create_stream_with_url(value), subtype)
63
62
 
64
63
  if isinstance(value, StreamResource):