fix: separate video payload from images in task API

This commit is contained in:
JOJO 2026-04-05 19:00:19 +08:00
parent a5412dae34
commit a5a3eb9974
3 changed files with 65 additions and 11 deletions

View File

@ -1,5 +1,6 @@
"""简单任务 API将聊天任务与 WebSocket 解耦,支持后台运行与轮询。"""
from __future__ import annotations
import mimetypes
import time
import threading
import uuid
@ -101,6 +102,7 @@ class TaskManager:
message: str,
images: List[Any],
conversation_id: Optional[str],
videos: Optional[List[Any]] = None,
model_key: Optional[str] = None,
thinking_mode: Optional[bool] = None,
run_mode: Optional[str] = None,
@ -137,7 +139,7 @@ class TaskManager:
record.session_data = {}
with self._lock:
self._tasks[task_id] = record
thread = threading.Thread(target=self._run_chat_task, args=(record, images), daemon=True)
thread = threading.Thread(target=self._run_chat_task, args=(record, images, videos or []), daemon=True)
record.thread = thread
record.status = "running"
record.updated_at = time.time()
@ -199,7 +201,7 @@ class TaskManager:
})
rec.updated_at = time.time()
def _run_chat_task(self, rec: TaskRecord, images: List[Any]):
def _run_chat_task(self, rec: TaskRecord, images: List[Any], videos: List[Any]):
username = rec.username
workspace_id = rec.workspace_id
terminal = None
@ -269,6 +271,7 @@ class TaskManager:
client_sid=rec.task_id,
workspace=workspace,
username=username,
videos=videos,
)
# 结束状态
@ -347,8 +350,7 @@ def create_task_api():
workspace_id = session.get("workspace_id") or "default"
payload = request.get_json() or {}
message = (payload.get("message") or "").strip()
images = payload.get("images") or []
videos = payload.get("videos") or []
images, videos = _normalize_media_payload(payload.get("images") or [], payload.get("videos") or [])
conversation_id = payload.get("conversation_id")
if not message and not images and not videos:
return jsonify({"success": False, "error": "消息不能为空"}), 400
@ -357,16 +359,14 @@ def create_task_api():
run_mode = payload.get("run_mode")
max_iterations = payload.get("max_iterations")
# 合并 images 和 videos 到 images 参数(后端统一处理)
all_media = images + videos
try:
rec = task_manager.create_chat_task(
username,
workspace_id,
message,
all_media,
images,
conversation_id,
videos=videos,
model_key=model_key,
thinking_mode=thinking_mode,
run_mode=run_mode,
@ -385,6 +385,48 @@ def create_task_api():
}), 202
def _media_path(item: Any) -> str:
if isinstance(item, dict):
return str(item.get("path") or "")
return str(item or "")
def _is_video_item(item: Any) -> bool:
path = _media_path(item)
if not path:
return False
mime, _ = mimetypes.guess_type(path)
return bool(mime and mime.startswith("video/"))
def _is_image_item(item: Any) -> bool:
path = _media_path(item)
if not path:
return False
mime, _ = mimetypes.guess_type(path)
return bool(mime and mime.startswith("image/"))
def _normalize_media_payload(images: List[Any], videos: List[Any]) -> tuple[List[Any], List[Any]]:
"""纠偏媒体字段:把误传到 images 的视频项自动归入 videos。"""
fixed_images: List[Any] = []
fixed_videos: List[Any] = []
for item in images or []:
if _is_video_item(item):
fixed_videos.append(item)
else:
fixed_images.append(item)
for item in videos or []:
if _is_image_item(item):
fixed_images.append(item)
else:
fixed_videos.append(item)
return fixed_images, fixed_videos
@tasks_bp.route("/api/tasks/<task_id>", methods=["GET"])
@api_login_required
def get_task_api(task_id: str):

View File

@ -205,6 +205,7 @@ class DeepSeekClient:
return text
qwen_video_fps = 2
parts: List[Dict[str, Any]] = []
extra_videos: List[Any] = []
if text:
parts.append({"type": "text", "text": text})
base_path = Path(self.project_path or ".")
@ -214,6 +215,11 @@ class DeepSeekClient:
if not abs_path.exists() or not abs_path.is_file():
continue
mime, _ = mimetypes.guess_type(abs_path.name)
if mime and mime.startswith("video/"):
extra_videos.append(path)
continue
if mime and not mime.startswith("image/"):
continue
if not mime:
mime = "image/png"
data = abs_path.read_bytes()
@ -221,7 +227,7 @@ class DeepSeekClient:
parts.append({"type": "image_url", "image_url": {"url": f"data:{mime};base64,{b64}"}})
except Exception:
continue
for item in videos:
for item in [*videos, *extra_videos]:
try:
if isinstance(item, dict):
path = item.get("path") or ""

View File

@ -1608,6 +1608,7 @@ class ContextManager:
if not images and not videos:
return text
parts: List[Dict[str, Any]] = []
extra_videos: List[Any] = []
supports_video_fps = getattr(getattr(self, "main_terminal", None), "model_key", None) == "qwen3-vl-plus"
qwen_video_fps = 2
if text:
@ -1617,9 +1618,14 @@ class ContextManager:
abs_path = Path(self.project_path) / path
if not abs_path.exists() or not abs_path.is_file():
continue
mime, _ = mimetypes.guess_type(abs_path.name)
if mime and mime.startswith("video/"):
extra_videos.append(path)
continue
if mime and not mime.startswith("image/"):
continue
data_url = self._compress_image_if_needed(abs_path)
if not data_url:
mime, _ = mimetypes.guess_type(abs_path.name)
if not mime:
mime = "image/png"
data = abs_path.read_bytes()
@ -1628,7 +1634,7 @@ class ContextManager:
parts.append({"type": "image_url", "image_url": {"url": data_url}})
except Exception:
continue
for item in videos:
for item in [*videos, *extra_videos]:
try:
if isinstance(item, dict):
path = item.get("path") or ""