model-compose 0.0.7__tar.gz → 0.0.9__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.7/src/model_compose.egg-info → model_compose-0.0.9}/PKG-INFO +1 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/pyproject.toml +1 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/component/engine/__init__.py +0 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/component/engine/base.py +7 -61
- model_compose-0.0.9/src/mindor/core/component/engine/http_client.py +120 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/compose/manager.py +1 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/controller/controller.py +3 -2
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/controller/engine/base.py +24 -57
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/controller/engine/http_server.py +3 -2
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/controller/engine/mcp_server.py +3 -2
- model_compose-0.0.9/src/mindor/core/gateway/__init__.py +2 -0
- model_compose-0.0.9/src/mindor/core/gateway/engine/__init__.py +2 -0
- model_compose-0.0.9/src/mindor/core/gateway/engine/base.py +12 -0
- model_compose-0.0.9/src/mindor/core/gateway/engine/http_tunnel.py +9 -0
- model_compose-0.0.9/src/mindor/core/gateway/gateway.py +9 -0
- model_compose-0.0.9/src/mindor/core/listener/__init__.py +2 -0
- model_compose-0.0.9/src/mindor/core/listener/engine/base.py +29 -0
- model_compose-0.0.9/src/mindor/core/listener/engine/http_callback.py +121 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/listener/listener.py +2 -2
- model_compose-0.0.9/src/mindor/core/services/__init__.py +2 -0
- model_compose-0.0.9/src/mindor/core/services/async_service.py +66 -0
- {model_compose-0.0.7/src/mindor/core/component/engine → model_compose-0.0.9/src/mindor/core/utils}/http_client.py +5 -49
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/utils/template.py +24 -12
- model_compose-0.0.9/src/mindor/core/utils/time.py +13 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/workflow/workflow.py +8 -6
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/impl/common.py +1 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/impl/http_client.py +13 -10
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/impl/http_client.py +1 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/compose.py +12 -2
- model_compose-0.0.9/src/mindor/dsl/schema/gateway/__init__.py +1 -0
- model_compose-0.0.9/src/mindor/dsl/schema/gateway/gateway.py +10 -0
- model_compose-0.0.9/src/mindor/dsl/schema/gateway/impl/__init__.py +2 -0
- model_compose-0.0.9/src/mindor/dsl/schema/gateway/impl/common.py +7 -0
- model_compose-0.0.9/src/mindor/dsl/schema/gateway/impl/http_tunnel.py +9 -0
- model_compose-0.0.9/src/mindor/dsl/schema/gateway/impl/types.py +4 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/listener/impl/common.py +1 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/listener/impl/http_callback.py +4 -2
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/workflow.py +4 -4
- {model_compose-0.0.7 → model_compose-0.0.9/src/model_compose.egg-info}/PKG-INFO +1 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/src/model_compose.egg-info/SOURCES.txt +15 -0
- model_compose-0.0.7/src/mindor/core/listener/engine/base.py +0 -13
- model_compose-0.0.7/src/mindor/core/listener/engine/http_callback.py +0 -37
- model_compose-0.0.7/src/mindor/dsl/schema/listener/__init__.py +0 -1
- {model_compose-0.0.7 → model_compose-0.0.9}/LICENSE +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/README.md +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/setup.cfg +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/cli/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/cli/compose.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/component/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/component/component.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/component/engine/context.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/component/engine/http_server.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/compose/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/compose/compose.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/controller/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/controller/engine/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/listener/engine/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/runtime/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/runtime/env.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/utils/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/utils/expiring.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/utils/http_headers.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/utils/resources.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/utils/workqueue.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/workflow/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/workflow/context.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/workflow/job.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/loader.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/action.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/impl/http_server.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/impl/mcp_client.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/action/impl/mcp_server.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/component.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/impl/common.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/impl/http_server.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/impl/mcp_client.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/impl/mcp_server.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/component/impl/types.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/controller/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/controller/controller.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/controller/impl/common.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/controller/impl/http_server.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/controller/impl/mcp_server.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
- {model_compose-0.0.7/src/mindor/core → model_compose-0.0.9/src/mindor/dsl/schema}/listener/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/listener/listener.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/runtime/impl/common.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/runtime/impl/docker.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/dsl/schema/runtime/runtime.py +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/model_compose.egg-info/dependency_links.txt +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/model_compose.egg-info/entry_points.txt +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/model_compose.egg-info/requires.txt +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/src/model_compose.egg-info/top_level.txt +0 -0
- {model_compose-0.0.7 → model_compose-0.0.9}/tests/test_cli.py +0 -0
|
@@ -3,12 +3,10 @@ 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
|
|
6
|
+
from mindor.core.services import AsyncService
|
|
6
7
|
from mindor.core.utils.workqueue import WorkQueue
|
|
7
8
|
from .context import ComponentContext
|
|
8
9
|
|
|
9
|
-
from threading import Thread
|
|
10
|
-
import asyncio
|
|
11
|
-
|
|
12
10
|
class ActionResolver:
|
|
13
11
|
def __init__(self, actions: Dict[str, ActionConfig]):
|
|
14
12
|
self.actions = actions
|
|
@@ -32,52 +30,17 @@ class ActionResolver:
|
|
|
32
30
|
|
|
33
31
|
return default_ids[0] if default_ids else "__default__"
|
|
34
32
|
|
|
35
|
-
class BaseComponent(
|
|
33
|
+
class BaseComponent(AsyncService):
|
|
36
34
|
def __init__(self, id: str, config: ComponentConfig, env: Dict[str, str], daemon: bool):
|
|
35
|
+
super().__init__(daemon)
|
|
36
|
+
|
|
37
37
|
self.id: str = id
|
|
38
38
|
self.config: ComponentConfig = config
|
|
39
39
|
self.env: Dict[str, str] = env
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
self.queue: Optional[WorkQueue] = None
|
|
43
|
-
self.thread: Optional[Thread] = None
|
|
44
|
-
self.thread_loop: Optional[asyncio.AbstractEventLoop] = None
|
|
45
|
-
self.daemon_task: Optional[asyncio.Task] = None
|
|
46
|
-
self.started: bool = False
|
|
47
|
-
|
|
40
|
+
|
|
48
41
|
if self.config.max_concurrent_count > 0:
|
|
49
42
|
self.queue = WorkQueue(self.config.max_concurrent_count, self._run)
|
|
50
43
|
|
|
51
|
-
async def start(self, background: bool = False) -> None:
|
|
52
|
-
if background:
|
|
53
|
-
def _start_in_thread():
|
|
54
|
-
self.thread_loop = asyncio.new_event_loop()
|
|
55
|
-
asyncio.set_event_loop(self.thread_loop)
|
|
56
|
-
self.thread_loop.run_until_complete(self._start())
|
|
57
|
-
|
|
58
|
-
self.thread = Thread(target=_start_in_thread)
|
|
59
|
-
self.thread.start()
|
|
60
|
-
else:
|
|
61
|
-
await self._start()
|
|
62
|
-
|
|
63
|
-
async def stop(self) -> None:
|
|
64
|
-
if self.thread:
|
|
65
|
-
future = asyncio.run_coroutine_threadsafe(self._stop(), self.thread_loop)
|
|
66
|
-
future.result()
|
|
67
|
-
self.thread_loop.close()
|
|
68
|
-
self.thread_loop = None
|
|
69
|
-
self.thread.join()
|
|
70
|
-
self.thread = None
|
|
71
|
-
else:
|
|
72
|
-
await self._stop()
|
|
73
|
-
|
|
74
|
-
async def wait_until_stopped(self) -> None:
|
|
75
|
-
if self.thread:
|
|
76
|
-
self.thread.join()
|
|
77
|
-
|
|
78
|
-
if self.daemon_task:
|
|
79
|
-
await self.daemon_task
|
|
80
|
-
|
|
81
44
|
async def run(self, action_id: Union[str, None], call_id: str, input: Dict[str, Any]) -> Dict[str, Any]:
|
|
82
45
|
_, action = ActionResolver(self.config.actions).resolve(action_id)
|
|
83
46
|
context = ComponentContext(call_id, input, self.env)
|
|
@@ -91,30 +54,13 @@ class BaseComponent(ABC):
|
|
|
91
54
|
if self.queue:
|
|
92
55
|
await self.queue.start()
|
|
93
56
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if self.daemon:
|
|
97
|
-
if not self.thread:
|
|
98
|
-
self.daemon_task = asyncio.create_task(self._serve())
|
|
99
|
-
else:
|
|
100
|
-
await self._serve()
|
|
57
|
+
await super()._start()
|
|
101
58
|
|
|
102
59
|
async def _stop(self) -> None:
|
|
103
60
|
if self.queue:
|
|
104
61
|
await self.queue.stop()
|
|
105
62
|
|
|
106
|
-
|
|
107
|
-
await self._shutdown()
|
|
108
|
-
|
|
109
|
-
self.started = False
|
|
110
|
-
|
|
111
|
-
@abstractmethod
|
|
112
|
-
async def _serve(self) -> None:
|
|
113
|
-
pass
|
|
114
|
-
|
|
115
|
-
@abstractmethod
|
|
116
|
-
async def _shutdown(self) -> None:
|
|
117
|
-
pass
|
|
63
|
+
await super()._stop()
|
|
118
64
|
|
|
119
65
|
@abstractmethod
|
|
120
66
|
async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, AsyncIterator, Any
|
|
2
|
+
from mindor.dsl.schema.component import HttpClientComponentConfig
|
|
3
|
+
from mindor.dsl.schema.action import ActionConfig, HttpClientActionConfig, HttpClientCompletionConfig
|
|
4
|
+
from mindor.core.listener import HttpCallbackListener
|
|
5
|
+
from mindor.core.utils.http_client import HttpClient
|
|
6
|
+
from mindor.core.utils.time import parse_duration
|
|
7
|
+
from .base import BaseComponent, ComponentType, ComponentEngineMap
|
|
8
|
+
from .context import ComponentContext
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
import asyncio
|
|
11
|
+
|
|
12
|
+
class HttpClientPollingCompletion:
|
|
13
|
+
def __init__(self, base_url: Optional[str], config: HttpClientCompletionConfig):
|
|
14
|
+
self.base_url: Optional[str] = base_url
|
|
15
|
+
self.config: HttpClientCompletionConfig = config
|
|
16
|
+
self.client: HttpClient = HttpClient()
|
|
17
|
+
|
|
18
|
+
async def run(self, context: ComponentContext) -> Any:
|
|
19
|
+
url = self._resolve_request_url(context)
|
|
20
|
+
method = context.render_template(self.config.method)
|
|
21
|
+
params = context.render_template(self.config.params)
|
|
22
|
+
body = context.render_template(self.config.body)
|
|
23
|
+
headers = context.render_template(self.config.headers)
|
|
24
|
+
|
|
25
|
+
interval = parse_duration(self.config.interval) if self.config.interval else 5.0
|
|
26
|
+
timeout = parse_duration(self.config.timeout) if self.config.timeout else 300
|
|
27
|
+
deadline = datetime.now(timezone.utc) + timeout
|
|
28
|
+
|
|
29
|
+
await asyncio.sleep(interval.total_seconds())
|
|
30
|
+
|
|
31
|
+
while datetime.now(timezone.utc) < deadline:
|
|
32
|
+
response = await self.client.request(url, method, params, body, headers)
|
|
33
|
+
|
|
34
|
+
context.register_source("response", response)
|
|
35
|
+
status = context.render_template(self.config.status) if self.config.status else response["status"]
|
|
36
|
+
|
|
37
|
+
success_when = context.render_template(self.config.success_when) if self.config.success_when else "completed"
|
|
38
|
+
if success_when and status == success_when:
|
|
39
|
+
return response
|
|
40
|
+
|
|
41
|
+
fail_when = context.render_template(self.config.fail_when) if self.config.fail_when else "failed"
|
|
42
|
+
if fail_when and status == fail_when:
|
|
43
|
+
raise RuntimeError(f"Polling failed: status='{status}'.")
|
|
44
|
+
|
|
45
|
+
await asyncio.sleep(interval.total_seconds())
|
|
46
|
+
|
|
47
|
+
raise TimeoutError(f"Polling timed out after {timeout}")
|
|
48
|
+
|
|
49
|
+
def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
50
|
+
if self.base_url and self.config.path:
|
|
51
|
+
return context.render_template(self.base_url) + context.render_template(self.config.path)
|
|
52
|
+
|
|
53
|
+
return context.render_template(self.config.endpoint)
|
|
54
|
+
|
|
55
|
+
class HttpClientCallbackCompletion:
|
|
56
|
+
def __init__(self, config: HttpClientCompletionConfig):
|
|
57
|
+
self.config: HttpClientCompletionConfig = config
|
|
58
|
+
|
|
59
|
+
async def run(self, context: ComponentContext) -> Any:
|
|
60
|
+
callback_id = context.render_template(self.config.wait_for)
|
|
61
|
+
future: asyncio.Future = asyncio.get_event_loop().create_future()
|
|
62
|
+
HttpCallbackListener.register_pending_future(callback_id, future)
|
|
63
|
+
|
|
64
|
+
return await future
|
|
65
|
+
|
|
66
|
+
class HttpClientAction:
|
|
67
|
+
def __init__(self, base_url: Optional[str], config: HttpClientActionConfig):
|
|
68
|
+
self.base_url: Optional[str] = base_url
|
|
69
|
+
self.config: HttpClientActionConfig = config
|
|
70
|
+
self.client: HttpClient = HttpClient()
|
|
71
|
+
|
|
72
|
+
async def run(self, context: ComponentContext) -> Any:
|
|
73
|
+
url = self._resolve_request_url(context)
|
|
74
|
+
method = context.render_template(self.config.method)
|
|
75
|
+
params = context.render_template(self.config.params)
|
|
76
|
+
body = context.render_template(self.config.body)
|
|
77
|
+
headers = context.render_template(self.config.headers)
|
|
78
|
+
|
|
79
|
+
response = await self.client.request(url, method, params, body, headers)
|
|
80
|
+
|
|
81
|
+
if response:
|
|
82
|
+
context.register_source("response", response)
|
|
83
|
+
|
|
84
|
+
if self.config.completion:
|
|
85
|
+
result = await self._handle_completion(self.config.completion, context)
|
|
86
|
+
|
|
87
|
+
if result:
|
|
88
|
+
context.register_source("result", result)
|
|
89
|
+
|
|
90
|
+
return context.render_template(self.config.output) if self.config.output else (result or response)
|
|
91
|
+
|
|
92
|
+
def _resolve_request_url(self, context: ComponentContext) -> str:
|
|
93
|
+
if self.base_url and self.config.path:
|
|
94
|
+
return context.render_template(self.base_url) + context.render_template(self.config.path)
|
|
95
|
+
|
|
96
|
+
return context.render_template(self.config.endpoint)
|
|
97
|
+
|
|
98
|
+
async def _handle_completion(self, completion: HttpClientCompletionConfig, context: ComponentContext) -> Any:
|
|
99
|
+
if completion.type == "polling":
|
|
100
|
+
return await HttpClientPollingCompletion(self.base_url, completion).run(context)
|
|
101
|
+
|
|
102
|
+
if completion.type == "callback":
|
|
103
|
+
return await HttpClientCallbackCompletion(completion).run(context)
|
|
104
|
+
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
class HttpClientComponent(BaseComponent):
|
|
108
|
+
def __init__(self, id: str, config: HttpClientComponentConfig, env: Dict[str, str], daemon: bool):
|
|
109
|
+
super().__init__(id, config, env, daemon)
|
|
110
|
+
|
|
111
|
+
async def _serve(self) -> None:
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
async def _shutdown(self) -> None:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
|
|
118
|
+
return await HttpClientAction(self.config.base_url, action).run(context)
|
|
119
|
+
|
|
120
|
+
ComponentEngineMap[ComponentType.HTTP_CLIENT] = HttpClientComponent
|
|
@@ -5,7 +5,7 @@ from mindor.core.controller import BaseController, TaskState, create_controller
|
|
|
5
5
|
class ComposeManager:
|
|
6
6
|
def __init__(self, config: ComposeConfig, env: Dict[str, str], daemon: bool = True):
|
|
7
7
|
self.config: ComposeConfig = config
|
|
8
|
-
self.controller: BaseController = create_controller(self.config.controller, self.config.components, self.config.workflows, env, daemon)
|
|
8
|
+
self.controller: BaseController = create_controller(self.config.controller, self.config.components, self.config.listeners, self.config.workflows, env, daemon)
|
|
9
9
|
|
|
10
10
|
async def launch_services(self, detach: bool, background: bool = False):
|
|
11
11
|
await self.controller.start(background=background)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
2
|
from mindor.dsl.schema.controller import ControllerConfig
|
|
3
3
|
from mindor.dsl.schema.component import ComponentConfig
|
|
4
|
+
from mindor.dsl.schema.listener import ListenerConfig
|
|
4
5
|
from mindor.dsl.schema.workflow import WorkflowConfig
|
|
5
6
|
from .engine import BaseController, ControllerEngineMap
|
|
6
7
|
|
|
7
|
-
def create_controller(config: ControllerConfig, components: Dict[str, ComponentConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool) -> BaseController:
|
|
8
|
+
def create_controller(config: ControllerConfig, components: Dict[str, ComponentConfig], listeners: List[ListenerConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool) -> BaseController:
|
|
8
9
|
try:
|
|
9
|
-
return ControllerEngineMap[config.type](config, components, workflows, env, daemon)
|
|
10
|
+
return ControllerEngineMap[config.type](config, components, listeners, workflows, env, daemon)
|
|
10
11
|
except KeyError:
|
|
11
12
|
raise ValueError(f"Unsupported controller type: {config.type}")
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
2
|
from dataclasses import dataclass
|
|
4
3
|
|
|
5
4
|
from mindor.dsl.schema.controller import ControllerConfig, ControllerType
|
|
6
5
|
from mindor.dsl.schema.component import ComponentConfig
|
|
6
|
+
from mindor.dsl.schema.listener import ListenerConfig
|
|
7
7
|
from mindor.dsl.schema.workflow import WorkflowConfig
|
|
8
|
+
from mindor.core.services import AsyncService
|
|
8
9
|
from mindor.core.component import BaseComponent, create_component
|
|
10
|
+
from mindor.core.listener import BaseListener, create_listener
|
|
9
11
|
from mindor.core.workflow import Workflow, WorkflowResolver, create_workflow
|
|
10
12
|
from mindor.core.utils.workqueue import WorkQueue
|
|
11
13
|
from mindor.core.utils.expiring import ExpiringDict
|
|
12
14
|
|
|
13
|
-
from threading import
|
|
15
|
+
from threading import Lock
|
|
14
16
|
import asyncio, ulid
|
|
15
17
|
|
|
16
18
|
@dataclass
|
|
@@ -20,54 +22,22 @@ class TaskState:
|
|
|
20
22
|
output: Optional[Any] = None
|
|
21
23
|
error: Optional[Any] = None
|
|
22
24
|
|
|
23
|
-
class BaseController(
|
|
24
|
-
def __init__(self, config: ControllerConfig, components: Dict[str, ComponentConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool):
|
|
25
|
+
class BaseController(AsyncService):
|
|
26
|
+
def __init__(self, config: ControllerConfig, components: Dict[str, ComponentConfig], listeners: List[ListenerConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool):
|
|
27
|
+
super().__init__(daemon)
|
|
28
|
+
|
|
25
29
|
self.config: ControllerConfig = config
|
|
26
30
|
self.components: Dict[str, ComponentConfig] = components
|
|
31
|
+
self.listeners: List[ListenerConfig] = listeners
|
|
27
32
|
self.workflows: Dict[str, WorkflowConfig] = workflows
|
|
28
33
|
self.env: Dict[str, str] = env
|
|
29
|
-
self.daemon: bool = daemon
|
|
30
34
|
self.queue: Optional[WorkQueue] = None
|
|
31
|
-
self.thread: Optional[Thread] = None
|
|
32
|
-
self.thread_loop: Optional[asyncio.AbstractEventLoop] = None
|
|
33
|
-
self.daemon_task: Optional[asyncio.Task] = None
|
|
34
35
|
self.task_states: ExpiringDict[TaskState] = ExpiringDict()
|
|
35
36
|
self.task_states_lock: Lock = Lock()
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
if self.config.max_concurrent_count > 0:
|
|
39
39
|
self.queue = WorkQueue(self.config.max_concurrent_count, self._run_workflow)
|
|
40
40
|
|
|
41
|
-
async def start(self, background: bool = False) -> None:
|
|
42
|
-
if background:
|
|
43
|
-
def _start_in_thread():
|
|
44
|
-
self.thread_loop = asyncio.new_event_loop()
|
|
45
|
-
asyncio.set_event_loop(self.thread_loop)
|
|
46
|
-
self.thread_loop.run_until_complete(self._start())
|
|
47
|
-
|
|
48
|
-
self.thread = Thread(target=_start_in_thread)
|
|
49
|
-
self.thread.start()
|
|
50
|
-
else:
|
|
51
|
-
await self._start()
|
|
52
|
-
|
|
53
|
-
async def stop(self) -> None:
|
|
54
|
-
if self.thread:
|
|
55
|
-
future = asyncio.run_coroutine_threadsafe(self._stop(), self.thread_loop)
|
|
56
|
-
future.result()
|
|
57
|
-
self.thread_loop.close()
|
|
58
|
-
self.thread_loop = None
|
|
59
|
-
self.thread.join()
|
|
60
|
-
self.thread = None
|
|
61
|
-
else:
|
|
62
|
-
await self._stop()
|
|
63
|
-
|
|
64
|
-
async def wait_until_stopped(self) -> None:
|
|
65
|
-
if self.thread:
|
|
66
|
-
self.thread.join()
|
|
67
|
-
|
|
68
|
-
if self.daemon_task:
|
|
69
|
-
await self.daemon_task
|
|
70
|
-
|
|
71
41
|
async def run_workflow(self, workflow_id: Optional[str], input: Dict[str, Any], wait_for_completion: bool = True) -> TaskState:
|
|
72
42
|
task_id = ulid.ulid()
|
|
73
43
|
state = TaskState(task_id=task_id, status="pending")
|
|
@@ -92,25 +62,21 @@ class BaseController(ABC):
|
|
|
92
62
|
if self.queue:
|
|
93
63
|
await self.queue.start()
|
|
94
64
|
|
|
95
|
-
self.started = True
|
|
96
|
-
|
|
97
65
|
if self.daemon:
|
|
98
66
|
await self._start_components()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
else:
|
|
103
|
-
await self._serve()
|
|
67
|
+
await self._start_listeners()
|
|
68
|
+
|
|
69
|
+
await super()._start()
|
|
104
70
|
|
|
105
71
|
async def _stop(self) -> None:
|
|
106
72
|
if self.queue:
|
|
107
73
|
await self.queue.stop()
|
|
108
74
|
|
|
109
75
|
if self.daemon:
|
|
76
|
+
await self._stop_listeners()
|
|
110
77
|
await self._stop_components()
|
|
111
|
-
await self._shutdown()
|
|
112
78
|
|
|
113
|
-
|
|
79
|
+
await super()._stop()
|
|
114
80
|
|
|
115
81
|
async def _start_components(self) -> None:
|
|
116
82
|
await asyncio.gather(*[ component.start() for component in self._create_components() ])
|
|
@@ -118,6 +84,12 @@ class BaseController(ABC):
|
|
|
118
84
|
async def _stop_components(self) -> None:
|
|
119
85
|
await asyncio.gather(*[ component.stop() for component in self._create_components() ])
|
|
120
86
|
|
|
87
|
+
async def _start_listeners(self) -> None:
|
|
88
|
+
await asyncio.gather(*[ listener.start() for listener in self._create_listeners() ])
|
|
89
|
+
|
|
90
|
+
async def _stop_listeners(self) -> None:
|
|
91
|
+
await asyncio.gather(*[ listener.stop() for listener in self._create_listeners() ])
|
|
92
|
+
|
|
121
93
|
async def _run_workflow(self, task_id: str, workflow_id: Optional[str], input: Dict[str, Any]) -> TaskState:
|
|
122
94
|
state = TaskState(task_id=task_id, status="processing")
|
|
123
95
|
with self.task_states_lock:
|
|
@@ -138,15 +110,10 @@ class BaseController(ABC):
|
|
|
138
110
|
def _create_components(self) -> List[BaseComponent]:
|
|
139
111
|
return [ create_component(component_id, config, self.env, self.daemon) for component_id, config in self.components.items() ]
|
|
140
112
|
|
|
113
|
+
def _create_listeners(self) -> List[BaseListener]:
|
|
114
|
+
return [ create_listener(config, self.env, self.daemon) for config in self.listeners ]
|
|
115
|
+
|
|
141
116
|
def _create_workflow(self, workflow_id: Optional[str]) -> Workflow:
|
|
142
117
|
return create_workflow(*WorkflowResolver(self.workflows).resolve(workflow_id), self.components, self.env)
|
|
143
118
|
|
|
144
|
-
@abstractmethod
|
|
145
|
-
async def _serve(self) -> None:
|
|
146
|
-
pass
|
|
147
|
-
|
|
148
|
-
@abstractmethod
|
|
149
|
-
async def _shutdown(self) -> None:
|
|
150
|
-
pass
|
|
151
|
-
|
|
152
119
|
ControllerEngineMap: Dict[ControllerType, Type[BaseController]] = {}
|
{model_compose-0.0.7 → model_compose-0.0.9}/src/mindor/core/controller/engine/http_server.py
RENAMED
|
@@ -3,6 +3,7 @@ from typing_extensions import Self
|
|
|
3
3
|
from pydantic import BaseModel
|
|
4
4
|
|
|
5
5
|
from mindor.dsl.schema.controller import HttpServerControllerConfig
|
|
6
|
+
from mindor.dsl.schema.listener import ListenerConfig
|
|
6
7
|
from mindor.dsl.schema.component import ComponentConfig
|
|
7
8
|
from mindor.dsl.schema.workflow import WorkflowConfig
|
|
8
9
|
from mindor.core.utils.resources import StreamResource
|
|
@@ -35,8 +36,8 @@ class TaskResult(BaseModel):
|
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
class HttpServerController(BaseController):
|
|
38
|
-
def __init__(self, config: HttpServerControllerConfig, components: Dict[str, ComponentConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool):
|
|
39
|
-
super().__init__(config, components, workflows, env, daemon)
|
|
39
|
+
def __init__(self, config: HttpServerControllerConfig, components: Dict[str, ComponentConfig], listeners: List[ListenerConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool):
|
|
40
|
+
super().__init__(config, components, listeners, workflows, env, daemon)
|
|
40
41
|
|
|
41
42
|
self.server: Optional[uvicorn.Server] = None
|
|
42
43
|
self.app: FastAPI = FastAPI()
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
2
|
from mindor.dsl.schema.controller import McpServerControllerConfig
|
|
3
|
+
from mindor.dsl.schema.listener import ListenerConfig
|
|
3
4
|
from mindor.dsl.schema.component import ComponentConfig
|
|
4
5
|
from mindor.dsl.schema.workflow import WorkflowConfig
|
|
5
6
|
from .base import BaseController, ControllerType, ControllerEngineMap
|
|
6
7
|
|
|
7
8
|
class McpServerController(BaseController):
|
|
8
|
-
def __init__(self, config: McpServerControllerConfig, components: Dict[str, ComponentConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool):
|
|
9
|
-
super().__init__(config, components, workflows, env, daemon)
|
|
9
|
+
def __init__(self, config: McpServerControllerConfig, components: Dict[str, ComponentConfig], listeners: List[ListenerConfig], workflows: Dict[str, WorkflowConfig], env: Dict[str, str], daemon: bool):
|
|
10
|
+
super().__init__(config, components, listeners, workflows, env, daemon)
|
|
10
11
|
|
|
11
12
|
async def _serve(self) -> None:
|
|
12
13
|
pass
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from mindor.dsl.schema.gateway import GatewayConfig, GatewayType
|
|
3
|
+
from mindor.core.services import AsyncService
|
|
4
|
+
|
|
5
|
+
class BaseGateway(AsyncService):
|
|
6
|
+
def __init__(self, config: GatewayConfig, env: Dict[str, str], daemon: bool):
|
|
7
|
+
super().__init__(daemon)
|
|
8
|
+
|
|
9
|
+
self.config: GatewayConfig = config
|
|
10
|
+
self.env: Dict[str, str] = env
|
|
11
|
+
|
|
12
|
+
GatewayEngineMap: Dict[GatewayType, Type[BaseGateway]] = {}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable, Iterator, Any
|
|
2
|
+
from mindor.dsl.schema.gateway import HttpTunnelGatewayConfig
|
|
3
|
+
from .base import BaseGateway, GatewayType, GatewayEngineMap
|
|
4
|
+
|
|
5
|
+
class HttpTunnelGateway(BaseGateway):
|
|
6
|
+
def __init__(self, config: HttpTunnelGatewayConfig, env: Dict[str, str], daemon: bool):
|
|
7
|
+
super().__init__(config, env, daemon)
|
|
8
|
+
|
|
9
|
+
GatewayEngineMap[GatewayType.HTTP_TUNNEL] = HttpTunnelGateway
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from mindor.dsl.schema.gateway import GatewayConfig
|
|
3
|
+
from .engine import BaseGateway, GatewayEngineMap
|
|
4
|
+
|
|
5
|
+
def create_gateway(config: GatewayConfig, env: Dict[str, str], daemon: bool) -> BaseGateway:
|
|
6
|
+
try:
|
|
7
|
+
return GatewayEngineMap[config.type](config, env, daemon)
|
|
8
|
+
except KeyError:
|
|
9
|
+
raise ValueError(f"Unsupported gateway type: {config.type}")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
|
|
2
|
+
from mindor.dsl.schema.listener import ListenerConfig, ListenerType
|
|
3
|
+
from mindor.core.services import AsyncService
|
|
4
|
+
from mindor.core.utils.workqueue import WorkQueue
|
|
5
|
+
|
|
6
|
+
class BaseListener(AsyncService):
|
|
7
|
+
def __init__(self, config: ListenerConfig, env: Dict[str, str], daemon: bool):
|
|
8
|
+
super().__init__(daemon)
|
|
9
|
+
|
|
10
|
+
self.config: ListenerConfig = config
|
|
11
|
+
self.env: Dict[str, str] = env
|
|
12
|
+
self.queue: Optional[WorkQueue] = None
|
|
13
|
+
|
|
14
|
+
# if self.config.max_concurrent_count > 0:
|
|
15
|
+
# self.queue = WorkQueue(self.config.max_concurrent_count, self._run_workflow)
|
|
16
|
+
|
|
17
|
+
async def _start(self) -> None:
|
|
18
|
+
if self.queue:
|
|
19
|
+
await self.queue.start()
|
|
20
|
+
|
|
21
|
+
await super()._start()
|
|
22
|
+
|
|
23
|
+
async def _stop(self) -> None:
|
|
24
|
+
if self.queue:
|
|
25
|
+
await self.queue.stop()
|
|
26
|
+
|
|
27
|
+
await super()._stop()
|
|
28
|
+
|
|
29
|
+
ListenerEngineMap: Dict[ListenerType, Type[BaseListener]] = {}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Callable, Iterator, Any
|
|
2
|
+
|
|
3
|
+
from mindor.dsl.schema.listener import HttpCallbackListenerConfig, HttpCallbackConfig
|
|
4
|
+
from mindor.core.utils.http_headers import parse_options_header, get_header_value
|
|
5
|
+
from mindor.core.utils.template import TemplateRenderer
|
|
6
|
+
from .base import BaseListener, ListenerType, ListenerEngineMap
|
|
7
|
+
|
|
8
|
+
from fastapi import FastAPI, APIRouter, Body, HTTPException, Request
|
|
9
|
+
from fastapi.responses import Response, JSONResponse
|
|
10
|
+
from threading import Lock
|
|
11
|
+
import asyncio, uvicorn
|
|
12
|
+
|
|
13
|
+
_pending_futures: Dict[str, asyncio.Future] = {}
|
|
14
|
+
_pending_futures_lock: Lock = Lock()
|
|
15
|
+
|
|
16
|
+
class HttpCallbackContext:
|
|
17
|
+
def __init__(self, body: Optional[Any], query: Optional[Dict[str, str]], bulk: bool, item: Optional[str]):
|
|
18
|
+
self.body: Optional[Any] = body
|
|
19
|
+
self.query: Optional[Dict[str, str]] = query
|
|
20
|
+
self.bulk: bool = bulk
|
|
21
|
+
self.item: Optional[str] = item
|
|
22
|
+
self.renderer: TemplateRenderer = TemplateRenderer(self._resolve_source)
|
|
23
|
+
|
|
24
|
+
def items(self) -> Iterator["HttpCallbackContext"]:
|
|
25
|
+
for item in self._items():
|
|
26
|
+
yield HttpCallbackContext(item, self.query, False, None)
|
|
27
|
+
|
|
28
|
+
def render_template(self, template: str) -> Any:
|
|
29
|
+
return self.renderer.render(template)
|
|
30
|
+
|
|
31
|
+
def _items(self) -> List[Any]:
|
|
32
|
+
item = self.renderer.render(self.item) if self.item else self.body
|
|
33
|
+
|
|
34
|
+
if self.bulk:
|
|
35
|
+
if not isinstance(item, list):
|
|
36
|
+
raise ValueError("Expected a list because 'bulk' is true, but got a non-list.")
|
|
37
|
+
return item
|
|
38
|
+
|
|
39
|
+
return [ item ]
|
|
40
|
+
|
|
41
|
+
def _resolve_source(self, key: str) -> Any:
|
|
42
|
+
if key == "body" or key == "item":
|
|
43
|
+
return self.body
|
|
44
|
+
if key == "query":
|
|
45
|
+
return self.query
|
|
46
|
+
raise KeyError(f"Unknown source: {key}")
|
|
47
|
+
|
|
48
|
+
class HttpCallbackListener(BaseListener):
|
|
49
|
+
def __init__(self, config: HttpCallbackListenerConfig, env: Dict[str, str], daemon: bool):
|
|
50
|
+
super().__init__(config, env, daemon)
|
|
51
|
+
|
|
52
|
+
self.server: Optional[uvicorn.Server] = None
|
|
53
|
+
self.app: FastAPI = FastAPI()
|
|
54
|
+
self.router: APIRouter = APIRouter()
|
|
55
|
+
|
|
56
|
+
self._configure_routes()
|
|
57
|
+
self.app.include_router(self.router, prefix=self.config.base_path)
|
|
58
|
+
|
|
59
|
+
def _configure_routes(self):
|
|
60
|
+
for callback in self.config.callbacks:
|
|
61
|
+
self.router.add_api_route(
|
|
62
|
+
path=callback.path,
|
|
63
|
+
endpoint=self._make_callback_handler(callback),
|
|
64
|
+
methods=[callback.method],
|
|
65
|
+
name=f"callback_{callback.path.strip('/').replace('/', '_')}",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def _make_callback_handler(self, callback: HttpCallbackConfig) -> Callable:
|
|
69
|
+
async def handler(request: Request) -> Response:
|
|
70
|
+
body, query = await self._parse_request_content(request), request.query_params
|
|
71
|
+
context = HttpCallbackContext(body, query, callback.bulk, callback.item)
|
|
72
|
+
|
|
73
|
+
for item in context.items():
|
|
74
|
+
callback_id = item.render_template(callback.identify_by)
|
|
75
|
+
future: asyncio.Future = self._get_pending_future(callback_id)
|
|
76
|
+
|
|
77
|
+
if future:
|
|
78
|
+
future.set_result(item.render_template(callback.result) if callback.result else item.body)
|
|
79
|
+
self._remove_pending_future(callback_id)
|
|
80
|
+
|
|
81
|
+
return Response()
|
|
82
|
+
|
|
83
|
+
return handler
|
|
84
|
+
|
|
85
|
+
async def _parse_request_content(self, request: Request) -> Any:
|
|
86
|
+
content_type, _ = parse_options_header(request.headers.get("Content-Type", ""))
|
|
87
|
+
|
|
88
|
+
if content_type == "application/json":
|
|
89
|
+
return await request.json()
|
|
90
|
+
|
|
91
|
+
if content_type.startswith("text/"):
|
|
92
|
+
return str(await request.body())
|
|
93
|
+
|
|
94
|
+
return await request.body()
|
|
95
|
+
|
|
96
|
+
async def _serve(self) -> None:
|
|
97
|
+
self.server = uvicorn.Server(uvicorn.Config(
|
|
98
|
+
self.app,
|
|
99
|
+
host=self.config.host,
|
|
100
|
+
port=self.config.port,
|
|
101
|
+
log_level="info"
|
|
102
|
+
))
|
|
103
|
+
await self.server.serve()
|
|
104
|
+
|
|
105
|
+
async def _shutdown(self) -> None:
|
|
106
|
+
self.server.should_exit = True
|
|
107
|
+
|
|
108
|
+
def _get_pending_future(self, id: str) -> Optional[asyncio.Future]:
|
|
109
|
+
with _pending_futures_lock:
|
|
110
|
+
return _pending_futures.get(id)
|
|
111
|
+
|
|
112
|
+
def _remove_pending_future(self, id: str) -> None:
|
|
113
|
+
with _pending_futures_lock:
|
|
114
|
+
_pending_futures.pop(id, None)
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def register_pending_future(cls, id: str, future: asyncio.Future) -> None:
|
|
118
|
+
with _pending_futures_lock:
|
|
119
|
+
_pending_futures[id] = future
|
|
120
|
+
|
|
121
|
+
ListenerEngineMap[ListenerType.HTTP_CALLBACK] = HttpCallbackListener
|
|
@@ -2,8 +2,8 @@ from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annot
|
|
|
2
2
|
from mindor.dsl.schema.listener import ListenerConfig
|
|
3
3
|
from .engine import BaseListener, ListenerEngineMap
|
|
4
4
|
|
|
5
|
-
def create_listener(config:
|
|
5
|
+
def create_listener(config: ListenerConfig, env: Dict[str, str], daemon: bool) -> BaseListener:
|
|
6
6
|
try:
|
|
7
|
-
return ListenerEngineMap[config.type](config, env)
|
|
7
|
+
return ListenerEngineMap[config.type](config, env, daemon)
|
|
8
8
|
except KeyError:
|
|
9
9
|
raise ValueError(f"Unsupported listener type: {config.type}")
|