model-compose 0.0.5__tar.gz → 0.0.7__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.
Files changed (90) hide show
  1. {model_compose-0.0.5/src/model_compose.egg-info → model_compose-0.0.7}/PKG-INFO +1 -1
  2. {model_compose-0.0.5 → model_compose-0.0.7}/pyproject.toml +1 -1
  3. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/component/engine/__init__.py +1 -1
  4. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/component/engine/base.py +1 -1
  5. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/component/engine/http_client.py +55 -25
  6. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/component/engine/http_server.py +1 -1
  7. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/controller/engine/http_server.py +53 -18
  8. model_compose-0.0.7/src/mindor/core/utils/resources.py +28 -0
  9. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/workflow/job.py +6 -6
  10. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/workflow/workflow.py +11 -10
  11. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/impl/http_client.py +3 -3
  12. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/impl/http_server.py +1 -1
  13. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/impl/mcp_client.py +1 -1
  14. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/impl/mcp_server.py +2 -2
  15. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/component.py +1 -1
  16. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/impl/common.py +1 -1
  17. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/impl/http_client.py +2 -2
  18. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/impl/http_server.py +1 -1
  19. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/impl/mcp_client.py +2 -2
  20. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/impl/mcp_server.py +1 -1
  21. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/controller/controller.py +1 -1
  22. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/controller/impl/http_server.py +1 -1
  23. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/controller/impl/mcp_server.py +1 -1
  24. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/listener/impl/http_callback.py +1 -1
  25. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/listener/listener.py +1 -1
  26. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/runtime/impl/docker.py +1 -1
  27. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/runtime/runtime.py +1 -1
  28. {model_compose-0.0.5 → model_compose-0.0.7/src/model_compose.egg-info}/PKG-INFO +1 -1
  29. {model_compose-0.0.5 → model_compose-0.0.7}/src/model_compose.egg-info/SOURCES.txt +1 -0
  30. {model_compose-0.0.5 → model_compose-0.0.7}/LICENSE +0 -0
  31. {model_compose-0.0.5 → model_compose-0.0.7}/README.md +0 -0
  32. {model_compose-0.0.5 → model_compose-0.0.7}/setup.cfg +0 -0
  33. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/__init__.py +0 -0
  34. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/cli/__init__.py +0 -0
  35. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/cli/compose.py +0 -0
  36. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/__init__.py +0 -0
  37. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/component/__init__.py +0 -0
  38. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/component/component.py +0 -0
  39. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/component/engine/context.py +0 -0
  40. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/compose/__init__.py +0 -0
  41. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/compose/compose.py +0 -0
  42. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/compose/manager.py +0 -0
  43. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/controller/__init__.py +0 -0
  44. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/controller/controller.py +0 -0
  45. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/controller/engine/__init__.py +0 -0
  46. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/controller/engine/base.py +0 -0
  47. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/controller/engine/mcp_server.py +0 -0
  48. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/listener/__init__.py +0 -0
  49. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/listener/engine/__init__.py +0 -0
  50. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/listener/engine/base.py +0 -0
  51. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/listener/engine/http_callback.py +0 -0
  52. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/listener/listener.py +0 -0
  53. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/runtime/__init__.py +0 -0
  54. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/runtime/env.py +0 -0
  55. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/utils/__init__.py +0 -0
  56. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/utils/expiring.py +0 -0
  57. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/utils/http_headers.py +0 -0
  58. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/utils/template.py +0 -0
  59. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/utils/workqueue.py +0 -0
  60. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/workflow/__init__.py +0 -0
  61. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/core/workflow/context.py +0 -0
  62. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/__init__.py +0 -0
  63. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/loader.py +0 -0
  64. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/__init__.py +0 -0
  65. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/__init__.py +0 -0
  66. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/action.py +0 -0
  67. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
  68. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/action/impl/common.py +0 -0
  69. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/__init__.py +0 -0
  70. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
  71. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/component/impl/types.py +0 -0
  72. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/compose.py +0 -0
  73. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/controller/__init__.py +0 -0
  74. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
  75. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/controller/impl/common.py +0 -0
  76. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
  77. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/listener/__init__.py +0 -0
  78. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
  79. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/listener/impl/common.py +0 -0
  80. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
  81. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
  82. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
  83. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/runtime/impl/common.py +0 -0
  84. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
  85. {model_compose-0.0.5 → model_compose-0.0.7}/src/mindor/dsl/schema/workflow.py +0 -0
  86. {model_compose-0.0.5 → model_compose-0.0.7}/src/model_compose.egg-info/dependency_links.txt +0 -0
  87. {model_compose-0.0.5 → model_compose-0.0.7}/src/model_compose.egg-info/entry_points.txt +0 -0
  88. {model_compose-0.0.5 → model_compose-0.0.7}/src/model_compose.egg-info/requires.txt +0 -0
  89. {model_compose-0.0.5 → model_compose-0.0.7}/src/model_compose.egg-info/top_level.txt +0 -0
  90. {model_compose-0.0.5 → model_compose-0.0.7}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: model-compose
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: model-compose: Declarative AI Model and Workflow Orchestrator (from Mindor)
5
5
  Author-email: Hanyeol Cho <hanyeol.cho@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "model-compose"
