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,201 @@
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
+ Text-to-Image Input.
23
+ """
24
+
25
+ prompt: str = Field(
26
+ ...,
27
+ description="正向提示词,用来描述生成图像中期望包含的元素和视觉特点,超过800自动截断",
28
+ )
29
+ size: Optional[str] = Field(
30
+ default=None,
31
+ description="输出图像的分辨率。默认值是1280*1280,可不填。",
32
+ )
33
+ negative_prompt: Optional[str] = Field(
34
+ default=None,
35
+ description="反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制,超过500个字符自动截断",
36
+ )
37
+ prompt_extend: Optional[bool] = Field(
38
+ default=None,
39
+ description="是否开启prompt智能改写,开启后使用大模型对输入prompt进行智能改写",
40
+ )
41
+ n: Optional[int] = Field(
42
+ default=1,
43
+ description="生成图片的数量。取值范围为1~4张 默认1",
44
+ )
45
+ watermark: Optional[bool] = Field(
46
+ default=None,
47
+ description="是否添加水印,默认不设置",
48
+ )
49
+ ctx: Optional[Context] = Field(
50
+ default=None,
51
+ description="HTTP request context containing headers for mcp only, "
52
+ "don't generate it",
53
+ )
54
+
55
+
56
+ class ImageGenOutput(BaseModel):
57
+ """
58
+ Text-to-Image Output.
59
+ """
60
+
61
+ results: list[str] = Field(title="Results", description="输出图片url 列表")
62
+ request_id: Optional[str] = Field(
63
+ default=None,
64
+ title="Request ID",
65
+ description="请求ID",
66
+ )
67
+
68
+
69
+ class ImageGenerationWan25(Tool[ImageGenInput, ImageGenOutput]):
70
+ """
71
+ Text-to-Image Call.
72
+ """
73
+
74
+ name: str = "modelstudio_image_gen_wan25"
75
+ description: str = "AI绘画(图像生成)服务,输入文本描述和图像分辨率,返回根据文本信息绘制的图片URL。"
76
+
77
+ @trace(trace_type="AIGC", trace_name="image_generation_wan25")
78
+ async def arun(self, args: ImageGenInput, **kwargs: Any) -> ImageGenOutput:
79
+ """Modelstudio Images generation from text prompts
80
+
81
+ This method wrap DashScope's ImageSynthesis service to generate images
82
+ based on text descriptions. It supports various image sizes and can
83
+ generate multiple images in a single request.
84
+
85
+ Args:
86
+ args: ImageGenInput containing the prompt, size, and number of
87
+ images to generate.
88
+ **kwargs: Additional keyword arguments including:
89
+ - request_id: Optional request ID for tracking
90
+ - trace_event: Optional trace event for logging
91
+ - model_name: Model name to use (defaults to wan2.2-t2i-flash)
92
+ - api_key: DashScope API key for authentication
93
+
94
+ Returns:
95
+ ImageGenOutput containing the list of generated image URLs and
96
+ request ID.
97
+
98
+ Raises:
99
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
100
+ """
101
+
102
+ trace_event = kwargs.pop("trace_event", None)
103
+ request_id = TracingUtil.get_request_id()
104
+
105
+ try:
106
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
107
+ except AssertionError as e:
108
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
109
+
110
+ model_name = kwargs.get(
111
+ "model_name",
112
+ os.getenv("IMAGE_GENERATION_MODEL_NAME", "wan2.5-t2i-preview"),
113
+ )
114
+
115
+ parameters = {}
116
+ if args.size:
117
+ parameters["size"] = args.size
118
+ if args.prompt_extend is not None:
119
+ parameters["prompt_extend"] = args.prompt_extend
120
+ if args.n is not None:
121
+ parameters["n"] = args.n
122
+ if args.watermark is not None:
123
+ parameters["watermark"] = args.watermark
124
+
125
+ task_response = await AioImageSynthesis.async_call(
126
+ model=model_name,
127
+ api_key=api_key,
128
+ prompt=args.prompt,
129
+ negative_prompt=args.negative_prompt,
130
+ **parameters,
131
+ )
132
+
133
+ if (
134
+ task_response.status_code != HTTPStatus.OK
135
+ or not task_response.output
136
+ ):
137
+ raise RuntimeError(f"Failed to submit task: {task_response}")
138
+
139
+ # 2. Loop to asynchronously query task status
140
+ max_wait_time = 300 # 5 minutes timeout
141
+ poll_interval = 2 # 2 seconds polling interval
142
+ start_time = time.time()
143
+
144
+ while True:
145
+ # Asynchronous wait
146
+ await asyncio.sleep(poll_interval)
147
+
148
+ # Query task result
149
+ res = await AioImageSynthesis.fetch(
150
+ api_key=api_key,
151
+ task=task_response,
152
+ )
153
+
154
+ if (
155
+ res.status_code != HTTPStatus.OK
156
+ or not res.output
157
+ or (
158
+ hasattr(res.output, "task_status")
159
+ and res.output.task_status in ["FAILED", "CANCELED"]
160
+ )
161
+ ):
162
+ raise RuntimeError(f"Failed to fetch result: {res}")
163
+
164
+ # Check if task is completed
165
+ if res.status_code == HTTPStatus.OK:
166
+ if hasattr(res.output, "task_status"):
167
+ if res.output.task_status == "SUCCEEDED":
168
+ break
169
+ elif res.output.task_status in ["FAILED", "CANCELED"]:
170
+ raise RuntimeError(f"Failed to generate: {res}")
171
+ else:
172
+ # If no task_status field, consider task completed
173
+ break
174
+
175
+ # Timeout check
176
+ if time.time() - start_time > max_wait_time:
177
+ raise TimeoutError(
178
+ f"Image generation timeout after {max_wait_time}s",
179
+ )
180
+
181
+ if request_id == "":
182
+ request_id = (
183
+ res.request_id if res.request_id else str(uuid.uuid4())
184
+ )
185
+
186
+ if trace_event:
187
+ trace_event.on_log(
188
+ "",
189
+ **{
190
+ "step_suffix": "results",
191
+ "payload": {
192
+ "request_id": request_id,
193
+ "image_query_result": res,
194
+ },
195
+ },
196
+ )
197
+ results = []
198
+ if res.status_code == HTTPStatus.OK:
199
+ for result in res.output.results:
200
+ results.append(result.url)
201
+ return ImageGenOutput(results=results, request_id=request_id)
@@ -0,0 +1,208 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=abstract-method, deprecated-module, wrong-import-order
3
+ # pylint:disable=redefined-builtin
4
+
5
+ import asyncio
6
+ import os
7
+ import uuid
8
+ from concurrent.futures import ThreadPoolExecutor
9
+ from http import HTTPStatus
10
+ from typing import Any, Optional
11
+
12
+ from dashscope.client.base_api import BaseAsyncApi
13
+ from dashscope.utils.oss_utils import check_and_upload_local
14
+ from mcp.server.fastmcp import Context
15
+ from pydantic import BaseModel, Field
16
+
17
+ from ..base import Tool
18
+ from ..utils.api_key_util import get_api_key, ApiNames
19
+ from ...engine.tracing import trace, TracingUtil
20
+
21
+
22
+ class ImageStyleRepaintInput(BaseModel):
23
+ """
24
+ Portrait Style Repaint Input
25
+ """
26
+
27
+ image_url: str = Field(
28
+ ...,
29
+ description="输入图像的URL地址。",
30
+ )
31
+
32
+ style_index: int = Field(
33
+ ...,
34
+ description="人像风格类型索引值,当前支持以下风格:-1:参考上传图像风格, "
35
+ "0:复古漫画, 1:3D童话, 2:二次元, 3:小清新, 4:未来科技, "
36
+ "5:国画古风, 6:将军百战, 7:炫彩卡通, 8:清雅国风, 9:喜迎新年。",
37
+ )
38
+
39
+ style_ref_url: Optional[str] = Field(
40
+ default=None,
41
+ description="风格参考图像的URL地址。当参数style_index等于-1时,必须传入," "其他风格无需传入。",
42
+ )
43
+
44
+ watermark: Optional[bool] = Field(
45
+ default=None,
46
+ description="是否添加水印,默认不设置",
47
+ )
48
+
49
+ ctx: Optional[Context] = Field(
50
+ default=None,
51
+ description="HTTP request context containing headers for mcp only, "
52
+ "don't generate it",
53
+ )
54
+
55
+
56
+ class ImageStyleRepaintOutput(BaseModel):
57
+ """
58
+ Portrait Style Repaint Output
59
+ """
60
+
61
+ results: list[str] = Field(title="Results", description="输出图片url 列表")
62
+ request_id: Optional[str] = Field(
63
+ default=None,
64
+ title="Request ID",
65
+ description="请求ID",
66
+ )
67
+
68
+
69
+ class ImageStyleRepaint(
70
+ Tool[ImageStyleRepaintInput, ImageStyleRepaintOutput],
71
+ ):
72
+ """
73
+ Portrait Style Repaint
74
+ """
75
+
76
+ name: str = "modelstudio_image_style_repaint"
77
+ description: str = "人像风格重绘服务,输入原始图像和风格数据(索引或参考图像),返回重绘后的图像。"
78
+
79
+ def __init__(self, name: str = None, description: str = None):
80
+ super().__init__(name=name, description=description)
81
+ # Create thread pool to execute synchronous BaseAsyncApi calls
82
+ self._executor = ThreadPoolExecutor(
83
+ max_workers=10,
84
+ thread_name_prefix="StyleRepaint",
85
+ )
86
+
87
+ @trace(trace_type="AIGC", trace_name="image_style_repaint")
88
+ async def arun(
89
+ self,
90
+ args: ImageStyleRepaintInput,
91
+ **kwargs: Any,
92
+ ) -> ImageStyleRepaintOutput:
93
+ """Modelstudio Image Style Repaint
94
+
95
+ This method wrap DashScope's ImageStyleRepaint service to generate
96
+ images based on image url and style index (or style reference image
97
+ url).
98
+
99
+ Args:
100
+ args: ImageStyleRepaintInput containing the image_url,
101
+ style_index, and style_ref_url.
102
+ **kwargs: Additional keyword arguments including:
103
+ - request_id: Optional request ID for tracking
104
+ - trace_event: Optional trace event for logging
105
+ - model_name: Model name to use (defaults to wanx2.1-t2i-turbo)
106
+ - api_key: DashScope API key for authentication
107
+
108
+ Returns:
109
+ ImageStyleRepaintOutput containing the list of generated image
110
+ URLs and request ID.
111
+
112
+ Raises:
113
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
114
+ """
115
+
116
+ trace_event = kwargs.pop("trace_event", None)
117
+ request_id = TracingUtil.get_request_id()
118
+
119
+ try:
120
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
121
+ except AssertionError as e:
122
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
123
+
124
+ model_name = kwargs.get(
125
+ "model_name",
126
+ os.getenv(
127
+ "IMAGE_STYLE_REPAINT_MODEL_NAME",
128
+ "wanx-style-repaint-v1",
129
+ ),
130
+ )
131
+
132
+ has_uploaded = False
133
+
134
+ image_url = args.image_url
135
+ if args.image_url:
136
+ uploaded, image_url, _ = check_and_upload_local(
137
+ model=model_name,
138
+ content=args.image_url,
139
+ api_key=api_key,
140
+ )
141
+ has_uploaded = True if uploaded is True else has_uploaded
142
+
143
+ style_ref_url = args.style_ref_url
144
+ if args.style_ref_url:
145
+ uploaded, style_ref_url = check_and_upload_local(
146
+ model=model_name,
147
+ content=args.style_ref_url,
148
+ api_key=api_key,
149
+ )
150
+ has_uploaded = True if uploaded is True else has_uploaded
151
+
152
+ kwargs = {}
153
+ if has_uploaded is True:
154
+ headers = {"X-DashScope-OssResourceResolve": "enable"}
155
+ kwargs["headers"] = headers
156
+
157
+ # 🔄 Put BaseAsyncApi.call into thread pool to avoid blocking
158
+ # event loop
159
+ def _sync_style_repaint_call() -> Any:
160
+ input = {
161
+ "image_url": image_url,
162
+ "style_index": args.style_index,
163
+ "style_ref_url": style_ref_url,
164
+ }
165
+ if args.watermark is not None:
166
+ input["watermark"] = args.watermark
167
+ return BaseAsyncApi.call(
168
+ model=model_name,
169
+ input=input,
170
+ task_group="aigc",
171
+ task="image-generation",
172
+ function="generation",
173
+ **kwargs,
174
+ )
175
+
176
+ # Execute synchronous calls asynchronously in thread pool
177
+ res = await asyncio.get_event_loop().run_in_executor(
178
+ self._executor,
179
+ _sync_style_repaint_call,
180
+ )
181
+
182
+ if res.status_code != HTTPStatus.OK or not res.output:
183
+ raise RuntimeError(f"Failed to generate image: {res}")
184
+
185
+ if request_id == "":
186
+ request_id = (
187
+ res.request_id if res.request_id else str(uuid.uuid4())
188
+ )
189
+
190
+ if trace_event:
191
+ trace_event.on_log(
192
+ "",
193
+ **{
194
+ "step_suffix": "results",
195
+ "payload": {
196
+ "request_id": request_id,
197
+ "image_query_result": res,
198
+ },
199
+ },
200
+ )
201
+
202
+ results = []
203
+ if res.status_code == HTTPStatus.OK:
204
+ for result in res.output.get("results"):
205
+ if result.get("url"):
206
+ results.append(result.get("url"))
207
+
208
+ return ImageStyleRepaintOutput(results=results, request_id=request_id)
@@ -0,0 +1,233 @@
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.video_synthesis import AioVideoSynthesis
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 ImageToVideoInput(BaseModel):
22
+ """
23
+ Image to video generation input model
24
+ """
25
+
26
+ image_url: str = Field(
27
+ ...,
28
+ description="输入图像,支持公网URL、Base64编码或本地文件路径",
29
+ )
30
+ prompt: Optional[str] = Field(
31
+ default=None,
32
+ description="正向提示词,用来描述生成视频中期望包含的元素和视觉特点",
33
+ )
34
+ negative_prompt: Optional[str] = Field(
35
+ default=None,
36
+ description="反向提示词,用来描述不希望在视频画面中看到的内容",
37
+ )
38
+ template: Optional[str] = Field(
39
+ default=None,
40
+ description="视频特效模板,可选值:squish(解压捏捏)、flying(魔法悬浮)、carousel(时光木马)等",
41
+ )
42
+ resolution: Optional[str] = Field(
43
+ default=None,
44
+ description="视频分辨率,默认不设置",
45
+ )
46
+ duration: Optional[int] = Field(
47
+ default=None,
48
+ description="视频生成时长,单位为秒,通常为5秒",
49
+ )
50
+ prompt_extend: Optional[bool] = Field(
51
+ default=None,
52
+ description="是否开启prompt智能改写,开启后使用大模型对输入prompt进行智能改写",
53
+ )
54
+ watermark: Optional[bool] = Field(
55
+ default=None,
56
+ description="是否添加水印,默认不设置",
57
+ )
58
+ ctx: Optional[Context] = Field(
59
+ default=None,
60
+ description="HTTP request context containing headers for mcp only, "
61
+ "don't generate it",
62
+ )
63
+
64
+
65
+ class ImageToVideoOutput(BaseModel):
66
+ """
67
+ Image to video generation output model
68
+ """
69
+
70
+ video_url: str = Field(
71
+ title="Video URL",
72
+ description="输出的视频url",
73
+ )
74
+ request_id: Optional[str] = Field(
75
+ default=None,
76
+ title="Request ID",
77
+ description="请求ID",
78
+ )
79
+
80
+
81
+ class ImageToVideo(Tool[ImageToVideoInput, ImageToVideoOutput]):
82
+ """
83
+ Image to video generation service that converts images into videos
84
+ using DashScope's VideoSynthesis API.
85
+ """
86
+
87
+ name: str = "modelstudio_image_to_video"
88
+ description: str = (
89
+ "通义万相-图生视频模型根据首帧图像和文本提示词,生成时长为5秒的无声视频。"
90
+ "同时支持特效模板,可添加“魔法悬浮”、“气球膨胀”等效果,适用于创意视频制作、娱乐特效展示等场景。"
91
+ )
92
+
93
+ @trace(trace_type="AIGC", trace_name="image_to_video")
94
+ async def arun(
95
+ self,
96
+ args: ImageToVideoInput,
97
+ **kwargs: Any,
98
+ ) -> ImageToVideoOutput:
99
+ """
100
+ Generate video from image using DashScope VideoSynthesis
101
+
102
+ This method wraps DashScope's VideoSynthesis service to generate videos
103
+ based on input images. It uses async call pattern for better
104
+ performance and supports polling for task completion.
105
+
106
+ Args:
107
+ args: ImageToVideoInput containing required image_url and optional
108
+ parameters
109
+ **kwargs: Additional keyword arguments including:
110
+ - request_id: Optional request ID for tracking
111
+ - model_name: Model name to use (defaults to wan2.2-i2v-flash)
112
+ - api_key: DashScope API key for authentication
113
+
114
+ Returns:
115
+ ImageToVideoOutput containing the generated video URL
116
+ and request ID
117
+
118
+ Raises:
119
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid
120
+ TimeoutError: If video generation takes too long
121
+ RuntimeError: If video generation fails
122
+ """
123
+ trace_event = kwargs.pop("trace_event", None)
124
+ request_id = TracingUtil.get_request_id()
125
+
126
+ try:
127
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
128
+ except AssertionError as e:
129
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
130
+
131
+ model_name = kwargs.get(
132
+ "model_name",
133
+ os.getenv("IMAGE_TO_VIDEO_MODEL_NAME", "wan2.2-i2v-flash"),
134
+ )
135
+
136
+ parameters = {}
137
+ if args.resolution:
138
+ parameters["resolution"] = args.resolution
139
+ if args.duration is not None:
140
+ parameters["duration"] = args.duration
141
+ if args.prompt_extend is not None:
142
+ parameters["prompt_extend"] = args.prompt_extend
143
+ if args.watermark is not None:
144
+ parameters["watermark"] = args.watermark
145
+
146
+ # Create AioVideoSynthesis instance
147
+ aio_video_synthesis = AioVideoSynthesis()
148
+
149
+ # Submit async task
150
+ task_response = await aio_video_synthesis.async_call(
151
+ model=model_name,
152
+ api_key=api_key,
153
+ img_url=args.image_url,
154
+ prompt=args.prompt,
155
+ negative_prompt=args.negative_prompt,
156
+ template=args.template,
157
+ **parameters,
158
+ )
159
+
160
+ if (
161
+ task_response.status_code != HTTPStatus.OK
162
+ or not task_response.output
163
+ or task_response.output.task_status in ["FAILED", "CANCELED"]
164
+ ):
165
+ raise RuntimeError(f"Failed to submit task: {task_response}")
166
+
167
+ # Poll for task completion using async methods
168
+ max_wait_time = 600 # 10 minutes timeout for video generation
169
+ poll_interval = 5 # 5 seconds polling interval
170
+ start_time = time.time()
171
+
172
+ while True:
173
+ # Wait before polling
174
+ await asyncio.sleep(poll_interval)
175
+
176
+ # Fetch task result using async method
177
+ res = await aio_video_synthesis.fetch(
178
+ api_key=api_key,
179
+ task=task_response,
180
+ )
181
+
182
+ if (
183
+ res.status_code != HTTPStatus.OK
184
+ or not res.output
185
+ or res.output.task_status in ["FAILED", "CANCELED"]
186
+ ):
187
+ raise RuntimeError(f"Failed to fetch result: {res}")
188
+
189
+ # Check task completion status
190
+ if res.status_code == HTTPStatus.OK:
191
+ if hasattr(res.output, "task_status"):
192
+ if res.output.task_status == "SUCCEEDED":
193
+ break
194
+ elif res.output.task_status in ["FAILED", "CANCELED"]:
195
+ raise RuntimeError(f"Failed to generate: {res}")
196
+ else:
197
+ # If no task_status field, assume completed
198
+ break
199
+
200
+ # Check timeout
201
+ if time.time() - start_time > max_wait_time:
202
+ raise TimeoutError(
203
+ f"Video generation timeout after {max_wait_time}s",
204
+ )
205
+
206
+ # Handle request ID
207
+ if not request_id:
208
+ request_id = (
209
+ res.request_id if res.request_id else str(uuid.uuid4())
210
+ )
211
+
212
+ # Log trace event if provided
213
+ if trace_event:
214
+ trace_event.on_log(
215
+ "",
216
+ **{
217
+ "step_suffix": "results",
218
+ "payload": {
219
+ "request_id": request_id,
220
+ "image_to_video_result": res,
221
+ },
222
+ },
223
+ )
224
+
225
+ # Extract video URL from response
226
+ if res.status_code == HTTPStatus.OK:
227
+ video_url = res.output.video_url
228
+ return ImageToVideoOutput(
229
+ video_url=video_url,
230
+ request_id=request_id,
231
+ )
232
+ else:
233
+ raise RuntimeError(f"Failed to get video URL: {res.message}")