model-compose 0.1.0__tar.gz → 0.1.1__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.0 → model_compose-0.1.1}/PKG-INFO +20 -28
- {model_compose-0.1.0 → model_compose-0.1.1}/README.md +19 -27
- {model_compose-0.1.0 → model_compose-0.1.1}/pyproject.toml +1 -1
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/component/engine/http_client.py +8 -6
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/engine/http_server.py +2 -1
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/webui/gradio.py +11 -2
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/webui/schema.py +5 -5
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/webui/webui.py +2 -1
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/listener/engine/http_callback.py +2 -1
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/template.py +1 -1
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/workflow/job.py +12 -4
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/workflow/workflow.py +2 -2
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/workflow.py +6 -5
- {model_compose-0.1.0 → model_compose-0.1.1}/src/model_compose.egg-info/PKG-INFO +20 -28
- {model_compose-0.1.0 → model_compose-0.1.1}/LICENSE +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/setup.cfg +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/cli/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/cli/compose.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/component/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/component/component.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/component/engine/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/component/engine/base.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/component/engine/context.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/component/engine/http_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/compose/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/compose/compose.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/compose/manager.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/controller.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/engine/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/engine/base.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/engine/mcp_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/runner/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/runner/client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/runner/http_client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/runner/mcp_client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/runner/runner.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/webui/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/gateway/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/gateway/engine/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/gateway/engine/base.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/gateway/engine/http_tunnel.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/gateway/gateway.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/listener/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/listener/engine/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/listener/engine/base.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/listener/listener.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/runtime/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/runtime/env.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/services/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/services/async_service.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/expiring.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/http_client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/http_request.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/image.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/streaming.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/time.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/utils/workqueue.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/workflow/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/workflow/context.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/loader.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/action.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/impl/common.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/impl/http_client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/impl/http_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/impl/mcp_client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/impl/mcp_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/component.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/common.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/http_client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/http_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/mcp_client.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/mcp_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/types.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/compose.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/controller.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/common.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/http_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/mcp_server.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/webui.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/gateway/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/gateway/gateway.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/gateway/impl/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/gateway/impl/common.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/gateway/impl/http_tunnel.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/gateway/impl/types.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/listener/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/listener/impl/common.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/listener/impl/http_callback.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/listener/listener.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/runtime/impl/common.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/runtime/impl/docker.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/runtime/runtime.py +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/model_compose.egg-info/SOURCES.txt +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/model_compose.egg-info/dependency_links.txt +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/model_compose.egg-info/entry_points.txt +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/model_compose.egg-info/requires.txt +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/src/model_compose.egg-info/top_level.txt +0 -0
- {model_compose-0.1.0 → model_compose-0.1.1}/tests/test_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: model-compose
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: model-compose: Declarative AI Model and Workflow Orchestrator (part of the Mindor project)
|
|
5
5
|
Author-email: Hanyeol Cho <hanyeol.cho@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -21,41 +21,28 @@ Requires-Dist: gradio
|
|
|
21
21
|
Requires-Dist: Pillow
|
|
22
22
|
Dynamic: license-file
|
|
23
23
|
|
|
24
|
-
# model-compose: Declarative AI Model and Workflow Orchestrator
|
|
24
|
+
# model-compose: Declarative AI Model and Workflow Orchestrator
|
|
25
25
|
|
|
26
26
|
[](https://python.org)
|
|
27
27
|
[](https://opensource.org/licenses/MIT)
|
|
28
28
|
[](http://makeapullrequest.com)
|
|
29
29
|
|
|
30
|
+

|
|
31
|
+
|
|
30
32
|
**model-compose** is an open-source, declarative workflow orchestrator inspired by `docker-compose`. It lets you define and run AI model pipelines using simple YAML files — no custom code required. Effortlessly connect external AI services or run local AI models, all within powerful, composable workflows.
|
|
31
33
|
|
|
32
34
|
---
|
|
33
35
|
|
|
34
36
|
## ✨ Features
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
#### 🛠️ Multi-Workflow Support
|
|
46
|
-
Define multiple named workflows in one project. Run them by name or set a default for quick execution.
|
|
47
|
-
|
|
48
|
-
#### 🧩 Modular Components
|
|
49
|
-
Break down logic into reusable components and jobs. Easily plug, swap, and extend them across workflows.
|
|
50
|
-
|
|
51
|
-
#### ⚙️ Flexible I/O Routing
|
|
52
|
-
Connect inputs and outputs between jobs using clean, scoped variables - no glue code needed.
|
|
53
|
-
|
|
54
|
-
#### 🧪 Run Locally, Serve Remotely
|
|
55
|
-
Execute workflows from the CLI or expose them as HTTP or MCP endpoints with an optional Web UI.
|
|
56
|
-
|
|
57
|
-
#### 📦 Works with Environment Variables
|
|
58
|
-
Easily inject secrets and configuration via `.env` files or environment variables to keep your YAML clean and secure.
|
|
38
|
+
- **Declarative by Design:** Define complete AI workflows using simple YAML files—no complex scripting required.
|
|
39
|
+
- **Compose Anything:** Combine multiple AI models, APIs, and tools into a single, unified pipeline.
|
|
40
|
+
- **Built for Orchestration:** Orchestrate multi-step model interactions with ease. Transform individual API calls into maintainable, end-to-end systems.
|
|
41
|
+
- **Multi-Workflow Support:** Define multiple named workflows in one project. Run them by name or set a default for quick execution.
|
|
42
|
+
- **Modular Components:** Break down logic into reusable components and jobs. Easily plug, swap, and extend them across workflows.
|
|
43
|
+
- **Flexible I/O Routing:** Connect inputs and outputs between jobs using clean, scoped variables—no glue code needed.
|
|
44
|
+
- **Run Locally, Serve Remotely:** Execute workflows from the CLI or expose them as HTTP or MCP endpoints with an optional Web UI.
|
|
45
|
+
- **Environment Variable Support:** Easily inject secrets and configuration via `.env` files or environment variables to keep your YAML clean and secure.
|
|
59
46
|
|
|
60
47
|
---
|
|
61
48
|
|
|
@@ -214,14 +201,14 @@ listener:
|
|
|
214
201
|
port: 8090
|
|
215
202
|
base_path: /callbacks
|
|
216
203
|
callbacks:
|
|
217
|
-
- path: /
|
|
204
|
+
- path: /chat-ai
|
|
218
205
|
method: POST
|
|
219
206
|
item: ${body.data}
|
|
220
207
|
identify_by: ${item.task_id}
|
|
221
208
|
result: ${item.choices[0].message.content}
|
|
222
209
|
```
|
|
223
210
|
|
|
224
|
-
This listener sets up an HTTP callback endpoint at
|
|
211
|
+
This listener sets up an HTTP callback endpoint at http://localhost:8090/callbacks/chat-ai to handle asynchronous responses from an external service that behaves like ChatGPT but supports delayed or push-based results. This is useful when integrating with services that notify results via webhook-style callbacks.
|
|
225
212
|
|
|
226
213
|
#### 🌐 Gateway Example
|
|
227
214
|
|
|
@@ -234,7 +221,12 @@ gateway:
|
|
|
234
221
|
|
|
235
222
|
This gateway configuration exposes the local listener defined above to the public internet using an HTTP tunnel powered by ngrok. It forwards incoming traffic from a secure, public URL (e.g., https://abc123.ngrok.io) directly to your local callback endpoint at http://localhost:8090. This is essential when integrating with third-party services that need to push data back to your workflow via webhooks or asynchronous callbacks.
|
|
236
223
|
|
|
237
|
-
> 📁 For more example model-compose.yml configurations, check the
|
|
224
|
+
> 📁 For more example model-compose.yml configurations, check the [examples directory](examples) in the source code.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
## 🏗 Architecture
|
|
228
|
+
|
|
229
|
+

|
|
238
230
|
|
|
239
231
|
---
|
|
240
232
|
|
|
@@ -1,38 +1,25 @@
|
|
|
1
|
-
# model-compose: Declarative AI Model and Workflow Orchestrator
|
|
1
|
+
# model-compose: Declarative AI Model and Workflow Orchestrator
|
|
2
2
|
|
|
3
3
|
[](https://python.org)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](http://makeapullrequest.com)
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
**model-compose** is an open-source, declarative workflow orchestrator inspired by `docker-compose`. It lets you define and run AI model pipelines using simple YAML files — no custom code required. Effortlessly connect external AI services or run local AI models, all within powerful, composable workflows.
|
|
8
10
|
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
## ✨ Features
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#### 🛠️ Multi-Workflow Support
|
|
23
|
-
Define multiple named workflows in one project. Run them by name or set a default for quick execution.
|
|
24
|
-
|
|
25
|
-
#### 🧩 Modular Components
|
|
26
|
-
Break down logic into reusable components and jobs. Easily plug, swap, and extend them across workflows.
|
|
27
|
-
|
|
28
|
-
#### ⚙️ Flexible I/O Routing
|
|
29
|
-
Connect inputs and outputs between jobs using clean, scoped variables - no glue code needed.
|
|
30
|
-
|
|
31
|
-
#### 🧪 Run Locally, Serve Remotely
|
|
32
|
-
Execute workflows from the CLI or expose them as HTTP or MCP endpoints with an optional Web UI.
|
|
33
|
-
|
|
34
|
-
#### 📦 Works with Environment Variables
|
|
35
|
-
Easily inject secrets and configuration via `.env` files or environment variables to keep your YAML clean and secure.
|
|
15
|
+
- **Declarative by Design:** Define complete AI workflows using simple YAML files—no complex scripting required.
|
|
16
|
+
- **Compose Anything:** Combine multiple AI models, APIs, and tools into a single, unified pipeline.
|
|
17
|
+
- **Built for Orchestration:** Orchestrate multi-step model interactions with ease. Transform individual API calls into maintainable, end-to-end systems.
|
|
18
|
+
- **Multi-Workflow Support:** Define multiple named workflows in one project. Run them by name or set a default for quick execution.
|
|
19
|
+
- **Modular Components:** Break down logic into reusable components and jobs. Easily plug, swap, and extend them across workflows.
|
|
20
|
+
- **Flexible I/O Routing:** Connect inputs and outputs between jobs using clean, scoped variables—no glue code needed.
|
|
21
|
+
- **Run Locally, Serve Remotely:** Execute workflows from the CLI or expose them as HTTP or MCP endpoints with an optional Web UI.
|
|
22
|
+
- **Environment Variable Support:** Easily inject secrets and configuration via `.env` files or environment variables to keep your YAML clean and secure.
|
|
36
23
|
|
|
37
24
|
---
|
|
38
25
|
|
|
@@ -191,14 +178,14 @@ listener:
|
|
|
191
178
|
port: 8090
|
|
192
179
|
base_path: /callbacks
|
|
193
180
|
callbacks:
|
|
194
|
-
- path: /
|
|
181
|
+
- path: /chat-ai
|
|
195
182
|
method: POST
|
|
196
183
|
item: ${body.data}
|
|
197
184
|
identify_by: ${item.task_id}
|
|
198
185
|
result: ${item.choices[0].message.content}
|
|
199
186
|
```
|
|
200
187
|
|
|
201
|
-
This listener sets up an HTTP callback endpoint at
|
|
188
|
+
This listener sets up an HTTP callback endpoint at http://localhost:8090/callbacks/chat-ai to handle asynchronous responses from an external service that behaves like ChatGPT but supports delayed or push-based results. This is useful when integrating with services that notify results via webhook-style callbacks.
|
|
202
189
|
|
|
203
190
|
#### 🌐 Gateway Example
|
|
204
191
|
|
|
@@ -211,7 +198,12 @@ gateway:
|
|
|
211
198
|
|
|
212
199
|
This gateway configuration exposes the local listener defined above to the public internet using an HTTP tunnel powered by ngrok. It forwards incoming traffic from a secure, public URL (e.g., https://abc123.ngrok.io) directly to your local callback endpoint at http://localhost:8090. This is essential when integrating with third-party services that need to push data back to your workflow via webhooks or asynchronous callbacks.
|
|
213
200
|
|
|
214
|
-
> 📁 For more example model-compose.yml configurations, check the
|
|
201
|
+
> 📁 For more example model-compose.yml configurations, check the [examples directory](examples) in the source code.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
## 🏗 Architecture
|
|
205
|
+
|
|
206
|
+

|
|
215
207
|
|
|
216
208
|
---
|
|
217
209
|
|
|
@@ -10,8 +10,9 @@ from datetime import datetime, timezone
|
|
|
10
10
|
import asyncio
|
|
11
11
|
|
|
12
12
|
class HttpClientPollingCompletion:
|
|
13
|
-
def __init__(self, base_url: Optional[str], config: HttpClientCompletionConfig):
|
|
13
|
+
def __init__(self, base_url: Optional[str], headers: Optional[Dict[str, str]], config: HttpClientCompletionConfig):
|
|
14
14
|
self.base_url: Optional[str] = base_url
|
|
15
|
+
self.headers: Optional[Dict[str, str]] = headers
|
|
15
16
|
self.config: HttpClientCompletionConfig = config
|
|
16
17
|
self.client: HttpClient = HttpClient()
|
|
17
18
|
|
|
@@ -20,7 +21,7 @@ class HttpClientPollingCompletion:
|
|
|
20
21
|
method = await context.render_template(self.config.method)
|
|
21
22
|
params = await context.render_template(self.config.params)
|
|
22
23
|
body = await context.render_template(self.config.body)
|
|
23
|
-
headers = await context.render_template(self.config.headers)
|
|
24
|
+
headers = await context.render_template({ **self.headers, **self.config.headers })
|
|
24
25
|
|
|
25
26
|
interval = parse_duration(self.config.interval) if self.config.interval else 5.0
|
|
26
27
|
timeout = parse_duration(self.config.timeout) if self.config.timeout else 300
|
|
@@ -67,8 +68,9 @@ class HttpClientCallbackCompletion:
|
|
|
67
68
|
return await future
|
|
68
69
|
|
|
69
70
|
class HttpClientAction:
|
|
70
|
-
def __init__(self, base_url: Optional[str], config: HttpClientActionConfig):
|
|
71
|
+
def __init__(self, base_url: Optional[str], headers: Optional[Dict[str, str]], config: HttpClientActionConfig):
|
|
71
72
|
self.base_url: Optional[str] = base_url
|
|
73
|
+
self.headers: Optional[Dict[str, str]] = headers
|
|
72
74
|
self.config: HttpClientActionConfig = config
|
|
73
75
|
self.client: HttpClient = HttpClient()
|
|
74
76
|
|
|
@@ -77,7 +79,7 @@ class HttpClientAction:
|
|
|
77
79
|
method = await context.render_template(self.config.method)
|
|
78
80
|
params = await context.render_template(self.config.params)
|
|
79
81
|
body = await context.render_template(self.config.body)
|
|
80
|
-
headers = await context.render_template(self.config.headers)
|
|
82
|
+
headers = await context.render_template({ **self.headers, **self.config.headers })
|
|
81
83
|
|
|
82
84
|
response, result = await self.client.request(url, method, params, body, headers), None
|
|
83
85
|
|
|
@@ -100,7 +102,7 @@ class HttpClientAction:
|
|
|
100
102
|
|
|
101
103
|
async def _handle_completion(self, completion: HttpClientCompletionConfig, context: ComponentContext) -> Any:
|
|
102
104
|
if completion.type == "polling":
|
|
103
|
-
return await HttpClientPollingCompletion(self.base_url, completion).run(context)
|
|
105
|
+
return await HttpClientPollingCompletion(self.base_url, self.headers, completion).run(context)
|
|
104
106
|
|
|
105
107
|
if completion.type == "callback":
|
|
106
108
|
return await HttpClientCallbackCompletion(completion).run(context)
|
|
@@ -118,6 +120,6 @@ class HttpClientComponent(ComponentEngine):
|
|
|
118
120
|
pass
|
|
119
121
|
|
|
120
122
|
async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
|
|
121
|
-
return await HttpClientAction(self.config.base_url, action).run(context)
|
|
123
|
+
return await HttpClientAction(self.config.base_url, self.config.headers, action).run(context)
|
|
122
124
|
|
|
123
125
|
ComponentEngineMap[ComponentType.HTTP_CLIENT] = HttpClientComponent
|
{model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/controller/engine/http_server.py
RENAMED
|
@@ -109,7 +109,8 @@ class HttpServerController(ControllerEngine):
|
|
|
109
109
|
await self.server.serve()
|
|
110
110
|
|
|
111
111
|
async def _shutdown(self) -> None:
|
|
112
|
-
self.server
|
|
112
|
+
if self.server:
|
|
113
|
+
self.server.should_exit = True
|
|
113
114
|
|
|
114
115
|
async def _parse_workflow_body(self, request: Request) -> WorkflowTaskRequestBody:
|
|
115
116
|
content_type, _ = parse_options_header(request.headers, "Content-Type")
|
|
@@ -5,6 +5,7 @@ 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
7
|
import gradio as gr
|
|
8
|
+
import json
|
|
8
9
|
|
|
9
10
|
class GradioWebUIBuilder:
|
|
10
11
|
def build(self, schema: Dict[str, WorkflowSchema], runner: Callable[[Optional[str], Any], Awaitable[Any]]) -> gr.Blocks:
|
|
@@ -44,8 +45,11 @@ class GradioWebUIBuilder:
|
|
|
44
45
|
|
|
45
46
|
if workflow.output:
|
|
46
47
|
if isinstance(output, dict):
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
outputs = []
|
|
49
|
+
for variable in workflow.output:
|
|
50
|
+
value = output[variable.name] if variable.name else output
|
|
51
|
+
outputs.append(await self._convert_type(value, variable.type, variable.subtype, variable.format))
|
|
52
|
+
output = outputs[0] if len(outputs) == 1 else outputs
|
|
49
53
|
else:
|
|
50
54
|
variable = workflow.output[0]
|
|
51
55
|
output = await self._convert_type(output, variable.type, variable.subtype, variable.format)
|
|
@@ -115,4 +119,9 @@ class GradioWebUIBuilder:
|
|
|
115
119
|
return await save_stream_to_temporary_file(value, subtype)
|
|
116
120
|
return None
|
|
117
121
|
|
|
122
|
+
if type == "string":
|
|
123
|
+
if isinstance(value, (dict, list)):
|
|
124
|
+
return json.dumps(value)
|
|
125
|
+
return str(value)
|
|
126
|
+
|
|
118
127
|
return value
|
|
@@ -26,7 +26,7 @@ class WorkflowVariableResolver:
|
|
|
26
26
|
key, path, type, subtype, format, default = match.group(1, 2, 3, 4, 5, 6)
|
|
27
27
|
|
|
28
28
|
if type and default:
|
|
29
|
-
default = self.
|
|
29
|
+
default = self._parse_as_type(default, type)
|
|
30
30
|
|
|
31
31
|
if key == wanted_key:
|
|
32
32
|
variables.append((path, type or "string", subtype, format, default))
|
|
@@ -52,7 +52,7 @@ class WorkflowVariableResolver:
|
|
|
52
52
|
type, subtype, format, default = match.group(3, 4, 5, 6)
|
|
53
53
|
|
|
54
54
|
if type and default:
|
|
55
|
-
default = self.
|
|
55
|
+
default = self._parse_as_type(default, type)
|
|
56
56
|
|
|
57
57
|
variables.append((key, type or "string", subtype, format, default))
|
|
58
58
|
|
|
@@ -67,12 +67,12 @@ class WorkflowVariableResolver:
|
|
|
67
67
|
if isinstance(value, list):
|
|
68
68
|
return sum([ self._enumerate_output_variables(f"{key}[{i}]" if key else f"[{i}]", v) for i, v in enumerate(value) ], [])
|
|
69
69
|
|
|
70
|
-
return []
|
|
70
|
+
return []
|
|
71
71
|
|
|
72
|
-
def
|
|
72
|
+
def _parse_as_type(self, value: Any, type: str) -> Any:
|
|
73
73
|
if type == "number":
|
|
74
74
|
return float(value)
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
if type == "integer":
|
|
77
77
|
return int(value)
|
|
78
78
|
|
|
@@ -54,7 +54,8 @@ class ControllerWebUI(AsyncService):
|
|
|
54
54
|
await self.server.serve()
|
|
55
55
|
|
|
56
56
|
async def _shutdown(self) -> None:
|
|
57
|
-
self.server
|
|
57
|
+
if self.server:
|
|
58
|
+
self.server.should_exit = True
|
|
58
59
|
|
|
59
60
|
async def _run_workflow(self, workflow_id: Optional[str], input: Any) -> Any:
|
|
60
61
|
return await self.runner.run_workflow(workflow_id, input)
|
{model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/core/listener/engine/http_callback.py
RENAMED
|
@@ -109,7 +109,8 @@ class HttpCallbackListener(ListenerEngine):
|
|
|
109
109
|
await self.server.serve()
|
|
110
110
|
|
|
111
111
|
async def _shutdown(self) -> None:
|
|
112
|
-
self.server
|
|
112
|
+
if self.server:
|
|
113
|
+
self.server.should_exit = True
|
|
113
114
|
|
|
114
115
|
def _get_pending_future(self, id: str) -> Optional[asyncio.Future]:
|
|
115
116
|
with _pending_futures_lock:
|
|
@@ -6,10 +6,10 @@ from .context import WorkflowContext
|
|
|
6
6
|
import ulid
|
|
7
7
|
|
|
8
8
|
class Job:
|
|
9
|
-
def __init__(self, id: str, config: JobConfig, component_provider: Callable[[Union[ComponentConfig, str]], ComponentEngine]):
|
|
9
|
+
def __init__(self, id: str, config: JobConfig, component_provider: Callable[[str, Union[ComponentConfig, str]], ComponentEngine]):
|
|
10
10
|
self.id: str = id
|
|
11
11
|
self.config: JobConfig = config
|
|
12
|
-
self.component_provider: Callable[[Union[ComponentConfig, str]], ComponentEngine] = component_provider
|
|
12
|
+
self.component_provider: Callable[[str, Union[ComponentConfig, str]], ComponentEngine] = component_provider
|
|
13
13
|
|
|
14
14
|
async def run(self, context: WorkflowContext) -> Any:
|
|
15
15
|
component: ComponentEngine = self.component_provider(self.id, self.config.component)
|
|
@@ -17,9 +17,17 @@ class Job:
|
|
|
17
17
|
if not component.started:
|
|
18
18
|
await component.start()
|
|
19
19
|
|
|
20
|
-
call_id = ulid.ulid()
|
|
21
20
|
input = (await context.render_template(self.config.input)) if self.config.input else context.input
|
|
22
|
-
|
|
21
|
+
outputs = []
|
|
22
|
+
|
|
23
|
+
for _ in range(self.config.repeats):
|
|
24
|
+
call_id = ulid.ulid()
|
|
25
|
+
output = await component.run(self.config.action, call_id, input)
|
|
26
|
+
|
|
27
|
+
if output:
|
|
28
|
+
outputs.append(output)
|
|
29
|
+
|
|
30
|
+
output = outputs[0] if len(outputs) == 1 else outputs or None
|
|
23
31
|
|
|
24
32
|
if output:
|
|
25
33
|
context.register_source("output", output)
|
|
@@ -74,9 +74,9 @@ class WorkflowResolver:
|
|
|
74
74
|
return default_ids[0] if default_ids else "__default__"
|
|
75
75
|
|
|
76
76
|
class WorkflowRunner:
|
|
77
|
-
def __init__(self, jobs: Dict[str, JobConfig], component_provider: Callable[[
|
|
77
|
+
def __init__(self, jobs: Dict[str, JobConfig], component_provider: Callable[[str, Union[ComponentConfig, str]], ComponentEngine]):
|
|
78
78
|
self.jobs: Dict[str, JobConfig] = jobs
|
|
79
|
-
self.component_provider: Callable[[
|
|
79
|
+
self.component_provider: Callable[[str, Union[ComponentConfig, str]], ComponentEngine] = component_provider
|
|
80
80
|
|
|
81
81
|
async def run(self, context: WorkflowContext) -> Any:
|
|
82
82
|
pending_jobs: Dict[str, Job] = { job_id: Job(job_id, job, self.component_provider) for job_id, job in self.jobs.items() }
|
|
@@ -5,11 +5,12 @@ from pydantic import model_validator
|
|
|
5
5
|
from .component import ComponentConfig
|
|
6
6
|
|
|
7
7
|
class JobConfig(BaseModel):
|
|
8
|
-
component: Optional[Union[
|
|
9
|
-
action: Optional[str] = "__default__"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
component: Optional[Union[str, 'ComponentConfig']] = Field(default="__default__", description="The component to execute. Can be a string identifier or a ComponentConfig object.")
|
|
9
|
+
action: Optional[str] = Field(default="__default__", description="The action to invoke within the component. Defaults to '__default__'.")
|
|
10
|
+
repeats: Optional[int] = Field(default=1, ge=1, description="Number of times to repeat the component execution. Must be at least 1.")
|
|
11
|
+
input: Optional[Any] = Field(default=None, description="The input data passed to the component. Can be of any type.")
|
|
12
|
+
output: Optional[Any] = Field(default=None, description="The expected output data from the component. Can be of any type.")
|
|
13
|
+
depends_on: Optional[List[str]] = Field(default_factory=list, description="List of job names that this job depends on. Ensures execution order.")
|
|
13
14
|
|
|
14
15
|
class WorkflowVariableType(str, Enum):
|
|
15
16
|
# Primitive data types
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: model-compose
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: model-compose: Declarative AI Model and Workflow Orchestrator (part of the Mindor project)
|
|
5
5
|
Author-email: Hanyeol Cho <hanyeol.cho@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -21,41 +21,28 @@ Requires-Dist: gradio
|
|
|
21
21
|
Requires-Dist: Pillow
|
|
22
22
|
Dynamic: license-file
|
|
23
23
|
|
|
24
|
-
# model-compose: Declarative AI Model and Workflow Orchestrator
|
|
24
|
+
# model-compose: Declarative AI Model and Workflow Orchestrator
|
|
25
25
|
|
|
26
26
|
[](https://python.org)
|
|
27
27
|
[](https://opensource.org/licenses/MIT)
|
|
28
28
|
[](http://makeapullrequest.com)
|
|
29
29
|
|
|
30
|
+

|
|
31
|
+
|
|
30
32
|
**model-compose** is an open-source, declarative workflow orchestrator inspired by `docker-compose`. It lets you define and run AI model pipelines using simple YAML files — no custom code required. Effortlessly connect external AI services or run local AI models, all within powerful, composable workflows.
|
|
31
33
|
|
|
32
34
|
---
|
|
33
35
|
|
|
34
36
|
## ✨ Features
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
#### 🛠️ Multi-Workflow Support
|
|
46
|
-
Define multiple named workflows in one project. Run them by name or set a default for quick execution.
|
|
47
|
-
|
|
48
|
-
#### 🧩 Modular Components
|
|
49
|
-
Break down logic into reusable components and jobs. Easily plug, swap, and extend them across workflows.
|
|
50
|
-
|
|
51
|
-
#### ⚙️ Flexible I/O Routing
|
|
52
|
-
Connect inputs and outputs between jobs using clean, scoped variables - no glue code needed.
|
|
53
|
-
|
|
54
|
-
#### 🧪 Run Locally, Serve Remotely
|
|
55
|
-
Execute workflows from the CLI or expose them as HTTP or MCP endpoints with an optional Web UI.
|
|
56
|
-
|
|
57
|
-
#### 📦 Works with Environment Variables
|
|
58
|
-
Easily inject secrets and configuration via `.env` files or environment variables to keep your YAML clean and secure.
|
|
38
|
+
- **Declarative by Design:** Define complete AI workflows using simple YAML files—no complex scripting required.
|
|
39
|
+
- **Compose Anything:** Combine multiple AI models, APIs, and tools into a single, unified pipeline.
|
|
40
|
+
- **Built for Orchestration:** Orchestrate multi-step model interactions with ease. Transform individual API calls into maintainable, end-to-end systems.
|
|
41
|
+
- **Multi-Workflow Support:** Define multiple named workflows in one project. Run them by name or set a default for quick execution.
|
|
42
|
+
- **Modular Components:** Break down logic into reusable components and jobs. Easily plug, swap, and extend them across workflows.
|
|
43
|
+
- **Flexible I/O Routing:** Connect inputs and outputs between jobs using clean, scoped variables—no glue code needed.
|
|
44
|
+
- **Run Locally, Serve Remotely:** Execute workflows from the CLI or expose them as HTTP or MCP endpoints with an optional Web UI.
|
|
45
|
+
- **Environment Variable Support:** Easily inject secrets and configuration via `.env` files or environment variables to keep your YAML clean and secure.
|
|
59
46
|
|
|
60
47
|
---
|
|
61
48
|
|
|
@@ -214,14 +201,14 @@ listener:
|
|
|
214
201
|
port: 8090
|
|
215
202
|
base_path: /callbacks
|
|
216
203
|
callbacks:
|
|
217
|
-
- path: /
|
|
204
|
+
- path: /chat-ai
|
|
218
205
|
method: POST
|
|
219
206
|
item: ${body.data}
|
|
220
207
|
identify_by: ${item.task_id}
|
|
221
208
|
result: ${item.choices[0].message.content}
|
|
222
209
|
```
|
|
223
210
|
|
|
224
|
-
This listener sets up an HTTP callback endpoint at
|
|
211
|
+
This listener sets up an HTTP callback endpoint at http://localhost:8090/callbacks/chat-ai to handle asynchronous responses from an external service that behaves like ChatGPT but supports delayed or push-based results. This is useful when integrating with services that notify results via webhook-style callbacks.
|
|
225
212
|
|
|
226
213
|
#### 🌐 Gateway Example
|
|
227
214
|
|
|
@@ -234,7 +221,12 @@ gateway:
|
|
|
234
221
|
|
|
235
222
|
This gateway configuration exposes the local listener defined above to the public internet using an HTTP tunnel powered by ngrok. It forwards incoming traffic from a secure, public URL (e.g., https://abc123.ngrok.io) directly to your local callback endpoint at http://localhost:8090. This is essential when integrating with third-party services that need to push data back to your workflow via webhooks or asynchronous callbacks.
|
|
236
223
|
|
|
237
|
-
> 📁 For more example model-compose.yml configurations, check the
|
|
224
|
+
> 📁 For more example model-compose.yml configurations, check the [examples directory](examples) in the source code.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
## 🏗 Architecture
|
|
228
|
+
|
|
229
|
+

|
|
238
230
|
|
|
239
231
|
---
|
|
240
232
|
|
|
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.0 → model_compose-0.1.1}/src/mindor/core/controller/runner/http_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/action/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/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.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/http_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/component/impl/mcp_client.py
RENAMED
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/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.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/src/mindor/dsl/schema/controller/impl/http_server.py
RENAMED
|
File without changes
|
{model_compose-0.1.0 → model_compose-0.1.1}/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.0 → model_compose-0.1.1}/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.0 → model_compose-0.1.1}/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
|