model-compose 0.0.4__tar.gz → 0.0.6__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.4/src/model_compose.egg-info → model_compose-0.0.6}/PKG-INFO +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/pyproject.toml +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/component/engine/__init__.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/component/engine/base.py +4 -4
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/component/engine/context.py +5 -1
- model_compose-0.0.6/src/mindor/core/component/engine/http_client.py +123 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/component/engine/http_server.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/controller/engine/base.py +2 -2
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/controller/engine/http_server.py +51 -11
- model_compose-0.0.6/src/mindor/core/listener/__init__.py +1 -0
- model_compose-0.0.6/src/mindor/core/listener/engine/__init__.py +2 -0
- model_compose-0.0.6/src/mindor/core/listener/engine/base.py +13 -0
- model_compose-0.0.6/src/mindor/core/listener/engine/http_callback.py +37 -0
- model_compose-0.0.6/src/mindor/core/listener/listener.py +9 -0
- model_compose-0.0.6/src/mindor/core/utils/http_headers.py +24 -0
- model_compose-0.0.6/src/mindor/core/utils/resources.py +28 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/workflow/context.py +5 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/workflow/job.py +8 -6
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/workflow/workflow.py +6 -5
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/http_client.py +22 -6
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/http_server.py +2 -2
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/mcp_client.py +2 -2
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/mcp_server.py +3 -3
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/component.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/common.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/http_client.py +6 -6
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/http_server.py +2 -2
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/mcp_client.py +3 -3
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/mcp_server.py +2 -2
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/controller.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/http_server.py +2 -2
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/mcp_server.py +2 -2
- model_compose-0.0.6/src/mindor/dsl/schema/listener/__init__.py +1 -0
- model_compose-0.0.6/src/mindor/dsl/schema/listener/impl/__init__.py +2 -0
- model_compose-0.0.6/src/mindor/dsl/schema/listener/impl/common.py +7 -0
- model_compose-0.0.6/src/mindor/dsl/schema/listener/impl/http_callback.py +25 -0
- model_compose-0.0.6/src/mindor/dsl/schema/listener/impl/types.py +4 -0
- model_compose-0.0.6/src/mindor/dsl/schema/listener/listener.py +10 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/runtime/impl/docker.py +2 -2
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/runtime/runtime.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6/src/model_compose.egg-info}/PKG-INFO +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/model_compose.egg-info/SOURCES.txt +13 -0
- model_compose-0.0.4/src/mindor/core/component/engine/http_client.py +0 -87
- {model_compose-0.0.4 → model_compose-0.0.6}/LICENSE +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/README.md +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/setup.cfg +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/cli/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/cli/compose.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/component/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/component/component.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/compose/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/compose/compose.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/compose/manager.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/controller/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/controller/controller.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/controller/engine/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/controller/engine/mcp_server.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/runtime/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/runtime/env.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/utils/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/utils/expiring.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/utils/template.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/utils/workqueue.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/workflow/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/loader.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/action.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/common.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/types.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/compose.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/common.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/runtime/impl/common.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/workflow.py +1 -1
- {model_compose-0.0.4 → model_compose-0.0.6}/src/model_compose.egg-info/dependency_links.txt +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/model_compose.egg-info/entry_points.txt +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/model_compose.egg-info/requires.txt +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/src/model_compose.egg-info/top_level.txt +0 -0
- {model_compose-0.0.4 → model_compose-0.0.6}/tests/test_cli.py +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable, Any
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
3
|
|
|
4
4
|
from mindor.dsl.schema.component import ComponentConfig, ComponentType
|
|
5
5
|
from mindor.dsl.schema.action import ActionConfig
|
|
@@ -78,9 +78,9 @@ class BaseComponent(ABC):
|
|
|
78
78
|
if self.daemon_task:
|
|
79
79
|
await self.daemon_task
|
|
80
80
|
|
|
81
|
-
async def run(self, action_id: Union[str, None], input: Dict[str, Any]) -> Dict[str, Any]:
|
|
81
|
+
async def run(self, action_id: Union[str, None], call_id: str, input: Dict[str, Any]) -> Dict[str, Any]:
|
|
82
82
|
_, action = ActionResolver(self.config.actions).resolve(action_id)
|
|
83
|
-
context = ComponentContext(input, self.env)
|
|
83
|
+
context = ComponentContext(call_id, input, self.env)
|
|
84
84
|
|
|
85
85
|
if self.queue:
|
|
86
86
|
return await (await self.queue.schedule(action, context))
|
|
@@ -117,7 +117,7 @@ class BaseComponent(ABC):
|
|
|
117
117
|
pass
|
|
118
118
|
|
|
119
119
|
@abstractmethod
|
|
120
|
-
async def _run(self, action: ActionConfig, context: ComponentContext) ->
|
|
120
|
+
async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
|
|
121
121
|
pass
|
|
122
122
|
|
|
123
123
|
ComponentEngineMap: Dict[ComponentType, Type[BaseComponent]] = {}
|
|
@@ -2,9 +2,11 @@ from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annot
|
|
|
2
2
|
from mindor.core.utils.template import TemplateRenderer
|
|
3
3
|
|
|
4
4
|
class ComponentContext:
|
|
5
|
-
def __init__(self, input: Dict[str, Any], env: Dict[str, str]):
|
|
5
|
+
def __init__(self, call_id: str, input: Dict[str, Any], env: Dict[str, str]):
|
|
6
|
+
self.call_id: str = call_id
|
|
6
7
|
self.input: Dict[str, Any] = input
|
|
7
8
|
self.env: Dict[str, str] = env
|
|
9
|
+
self.context: Dict[str, Any] = { "call_id": call_id }
|
|
8
10
|
self.sources: Dict[str, Any] = {}
|
|
9
11
|
self.renderer: TemplateRenderer = TemplateRenderer(self._resolve_source)
|
|
10
12
|
|
|
@@ -21,4 +23,6 @@ class ComponentContext:
|
|
|
21
23
|
return self.input
|
|
22
24
|
if key == "env":
|
|
23
25
|
return self.env
|
|
26
|
+
if key == "context":
|
|
27
|
+
return self.context
|
|
24
28
|
raise KeyError(f"Unknown source: {key}")
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, AsyncIterator, Any
|
|
2
|
+
from mindor.dsl.schema.component import HttpClientComponentConfig, HttpClientActionConfig
|
|
3
|
+
from mindor.core.utils.http_headers import parse_options_header, get_header_value
|
|
4
|
+
from mindor.core.utils.resources import StreamResource
|
|
5
|
+
from .base import BaseComponent, ComponentType, ComponentEngineMap, ActionConfig
|
|
6
|
+
from .context import ComponentContext
|
|
7
|
+
|
|
8
|
+
from urllib.parse import urlencode
|
|
9
|
+
import aiohttp, json
|
|
10
|
+
|
|
11
|
+
class HttpStreamResource(StreamResource):
|
|
12
|
+
def __init__(self, session: aiohttp.ClientSession, stream: aiohttp.StreamReader, filename: Optional[str], content_type: Optional[str]):
|
|
13
|
+
super().__init__(content_type, filename)
|
|
14
|
+
|
|
15
|
+
self.session: aiohttp.ClientSession = session
|
|
16
|
+
self.stream: aiohttp.StreamReader = stream
|
|
17
|
+
|
|
18
|
+
def get_stream(self):
|
|
19
|
+
return self.stream
|
|
20
|
+
|
|
21
|
+
async def close(self):
|
|
22
|
+
await self.session.close()
|
|
23
|
+
|
|
24
|
+
async def _iterate_stream(self) -> AsyncIterator[bytes]:
|
|
25
|
+
_, buffer_size = self.stream.get_read_buffer_limits()
|
|
26
|
+
chunk_size = buffer_size or 65536
|
|
27
|
+
|
|
28
|
+
while not self.stream.at_eof():
|
|
29
|
+
chunk = await self.stream.read(chunk_size)
|
|
30
|
+
if not chunk:
|
|
31
|
+
break
|
|
32
|
+
yield chunk
|
|
33
|
+
|
|
34
|
+
class HttpClientAction:
|
|
35
|
+
def __init__(self, base_url: Optional[str], config: HttpClientActionConfig):
|
|
36
|
+
self.base_url: Optional[str] = base_url
|
|
37
|
+
self.config: HttpClientActionConfig = config
|
|
38
|
+
|
|
39
|
+
async def run(self, context: ComponentContext) -> Any:
|
|
40
|
+
url = self._resolve_request_url(context)
|
|
41
|
+
method = context.render_template(self.config.method)
|
|
42
|
+
params = context.render_template(self.config.params)
|
|
43
|
+
body = context.render_template(self.config.body)
|
|
44
|
+
headers = context.render_template(self.config.headers)
|
|
45
|
+
|
|
46
|
+
response = await self._request(url, method, params, body, headers)
|
|
47
|
+
|
|
48
|
+
if response:
|
|
49
|
+
context.register_source("response", response)
|
|
50
|
+
|
|
51
|
+
return context.render_template(self.config.output) if self.config.output else response
|
|
52
|
+
|
|
53
|
+
async def _request(self, url: str, method: str, params: Optional[Dict[str, Any]], body: Optional[Any], headers: Optional[Dict[str, str]]) -> Any:
|
|
54
|
+
session = aiohttp.ClientSession()
|
|
55
|
+
try:
|
|
56
|
+
response = await session.request(
|
|
57
|
+
method,
|
|
58
|
+
url,
|
|
59
|
+
params=params,
|
|
60
|
+
data=self._serialize_request_body(body, headers) if body is not None else None,
|
|
61
|
+
headers=headers
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if response.status >= 400:
|
|
65
|
+
raise ValueError(f"Request failed with status {response.status}")
|
|
66
|
+
|
|
67
|
+
content = await self._parse_response_content(session, response)
|
|
68
|
+
|
|
69
|
+
if isinstance(content, HttpStreamResource):
|
|
70
|
+
pass
|
|
71
|
+
else:
|
|
72
|
+
await session.close()
|
|
73
|
+
|
|
74
|
+
return content
|
|
75
|
+
except:
|
|
76
|
+
await session.close()
|
|
77
|
+
raise
|
|
78
|
+
|
|
79
|
+
def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
80
|
+
if self.base_url and self.config.path:
|
|
81
|
+
return context.render_template(str(self.base_url)) + context.render_template(self.config.path)
|
|
82
|
+
|
|
83
|
+
return context.render_template(str(self.config.endpoint))
|
|
84
|
+
|
|
85
|
+
def _serialize_request_body(self, body: Any, headers: Optional[Dict[str, str]]) -> Any:
|
|
86
|
+
content_type, _ = parse_options_header(get_header_value(headers, "Content-Type", ""))
|
|
87
|
+
|
|
88
|
+
if content_type == "application/x-www-form-urlencoded":
|
|
89
|
+
return urlencode(body)
|
|
90
|
+
|
|
91
|
+
if isinstance(body, (str, bytes)):
|
|
92
|
+
return body
|
|
93
|
+
|
|
94
|
+
return json.dumps(body)
|
|
95
|
+
|
|
96
|
+
async def _parse_response_content(self, session: aiohttp.ClientSession, response: aiohttp.ClientResponse) -> Any:
|
|
97
|
+
content_type, _ = parse_options_header(response.headers.get("Content-Type", ""))
|
|
98
|
+
|
|
99
|
+
if content_type == "application/json":
|
|
100
|
+
return await response.json()
|
|
101
|
+
|
|
102
|
+
if content_type.startswith("text/"):
|
|
103
|
+
return await response.text()
|
|
104
|
+
|
|
105
|
+
_, disposition = parse_options_header(response.headers.get("Content-Disposition", ""))
|
|
106
|
+
filename = disposition.get("filename")
|
|
107
|
+
|
|
108
|
+
return HttpStreamResource(session, response.content, filename, content_type)
|
|
109
|
+
|
|
110
|
+
class HttpClientComponent(BaseComponent):
|
|
111
|
+
def __init__(self, id: str, config: HttpClientComponentConfig, env: Dict[str, str], daemon: bool):
|
|
112
|
+
super().__init__(id, config, env, daemon)
|
|
113
|
+
|
|
114
|
+
async def _serve(self) -> None:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
async def _shutdown(self) -> None:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
|
|
121
|
+
return await HttpClientAction(self.config.base_url, action).run(context)
|
|
122
|
+
|
|
123
|
+
ComponentEngineMap[ComponentType.HTTP_CLIENT] = HttpClientComponent
|
|
@@ -13,7 +13,7 @@ class HttpServerComponent(BaseComponent):
|
|
|
13
13
|
async def _shutdown(self) -> None:
|
|
14
14
|
pass
|
|
15
15
|
|
|
16
|
-
async def _run(self, action: ActionConfig, context: ComponentContext) ->
|
|
16
|
+
async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
|
|
17
17
|
return {}
|
|
18
18
|
|
|
19
19
|
ComponentEngineMap[ComponentType.HTTP_SERVER] = HttpServerComponent
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
2
3
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
4
4
|
|
|
5
5
|
from mindor.dsl.schema.controller import ControllerConfig, ControllerType
|
|
6
6
|
from mindor.dsl.schema.component import ComponentConfig
|
|
@@ -125,7 +125,7 @@ class BaseController(ABC):
|
|
|
125
125
|
|
|
126
126
|
try:
|
|
127
127
|
workflow = create_workflow(*WorkflowResolver(self.workflows).resolve(workflow_id), self.components, self.env)
|
|
128
|
-
output = await workflow.run(input)
|
|
128
|
+
output = await workflow.run(task_id, input)
|
|
129
129
|
state = TaskState(task_id=task_id, status="completed", output=output)
|
|
130
130
|
except Exception as e:
|
|
131
131
|
state = TaskState(task_id=task_id, status="failed", error=str(e))
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/core/controller/engine/http_server.py
RENAMED
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
from pydantic import BaseModel
|
|
2
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
3
2
|
from typing_extensions import Self
|
|
3
|
+
from pydantic import BaseModel
|
|
4
4
|
|
|
5
5
|
from mindor.dsl.schema.controller import HttpServerControllerConfig
|
|
6
6
|
from mindor.dsl.schema.component import ComponentConfig
|
|
7
7
|
from mindor.dsl.schema.workflow import WorkflowConfig
|
|
8
|
+
from mindor.core.utils.resources import StreamResource
|
|
8
9
|
from .base import BaseController, ControllerType, ControllerEngineMap, TaskState
|
|
9
10
|
|
|
10
11
|
from fastapi import FastAPI, APIRouter, Body, HTTPException
|
|
11
|
-
import
|
|
12
|
+
from fastapi.responses import JSONResponse, StreamingResponse
|
|
13
|
+
from starlette.background import BackgroundTask
|
|
14
|
+
import asyncio, uvicorn
|
|
12
15
|
|
|
13
16
|
class WorkflowTaskRequestBody(BaseModel):
|
|
14
17
|
workflow_id: Optional[str] = None
|
|
15
18
|
input: Optional[Dict[str, Any]] = None
|
|
16
19
|
wait_for_completion: bool = True
|
|
20
|
+
output_only: bool = False
|
|
17
21
|
|
|
18
22
|
class TaskResult(BaseModel):
|
|
19
23
|
task_id: str
|
|
@@ -42,30 +46,66 @@ class HttpServerController(BaseController):
|
|
|
42
46
|
self.app.include_router(self.router, prefix=self.config.base_path)
|
|
43
47
|
|
|
44
48
|
def _configure_routes(self):
|
|
45
|
-
@self.router.post("/workflows"
|
|
49
|
+
@self.router.post("/workflows")
|
|
46
50
|
async def run_workflow(
|
|
47
51
|
body: WorkflowTaskRequestBody = Body(...)
|
|
48
52
|
):
|
|
49
53
|
state = await self.run_workflow(body.workflow_id, body.input, body.wait_for_completion)
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
if body.output_only and body.wait_for_completion:
|
|
56
|
+
if state.status == "failed":
|
|
57
|
+
raise HTTPException(status_code=500, detail=str(state.error))
|
|
58
|
+
|
|
59
|
+
if isinstance(state.output, StreamResource):
|
|
60
|
+
return StreamingResponse(
|
|
61
|
+
state.output,
|
|
62
|
+
media_type=state.output.content_type,
|
|
63
|
+
headers = {
|
|
64
|
+
"Content-Disposition": f"attachment; filename={state.output.filename}"
|
|
65
|
+
} if state.output.filename else {},
|
|
66
|
+
#background=BackgroundTask(lambda: asyncio.create_task(state.output.close()))
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return state.output
|
|
70
|
+
|
|
71
|
+
return JSONResponse(content=TaskResult.from_instance(state).model_dump(exclude_none=True))
|
|
52
72
|
|
|
53
|
-
@self.router.get("/tasks/{task_id}"
|
|
73
|
+
@self.router.get("/tasks/{task_id}")
|
|
54
74
|
async def get_task_state(
|
|
55
|
-
task_id: str
|
|
75
|
+
task_id: str,
|
|
76
|
+
output_only: bool = False
|
|
56
77
|
):
|
|
57
78
|
state = self.get_task_state(task_id)
|
|
58
79
|
|
|
59
80
|
if not state:
|
|
60
|
-
raise HTTPException(status_code=404, detail="Task not found")
|
|
81
|
+
raise HTTPException(status_code=404, detail="Task not found.")
|
|
61
82
|
|
|
62
|
-
|
|
83
|
+
if output_only:
|
|
84
|
+
if state.status in [ "pending", "processing" ]:
|
|
85
|
+
raise HTTPException(status_code=202, detail="Task is still in progress.")
|
|
86
|
+
|
|
87
|
+
if state.status == "failed":
|
|
88
|
+
raise HTTPException(status_code=500, detail=str(state.error))
|
|
89
|
+
|
|
90
|
+
if isinstance(state.output, StreamResource):
|
|
91
|
+
return StreamingResponse(
|
|
92
|
+
state.output,
|
|
93
|
+
media_type=state.output.content_type,
|
|
94
|
+
headers = {
|
|
95
|
+
"Content-Disposition": f"attachment; filename={state.output.filename}"
|
|
96
|
+
} if state.output.filename else {},
|
|
97
|
+
# background=BackgroundTask(lambda: asyncio.create_task(state.output.close()))
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return state.output
|
|
101
|
+
|
|
102
|
+
return JSONResponse(content=TaskResult.from_instance(state).model_dump(exclude_none=True))
|
|
63
103
|
|
|
64
104
|
async def _serve(self) -> None:
|
|
65
105
|
self.server = uvicorn.Server(uvicorn.Config(
|
|
66
|
-
self.app,
|
|
67
|
-
host=self.config.host,
|
|
68
|
-
port=self.config.port,
|
|
106
|
+
self.app,
|
|
107
|
+
host=self.config.host,
|
|
108
|
+
port=self.config.port,
|
|
69
109
|
log_level="info"
|
|
70
110
|
))
|
|
71
111
|
await self.server.serve()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .listener import *
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from mindor.dsl.schema.listener import ListenerConfig, ListenerType
|
|
6
|
+
from mindor.core.listener import BaseListener, create_listener
|
|
7
|
+
|
|
8
|
+
class BaseListener(ABC):
|
|
9
|
+
def __init__(self, config: ListenerConfig, env: Dict[str, str]):
|
|
10
|
+
self.config: ListenerConfig = config
|
|
11
|
+
self.env: Dict[str, str] = env
|
|
12
|
+
|
|
13
|
+
ListenerEngineMap: Dict[ListenerType, Type[BaseListener]] = {}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from typing_extensions import Self
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from mindor.dsl.schema.listener import HttpCallbackListenerConfig
|
|
6
|
+
from .base import BaseListener, ListenerType, ListenerEngineMap
|
|
7
|
+
|
|
8
|
+
from fastapi import FastAPI, APIRouter, Body, HTTPException
|
|
9
|
+
import uvicorn
|
|
10
|
+
|
|
11
|
+
class HttpCallbackListener(BaseListener):
|
|
12
|
+
def __init__(self, config: HttpCallbackListenerConfig, env: Dict[str, str]):
|
|
13
|
+
super().__init__(config, env)
|
|
14
|
+
|
|
15
|
+
self.server: Optional[uvicorn.Server] = None
|
|
16
|
+
self.app: FastAPI = FastAPI()
|
|
17
|
+
self.router: APIRouter = APIRouter()
|
|
18
|
+
|
|
19
|
+
self._configure_routes()
|
|
20
|
+
self.app.include_router(self.router, prefix=self.config.base_path)
|
|
21
|
+
|
|
22
|
+
def _configure_routes(self):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
async def _serve(self) -> None:
|
|
26
|
+
self.server = uvicorn.Server(uvicorn.Config(
|
|
27
|
+
self.app,
|
|
28
|
+
host=self.config.host,
|
|
29
|
+
port=self.config.port,
|
|
30
|
+
log_level="info"
|
|
31
|
+
))
|
|
32
|
+
await self.server.serve()
|
|
33
|
+
|
|
34
|
+
async def _shutdown(self) -> None:
|
|
35
|
+
self.server.should_exit = True
|
|
36
|
+
|
|
37
|
+
ListenerEngineMap[ListenerType.HTTP_CALLBACK] = HttpCallbackListener
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from mindor.dsl.schema.listener import ListenerConfig
|
|
3
|
+
from .engine import BaseListener, ListenerEngineMap
|
|
4
|
+
|
|
5
|
+
def create_listener(config: ControllerConfig, env: Dict[str, str]) -> BaseListener:
|
|
6
|
+
try:
|
|
7
|
+
return ListenerEngineMap[config.type](config, env)
|
|
8
|
+
except KeyError:
|
|
9
|
+
raise ValueError(f"Unsupported listener type: {config.type}")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Dict, Tuple, Optional
|
|
2
|
+
from requests.structures import CaseInsensitiveDict
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
_token_pattern = r"([\w!#$%&'*+\-.^_`|~]+)"
|
|
6
|
+
_quoted_pattern = r'"([^"]*)"'
|
|
7
|
+
_option_regex = re.compile(fr";\s*{_token_pattern}=(?:{_token_pattern}|{_quoted_pattern})", re.ASCII)
|
|
8
|
+
_firefox_escape_fix = re.compile(r'\\"(?!; |\s*$)')
|
|
9
|
+
|
|
10
|
+
def parse_options_header(value: str) -> Tuple[str, Dict[str, str]]:
|
|
11
|
+
value = _firefox_escape_fix.sub("%22", value)
|
|
12
|
+
pos = value.find(";")
|
|
13
|
+
if pos >= 0:
|
|
14
|
+
options = {
|
|
15
|
+
m.group(1).lower(): m.group(2) or m.group(3).replace("%22", "") for m in _option_regex.finditer(value[pos:])
|
|
16
|
+
}
|
|
17
|
+
value = value[:pos]
|
|
18
|
+
else:
|
|
19
|
+
options = {}
|
|
20
|
+
|
|
21
|
+
return value.strip().lower(), options
|
|
22
|
+
|
|
23
|
+
def get_header_value(headers: Dict[str, str], header: str, default_value: Optional[str] = None) -> Optional[str]:
|
|
24
|
+
return CaseInsensitiveDict(headers or {}).get(header, default_value)
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
|
@@ -2,9 +2,11 @@ from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annot
|
|
|
2
2
|
from mindor.core.utils.template import TemplateRenderer
|
|
3
3
|
|
|
4
4
|
class WorkflowContext:
|
|
5
|
-
def __init__(self, input: Dict[str, Any], env: Dict[str, str]):
|
|
5
|
+
def __init__(self, task_id: str, input: Dict[str, Any], env: Dict[str, str]):
|
|
6
|
+
self.task_id: str = task_id
|
|
6
7
|
self.input: Dict[str, Any] = input
|
|
7
8
|
self.env: Dict[str, str] = env
|
|
9
|
+
self.context: Dict[str, Any] = { "task_id": task_id }
|
|
8
10
|
self.sources: Dict[str, Any] = { "jobs": {} }
|
|
9
11
|
self.renderer = TemplateRenderer(self._resolve_source)
|
|
10
12
|
|
|
@@ -24,4 +26,6 @@ class WorkflowContext:
|
|
|
24
26
|
return self.input
|
|
25
27
|
if key == "env":
|
|
26
28
|
return self.env
|
|
29
|
+
if key == "context":
|
|
30
|
+
return self.context
|
|
27
31
|
raise KeyError(f"Unknown source: {key}")
|
|
@@ -3,6 +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 BaseComponent
|
|
5
5
|
from .context import WorkflowContext
|
|
6
|
+
import ulid
|
|
6
7
|
|
|
7
8
|
class Job:
|
|
8
9
|
def __init__(self, id: str, config: JobConfig, component_provider: Callable[[Union[ComponentConfig, str]], BaseComponent]):
|
|
@@ -10,16 +11,17 @@ class Job:
|
|
|
10
11
|
self.config: JobConfig = config
|
|
11
12
|
self.component_provider: Callable[[Union[ComponentConfig, str]], BaseComponent] = component_provider
|
|
12
13
|
|
|
13
|
-
async def run(self, context: WorkflowContext) ->
|
|
14
|
+
async def run(self, context: WorkflowContext) -> Any:
|
|
14
15
|
component = self.component_provider(self.config.component)
|
|
15
16
|
|
|
16
17
|
if not component.started:
|
|
17
18
|
await component.start()
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
call_id = ulid.ulid()
|
|
21
|
+
input = context.render_template(self.config.input) if self.config.output else context.input
|
|
22
|
+
output = await component.run(self.config.action, call_id, input)
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
if output:
|
|
25
|
+
context.register_source("output", output)
|
|
24
26
|
|
|
25
|
-
return output
|
|
27
|
+
return context.render_template(self.config.output) if self.config.output else output
|
|
@@ -80,12 +80,12 @@ class WorkflowRunner:
|
|
|
80
80
|
self.jobs: Dict[str, JobConfig] = jobs
|
|
81
81
|
self.component_provider: Callable[[Union[ComponentConfig, str]], BaseComponent] = component_provider
|
|
82
82
|
|
|
83
|
-
async def run(self, context: WorkflowContext) ->
|
|
83
|
+
async def run(self, context: WorkflowContext) -> Any:
|
|
84
84
|
pending_jobs: Dict[str, Job] = { job_id: Job(job_id, job, self.component_provider) for job_id, job in self.jobs.items() }
|
|
85
85
|
running_job_ids: Set[str] = set()
|
|
86
86
|
completed_job_ids: Set[str] = set()
|
|
87
87
|
scheduled_job_tasks: Dict[str, asyncio.Task] = {}
|
|
88
|
-
output:
|
|
88
|
+
output: Any = {}
|
|
89
89
|
|
|
90
90
|
while pending_jobs:
|
|
91
91
|
runnable_jobs = [ job for job in pending_jobs.values() if self._can_run_job(job, running_job_ids, completed_job_ids) ]
|
|
@@ -107,7 +107,8 @@ class WorkflowRunner:
|
|
|
107
107
|
context.complete_job(completed_job_id, completed_job_output)
|
|
108
108
|
|
|
109
109
|
if self._is_terminal_job(completed_job_id):
|
|
110
|
-
output
|
|
110
|
+
output = completed_job_output
|
|
111
|
+
# output.update(completed_job_output)
|
|
111
112
|
|
|
112
113
|
running_job_ids.remove(completed_job_id)
|
|
113
114
|
completed_job_ids.add(completed_job_id)
|
|
@@ -129,8 +130,8 @@ class Workflow:
|
|
|
129
130
|
self.components: Dict[str, ComponentConfig] = components
|
|
130
131
|
self.env: Dict[str, str] = env
|
|
131
132
|
|
|
132
|
-
async def run(self, input: Dict[str, Any]) ->
|
|
133
|
-
context = WorkflowContext(input, self.env)
|
|
133
|
+
async def run(self, task_id: str, input: Dict[str, Any]) -> Any:
|
|
134
|
+
context = WorkflowContext(task_id, input, self.env)
|
|
134
135
|
runner = WorkflowRunner(self.config.jobs, self._create_component)
|
|
135
136
|
|
|
136
137
|
output = await runner.run(context)
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/http_client.py
RENAMED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from .common import CommonActionConfig
|
|
5
5
|
|
|
6
|
-
class
|
|
7
|
-
|
|
6
|
+
class CommonCompletionConfig(BaseModel):
|
|
7
|
+
type: Literal[ "callback", "polling" ]
|
|
8
|
+
|
|
9
|
+
class CallbackCompletionConfig(CommonCompletionConfig):
|
|
10
|
+
type: Literal["callback"]
|
|
11
|
+
wait_for: str
|
|
12
|
+
|
|
13
|
+
class PollingCompletionConfig(CommonCompletionConfig):
|
|
14
|
+
type: Literal["polling"]
|
|
15
|
+
endpoint: Optional[str] = None
|
|
8
16
|
path: Optional[str] = None
|
|
9
17
|
method: Literal[ "GET", "POST", "PUT", "DELETE", "PATCH" ] = "GET"
|
|
10
18
|
headers: Optional[Dict[str, str]] = Field(default_factory=dict)
|
|
@@ -19,15 +27,23 @@ class HttpClientActionCompletionConfig(CommonActionConfig):
|
|
|
19
27
|
raise ValueError("Either 'endpoint' or 'path' must be set, but not both.")
|
|
20
28
|
return values
|
|
21
29
|
|
|
30
|
+
HttpClientCompletionConfig = Annotated[
|
|
31
|
+
Union[
|
|
32
|
+
CallbackCompletionConfig,
|
|
33
|
+
PollingCompletionConfig
|
|
34
|
+
],
|
|
35
|
+
Field(discriminator="type")
|
|
36
|
+
]
|
|
37
|
+
|
|
22
38
|
class HttpClientActionConfig(CommonActionConfig):
|
|
23
|
-
endpoint: Optional[
|
|
39
|
+
endpoint: Optional[str] = None
|
|
24
40
|
path: Optional[str] = None
|
|
25
41
|
method: Literal[ "GET", "POST", "PUT", "DELETE", "PATCH" ] = "POST"
|
|
26
42
|
headers: Optional[Dict[str, str]] = Field(default_factory=dict)
|
|
27
43
|
body: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
|
28
44
|
params: Optional[Dict[str, str]] = Field(default_factory=dict)
|
|
29
45
|
deferred: bool = False
|
|
30
|
-
completion: Optional[
|
|
46
|
+
completion: Optional[HttpClientCompletionConfig] = None
|
|
31
47
|
|
|
32
48
|
@model_validator(mode="before")
|
|
33
49
|
def validate_endpoint_or_path(cls, values):
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/action/impl/http_server.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from .common import CommonActionConfig
|
|
5
5
|
|
|
6
6
|
class HttpServerActionConfig(CommonActionConfig):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from .common import CommonActionConfig
|
|
5
5
|
|
|
6
6
|
class McpClientActionConfig(CommonActionConfig):
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from .common import CommonActionConfig
|
|
5
5
|
|
|
6
6
|
class McpServerActionConfig(CommonActionConfig):
|
|
7
7
|
def __init__(self):
|
|
8
|
-
super.__init__(
|
|
8
|
+
super().__init__()
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/http_client.py
RENAMED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from mindor.dsl.schema.action import HttpClientActionConfig
|
|
5
5
|
from .common import ComponentType, CommonComponentConfig
|
|
6
6
|
|
|
7
7
|
class HttpClientComponentConfig(CommonComponentConfig):
|
|
8
8
|
type: Literal[ComponentType.HTTP_CLIENT]
|
|
9
|
-
base_url: Optional[
|
|
9
|
+
base_url: Optional[str] = None
|
|
10
10
|
headers: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
|
11
11
|
actions: Optional[Dict[str, HttpClientActionConfig]] = Field(default_factory=dict)
|
|
12
12
|
|
|
@@ -27,7 +27,7 @@ class HttpClientComponentConfig(CommonComponentConfig):
|
|
|
27
27
|
|
|
28
28
|
@model_validator(mode="after")
|
|
29
29
|
def validate_baseurl_with_completion(self):
|
|
30
|
-
for action_id, action in self.actions.items():
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
# for action_id, action in self.actions.items():
|
|
31
|
+
# if action.completion and action.completion.path and not self.base_url:
|
|
32
|
+
# raise ValueError(f"Completion for action '{action_id}' uses 'path' but 'base_url' is not set in the component.")
|
|
33
33
|
return self
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/http_server.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from mindor.dsl.schema.action import HttpServerActionConfig
|
|
5
5
|
from .common import ComponentType, CommonComponentConfig
|
|
6
6
|
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/mcp_client.py
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from mindor.dsl.schema.action import McpClientActionConfig
|
|
5
5
|
from .common import ComponentType, CommonComponentConfig
|
|
6
6
|
|
|
7
7
|
class McpClientComponentConfig(CommonComponentConfig):
|
|
8
8
|
type: Literal[ComponentType.MCP_CLIENT]
|
|
9
|
-
endpoint:
|
|
9
|
+
endpoint: Optional[str] = None
|
|
10
10
|
actions: Optional[Dict[str, McpClientActionConfig]] = Field(default_factory=dict)
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/mcp_server.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from mindor.dsl.schema.action import McpServerActionConfig
|
|
5
5
|
from .common import ComponentType, CommonComponentConfig
|
|
6
6
|
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/http_server.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from .common import ControllerType, CommonControllerConfig
|
|
5
5
|
|
|
6
6
|
class HttpServerControllerConfig(CommonControllerConfig):
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/mcp_server.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from .common import ControllerType, CommonControllerConfig
|
|
5
5
|
|
|
6
6
|
class McpServerControllerConfig(CommonControllerConfig):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .listener import *
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from .types import ListenerType
|
|
4
|
+
|
|
5
|
+
class CommonListenerConfig(BaseModel):
|
|
6
|
+
type: ListenerType
|
|
7
|
+
runtime: Literal[ "docker", "native" ] = "native"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
|
+
from .common import ListenerType, CommonListenerConfig
|
|
5
|
+
|
|
6
|
+
class HttpCallbackConfig(BaseModel):
|
|
7
|
+
path: str
|
|
8
|
+
method: Literal[ "GET", "POST", "PUT", "DELETE", "PATCH" ] = "GET"
|
|
9
|
+
bulk: bool = False
|
|
10
|
+
identify_by: str
|
|
11
|
+
|
|
12
|
+
class HttpCallbackListenerConfig(CommonListenerConfig):
|
|
13
|
+
type: Literal[ListenerType.HTTP_CALLBACK]
|
|
14
|
+
host: Optional[str] = "0.0.0.0"
|
|
15
|
+
port: Optional[int] = 8090
|
|
16
|
+
base_path: Optional[str] = None
|
|
17
|
+
callbacks: Optional[List[HttpCallbackConfig]] = Field(default_factory=list)
|
|
18
|
+
|
|
19
|
+
@model_validator(mode="before")
|
|
20
|
+
def inflate_single_callback(cls, values):
|
|
21
|
+
if "callbacks" not in values:
|
|
22
|
+
callback_keys = set(HttpCallbackConfig.model_fields.keys())
|
|
23
|
+
if any(k in values for k in callback_keys):
|
|
24
|
+
values["callbacks"] = [ { k: values.pop(k) for k in callback_keys if k in values } ]
|
|
25
|
+
return values
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from .impl import *
|
|
4
|
+
|
|
5
|
+
ListenerConfig = Annotated[
|
|
6
|
+
Union[
|
|
7
|
+
HttpCallbackListenerConfig
|
|
8
|
+
],
|
|
9
|
+
Field(discriminator="type")
|
|
10
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, HttpUrl
|
|
2
|
-
from pydantic import model_validator
|
|
3
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from pydantic import model_validator
|
|
4
4
|
from .common import RuntimeType, CommonRumtimeConfig
|
|
5
5
|
|
|
6
6
|
class DockerRuntimeConfig(CommonRumtimeConfig):
|
|
@@ -21,10 +21,17 @@ src/mindor/core/controller/engine/__init__.py
|
|
|
21
21
|
src/mindor/core/controller/engine/base.py
|
|
22
22
|
src/mindor/core/controller/engine/http_server.py
|
|
23
23
|
src/mindor/core/controller/engine/mcp_server.py
|
|
24
|
+
src/mindor/core/listener/__init__.py
|
|
25
|
+
src/mindor/core/listener/listener.py
|
|
26
|
+
src/mindor/core/listener/engine/__init__.py
|
|
27
|
+
src/mindor/core/listener/engine/base.py
|
|
28
|
+
src/mindor/core/listener/engine/http_callback.py
|
|
24
29
|
src/mindor/core/runtime/__init__.py
|
|
25
30
|
src/mindor/core/runtime/env.py
|
|
26
31
|
src/mindor/core/utils/__init__.py
|
|
27
32
|
src/mindor/core/utils/expiring.py
|
|
33
|
+
src/mindor/core/utils/http_headers.py
|
|
34
|
+
src/mindor/core/utils/resources.py
|
|
28
35
|
src/mindor/core/utils/template.py
|
|
29
36
|
src/mindor/core/utils/workqueue.py
|
|
30
37
|
src/mindor/core/workflow/__init__.py
|
|
@@ -60,6 +67,12 @@ src/mindor/dsl/schema/controller/impl/common.py
|
|
|
60
67
|
src/mindor/dsl/schema/controller/impl/http_server.py
|
|
61
68
|
src/mindor/dsl/schema/controller/impl/mcp_server.py
|
|
62
69
|
src/mindor/dsl/schema/controller/impl/types.py
|
|
70
|
+
src/mindor/dsl/schema/listener/__init__.py
|
|
71
|
+
src/mindor/dsl/schema/listener/listener.py
|
|
72
|
+
src/mindor/dsl/schema/listener/impl/__init__.py
|
|
73
|
+
src/mindor/dsl/schema/listener/impl/common.py
|
|
74
|
+
src/mindor/dsl/schema/listener/impl/http_callback.py
|
|
75
|
+
src/mindor/dsl/schema/listener/impl/types.py
|
|
63
76
|
src/mindor/dsl/schema/runtime/__init__.py
|
|
64
77
|
src/mindor/dsl/schema/runtime/runtime.py
|
|
65
78
|
src/mindor/dsl/schema/runtime/impl/__init__.py
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
-
from mindor.dsl.schema.component import HttpClientComponentConfig, HttpClientActionConfig, HttpUrl
|
|
3
|
-
from .base import BaseComponent, ComponentType, ComponentEngineMap, ActionConfig
|
|
4
|
-
from .context import ComponentContext
|
|
5
|
-
|
|
6
|
-
from requests.structures import CaseInsensitiveDict
|
|
7
|
-
from urllib.parse import urlencode, urljoin
|
|
8
|
-
import aiohttp, json
|
|
9
|
-
|
|
10
|
-
class HttpClientAction:
|
|
11
|
-
def __init__(self, base_url: Union[str, None], config: HttpClientActionConfig):
|
|
12
|
-
self.base_url: Union[HttpUrl, None] = base_url
|
|
13
|
-
self.config: HttpClientActionConfig = config
|
|
14
|
-
|
|
15
|
-
async def run(self, context: ComponentContext) -> Dict[str, Any]:
|
|
16
|
-
url = self._resolve_request_url(context)
|
|
17
|
-
method = context.render_template(self.config.method)
|
|
18
|
-
params = context.render_template(self.config.params)
|
|
19
|
-
body = context.render_template(self.config.body)
|
|
20
|
-
headers = context.render_template(self.config.headers)
|
|
21
|
-
|
|
22
|
-
response = await self._request(url, method, params, body, headers)
|
|
23
|
-
|
|
24
|
-
if not self.config.output:
|
|
25
|
-
return response or {}
|
|
26
|
-
|
|
27
|
-
if response:
|
|
28
|
-
context.register_source("response", response)
|
|
29
|
-
|
|
30
|
-
return context.render_template(self.config.output)
|
|
31
|
-
|
|
32
|
-
async def _request(self, url: str, method: str, params: Optional[Dict[str, Any]], body: Optional[Dict[str, Any]], headers: Optional[Dict[str, str]]) -> Union[dict, str, bytes]:
|
|
33
|
-
async with aiohttp.ClientSession() as session:
|
|
34
|
-
async with session.request(
|
|
35
|
-
method,
|
|
36
|
-
url,
|
|
37
|
-
params=params,
|
|
38
|
-
data=self._serialize_request_body(body, headers) if body is not None else None,
|
|
39
|
-
headers=headers
|
|
40
|
-
) as response:
|
|
41
|
-
if response.status >= 400:
|
|
42
|
-
raise ValueError(f"Request failed with status {response.status}")
|
|
43
|
-
|
|
44
|
-
return await self._deserialize_response_body(response)
|
|
45
|
-
|
|
46
|
-
def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
47
|
-
if self.base_url and self.config.path:
|
|
48
|
-
return context.render_template(str(self.base_url)) + context.render_template(self.config.path)
|
|
49
|
-
|
|
50
|
-
return context.render_template(str(self.config.endpoint))
|
|
51
|
-
|
|
52
|
-
def _serialize_request_body(self, body: Dict[str, Any], headers: Optional[Dict[str, str]]) -> str:
|
|
53
|
-
content_type = CaseInsensitiveDict(headers or {}).get("Content-Type", "").lower()
|
|
54
|
-
|
|
55
|
-
if content_type == "application/json":
|
|
56
|
-
return json.dumps(body)
|
|
57
|
-
|
|
58
|
-
if content_type == "application/x-www-form-urlencoded":
|
|
59
|
-
return urlencode(body)
|
|
60
|
-
|
|
61
|
-
return str(body)
|
|
62
|
-
|
|
63
|
-
async def _deserialize_response_body(self, response: aiohttp.ClientResponse) -> Union[dict, str, bytes]:
|
|
64
|
-
content_type = response.headers.get("Content-Type", "").lower()
|
|
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
|
-
return await response.read()
|
|
73
|
-
|
|
74
|
-
class HttpClientComponent(BaseComponent):
|
|
75
|
-
def __init__(self, id: str, config: HttpClientComponentConfig, env: Dict[str, str], daemon: bool):
|
|
76
|
-
super().__init__(id, config, env, daemon)
|
|
77
|
-
|
|
78
|
-
async def _serve(self) -> None:
|
|
79
|
-
pass
|
|
80
|
-
|
|
81
|
-
async def _shutdown(self) -> None:
|
|
82
|
-
pass
|
|
83
|
-
|
|
84
|
-
async def _run(self, action: ActionConfig, context: ComponentContext) -> Dict[str, Any]:
|
|
85
|
-
return await HttpClientAction(self.config.base_url, action).run(context)
|
|
86
|
-
|
|
87
|
-
ComponentEngineMap[ComponentType.HTTP_CLIENT] = HttpClientComponent
|
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field
|
|
2
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
3
|
|
|
4
4
|
class CommonActionConfig(BaseModel):
|
|
5
5
|
output: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
|
File without changes
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/component/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
1
2
|
from pydantic import BaseModel, Field
|
|
2
3
|
from pydantic import model_validator
|
|
3
|
-
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
4
4
|
|
|
5
5
|
from .controller import ControllerConfig
|
|
6
6
|
from .component import ComponentConfig
|
|
File without changes
|
{model_compose-0.0.4 → model_compose-0.0.6}/src/mindor/dsl/schema/controller/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
1
2
|
from pydantic import BaseModel, Field
|
|
2
3
|
from pydantic import model_validator
|
|
3
|
-
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
4
4
|
from .component import ComponentConfig
|
|
5
5
|
|
|
6
6
|
class JobConfig(BaseModel):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|