fix: support vision prompt modes

This commit is contained in:
Xin Wang
2026-08-12 19:13:04 +08:00
parent 09b07751b6
commit 94a02d7e2b
29 changed files with 676 additions and 439 deletions
+14 -44
View File
@@ -2,7 +2,7 @@
import uuid
from models import RuntimeModelResource, RuntimeTool
from models import VISION_PROMPT_MODES, RuntimeTool
from db.models import (
Assistant,
AssistantModelBinding,
@@ -16,15 +16,12 @@ from fastapi import APIRouter, Depends, HTTPException
from schemas import (
AssistantOut,
AssistantUpsert,
VisionFocusPromptGenerateRequest,
VisionFocusPromptGenerateResult,
)
from services.auth import require_admin
from services.masking import mask, resolve_incoming_key
from services.node_specs import graph_references, normalize_graph, validate_graph
from services.system_tools import system_tool_kind
from services.tool_policy import policy_for_tool
from services.vision import generate_vision_focus_prompt
from services.workflow_engine import WorkflowEngine
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -79,7 +76,8 @@ def _validate_workflow(body: AssistantUpsert) -> None:
# capability summary used by clients to decide whether to open the camera.
body.vision_enabled = WorkflowEngine(body.graph).uses_vision()
body.vision_model_resource_id = None
body.vision_focus_prompt = ""
body.vision_prompt_mode = "runtime"
body.vision_prompt = ""
refs = graph_references(body.graph)
body.tool_ids = list(dict.fromkeys([*body.tool_ids, *sorted(refs["tools"])]))
@@ -271,6 +269,13 @@ async def _validate_vision_model(
if body.type == "workflow":
return
if body.vision_enabled:
if body.vision_prompt_mode not in VISION_PROMPT_MODES:
raise HTTPException(400, "视觉提示词模式无效")
if (
body.vision_prompt_mode in {"fixed", "guided"}
and not body.vision_prompt.strip()
):
raise HTTPException(400, "固定或引导模式必须填写视觉提示词")
if body.vision_model_resource_id:
resource = await session.get(ModelResource, body.vision_model_resource_id)
else:
@@ -396,7 +401,8 @@ async def _to_out(session: AsyncSession, assistant: Assistant) -> AssistantOut:
startup=assistant.startup or {},
vision_enabled=assistant.vision_enabled,
vision_model_resource_id=assistant.vision_model_resource_id,
vision_focus_prompt=assistant.vision_focus_prompt or "",
vision_prompt_mode=assistant.vision_prompt_mode or "runtime",
vision_prompt=assistant.vision_prompt or "",
model_resource_ids=await _resource_ids(session, assistant.id),
knowledge_base_id=assistant.knowledge_base_id,
knowledge_retrieval_config=assistant.knowledge_retrieval_config or {},
@@ -448,43 +454,6 @@ async def create_assistant(
return await _to_out(session, assistant)
@router.post(
"/vision-focus/generate",
response_model=VisionFocusPromptGenerateResult,
)
async def generate_assistant_vision_focus(
body: VisionFocusPromptGenerateRequest,
session: AsyncSession = Depends(get_session),
):
"""Generate an editable visual focus draft from an assistant prompt."""
resource = await session.get(ModelResource, body.model_resource_id)
if (
resource is None
or resource.capability != "LLM"
or not resource.enabled
):
raise HTTPException(400, "请选择可用的大语言模型生成视觉关注目标")
try:
focus_prompt = await generate_vision_focus_prompt(
RuntimeModelResource(
id=resource.id,
name=resource.name,
capability=resource.capability,
interface_type=resource.interface_type,
values=resource.values or {},
secrets=resource.secrets or {},
support_image_input=bool(resource.support_image_input),
),
body.source_prompt,
)
except ValueError as exc:
raise HTTPException(400, str(exc)) from exc
except Exception as exc:
raise HTTPException(502, "生成视觉关注目标失败,请稍后重试") from exc
return VisionFocusPromptGenerateResult(focus_prompt=focus_prompt)
@router.get("/{assistant_id}", response_model=AssistantOut)
async def get_assistant(
assistant_id: str, session: AsyncSession = Depends(get_session)
@@ -513,7 +482,8 @@ async def duplicate_assistant(
startup=dict(source.startup or {}),
vision_enabled=source.vision_enabled,
vision_model_resource_id=source.vision_model_resource_id,
vision_focus_prompt=source.vision_focus_prompt or "",
vision_prompt_mode=source.vision_prompt_mode or "runtime",
vision_prompt=source.vision_prompt or "",
knowledge_base_id=source.knowledge_base_id,
knowledge_retrieval_config=dict(source.knowledge_retrieval_config or {}),
prompt=source.prompt,