From e0d560c0f9aa775abe9495c3a069762e76e48ded Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Fri, 26 Dec 2025 16:32:04 +0800 Subject: [PATCH 1/5] chore: support seedance 1.5 pro --- pyproject.toml | 4 +-- veadk/tools/builtin_tools/image_generate.py | 2 +- veadk/tools/builtin_tools/video_generate.py | 37 ++++++++++++++++++--- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0ea022d0..0bd772dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,8 +22,8 @@ dependencies = [ "opentelemetry-instrumentation-logging>=0.56b0", "wrapt==1.17.2", # For patching built-in functions "openai<1.100", # For fix https://github.com/BerriAI/litellm/issues/13710 - "volcengine-python-sdk==4.0.33", # For Volcengine API - "volcengine==1.0.193", # For Volcengine sign + "volcengine-python-sdk>=5.0.1", # For Volcengine API + "volcengine>=1.0.193", # For Volcengine sign "agent-pilot-sdk==0.1.2", # Prompt optimization by Volcengine AgentPilot/PromptPilot toolkits "fastmcp==2.12.3", # For running MCP "trustedmcp==0.0.5", # For running TrustedMCP diff --git a/veadk/tools/builtin_tools/image_generate.py b/veadk/tools/builtin_tools/image_generate.py index c2290722..da0df7b0 100644 --- a/veadk/tools/builtin_tools/image_generate.py +++ b/veadk/tools/builtin_tools/image_generate.py @@ -229,7 +229,7 @@ def handle_single_task_sync( async def image_generate(tasks: list[dict], tool_context) -> Dict: - """Generate images with Seedream 4.0. + """Generate images with Seedream 4.0 / 4.5 Commit batch image generation requests via tasks. diff --git a/veadk/tools/builtin_tools/video_generate.py b/veadk/tools/builtin_tools/video_generate.py index 4fb3aa89..a03b99d0 100644 --- a/veadk/tools/builtin_tools/video_generate.py +++ b/veadk/tools/builtin_tools/video_generate.py @@ -41,14 +41,27 @@ ) -async def generate(prompt, first_frame_image=None, last_frame_image=None): +async def generate( + prompt, first_frame_image=None, last_frame_image=None, generate_audio=None +): try: + if generate_audio is False: + generate_audio = None + model_name = getenv("MODEL_VIDEO_NAME", DEFAULT_VIDEO_MODEL_NAME) + + if model_name.startswith("doubao-seedance-1-0") and generate_audio: + logger.warning( + "The `doubao-seedance-1-0` series models do not support enabling the audio field. " + "Please upgrade to the `doubao-seedance-1-5` series." + ) + generate_audio = None if first_frame_image is None: response = client.content_generation.tasks.create( model=getenv("MODEL_VIDEO_NAME", DEFAULT_VIDEO_MODEL_NAME), content=[ {"type": "text", "text": prompt}, ], + generate_audio=generate_audio, extra_headers={ "veadk-source": "veadk", "veadk-version": VERSION, @@ -148,6 +161,12 @@ async def video_generate( URL or Base64 string (data URL) for the **last frame** (role = `last_frame`). Use when you want the clip to end on a specific image. + - generate_audio (bool | None): + Boolean value, used to determine whether the generated video should have sound. + If this field is not configured (None) or its value is `False`, no sound will be generated. + If it is configured as `True`, sound can be generated. + If you want to describe the sound content in detail, you can do so in the `prompt` field. + Notes on first/last frame: * When both frames are provided, **match width/height** to avoid cropping; if they differ, the tail frame may be auto-cropped to fit. @@ -243,22 +262,32 @@ async def video_generate( prompt = item["prompt"] first_frame = item.get("first_frame", None) last_frame = item.get("last_frame", None) + generate_audio = item.get("generate_audio", None) try: if not first_frame: logger.debug( f"video_generate task_{idx} text generation: prompt={prompt}" ) - response = await generate(prompt) + response = await generate(prompt, generate_audio=generate_audio) elif not last_frame: logger.debug( f"video_generate task_{idx} first frame generation: prompt={prompt}, first_frame={first_frame}" ) - response = await generate(prompt, first_frame) + response = await generate( + prompt, + first_frame_image=first_frame, + generate_audio=generate_audio, + ) else: logger.debug( f"video_generate task_{idx} first and last frame generation: prompt={prompt}, first_frame={first_frame}, last_frame={last_frame}" ) - response = await generate(prompt, first_frame, last_frame) + response = await generate( + prompt, + first_frame_image=first_frame, + last_frame_image=last_frame, + generate_audio=generate_audio, + ) logger.debug( f"batch_{start_idx // batch_size} video_generate task_{idx} response: {response}" ) From 685589f5c022e098af7af718ace5ee5508c89bd0 Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Fri, 26 Dec 2025 17:02:47 +0800 Subject: [PATCH 2/5] chore: video generate sleep with `max_wait_seconds` --- veadk/tools/builtin_tools/video_generate.py | 36 +++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/veadk/tools/builtin_tools/video_generate.py b/veadk/tools/builtin_tools/video_generate.py index a03b99d0..b7b38e0f 100644 --- a/veadk/tools/builtin_tools/video_generate.py +++ b/veadk/tools/builtin_tools/video_generate.py @@ -13,7 +13,7 @@ # limitations under the License. import json -import time +import asyncio import traceback from typing import Dict, cast @@ -125,7 +125,10 @@ async def generate( async def video_generate( - params: list, tool_context: ToolContext, batch_size: int = 10 + params: list, + tool_context: ToolContext, + batch_size: int = 10, + max_wait_seconds: int = 1200, ) -> Dict: """ Generate videos in **batch** from text prompts, optionally guided by a first/last frame, @@ -139,6 +142,10 @@ async def video_generate( A list of video generation requests. Each item supports the fields below. batch_size (int): The number of videos to generate in a batch. Defaults to 10. + max_wait_seconds (int): + Maximum time in seconds to wait for all video tasks in each batch. + Default is 20 minutes (1200 seconds). When the timeout is reached, + unfinished tasks will be marked as timeout errors. Required per item: - video_name (str): @@ -241,6 +248,7 @@ async def video_generate( """ success_list = [] error_list = [] + timeout_tasks = [] logger.debug(f"Using model: {getenv('MODEL_VIDEO_NAME', DEFAULT_VIDEO_MODEL_NAME)}") logger.debug(f"video_generate params: {params}") @@ -299,6 +307,10 @@ async def video_generate( logger.debug("begin query video_generate task status...") + sleep_interval = 10 + max_sleep_times = max_wait_seconds // sleep_interval + sleep_times = 0 + while True: task_list = list(task_dict.keys()) if len(task_list) == 0: @@ -332,7 +344,23 @@ async def video_generate( logger.debug( f"{task_dict[task_id]} video_generate current status: {status}, Retrying after 10 seconds..." ) - time.sleep(10) + if sleep_times >= max_sleep_times: + logger.error( + f"video_generate polling timed out after {max_wait_seconds} seconds; remaining tasks: {task_dict}" + ) + for task_id, video_name in task_dict.items(): + timeout_tasks.append( + { + "task_id": task_id, + "video_name": video_name, + } + ) + error_list.append(video_name) + task_dict.clear() + break + + await asyncio.sleep(sleep_interval) + sleep_times += 1 add_span_attributes( span, @@ -353,6 +381,7 @@ async def video_generate( "status": "error", "success_list": success_list, "error_list": error_list, + "timeout_tasks": timeout_tasks, } else: logger.debug( @@ -362,6 +391,7 @@ async def video_generate( "status": "success", "success_list": success_list, "error_list": error_list, + "timeout_tasks": timeout_tasks, } From a01b2b92dc506a8b5ec1e38c436e93c4af659fb5 Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Fri, 26 Dec 2025 17:37:10 +0800 Subject: [PATCH 3/5] fix: timeout of image generate --- veadk/tools/builtin_tools/image_generate.py | 15 ++++++++++++--- veadk/tools/builtin_tools/video_generate.py | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/veadk/tools/builtin_tools/image_generate.py b/veadk/tools/builtin_tools/image_generate.py index da0df7b0..73054d57 100644 --- a/veadk/tools/builtin_tools/image_generate.py +++ b/veadk/tools/builtin_tools/image_generate.py @@ -83,7 +83,10 @@ def _build_input_parts(item: dict, task_type: str, image_field): def handle_single_task_sync( - idx: int, item: dict, tool_context + idx: int, + item: dict, + timeout: int, + tool_context, ) -> tuple[list[dict], list[str]]: logger.debug(f"handle_single_task_sync item {idx}: {item}") success_list: list[dict] = [] @@ -139,6 +142,7 @@ def handle_single_task_sync( "MODEL_AGENT_CLIENT_REQ_ID", f"veadk/{VERSION}" ), }, + timeout=timeout, ) else: response = client.images.generate( @@ -152,6 +156,7 @@ def handle_single_task_sync( "MODEL_AGENT_CLIENT_REQ_ID", f"veadk/{VERSION}" ), }, + timeout=timeout, ) if not response.error: @@ -228,7 +233,7 @@ def handle_single_task_sync( return success_list, error_list -async def image_generate(tasks: list[dict], tool_context) -> Dict: +async def image_generate(tasks: list[dict], tool_context, timeout: int = 600) -> Dict: """Generate images with Seedream 4.0 / 4.5 Commit batch image generation requests via tasks. @@ -236,6 +241,8 @@ async def image_generate(tasks: list[dict], tool_context) -> Dict: Args: tasks (list[dict]): A list of image-generation tasks. Each task is a dict. + timeout (int) + The timeout limit for the image generation task request, in seconds, with a default value of 600 seconds. Per-task schema --------------- Required: @@ -336,7 +343,9 @@ async def image_generate(tasks: list[dict], tool_context) -> Dict: def make_task(idx, item): ctx = base_ctx.copy() - return lambda: ctx.run(handle_single_task_sync, idx, item, tool_context) + return lambda: ctx.run( + handle_single_task_sync, idx, item, timeout, tool_context + ) loop = asyncio.get_event_loop() futures = [ diff --git a/veadk/tools/builtin_tools/video_generate.py b/veadk/tools/builtin_tools/video_generate.py index b7b38e0f..97414fed 100644 --- a/veadk/tools/builtin_tools/video_generate.py +++ b/veadk/tools/builtin_tools/video_generate.py @@ -52,7 +52,7 @@ async def generate( if model_name.startswith("doubao-seedance-1-0") and generate_audio: logger.warning( "The `doubao-seedance-1-0` series models do not support enabling the audio field. " - "Please upgrade to the `doubao-seedance-1-5` series." + "Please upgrade to the doubao-seedance-1-5 series of you want to generate video with audio." ) generate_audio = None if first_frame_image is None: From 8de6437c4d2c3caee59abee81c7c1c580465bece Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Fri, 26 Dec 2025 17:49:41 +0800 Subject: [PATCH 4/5] fix: config yaml --- config.yaml.full | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.yaml.full b/config.yaml.full index 92d95a65..37dc8cd0 100644 --- a/config.yaml.full +++ b/config.yaml.full @@ -20,7 +20,7 @@ model: api_base: https://ark.cn-beijing.volces.com/api/v3/ api_key: video: - name: doubao-seedance-1-0-pro-250528 + name: doubao-seedance-1-5-pro-251215 api_base: https://ark.cn-beijing.volces.com/api/v3/ api_key: image: From 057bf562ef160757b69f92674d37d757421d84ea Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Fri, 26 Dec 2025 17:51:15 +0800 Subject: [PATCH 5/5] fix: consts --- veadk/consts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/veadk/consts.py b/veadk/consts.py index a050c1de..209c81e3 100644 --- a/veadk/consts.py +++ b/veadk/consts.py @@ -65,7 +65,7 @@ DEFAULT_IMAGE_EDIT_MODEL_NAME = "doubao-seededit-3-0-i2i-250628" DEFAULT_IMAGE_EDIT_MODEL_API_BASE = "https://ark.cn-beijing.volces.com/api/v3/" -DEFAULT_VIDEO_MODEL_NAME = "doubao-seedance-1-0-pro-250528" +DEFAULT_VIDEO_MODEL_NAME = "doubao-seedance-1-5-pro-251215" DEFAULT_VIDEO_MODEL_API_BASE = "https://ark.cn-beijing.volces.com/api/v3/" DEFAULT_IMAGE_GENERATE_MODEL_NAME = "doubao-seedream-4-5-251128"