feat: support uploaded images in debug voice preview

Allow paste/drag temporary image assets so vision turns work without a live camera frame.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xin Wang
2026-08-05 19:52:37 +08:00
parent 555c8f5fa6
commit e36ca308b8
16 changed files with 686 additions and 94 deletions

View File

@@ -27,6 +27,7 @@ from services.pipecat.service_factory import (
)
from db.session import SessionLocal
from services.knowledge import search as search_knowledge
from services.input_assets import consume_input_image
from services.client_tools import ClientToolBroker
from services.tool_policy import policy_for_tool
from services.vision import (
@@ -36,6 +37,7 @@ from services.vision import (
config_with_main_llm_as_vision,
config_with_vision_resource,
image_data_uri,
image_frame_from_jpeg,
image_jpeg_bytes,
)
from services.workflow_engine import WorkflowEngine
@@ -734,7 +736,7 @@ async def run_pipeline(
greeting = await brain.greeting(cfg)
async def submit_user_input(value: UserInput) -> None:
if not value.has_camera_frame:
if not value.has_image:
if not value.run_immediately:
brain.record_user_message(value.text)
await worker.queue_frame(
@@ -749,28 +751,49 @@ async def run_pipeline(
raise ValueError("P0 图片输入必须立即触发回复")
if not vision_enabled:
raise ValueError("当前助手未启用视觉能力")
user_id = vision_state.get("client_id")
if not user_id:
raise ValueError("当前没有可用的摄像头视频流")
native_vision = active_vision_uses_main_llm()
analysis_cfg = None if native_vision else active_vision_config()
request = UserImageRequestFrame(
user_id=user_id,
text=value.prompt_text,
append_to_context=False,
)
try:
image_frame = await vision_capture.request_image(llm, request)
except asyncio.TimeoutError as exc:
raise ValueError("等待摄像头视频帧超时") from exc
if value.image_asset_token:
try:
image_bytes = await asyncio.to_thread(
consume_input_image,
value.image_asset_token,
)
image_frame = await asyncio.to_thread(
image_frame_from_jpeg,
image_bytes,
)
except ValueError:
raise
except Exception as exc:
raise ValueError("上传图片暂时无法读取") from exc
else:
user_id = vision_state.get("client_id")
if not user_id:
raise ValueError("当前没有可用的摄像头视频流")
request = UserImageRequestFrame(
user_id=user_id,
text=value.prompt_text,
append_to_context=False,
)
try:
image_frame = await vision_capture.request_image(llm, request)
except asyncio.TimeoutError as exc:
raise ValueError("等待摄像头视频帧超时") from exc
image_bytes = await asyncio.to_thread(image_jpeg_bytes, image_frame)
if recorder:
image_bytes = await asyncio.to_thread(image_jpeg_bytes, image_frame)
recorder.record_image_later(
image_bytes,
input_id=value.input_id,
timestamp=time_now_iso8601(),
content=value.text,
source=(
"uploaded_asset"
if value.image_asset_token
else "camera_capture"
),
)
if native_vision: