agentscope-runtime 0.2.0b2__py3-none-any.whl → 1.0.0b1__py3-none-any.whl

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 (183) hide show
  1. agentscope_runtime/adapters/__init__.py +0 -0
  2. agentscope_runtime/adapters/agentscope/__init__.py +0 -0
  3. agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +6 -0
  4. agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +258 -0
  5. agentscope_runtime/adapters/agentscope/memory/__init__.py +6 -0
  6. agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +152 -0
  7. agentscope_runtime/adapters/agentscope/message.py +535 -0
  8. agentscope_runtime/adapters/agentscope/stream.py +474 -0
  9. agentscope_runtime/adapters/agentscope/tool/__init__.py +9 -0
  10. agentscope_runtime/adapters/agentscope/tool/sandbox_tool.py +69 -0
  11. agentscope_runtime/adapters/agentscope/tool/tool.py +233 -0
  12. agentscope_runtime/adapters/autogen/__init__.py +0 -0
  13. agentscope_runtime/adapters/autogen/tool/__init__.py +7 -0
  14. agentscope_runtime/adapters/autogen/tool/tool.py +211 -0
  15. agentscope_runtime/adapters/text/__init__.py +0 -0
  16. agentscope_runtime/adapters/text/stream.py +29 -0
  17. agentscope_runtime/common/collections/redis_mapping.py +4 -1
  18. agentscope_runtime/common/container_clients/fc_client.py +855 -0
  19. agentscope_runtime/common/utils/__init__.py +0 -0
  20. agentscope_runtime/common/utils/lazy_loader.py +57 -0
  21. agentscope_runtime/engine/__init__.py +25 -18
  22. agentscope_runtime/engine/app/agent_app.py +161 -91
  23. agentscope_runtime/engine/app/base_app.py +4 -118
  24. agentscope_runtime/engine/constant.py +8 -0
  25. agentscope_runtime/engine/deployers/__init__.py +8 -0
  26. agentscope_runtime/engine/deployers/adapter/__init__.py +2 -0
  27. agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +0 -21
  28. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +28 -9
  29. agentscope_runtime/engine/deployers/adapter/responses/__init__.py +2 -0
  30. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -2
  31. agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +1 -1
  32. agentscope_runtime/engine/deployers/agentrun_deployer.py +2541 -0
  33. agentscope_runtime/engine/deployers/cli_fc_deploy.py +1 -1
  34. agentscope_runtime/engine/deployers/kubernetes_deployer.py +9 -21
  35. agentscope_runtime/engine/deployers/local_deployer.py +47 -74
  36. agentscope_runtime/engine/deployers/modelstudio_deployer.py +216 -50
  37. agentscope_runtime/engine/deployers/utils/app_runner_utils.py +29 -0
  38. agentscope_runtime/engine/deployers/utils/detached_app.py +510 -0
  39. agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +1 -1
  40. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +1 -1
  41. agentscope_runtime/engine/deployers/utils/docker_image_utils/{runner_image_factory.py → image_factory.py} +121 -61
  42. agentscope_runtime/engine/deployers/utils/package.py +693 -0
  43. agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -5
  44. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +256 -282
  45. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +2 -4
  46. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +23 -1
  47. agentscope_runtime/engine/deployers/utils/templates/app_main.py.j2 +84 -0
  48. agentscope_runtime/engine/deployers/utils/templates/runner_main.py.j2 +95 -0
  49. agentscope_runtime/engine/deployers/utils/{service_utils → templates}/standalone_main.py.j2 +0 -45
  50. agentscope_runtime/engine/deployers/utils/wheel_packager.py +119 -18
  51. agentscope_runtime/engine/helpers/runner.py +40 -0
  52. agentscope_runtime/engine/runner.py +170 -130
  53. agentscope_runtime/engine/schemas/agent_schemas.py +114 -3
  54. agentscope_runtime/engine/schemas/modelstudio_llm.py +4 -2
  55. agentscope_runtime/engine/schemas/oai_llm.py +23 -23
  56. agentscope_runtime/engine/schemas/response_api.py +65 -0
  57. agentscope_runtime/engine/schemas/session.py +24 -0
  58. agentscope_runtime/engine/services/__init__.py +0 -9
  59. agentscope_runtime/engine/services/agent_state/__init__.py +16 -0
  60. agentscope_runtime/engine/services/agent_state/redis_state_service.py +113 -0
  61. agentscope_runtime/engine/services/agent_state/state_service.py +179 -0
  62. agentscope_runtime/engine/services/memory/__init__.py +24 -0
  63. agentscope_runtime/engine/services/{mem0_memory_service.py → memory/mem0_memory_service.py} +17 -13
  64. agentscope_runtime/engine/services/{memory_service.py → memory/memory_service.py} +28 -7
  65. agentscope_runtime/engine/services/{redis_memory_service.py → memory/redis_memory_service.py} +1 -1
  66. agentscope_runtime/engine/services/{reme_personal_memory_service.py → memory/reme_personal_memory_service.py} +9 -6
  67. agentscope_runtime/engine/services/{reme_task_memory_service.py → memory/reme_task_memory_service.py} +2 -2
  68. agentscope_runtime/engine/services/{tablestore_memory_service.py → memory/tablestore_memory_service.py} +12 -18
  69. agentscope_runtime/engine/services/sandbox/__init__.py +13 -0
  70. agentscope_runtime/engine/services/{sandbox_service.py → sandbox/sandbox_service.py} +86 -71
  71. agentscope_runtime/engine/services/session_history/__init__.py +23 -0
  72. agentscope_runtime/engine/services/{redis_session_history_service.py → session_history/redis_session_history_service.py} +3 -2
  73. agentscope_runtime/engine/services/{session_history_service.py → session_history/session_history_service.py} +44 -34
  74. agentscope_runtime/engine/services/{tablestore_session_history_service.py → session_history/tablestore_session_history_service.py} +14 -19
  75. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +2 -2
  76. agentscope_runtime/engine/tracing/base.py +10 -9
  77. agentscope_runtime/engine/tracing/message_util.py +1 -1
  78. agentscope_runtime/engine/tracing/tracing_util.py +7 -2
  79. agentscope_runtime/sandbox/__init__.py +10 -2
  80. agentscope_runtime/sandbox/box/agentbay/__init__.py +4 -0
  81. agentscope_runtime/sandbox/box/agentbay/agentbay_sandbox.py +559 -0
  82. agentscope_runtime/sandbox/box/base/base_sandbox.py +12 -0
  83. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +115 -11
  84. agentscope_runtime/sandbox/box/cloud/__init__.py +4 -0
  85. agentscope_runtime/sandbox/box/cloud/cloud_sandbox.py +254 -0
  86. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +66 -0
  87. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +42 -0
  88. agentscope_runtime/sandbox/box/mobile/__init__.py +4 -0
  89. agentscope_runtime/sandbox/box/mobile/box/__init__.py +0 -0
  90. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +216 -0
  91. agentscope_runtime/sandbox/box/training_box/training_box.py +2 -2
  92. agentscope_runtime/sandbox/client/http_client.py +1 -0
  93. agentscope_runtime/sandbox/enums.py +2 -0
  94. agentscope_runtime/sandbox/manager/sandbox_manager.py +18 -2
  95. agentscope_runtime/sandbox/manager/server/app.py +12 -0
  96. agentscope_runtime/sandbox/manager/server/config.py +19 -0
  97. agentscope_runtime/sandbox/model/manager_config.py +79 -2
  98. agentscope_runtime/sandbox/utils.py +0 -18
  99. agentscope_runtime/tools/RAGs/__init__.py +0 -0
  100. agentscope_runtime/tools/RAGs/modelstudio_rag.py +377 -0
  101. agentscope_runtime/tools/RAGs/modelstudio_rag_lite.py +219 -0
  102. agentscope_runtime/tools/__init__.py +119 -0
  103. agentscope_runtime/tools/_constants.py +18 -0
  104. agentscope_runtime/tools/alipay/__init__.py +4 -0
  105. agentscope_runtime/tools/alipay/base.py +334 -0
  106. agentscope_runtime/tools/alipay/payment.py +835 -0
  107. agentscope_runtime/tools/alipay/subscribe.py +551 -0
  108. agentscope_runtime/tools/base.py +264 -0
  109. agentscope_runtime/tools/cli/__init__.py +0 -0
  110. agentscope_runtime/tools/cli/modelstudio_mcp_server.py +78 -0
  111. agentscope_runtime/tools/generations/__init__.py +75 -0
  112. agentscope_runtime/tools/generations/async_image_to_video.py +350 -0
  113. agentscope_runtime/tools/generations/async_image_to_video_wan25.py +366 -0
  114. agentscope_runtime/tools/generations/async_speech_to_video.py +422 -0
  115. agentscope_runtime/tools/generations/async_text_to_video.py +320 -0
  116. agentscope_runtime/tools/generations/async_text_to_video_wan25.py +334 -0
  117. agentscope_runtime/tools/generations/image_edit.py +208 -0
  118. agentscope_runtime/tools/generations/image_edit_wan25.py +193 -0
  119. agentscope_runtime/tools/generations/image_generation.py +202 -0
  120. agentscope_runtime/tools/generations/image_generation_wan25.py +201 -0
  121. agentscope_runtime/tools/generations/image_style_repaint.py +208 -0
  122. agentscope_runtime/tools/generations/image_to_video.py +233 -0
  123. agentscope_runtime/tools/generations/qwen_image_edit.py +205 -0
  124. agentscope_runtime/tools/generations/qwen_image_generation.py +214 -0
  125. agentscope_runtime/tools/generations/qwen_text_to_speech.py +154 -0
  126. agentscope_runtime/tools/generations/speech_to_text.py +260 -0
  127. agentscope_runtime/tools/generations/speech_to_video.py +314 -0
  128. agentscope_runtime/tools/generations/text_to_video.py +221 -0
  129. agentscope_runtime/tools/mcp_wrapper.py +215 -0
  130. agentscope_runtime/tools/realtime_clients/__init__.py +13 -0
  131. agentscope_runtime/tools/realtime_clients/asr_client.py +27 -0
  132. agentscope_runtime/tools/realtime_clients/azure_asr_client.py +195 -0
  133. agentscope_runtime/tools/realtime_clients/azure_tts_client.py +383 -0
  134. agentscope_runtime/tools/realtime_clients/modelstudio_asr_client.py +151 -0
  135. agentscope_runtime/tools/realtime_clients/modelstudio_tts_client.py +199 -0
  136. agentscope_runtime/tools/realtime_clients/realtime_tool.py +55 -0
  137. agentscope_runtime/tools/realtime_clients/tts_client.py +33 -0
  138. agentscope_runtime/tools/searches/__init__.py +3 -0
  139. agentscope_runtime/tools/searches/modelstudio_search.py +877 -0
  140. agentscope_runtime/tools/searches/modelstudio_search_lite.py +310 -0
  141. agentscope_runtime/tools/utils/__init__.py +0 -0
  142. agentscope_runtime/tools/utils/api_key_util.py +45 -0
  143. agentscope_runtime/tools/utils/crypto_utils.py +99 -0
  144. agentscope_runtime/tools/utils/mcp_util.py +35 -0
  145. agentscope_runtime/version.py +1 -1
  146. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/METADATA +234 -165
  147. agentscope_runtime-1.0.0b1.dist-info/RECORD +240 -0
  148. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/entry_points.txt +1 -0
  149. agentscope_runtime/engine/agents/__init__.py +0 -2
  150. agentscope_runtime/engine/agents/agentscope_agent.py +0 -488
  151. agentscope_runtime/engine/agents/agno_agent.py +0 -220
  152. agentscope_runtime/engine/agents/autogen_agent.py +0 -250
  153. agentscope_runtime/engine/agents/base_agent.py +0 -29
  154. agentscope_runtime/engine/agents/langgraph_agent.py +0 -59
  155. agentscope_runtime/engine/agents/utils.py +0 -53
  156. agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -1163
  157. agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
  158. agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
  159. agentscope_runtime/engine/helpers/helper.py +0 -179
  160. agentscope_runtime/engine/schemas/context.py +0 -54
  161. agentscope_runtime/engine/services/context_manager.py +0 -164
  162. agentscope_runtime/engine/services/environment_manager.py +0 -50
  163. agentscope_runtime/engine/services/manager.py +0 -174
  164. agentscope_runtime/engine/services/rag_service.py +0 -195
  165. agentscope_runtime/engine/services/tablestore_rag_service.py +0 -143
  166. agentscope_runtime/sandbox/tools/__init__.py +0 -12
  167. agentscope_runtime/sandbox/tools/base/__init__.py +0 -8
  168. agentscope_runtime/sandbox/tools/base/tool.py +0 -52
  169. agentscope_runtime/sandbox/tools/browser/__init__.py +0 -57
  170. agentscope_runtime/sandbox/tools/browser/tool.py +0 -597
  171. agentscope_runtime/sandbox/tools/filesystem/__init__.py +0 -32
  172. agentscope_runtime/sandbox/tools/filesystem/tool.py +0 -319
  173. agentscope_runtime/sandbox/tools/function_tool.py +0 -321
  174. agentscope_runtime/sandbox/tools/gui/__init__.py +0 -7
  175. agentscope_runtime/sandbox/tools/gui/tool.py +0 -77
  176. agentscope_runtime/sandbox/tools/mcp_tool.py +0 -195
  177. agentscope_runtime/sandbox/tools/sandbox_tool.py +0 -104
  178. agentscope_runtime/sandbox/tools/tool.py +0 -238
  179. agentscope_runtime/sandbox/tools/utils.py +0 -68
  180. agentscope_runtime-0.2.0b2.dist-info/RECORD +0 -183
  181. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/WHEEL +0 -0
  182. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/licenses/LICENSE +0 -0
  183. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,208 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=abstract-method, deprecated-module, wrong-import-order
