model-compose 0.1.1__tar.gz → 0.1.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.
- {model_compose-0.1.1 → model_compose-0.1.2}/PKG-INFO +1 -1
- {model_compose-0.1.1 → model_compose-0.1.2}/pyproject.toml +1 -1
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/component/engine/http_client.py +3 -7
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/webui/gradio.py +52 -27
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/webui/schema.py +88 -25
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/listener/engine/http_callback.py +2 -2
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/http_client.py +3 -3
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/template.py +2 -2
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/workflow/job.py +11 -9
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/workflow.py +13 -2
- {model_compose-0.1.1 → model_compose-0.1.2}/src/model_compose.egg-info/PKG-INFO +1 -1
- {model_compose-0.1.1 → model_compose-0.1.2}/LICENSE +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/README.md +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/setup.cfg +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/cli/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/cli/compose.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/component/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/component/component.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/component/engine/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/component/engine/base.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/component/engine/context.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/component/engine/http_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/compose/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/compose/compose.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/compose/manager.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/controller.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/engine/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/engine/base.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/engine/http_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/engine/mcp_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/runner/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/runner/client.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/runner/http_client.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/runner/mcp_client.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/runner/runner.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/webui/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/webui/webui.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/gateway/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/gateway/engine/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/gateway/engine/base.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/gateway/engine/http_tunnel.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/gateway/gateway.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/listener/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/listener/engine/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/listener/engine/base.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/listener/listener.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/runtime/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/runtime/env.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/services/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/services/async_service.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/expiring.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/http_request.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/image.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/streaming.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/time.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/utils/workqueue.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/workflow/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/workflow/context.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/workflow/workflow.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/loader.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/action.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/common.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/http_client.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/http_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/mcp_client.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/mcp_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/component.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/common.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/http_client.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/http_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/mcp_client.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/mcp_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/types.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/compose.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/controller.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/common.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/http_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/mcp_server.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/webui.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/gateway/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/gateway/gateway.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/gateway/impl/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/gateway/impl/common.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/gateway/impl/http_tunnel.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/gateway/impl/types.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/listener/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/listener/impl/common.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/listener/impl/http_callback.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/listener/listener.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/runtime/impl/common.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/runtime/impl/docker.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/runtime/runtime.py +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/model_compose.egg-info/SOURCES.txt +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/model_compose.egg-info/dependency_links.txt +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/model_compose.egg-info/entry_points.txt +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/model_compose.egg-info/requires.txt +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/src/model_compose.egg-info/top_level.txt +0 -0
- {model_compose-0.1.1 → model_compose-0.1.2}/tests/test_cli.py +0 -0
|
@@ -31,8 +31,8 @@ class HttpClientPollingCompletion:
|
|
|
31
31
|
|
|
32
32
|
while datetime.now(timezone.utc) < deadline:
|
|
33
33
|
response = await self.client.request(url, method, params, body, headers)
|
|
34
|
-
|
|
35
34
|
context.register_source("result", response)
|
|
35
|
+
|
|
36
36
|
status = (await context.render_template(self.config.status)) if self.config.status else response["status"]
|
|
37
37
|
|
|
38
38
|
if not status:
|
|
@@ -82,15 +82,11 @@ class HttpClientAction:
|
|
|
82
82
|
headers = await context.render_template({ **self.headers, **self.config.headers })
|
|
83
83
|
|
|
84
84
|
response, result = await self.client.request(url, method, params, body, headers), None
|
|
85
|
-
|
|
86
|
-
if response:
|
|
87
|
-
context.register_source("response", response)
|
|
85
|
+
context.register_source("response", response)
|
|
88
86
|
|
|
89
87
|
if self.config.completion:
|
|
90
88
|
result = await self._handle_completion(self.config.completion, context)
|
|
91
|
-
|
|
92
|
-
if result:
|
|
93
|
-
context.register_source("result", result)
|
|
89
|
+
context.register_source("result", result)
|
|
94
90
|
|
|
95
91
|
return (await context.render_template(self.config.output)) if self.config.output else (result or response)
|
|
96
92
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable, Awaitable, Any
|
|
2
|
-
from mindor.dsl.schema.workflow import WorkflowVariableConfig
|
|
2
|
+
from mindor.dsl.schema.workflow import WorkflowVariableConfig, WorkflowVariableGroupConfig
|
|
3
3
|
from mindor.core.utils.http_client import HttpStreamResource
|
|
4
4
|
from mindor.core.utils.image import load_image_from_stream
|
|
5
5
|
from mindor.core.utils.streaming import Base64StreamResource, save_stream_to_temporary_file
|
|
@@ -7,18 +7,23 @@ from .schema import WorkflowSchema
|
|
|
7
7
|
import gradio as gr
|
|
8
8
|
import json
|
|
9
9
|
|
|
10
|
+
class ComponentGroup:
|
|
11
|
+
def __init__(self, group: gr.Component, components: List[gr.Component]):
|
|
12
|
+
self.group: gr.Component = group
|
|
13
|
+
self.components: List[gr.Component] = components
|
|
14
|
+
|
|
10
15
|
class GradioWebUIBuilder:
|
|
11
16
|
def build(self, schema: Dict[str, WorkflowSchema], runner: Callable[[Optional[str], Any], Awaitable[Any]]) -> gr.Blocks:
|
|
12
17
|
with gr.Blocks() as blocks:
|
|
13
18
|
for workflow_id, workflow in schema.items():
|
|
14
|
-
async def
|
|
19
|
+
async def _run_workflow(input: Any, workflow_id=workflow_id) -> Any:
|
|
15
20
|
return await runner(workflow_id, input)
|
|
16
21
|
|
|
17
22
|
if len(schema) > 1:
|
|
18
23
|
with gr.Tab(label=workflow.name or workflow_id):
|
|
19
|
-
self._build_workflow_section(workflow,
|
|
24
|
+
self._build_workflow_section(workflow, _run_workflow)
|
|
20
25
|
else:
|
|
21
|
-
self._build_workflow_section(workflow,
|
|
26
|
+
self._build_workflow_section(workflow, _run_workflow)
|
|
22
27
|
|
|
23
28
|
return blocks
|
|
24
29
|
|
|
@@ -39,27 +44,16 @@ class GradioWebUIBuilder:
|
|
|
39
44
|
if not output_components:
|
|
40
45
|
output_components = gr.Textbox(label="", lines=8, interactive=False, show_copy_button=True)
|
|
41
46
|
|
|
42
|
-
async def
|
|
43
|
-
input = { variable.name: value for variable, value in zip(workflow.input, args) }
|
|
47
|
+
async def _run_workflow(*args):
|
|
48
|
+
input = { variable.name: value if value != "" else None for variable, value in zip(workflow.input, args) }
|
|
44
49
|
output = await runner(input)
|
|
45
|
-
|
|
46
|
-
if
|
|
47
|
-
if isinstance(output, dict):
|
|
48
|
-
outputs = []
|
|
49
|
-
for variable in workflow.output:
|
|
50
|
-
value = output[variable.name] if variable.name else output
|
|
51
|
-
outputs.append(await self._convert_type(value, variable.type, variable.subtype, variable.format))
|
|
52
|
-
output = outputs[0] if len(outputs) == 1 else outputs
|
|
53
|
-
else:
|
|
54
|
-
variable = workflow.output[0]
|
|
55
|
-
output = await self._convert_type(output, variable.type, variable.subtype, variable.format)
|
|
56
|
-
|
|
57
|
-
return output
|
|
50
|
+
output = await self._flatten_output(output, workflow.output)
|
|
51
|
+
return output[0] if len(output) == 1 else output
|
|
58
52
|
|
|
59
53
|
run_button.click(
|
|
60
|
-
fn=
|
|
54
|
+
fn=_run_workflow,
|
|
61
55
|
inputs=input_components,
|
|
62
|
-
outputs=output_components
|
|
56
|
+
outputs=self._flatten_output_components(output_components)
|
|
63
57
|
)
|
|
64
58
|
|
|
65
59
|
return section
|
|
@@ -89,7 +83,16 @@ class GradioWebUIBuilder:
|
|
|
89
83
|
|
|
90
84
|
return gr.Textbox(label=label, value=default, info=f"Unsupported type: {variable.type}")
|
|
91
85
|
|
|
92
|
-
def _build_output_component(self, variable: WorkflowVariableConfig) -> gr.Component:
|
|
86
|
+
def _build_output_component(self, variable: Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]) -> Union[gr.Component, List[ComponentGroup]]:
|
|
87
|
+
if isinstance(variable, WorkflowVariableGroupConfig):
|
|
88
|
+
groups: List[ComponentGroup] = []
|
|
89
|
+
for index in range(variable.repeat_count if variable.repeat_count != 0 else 100):
|
|
90
|
+
visible = True if variable.repeat_count != 0 or index == 0 else False
|
|
91
|
+
with gr.Column(visible=visible) as group:
|
|
92
|
+
components = [ self._build_output_component(v) for v in variable.variables ]
|
|
93
|
+
groups.append(ComponentGroup(group, components))
|
|
94
|
+
return groups
|
|
95
|
+
|
|
93
96
|
label = variable.name or ""
|
|
94
97
|
info = variable.description or ""
|
|
95
98
|
|
|
@@ -104,7 +107,34 @@ class GradioWebUIBuilder:
|
|
|
104
107
|
|
|
105
108
|
return gr.Textbox(label=label, info=f"Unsupported type: {variable.type}")
|
|
106
109
|
|
|
110
|
+
def _flatten_output_components(self, components: List[Union[gr.Component, List[ComponentGroup]]]) -> List[gr.Component]:
|
|
111
|
+
flattened = []
|
|
112
|
+
for item in components:
|
|
113
|
+
if isinstance(item, list):
|
|
114
|
+
for group in item:
|
|
115
|
+
flattened.extend(group.components)
|
|
116
|
+
else:
|
|
117
|
+
flattened.append(item)
|
|
118
|
+
return flattened
|
|
119
|
+
|
|
120
|
+
async def _flatten_output(self, output: Any, variables: List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]]) -> Any:
|
|
121
|
+
flattened = []
|
|
122
|
+
for variable in variables:
|
|
123
|
+
if isinstance(variable, WorkflowVariableGroupConfig):
|
|
124
|
+
group = output[variable.name] if variable.name else output
|
|
125
|
+
for value in group:
|
|
126
|
+
flattened.extend(await self._flatten_output(value, variable.variables))
|
|
127
|
+
else:
|
|
128
|
+
value = output[variable.name] if variable.name else output
|
|
129
|
+
flattened.append(await self._convert_type(value, variable.type, variable.subtype, variable.format))
|
|
130
|
+
return flattened
|
|
131
|
+
|
|
107
132
|
async def _convert_type(self, value: Any, type: Optional[str], subtype: Optional[str], format: Optional[str]) -> Any:
|
|
133
|
+
if type == "string":
|
|
134
|
+
if isinstance(value, (dict, list)):
|
|
135
|
+
return json.dumps(value)
|
|
136
|
+
return str(value)
|
|
137
|
+
|
|
108
138
|
if type == "image":
|
|
109
139
|
if format == "base64" and isinstance(value, str):
|
|
110
140
|
return await load_image_from_stream(Base64StreamResource(value), subtype)
|
|
@@ -119,9 +149,4 @@ class GradioWebUIBuilder:
|
|
|
119
149
|
return await save_stream_to_temporary_file(value, subtype)
|
|
120
150
|
return None
|
|
121
151
|
|
|
122
|
-
if type == "string":
|
|
123
|
-
if isinstance(value, (dict, list)):
|
|
124
|
-
return json.dumps(value)
|
|
125
|
-
return str(value)
|
|
126
|
-
|
|
127
152
|
return value
|
|
@@ -1,8 +1,41 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
2
|
from pydantic import BaseModel
|
|
3
|
-
from mindor.dsl.schema.workflow import WorkflowConfig, WorkflowVariableConfig
|
|
3
|
+
from mindor.dsl.schema.workflow import WorkflowConfig, WorkflowVariableConfig, WorkflowVariableGroupConfig
|
|
4
4
|
from mindor.dsl.schema.component import ComponentConfig
|
|
5
|
-
import re, json
|
|
5
|
+
import re, json
|
|
6
|
+
|
|
7
|
+
class WorkflowVariable:
|
|
8
|
+
def __init__(self, name: Optional[str], type: str, subtype: Optional[str], format: Optional[str], default: Optional[Any]):
|
|
9
|
+
self.name: Optional[str] = name
|
|
10
|
+
self.type: str = type
|
|
11
|
+
self.subtype: Optional[str] = subtype
|
|
12
|
+
self.format: Optional[str] = format
|
|
13
|
+
self.default: Optional[Any] = default
|
|
14
|
+
|
|
15
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
16
|
+
return {
|
|
17
|
+
"name": self.name,
|
|
18
|
+
"type": self.type,
|
|
19
|
+
"subtype": self.subtype,
|
|
20
|
+
"format": self.format,
|
|
21
|
+
"default": self.default
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def __eq__(self, other):
|
|
25
|
+
if not isinstance(other, WorkflowVariable):
|
|
26
|
+
return False
|
|
27
|
+
if self.name is None or other.name is None:
|
|
28
|
+
return False
|
|
29
|
+
return self.name == other.name
|
|
30
|
+
|
|
31
|
+
def __hash__(self):
|
|
32
|
+
return hash(self.name) if self.name is not None else id(self)
|
|
33
|
+
|
|
34
|
+
class WorkflowVariableGroup:
|
|
35
|
+
def __init__(self, name: Optional[str], variables: List[WorkflowVariable], repeat_count: int):
|
|
36
|
+
self.name: Optional[str] = name
|
|
37
|
+
self.variables: List[WorkflowVariable] = variables
|
|
38
|
+
self.repeat_count: int = repeat_count
|
|
6
39
|
|
|
7
40
|
class WorkflowVariableResolver:
|
|
8
41
|
def __init__(self):
|
|
@@ -15,12 +48,13 @@ class WorkflowVariableResolver:
|
|
|
15
48
|
(?:\s*\|\s*([^\}]+))? # default value after `|`
|
|
16
49
|
\}""", # }
|
|
17
50
|
re.VERBOSE,
|
|
18
|
-
)
|
|
51
|
+
),
|
|
52
|
+
"keypath": re.compile(r"[-_\w]+|\[\d+\]"),
|
|
19
53
|
}
|
|
20
54
|
|
|
21
|
-
def _enumerate_input_variables(self, value: Any, wanted_key: str) -> List[
|
|
55
|
+
def _enumerate_input_variables(self, value: Any, wanted_key: str) -> List[WorkflowVariable]:
|
|
22
56
|
if isinstance(value, str):
|
|
23
|
-
variables: List[
|
|
57
|
+
variables: List[WorkflowVariable] = []
|
|
24
58
|
|
|
25
59
|
for match in self.patterns["variable"].finditer(value):
|
|
26
60
|
key, path, type, subtype, format, default = match.group(1, 2, 3, 4, 5, 6)
|
|
@@ -29,7 +63,7 @@ class WorkflowVariableResolver:
|
|
|
29
63
|
default = self._parse_as_type(default, type)
|
|
30
64
|
|
|
31
65
|
if key == wanted_key:
|
|
32
|
-
variables.append((path, type or "string", subtype, format, default))
|
|
66
|
+
variables.append(WorkflowVariable(path, type or "string", subtype, format, default))
|
|
33
67
|
|
|
34
68
|
return variables
|
|
35
69
|
|
|
@@ -44,31 +78,51 @@ class WorkflowVariableResolver:
|
|
|
44
78
|
|
|
45
79
|
return []
|
|
46
80
|
|
|
47
|
-
def _enumerate_output_variables(self,
|
|
48
|
-
variables: List[
|
|
81
|
+
def _enumerate_output_variables(self, name: Optional[str], value: Any) -> List[WorkflowVariable]:
|
|
82
|
+
variables: List[WorkflowVariable] = []
|
|
49
83
|
|
|
50
84
|
if isinstance(value, str):
|
|
51
85
|
for match in self.patterns["variable"].finditer(value):
|
|
52
|
-
type, subtype, format, default = match.group(3, 4, 5, 6)
|
|
86
|
+
key, path, type, subtype, format, default = match.group(1, 2, 3, 4, 5, 6)
|
|
53
87
|
|
|
54
88
|
if type and default:
|
|
55
89
|
default = self._parse_as_type(default, type)
|
|
56
90
|
|
|
57
|
-
variables.append((
|
|
91
|
+
variables.append(WorkflowVariable(name, type or "string", subtype, format, default))
|
|
58
92
|
|
|
59
93
|
return variables
|
|
60
94
|
|
|
61
95
|
if isinstance(value, BaseModel):
|
|
62
|
-
return self._enumerate_output_variables(
|
|
96
|
+
return self._enumerate_output_variables(name, value.model_dump(exclude_none=True))
|
|
63
97
|
|
|
64
98
|
if isinstance(value, dict):
|
|
65
|
-
return sum([ self._enumerate_output_variables(f"{
|
|
99
|
+
return sum([ self._enumerate_output_variables(f"{name}.{k}" if name else f"{k}", v) for k, v in value.items() ], [])
|
|
66
100
|
|
|
67
101
|
if isinstance(value, list):
|
|
68
|
-
return sum([ self._enumerate_output_variables(f"{
|
|
102
|
+
return sum([ self._enumerate_output_variables(f"{name}[{i}]" if name else f"[{i}]", v) for i, v in enumerate(value) ], [])
|
|
69
103
|
|
|
70
104
|
return []
|
|
71
105
|
|
|
106
|
+
def _to_variable_config_list(self, variables: List[Union[WorkflowVariable, WorkflowVariableGroup]]) -> List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]]:
|
|
107
|
+
configs: List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]] = []
|
|
108
|
+
seen_single: Set[WorkflowVariable] = set()
|
|
109
|
+
|
|
110
|
+
for item in variables:
|
|
111
|
+
if isinstance(item, WorkflowVariableGroup):
|
|
112
|
+
group: List[WorkflowVariableConfig] = []
|
|
113
|
+
seen_in_group: Set[WorkflowVariable] = set()
|
|
114
|
+
for v in item.variables:
|
|
115
|
+
if v not in seen_in_group:
|
|
116
|
+
seen_in_group.add(v)
|
|
117
|
+
group.append(WorkflowVariableConfig(**v.to_dict()))
|
|
118
|
+
configs.append(WorkflowVariableGroupConfig(name=item.name, variables=group, repeat_count=item.repeat_count))
|
|
119
|
+
else:
|
|
120
|
+
if item not in seen_single:
|
|
121
|
+
seen_single.add(item)
|
|
122
|
+
configs.append(WorkflowVariableConfig(**item.to_dict()))
|
|
123
|
+
|
|
124
|
+
return configs
|
|
125
|
+
|
|
72
126
|
def _parse_as_type(self, value: Any, type: str) -> Any:
|
|
73
127
|
if type == "number":
|
|
74
128
|
return float(value)
|
|
@@ -86,7 +140,7 @@ class WorkflowVariableResolver:
|
|
|
86
140
|
|
|
87
141
|
class WorkflowInputResolver(WorkflowVariableResolver):
|
|
88
142
|
def resolve(self, workflow: WorkflowConfig, components: Dict[str, ComponentConfig]) -> List[WorkflowVariableConfig]:
|
|
89
|
-
variables: List[
|
|
143
|
+
variables: List[WorkflowVariable] = []
|
|
90
144
|
|
|
91
145
|
for job in workflow.jobs.values():
|
|
92
146
|
if not job.input or job.input == "${input}":
|
|
@@ -100,36 +154,45 @@ class WorkflowInputResolver(WorkflowVariableResolver):
|
|
|
100
154
|
else:
|
|
101
155
|
variables.extend(self._enumerate_input_variables(job.input, "input"))
|
|
102
156
|
|
|
103
|
-
|
|
157
|
+
for value in [ job.repeat_count ]:
|
|
158
|
+
variables.extend(self._enumerate_input_variables(value, "input"))
|
|
159
|
+
|
|
160
|
+
return self._to_variable_config_list(variables)
|
|
104
161
|
|
|
105
162
|
class WorkflowOutputResolver(WorkflowVariableResolver):
|
|
106
|
-
def resolve(self, workflow: WorkflowConfig, components: Dict[str, ComponentConfig]) -> List[WorkflowVariableConfig]:
|
|
107
|
-
variables: List[
|
|
108
|
-
|
|
163
|
+
def resolve(self, workflow: WorkflowConfig, components: Dict[str, ComponentConfig]) -> List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]]:
|
|
164
|
+
variables: List[Union[WorkflowVariable, WorkflowVariableGroup]] = []
|
|
165
|
+
|
|
109
166
|
for job_id, job in workflow.jobs.items():
|
|
110
167
|
if not self._is_terminal_job(workflow, job_id):
|
|
111
168
|
continue
|
|
112
|
-
|
|
169
|
+
|
|
170
|
+
job_variables: List[WorkflowVariable] = variables
|
|
171
|
+
repeat_count: int = job.repeat_count if isinstance(job.repeat_count, int) else 0
|
|
172
|
+
|
|
173
|
+
if repeat_count != 1:
|
|
174
|
+
variables.append(WorkflowVariableGroup(None, job_variables := [], repeat_count))
|
|
175
|
+
|
|
113
176
|
if not job.output or job.output == "${output}":
|
|
114
177
|
action_id = job.action or "__default__"
|
|
115
178
|
if isinstance(job.component, str):
|
|
116
179
|
component: Optional[ComponentConfig] = components[job.component] if job.component in components else None
|
|
117
180
|
if component:
|
|
118
|
-
|
|
181
|
+
job_variables.extend(self._enumerate_output_variables(None, component.actions[action_id].output))
|
|
119
182
|
else:
|
|
120
|
-
|
|
183
|
+
job_variables.extend(self._enumerate_output_variables(None, job.component.actions[action_id].output))
|
|
121
184
|
else:
|
|
122
|
-
|
|
185
|
+
job_variables.extend(self._enumerate_output_variables(None, job.output))
|
|
123
186
|
|
|
124
|
-
return
|
|
187
|
+
return self._to_variable_config_list(variables)
|
|
125
188
|
|
|
126
189
|
def _is_terminal_job(self, workflow: WorkflowConfig, job_id: str) -> bool:
|
|
127
190
|
return all(job_id not in job.depends_on for other_id, job in workflow.jobs.items() if other_id != job_id)
|
|
128
191
|
|
|
129
192
|
class WorkflowSchema:
|
|
130
|
-
def __init__(self, name: str, title: str, description: Optional[str], input: List[WorkflowVariableConfig], output: List[WorkflowVariableConfig]):
|
|
193
|
+
def __init__(self, name: str, title: str, description: Optional[str], input: List[WorkflowVariableConfig], output: List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]]):
|
|
131
194
|
self.name: str = name
|
|
132
195
|
self.title: str = title
|
|
133
196
|
self.description: Optional[str] = description
|
|
134
197
|
self.input: List[WorkflowVariableConfig] = input
|
|
135
|
-
self.output: List[WorkflowVariableConfig] = output
|
|
198
|
+
self.output: List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]] = output
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/listener/engine/http_callback.py
RENAMED
|
@@ -66,7 +66,7 @@ class HttpCallbackListener(ListenerEngine):
|
|
|
66
66
|
)
|
|
67
67
|
|
|
68
68
|
def _make_callback_handler(self, callback: HttpCallbackConfig) -> Callable:
|
|
69
|
-
async def
|
|
69
|
+
async def _handler(request: Request) -> Response:
|
|
70
70
|
content_type, _ = parse_options_header(request.headers, "Content-Type")
|
|
71
71
|
body, query = await parse_request_body(request, content_type), request.query_params
|
|
72
72
|
context: HttpCallbackContext = HttpCallbackContext(body, query, callback.bulk, callback.item)
|
|
@@ -85,7 +85,7 @@ class HttpCallbackListener(ListenerEngine):
|
|
|
85
85
|
|
|
86
86
|
return Response()
|
|
87
87
|
|
|
88
|
-
return
|
|
88
|
+
return _handler
|
|
89
89
|
|
|
90
90
|
async def _is_callback_succeeded(self, callback: HttpCallbackConfig, context: HttpCallbackContext) -> bool:
|
|
91
91
|
status = (await context.render_template(callback.status)) if callback.status else None
|
|
@@ -39,11 +39,11 @@ class HttpClient:
|
|
|
39
39
|
headers=headers
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
-
if response.status >= 400:
|
|
43
|
-
raise ValueError(f"Request failed with status {response.status}")
|
|
44
|
-
|
|
45
42
|
content = await self._parse_response_content(session, response)
|
|
46
43
|
|
|
44
|
+
if response.status >= 400:
|
|
45
|
+
raise ValueError(f"Request failed with status {response.status}: {content}")
|
|
46
|
+
|
|
47
47
|
if not isinstance(content, HttpStreamResource):
|
|
48
48
|
await session.close()
|
|
49
49
|
|
|
@@ -48,7 +48,7 @@ class TemplateRenderer:
|
|
|
48
48
|
|
|
49
49
|
value = default if value is None else value
|
|
50
50
|
|
|
51
|
-
if type and value:
|
|
51
|
+
if type and value is not None:
|
|
52
52
|
value = await self._convert_type(value, type, subtype, format)
|
|
53
53
|
|
|
54
54
|
if variable == text:
|
|
@@ -85,7 +85,7 @@ class TemplateRenderer:
|
|
|
85
85
|
|
|
86
86
|
if type == "integer":
|
|
87
87
|
return int(value)
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
if type == "json":
|
|
90
90
|
return json.loads(value)
|
|
91
91
|
|
|
@@ -3,7 +3,7 @@ from mindor.dsl.schema.workflow import JobConfig
|
|
|
3
3
|
from mindor.dsl.schema.component import ComponentConfig
|
|
4
4
|
from mindor.core.component import ComponentEngine
|
|
5
5
|
from .context import WorkflowContext
|
|
6
|
-
import ulid
|
|
6
|
+
import asyncio, ulid
|
|
7
7
|
|
|
8
8
|
class Job:
|
|
9
9
|
def __init__(self, id: str, config: JobConfig, component_provider: Callable[[str, Union[ComponentConfig, str]], ComponentEngine]):
|
|
@@ -20,16 +20,18 @@ class Job:
|
|
|
20
20
|
input = (await context.render_template(self.config.input)) if self.config.input else context.input
|
|
21
21
|
outputs = []
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
async def _run_once():
|
|
24
24
|
call_id = ulid.ulid()
|
|
25
25
|
output = await component.run(self.config.action, call_id, input)
|
|
26
|
-
|
|
27
|
-
if output:
|
|
28
|
-
outputs.append(output)
|
|
26
|
+
context.register_source("output", output)
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
output = (await context.render_template(self.config.output)) if self.config.output else output
|
|
29
|
+
outputs.append(output)
|
|
31
30
|
|
|
32
|
-
if
|
|
33
|
-
|
|
31
|
+
repeat_count = (await context.render_template(self.config.repeat_count)) if self.config.repeat_count else None
|
|
32
|
+
await asyncio.gather(*[ _run_once() for _ in range(int(repeat_count or 1)) ])
|
|
33
|
+
|
|
34
|
+
output = outputs[0] if len(outputs) == 1 else outputs or None
|
|
35
|
+
context.register_source("output", output)
|
|
34
36
|
|
|
35
|
-
return
|
|
37
|
+
return output
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
|
-
from pydantic import model_validator
|
|
4
|
+
from pydantic import model_validator, field_validator
|
|
5
5
|
from .component import ComponentConfig
|
|
6
6
|
|
|
7
7
|
class JobConfig(BaseModel):
|
|
8
8
|
component: Optional[Union[str, 'ComponentConfig']] = Field(default="__default__", description="The component to execute. Can be a string identifier or a ComponentConfig object.")
|
|
9
9
|
action: Optional[str] = Field(default="__default__", description="The action to invoke within the component. Defaults to '__default__'.")
|
|
10
|
-
repeats: Optional[int] = Field(default=1, ge=1, description="Number of times to repeat the component execution. Must be at least 1.")
|
|
11
10
|
input: Optional[Any] = Field(default=None, description="The input data passed to the component. Can be of any type.")
|
|
12
11
|
output: Optional[Any] = Field(default=None, description="The expected output data from the component. Can be of any type.")
|
|
12
|
+
repeat_count: Optional[Union[int, str]] = Field(default=1, description="Number of times to repeat the component execution. Must be at least 1.")
|
|
13
13
|
depends_on: Optional[List[str]] = Field(default_factory=list, description="List of job names that this job depends on. Ensures execution order.")
|
|
14
14
|
|
|
15
|
+
@field_validator("repeat_count")
|
|
16
|
+
def validate_repeat_count(cls, value):
|
|
17
|
+
if isinstance(value, int) and value < 1:
|
|
18
|
+
raise ValueError("'repeat_count' must be at least 1")
|
|
19
|
+
return value
|
|
20
|
+
|
|
15
21
|
class WorkflowVariableType(str, Enum):
|
|
16
22
|
# Primitive data types
|
|
17
23
|
STRING = "string"
|
|
@@ -44,6 +50,11 @@ class WorkflowVariableConfig(BaseModel):
|
|
|
44
50
|
description: Optional[str] = Field(default=None, description="Description of the variable")
|
|
45
51
|
options: Optional[List[Any]] = Field(default=None, description="List of valid options for select type")
|
|
46
52
|
|
|
53
|
+
class WorkflowVariableGroupConfig(BaseModel):
|
|
54
|
+
name: Optional[str] = Field(default=None, description="The name of the group of variables")
|
|
55
|
+
variables: List[WorkflowVariableConfig] = Field(default_factory=list, description="List of variables included in this group")
|
|
56
|
+
repeat_count: int = Field(default=1, description="The number of times this group of variables should be repeated")
|
|
57
|
+
|
|
47
58
|
class WorkflowConfig(BaseModel):
|
|
48
59
|
name: Optional[str] = None
|
|
49
60
|
title: Optional[str] = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/engine/http_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/core/controller/runner/http_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/action/impl/http_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/mcp_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/component/impl/mcp_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/controller/impl/mcp_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/gateway/impl/http_tunnel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.1 → model_compose-0.1.2}/src/mindor/dsl/schema/listener/impl/http_callback.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|