model-compose 0.3.9__tar.gz → 0.3.10__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 (226) hide show
  1. {model_compose-0.3.9 → model_compose-0.3.10}/PKG-INFO +1 -1
  2. {model_compose-0.3.9 → model_compose-0.3.10}/pyproject.toml +1 -1
  3. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/context.py +9 -3
  4. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/base.py +35 -12
  5. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/tasks/__init__.py +1 -0
  6. model_compose-0.3.10/src/mindor/core/component/services/model/tasks/image_to_text.py +155 -0
  7. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/tasks/summarization.py +12 -10
  8. model_compose-0.3.10/src/mindor/core/component/services/model/tasks/text_classification.py +122 -0
  9. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/tasks/text_embedding.py +56 -22
  10. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/tasks/text_generation.py +12 -10
  11. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/tasks/translation.py +12 -10
  12. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runner/http_client.py +2 -1
  13. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runner/mcp_client.py +1 -15
  14. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/services/http_server.py +22 -27
  15. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/services/mcp_server.py +4 -2
  16. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/webui/gradio.py +5 -5
  17. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/webui/webui.py +6 -4
  18. model_compose-0.3.10/src/mindor/core/gateway/services/http_tunnel.py +82 -0
  19. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/listener/services/http_callback.py +4 -2
  20. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/http_client.py +3 -3
  21. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/image.py +1 -1
  22. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/renderers.py +44 -9
  23. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/streaming.py +9 -0
  24. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/context.py +6 -3
  25. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/schema.py +1 -1
  26. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/action.py +1 -1
  27. {model_compose-0.3.9/src/mindor/dsl/schema/component → model_compose-0.3.10/src/mindor/dsl/schema/action}/impl/model/impl/__init__.py +1 -0
  28. model_compose-0.3.10/src/mindor/dsl/schema/action/impl/model/impl/image_to_text.py +12 -0
  29. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/model.py +1 -0
  30. {model_compose-0.3.9/src/mindor/dsl/schema/action → model_compose-0.3.10/src/mindor/dsl/schema/component}/impl/model/impl/__init__.py +1 -0
  31. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/impl/common.py +0 -8
  32. model_compose-0.3.10/src/mindor/dsl/schema/component/impl/model/impl/image_to_text.py +27 -0
  33. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/impl/summarization.py +8 -0
  34. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/impl/text_classification.py +8 -0
  35. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/impl/text_embedding.py +8 -0
  36. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/impl/text_generation.py +8 -0
  37. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/impl/translation.py +8 -0
  38. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/impl/types.py +1 -0
  39. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/model.py +1 -0
  40. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/text_splitter.py +1 -1
  41. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/workflow.py +1 -1
  42. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/webui/webui.py +0 -1
  43. model_compose-0.3.10/src/mindor/dsl/schema/gateway/impl/http_tunnel/__init__.py +1 -0
  44. model_compose-0.3.10/src/mindor/dsl/schema/gateway/impl/http_tunnel/http_tunnel.py +11 -0
  45. model_compose-0.3.10/src/mindor/dsl/schema/gateway/impl/http_tunnel/impl/__init__.py +3 -0
  46. model_compose-0.3.10/src/mindor/dsl/schema/gateway/impl/http_tunnel/impl/cloudflare.py +7 -0
  47. model_compose-0.3.9/src/mindor/dsl/schema/gateway/impl/http_tunnel.py → model_compose-0.3.10/src/mindor/dsl/schema/gateway/impl/http_tunnel/impl/common.py +5 -4
  48. model_compose-0.3.10/src/mindor/dsl/schema/gateway/impl/http_tunnel/impl/ngrok.py +7 -0
  49. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/gateway/impl/ssh_tunnel.py +2 -0
  50. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/listener/impl/http_callback.py +4 -4
  51. model_compose-0.3.10/src/mindor/dsl/schema/transport/ssh.py +33 -0
  52. model_compose-0.3.10/src/mindor/dsl/utils/__init__.py +0 -0
  53. {model_compose-0.3.9 → model_compose-0.3.10}/src/model_compose.egg-info/PKG-INFO +1 -1
  54. {model_compose-0.3.9 → model_compose-0.3.10}/src/model_compose.egg-info/SOURCES.txt +11 -1
  55. model_compose-0.3.9/src/mindor/core/component/services/model/tasks/text_classification.py +0 -90
  56. model_compose-0.3.9/src/mindor/core/gateway/services/http_tunnel.py +0 -32
  57. {model_compose-0.3.9 → model_compose-0.3.10}/LICENSE +0 -0
  58. {model_compose-0.3.9 → model_compose-0.3.10}/README.md +0 -0
  59. {model_compose-0.3.9 → model_compose-0.3.10}/setup.cfg +0 -0
  60. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/__init__.py +0 -0
  61. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/cli/__init__.py +0 -0
  62. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/cli/compose.py +0 -0
  63. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/__init__.py +0 -0
  64. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/__init__.py +0 -0
  65. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/base.py +0 -0
  66. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/component.py +0 -0
  67. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/__init__.py +0 -0
  68. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/http_client.py +0 -0
  69. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/http_server.py +0 -0
  70. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/mcp_client.py +0 -0
  71. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/mcp_server.py +0 -0
  72. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/__init__.py +0 -0
  73. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/model/model.py +0 -0
  74. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/shell.py +0 -0
  75. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/text_splitter.py +0 -0
  76. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/component/services/workflow.py +0 -0
  77. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/compose/__init__.py +0 -0
  78. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/compose/compose.py +0 -0
  79. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/compose/manager.py +0 -0
  80. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/__init__.py +0 -0
  81. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/base.py +0 -0
  82. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/controller.py +0 -0
  83. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runner/__init__.py +0 -0
  84. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runner/client.py +0 -0
  85. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runner/runner.py +0 -0
  86. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runtime/__init__.py +0 -0
  87. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runtime/docker/__init__.py +0 -0
  88. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runtime/docker/context/Dockerfile +0 -0
  89. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runtime/docker/docker.py +0 -0
  90. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runtime/native/__init__.py +0 -0
  91. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runtime/native/native.py +0 -0
  92. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/runtime/specs.py +0 -0
  93. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/services/__init__.py +0 -0
  94. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/controller/webui/__init__.py +0 -0
  95. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/gateway/__init__.py +0 -0
  96. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/gateway/base.py +0 -0
  97. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/gateway/gateway.py +0 -0
  98. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/gateway/services/__init__.py +0 -0
  99. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/gateway/services/ssh_tunnel.py +0 -0
  100. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/listener/__init__.py +0 -0
  101. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/listener/base.py +0 -0
  102. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/listener/listener.py +0 -0
  103. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/listener/services/__init__.py +0 -0
  104. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/logger/__init__.py +0 -0
  105. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/logger/base.py +0 -0
  106. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/logger/logger.py +0 -0
  107. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/logger/logging.py +0 -0
  108. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/logger/services/__init__.py +0 -0
  109. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/logger/services/console.py +0 -0
  110. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/logger/services/file.py +0 -0
  111. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/runtime/__init__.py +0 -0
  112. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/runtime/docker/__init__.py +0 -0
  113. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/runtime/docker/docker.py +0 -0
  114. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/runtime/env.py +0 -0
  115. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/services/__init__.py +0 -0
  116. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/services/async_service.py +0 -0
  117. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/__init__.py +0 -0
  118. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/caching.py +0 -0
  119. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/http_request.py +0 -0
  120. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/http_status.py +0 -0
  121. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/mcp_client.py +0 -0
  122. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/resolvers.py +0 -0
  123. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/shell.py +0 -0
  124. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/ssh_client.py +0 -0
  125. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/streamer.py +0 -0
  126. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/time.py +0 -0
  127. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/utils/workqueue.py +0 -0
  128. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/__init__.py +0 -0
  129. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/__init__.py +0 -0
  130. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/base.py +0 -0
  131. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/impl/__init__.py +0 -0
  132. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/impl/action.py +0 -0
  133. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/impl/delay.py +0 -0
  134. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/impl/if_.py +0 -0
  135. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/impl/random_router.py +0 -0
  136. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/impl/switch.py +0 -0
  137. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/job/job.py +0 -0
  138. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/core/workflow/workflow.py +0 -0
  139. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/__init__.py +0 -0
  140. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/loader.py +0 -0
  141. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/__init__.py +0 -0
  142. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/__init__.py +0 -0
  143. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/__init__.py +0 -0
  144. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/common.py +0 -0
  145. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/http_client.py +0 -0
  146. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/http_server.py +0 -0
  147. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/mcp_client.py +0 -0
  148. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/mcp_server.py +0 -0
  149. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/__init__.py +0 -0
  150. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/impl/common.py +0 -0
  151. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/impl/summarization.py +0 -0
  152. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/impl/text_classification.py +0 -0
  153. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/impl/text_embedding.py +0 -0
  154. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/impl/text_generation.py +0 -0
  155. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/model/impl/translation.py +0 -0
  156. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/shell.py +0 -0
  157. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/text_splitter.py +0 -0
  158. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/action/impl/workflow.py +0 -0
  159. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/__init__.py +0 -0
  160. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/component.py +0 -0
  161. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/__init__.py +0 -0
  162. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/common.py +0 -0
  163. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/http_client.py +0 -0
  164. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/http_server.py +0 -0
  165. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/mcp_client.py +0 -0
  166. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/mcp_server.py +0 -0
  167. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/model/__init__.py +0 -0
  168. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/shell.py +0 -0
  169. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/component/impl/types.py +0 -0
  170. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/compose.py +0 -0
  171. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/__init__.py +0 -0
  172. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/controller.py +0 -0
  173. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/impl/__init__.py +0 -0
  174. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/impl/common.py +0 -0
  175. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/impl/http_server.py +0 -0
  176. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/impl/mcp_server.py +0 -0
  177. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/impl/types.py +0 -0
  178. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/webui/__init__.py +0 -0
  179. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/webui/impl/__init__.py +0 -0
  180. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/webui/impl/common.py +0 -0
  181. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/webui/impl/dynamic.py +0 -0
  182. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/webui/impl/gradio.py +0 -0
  183. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/controller/webui/impl/static.py +0 -0
  184. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/gateway/__init__.py +0 -0
  185. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/gateway/gateway.py +0 -0
  186. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/gateway/impl/__init__.py +0 -0
  187. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/gateway/impl/common.py +0 -0
  188. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/gateway/impl/types.py +0 -0
  189. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/__init__.py +0 -0
  190. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/__init__.py +0 -0
  191. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/action.py +0 -0
  192. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/common.py +0 -0
  193. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/delay.py +0 -0
  194. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/if_.py +0 -0
  195. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/random_router.py +0 -0
  196. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/switch.py +0 -0
  197. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/impl/types.py +0 -0
  198. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/job/job.py +0 -0
  199. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/listener/__init__.py +0 -0
  200. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/listener/impl/__init__.py +0 -0
  201. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/listener/impl/common.py +0 -0
  202. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/listener/impl/types.py +0 -0
  203. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/listener/listener.py +0 -0
  204. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/logger/__init__.py +0 -0
  205. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/logger/impl/__init__.py +0 -0
  206. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/logger/impl/common.py +0 -0
  207. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/logger/impl/console.py +0 -0
  208. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/logger/impl/file.py +0 -0
  209. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/logger/impl/types.py +0 -0
  210. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/logger/logger.py +0 -0
  211. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/runtime/__init__.py +0 -0
  212. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/runtime/impl/__init__.py +0 -0
  213. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/runtime/impl/common.py +0 -0
  214. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/runtime/impl/docker.py +0 -0
  215. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/runtime/impl/native.py +0 -0
  216. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/runtime/impl/types.py +0 -0
  217. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/runtime/runtime.py +0 -0
  218. {model_compose-0.3.9/src/mindor/dsl/utils → model_compose-0.3.10/src/mindor/dsl/schema/transport}/__init__.py +0 -0
  219. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/schema/workflow.py +0 -0
  220. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/utils/annotation.py +0 -0
  221. {model_compose-0.3.9 → model_compose-0.3.10}/src/mindor/dsl/utils/enum.py +0 -0
  222. {model_compose-0.3.9 → model_compose-0.3.10}/src/model_compose.egg-info/dependency_links.txt +0 -0
  223. {model_compose-0.3.9 → model_compose-0.3.10}/src/model_compose.egg-info/entry_points.txt +0 -0
  224. {model_compose-0.3.9 → model_compose-0.3.10}/src/model_compose.egg-info/requires.txt +0 -0
  225. {model_compose-0.3.9 → model_compose-0.3.10}/src/model_compose.egg-info/top_level.txt +0 -0
  226. {model_compose-0.3.9 → model_compose-0.3.10}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: model-compose