3
+ # pylint:disable=no-else-break, too-many-branches
4
+
5
+ import asyncio
6
+ import os
7
+ import time
8
+ import uuid
9
+ from http import HTTPStatus
10
+ from typing import Any, Optional
11
+
12
+ from dashscope.aigc.image_synthesis import AioImageSynthesis
13
+ from mcp.server.fastmcp import Context
14
+ from pydantic import BaseModel, Field
15
+
16
+ from ..base import Tool
17
+ from ..utils.api_key_util import get_api_key, ApiNames
18
+ from ...engine.tracing import trace, TracingUtil
19
+
20
+
21
+ class ImageGenInput(BaseModel):
22
+ """
23
+ Image-to-Image Input
24
+ """
25
+
26
+ function: str = Field(
27
+ ..., # Required
28
+ description="图像编辑功能。支持 "
29
+ "stylization_all、stylization_local、description_edit"
30
+ "、description_edit_with_mask、remove_watermark、expand"
31
+ "、super_resolution、colorization、doodle"
32
+ "、control_cartoon_feature。",
33
+ )
34
+ base_image_url: str = Field(
35
+ ..., # Required
36
+ description="输入图像的URL地址,需为公网可访问地址,支持 HTTP 或 HTTPS "
37
+ "协议。格式:JPG、JPEG、PNG、BMP、TIFF、WEBP,分辨率[512,"
38
+ "4096],大小不超过10MB。URL不能包含中文字符。",
39
+ )
40
+ mask_image_url: Optional[str] = Field(
41
+ default=None,
42
+ description="仅当function为description_edit_with_mask时必填,"
43
+ "其余情况无需填写。要求:URL,分辨率与base_image_url一致,"
44
+ "格式:JPG、JPEG、PNG、BMP、TIFF、WEBP,大小不超过10MB,"
45
+ "白色为编辑区域,黑色为不变区域。",
46
+ )
47
+ prompt: str = Field(
48
+ ...,
49
+ description="正向提示词,用来描述生成图像中期望包含的元素和视觉特点, 超过800自动截断",
50
+ )
51
+ n: Optional[int] = Field(
52
+ default=1,
53
+ description="生成图片的数量。取值范围为1~4张 默认1",
54
+ )
55
+ watermark: Optional[bool] = Field(
56
+ default=None,
57
+ description="是否添加水印,默认不设置",
58
+ )
59
+ ctx: Optional[Context] = Field(
60
+ default=None,
61
+ description="HTTP request context containing headers for mcp only, "
62
+ "don't generate it",
63
+ )
64
+
65
+
66
+ class ImageGenOutput(BaseModel):
67
+ """
68
+ Text-to-Image Output.
69
+ """
70
+
71
+ results: list[str] = Field(title="Results", description="输出图片url 列表")
72
+ request_id: Optional[str] = Field(
73
+ default=None,
74
+ title="Request ID",
75
+ description="请求ID",
76
+ )
77
+
78
+
79
+ class ImageEdit(Tool[ImageGenInput, ImageGenOutput]):
80
+ """
81
+ Image-to-image Call
82
+ """
83
+
84
+ name: str = "modelstudio_image_edit"
85
+ description: str = "AI图像编辑(图生图)服务,输入原图URL、编辑功能、文本描述和分辨率," "返回编辑后的图片URL。"
86
+
87
+ @trace(trace_type="AIGC", trace_name="image_edit")
88
+ async def arun(self, args: ImageGenInput, **kwargs: Any) -> ImageGenOutput:
89
+ """Modelstudio image editing from base image and text prompts
90
+
91
+ This method wraps DashScope's ImageSynthesis service to generate new
92
+ images based on the input image and editing instructions. Supports
93
+ various editing functions, resolutions, and batch generation.
94
+
95
+ Args:
96
+ args: ImageGenInput containing function, base_image_url,
97
+ mask_image_url, prompt, size, n.
98
+ **kwargs: Additional keyword arguments including request_id,
99
+ trace_event, model_name, api_key.
100
+
101
+ Returns:
102
+ ImageGenOutput containing the list of generated image URLs and
103
+ request ID.
104
+
105
+ Raises:
106
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
107
+ """
108
+
109
+ trace_event = kwargs.pop("trace_event", None)
110
+ request_id = TracingUtil.get_request_id()
111
+
112
+ try:
113
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
114
+ except AssertionError as e:
115
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
116
+
117
+ model_name = kwargs.get(
118
+ "model_name",
119
+ os.getenv("IMAGE_EDIT_MODEL_NAME", "wanx2.1-imageedit"),
120
+ )
121
+
122
+ parameters = {}
123
+ if args.n is not None:
124
+ parameters["n"] = args.n
125
+ if args.watermark is not None:
126
+ parameters["watermark"] = args.watermark
127
+
128
+ # 🔄 Use DashScope asynchronous task API to achieve true concurrency
129
+ # 1. Submit asynchronous task
130
+ task_response = await AioImageSynthesis.async_call(
131
+ model=model_name,
132
+ api_key=api_key,
133
+ function=args.function,
134
+ prompt=args.prompt,
135
+ base_image_url=args.base_image_url,
136
+ mask_image_url=args.mask_image_url,
137
+ **parameters,
138
+ )
139
+
140
+ if (
141
+ task_response.status_code != HTTPStatus.OK
142
+ or not task_response.output
143
+ ):
144
+ raise RuntimeError(f"Failed to submit task: {task_response}")
145
+
146
+ # 2. Loop to asynchronously query task status
147
+ max_wait_time = 300 # 5 minutes timeout
148
+ poll_interval = 2 # 2 seconds polling interval
149
+ start_time = time.time()
150
+
151
+ while True:
152
+ # Asynchronous wait
153
+ await asyncio.sleep(poll_interval)
154
+
155
+ # Query task result
156
+ res = await AioImageSynthesis.fetch(
157
+ api_key=api_key,
158
+ task=task_response,
159
+ )
160
+
161
+ if (
162
+ res.status_code != HTTPStatus.OK
163
+ or not res.output
164
+ or (
165
+ hasattr(res.output, "task_status")
166
+ and res.output.task_status in ["FAILED", "CANCELED"]
167
+ )
168
+ ):
169
+ raise RuntimeError(f"Failed to fetch result: {res}")
170
+
171
+ # Check if task is completed
172
+ if res.status_code == HTTPStatus.OK:
173
+ if hasattr(res.output, "task_status"):
174
+ if res.output.task_status == "SUCCEEDED":
175
+ break
176
+ elif res.output.task_status in ["FAILED", "CANCELED"]:
177
+ raise RuntimeError(f"Failed to generate: {res}")
178
+ else:
179
+ # If no task_status field, consider it completed
180
+ break
181
+
182
+ # Timeout check
183
+ if time.time() - start_time > max_wait_time:
184
+ raise TimeoutError(
185
+ f"Image editing timeout after {max_wait_time}s",
186
+ )
187
+
188
+ if request_id == "":
189
+ request_id = (
190
+ res.request_id if res.request_id else str(uuid.uuid4())
191
+ )
192
+
193
+ if trace_event:
194
+ trace_event.on_log(
195
+ "",
196
+ **{
197
+ "step_suffix": "results",
198
+ "payload": {
199
+ "request_id": request_id,
200
+ "image_query_result": res,
201
+ },
202
+ },
203
+ )
204
+ results = []
205
+ if res.status_code == HTTPStatus.OK:
206
+ for result in res.output.results:
207
+ results.append(result.url)
208
+ return ImageGenOutput(results=results, request_id=request_id)
@@ -0,0 +1,193 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=no-else-break, too-many-branches, abstract-method
3
+
4
+ import asyncio
5
+ import os
6
+ import time
7
+ import uuid
8
+ from http import HTTPStatus
9
+ from typing import Any, Optional
10
+
11
+ from dashscope.aigc.image_synthesis import AioImageSynthesis
12
+ from mcp.server.fastmcp import Context
13
+ from pydantic import BaseModel, Field
14
+
15
+ from ..base import Tool
16
+ from ..utils.api_key_util import ApiNames, get_api_key
17
+ from ...engine.tracing import trace, TracingUtil
18
+
19
+
20
+ class ImageGenInput(BaseModel):
21
+ """
22
+ Image-to-Image Input
23
+ """
24
+
25
+ images: list[str] = Field(
26
+ ..., # Required
27
+ description="输入图像URL数组。URL不能包含中文字符,需为公网可访问地址。",
28
+ )
29
+ prompt: str = Field(
30
+ ...,
31
+ description="正向提示词,用来描述生成图像中期望包含的元素和视觉特点, 超过2000自动截断",
32
+ )
33
+ negative_prompt: Optional[str] = Field(
34
+ default=None,
35
+ description="反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制,超过500个字符自动截断",
36
+ )
37
+ n: Optional[int] = Field(
38
+ default=1,
39
+ description="生成图片的数量。取值范围为1~4张 默认1",
40
+ )
41
+ watermark: Optional[bool] = Field(
42
+ default=None,
43
+ description="是否添加水印,默认不设置",
44
+ )
45
+ ctx: Optional[Context] = Field(
46
+ default=None,
47
+ description="HTTP request context containing headers for mcp only, "
48
+ "don't generate it",
49
+ )
50
+
51
+
52
+ class ImageGenOutput(BaseModel):
53
+ """
54
+ Text-to-Image Output.
55
+ """
56
+
57
+ results: list[str] = Field(title="Results", description="输出图片url 列表")
58
+ request_id: Optional[str] = Field(
59
+ default=None,
60
+ title="Request ID",
61
+ description="请求ID",
62
+ )
63
+
64
+
65
+ class ImageEditWan25(Tool[ImageGenInput, ImageGenOutput]):
66
+ """
67
+ Image-to-Image Call.
68
+ """
69
+
70
+ name: str = "modelstudio_image_edit_wan25"
71
+ description: str = "AI图像编辑(图生图)服务,输入原图URL、编辑功能、文本描述和分辨率,返回编辑后的图片URL。"
72
+
73
+ @trace(trace_type="AIGC", trace_name="image_edit_wan25")
74
+ async def arun(self, args: ImageGenInput, **kwargs: Any) -> ImageGenOutput:
75
+ """Modelstudio image editing from base image and text prompts
76
+
77
+ This method wraps DashScope's ImageSynthesis service to generate new
78
+ images based on the input image and editing instructions. Supports
79
+ various editing functions, resolutions, and batch generation.
80
+
81
+ Args:
82
+ args: ImageGenInput containing function, base_image_url,
83
+ mask_image_url, prompt, size, n.
84
+ **kwargs: Additional keyword arguments including request_id,
85
+ trace_event, model_name, api_key.
86
+
87
+ Returns:
88
+ ImageGenOutput containing the list of generated image URLs and
89
+ request ID.
90
+
91
+ Raises:
92
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
93
+ """
94
+
95
+ trace_event = kwargs.pop("trace_event", None)
96
+ request_id = TracingUtil.get_request_id()
97
+
98
+ try:
99
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
100
+ except AssertionError as e:
101
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
102
+
103
+ model_name = kwargs.get(
104
+ "model_name",
105
+ os.getenv("IMAGE_EDIT_MODEL_NAME", "wan2.5-i2i-preview"),
106
+ )
107
+
108
+ parameters = {}
109
+ if args.n is not None:
110
+ parameters["n"] = args.n
111
+ if args.watermark is not None:
112
+ parameters["watermark"] = args.watermark
113
+
114
+ # 🔄 Use DashScope asynchronous task API to achieve true concurrency
115
+ # 1. Submit asynchronous task
116
+ task_response = await AioImageSynthesis.async_call(
117
+ model=model_name,
118
+ api_key=api_key,
119
+ prompt=args.prompt,
120
+ negative_prompt=args.negative_prompt,
121
+ images=args.images,
122
+ **parameters,
123
+ )
124
+
125
+ if (
126
+ task_response.status_code != HTTPStatus.OK
127
+ or not task_response.output
128
+ ):
129
+ raise RuntimeError(f"Failed to submit task: {task_response}")
130
+
131
+ # 2. Loop to asynchronously query task status
132
+ max_wait_time = 300 # 5 minutes timeout
133
+ poll_interval = 2 # 2 seconds polling interval
134
+ start_time = time.time()
135
+
136
+ while True:
137
+ # Asynchronous wait
138
+ await asyncio.sleep(poll_interval)
139
+
140
+ # Query task result
141
+ res = await AioImageSynthesis.fetch(
142
+ api_key=api_key,
143
+ task=task_response,
144
+ )
145
+
146
+ if (
147
+ res.status_code != HTTPStatus.OK
148
+ or not res.output
149
+ or (
150
+ hasattr(res.output, "task_status")
151
+ and res.output.task_status in ["FAILED", "CANCELED"]
152
+ )
153
+ ):
154
+ raise RuntimeError(f"Failed to fetch result: {res}")
155
+
156
+ # Check if task is completed
157
+ if res.status_code == HTTPStatus.OK:
158
+ if hasattr(res.output, "task_status"):
159
+ if res.output.task_status == "SUCCEEDED":
160
+ break
161
+ elif res.output.task_status in ["FAILED", "CANCELED"]:
162
+ raise RuntimeError(f"Failed to generate: {res}")
163
+ else:
164
+ # If no task_status field, consider task completed
165
+ break
166
+
167
+ # Timeout check
168
+ if time.time() - start_time > max_wait_time:
169
+ raise TimeoutError(
170
+ f"Image editing timeout after {max_wait_time}s",
171
+ )
172
+
173
+ if request_id == "":
174
+ request_id = (
175
+ res.request_id if res.request_id else str(uuid.uuid4())
176
+ )
177
+
178
+ if trace_event:
179
+ trace_event.on_log(
180
+ "",
181
+ **{
182
+ "step_suffix": "results",
183
+ "payload": {
184
+ "request_id": request_id,
185
+ "image_query_result": res,
186
+ },
187
+ },
188
+ )
189
+ results = []
190
+ if res.status_code == HTTPStatus.OK:
191
+ for result in res.output.results:
192
+ results.append(result.url)
193
+ return ImageGenOutput(results=results, request_id=request_id)
@@ -0,0 +1,202 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=abstract-method, deprecated-module, wrong-import-order
3
+ # pylint:disable=no-else-break, too-many-branches
4
+
5
+ import asyncio
6
+ import os
7
+ import time
8
+ import uuid
9
+ from http import HTTPStatus
10
+ from typing import Any, Optional
11
+
12
+ from dashscope.aigc.image_synthesis import AioImageSynthesis
13
+ from mcp.server.fastmcp import Context
14
+ from pydantic import BaseModel, Field
15
+
16
+ from ..base import Tool
17
+ from ..utils.api_key_util import get_api_key, ApiNames
18
+ from ...engine.tracing import trace, TracingUtil
19
+
20
+
21
+ class ImageGenInput(BaseModel):
22
+ """
23
+ Text-to-Image Input
24
+ """
25
+
26
+ prompt: str = Field(
27
+ ...,
28
+ description="正向提示词,用来描述生成图像中期望包含的元素和视觉特点,超过800自动截断",
29
+ )
30
+ size: Optional[str] = Field(
31
+ default=None,
32
+ description="输出图像的分辨率。默认值是1024*1024 最高可达200万像素",
33
+ )
34
+ negative_prompt: Optional[str] = Field(
35
+ default=None,
36
+ description="反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制,超过500个字符自动截断",
37
+ )
38
+ prompt_extend: Optional[bool] = Field(
39
+ default=None,
40
+ description="是否开启prompt智能改写,开启后使用大模型对输入prompt进行智能改写",
41
+ )
42
+ n: Optional[int] = Field(
43
+ default=1,
44
+ description="生成图片的数量。取值范围为1~4张 默认1",
45
+ )
46
+ watermark: Optional[bool] = Field(
47
+ default=None,
48
+ description="是否添加水印,默认不设置",
49
+ )
50
+ ctx: Optional[Context] = Field(
51
+ default=None,
52
+ description="HTTP request context containing headers for mcp only, "
53
+ "don't generate it",
54
+ )
55
+
56
+
57
+ class ImageGenOutput(BaseModel):
58
+ """
59
+ Text-to-Image Output.
60
+ """
61
+
62
+ results: list[str] = Field(title="Results", description="输出图片url 列表")
63
+ request_id: Optional[str] = Field(
64
+ default=None,
65
+ title="Request ID",
66
+ description="请求ID",
67
+ )
68
+
69
+
70
+ class ImageGeneration(Tool[ImageGenInput, ImageGenOutput]):
71
+ """
72
+ Text-to-Image Call.
73
+ """
74
+
75
+ name: str = "modelstudio_image_gen"
76
+ description: str = "AI绘画(图像生成)服务,输入文本描述和图像分辨率,返回根据文本信息绘制的图片URL。"
77
+
78
+ @trace(trace_type="AIGC", trace_name="image_generation")
79
+ async def arun(self, args: ImageGenInput, **kwargs: Any) -> ImageGenOutput:
80
+ """Modelstudio Images generation from text prompts
81
+
82
+ This method wrap DashScope's ImageSynthesis service to generate images
83
+ based on text descriptions. It supports various image sizes and can
84
+ generate multiple images in a single request.
85
+
86
+ Args:
87
+ args: ImageGenInput containing the prompt, size, and number of
88
+ images to generate.
89
+ **kwargs: Additional keyword arguments including:
90
+ - request_id: Optional request ID for tracking
91
+ - trace_event: Optional trace event for logging
92
+ - model_name: Model name to use (defaults to wan2.2-t2i-flash)
93
+ - api_key: DashScope API key for authentication
94
+
95
+ Returns:
96
+ ImageGenOutput containing the list of generated image URLs and
97
+ request ID.
98
+
99
+ Raises:
100
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
101
+ """
102
+
103
+ trace_event = kwargs.pop("trace_event", None)
104
+ request_id = TracingUtil.get_request_id()
105
+
106
+ try:
107
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
108
+ except AssertionError as e:
109
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
110
+
111
+ model_name = kwargs.get(
112
+ "model_name",
113
+ os.getenv("IMAGE_GENERATION_MODEL_NAME", "wan2.2-t2i-flash"),
114
+ )
115
+
116
+ parameters = {}
117
+ if args.size:
118
+ parameters["size"] = args.size
119
+ if args.prompt_extend is not None:
120
+ parameters["prompt_extend"] = args.prompt_extend
121
+ if args.n is not None:
122
+ parameters["n"] = args.n
123
+ if args.watermark is not None:
124
+ parameters["watermark"] = args.watermark
125
+
126
+ task_response = await AioImageSynthesis.async_call(
127
+ model=model_name,
128
+ api_key=api_key,
129
+ prompt=args.prompt,
130
+ negative_prompt=args.negative_prompt,
131
+ **parameters,
132
+ )
133
+
134
+ if (
135
+ task_response.status_code != HTTPStatus.OK
136
+ or not task_response.output
137
+ ):
138
+ raise RuntimeError(f"Failed to submit task: {task_response}")
139
+
140
+ # 2. Loop to asynchronously query task status
141
+ max_wait_time = 300 # 5 minutes timeout
142
+ poll_interval = 2 # 2 seconds polling interval
143
+ start_time = time.time()
144
+
145
+ while True:
146
+ # Asynchronous wait
147
+ await asyncio.sleep(poll_interval)
148
+
149
+ # Query task result
150
+ res = await AioImageSynthesis.fetch(
151
+ api_key=api_key,
152
+ task=task_response,
153
+ )
154
+
155
+ if (
156
+ res.status_code != HTTPStatus.OK
157
+ or not res.output
158
+ or (
159
+ hasattr(res.output, "task_status")
160
+ and res.output.task_status in ["FAILED", "CANCELED"]
161
+ )
162
+ ):
163
+ raise RuntimeError(f"Failed to fetch result: {res}")
164
+
165
+ # Check if task is completed
166
+ if res.status_code == HTTPStatus.OK:
167
+ if hasattr(res.output, "task_status"):
168
+ if res.output.task_status == "SUCCEEDED":
169
+ break
170
+ elif res.output.task_status in ["FAILED", "CANCELED"]:
171
+ raise RuntimeError(f"Failed to generate: {res}")
172
+ else:
173
+ # If no task_status field, consider task completed
174
+ break
175
+
176
+ # Timeout check
177
+ if time.time() - start_time > max_wait_time:
178
+ raise TimeoutError(
179
+ f"Image generation timeout after {max_wait_time}s",
180
+ )
181
+
182
+ if request_id == "":
183
+ request_id = (
184
+ res.request_id if res.request_id else str(uuid.uuid4())
185
+ )
186
+
187
+ if trace_event:
188
+ trace_event.on_log(
189
+ "",
190
+ **{
191
+ "step_suffix": "results",
192
+ "payload": {
193
+ "request_id": request_id,
194
+ "image_query_result": res,
195
+ },
196
+ },
197
+ )
198
+ results = []
199
+ if res.status_code == HTTPStatus.OK:
200
+ for result in res.output.results:
201
+ results.append(result.url)
202
+ return ImageGenOutput(results=results, request_id=request_id)