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.
- agentscope_runtime/adapters/__init__.py +0 -0
- agentscope_runtime/adapters/agentscope/__init__.py +0 -0
- agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +6 -0
- agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +258 -0
- agentscope_runtime/adapters/agentscope/memory/__init__.py +6 -0
- agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +152 -0
- agentscope_runtime/adapters/agentscope/message.py +535 -0
- agentscope_runtime/adapters/agentscope/stream.py +474 -0
- agentscope_runtime/adapters/agentscope/tool/__init__.py +9 -0
- agentscope_runtime/adapters/agentscope/tool/sandbox_tool.py +69 -0
- agentscope_runtime/adapters/agentscope/tool/tool.py +233 -0
- agentscope_runtime/adapters/autogen/__init__.py +0 -0
- agentscope_runtime/adapters/autogen/tool/__init__.py +7 -0
- agentscope_runtime/adapters/autogen/tool/tool.py +211 -0
- agentscope_runtime/adapters/text/__init__.py +0 -0
- agentscope_runtime/adapters/text/stream.py +29 -0
- agentscope_runtime/common/collections/redis_mapping.py +4 -1
- agentscope_runtime/common/container_clients/fc_client.py +855 -0
- agentscope_runtime/common/utils/__init__.py +0 -0
- agentscope_runtime/common/utils/lazy_loader.py +57 -0
- agentscope_runtime/engine/__init__.py +25 -18
- agentscope_runtime/engine/app/agent_app.py +161 -91
- agentscope_runtime/engine/app/base_app.py +4 -118
- agentscope_runtime/engine/constant.py +8 -0
- agentscope_runtime/engine/deployers/__init__.py +8 -0
- agentscope_runtime/engine/deployers/adapter/__init__.py +2 -0
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +0 -21
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +28 -9
- agentscope_runtime/engine/deployers/adapter/responses/__init__.py +2 -0
- agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -2
- agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +1 -1
- agentscope_runtime/engine/deployers/agentrun_deployer.py +2541 -0
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +1 -1
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +9 -21
- agentscope_runtime/engine/deployers/local_deployer.py +47 -74
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +216 -50
- agentscope_runtime/engine/deployers/utils/app_runner_utils.py +29 -0
- agentscope_runtime/engine/deployers/utils/detached_app.py +510 -0
- agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +1 -1
- agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +1 -1
- agentscope_runtime/engine/deployers/utils/docker_image_utils/{runner_image_factory.py → image_factory.py} +121 -61
- agentscope_runtime/engine/deployers/utils/package.py +693 -0
- agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -5
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +256 -282
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +2 -4
- agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +23 -1
- agentscope_runtime/engine/deployers/utils/templates/app_main.py.j2 +84 -0
- agentscope_runtime/engine/deployers/utils/templates/runner_main.py.j2 +95 -0
- agentscope_runtime/engine/deployers/utils/{service_utils → templates}/standalone_main.py.j2 +0 -45
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +119 -18
- agentscope_runtime/engine/helpers/runner.py +40 -0
- agentscope_runtime/engine/runner.py +170 -130
- agentscope_runtime/engine/schemas/agent_schemas.py +114 -3
- agentscope_runtime/engine/schemas/modelstudio_llm.py +4 -2
- agentscope_runtime/engine/schemas/oai_llm.py +23 -23
- agentscope_runtime/engine/schemas/response_api.py +65 -0
- agentscope_runtime/engine/schemas/session.py +24 -0
- agentscope_runtime/engine/services/__init__.py +0 -9
- agentscope_runtime/engine/services/agent_state/__init__.py +16 -0
- agentscope_runtime/engine/services/agent_state/redis_state_service.py +113 -0
- agentscope_runtime/engine/services/agent_state/state_service.py +179 -0
- agentscope_runtime/engine/services/memory/__init__.py +24 -0
- agentscope_runtime/engine/services/{mem0_memory_service.py → memory/mem0_memory_service.py} +17 -13
- agentscope_runtime/engine/services/{memory_service.py → memory/memory_service.py} +28 -7
- agentscope_runtime/engine/services/{redis_memory_service.py → memory/redis_memory_service.py} +1 -1
- agentscope_runtime/engine/services/{reme_personal_memory_service.py → memory/reme_personal_memory_service.py} +9 -6
- agentscope_runtime/engine/services/{reme_task_memory_service.py → memory/reme_task_memory_service.py} +2 -2
- agentscope_runtime/engine/services/{tablestore_memory_service.py → memory/tablestore_memory_service.py} +12 -18
- agentscope_runtime/engine/services/sandbox/__init__.py +13 -0
- agentscope_runtime/engine/services/{sandbox_service.py → sandbox/sandbox_service.py} +86 -71
- agentscope_runtime/engine/services/session_history/__init__.py +23 -0
- agentscope_runtime/engine/services/{redis_session_history_service.py → session_history/redis_session_history_service.py} +3 -2
- agentscope_runtime/engine/services/{session_history_service.py → session_history/session_history_service.py} +44 -34
- agentscope_runtime/engine/services/{tablestore_session_history_service.py → session_history/tablestore_session_history_service.py} +14 -19
- agentscope_runtime/engine/services/utils/tablestore_service_utils.py +2 -2
- agentscope_runtime/engine/tracing/base.py +10 -9
- agentscope_runtime/engine/tracing/message_util.py +1 -1
- agentscope_runtime/engine/tracing/tracing_util.py +7 -2
- agentscope_runtime/sandbox/__init__.py +10 -2
- agentscope_runtime/sandbox/box/agentbay/__init__.py +4 -0
- agentscope_runtime/sandbox/box/agentbay/agentbay_sandbox.py +559 -0
- agentscope_runtime/sandbox/box/base/base_sandbox.py +12 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +115 -11
- agentscope_runtime/sandbox/box/cloud/__init__.py +4 -0
- agentscope_runtime/sandbox/box/cloud/cloud_sandbox.py +254 -0
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +66 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +42 -0
- agentscope_runtime/sandbox/box/mobile/__init__.py +4 -0
- agentscope_runtime/sandbox/box/mobile/box/__init__.py +0 -0
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +216 -0
- agentscope_runtime/sandbox/box/training_box/training_box.py +2 -2
- agentscope_runtime/sandbox/client/http_client.py +1 -0
- agentscope_runtime/sandbox/enums.py +2 -0
- agentscope_runtime/sandbox/manager/sandbox_manager.py +18 -2
- agentscope_runtime/sandbox/manager/server/app.py +12 -0
- agentscope_runtime/sandbox/manager/server/config.py +19 -0
- agentscope_runtime/sandbox/model/manager_config.py +79 -2
- agentscope_runtime/sandbox/utils.py +0 -18
- agentscope_runtime/tools/RAGs/__init__.py +0 -0
- agentscope_runtime/tools/RAGs/modelstudio_rag.py +377 -0
- agentscope_runtime/tools/RAGs/modelstudio_rag_lite.py +219 -0
- agentscope_runtime/tools/__init__.py +119 -0
- agentscope_runtime/tools/_constants.py +18 -0
- agentscope_runtime/tools/alipay/__init__.py +4 -0
- agentscope_runtime/tools/alipay/base.py +334 -0
- agentscope_runtime/tools/alipay/payment.py +835 -0
- agentscope_runtime/tools/alipay/subscribe.py +551 -0
- agentscope_runtime/tools/base.py +264 -0
- agentscope_runtime/tools/cli/__init__.py +0 -0
- agentscope_runtime/tools/cli/modelstudio_mcp_server.py +78 -0
- agentscope_runtime/tools/generations/__init__.py +75 -0
- agentscope_runtime/tools/generations/async_image_to_video.py +350 -0
- agentscope_runtime/tools/generations/async_image_to_video_wan25.py +366 -0
- agentscope_runtime/tools/generations/async_speech_to_video.py +422 -0
- agentscope_runtime/tools/generations/async_text_to_video.py +320 -0
- agentscope_runtime/tools/generations/async_text_to_video_wan25.py +334 -0
- agentscope_runtime/tools/generations/image_edit.py +208 -0
- agentscope_runtime/tools/generations/image_edit_wan25.py +193 -0
- agentscope_runtime/tools/generations/image_generation.py +202 -0
- agentscope_runtime/tools/generations/image_generation_wan25.py +201 -0
- agentscope_runtime/tools/generations/image_style_repaint.py +208 -0
- agentscope_runtime/tools/generations/image_to_video.py +233 -0
- agentscope_runtime/tools/generations/qwen_image_edit.py +205 -0
- agentscope_runtime/tools/generations/qwen_image_generation.py +214 -0
- agentscope_runtime/tools/generations/qwen_text_to_speech.py +154 -0
- agentscope_runtime/tools/generations/speech_to_text.py +260 -0
- agentscope_runtime/tools/generations/speech_to_video.py +314 -0
- agentscope_runtime/tools/generations/text_to_video.py +221 -0
- agentscope_runtime/tools/mcp_wrapper.py +215 -0
- agentscope_runtime/tools/realtime_clients/__init__.py +13 -0
- agentscope_runtime/tools/realtime_clients/asr_client.py +27 -0
- agentscope_runtime/tools/realtime_clients/azure_asr_client.py +195 -0
- agentscope_runtime/tools/realtime_clients/azure_tts_client.py +383 -0
- agentscope_runtime/tools/realtime_clients/modelstudio_asr_client.py +151 -0
- agentscope_runtime/tools/realtime_clients/modelstudio_tts_client.py +199 -0
- agentscope_runtime/tools/realtime_clients/realtime_tool.py +55 -0
- agentscope_runtime/tools/realtime_clients/tts_client.py +33 -0
- agentscope_runtime/tools/searches/__init__.py +3 -0
- agentscope_runtime/tools/searches/modelstudio_search.py +877 -0
- agentscope_runtime/tools/searches/modelstudio_search_lite.py +310 -0
- agentscope_runtime/tools/utils/__init__.py +0 -0
- agentscope_runtime/tools/utils/api_key_util.py +45 -0
- agentscope_runtime/tools/utils/crypto_utils.py +99 -0
- agentscope_runtime/tools/utils/mcp_util.py +35 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/METADATA +234 -165
- agentscope_runtime-1.0.0b1.dist-info/RECORD +240 -0
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/entry_points.txt +1 -0
- agentscope_runtime/engine/agents/__init__.py +0 -2
- agentscope_runtime/engine/agents/agentscope_agent.py +0 -488
- agentscope_runtime/engine/agents/agno_agent.py +0 -220
- agentscope_runtime/engine/agents/autogen_agent.py +0 -250
- agentscope_runtime/engine/agents/base_agent.py +0 -29
- agentscope_runtime/engine/agents/langgraph_agent.py +0 -59
- agentscope_runtime/engine/agents/utils.py +0 -53
- agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -1163
- agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
- agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
- agentscope_runtime/engine/helpers/helper.py +0 -179
- agentscope_runtime/engine/schemas/context.py +0 -54
- agentscope_runtime/engine/services/context_manager.py +0 -164
- agentscope_runtime/engine/services/environment_manager.py +0 -50
- agentscope_runtime/engine/services/manager.py +0 -174
- agentscope_runtime/engine/services/rag_service.py +0 -195
- agentscope_runtime/engine/services/tablestore_rag_service.py +0 -143
- agentscope_runtime/sandbox/tools/__init__.py +0 -12
- agentscope_runtime/sandbox/tools/base/__init__.py +0 -8
- agentscope_runtime/sandbox/tools/base/tool.py +0 -52
- agentscope_runtime/sandbox/tools/browser/__init__.py +0 -57
- agentscope_runtime/sandbox/tools/browser/tool.py +0 -597
- agentscope_runtime/sandbox/tools/filesystem/__init__.py +0 -32
- agentscope_runtime/sandbox/tools/filesystem/tool.py +0 -319
- agentscope_runtime/sandbox/tools/function_tool.py +0 -321
- agentscope_runtime/sandbox/tools/gui/__init__.py +0 -7
- agentscope_runtime/sandbox/tools/gui/tool.py +0 -77
- agentscope_runtime/sandbox/tools/mcp_tool.py +0 -195
- agentscope_runtime/sandbox/tools/sandbox_tool.py +0 -104
- agentscope_runtime/sandbox/tools/tool.py +0 -238
- agentscope_runtime/sandbox/tools/utils.py +0 -68
- agentscope_runtime-0.2.0b2.dist-info/RECORD +0 -183
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {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}")
|