feat: route workflow image inputs natively
This commit is contained in:
@@ -141,6 +141,25 @@ def _vision_uses_main_llm(cfg: AssistantConfig) -> bool:
|
||||
return not cfg.vision_model_resource_id and cfg.llm_support_image_input
|
||||
|
||||
|
||||
def _workflow_vision_uses_main_llm(
|
||||
cfg: AssistantConfig,
|
||||
scope: dict[str, Any],
|
||||
) -> bool:
|
||||
"""Resolve native versus auxiliary vision for the active Workflow Agent."""
|
||||
if not scope.get("enabled"):
|
||||
raise ValueError("当前 Workflow Agent 节点未启用视觉能力")
|
||||
if scope.get("vision_model_resource_id"):
|
||||
return False
|
||||
|
||||
llm_resource_id = str(scope.get("llm_resource_id") or "")
|
||||
resource = cfg.workflow_model_resources.get(llm_resource_id)
|
||||
if not resource:
|
||||
raise ValueError(f"当前 Workflow Agent 的 LLM 资源未加载:{llm_resource_id}")
|
||||
if not resource.support_image_input:
|
||||
raise ValueError("当前 Workflow Agent 的 LLM 不支持图片输入")
|
||||
return True
|
||||
|
||||
|
||||
def _image_data_uri(frame: UserImageRawFrame) -> str:
|
||||
if not frame.format:
|
||||
raise ValueError("摄像头图片帧缺少 format,无法编码给视觉模型")
|
||||
@@ -388,6 +407,11 @@ async def run_pipeline(
|
||||
"vision_model_resource_id": None,
|
||||
"llm_resource_id": None,
|
||||
}
|
||||
|
||||
def active_vision_uses_main_llm() -> bool:
|
||||
if cfg.type == "workflow":
|
||||
return _workflow_vision_uses_main_llm(cfg, workflow_vision_scope)
|
||||
return vision_native_mode
|
||||
vision_schema = FunctionSchema(
|
||||
name=VISION_TOOL_NAME,
|
||||
description=(
|
||||
@@ -510,6 +534,27 @@ async def run_pipeline(
|
||||
raise ValueError(f"视觉模型资源未加载:{vision_resource_id}")
|
||||
|
||||
if cfg.type == "workflow" and vision_enabled:
|
||||
async def native_flow_fetch_user_image(params: FunctionCallParams) -> None:
|
||||
question = str(params.arguments.get("question") or "请描述当前画面。")
|
||||
user_id = vision_state.get("client_id")
|
||||
if not user_id:
|
||||
await params.result_callback(
|
||||
{
|
||||
"status": "no_video_client",
|
||||
"message": "当前还没有可用的摄像头视频流。",
|
||||
}
|
||||
)
|
||||
return
|
||||
request = UserImageRequestFrame(
|
||||
user_id=user_id,
|
||||
text=question,
|
||||
append_to_context=True,
|
||||
function_name=params.function_name,
|
||||
tool_call_id=params.tool_call_id,
|
||||
result_callback=params.result_callback,
|
||||
)
|
||||
await params.llm.push_frame(request, FrameDirection.UPSTREAM)
|
||||
|
||||
async def flow_fetch_user_image(args, _flow_manager):
|
||||
if not workflow_vision_scope.get("enabled"):
|
||||
return {
|
||||
@@ -547,6 +592,20 @@ async def run_pipeline(
|
||||
logger.warning(f"Workflow 视觉理解失败:{exc}")
|
||||
return {"status": "error", "message": "视觉理解暂时不可用。"}
|
||||
|
||||
# ConfiguredFlowManager keeps the Flows handler for auxiliary models,
|
||||
# but uses Pipecat's native function-call image path when the active
|
||||
# Agent selected its own visual-capable LLM.
|
||||
setattr(
|
||||
flow_fetch_user_image,
|
||||
"_workflow_native_vision_handler",
|
||||
native_flow_fetch_user_image,
|
||||
)
|
||||
setattr(
|
||||
flow_fetch_user_image,
|
||||
"_workflow_native_vision_enabled",
|
||||
active_vision_uses_main_llm,
|
||||
)
|
||||
|
||||
workflow_vision_function = FlowsFunctionSchema(
|
||||
name=VISION_TOOL_NAME,
|
||||
description=vision_schema.description,
|
||||
@@ -710,7 +769,8 @@ async def run_pipeline(
|
||||
user_id = vision_state.get("client_id")
|
||||
if not user_id:
|
||||
raise ValueError("当前没有可用的摄像头视频流")
|
||||
analysis_cfg = None if vision_native_mode else active_vision_config()
|
||||
native_vision = active_vision_uses_main_llm()
|
||||
analysis_cfg = None if native_vision else active_vision_config()
|
||||
|
||||
request = UserImageRequestFrame(
|
||||
user_id=user_id,
|
||||
@@ -722,7 +782,7 @@ async def run_pipeline(
|
||||
except asyncio.TimeoutError as exc:
|
||||
raise ValueError("等待摄像头视频帧超时") from exc
|
||||
|
||||
if vision_native_mode:
|
||||
if native_vision:
|
||||
image_frame.text = value.prompt_text
|
||||
image_frame.append_to_context = True
|
||||
image_frame.request = None
|
||||
|
||||
@@ -713,7 +713,10 @@ class UserTurnRoutingProcessor(FrameProcessor):
|
||||
self._last_user_message = user_message
|
||||
|
||||
content = message_text(user_message)
|
||||
handled = await self._brain.on_user_turn_end(content)
|
||||
handled = await self._brain.on_user_turn_end(
|
||||
content,
|
||||
user_message=user_message,
|
||||
)
|
||||
if not handled:
|
||||
await self.push_frame(frame, direction)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user