3
- version = "0.0.5"
3
+ version = "0.0.7"
4
4
  description = "model-compose: Declarative AI Model and Workflow Orchestrator (from Mindor)"
5
5
  authors = [
6
6
  { name = "Hanyeol Cho", email = "hanyeol.cho@gmail.com" }
@@ -1,4 +1,4 @@
1
1
  from .base import *
2
+ from .stream import *
2
3
  from .http_server import *
3
4
  from .http_client import *
4
-
@@ -117,7 +117,7 @@ class BaseComponent(ABC):
117
117
  pass
118
118
 
119
119
  @abstractmethod
120
- async def _run(self, action: ActionConfig, context: ComponentContext) -> Dict[str, Any]:
120
+ async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
121
121
  pass
122
122
 
123
123
  ComponentEngineMap: Dict[ComponentType, Type[BaseComponent]] = {}
@@ -1,18 +1,42 @@
1
- from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from mindor.dsl.schema.component import HttpClientComponentConfig, HttpClientActionConfig, HttpUrl
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
3
  from mindor.core.utils.http_headers import parse_options_header, get_header_value
4
+ from mindor.core.utils.resources import StreamResource
4
5
  from .base import BaseComponent, ComponentType, ComponentEngineMap, ActionConfig
5
6
  from .context import ComponentContext
6
7
 
7
8
  from urllib.parse import urlencode
8
9
  import aiohttp, json
9
10
 
11
+ class HttpStreamResource(StreamResource):
12
+ def __init__(self, session: aiohttp.ClientSession, stream: aiohttp.StreamReader, content_type: Optional[str], filename: 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
+
10
34
  class HttpClientAction:
11
- def __init__(self, base_url: Union[str, None], config: HttpClientActionConfig):
12
- self.base_url: Union[HttpUrl, None] = base_url
35
+ def __init__(self, base_url: Optional[str], config: HttpClientActionConfig):
36
+ self.base_url: Optional[str] = base_url
13
37
  self.config: HttpClientActionConfig = config
14
38
 
15
- async def run(self, context: ComponentContext) -> Dict[str, Any]:
39
+ async def run(self, context: ComponentContext) -> Any:
16
40
  url = self._resolve_request_url(context)
17
41
  method = context.render_template(self.config.method)
18
42
  params = context.render_template(self.config.params)
@@ -21,32 +45,41 @@ class HttpClientAction:
21
45
 
22
46
  response = await self._request(url, method, params, body, headers)
23
47
 
24
- if not self.config.output:
25
- return response
26
-
27
48
  if response:
28
49
  context.register_source("response", response)
29
50
 
30
- return context.render_template(self.config.output)
51
+ return context.render_template(self.config.output) if self.config.output else response
31
52
 
32
53
  async def _request(self, url: str, method: str, params: Optional[Dict[str, Any]], body: Optional[Any], headers: Optional[Dict[str, str]]) -> Any:
33
- async with aiohttp.ClientSession() as session:
34
- async with session.request(
54
+ session = aiohttp.ClientSession()
55
+ try:
56
+ response = await session.request(
35
57
  method,
36
58
  url,
37
59
  params=params,
38
60
  data=self._serialize_request_body(body, headers) if body is not None else None,
39
61
  headers=headers
40
- ) as response:
41
- if response.status >= 400:
42
- raise ValueError(f"Request failed with status {response.status}")
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)
43
68
 
44
- return await self._deserialize_response_body(response)
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
45
78
 
46
79
  def _resolve_request_url(self, context: ComponentContext) -> str:
47
80
  if self.base_url and self.config.path:
48
81
  return context.render_template(str(self.base_url)) + context.render_template(self.config.path)
49
-
82
+
50
83
  return context.render_template(str(self.config.endpoint))
51
84
 
52
85
  def _serialize_request_body(self, body: Any, headers: Optional[Dict[str, str]]) -> Any:
@@ -60,22 +93,19 @@ class HttpClientAction:
60
93
 
61
94
  return json.dumps(body)
62
95
 
63
- async def _deserialize_response_body(self, response: aiohttp.ClientResponse) -> Any:
96
+ async def _parse_response_content(self, session: aiohttp.ClientSession, response: aiohttp.ClientResponse) -> Any:
64
97
  content_type, _ = parse_options_header(response.headers.get("Content-Type", ""))
65
98
 
66
99
  if content_type == "application/json":
67
100
  return await response.json()
68
-
101
+
69
102
  if content_type.startswith("text/"):
70
103
  return await response.text()
71
104
 
72
- _, disposition = parse_options_header(response.headers.get("Content-Disposition"))
105
+ _, disposition = parse_options_header(response.headers.get("Content-Disposition", ""))
106
+ filename = disposition.get("filename")
73
107
 
74
- return {
75
- "filename": disposition.get("filename"),
76
- "content_type": content_type,
77
- "stream": response.content
78
- }
108
+ return HttpStreamResource(session, response.content, content_type, filename)
79
109
 
80
110
  class HttpClientComponent(BaseComponent):
81
111
  def __init__(self, id: str, config: HttpClientComponentConfig, env: Dict[str, str], daemon: bool):
@@ -87,7 +117,7 @@ class HttpClientComponent(BaseComponent):
87
117
  async def _shutdown(self) -> None:
88
118
  pass
89
119
 
90
- async def _run(self, action: ActionConfig, context: ComponentContext) -> Dict[str, Any]:
120
+ async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
91
121
  return await HttpClientAction(self.config.base_url, action).run(context)
92
122
 
93
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) -> Dict[str, Any]:
16
+ async def _run(self, action: ActionConfig, context: ComponentContext) -> Any:
17
17
  return {}
18
18
 
19
19
  ComponentEngineMap[ComponentType.HTTP_SERVER] = HttpServerComponent
@@ -5,15 +5,19 @@ from pydantic import BaseModel
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 uvicorn
12
+ from fastapi.responses import Response, 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
- input: Optional[Dict[str, Any]] = None
18
+ input: Optional[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,35 +46,31 @@ 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", response_model=TaskResult, response_model_exclude_none=True)
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
- return TaskResult.from_instance(state)
55
+ if body.output_only and body.wait_for_completion:
56
+ return self._render_task_output(state)
57
+
58
+ return self._render_task_state(state)
52
59
 
53
- @self.router.get("/tasks/{task_id}", response_model=TaskResult, response_model_exclude_none=True)
60
+ @self.router.get("/tasks/{task_id}")
54
61
  async def get_task_state(
55
- task_id: str
62
+ task_id: str,
63
+ output_only: bool = False
56
64
  ):
57
65
  state = self.get_task_state(task_id)
58
66
 
59
67
  if not state:
60
- raise HTTPException(status_code=404, detail="Task not found")
68
+ raise HTTPException(status_code=404, detail="Task not found.")
61
69
 
62
- return TaskResult.from_instance(state)
63
-
64
- @self.router.get("/tasks/{task_id}/output")
65
- async def get_task_output(
66
- task_id: str
67
- ):
68
- state = self.get_task_state(task_id)
70
+ if output_only:
71
+ return self._render_task_output(state)
69
72
 
70
- if not state:
71
- raise HTTPException(status_code=404, detail="Task not found")
72
-
73
- return state.output
73
+ return self._render_task_state(state)
74
74
 
75
75
  async def _serve(self) -> None:
76
76
  self.server = uvicorn.Server(uvicorn.Config(
@@ -84,4 +84,39 @@ class HttpServerController(BaseController):
84
84
  async def _shutdown(self) -> None:
85
85
  self.server.should_exit = True
86
86
 
87
+ def _render_task_output(self, state: TaskState) -> Response:
88
+ if state.status in [ "pending", "processing" ]:
89
+ raise HTTPException(status_code=202, detail="Task is still in progress.")
90
+
91
+ if state.status == "failed":
92
+ raise HTTPException(status_code=500, detail=str(state.error))
93
+
94
+ if isinstance(state.output, StreamResource):
95
+ return self._render_stream_resource(state.output)
96
+
97
+ return JSONResponse(content=state.output)
98
+
99
+ def _render_task_state(self, state: TaskState) -> Response:
100
+ return JSONResponse(content=TaskResult.from_instance(state).model_dump(exclude_none=True))
101
+
102
+ def _render_stream_resource(self, resource: StreamResource) -> Response:
103
+ def _close_stream():
104
+ asyncio.run(resource.close())
105
+
106
+ return StreamingResponse(
107
+ resource,
108
+ media_type=resource.content_type,
109
+ headers=self._build_stream_resource_headers(resource),
110
+ background=BackgroundTask(_close_stream)
111
+ )
112
+
113
+ def _build_stream_resource_headers(self, resource: StreamResource) -> Dict[str, str]:
114
+ headers: Dict[str, str] = { "Cache-Control": "no-cache" }
115
+
116
+ if resource.filename:
117
+ filename = resource.filename.replace('"', '\\"')
118
+ headers["Content-Disposition"] = f'attachment; filename="{filename}"'
119
+
120
+ return headers
121
+
87
122
  ControllerEngineMap[ControllerType.HTTP_SERVER] = HttpServerController
@@ -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
@@ -11,17 +11,17 @@ class Job:
11
11
  self.config: JobConfig = config
12
12
  self.component_provider: Callable[[Union[ComponentConfig, str]], BaseComponent] = component_provider
13
13
 
14
- async def run(self, context: WorkflowContext) -> Dict[str, Any]:
15
- component = self.component_provider(self.config.component)
14
+ async def run(self, context: WorkflowContext) -> Any:
15
+ component = self.component_provider(self.id, self.config.component)
16
16
 
17
17
  if not component.started:
18
18
  await component.start()
19
19
 
20
20
  call_id = ulid.ulid()
21
- input = context.render_template(self.config.input)
21
+ input = context.render_template(self.config.input) if self.config.input else context.input
22
22
  output = await component.run(self.config.action, call_id, input)
23
23
 
24
- context.register_source("output", output)
25
- output = context.render_template(self.config.output)
24
+ if output:
25
+ context.register_source("output", output)
26
26
 
27
- return output
27
+ return context.render_template(self.config.output) if self.config.output else output
@@ -76,16 +76,16 @@ class WorkflowResolver:
76
76
  return default_ids[0] if default_ids else "__default__"
77
77
 
78
78
  class WorkflowRunner:
79
- def __init__(self, jobs: Dict[str, JobConfig], component_provider: Callable[[Union[ComponentConfig, str]], BaseComponent]):
79
+ def __init__(self, jobs: Dict[str, JobConfig], component_provider: Callable[[Union[str, ComponentConfig, str]], BaseComponent]):
80
80
  self.jobs: Dict[str, JobConfig] = jobs
81
- self.component_provider: Callable[[Union[ComponentConfig, str]], BaseComponent] = component_provider
81
+ self.component_provider: Callable[[Union[str, ComponentConfig, str]], BaseComponent] = component_provider
82
82
 
83
- async def run(self, context: WorkflowContext) -> Dict[str, Any]:
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: Dict[str, Any] = {}
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.update(completed_job_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,7 +130,7 @@ 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, task_id: str, input: Dict[str, Any]) -> Dict[str, Any]:
133
+ async def run(self, task_id: str, input: Dict[str, Any]) -> Any:
133
134
  context = WorkflowContext(task_id, input, self.env)
134
135
  runner = WorkflowRunner(self.config.jobs, self._create_component)
135
136
 
@@ -143,14 +144,14 @@ class Workflow:
143
144
  def validate(self) -> None:
144
145
  JobGraphValidator(self.config.jobs).validate()
145
146
 
146
- def _create_component(self, component: Union[ComponentConfig, str]) -> BaseComponent:
147
- return create_component(*self._resolve_component(component), env=self.env, daemon=False)
147
+ def _create_component(self, id: str, component: Union[ComponentConfig, str]) -> BaseComponent:
148
+ return create_component(*self._resolve_component(id, component), env=self.env, daemon=False)
148
149
 
149
- def _resolve_component(self, component: Union[ComponentConfig, str]) -> Tuple[str, ComponentConfig]:
150
+ def _resolve_component(self, id: str, component: Union[ComponentConfig, str]) -> Tuple[str, ComponentConfig]:
150
151
  if isinstance(component, str):
151
152
  return ComponentResolver(self.components).resolve(component)
152
153
 
153
- return self.id, component
154
+ return id, component
154
155
 
155
156
  def create_workflow(id: str, config: WorkflowConfig, components: Dict[str, ComponentConfig], env: Dict[str, str]) -> Workflow:
156
157
  return Workflow(id, config, components, env)
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from .common import CommonActionConfig
5
5
 
@@ -12,7 +12,7 @@ class CallbackCompletionConfig(CommonCompletionConfig):
12
12
 
13
13
  class PollingCompletionConfig(CommonCompletionConfig):
14
14
  type: Literal["polling"]
15
- endpoint: Optional[HttpUrl] = None
15
+ endpoint: Optional[str] = None
16
16
  path: Optional[str] = None
17
17
  method: Literal[ "GET", "POST", "PUT", "DELETE", "PATCH" ] = "GET"
18
18
  headers: Optional[Dict[str, str]] = Field(default_factory=dict)
@@ -36,7 +36,7 @@ HttpClientCompletionConfig = Annotated[
36
36
  ]
37
37
 
38
38
  class HttpClientActionConfig(CommonActionConfig):
39
- endpoint: Optional[HttpUrl] = None
39
+ endpoint: Optional[str] = None
40
40
  path: Optional[str] = None
41
41
  method: Literal[ "GET", "POST", "PUT", "DELETE", "PATCH" ] = "POST"
42
42
  headers: Optional[Dict[str, str]] = Field(default_factory=dict)
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from .common import CommonActionConfig
5
5
 
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from .common import CommonActionConfig
5
5
 
@@ -1,8 +1,8 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
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__(self)
8
+ super().__init__()
@@ -1,5 +1,5 @@
1
- from pydantic import BaseModel, Field, HttpUrl
2
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
+ from pydantic import BaseModel, Field
3
3
  from .impl import *
4
4
 
5
5
  ComponentConfig = Annotated[
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from .types import ComponentType
4
4
 
5
5
  class CommonComponentConfig(BaseModel):
@@ -1,12 +1,12 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
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[HttpUrl] = None
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
 
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from mindor.dsl.schema.action import HttpServerActionConfig
5
5
  from .common import ComponentType, CommonComponentConfig
@@ -1,10 +1,10 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
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: HttpUrl
9
+ endpoint: Optional[str] = None
10
10
  actions: Optional[Dict[str, McpClientActionConfig]] = Field(default_factory=dict)
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from mindor.dsl.schema.action import McpServerActionConfig
5
5
  from .common import ComponentType, CommonComponentConfig
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from .impl import *
4
4
 
5
5
  ControllerConfig = Annotated[
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from .common import ControllerType, CommonControllerConfig
5
5
 
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from .common import ControllerType, CommonControllerConfig
5
5
 
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from .common import ListenerType, CommonListenerConfig
5
5
 
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from .impl import *
4
4
 
5
5
  ListenerConfig = Annotated[
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from pydantic import model_validator
4
4
  from .common import RuntimeType, CommonRumtimeConfig
5
5
 
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from pydantic import BaseModel, Field, HttpUrl
2
+ from pydantic import BaseModel, Field
3
3
  from .impl import *
4
4
 
5
5
  RuntimeConfig = Annotated[
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: model-compose
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: model-compose: Declarative AI Model and Workflow Orchestrator (from Mindor)
5
5
  Author-email: Hanyeol Cho <hanyeol.cho@gmail.com>
6
6
  License-Expression: MIT
@@ -31,6 +31,7 @@ src/mindor/core/runtime/env.py
31
31
  src/mindor/core/utils/__init__.py
32
32
  src/mindor/core/utils/expiring.py
33
33
  src/mindor/core/utils/http_headers.py
34
+ src/mindor/core/utils/resources.py
34
35
  src/mindor/core/utils/template.py
35
36
  src/mindor/core/utils/workqueue.py
36
37
  src/mindor/core/workflow/__init__.py
File without changes
File without changes
File without changes