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,205 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=abstract-method, deprecated-module, wrong-import-order
3
+ # pylint:disable=too-many-nested-blocks, too-many-branches, unused-import
4
+
5
+ import os
6
+ import uuid
7
+ from typing import Any, Optional
8
+
9
+ from dashscope import AioMultiModalConversation
10
+ from mcp.server.fastmcp import Context
11
+ from pydantic import BaseModel, Field
12
+
13
+ from ..base import Tool
14
+ from ..utils.api_key_util import get_api_key, ApiNames
15
+ from ...engine.tracing import trace, TracingUtil
16
+
17
+
18
+ class QwenImageEditInput(BaseModel):
19
+ """
20
+ Qwen Image Edit Input
21
+ """
22
+
23
+ image_url: str = Field(
24
+ ...,
25
+ description="输入图像的URL地址,需为公网可访问地址,支持 HTTP 或 HTTPS "
26
+ "协议。格式:JPG、JPEG、PNG、BMP、TIFF、WEBP,分辨率[384,"
27
+ "3072],大小不超过10MB。URL不能包含中文字符。",
28
+ )
29
+ prompt: str = Field(
30
+ ...,
31
+ description="正向提示词,用来描述生成图像中期望包含的元素和视觉特点, 超过800个字符自动截断",
32
+ )
33
+ negative_prompt: Optional[str] = Field(
34
+ default=None,
35
+ description="反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制,超过500个字符自动截断",
36
+ )
37
+ watermark: Optional[bool] = Field(
38
+ default=None,
39
+ description="是否添加水印,默认不设置",
40
+ )
41
+ ctx: Optional[Context] = Field(
42
+ default=None,
43
+ description="HTTP request context containing headers for mcp only, "
44
+ "don't generate it",
45
+ )
46
+
47
+
48
+ class QwenImageEditOutput(BaseModel):
49
+ """
50
+ Qwen Image Edit Output
51
+ """
52
+
53
+ results: list[str] = Field(
54
+ title="Results",
55
+ description="输出的图片url列表",
56
+ )
57
+ request_id: Optional[str] = Field(
58
+ default=None,
59
+ title="Request ID",
60
+ description="请求ID",
61
+ )
62
+
63
+
64
+ class QwenImageEdit(Tool[QwenImageEditInput, QwenImageEditOutput]):
65
+ """
66
+ Qwen Image Edit Tool for AI-powered image editing.
67
+ """
68
+
69
+ name: str = "modelstudio_qwen_image_edit"
70
+ description: str = (
71
+ "通义千问-图像编辑模型支持精准的中英双语文字编辑、调色、细节增强、风格迁移、增删物体、改变位置和动作等操作,可实现复杂的图文编辑。"
72
+ )
73
+
74
+ @trace(trace_type="AIGC", trace_name="qwen_image_edit")
75
+ async def arun(
76
+ self,
77
+ args: QwenImageEditInput,
78
+ **kwargs: Any,
79
+ ) -> QwenImageEditOutput:
80
+ """Qwen Image Edit using MultiModalConversation API
81
+
82
+ This method uses DashScope's MultiModalConversation service to edit
83
+ images based on text prompts. The API supports various image editing
84
+ operations through natural language instructions.
85
+
86
+ Args:
87
+ args: QwenImageEditInput containing image_url, text_prompt,
88
+ watermark, and negative_prompt.
89
+ **kwargs: Additional keyword arguments including request_id,
90
+ trace_event, model_name, api_key.
91
+
92
+ Returns:
93
+ QwenImageEditOutput containing the edited image URL and request ID.
94
+
95
+ Raises:
96
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
97
+ RuntimeError: If the API call fails or returns an error.
98
+ """
99
+
100
+ trace_event = kwargs.pop("trace_event", None)
101
+ request_id = TracingUtil.get_request_id()
102
+
103
+ try:
104
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
105
+ except AssertionError as e:
106
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
107
+
108
+ model_name = kwargs.get(
109
+ "model_name",
110
+ os.getenv("QWEN_IMAGE_EDIT_MODEL_NAME", "qwen-image-edit"),
111
+ )
112
+
113
+ # Prepare messages in the format expected by MultiModalConversation
114
+ messages = [
115
+ {
116
+ "role": "user",
117
+ "content": [
118
+ {"image": args.image_url},
119
+ {"text": args.prompt},
120
+ ],
121
+ },
122
+ ]
123
+
124
+ parameters = {}
125
+ if args.negative_prompt:
126
+ parameters["negative_prompt"] = args.negative_prompt
127
+ if args.watermark is not None:
128
+ parameters["watermark"] = args.watermark
129
+
130
+ # Call the AioMultiModalConversation API asynchronously
131
+ try:
132
+ response = await AioMultiModalConversation.call(
133
+ api_key=api_key,
134
+ model=model_name,
135
+ messages=messages,
136
+ **parameters,
137
+ )
138
+ except Exception as e:
139
+ raise RuntimeError(
140
+ f"Failed to call Qwen Image Edit API: " f"{str(e)}",
141
+ ) from e
142
+
143
+ # Check response status
144
+ if response.status_code != 200 or not response.output:
145
+ raise RuntimeError(f"Failed to generate: {response}")
146
+
147
+ # Extract the edited image URLs from response
148
+ try:
149
+ # The response structure may vary, try different possible locations
150
+ results = []
151
+
152
+ # Try to get from output.choices[0].message.content
153
+ if hasattr(response, "output") and response.output:
154
+ choices = getattr(response.output, "choices", [])
155
+ if choices:
156
+ message = getattr(choices[0], "message", {})
157
+ if hasattr(message, "content"):
158
+ content = message.content
159
+ if isinstance(content, list):
160
+ # Look for image content in the list
161
+ for item in content:
162
+ if isinstance(item, dict) and "image" in item:
163
+ results.append(item["image"])
164
+ elif isinstance(content, str):
165
+ results.append(content)
166
+ elif isinstance(content, dict) and "image" in content:
167
+ results.append(content["image"])
168
+
169
+ if not results:
170
+ raise RuntimeError(
171
+ f"Could not extract edited image URLs from response: "
172
+ f"{response}",
173
+ )
174
+
175
+ except Exception as e:
176
+ raise RuntimeError(
177
+ f"Failed to parse response from Qwen Image Edit API: {str(e)}",
178
+ ) from e
179
+
180
+ # Get request ID
181
+ if request_id == "":
182
+ request_id = getattr(response, "request_id", None) or str(
183
+ uuid.uuid4(),
184
+ )
185
+
186
+ # Log trace event if provided
187
+ if trace_event:
188
+ trace_event.on_log(
189
+ "",
190
+ **{
191
+ "step_suffix": "results",
192
+ "payload": {
193
+ "request_id": request_id,
194
+ "qwen_image_edit_result": {
195
+ "status_code": response.status_code,
196
+ "results": results,
197
+ },
198
+ },
199
+ },
200
+ )
201
+
202
+ return QwenImageEditOutput(
203
+ results=results,
204
+ request_id=request_id,
205
+ )
@@ -0,0 +1,214 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=abstract-method, deprecated-module, wrong-import-order
3
+ # pylint:disable=too-many-nested-blocks, too-many-branches, too-many-statements
4
+
5
+ import os
6
+ import uuid
7
+ from typing import Any, Optional
8
+
9
+ from dashscope import AioMultiModalConversation
10
+ from mcp.server.fastmcp import Context
11
+ from pydantic import BaseModel, Field
12
+
13
+ from ..base import Tool
14
+ from ..utils.api_key_util import get_api_key, ApiNames
15
+ from ...engine.tracing import trace, TraceType, TracingUtil
16
+
17
+
18
+ class QwenImageGenInput(BaseModel):
19
+ """
20
+ Qwen Image Edit Input
21
+ """
22
+
23
+ prompt: str = Field(
24
+ ...,
25
+ description="正向提示词,用来描述生成图像中期望包含的元素和视觉特点, 超过800个字符自动截断",
26
+ )
27
+ negative_prompt: Optional[str] = Field(
28
+ default=None,
29
+ description="反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制,超过500个字符自动截断",
30
+ )
31
+ size: str = Field(
32
+ default=None,
33
+ description="输出图像的分辨率,格式为宽*高。默认分辨率为1328*1328",
34
+ )
35
+ n: int = Field(
36
+ default=None,
37
+ description="生成图片的数量。默认值为1。",
38
+ )
39
+ prompt_extend: Optional[bool] = Field(
40
+ default=None,
41
+ description="是否开启prompt智能改写,开启后使用大模型对输入prompt进行智能改写",
42
+ )
43
+ watermark: Optional[bool] = Field(
44
+ default=None,
45
+ description="是否添加水印,默认不设置",
46
+ )
47
+ ctx: Optional[Context] = Field(
48
+ default=None,
49
+ description="HTTP request context containing headers for mcp only, "
50
+ "don't generate it",
51
+ )
52
+
53
+
54
+ class QwenImageGenOutput(BaseModel):
55
+ """
56
+ Qwen Image Edit Output
57
+ """
58
+
59
+ results: list[str] = Field(
60
+ title="Results",
61
+ description="输出的图片url列表",
62
+ )
63
+ request_id: Optional[str] = Field(
64
+ default=None,
65
+ title="Request ID",
66
+ description="请求ID",
67
+ )
68
+
69
+
70
+ class QwenImageGen(Tool[QwenImageGenInput, QwenImageGenOutput]):
71
+ """
72
+ Qwen Image Generation Tool for AI-powered image editing.
73
+ """
74
+
75
+ name: str = "modelstudio_qwen_image_gen"
76
+ description: str = "通义千问-文生图模型是一款通用图像生成模型,支持多种艺术风格,尤其擅长复杂文本渲染,特别是中英文文本渲染。"
77
+
78
+ @trace(trace_type=TraceType.AIGC, trace_name="qwen_image_gen")
79
+ async def arun(
80
+ self,
81
+ args: QwenImageGenInput,
82
+ **kwargs: Any,
83
+ ) -> QwenImageGenOutput:
84
+ """Qwen Image Edit using MultiModalConversation API
85
+
86
+ This method uses DashScope's MultiModalConversation service to edit
87
+ images based on text prompts. The API supports various image editing
88
+ operations through natural language instructions.
89
+
90
+ Args:
91
+ args: QwenImageGenInput containing text_prompt,
92
+ watermark, and negative_prompt.
93
+ **kwargs: Additional keyword arguments including request_id,
94
+ trace_event, model_name, api_key.
95
+
96
+ Returns:
97
+ QwenImageEditOutput containing the edited image URL and request ID.
98
+
99
+ Raises:
100
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
101
+ RuntimeError: If the API call fails or returns an error.
102
+ """
103
+
104
+ trace_event = kwargs.pop("trace_event", None)
105
+ request_id = TracingUtil.get_request_id()
106
+
107
+ try:
108
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
109
+ except AssertionError as e:
110
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
111
+
112
+ model_name = kwargs.get(
113
+ "model_name",
114
+ os.getenv("QWEN_IMAGE_GENERATION_MODEL_NAME", "qwen-image"),
115
+ )
116
+
117
+ # Prepare messages in the format expected by MultiModalConversation
118
+ messages = [
119
+ {
120
+ "role": "user",
121
+ "content": [
122
+ {"text": args.prompt},
123
+ ],
124
+ },
125
+ ]
126
+
127
+ parameters = {}
128
+ if args.negative_prompt:
129
+ parameters["negative_prompt"] = args.negative_prompt
130
+ if args.size:
131
+ parameters["size"] = args.size
132
+ if args.n is not None:
133
+ parameters["n"] = args.n
134
+ if args.prompt_extend is not None:
135
+ parameters["prompt_extend"] = args.prompt_extend
136
+ if args.watermark is not None:
137
+ parameters["watermark"] = args.watermark
138
+
139
+ # Call the AioMultiModalConversation API asynchronously
140
+ try:
141
+ response = await AioMultiModalConversation.call(
142
+ api_key=api_key,
143
+ model=model_name,
144
+ messages=messages,
145
+ **parameters,
146
+ )
147
+ except Exception as e:
148
+ raise RuntimeError(
149
+ f"Failed to call Qwen Image Edit API: " f"{str(e)}",
150
+ ) from e
151
+
152
+ # Check response status
153
+ if response.status_code != 200 or not response.output:
154
+ raise RuntimeError(f"Failed to generate: {response}")
155
+
156
+ # Extract the edited image URLs from response
157
+ try:
158
+ # The response structure may vary, try different possible locations
159
+ results = []
160
+
161
+ # Try to get from output.choices[0].message.content
162
+ if hasattr(response, "output") and response.output:
163
+ choices = getattr(response.output, "choices", [])
164
+ if choices:
165
+ message = getattr(choices[0], "message", {})
166
+ if hasattr(message, "content"):
167
+ content = message.content
168
+ if isinstance(content, list):
169
+ # Look for image content in the list
170
+ for item in content:
171
+ if isinstance(item, dict) and "image" in item:
172
+ results.append(item["image"])
173
+ elif isinstance(content, str):
174
+ results.append(content)
175
+ elif isinstance(content, dict) and "image" in content:
176
+ results.append(content["image"])
177
+
178
+ if not results:
179
+ raise RuntimeError(
180
+ f"Could not extract edited image URLs from response: "
181
+ f"{response}",
182
+ )
183
+
184
+ except Exception as e:
185
+ raise RuntimeError(
186
+ f"Failed to parse response from Qwen Image Edit API: {str(e)}",
187
+ ) from e
188
+
189
+ # Get request ID
190
+ if request_id == "":
191
+ request_id = getattr(response, "request_id", None) or str(
192
+ uuid.uuid4(),
193
+ )
194
+
195
+ # Log trace event if provided
196
+ if trace_event:
197
+ trace_event.on_log(
198
+ "",
199
+ **{
200
+ "step_suffix": "results",
201
+ "payload": {
202
+ "request_id": request_id,
203
+ "qwen_image_edit_result": {
204
+ "status_code": response.status_code,
205
+ "results": results,
206
+ },
207
+ },
208
+ },
209
+ )
210
+
211
+ return QwenImageGenOutput(
212
+ results=results,
213
+ request_id=request_id,
214
+ )
@@ -0,0 +1,154 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=abstract-method
3
+
4
+ import os
5
+ import uuid
6
+ from typing import Any, Optional
7
+
8
+ import dashscope
9
+ from mcp.server.fastmcp import Context
10
+ from pydantic import BaseModel, Field
11
+
12
+ from ..base import Tool
13
+ from ..utils.api_key_util import get_api_key, ApiNames
14
+ from ...engine.tracing import trace, TracingUtil
15
+
16
+
17
+ class QwenTextToSpeechInput(BaseModel):
18
+ """
19
+ Qwen Image Edit Input
20
+ """
21
+
22
+ text: str = Field(
23
+ ...,
24
+ description="要合成的文本,支持中文、英文、中英混合输入。最长输入为512 Token",
25
+ )
26
+ voice: Optional[str] = Field(
27
+ default=None,
28
+ description="使用的音色,可选值Cherry,Serena,Ethan,Chelsie等",
29
+ )
30
+ ctx: Optional[Context] = Field(
31
+ default=None,
32
+ description="HTTP request context containing headers for mcp only, "
33
+ "don't generate it",
34
+ )
35
+
36
+
37
+ class QwenTextToSpeechOutput(BaseModel):
38
+ """
39
+ Qwen Image Edit Output
40
+ """
41
+
42
+ result: str = Field(
43
+ title="Results",
44
+ description="输出的音频url",
45
+ )
46
+ request_id: Optional[str] = Field(
47
+ default=None,
48
+ title="Request ID",
49
+ description="请求ID",
50
+ )
51
+
52
+
53
+ class QwenTextToSpeech(
54
+ Tool[QwenTextToSpeechInput, QwenTextToSpeechOutput],
55
+ ):
56
+ """
57
+ Qwen Text To Speech Tool for AI-powered speech synthesis.
58
+ """
59
+
60
+ name: str = "modelstudio_qwen_tts"
61
+ description: str = "Qwen-TTS 是通义千问系列的语音合成模型,支持输入中文、英文、中英混合的文本,并流式输出音频。"
62
+
63
+ @trace(trace_type="AIGC", trace_name="qwen_tts")
64
+ async def arun(
65
+ self,
66
+ args: QwenTextToSpeechInput,
67
+ **kwargs: Any,
68
+ ) -> QwenTextToSpeechOutput:
69
+ """Qwen TTS using MultiModalConversation API
70
+
71
+ This method uses DashScope service to synthesis audio based on text.
72
+
73
+ Args:
74
+ args: TextToSpeechInput containing text and voice.
75
+ **kwargs: Additional keyword arguments including request_id,
76
+ trace_event, model_name, api_key.
77
+
78
+ Returns:
79
+ TextToSpeechOutput containing the audio URL and request ID.
80
+
81
+ Raises:
82
+ ValueError: If DASHSCOPE_API_KEY is not set or invalid.
83
+ RuntimeError: If the API call fails or returns an error.
84
+ """
85
+
86
+ trace_event = kwargs.pop("trace_event", None)
87
+ request_id = TracingUtil.get_request_id()
88
+
89
+ try:
90
+ api_key = get_api_key(ApiNames.dashscope_api_key, **kwargs)
91
+ except AssertionError as e:
92
+ raise ValueError("Please set valid DASHSCOPE_API_KEY!") from e
93
+
94
+ model_name = kwargs.get(
95
+ "model_name",
96
+ os.getenv("QWEN_TEXT_TO_SPEECH_MODEL_NAME", "qwen-tts"),
97
+ )
98
+
99
+ try:
100
+ response = dashscope.audio.qwen_tts.SpeechSynthesizer.call(
101
+ api_key=api_key,
102
+ model=model_name,
103
+ text=args.text,
104
+ voice=args.voice,
105
+ )
106
+ except Exception as e:
107
+ raise RuntimeError(f"Failed to call Qwen TTS API: {str(e)}") from e
108
+
109
+ # Check response status
110
+ if response.status_code != 200 or not response.output:
111
+ raise RuntimeError(f"Failed to generate: {response}")
112
+
113
+ # Extract the edited image URLs from response
114
+ try:
115
+ # The response structure may vary, try different possible locations
116
+ result = response.output.audio["url"]
117
+
118
+ if not result:
119
+ raise RuntimeError(
120
+ f"Could not extract audio URLs from response: "
121
+ f"{response}",
122
+ )
123
+
124
+ except Exception as e:
125
+ raise RuntimeError(
126
+ f"Failed to parse response from Qwen TTS API: {str(e)}",
127
+ ) from e
128
+
129
+ # Get request ID
130
+ if request_id == "":
131
+ request_id = getattr(response, "request_id", None) or str(
132
+ uuid.uuid4(),
133
+ )
134
+
135
+ # Log trace event if provided
136
+ if trace_event:
137
+ trace_event.on_log(
138
+ "",
139
+ **{
140
+ "step_suffix": "results",
141
+ "payload": {
142
+ "request_id": request_id,
143
+ "qwen_tts_result": {
144
+ "status_code": response.status_code,
145
+ "result": result,
146
+ },
147
+ },
148
+ },
149
+ )
150
+
151
+ return QwenTextToSpeechOutput(
152
+ result=result,
153
+ request_id=request_id,
154
+ )