fix: separate video payload from images in task API
This commit is contained in:
parent
a5412dae34
commit
a5a3eb9974
@ -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):
|
||||
|
||||
@ -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 ""
|
||||
|
||||
@ -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 ""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user