model-compose 0.0.17__tar.gz → 0.1.0__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.0.17 → model_compose-0.1.0}/PKG-INFO +1 -1
- {model_compose-0.0.17 → model_compose-0.1.0}/pyproject.toml +1 -1
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/engine/context.py +3 -3
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/engine/http_client.py +21 -21
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/engine/http_server.py +1 -1
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/webui/gradio.py +21 -7
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/webui/schema.py +21 -21
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/listener/engine/http_callback.py +16 -16
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/utils/http_client.py +3 -4
- model_compose-0.1.0/src/mindor/core/utils/image.py +14 -0
- model_compose-0.1.0/src/mindor/core/utils/streaming.py +63 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/utils/template.py +28 -22
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/workflow/context.py +3 -3
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/workflow/job.py +2 -2
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/workflow.py +26 -2
- {model_compose-0.0.17 → model_compose-0.1.0}/src/model_compose.egg-info/PKG-INFO +1 -1
- model_compose-0.0.17/src/mindor/core/utils/image.py +0 -9
- model_compose-0.0.17/src/mindor/core/utils/streaming.py +0 -28
- {model_compose-0.0.17 → model_compose-0.1.0}/LICENSE +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/README.md +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/setup.cfg +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/cli/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/cli/compose.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/component.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/engine/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/engine/base.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/engine/http_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/compose/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/compose/compose.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/compose/manager.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/controller.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/engine/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/engine/base.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/engine/mcp_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/runner/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/runner/client.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/runner/http_client.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/runner/mcp_client.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/runner/runner.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/webui/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/webui/webui.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/gateway/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/gateway/engine/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/gateway/engine/base.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/gateway/engine/http_tunnel.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/gateway/gateway.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/listener/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/listener/engine/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/listener/engine/base.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/listener/listener.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/runtime/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/runtime/env.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/services/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/services/async_service.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/utils/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/utils/expiring.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/utils/http_request.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/utils/time.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/utils/workqueue.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/workflow/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/workflow/workflow.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/loader.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/action.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/common.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/http_client.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/http_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/mcp_client.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/mcp_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/component.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/common.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/http_client.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/http_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/mcp_client.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/mcp_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/types.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/compose.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/controller.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/common.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/http_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/mcp_server.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/webui.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/gateway/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/gateway/gateway.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/gateway/impl/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/gateway/impl/common.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/gateway/impl/http_tunnel.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/gateway/impl/types.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/listener/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/listener/impl/common.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/listener/impl/http_callback.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/listener/listener.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/runtime/impl/common.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/runtime/impl/docker.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/runtime/runtime.py +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/model_compose.egg-info/SOURCES.txt +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/model_compose.egg-info/dependency_links.txt +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/model_compose.egg-info/entry_points.txt +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/model_compose.egg-info/requires.txt +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/src/model_compose.egg-info/top_level.txt +0 -0
- {model_compose-0.0.17 → model_compose-0.1.0}/tests/test_cli.py +0 -0
|
@@ -14,10 +14,10 @@ class ComponentContext:
|
|
|
14
14
|
def register_source(self, key: str, source: Any) -> None:
|
|
15
15
|
self.sources[key] = source
|
|
16
16
|
|
|
17
|
-
def render_template(self, data: Dict[str, Any]) -> Any:
|
|
18
|
-
return self.renderer.render(data)
|
|
17
|
+
async def render_template(self, data: Dict[str, Any]) -> Any:
|
|
18
|
+
return await self.renderer.render(data)
|
|
19
19
|
|
|
20
|
-
def _resolve_source(self, key: str) -> Any:
|
|
20
|
+
async def _resolve_source(self, key: str) -> Any:
|
|
21
21
|
if key in self.sources:
|
|
22
22
|
return self.sources[key]
|
|
23
23
|
if key == "input":
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/component/engine/http_client.py
RENAMED
|
@@ -16,11 +16,11 @@ class HttpClientPollingCompletion:
|
|
|
16
16
|
self.client: HttpClient = HttpClient()
|
|
17
17
|
|
|
18
18
|
async def run(self, context: ComponentContext) -> Any:
|
|
19
|
-
url = self._resolve_request_url(context)
|
|
20
|
-
method = context.render_template(self.config.method)
|
|
21
|
-
params = context.render_template(self.config.params)
|
|
22
|
-
body = context.render_template(self.config.body)
|
|
23
|
-
headers = context.render_template(self.config.headers)
|
|
19
|
+
url = await self._resolve_request_url(context)
|
|
20
|
+
method = await context.render_template(self.config.method)
|
|
21
|
+
params = await context.render_template(self.config.params)
|
|
22
|
+
body = await context.render_template(self.config.body)
|
|
23
|
+
headers = await context.render_template(self.config.headers)
|
|
24
24
|
|
|
25
25
|
interval = parse_duration(self.config.interval) if self.config.interval else 5.0
|
|
26
26
|
timeout = parse_duration(self.config.timeout) if self.config.timeout else 300
|
|
@@ -32,35 +32,35 @@ class HttpClientPollingCompletion:
|
|
|
32
32
|
response = await self.client.request(url, method, params, body, headers)
|
|
33
33
|
|
|
34
34
|
context.register_source("result", response)
|
|
35
|
-
status = context.render_template(self.config.status) if self.config.status else response["status"]
|
|
35
|
+
status = (await context.render_template(self.config.status)) if self.config.status else response["status"]
|
|
36
36
|
|
|
37
37
|
if not status:
|
|
38
38
|
raise RuntimeError(f"Polling failed: no status found in response.")
|
|
39
39
|
|
|
40
40
|
for success_when in self.config.success_when if self.config.success_when else [ "completed" ]:
|
|
41
|
-
if status == context.render_template(success_when):
|
|
41
|
+
if status == await context.render_template(success_when):
|
|
42
42
|
return response
|
|
43
43
|
|
|
44
44
|
for fail_when in self.config.fail_when if self.config.fail_when else [ "failed" ]:
|
|
45
|
-
if status == context.render_template(fail_when):
|
|
45
|
+
if status == await context.render_template(fail_when):
|
|
46
46
|
raise RuntimeError(f"Polling failed: status '{status}' matched a failure condition.")
|
|
47
47
|
|
|
48
48
|
await asyncio.sleep(interval.total_seconds())
|
|
49
49
|
|
|
50
50
|
raise TimeoutError(f"Polling timed out after {timeout}.")
|
|
51
51
|
|
|
52
|
-
def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
52
|
+
async def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
53
53
|
if self.base_url and self.config.path:
|
|
54
|
-
return context.render_template(self.base_url) + context.render_template(self.config.path)
|
|
54
|
+
return await context.render_template(self.base_url) + await context.render_template(self.config.path)
|
|
55
55
|
|
|
56
|
-
return context.render_template(self.config.endpoint)
|
|
56
|
+
return await context.render_template(self.config.endpoint)
|
|
57
57
|
|
|
58
58
|
class HttpClientCallbackCompletion:
|
|
59
59
|
def __init__(self, config: HttpClientCompletionConfig):
|
|
60
60
|
self.config: HttpClientCompletionConfig = config
|
|
61
61
|
|
|
62
62
|
async def run(self, context: ComponentContext) -> Any:
|
|
63
|
-
callback_id = context.render_template(self.config.wait_for)
|
|
63
|
+
callback_id = await context.render_template(self.config.wait_for)
|
|
64
64
|
future: asyncio.Future = asyncio.get_event_loop().create_future()
|
|
65
65
|
HttpCallbackListener.register_pending_future(callback_id, future)
|
|
66
66
|
|
|
@@ -73,11 +73,11 @@ class HttpClientAction:
|
|
|
73
73
|
self.client: HttpClient = HttpClient()
|
|
74
74
|
|
|
75
75
|
async def run(self, context: ComponentContext) -> Any:
|
|
76
|
-
url = self._resolve_request_url(context)
|
|
77
|
-
method = context.render_template(self.config.method)
|
|
78
|
-
params = context.render_template(self.config.params)
|
|
79
|
-
body = context.render_template(self.config.body)
|
|
80
|
-
headers = context.render_template(self.config.headers)
|
|
76
|
+
url = await self._resolve_request_url(context)
|
|
77
|
+
method = await context.render_template(self.config.method)
|
|
78
|
+
params = await context.render_template(self.config.params)
|
|
79
|
+
body = await context.render_template(self.config.body)
|
|
80
|
+
headers = await context.render_template(self.config.headers)
|
|
81
81
|
|
|
82
82
|
response, result = await self.client.request(url, method, params, body, headers), None
|
|
83
83
|
|
|
@@ -90,13 +90,13 @@ class HttpClientAction:
|
|
|
90
90
|
if result:
|
|
91
91
|
context.register_source("result", result)
|
|
92
92
|
|
|
93
|
-
return context.render_template(self.config.output) if self.config.output else (result or response)
|
|
93
|
+
return (await context.render_template(self.config.output)) if self.config.output else (result or response)
|
|
94
94
|
|
|
95
|
-
def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
95
|
+
async def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
96
96
|
if self.base_url and self.config.path:
|
|
97
|
-
return context.render_template(self.base_url) + context.render_template(self.config.path)
|
|
97
|
+
return await context.render_template(self.base_url) + await context.render_template(self.config.path)
|
|
98
98
|
|
|
99
|
-
return context.render_template(self.config.endpoint)
|
|
99
|
+
return await context.render_template(self.config.endpoint)
|
|
100
100
|
|
|
101
101
|
async def _handle_completion(self, completion: HttpClientCompletionConfig, context: ComponentContext) -> Any:
|
|
102
102
|
if completion.type == "polling":
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/engine/http_server.py
RENAMED
|
@@ -148,7 +148,7 @@ class HttpServerController(ControllerEngine):
|
|
|
148
148
|
resource,
|
|
149
149
|
media_type=resource.content_type,
|
|
150
150
|
headers=self._build_stream_resource_headers(resource),
|
|
151
|
-
background=BackgroundTask(_close_stream
|
|
151
|
+
background=BackgroundTask(_close_stream)
|
|
152
152
|
)
|
|
153
153
|
|
|
154
154
|
def _build_stream_resource_headers(self, resource: StreamResource) -> Dict[str, str]:
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable, Awaitable, Any
|
|
2
2
|
from mindor.dsl.schema.workflow import WorkflowVariableConfig
|
|
3
|
-
from mindor.core.utils.
|
|
3
|
+
from mindor.core.utils.http_client import HttpStreamResource
|
|
4
|
+
from mindor.core.utils.image import load_image_from_stream
|
|
5
|
+
from mindor.core.utils.streaming import Base64StreamResource, save_stream_to_temporary_file
|
|
4
6
|
from .schema import WorkflowSchema
|
|
5
7
|
import gradio as gr
|
|
6
8
|
|
|
@@ -42,11 +44,11 @@ class GradioWebUIBuilder:
|
|
|
42
44
|
|
|
43
45
|
if workflow.output:
|
|
44
46
|
if isinstance(output, dict):
|
|
45
|
-
output = [ self._convert_type(output[variable.name], variable.type, variable.format) for variable in workflow.output ]
|
|
47
|
+
output = [ await self._convert_type(output[variable.name], variable.type, variable.subtype, variable.format) for variable in workflow.output ]
|
|
46
48
|
output = output[0] if len(output) == 1 else output
|
|
47
49
|
else:
|
|
48
50
|
variable = workflow.output[0]
|
|
49
|
-
output = self._convert_type(output, variable.type, variable.format)
|
|
51
|
+
output = await self._convert_type(output, variable.type, variable.subtype, variable.format)
|
|
50
52
|
|
|
51
53
|
return output
|
|
52
54
|
|
|
@@ -84,21 +86,33 @@ class GradioWebUIBuilder:
|
|
|
84
86
|
return gr.Textbox(label=label, value=default, info=f"Unsupported type: {variable.type}")
|
|
85
87
|
|
|
86
88
|
def _build_output_component(self, variable: WorkflowVariableConfig) -> gr.Component:
|
|
87
|
-
label = variable.name
|
|
89
|
+
label = variable.name or ""
|
|
88
90
|
info = variable.description or ""
|
|
89
|
-
|
|
91
|
+
|
|
90
92
|
if variable.type == "string":
|
|
91
93
|
return gr.Textbox(label=label, interactive=False, show_copy_button=True, info=info)
|
|
92
94
|
|
|
93
95
|
if variable.type == "image":
|
|
94
96
|
return gr.Image(label=label, interactive=False)
|
|
95
97
|
|
|
98
|
+
if variable.type == "audio":
|
|
99
|
+
return gr.Audio(label=label)
|
|
100
|
+
|
|
96
101
|
return gr.Textbox(label=label, info=f"Unsupported type: {variable.type}")
|
|
97
102
|
|
|
98
|
-
def _convert_type(self, value: Any, type: str, format: Optional[str]) -> Any:
|
|
103
|
+
async def _convert_type(self, value: Any, type: Optional[str], subtype: Optional[str], format: Optional[str]) -> Any:
|
|
99
104
|
if type == "image":
|
|
100
105
|
if format == "base64" and isinstance(value, str):
|
|
101
|
-
return
|
|
106
|
+
return await load_image_from_stream(Base64StreamResource(value), subtype)
|
|
107
|
+
if isinstance(value, HttpStreamResource):
|
|
108
|
+
return await load_image_from_stream(value, subtype)
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
if type == "audio":
|
|
112
|
+
if format == "base64" and isinstance(value, str):
|
|
113
|
+
return await save_stream_to_temporary_file(Base64StreamResource(value), subtype)
|
|
114
|
+
if isinstance(value, HttpStreamResource):
|
|
115
|
+
return await save_stream_to_temporary_file(value, subtype)
|
|
102
116
|
return None
|
|
103
117
|
|
|
104
118
|
return value
|
|
@@ -8,28 +8,28 @@ class WorkflowVariableResolver:
|
|
|
8
8
|
def __init__(self):
|
|
9
9
|
self.patterns: Dict[str, re.Pattern] = {
|
|
10
10
|
"variable": re.compile(
|
|
11
|
-
r"""\$\{
|
|
12
|
-
([a-zA-Z_][^.\s]*)
|
|
13
|
-
(?:\.([^\s\|\}]+))?
|
|
14
|
-
(?:\s*as\s*([^\s\|\}
|
|
15
|
-
(?:\s*\|\s*([^\}]+))?
|
|
16
|
-
\}""",
|
|
11
|
+
r"""\$\{ # ${
|
|
12
|
+
([a-zA-Z_][^.\s]*) # key: input, env, etc.
|
|
13
|
+
(?:\.([^\s\|\}]+))? # path: key, key.path[0], etc.
|
|
14
|
+
(?:\s*as\s*([^\s\|\}/;]+)(?:/([^\s\|\};]+))?(?:;([^\s\|\}]+))?)? # type/subtype;format
|
|
15
|
+
(?:\s*\|\s*([^\}]+))? # default value after `|`
|
|
16
|
+
\}""", # }
|
|
17
17
|
re.VERBOSE,
|
|
18
18
|
)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
def _enumerate_input_variables(self, value: Any, wanted_key: str) -> List[Tuple[str, str, str, Optional[Any]]]:
|
|
22
22
|
if isinstance(value, str):
|
|
23
|
-
variables: List[Tuple[str, str, str, Optional[Any]]] = []
|
|
23
|
+
variables: List[Tuple[str, str, str, str, Optional[Any]]] = []
|
|
24
24
|
|
|
25
25
|
for match in self.patterns["variable"].finditer(value):
|
|
26
|
-
key, path, type, format, default = match.group(1, 2, 3, 4, 5)
|
|
26
|
+
key, path, type, subtype, format, default = match.group(1, 2, 3, 4, 5, 6)
|
|
27
27
|
|
|
28
28
|
if type and default:
|
|
29
|
-
default = self._convert_type(default, type, format)
|
|
29
|
+
default = self._convert_type(default, type, subtype, format)
|
|
30
30
|
|
|
31
31
|
if key == wanted_key:
|
|
32
|
-
variables.append((path, type or "string", format, default))
|
|
32
|
+
variables.append((path, type or "string", subtype, format, default))
|
|
33
33
|
|
|
34
34
|
return variables
|
|
35
35
|
|
|
@@ -45,16 +45,16 @@ class WorkflowVariableResolver:
|
|
|
45
45
|
return []
|
|
46
46
|
|
|
47
47
|
def _enumerate_output_variables(self, key: str, value: Any) -> List[Tuple[str, str, str, Optional[Any]]]:
|
|
48
|
-
variables: List[Tuple[str, str, str, Optional[Any]]] = []
|
|
48
|
+
variables: List[Tuple[str, str, str, str, Optional[Any]]] = []
|
|
49
49
|
|
|
50
50
|
if isinstance(value, str):
|
|
51
51
|
for match in self.patterns["variable"].finditer(value):
|
|
52
|
-
type, format, default = match.group(3, 4, 5)
|
|
52
|
+
type, subtype, format, default = match.group(3, 4, 5, 6)
|
|
53
53
|
|
|
54
54
|
if type and default:
|
|
55
|
-
default = self._convert_type(default, type, format)
|
|
55
|
+
default = self._convert_type(default, type, subtype, format)
|
|
56
56
|
|
|
57
|
-
variables.append((key, type or "string", format, default))
|
|
57
|
+
variables.append((key, type or "string", subtype, format, default))
|
|
58
58
|
|
|
59
59
|
return variables
|
|
60
60
|
|
|
@@ -69,18 +69,18 @@ class WorkflowVariableResolver:
|
|
|
69
69
|
|
|
70
70
|
return []
|
|
71
71
|
|
|
72
|
-
def _convert_type(self, value: Any, type: str, format: Optional[str]) -> Any:
|
|
72
|
+
def _convert_type(self, value: Any, type: str, subtype: str, format: Optional[str]) -> Any:
|
|
73
73
|
if type == "number":
|
|
74
74
|
return float(value)
|
|
75
75
|
|
|
76
76
|
if type == "integer":
|
|
77
77
|
return int(value)
|
|
78
78
|
|
|
79
|
-
if type == "json":
|
|
80
|
-
return json.loads(value)
|
|
81
|
-
|
|
82
79
|
if type == "boolean":
|
|
83
80
|
return str(value).lower() in [ "true", "1" ]
|
|
81
|
+
|
|
82
|
+
if type == "json":
|
|
83
|
+
return json.loads(value)
|
|
84
84
|
|
|
85
85
|
return value
|
|
86
86
|
|
|
@@ -100,11 +100,11 @@ class WorkflowInputResolver(WorkflowVariableResolver):
|
|
|
100
100
|
else:
|
|
101
101
|
variables.extend(self._enumerate_input_variables(job.input, "input"))
|
|
102
102
|
|
|
103
|
-
return [ WorkflowVariableConfig(name=name, type=type, format=format, default=default) for name, type, format, default in set(variables) ]
|
|
103
|
+
return [ WorkflowVariableConfig(name=name, type=type, subtype=subtype, format=format, default=default) for name, type, subtype, format, default in set(variables) ]
|
|
104
104
|
|
|
105
105
|
class WorkflowOutputResolver(WorkflowVariableResolver):
|
|
106
106
|
def resolve(self, workflow: WorkflowConfig, components: Dict[str, ComponentConfig]) -> List[WorkflowVariableConfig]:
|
|
107
|
-
variables: List[Tuple[str, str, Optional[Any]]] = []
|
|
107
|
+
variables: List[Tuple[str, str, str, Optional[Any]]] = []
|
|
108
108
|
|
|
109
109
|
for job_id, job in workflow.jobs.items():
|
|
110
110
|
if not self._is_terminal_job(workflow, job_id):
|
|
@@ -121,7 +121,7 @@ class WorkflowOutputResolver(WorkflowVariableResolver):
|
|
|
121
121
|
else:
|
|
122
122
|
variables.extend(self._enumerate_output_variables(None, job.output))
|
|
123
123
|
|
|
124
|
-
return [ WorkflowVariableConfig(name=name, type=type, format=format, default=default) for name, type, format, default in set(variables) ]
|
|
124
|
+
return [ WorkflowVariableConfig(name=name, type=type, subtype=subtype, format=format, default=default) for name, type, subtype, format, default in set(variables) ]
|
|
125
125
|
|
|
126
126
|
def _is_terminal_job(self, workflow: WorkflowConfig, job_id: str) -> bool:
|
|
127
127
|
return all(job_id not in job.depends_on for other_id, job in workflow.jobs.items() if other_id != job_id)
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/listener/engine/http_callback.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable,
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable, AsyncIterator, Any
|
|
2
2
|
|
|
3
3
|
from mindor.dsl.schema.listener import HttpCallbackListenerConfig, HttpCallbackConfig
|
|
4
4
|
from mindor.core.utils.http_request import parse_request_body, parse_options_header
|
|
@@ -21,15 +21,15 @@ class HttpCallbackContext:
|
|
|
21
21
|
self.item: Optional[str] = item
|
|
22
22
|
self.renderer: TemplateRenderer = TemplateRenderer(self._resolve_source)
|
|
23
23
|
|
|
24
|
-
def items(self) ->
|
|
25
|
-
for item in self._items():
|
|
24
|
+
async def items(self) -> AsyncIterator["HttpCallbackContext"]:
|
|
25
|
+
async for item in self._items():
|
|
26
26
|
yield HttpCallbackContext(item, self.query, False, None)
|
|
27
27
|
|
|
28
|
-
def render_template(self, template: str) -> Any:
|
|
29
|
-
return self.renderer.render(template)
|
|
28
|
+
async def render_template(self, template: str) -> Any:
|
|
29
|
+
return await self.renderer.render(template)
|
|
30
30
|
|
|
31
|
-
def _items(self) -> List[Any]:
|
|
32
|
-
item = self.renderer.render(self.item) if self.item else self.body
|
|
31
|
+
async def _items(self) -> List[Any]:
|
|
32
|
+
item = await self.renderer.render(self.item) if self.item else self.body
|
|
33
33
|
|
|
34
34
|
if self.bulk:
|
|
35
35
|
if not isinstance(item, list):
|
|
@@ -38,7 +38,7 @@ class HttpCallbackContext:
|
|
|
38
38
|
|
|
39
39
|
return [ item ]
|
|
40
40
|
|
|
41
|
-
def _resolve_source(self, key: str) -> Any:
|
|
41
|
+
async def _resolve_source(self, key: str) -> Any:
|
|
42
42
|
if key == "body" or key == "item":
|
|
43
43
|
return self.body
|
|
44
44
|
if key == "query":
|
|
@@ -70,15 +70,15 @@ class HttpCallbackListener(ListenerEngine):
|
|
|
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)
|
|
73
|
-
succeeded: bool = self._is_callback_succeeded(callback, context)
|
|
73
|
+
succeeded: bool = await self._is_callback_succeeded(callback, context)
|
|
74
74
|
|
|
75
|
-
for item in context.items():
|
|
76
|
-
callback_id = item.render_template(callback.identify_by)
|
|
75
|
+
async for item in context.items():
|
|
76
|
+
callback_id = await item.render_template(callback.identify_by)
|
|
77
77
|
future: asyncio.Future = self._get_pending_future(callback_id)
|
|
78
78
|
|
|
79
79
|
if future:
|
|
80
80
|
if succeeded:
|
|
81
|
-
future.set_result(item.render_template(callback.result) if callback.result else item.body)
|
|
81
|
+
future.set_result((await item.render_template(callback.result)) if callback.result else item.body)
|
|
82
82
|
else:
|
|
83
83
|
future.set_exception(RuntimeError(f"Task failed for '{callback_id}'"))
|
|
84
84
|
self._remove_pending_future(callback_id)
|
|
@@ -87,14 +87,14 @@ class HttpCallbackListener(ListenerEngine):
|
|
|
87
87
|
|
|
88
88
|
return handler
|
|
89
89
|
|
|
90
|
-
def _is_callback_succeeded(self, callback: HttpCallbackConfig, context: HttpCallbackContext) -> bool:
|
|
91
|
-
status = context.render_template(callback.status) if callback.status else None
|
|
90
|
+
async def _is_callback_succeeded(self, callback: HttpCallbackConfig, context: HttpCallbackContext) -> bool:
|
|
91
|
+
status = (await context.render_template(callback.status)) if callback.status else None
|
|
92
92
|
|
|
93
93
|
if status:
|
|
94
|
-
if callback.success_when and status == context.render_template(callback.success_when):
|
|
94
|
+
if callback.success_when and status == await context.render_template(callback.success_when):
|
|
95
95
|
return True
|
|
96
96
|
|
|
97
|
-
if callback.fail_when and status == context.render_template(callback.fail_when):
|
|
97
|
+
if callback.fail_when and status == await context.render_template(callback.fail_when):
|
|
98
98
|
return False
|
|
99
99
|
|
|
100
100
|
return True
|
|
@@ -6,17 +6,16 @@ from urllib.parse import urlencode
|
|
|
6
6
|
import aiohttp, json
|
|
7
7
|
|
|
8
8
|
class HttpStreamResource(StreamResource):
|
|
9
|
-
def __init__(self, session: aiohttp.ClientSession, stream: aiohttp.StreamReader, content_type: Optional[str], filename: Optional[str]):
|
|
9
|
+
def __init__(self, session: aiohttp.ClientSession, stream: aiohttp.StreamReader, content_type: Optional[str] = None, filename: Optional[str] = None):
|
|
10
10
|
super().__init__(content_type, filename)
|
|
11
11
|
|
|
12
12
|
self.session: aiohttp.ClientSession = session
|
|
13
13
|
self.stream: aiohttp.StreamReader = stream
|
|
14
14
|
|
|
15
|
-
def get_stream(self):
|
|
16
|
-
return self.stream
|
|
17
|
-
|
|
18
15
|
async def close(self):
|
|
19
16
|
await self.session.close()
|
|
17
|
+
self.session = None
|
|
18
|
+
self.stream = None
|
|
20
19
|
|
|
21
20
|
async def _iterate_stream(self) -> AsyncIterator[bytes]:
|
|
22
21
|
_, buffer_size = self.stream.get_read_buffer_limits()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from .streaming import StreamResource
|
|
3
|
+
from PIL import Image as PILImage
|
|
4
|
+
import io, base64
|
|
5
|
+
|
|
6
|
+
async def load_image_from_stream(stream: StreamResource, extension: Optional[str]) -> Optional[PILImage.Image]:
|
|
7
|
+
try:
|
|
8
|
+
data = bytearray()
|
|
9
|
+
async with stream:
|
|
10
|
+
async for chunk in stream:
|
|
11
|
+
data.extend(chunk)
|
|
12
|
+
return PILImage.open(io.BytesIO(data))
|
|
13
|
+
except Exception:
|
|
14
|
+
return None
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Optional, AsyncIterator
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from tempfile import NamedTemporaryFile
|
|
4
|
+
import io, base64
|
|
5
|
+
|
|
6
|
+
class StreamResource(ABC):
|
|
7
|
+
def __init__(self, content_type: Optional[str], filename: Optional[str]):
|
|
8
|
+
self.content_type = content_type or "application/octet-stream"
|
|
9
|
+
self.filename = filename
|
|
10
|
+
|
|
11
|
+
async def __aenter__(self):
|
|
12
|
+
return self
|
|
13
|
+
|
|
14
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
15
|
+
await self.close()
|
|
16
|
+
|
|
17
|
+
def __aiter__(self):
|
|
18
|
+
return self._iterate_stream()
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
async def close(self):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
async def _iterate_stream(self) -> AsyncIterator[bytes]:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
class Base64StreamResource(StreamResource):
|
|
29
|
+
def __init__(self, encoded: str, content_type: Optional[str] = None, filename: Optional[str] = None):
|
|
30
|
+
super().__init__(content_type, filename)
|
|
31
|
+
|
|
32
|
+
self.stream: Optional[io.BytesIO] = io.BytesIO(base64.b64decode(encoded))
|
|
33
|
+
|
|
34
|
+
async def close(self):
|
|
35
|
+
self.stream.close()
|
|
36
|
+
self.stream = None
|
|
37
|
+
|
|
38
|
+
async def _iterate_stream(self) -> AsyncIterator[bytes]:
|
|
39
|
+
while True:
|
|
40
|
+
chunk = self.stream.read(8192)
|
|
41
|
+
if not chunk:
|
|
42
|
+
break
|
|
43
|
+
yield chunk
|
|
44
|
+
|
|
45
|
+
async def save_stream_to_temporary_file(stream: StreamResource, extension: Optional[str]) -> Optional[str]:
|
|
46
|
+
try:
|
|
47
|
+
file = NamedTemporaryFile(suffix=f".{extension}" if extension else None, delete=False)
|
|
48
|
+
async with stream:
|
|
49
|
+
async for chunk in stream:
|
|
50
|
+
file.write(chunk)
|
|
51
|
+
file.flush()
|
|
52
|
+
file.close()
|
|
53
|
+
return file.name
|
|
54
|
+
except Exception:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
async def encode_stream_to_base64(stream: StreamResource) -> str:
|
|
58
|
+
buffer = io.BytesIO()
|
|
59
|
+
async with stream:
|
|
60
|
+
async for chunk in stream:
|
|
61
|
+
buffer.write(chunk)
|
|
62
|
+
buffer.seek(0)
|
|
63
|
+
return base64.b64encode(buffer.read()).decode("utf-8")
|
|
@@ -1,54 +1,55 @@
|
|
|
1
|
-
from typing import Callable, Dict, List, Optional, Any
|
|
2
|
-
import
|
|
1
|
+
from typing import Callable, Dict, List, Optional, Awaitable, Any
|
|
2
|
+
from .streaming import StreamResource, encode_stream_to_base64
|
|
3
|
+
import re, json, base64
|
|
3
4
|
|
|
4
5
|
class TemplateRenderer:
|
|
5
|
-
def __init__(self, source_resolver: Callable[[str], Any]):
|
|
6
|
-
self.source_resolver: Callable[[str], Any] = source_resolver
|
|
6
|
+
def __init__(self, source_resolver: Callable[[str], Awaitable[Any]]):
|
|
7
|
+
self.source_resolver: Callable[[str], Awaitable[Any]] = source_resolver
|
|
7
8
|
self.patterns: Dict[str, re.Pattern] = {
|
|
8
9
|
"variable": re.compile(
|
|
9
|
-
r"""\$\{
|
|
10
|
-
([a-zA-Z_][^.\s]*)
|
|
11
|
-
(?:\.([^\s\|\}]+))?
|
|
12
|
-
(?:\s*as\s*([^\s\|\}
|
|
13
|
-
(?:\s*\|\s*([^\}]+))?
|
|
14
|
-
\}""",
|
|
10
|
+
r"""\$\{ # ${
|
|
11
|
+
([a-zA-Z_][^.\s]*) # key: input, env, etc.
|
|
12
|
+
(?:\.([^\s\|\}]+))? # path: key, key.path[0], etc.
|
|
13
|
+
(?:\s*as\s*([^\s\|\}/;]+)(?:/([^\s\|\};]+))?(?:;([^\s\|\}]+))?)? # type/subtype;format
|
|
14
|
+
(?:\s*\|\s*([^\}]+))? # default value after `|`
|
|
15
|
+
\}""", # }
|
|
15
16
|
re.VERBOSE,
|
|
16
17
|
),
|
|
17
18
|
"keypath": re.compile(r"[-_\w]+|\[\d+\]"),
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
def render(self, data: Any) -> Any:
|
|
21
|
-
return self._render_element(data)
|
|
21
|
+
async def render(self, data: Any) -> Any:
|
|
22
|
+
return await self._render_element(data)
|
|
22
23
|
|
|
23
|
-
def _render_element(self, element: Any) -> Any:
|
|
24
|
+
async def _render_element(self, element: Any) -> Any:
|
|
24
25
|
if isinstance(element, str):
|
|
25
|
-
return self._render_text(element)
|
|
26
|
+
return await self._render_text(element)
|
|
26
27
|
|
|
27
28
|
if isinstance(element, dict):
|
|
28
|
-
return { key: self._render_element(value) for key, value in element.items() }
|
|
29
|
+
return { key: await self._render_element(value) for key, value in element.items() }
|
|
29
30
|
|
|
30
31
|
if isinstance(element, list):
|
|
31
|
-
return [ self._render_element(item) for item in element ]
|
|
32
|
+
return [ await self._render_element(item) for item in element ]
|
|
32
33
|
|
|
33
34
|
return element
|
|
34
35
|
|
|
35
|
-
def _render_text(self, text: str) -> Any:
|
|
36
|
+
async def _render_text(self, text: str) -> Any:
|
|
36
37
|
while True:
|
|
37
38
|
match = self.patterns["variable"].search(text)
|
|
38
39
|
|
|
39
40
|
if not match:
|
|
40
41
|
break
|
|
41
42
|
|
|
42
|
-
key, path, type, format, default, variable = (*match.group(1, 2, 3, 4, 5), match.group(0))
|
|
43
|
+
key, path, type, subtype, format, default, variable = (*match.group(1, 2, 3, 4, 5, 6), match.group(0))
|
|
43
44
|
try:
|
|
44
|
-
value = self._resolve_by_path(self.source_resolver(key), path)
|
|
45
|
+
value = self._resolve_by_path(await self.source_resolver(key), path)
|
|
45
46
|
except Exception:
|
|
46
47
|
value = None
|
|
47
48
|
|
|
48
49
|
value = default if not value else value
|
|
49
50
|
|
|
50
|
-
if
|
|
51
|
-
value = self._convert_type(value, type, format)
|
|
51
|
+
if type and value:
|
|
52
|
+
value = await self._convert_type(value, type, subtype, format)
|
|
52
53
|
|
|
53
54
|
if variable == text:
|
|
54
55
|
return value
|
|
@@ -78,7 +79,7 @@ class TemplateRenderer:
|
|
|
78
79
|
|
|
79
80
|
return current
|
|
80
81
|
|
|
81
|
-
def _convert_type(self, value: Any, type: str, format: Optional[str]) -> Any:
|
|
82
|
+
async def _convert_type(self, value: Any, type: str, subtype: str, format: Optional[str]) -> Any:
|
|
82
83
|
if type == "number":
|
|
83
84
|
return float(value)
|
|
84
85
|
|
|
@@ -91,4 +92,9 @@ class TemplateRenderer:
|
|
|
91
92
|
if type == "boolean":
|
|
92
93
|
return str(value).lower() in [ "true", "1" ]
|
|
93
94
|
|
|
95
|
+
if type == "base64":
|
|
96
|
+
if isinstance(value, StreamResource):
|
|
97
|
+
return await encode_stream_to_base64(value)
|
|
98
|
+
return base64.b64encode(value)
|
|
99
|
+
|
|
94
100
|
return value
|
|
@@ -16,10 +16,10 @@ class WorkflowContext:
|
|
|
16
16
|
def register_source(self, key: str, source: Any) -> None:
|
|
17
17
|
self.sources[key] = source
|
|
18
18
|
|
|
19
|
-
def render_template(self, data: Dict[str, Any]) -> Any:
|
|
20
|
-
return self.renderer.render(data)
|
|
19
|
+
async def render_template(self, data: Dict[str, Any]) -> Any:
|
|
20
|
+
return await self.renderer.render(data)
|
|
21
21
|
|
|
22
|
-
def _resolve_source(self, key: str) -> Any:
|
|
22
|
+
async def _resolve_source(self, key: str) -> Any:
|
|
23
23
|
if key in self.sources:
|
|
24
24
|
return self.sources[key]
|
|
25
25
|
if key == "input":
|
|
@@ -18,10 +18,10 @@ class Job:
|
|
|
18
18
|
await component.start()
|
|
19
19
|
|
|
20
20
|
call_id = ulid.ulid()
|
|
21
|
-
input = context.render_template(self.config.input) if self.config.input else context.input
|
|
21
|
+
input = (await context.render_template(self.config.input)) if self.config.input else context.input
|
|
22
22
|
output = await component.run(self.config.action, call_id, input)
|
|
23
23
|
|
|
24
24
|
if output:
|
|
25
25
|
context.register_source("output", output)
|
|
26
26
|
|
|
27
|
-
return context.render_template(self.config.output) if self.config.output else output
|
|
27
|
+
return (await context.render_template(self.config.output)) if self.config.output else output
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from enum import Enum
|
|
2
3
|
from pydantic import BaseModel, Field
|
|
3
4
|
from pydantic import model_validator
|
|
4
5
|
from .component import ComponentConfig
|
|
@@ -10,10 +11,33 @@ class JobConfig(BaseModel):
|
|
|
10
11
|
output: Optional[Any] = None
|
|
11
12
|
depends_on: Optional[List[str]] = Field(default_factory=list)
|
|
12
13
|
|
|
14
|
+
class WorkflowVariableType(str, Enum):
|
|
15
|
+
# Primitive data types
|
|
16
|
+
STRING = "string"
|
|
17
|
+
TEXT = "text"
|
|
18
|
+
INTEGER = "integer"
|
|
19
|
+
NUMBER = "number"
|
|
20
|
+
BOOLEAN = "boolean"
|
|
21
|
+
JSON = "json"
|
|
22
|
+
# Encoded data
|
|
23
|
+
BASE64 = "base64"
|
|
24
|
+
# Media and files
|
|
25
|
+
IMAGE = "image"
|
|
26
|
+
AUDIO = "audio"
|
|
27
|
+
FILE = "file"
|
|
28
|
+
# UI-related types
|
|
29
|
+
SELECT = "select"
|
|
30
|
+
|
|
31
|
+
class WorkflowVariableFormat(str, Enum):
|
|
32
|
+
BASE64 = "base64"
|
|
33
|
+
URL = "url"
|
|
34
|
+
PATH = "path"
|
|
35
|
+
|
|
13
36
|
class WorkflowVariableConfig(BaseModel):
|
|
14
37
|
name: Optional[str] = Field(default=None, description="The name of the variable")
|
|
15
|
-
type:
|
|
16
|
-
|
|
38
|
+
type: WorkflowVariableType = Field(..., description="Type of the variable")
|
|
39
|
+
subtype: Optional[str] = Field(default=None, description="Subtype of the variable")
|
|
40
|
+
format: Optional[WorkflowVariableFormat] = Field(default=None, description="Format of the variable")
|
|
17
41
|
required: bool = Field(default=False, description="Whether this variable is required")
|
|
18
42
|
default: Optional[Any] = Field(default=None, description="Default value if not provided")
|
|
19
43
|
description: Optional[str] = Field(default=None, description="Description of the variable")
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
from typing import Optional, AsyncIterator
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
|
-
|
|
4
|
-
class StreamResource(ABC):
|
|
5
|
-
def __init__(self, content_type: Optional[str], filename: Optional[str]):
|
|
6
|
-
self.content_type = content_type or "application/octet-stream"
|
|
7
|
-
self.filename = filename
|
|
8
|
-
|
|
9
|
-
async def __aenter__(self):
|
|
10
|
-
return self
|
|
11
|
-
|
|
12
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
13
|
-
await self.close()
|
|
14
|
-
|
|
15
|
-
def __aiter__(self):
|
|
16
|
-
return self._iterate_stream()
|
|
17
|
-
|
|
18
|
-
@abstractmethod
|
|
19
|
-
def get_stream(self):
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
|
-
@abstractmethod
|
|
23
|
-
async def close(self):
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
@abstractmethod
|
|
27
|
-
async def _iterate_stream(self) -> AsyncIterator[bytes]:
|
|
28
|
-
pass
|
|
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.0.17 → model_compose-0.1.0}/src/mindor/core/component/engine/http_server.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
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/engine/mcp_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/runner/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/core/controller/runner/mcp_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
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/mcp_client.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/action/impl/mcp_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/component/impl/mcp_client.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/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.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/__init__.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/common.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/controller/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/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.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/gateway/impl/http_tunnel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/mindor/dsl/schema/listener/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.0.17 → model_compose-0.1.0}/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
|
{model_compose-0.0.17 → model_compose-0.1.0}/src/model_compose.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|