3
- Version: 0.3.9
3
+ Version: 0.3.10
4
4
  Summary: model-compose: Declarative AI Model and Workflow Orchestrator
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.3.9"
3
+ version = "0.3.10"
4
4
  description = "model-compose: Declarative AI Model and Workflow Orchestrator"
5
5
  authors = [
6
6
  { name = "Hanyeol Cho", email = "hanyeol.cho@gmail.com" }
@@ -1,5 +1,5 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
- from mindor.core.utils.renderers import VariableRenderer
2
+ from mindor.core.utils.renderers import VariableRenderer, ImageValueRenderer
3
3
  from mindor.core.gateway import find_gateway_by_port
4
4
 
5
5
  class ComponentActionContext:
@@ -13,8 +13,14 @@ class ComponentActionContext:
13
13
  def register_source(self, key: str, source: Any) -> None:
14
14
  self.sources[key] = source
15
15
 
16
- async def render_variable(self, data: Dict[str, Any], ignore_files: bool = False) -> Any:
17
- return await self.renderer.render(data, ignore_files)
16
+ def has_reference(self, key: str, value: Any) -> bool:
17
+ return self.renderer.has_reference(key, value)
18
+
19
+ async def render_variable(self, value: Any, ignore_files: bool = False) -> Any:
20
+ return await self.renderer.render(value, ignore_files)
21
+
22
+ async def render_image(self, value: Any) -> Any:
23
+ return await ImageValueRenderer().render(await self.render_variable(value))
18
24
 
19
25
  async def _resolve_source(self, key: str, index: Optional[int]) -> Any:
20
26
  if key in self.sources:
@@ -4,7 +4,7 @@ from mindor.dsl.schema.component import ModelComponentConfig, ModelTaskType, Dev
4
4
  from mindor.dsl.schema.action import ModelActionConfig
5
5
  from mindor.core.services import AsyncService
6
6
  from ...context import ComponentActionContext
7
- from transformers import PreTrainedModel, PreTrainedTokenizer
7
+ from transformers import PreTrainedModel, PreTrainedTokenizer, ProcessorMixin
8
8
  import torch, asyncio
9
9
 
10
10
  class ModelTaskService(AsyncService):
@@ -41,7 +41,7 @@ class ModelTaskService(AsyncService):
41
41
 
42
42
  def _get_common_model_params(self) -> Dict[str, Any]:
43
43
  params: Dict[str, Any] = {}
44
-
44
+
45
45
  if self.config.device_mode != DeviceMode.SINGLE:
46
46
  params["device_map"] = self.config.device_mode.value
47
47
 
@@ -61,7 +61,10 @@ class ModelTaskService(AsyncService):
61
61
  params["local_files_only"] = True
62
62
 
63
63
  return params
64
-
64
+
65
+ def _get_model_class(self) -> Type[PreTrainedModel]:
66
+ raise NotImplementedError("Model class loader not implemented.")
67
+
65
68
  def _load_pretrained_tokenizer(self, extra_params: Optional[Dict[str, Any]] = None) -> PreTrainedTokenizer:
66
69
  params = self._get_common_tokenizer_params()
67
70
 
@@ -86,17 +89,37 @@ class ModelTaskService(AsyncService):
86
89
  params["local_files_only"] = True
87
90
 
88
91
  return params
89
-
90
- def _get_model_device(self, model: PreTrainedModel) -> torch.device:
91
- return next(model.parameters()).device
92
92
 
93
- @abstractmethod
94
- def _get_model_class(self) -> Type[PreTrainedModel]:
95
- pass
96
-
97
- @abstractmethod
98
93
  def _get_tokenizer_class(self) -> Type[PreTrainedTokenizer]:
99
- pass
94
+ raise NotImplementedError("Tokenizer class loader not implemented.")
95
+
96
+ def _load_pretrained_processor(self, extra_params: Optional[Dict[str, Any]] = None) -> ProcessorMixin:
97
+ params = self._get_common_processor_params()
98
+
99
+ if extra_params:
100
+ params.update(extra_params)
101
+
102
+ return self._get_processor_class().from_pretrained(self.config.model, **params)
103
+
104
+ def _get_common_processor_params(self) -> Dict[str, Any]:
105
+ params: Dict[str, Any] = {}
106
+
107
+ if self.config.revision:
108
+ params["revision"] = self.config.revision
109
+
110
+ if self.config.cache_dir:
111
+ params["cache_dir"] = self.config.cache_dir
112
+
113
+ if self.config.local_files_only:
114
+ params["local_files_only"] = True
115
+
116
+ return params
117
+
118
+ def _get_processor_class(self) -> Type[ProcessorMixin]:
119
+ raise NotImplementedError("Processor class loader not implemented.")
120
+
121
+ def _get_model_device(self, model: PreTrainedModel) -> torch.device:
122
+ return next(model.parameters()).device
100
123
 
101
124
  def register_model_task_service(type: ModelTaskType):
102
125
  def decorator(cls: Type[ModelTaskService]) -> Type[ModelTaskService]:
@@ -3,3 +3,4 @@ from .summarization import *
3
3
  from .translation import *
4
4
  from .text_classification import *
5
5
  from .text_embedding import *
6
+ from .image_to_text import *
@@ -0,0 +1,155 @@
1
+ from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Protocol, Any
2
+ from mindor.dsl.schema.component import ModelComponentConfig, ImageToTextModelArchitecture
3
+ from mindor.dsl.schema.action import ModelActionConfig, ImageToTextModelActionConfig
4
+ from mindor.core.utils.streamer import AsyncStreamer
5
+ from mindor.core.logger import logging
6
+ from ..base import ModelTaskService, ModelTaskType, register_model_task_service
7
+ from ..base import ComponentActionContext
8
+ from transformers import PreTrainedModel, PreTrainedTokenizer, ProcessorMixin, GenerationMixin, TextIteratorStreamer
9
+ from PIL import Image as PILImage
10
+ from threading import Thread
11
+ from torch import Tensor
12
+ import torch, asyncio
13
+
14
+ class WithTokenizer(Protocol):
15
+ tokenizer: PreTrainedTokenizer
16
+
17
+ class ImageToTextTaskAction:
18
+ def __init__(self, config: ImageToTextModelActionConfig, model: PreTrainedModel, processor: ProcessorMixin, device: torch.device):
19
+ self.config: ImageToTextModelActionConfig = config
20
+ self.model: Union[PreTrainedModel, GenerationMixin] = model
21
+ self.processor: Union[ProcessorMixin, WithTokenizer] = processor
22
+ self.device: torch.device = device
23
+
24
+ async def run(self, context: ComponentActionContext, loop: asyncio.AbstractEventLoop) -> Any:
25
+ image: Union[PILImage.Image, List[PILImage.Image]] = await context.render_image(self.config.image)
26
+ prompt: Optional[Union[str, List[str]]] = await context.render_variable(self.config.prompt)
27
+
28
+ batch_size = await context.render_variable(self.config.params.batch_size)
29
+ stream = await context.render_variable(self.config.stream)
30
+
31
+ is_single_input: bool = True if not isinstance(images, list) else False
32
+ images: List[PILImage.Image] = [ image ] if is_single_input else image
33
+ prompts: Optional[List[str]] = [ prompt ] if is_single_input else prompt
34
+ results = []
35
+
36
+ if stream and (batch_size != 1 or len(images) != 1):
37
+ raise ValueError("Streaming mode only supports a single input image with batch size of 1.")
38
+
39
+ streamer = TextIteratorStreamer(self.processor.tokenizer, skip_prompt=True, skip_special_tokens=True) if stream else None
40
+ for index in range(0, len(images), batch_size):
41
+ batch_images = images[index:index + batch_size]
42
+ batch_prompts = prompts[index:index + batch_size] if prompts else None
43
+
44
+ inputs: Tensor = self.processor(images=batch_images, text=batch_prompts, return_tensors="pt")
45
+ inputs = inputs.to(self.device)
46
+
47
+ def _generate():
48
+ with torch.inference_mode():
49
+ outputs = self.model.generate(
50
+ **inputs,
51
+ streamer=streamer
52
+ )
53
+
54
+ outputs = self.processor.tokenizer.batch_decode(outputs, skip_special_tokens=True)
55
+ results.extend(outputs)
56
+
57
+ if stream:
58
+ thread = Thread(target=_generate)
59
+ thread.start()
60
+ else:
61
+ _generate()
62
+
63
+ if stream:
64
+ async def _stream_generator():
65
+ async for chunk in AsyncStreamer(streamer, loop):
66
+ context.register_source("result", chunk)
67
+ yield (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else chunk
68
+
69
+ return _stream_generator()
70
+ else:
71
+ result = results[0] if is_single_input else results
72
+ context.register_source("result", result)
73
+
74
+ return (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
75
+
76
+ @register_model_task_service(ModelTaskType.IMAGE_TO_TEXT)
77
+ class ImageToTextTaskService(ModelTaskService):
78
+ def __init__(self, id: str, config: ModelComponentConfig, daemon: bool):
79
+ super().__init__(id, config, daemon)
80
+
81
+ self.model: Optional[Union[PreTrainedModel, GenerationMixin]] = None
82
+ self.processor: Optional[ProcessorMixin] = None
83
+ self.device: Optional[torch.device] = None
84
+
85
+ async def _serve(self) -> None:
86
+ try:
87
+ self.model = self._load_pretrained_model()
88
+ self.processor = self._load_pretrained_processor()
89
+ self.device = self._get_model_device(self.model)
90
+ logging.info(f"Model and processor loaded successfully on device '{self.device}': {self.config.model}")
91
+ except Exception as e:
92
+ logging.error(f"Failed to load model '{self.config.model}': {e}")
93
+ raise
94
+
95
+ async def _shutdown(self) -> None:
96
+ self.model = None
97
+ self.processor = None
98
+ self.device = None
99
+
100
+ async def _run(self, action: ModelActionConfig, context: ComponentActionContext, loop: asyncio.AbstractEventLoop) -> Any:
101
+ return await ImageToTextTaskAction(action, self.model, self.processor, self.device).run(context, loop)
102
+
103
+ def _get_model_class(self) -> Type[PreTrainedModel]:
104
+ if self.config.architecture == ImageToTextModelArchitecture.BLIP:
105
+ from transformers import BlipForConditionalGeneration
106
+ return BlipForConditionalGeneration
107
+
108
+ if self.config.architecture == ImageToTextModelArchitecture.BLIP2:
109
+ from transformers import Blip2ForConditionalGeneration
110
+ return Blip2ForConditionalGeneration
111
+
112
+ if self.config.architecture == ImageToTextModelArchitecture.GIT:
113
+ from transformers import GitForCausalLM
114
+ return GitForCausalLM
115
+
116
+ if self.config.architecture == ImageToTextModelArchitecture.PIX2STRUCT:
117
+ from transformers import Pix2StructForConditionalGeneration
118
+ return Pix2StructForConditionalGeneration
119
+
120
+ if self.config.architecture == ImageToTextModelArchitecture.DONUT:
121
+ from transformers import VisionEncoderDecoderModel # Donut uses this
122
+ return VisionEncoderDecoderModel
123
+
124
+ if self.config.architecture == ImageToTextModelArchitecture.KOSMOS2:
125
+ from transformers import Kosmos2ForConditionalGeneration
126
+ return Kosmos2ForConditionalGeneration
127
+
128
+ raise ValueError(f"Unknown architecture: {self.config.architecture}")
129
+
130
+ def _get_processor_class(self) -> Type[ProcessorMixin]:
131
+ if self.config.architecture == ImageToTextModelArchitecture.BLIP:
132
+ from transformers import BlipProcessor
133
+ return BlipProcessor
134
+
135
+ if self.config.architecture == ImageToTextModelArchitecture.BLIP2:
136
+ from transformers import Blip2Processor
137
+ return Blip2Processor
138
+
139
+ if self.config.architecture == ImageToTextModelArchitecture.GIT:
140
+ from transformers import GitProcessor
141
+ return GitProcessor
142
+
143
+ if self.config.architecture == ImageToTextModelArchitecture.PIX2STRUCT:
144
+ from transformers import Pix2StructProcessor
145
+ return Pix2StructProcessor
146
+
147
+ if self.config.architecture == ImageToTextModelArchitecture.DONUT:
148
+ from transformers import DonutProcessor
149
+ return DonutProcessor
150
+
151
+ if self.config.architecture == ImageToTextModelArchitecture.KOSMOS2:
152
+ from transformers import Kosmos2Processor
153
+ return Kosmos2Processor
154
+
155
+ raise ValueError(f"Unknown architecture: {self.config.architecture}")
@@ -11,7 +11,7 @@ from torch import Tensor
11
11
  import torch, asyncio
12
12
 
13
13
  class SummarizationTaskAction:
14
- def __init__(self, config: SummarizationModelActionConfig, model: Union[PreTrainedModel, GenerationMixin], tokenizer: PreTrainedTokenizer, device: torch.device):
14
+ def __init__(self, config: SummarizationModelActionConfig, model: PreTrainedModel, tokenizer: PreTrainedTokenizer, device: torch.device):
15
15
  self.config: SummarizationModelActionConfig = config
16
16
  self.model: Union[PreTrainedModel, GenerationMixin] = model
17
17
  self.tokenizer: PreTrainedTokenizer = tokenizer
@@ -32,7 +32,8 @@ class SummarizationTaskAction:
32
32
  batch_size = await context.render_variable(self.config.params.batch_size)
33
33
  stream = await context.render_variable(self.config.stream)
34
34
 
35
- texts: List[str] = [ text ] if isinstance(text, str) else text
35
+ is_single_input: bool = True if not isinstance(text, list) else False
36
+ texts: List[str] = [ text ] if is_single_input else text
36
37
  results = []
37
38
 
38
39
  if stream and (batch_size != 1 or len(texts) != 1):
@@ -45,7 +46,7 @@ class SummarizationTaskAction:
45
46
  inputs = { k: v.to(self.device) for k, v in inputs.items() }
46
47
 
47
48
  def _generate():
48
- with torch.no_grad():
49
+ with torch.inference_mode():
49
50
  outputs = self.model.generate(
50
51
  **inputs,
51
52
  max_length=max_output_length,
@@ -61,8 +62,9 @@ class SummarizationTaskAction:
61
62
  streamer=streamer
62
63
  )
63
64
 
64
- outputs = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)
65
- results.extend(outputs)
65
+ if not stream:
66
+ outputs = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)
67
+ results.extend(outputs)
66
68
 
67
69
  if stream:
68
70
  thread = Thread(target=_generate)
@@ -70,15 +72,15 @@ class SummarizationTaskAction:
70
72
  else:
71
73
  _generate()
72
74
 
73
- if stream:
75
+ if stream:
74
76
  async def _stream_generator():
75
- async for result in AsyncStreamer(streamer, loop):
76
- context.register_source("result", result)
77
- yield (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
77
+ async for chunk in AsyncStreamer(streamer, loop):
78
+ context.register_source("result", chunk)
79
+ yield (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else chunk
78
80
 
79
81
  return _stream_generator()
80
82
  else:
81
- result = results if len(results) > 1 else results[0]
83
+ result = results[0] if is_single_input else results
82
84
  context.register_source("result", result)
83
85
 
84
86
  return (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
@@ -0,0 +1,122 @@
1
+ from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
+ from mindor.dsl.schema.component import ModelComponentConfig
3
+ from mindor.dsl.schema.action import ModelActionConfig, TextClassificationModelActionConfig
4
+ from mindor.core.utils.streamer import AsyncStreamer
5
+ from mindor.core.logger import logging
6
+ from ..base import ModelTaskService, ModelTaskType, register_model_task_service
7
+ from ..base import ComponentActionContext
8
+ from transformers import AutoModelForSequenceClassification, AutoTokenizer, PreTrainedModel, PreTrainedTokenizer
9
+ from transformers.modeling_outputs import SequenceClassifierOutput
10
+ import torch.nn.functional as F
11
+ from torch import Tensor
12
+ import torch, asyncio
13
+
14
+ class TextClassificationTaskAction:
15
+ def __init__(self, config: TextClassificationModelActionConfig, model: PreTrainedModel, tokenizer: PreTrainedTokenizer, device: torch.device):
16
+ self.config: TextClassificationModelActionConfig = config
17
+ self.model: PreTrainedModel = model
18
+ self.tokenizer: PreTrainedTokenizer = tokenizer
19
+ self.device: torch.device = device
20
+
21
+ async def run(self, context: ComponentActionContext, labels: Optional[List[str]]) -> Any:
22
+ text: Union[str, List[str]] = await context.render_variable(self.config.text)
23
+
24
+ return_probabilities = await context.render_variable(self.config.params.return_probabilities)
25
+ batch_size = await context.render_variable(self.config.params.batch_size)
26
+ stream = await context.render_variable(self.config.stream)
27
+
28
+ is_single_input: bool = True if not isinstance(text, list) else False
29
+ has_iterable_result: bool = context.has_reference("result[]", self.config.output)
30
+ texts: List[str] = [ text ] if is_single_input else text
31
+ results = []
32
+
33
+ async def _predict():
34
+ for index in range(0, len(texts), batch_size):
35
+ batch_texts = texts[index:index + batch_size]
36
+ inputs: Dict[str, Tensor] = self.tokenizer(batch_texts, return_tensors="pt", padding=True, truncation=True)
37
+ inputs = { k: v.to(self.device) for k, v in inputs.items() }
38
+
39
+ with torch.inference_mode():
40
+ outputs: SequenceClassifierOutput = self.model(**inputs)
41
+ logits = outputs.logits # shape: (batch_size, num_classes)
42
+ predictions = []
43
+
44
+ if return_probabilities:
45
+ probs = F.softmax(logits, dim=-1).cpu()
46
+ for prob in probs:
47
+ predicted_index = torch.argmax(prob).item()
48
+ predictions.append({
49
+ "label": labels[predicted_index] if labels else predicted_index,
50
+ "probabilities": prob.tolist()
51
+ })
52
+ else:
53
+ predicted_indices = torch.argmax(logits, dim=-1).tolist()
54
+ for predicted_index in predicted_indices:
55
+ predictions.append(labels[predicted_index] if labels else predicted_index)
56
+
57
+ if self.config.output and has_iterable_result:
58
+ rendered_outputs = []
59
+ for prediction in predictions:
60
+ context.register_source("result[]", prediction)
61
+ output = await context.render_variable(self.config.output, ignore_files=True)
62
+ rendered_outputs.append(output)
63
+ yield rendered_outputs
64
+ else:
65
+ yield predictions
66
+
67
+ if stream:
68
+ async def _stream_generator():
69
+ async for predictions in _predict():
70
+ if not has_iterable_result:
71
+ for prediction in predictions:
72
+ context.register_source("result", prediction)
73
+ yield (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
74
+ else:
75
+ for prediction in predictions:
76
+ yield prediction
77
+
78
+ return _stream_generator()
79
+ else:
80
+ async for predictions in _predict():
81
+ results.extend(predictions)
82
+
83
+ if not has_iterable_result:
84
+ result = results[0] if is_single_input else results
85
+ context.register_source("result", result)
86
+
87
+ return (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
88
+ else:
89
+ return results
90
+
91
+ @register_model_task_service(ModelTaskType.TEXT_CLASSIFICATION)
92
+ class TextClassificationTaskService(ModelTaskService):
93
+ def __init__(self, id: str, config: ModelComponentConfig, daemon: bool):
94
+ super().__init__(id, config, daemon)
95
+
96
+ self.model: Optional[PreTrainedModel] = None
97
+ self.tokenizer: Optional[PreTrainedTokenizer] = None
98
+ self.device: Optional[torch.device] = None
99
+
100
+ async def _serve(self) -> None:
101
+ try:
102
+ self.model = self._load_pretrained_model()
103
+ self.tokenizer = self._load_pretrained_tokenizer()
104
+ self.device = self._get_model_device(self.model)
105
+ logging.info(f"Model and tokenizer loaded successfully on device '{self.device}': {self.config.model}")
106
+ except Exception as e:
107
+ logging.error(f"Failed to load model '{self.config.model}': {e}")
108
+ raise
109
+
110
+ async def _shutdown(self) -> None:
111
+ self.model = None
112
+ self.tokenizer = None
113
+ self.device = None
114
+
115
+ async def _run(self, action: ModelActionConfig, context: ComponentActionContext, loop: asyncio.AbstractEventLoop) -> Any:
116
+ return await TextClassificationTaskAction(action, self.model, self.tokenizer, self.device).run(context, self.config.labels)
117
+
118
+ def _get_model_class(self) -> Type[PreTrainedModel]:
119
+ return AutoModelForSequenceClassification
120
+
121
+ def _get_tokenizer_class(self) -> Type[PreTrainedTokenizer]:
122
+ return AutoTokenizer
@@ -1,6 +1,7 @@
1
1
  from typing import Type, Union, Literal, Optional, Dict, List, Tuple, Set, Annotated, Any
2
2
  from mindor.dsl.schema.component import ModelComponentConfig
3
3
  from mindor.dsl.schema.action import ModelActionConfig, TextEmbeddingModelActionConfig
4
+ from mindor.core.utils.streamer import AsyncStreamer
4
5
  from mindor.core.logger import logging
5
6
  from ..base import ModelTaskService, ModelTaskType, register_model_task_service
6
7
  from ..base import ComponentActionContext
@@ -23,31 +24,64 @@ class TextEmbeddingTaskAction:
23
24
  pooling = await context.render_variable(self.config.params.pooling)
24
25
  normalize = await context.render_variable(self.config.params.normalize)
25
26
  batch_size = await context.render_variable(self.config.params.batch_size)
27
+ stream = await context.render_variable(self.config.stream)
26
28
 
27
- texts: List[str] = [ text ] if isinstance(text, str) else text
29
+ is_single_input: bool = True if not isinstance(text, list) else False
30
+ has_iterable_result: bool = context.has_reference("result[]", self.config.output)
31
+ texts: List[str] = [ text ] if is_single_input else text
28
32
  results = []
29
33
 
30
- for index in range(0, len(texts), batch_size):
31
- batch_texts = texts[index:index + batch_size]
32
- inputs: Dict[str, Tensor] = self.tokenizer(batch_texts, return_tensors="pt", max_length=max_input_length, padding=True, truncation=True)
33
- inputs = { k: v.to(self.device) for k, v in inputs.items() }
34
-
35
- with torch.no_grad():
36
- outputs: BaseModelOutput = self.model(**inputs)
37
- last_hidden_state = outputs.last_hidden_state # (batch_size, seq_len, hidden_size)
38
-
39
- attention_mask = inputs.get("attention_mask", None)
40
- embeddings = self._pool_hidden_state(last_hidden_state, attention_mask, pooling)
41
-
42
- if normalize:
43
- embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1)
44
-
45
- results.extend(embeddings.cpu().tolist())
46
-
47
- result = results if len(results) > 1 else results[0]
48
- context.register_source("result", result)
49
-
50
- return (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
34
+ async def _embed():
35
+ for index in range(0, len(texts), batch_size):
36
+ batch_texts = texts[index:index + batch_size]
37
+ inputs: Dict[str, Tensor] = self.tokenizer(batch_texts, return_tensors="pt", max_length=max_input_length, padding=True, truncation=True)
38
+ inputs = { k: v.to(self.device) for k, v in inputs.items() }
39
+
40
+ with torch.inference_mode():
41
+ outputs: BaseModelOutput = self.model(**inputs)
42
+ last_hidden_state = outputs.last_hidden_state # (batch_size, seq_len, hidden_size)
43
+
44
+ attention_mask = inputs.get("attention_mask", None)
45
+ embeddings = self._pool_hidden_state(last_hidden_state, attention_mask, pooling)
46
+
47
+ if normalize:
48
+ embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1, eps=1e-12)
49
+
50
+ embeddings = embeddings.cpu().tolist()
51
+
52
+ if self.config.output and has_iterable_result:
53
+ rendered_outputs = []
54
+ for embedding in embeddings:
55
+ context.register_source("result[]", embedding)
56
+ output = await context.render_variable(self.config.output, ignore_files=True)
57
+ rendered_outputs.append(output)
58
+ yield rendered_outputs
59
+ else:
60
+ yield embeddings
61
+
62
+ if stream:
63
+ async def _stream_generator():
64
+ async for embeddings in _embed():
65
+ if not has_iterable_result:
66
+ for embedding in embeddings:
67
+ context.register_source("result", embedding)
68
+ yield (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
69
+ else:
70
+ for embedding in embeddings:
71
+ yield embedding
72
+
73
+ return _stream_generator()
74
+ else:
75
+ async for embeddings in _embed():
76
+ results.extend(embeddings)
77
+
78
+ if not has_iterable_result:
79
+ result = results[0] if is_single_input else results
80
+ context.register_source("result", result)
81
+
82
+ return (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
83
+ else:
84
+ return results
51
85
 
52
86
  def _pool_hidden_state(self, last_hidden_state: Tensor, attention_mask: Optional[Tensor], pooling: str) -> Tensor:
53
87
  if pooling == "mean":
@@ -11,7 +11,7 @@ from torch import Tensor
11
11
  import torch, asyncio
12
12
 
13
13
  class TextGenerationTaskAction:
14
- def __init__(self, config: TextGenerationModelActionConfig, model: Union[PreTrainedModel, GenerationMixin], tokenizer: PreTrainedTokenizer, device: torch.device):
14
+ def __init__(self, config: TextGenerationModelActionConfig, model: PreTrainedModel, tokenizer: PreTrainedTokenizer, device: torch.device):
15
15
  self.config: TextGenerationModelActionConfig = config
16
16
  self.model: Union[PreTrainedModel, GenerationMixin] = model
17
17
  self.tokenizer: PreTrainedTokenizer = tokenizer
@@ -28,7 +28,8 @@ class TextGenerationTaskAction:
28
28
  batch_size = await context.render_variable(self.config.params.batch_size)
29
29
  stream = await context.render_variable(self.config.stream)
30
30
 
31
- prompts: List[str] = [ prompt ] if isinstance(prompt, str) else prompt
31
+ is_single_input: bool = True if not isinstance(prompt, list) else False
32
+ prompts: List[str] = [ prompt ] if is_single_input else prompt
32
33
  results = []
33
34
 
34
35
  if stream and (batch_size != 1 or len(prompts) != 1):
@@ -41,7 +42,7 @@ class TextGenerationTaskAction:
41
42
  inputs = { k: v.to(self.device) for k, v in inputs.items() }
42
43
 
43
44
  def _generate():
44
- with torch.no_grad():
45
+ with torch.inference_mode():
45
46
  outputs = self.model.generate(
46
47
  **inputs,
47
48
  max_new_tokens=max_output_length,
@@ -53,8 +54,9 @@ class TextGenerationTaskAction:
53
54
  streamer=streamer
54
55
  )
55
56
 
56
- outputs = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)
57
- results.extend(outputs)
57
+ if not stream:
58
+ outputs = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)
59
+ results.extend(outputs)
58
60
 
59
61
  if stream:
60
62
  thread = Thread(target=_generate)
@@ -62,15 +64,15 @@ class TextGenerationTaskAction:
62
64
  else:
63
65
  _generate()
64
66
 
65
- if stream:
67
+ if stream:
66
68
  async def _stream_generator():
67
- async for result in AsyncStreamer(streamer, loop):
68
- context.register_source("result", result)
69
- yield (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result
69
+ async for chunk in AsyncStreamer(streamer, loop):
70
+ context.register_source("result", chunk)
71
+ yield (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else chunk
70
72
 
71
73
  return _stream_generator()
72
74
  else:
73
- result = results if len(results) > 1 else results[0]
75
+ result = results[0] if is_single_input else results
74
76
  context.register_source("result", result)
75
77
 
76
78
  return (await context.render_variable(self.config.output, ignore_files=True)) if self.config.output else result