model-compose 0.1.2__tar.gz → 0.1.4__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.2 → model_compose-0.1.4}/PKG-INFO +1 -1
- {model_compose-0.1.2 → model_compose-0.1.4}/pyproject.toml +1 -1
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/engine/http_server.py +2 -2
- model_compose-0.1.4/src/mindor/core/controller/runner/http_client.py +68 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/webui/gradio.py +55 -13
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/webui/schema.py +16 -8
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/webui/webui.py +3 -3
- model_compose-0.1.4/src/mindor/core/utils/http_client.py +97 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/http_request.py +21 -4
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/workflow/job.py +2 -2
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/workflow.py +3 -1
- {model_compose-0.1.2 → model_compose-0.1.4}/src/model_compose.egg-info/PKG-INFO +1 -1
- model_compose-0.1.2/src/mindor/core/controller/runner/http_client.py +0 -26
- model_compose-0.1.2/src/mindor/core/utils/http_client.py +0 -75
- {model_compose-0.1.2 → model_compose-0.1.4}/LICENSE +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/README.md +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/setup.cfg +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/cli/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/cli/compose.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/component/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/component/component.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/component/engine/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/component/engine/base.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/component/engine/context.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/component/engine/http_client.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/component/engine/http_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/compose/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/compose/compose.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/compose/manager.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/controller.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/engine/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/engine/base.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/engine/mcp_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/runner/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/runner/client.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/runner/mcp_client.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/runner/runner.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/webui/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/gateway/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/gateway/engine/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/gateway/engine/base.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/gateway/engine/http_tunnel.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/gateway/gateway.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/listener/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/listener/engine/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/listener/engine/base.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/listener/engine/http_callback.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/listener/listener.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/runtime/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/runtime/env.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/services/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/services/async_service.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/expiring.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/image.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/streaming.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/template.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/time.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/utils/workqueue.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/workflow/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/workflow/context.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/workflow/workflow.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/loader.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/action.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/impl/common.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/impl/http_client.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/impl/http_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/impl/mcp_client.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/impl/mcp_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/component.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/common.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/http_client.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/http_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/mcp_client.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/mcp_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/types.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/compose.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/controller.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/common.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/http_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/mcp_server.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/webui.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/gateway/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/gateway/gateway.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/gateway/impl/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/gateway/impl/common.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/gateway/impl/http_tunnel.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/gateway/impl/types.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/listener/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/listener/impl/common.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/listener/impl/http_callback.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/listener/listener.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/runtime/impl/common.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/runtime/impl/docker.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/runtime/runtime.py +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/model_compose.egg-info/SOURCES.txt +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/model_compose.egg-info/dependency_links.txt +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/model_compose.egg-info/entry_points.txt +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/model_compose.egg-info/requires.txt +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/src/model_compose.egg-info/top_level.txt +0 -0
- {model_compose-0.1.2 → model_compose-0.1.4}/tests/test_cli.py +0 -0
{model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/core/controller/engine/http_server.py
RENAMED
|
@@ -15,7 +15,7 @@ from fastapi import FastAPI, APIRouter, Request, Body, HTTPException
|
|
|
15
15
|
from fastapi.middleware.cors import CORSMiddleware
|
|
16
16
|
from fastapi.responses import Response, JSONResponse, StreamingResponse
|
|
17
17
|
from starlette.background import BackgroundTask
|
|
18
|
-
import
|
|
18
|
+
import uvicorn
|
|
19
19
|
|
|
20
20
|
class WorkflowTaskRequestBody(BaseModel):
|
|
21
21
|
workflow_id: Optional[str] = None
|
|
@@ -50,7 +50,7 @@ class HttpServerController(ControllerEngine):
|
|
|
50
50
|
daemon: bool
|
|
51
51
|
):
|
|
52
52
|
super().__init__(config, components, listeners, gateways, workflows, env, daemon)
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
self.server: Optional[uvicorn.Server] = None
|
|
55
55
|
self.app: FastAPI = FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
|
|
56
56
|
self.router: APIRouter = APIRouter()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from mindor.dsl.schema.controller import ControllerConfig
|
|
3
|
+
from mindor.core.utils.http_client import HttpClient
|
|
4
|
+
from .runner import ControllerClient
|
|
5
|
+
from starlette.datastructures import UploadFile
|
|
6
|
+
|
|
7
|
+
class HttpControllerClient(ControllerClient):
|
|
8
|
+
def __init__(self, config: ControllerConfig):
|
|
9
|
+
super().__init__(config)
|
|
10
|
+
|
|
11
|
+
self.client: HttpClient = HttpClient()
|
|
12
|
+
|
|
13
|
+
async def run_workflow(self, workflow_id: Optional[str], input: Any) -> Any:
|
|
14
|
+
base_path = self.config.base_path if self.config.base_path else ""
|
|
15
|
+
url = f"http://localhost:{self.config.port}{base_path}/workflows"
|
|
16
|
+
method = "POST"
|
|
17
|
+
body = {
|
|
18
|
+
"workflow_id": workflow_id,
|
|
19
|
+
"input": input,
|
|
20
|
+
"wait_for_completion": True,
|
|
21
|
+
"output_only": True
|
|
22
|
+
}
|
|
23
|
+
headers = {
|
|
24
|
+
"Content-Type": "application/json"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if self._contains_upload_file(input):
|
|
28
|
+
body = { key: value for key, value in self._flatten_for_multipart(body) }
|
|
29
|
+
headers = {
|
|
30
|
+
"Content-Type": "multipart/form-data"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return await self.client.request(url, method, None, body, headers)
|
|
34
|
+
|
|
35
|
+
def _flatten_for_multipart(self, body: Dict[str, Any], key: str = "") -> List[Tuple[str, Any]]:
|
|
36
|
+
flattened = []
|
|
37
|
+
|
|
38
|
+
for subkey, value in body.items():
|
|
39
|
+
full_key = f"{key}[{subkey}]" if key else subkey
|
|
40
|
+
|
|
41
|
+
if isinstance(value, dict):
|
|
42
|
+
flattened.extend(self._flatten_for_multipart(value, full_key))
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
if isinstance(value, list):
|
|
46
|
+
for item in value:
|
|
47
|
+
list_key = f"{full_key}[]"
|
|
48
|
+
if isinstance(item, (dict, list)):
|
|
49
|
+
flattened.extend(self._flatten_for_multipart({ list_key: item }))
|
|
50
|
+
else:
|
|
51
|
+
flattened.append((list_key, item))
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
flattened.append((full_key, value))
|
|
55
|
+
|
|
56
|
+
return flattened
|
|
57
|
+
|
|
58
|
+
def _contains_upload_file(self, value: Any) -> bool:
|
|
59
|
+
if isinstance(value, UploadFile):
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
if isinstance(value, dict):
|
|
63
|
+
return any(self._contains_upload_file(v) for v in value.values())
|
|
64
|
+
|
|
65
|
+
if isinstance(value, list):
|
|
66
|
+
return any(self._contains_upload_file(v) for v in value)
|
|
67
|
+
|
|
68
|
+
return False
|
|
@@ -1,11 +1,12 @@
|
|
|
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, WorkflowVariableGroupConfig
|
|
3
|
-
from mindor.core.utils.http_client import HttpStreamResource
|
|
3
|
+
from mindor.core.utils.http_client import HttpClient, 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
|
|
6
6
|
from .schema import WorkflowSchema
|
|
7
|
+
from starlette.datastructures import UploadFile
|
|
7
8
|
import gradio as gr
|
|
8
|
-
import json
|
|
9
|
+
import json, os
|
|
9
10
|
|
|
10
11
|
class ComponentGroup:
|
|
11
12
|
def __init__(self, group: gr.Component, components: List[gr.Component]):
|
|
@@ -29,8 +30,8 @@ class GradioWebUIBuilder:
|
|
|
29
30
|
|
|
30
31
|
def _build_workflow_section(self, workflow: WorkflowSchema, runner: Callable[[Any], Awaitable[Any]]) -> gr.Column:
|
|
31
32
|
with gr.Column() as section:
|
|
32
|
-
gr.Markdown(f"## **{workflow.title or 'Untitled Workflow'}**")
|
|
33
|
-
|
|
33
|
+
gr.Markdown(f"## **{workflow.title or 'Untitled Workflow'}**")
|
|
34
|
+
|
|
34
35
|
if workflow.description:
|
|
35
36
|
gr.Markdown(f"📝 {workflow.description}")
|
|
36
37
|
|
|
@@ -45,9 +46,9 @@ class GradioWebUIBuilder:
|
|
|
45
46
|
output_components = gr.Textbox(label="", lines=8, interactive=False, show_copy_button=True)
|
|
46
47
|
|
|
47
48
|
async def _run_workflow(*args):
|
|
48
|
-
input =
|
|
49
|
+
input = self._build_input_values(args, workflow.input)
|
|
49
50
|
output = await runner(input)
|
|
50
|
-
output = await self.
|
|
51
|
+
output = await self._flatten_output_values(output, workflow.output)
|
|
51
52
|
return output[0] if len(output) == 1 else output
|
|
52
53
|
|
|
53
54
|
run_button.click(
|
|
@@ -74,14 +75,48 @@ class GradioWebUIBuilder:
|
|
|
74
75
|
|
|
75
76
|
if variable.type == "boolean":
|
|
76
77
|
return gr.Checkbox(label=label, value=default or False, info=info)
|
|
78
|
+
|
|
79
|
+
if variable.type == "image":
|
|
80
|
+
return gr.Image(label=label, type="filepath")
|
|
81
|
+
|
|
82
|
+
if variable.type == "audio":
|
|
83
|
+
return gr.Audio(label=label, type="filepath")
|
|
84
|
+
|
|
85
|
+
if variable.type == "video":
|
|
86
|
+
return gr.Video(label=label, type="filepath")
|
|
77
87
|
|
|
78
88
|
if variable.type == "file":
|
|
79
|
-
return gr.File(label=label
|
|
89
|
+
return gr.File(label=label)
|
|
80
90
|
|
|
81
91
|
if variable.type == "select":
|
|
82
92
|
return gr.Dropdown(choices=variable.options or [], label=label, value=default, info=info)
|
|
83
|
-
|
|
93
|
+
|
|
84
94
|
return gr.Textbox(label=label, value=default, info=f"Unsupported type: {variable.type}")
|
|
95
|
+
|
|
96
|
+
def _build_input_values(self, arguments: List[Any], variables: List[WorkflowVariableConfig]) -> Any:
|
|
97
|
+
if len(variables) == 1 and not variables[0].name:
|
|
98
|
+
return arguments[0]
|
|
99
|
+
|
|
100
|
+
input: Dict[str, Any] = {}
|
|
101
|
+
|
|
102
|
+
for value, variable in zip(arguments, variables):
|
|
103
|
+
input[variable.name] = self._convert_input_value(value, variable)
|
|
104
|
+
|
|
105
|
+
return input
|
|
106
|
+
|
|
107
|
+
def _convert_input_value(self, value: Any, variable: WorkflowVariableConfig) -> Any:
|
|
108
|
+
if variable.type in [ "image", "audio", "video", "file" ]:
|
|
109
|
+
file, filename = open(value, "rb"), os.path.basename(value)
|
|
110
|
+
headers = {
|
|
111
|
+
"Content-Type": self._guess_file_content_type(filename, variable),
|
|
112
|
+
"Content-Disposition": f'form-data; filename="{filename}"'
|
|
113
|
+
}
|
|
114
|
+
return UploadFile(file=file, filename=filename, headers=headers)
|
|
115
|
+
|
|
116
|
+
return value if value != "" else None
|
|
117
|
+
|
|
118
|
+
def _guess_file_content_type(self, filename: str, variable: WorkflowVariableConfig) -> str:
|
|
119
|
+
return "application/octet-stream"
|
|
85
120
|
|
|
86
121
|
def _build_output_component(self, variable: Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]) -> Union[gr.Component, List[ComponentGroup]]:
|
|
87
122
|
if isinstance(variable, WorkflowVariableGroupConfig):
|
|
@@ -98,13 +133,16 @@ class GradioWebUIBuilder:
|
|
|
98
133
|
|
|
99
134
|
if variable.type == "string":
|
|
100
135
|
return gr.Textbox(label=label, interactive=False, show_copy_button=True, info=info)
|
|
101
|
-
|
|
136
|
+
|
|
102
137
|
if variable.type == "image":
|
|
103
138
|
return gr.Image(label=label, interactive=False)
|
|
104
139
|
|
|
105
140
|
if variable.type == "audio":
|
|
106
141
|
return gr.Audio(label=label)
|
|
107
142
|
|
|
143
|
+
if variable.type == "video":
|
|
144
|
+
return gr.Video(label=label)
|
|
145
|
+
|
|
108
146
|
return gr.Textbox(label=label, info=f"Unsupported type: {variable.type}")
|
|
109
147
|
|
|
110
148
|
def _flatten_output_components(self, components: List[Union[gr.Component, List[ComponentGroup]]]) -> List[gr.Component]:
|
|
@@ -117,13 +155,13 @@ class GradioWebUIBuilder:
|
|
|
117
155
|
flattened.append(item)
|
|
118
156
|
return flattened
|
|
119
157
|
|
|
120
|
-
async def
|
|
158
|
+
async def _flatten_output_values(self, output: Any, variables: List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]]) -> Any:
|
|
121
159
|
flattened = []
|
|
122
160
|
for variable in variables:
|
|
123
161
|
if isinstance(variable, WorkflowVariableGroupConfig):
|
|
124
162
|
group = output[variable.name] if variable.name else output
|
|
125
163
|
for value in group:
|
|
126
|
-
flattened.extend(await self.
|
|
164
|
+
flattened.extend(await self._flatten_output_values(value, variable.variables))
|
|
127
165
|
else:
|
|
128
166
|
value = output[variable.name] if variable.name else output
|
|
129
167
|
flattened.append(await self._convert_type(value, variable.type, variable.subtype, variable.format))
|
|
@@ -134,15 +172,19 @@ class GradioWebUIBuilder:
|
|
|
134
172
|
if isinstance(value, (dict, list)):
|
|
135
173
|
return json.dumps(value)
|
|
136
174
|
return str(value)
|
|
137
|
-
|
|
175
|
+
|
|
138
176
|
if type == "image":
|
|
177
|
+
if format == "url" and isinstance(value, str):
|
|
178
|
+
return await load_image_from_stream(await HttpClient().request(value), subtype)
|
|
139
179
|
if format == "base64" and isinstance(value, str):
|
|
140
180
|
return await load_image_from_stream(Base64StreamResource(value), subtype)
|
|
141
181
|
if isinstance(value, HttpStreamResource):
|
|
142
182
|
return await load_image_from_stream(value, subtype)
|
|
143
183
|
return None
|
|
144
184
|
|
|
145
|
-
if type
|
|
185
|
+
if type in [ "audio", "video" ]:
|
|
186
|
+
if format == "url" and isinstance(value, str):
|
|
187
|
+
return await save_stream_to_temporary_file(await HttpClient().request(value), subtype)
|
|
146
188
|
if format == "base64" and isinstance(value, str):
|
|
147
189
|
return await save_stream_to_temporary_file(Base64StreamResource(value), subtype)
|
|
148
190
|
if isinstance(value, HttpStreamResource):
|
|
@@ -111,17 +111,25 @@ class WorkflowVariableResolver:
|
|
|
111
111
|
if isinstance(item, WorkflowVariableGroup):
|
|
112
112
|
group: List[WorkflowVariableConfig] = []
|
|
113
113
|
seen_in_group: Set[WorkflowVariable] = set()
|
|
114
|
-
for
|
|
115
|
-
if
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
for variable in item.variables:
|
|
115
|
+
if variable not in seen_in_group:
|
|
116
|
+
group.append(self._to_variable_config(variable))
|
|
117
|
+
seen_in_group.add(variable)
|
|
118
118
|
configs.append(WorkflowVariableGroupConfig(name=item.name, variables=group, repeat_count=item.repeat_count))
|
|
119
119
|
else:
|
|
120
120
|
if item not in seen_single:
|
|
121
|
+
configs.append(self._to_variable_config(item))
|
|
121
122
|
seen_single.add(item)
|
|
122
|
-
configs.append(WorkflowVariableConfig(**item.to_dict()))
|
|
123
123
|
|
|
124
124
|
return configs
|
|
125
|
+
|
|
126
|
+
def _to_variable_config(self, variable: WorkflowVariable) -> WorkflowVariableConfig:
|
|
127
|
+
config_dict = variable.to_dict()
|
|
128
|
+
|
|
129
|
+
if variable.type == "select" and variable.subtype:
|
|
130
|
+
config_dict["options"] = variable.subtype.split(",")
|
|
131
|
+
|
|
132
|
+
return WorkflowVariableConfig(**config_dict)
|
|
125
133
|
|
|
126
134
|
def _parse_as_type(self, value: Any, type: str) -> Any:
|
|
127
135
|
if type == "number":
|
|
@@ -138,7 +146,7 @@ class WorkflowVariableResolver:
|
|
|
138
146
|
|
|
139
147
|
return value
|
|
140
148
|
|
|
141
|
-
class
|
|
149
|
+
class WorkflowInputVariableResolver(WorkflowVariableResolver):
|
|
142
150
|
def resolve(self, workflow: WorkflowConfig, components: Dict[str, ComponentConfig]) -> List[WorkflowVariableConfig]:
|
|
143
151
|
variables: List[WorkflowVariable] = []
|
|
144
152
|
|
|
@@ -154,12 +162,12 @@ class WorkflowInputResolver(WorkflowVariableResolver):
|
|
|
154
162
|
else:
|
|
155
163
|
variables.extend(self._enumerate_input_variables(job.input, "input"))
|
|
156
164
|
|
|
157
|
-
for value in [ job.repeat_count ]:
|
|
165
|
+
for value in [ job.component, job.action, job.repeat_count ]:
|
|
158
166
|
variables.extend(self._enumerate_input_variables(value, "input"))
|
|
159
167
|
|
|
160
168
|
return self._to_variable_config_list(variables)
|
|
161
169
|
|
|
162
|
-
class
|
|
170
|
+
class WorkflowOutputVariableResolver(WorkflowVariableResolver):
|
|
163
171
|
def resolve(self, workflow: WorkflowConfig, components: Dict[str, ComponentConfig]) -> List[Union[WorkflowVariableConfig, WorkflowVariableGroupConfig]]:
|
|
164
172
|
variables: List[Union[WorkflowVariable, WorkflowVariableGroup]] = []
|
|
165
173
|
|
|
@@ -4,7 +4,7 @@ from mindor.dsl.schema.component import ComponentConfig
|
|
|
4
4
|
from mindor.dsl.schema.workflow import WorkflowConfig
|
|
5
5
|
from mindor.core.controller.runner import ControllerRunner
|
|
6
6
|
from mindor.core.services import AsyncService
|
|
7
|
-
from .schema import WorkflowSchema,
|
|
7
|
+
from .schema import WorkflowSchema, WorkflowInputVariableResolver, WorkflowOutputVariableResolver
|
|
8
8
|
from .gradio import GradioWebUIBuilder
|
|
9
9
|
|
|
10
10
|
from fastapi import FastAPI, APIRouter, Body, HTTPException
|
|
@@ -68,8 +68,8 @@ class ControllerWebUI(AsyncService):
|
|
|
68
68
|
name=workflow.name,
|
|
69
69
|
title=workflow.title,
|
|
70
70
|
description=workflow.description,
|
|
71
|
-
input=
|
|
72
|
-
output=
|
|
71
|
+
input=WorkflowInputVariableResolver().resolve(workflow, self.components),
|
|
72
|
+
output=WorkflowOutputVariableResolver().resolve(workflow, self.components)
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
return schema
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, AsyncIterator, Any
|
|
2
|
+
from .http_request import build_request_body, parse_options_header
|
|
3
|
+
from .streaming import StreamResource
|
|
4
|
+
from requests.structures import CaseInsensitiveDict
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
class HttpStreamResource(StreamResource):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
session: aiohttp.ClientSession,
|
|
11
|
+
stream: aiohttp.StreamReader,
|
|
12
|
+
content_type: Optional[str] = None,
|
|
13
|
+
filename: Optional[str] = None
|
|
14
|
+
):
|
|
15
|
+
super().__init__(content_type, filename)
|
|
16
|
+
|
|
17
|
+
self.session: aiohttp.ClientSession = session
|
|
18
|
+
self.stream: aiohttp.StreamReader = stream
|
|
19
|
+
|
|
20
|
+
async def close(self):
|
|
21
|
+
await self.session.close()
|
|
22
|
+
self.session = None
|
|
23
|
+
self.stream = None
|
|
24
|
+
|
|
25
|
+
async def _iterate_stream(self) -> AsyncIterator[bytes]:
|
|
26
|
+
_, buffer_size = self.stream.get_read_buffer_limits()
|
|
27
|
+
chunk_size = buffer_size or 65536
|
|
28
|
+
|
|
29
|
+
while not self.stream.at_eof():
|
|
30
|
+
chunk = await self.stream.read(chunk_size)
|
|
31
|
+
if not chunk:
|
|
32
|
+
break
|
|
33
|
+
yield chunk
|
|
34
|
+
|
|
35
|
+
class HttpClient:
|
|
36
|
+
async def request(
|
|
37
|
+
self,
|
|
38
|
+
url: str,
|
|
39
|
+
method: Optional[str] = "GET",
|
|
40
|
+
params: Optional[Dict[str, Any]] = None,
|
|
41
|
+
body: Optional[Any] = None,
|
|
42
|
+
headers: Optional[Dict[str, str]] = None
|
|
43
|
+
) -> Any:
|
|
44
|
+
session = aiohttp.ClientSession()
|
|
45
|
+
try:
|
|
46
|
+
response = await self._request_with_session(session, url, method, params, body, headers)
|
|
47
|
+
content, _ = await self._parse_response_content(session, response)
|
|
48
|
+
|
|
49
|
+
if response.status >= 400:
|
|
50
|
+
raise ValueError(f"Request failed with status {response.status}: {content}")
|
|
51
|
+
|
|
52
|
+
if not isinstance(content, HttpStreamResource):
|
|
53
|
+
await session.close()
|
|
54
|
+
|
|
55
|
+
return content
|
|
56
|
+
except:
|
|
57
|
+
await session.close()
|
|
58
|
+
raise
|
|
59
|
+
|
|
60
|
+
async def _request_with_session(
|
|
61
|
+
self,
|
|
62
|
+
session: aiohttp.ClientSession,
|
|
63
|
+
url: str,
|
|
64
|
+
method: str,
|
|
65
|
+
params: Optional[Dict[str, Any]],
|
|
66
|
+
body: Optional[Any],
|
|
67
|
+
headers: Optional[Dict[str, str]]
|
|
68
|
+
) -> aiohttp.ClientResponse:
|
|
69
|
+
data, content_type = self._build_request_body(body, headers)
|
|
70
|
+
|
|
71
|
+
if content_type == "multipart/form-data":
|
|
72
|
+
headers = CaseInsensitiveDict(headers)
|
|
73
|
+
headers.pop("Content-Type", None)
|
|
74
|
+
|
|
75
|
+
return await session.request(method, url, params=params, data=data, headers=headers)
|
|
76
|
+
|
|
77
|
+
def _build_request_body(self, body: Optional[Any], headers: Optional[Dict[str, str]]) -> Tuple[Any, str]:
|
|
78
|
+
content_type, _ = parse_options_header(headers, "Content-Type")
|
|
79
|
+
|
|
80
|
+
if content_type and body is not None:
|
|
81
|
+
return (build_request_body(body, content_type), content_type)
|
|
82
|
+
|
|
83
|
+
return (body, content_type)
|
|
84
|
+
|
|
85
|
+
async def _parse_response_content(self, session: aiohttp.ClientSession, response: aiohttp.ClientResponse) -> Tuple[Any, str]:
|
|
86
|
+
content_type, _ = parse_options_header(response.headers, "Content-Type")
|
|
87
|
+
|
|
88
|
+
if content_type == "application/json":
|
|
89
|
+
return (await response.json(), content_type)
|
|
90
|
+
|
|
91
|
+
if content_type.startswith("text/"):
|
|
92
|
+
return (await response.text(), content_type)
|
|
93
|
+
|
|
94
|
+
_, disposition = parse_options_header(response.headers, "Content-Disposition")
|
|
95
|
+
filename = disposition.get("filename")
|
|
96
|
+
|
|
97
|
+
return (HttpStreamResource(session, response.content, content_type, filename), content_type)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from typing import Dict, List, Tuple, Optional, Any
|
|
2
2
|
from requests.structures import CaseInsensitiveDict
|
|
3
|
-
from starlette.datastructures import FormData
|
|
3
|
+
from starlette.datastructures import FormData, UploadFile
|
|
4
4
|
from urllib.parse import urlencode
|
|
5
5
|
from fastapi import Request
|
|
6
|
-
import re, json
|
|
6
|
+
import aiohttp, re, json
|
|
7
7
|
|
|
8
8
|
_token_pattern = r"([\w!#$%&'*+\-.^_`|~]+)"
|
|
9
9
|
_quoted_pattern = r'"([^"]*)"'
|
|
@@ -37,12 +37,12 @@ class NestedFormParser:
|
|
|
37
37
|
|
|
38
38
|
return parts
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
def build_request_body(body: Any, content_type: Optional[str]) -> Any:
|
|
41
41
|
if content_type == "application/json":
|
|
42
42
|
return json.dumps(body)
|
|
43
43
|
|
|
44
44
|
if content_type == "multipart/form-data":
|
|
45
|
-
return
|
|
45
|
+
return build_request_form(body)
|
|
46
46
|
|
|
47
47
|
if content_type == "application/x-www-form-urlencoded":
|
|
48
48
|
return urlencode(body)
|
|
@@ -52,6 +52,22 @@ async def serialize_request_body(body: Any, content_type: Optional[str]) -> Any:
|
|
|
52
52
|
|
|
53
53
|
return body
|
|
54
54
|
|
|
55
|
+
def build_request_form(body: Any) -> aiohttp.FormData:
|
|
56
|
+
form = aiohttp.FormData()
|
|
57
|
+
|
|
58
|
+
for key, value in body.items() if isinstance(body, dict) else {}:
|
|
59
|
+
if isinstance(value, UploadFile):
|
|
60
|
+
form.add_field(
|
|
61
|
+
name=key,
|
|
62
|
+
value=value.file,
|
|
63
|
+
filename=value.filename,
|
|
64
|
+
content_type=value.content_type or "application/octet-stream"
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
form.add_field(name=key, value=str(value))
|
|
68
|
+
|
|
69
|
+
return form
|
|
70
|
+
|
|
55
71
|
async def parse_request_body(request: Request, content_type: str, nested: bool = False) -> Any:
|
|
56
72
|
if content_type in [ "multipart/form-data", "application/x-www-form-urlencoded" ]:
|
|
57
73
|
return await parse_request_form(request, nested)
|
|
@@ -75,6 +91,7 @@ async def parse_request_form(request: Request, nested: bool = False) -> Any:
|
|
|
75
91
|
def parse_options_header(headers: Dict[str, str], header_name: str) -> Tuple[str, Dict[str, str]]:
|
|
76
92
|
value = CaseInsensitiveDict(headers or {}).get(header_name, "")
|
|
77
93
|
pos = _firefox_escape_fix_regex.sub("%22", value).find(";")
|
|
94
|
+
|
|
78
95
|
if pos >= 0:
|
|
79
96
|
options = {
|
|
80
97
|
m.group(1).lower(): m.group(2) or m.group(3).replace("%22", "") for m in _option_regex.finditer(value[pos:])
|
|
@@ -12,7 +12,7 @@ class Job:
|
|
|
12
12
|
self.component_provider: Callable[[str, Union[ComponentConfig, str]], ComponentEngine] = component_provider
|
|
13
13
|
|
|
14
14
|
async def run(self, context: WorkflowContext) -> Any:
|
|
15
|
-
component: ComponentEngine = self.component_provider(self.id, self.config.component)
|
|
15
|
+
component: ComponentEngine = self.component_provider(self.id, await context.render_template(self.config.component))
|
|
16
16
|
|
|
17
17
|
if not component.started:
|
|
18
18
|
await component.start()
|
|
@@ -22,7 +22,7 @@ class Job:
|
|
|
22
22
|
|
|
23
23
|
async def _run_once():
|
|
24
24
|
call_id = ulid.ulid()
|
|
25
|
-
output = await component.run(self.config.action, call_id, input)
|
|
25
|
+
output = await component.run(await context.render_template(self.config.action), call_id, input)
|
|
26
26
|
context.register_source("output", output)
|
|
27
27
|
|
|
28
28
|
output = (await context.render_template(self.config.output)) if self.config.output else output
|
|
@@ -31,6 +31,7 @@ class WorkflowVariableType(str, Enum):
|
|
|
31
31
|
# Media and files
|
|
32
32
|
IMAGE = "image"
|
|
33
33
|
AUDIO = "audio"
|
|
34
|
+
VIDEO = "video"
|
|
34
35
|
FILE = "file"
|
|
35
36
|
# UI-related types
|
|
36
37
|
SELECT = "select"
|
|
@@ -39,16 +40,17 @@ class WorkflowVariableFormat(str, Enum):
|
|
|
39
40
|
BASE64 = "base64"
|
|
40
41
|
URL = "url"
|
|
41
42
|
PATH = "path"
|
|
43
|
+
STREAM = "stream"
|
|
42
44
|
|
|
43
45
|
class WorkflowVariableConfig(BaseModel):
|
|
44
46
|
name: Optional[str] = Field(default=None, description="The name of the variable")
|
|
45
47
|
type: WorkflowVariableType = Field(..., description="Type of the variable")
|
|
46
48
|
subtype: Optional[str] = Field(default=None, description="Subtype of the variable")
|
|
47
49
|
format: Optional[WorkflowVariableFormat] = Field(default=None, description="Format of the variable")
|
|
50
|
+
options: Optional[List[str]] = Field(default=None, description="List of valid options for select type")
|
|
48
51
|
required: bool = Field(default=False, description="Whether this variable is required")
|
|
49
52
|
default: Optional[Any] = Field(default=None, description="Default value if not provided")
|
|
50
53
|
description: Optional[str] = Field(default=None, description="Description of the variable")
|
|
51
|
-
options: Optional[List[Any]] = Field(default=None, description="List of valid options for select type")
|
|
52
54
|
|
|
53
55
|
class WorkflowVariableGroupConfig(BaseModel):
|
|
54
56
|
name: Optional[str] = Field(default=None, description="The name of the group of variables")
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
-
from mindor.dsl.schema.controller import ControllerConfig
|
|
3
|
-
from mindor.core.utils.http_client import HttpClient
|
|
4
|
-
from .runner import ControllerClient
|
|
5
|
-
|
|
6
|
-
class HttpControllerClient(ControllerClient):
|
|
7
|
-
def __init__(self, config: ControllerConfig):
|
|
8
|
-
super().__init__(config)
|
|
9
|
-
|
|
10
|
-
self.client: HttpClient = HttpClient()
|
|
11
|
-
|
|
12
|
-
async def run_workflow(self, workflow_id: Optional[str], input: Any) -> Any:
|
|
13
|
-
base_path = self.config.base_path if self.config.base_path else ""
|
|
14
|
-
url = f"http://localhost:{self.config.port}{base_path}/workflows"
|
|
15
|
-
method = "POST"
|
|
16
|
-
body = {
|
|
17
|
-
"workflow_id": workflow_id,
|
|
18
|
-
"input": input,
|
|
19
|
-
"wait_for_completion": True,
|
|
20
|
-
"output_only": True
|
|
21
|
-
}
|
|
22
|
-
headers = {
|
|
23
|
-
"Content-Type": "application/json"
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return await self.client.request(url, method, None, body, headers)
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, AsyncIterator, Any
|
|
2
|
-
from .http_request import serialize_request_body, parse_options_header
|
|
3
|
-
from .streaming import StreamResource
|
|
4
|
-
|
|
5
|
-
from urllib.parse import urlencode
|
|
6
|
-
import aiohttp, json
|
|
7
|
-
|
|
8
|
-
class HttpStreamResource(StreamResource):
|
|
9
|
-
def __init__(self, session: aiohttp.ClientSession, stream: aiohttp.StreamReader, content_type: Optional[str] = None, filename: Optional[str] = None):
|
|
10
|
-
super().__init__(content_type, filename)
|
|
11
|
-
|
|
12
|
-
self.session: aiohttp.ClientSession = session
|
|
13
|
-
self.stream: aiohttp.StreamReader = stream
|
|
14
|
-
|
|
15
|
-
async def close(self):
|
|
16
|
-
await self.session.close()
|
|
17
|
-
self.session = None
|
|
18
|
-
self.stream = None
|
|
19
|
-
|
|
20
|
-
async def _iterate_stream(self) -> AsyncIterator[bytes]:
|
|
21
|
-
_, buffer_size = self.stream.get_read_buffer_limits()
|
|
22
|
-
chunk_size = buffer_size or 65536
|
|
23
|
-
|
|
24
|
-
while not self.stream.at_eof():
|
|
25
|
-
chunk = await self.stream.read(chunk_size)
|
|
26
|
-
if not chunk:
|
|
27
|
-
break
|
|
28
|
-
yield chunk
|
|
29
|
-
|
|
30
|
-
class HttpClient:
|
|
31
|
-
async def request(self, url: str, method: str, params: Optional[Dict[str, Any]], body: Optional[Any], headers: Optional[Dict[str, str]]) -> Any:
|
|
32
|
-
session = aiohttp.ClientSession()
|
|
33
|
-
try:
|
|
34
|
-
response = await session.request(
|
|
35
|
-
method,
|
|
36
|
-
url,
|
|
37
|
-
params=params,
|
|
38
|
-
data=await self._serialize_request_body(body, headers),
|
|
39
|
-
headers=headers
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
content = await self._parse_response_content(session, response)
|
|
43
|
-
|
|
44
|
-
if response.status >= 400:
|
|
45
|
-
raise ValueError(f"Request failed with status {response.status}: {content}")
|
|
46
|
-
|
|
47
|
-
if not isinstance(content, HttpStreamResource):
|
|
48
|
-
await session.close()
|
|
49
|
-
|
|
50
|
-
return content
|
|
51
|
-
except:
|
|
52
|
-
await session.close()
|
|
53
|
-
raise
|
|
54
|
-
|
|
55
|
-
async def _serialize_request_body(self, body: Optional[Any], headers: Optional[Dict[str, str]]) -> Any:
|
|
56
|
-
content_type, _ = parse_options_header(headers, "Content-Type")
|
|
57
|
-
|
|
58
|
-
if content_type and body:
|
|
59
|
-
return await serialize_request_body(body, content_type)
|
|
60
|
-
|
|
61
|
-
return body
|
|
62
|
-
|
|
63
|
-
async def _parse_response_content(self, session: aiohttp.ClientSession, response: aiohttp.ClientResponse) -> Any:
|
|
64
|
-
content_type, _ = parse_options_header(response.headers, "Content-Type")
|
|
65
|
-
|
|
66
|
-
if content_type == "application/json":
|
|
67
|
-
return await response.json()
|
|
68
|
-
|
|
69
|
-
if content_type.startswith("text/"):
|
|
70
|
-
return await response.text()
|
|
71
|
-
|
|
72
|
-
_, disposition = parse_options_header(response.headers, "Content-Disposition")
|
|
73
|
-
filename = disposition.get("filename")
|
|
74
|
-
|
|
75
|
-
return HttpStreamResource(session, response.content, content_type, filename)
|
|
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.2 → model_compose-0.1.4}/src/mindor/core/listener/engine/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
|
|
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.2 → model_compose-0.1.4}/src/mindor/dsl/schema/action/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.2 → model_compose-0.1.4}/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.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/component/impl/mcp_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.2 → model_compose-0.1.4}/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.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.1.2 → model_compose-0.1.4}/src/mindor/dsl/schema/controller/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.1.2 → model_compose-0.1.4}/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.2 → model_compose-0.1.4}/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.2 → model_compose-0.1.4}/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
|