feat: add configurable vision focus prompts

This commit is contained in:
Xin Wang
2026-08-12 17:29:30 +08:00
parent 7560f9a525
commit 09b07751b6
26 changed files with 477 additions and 20 deletions
+48 -2
View File
@@ -2,7 +2,7 @@
import uuid
from models import RuntimeTool
from models import RuntimeModelResource, RuntimeTool
from db.models import (
Assistant,
AssistantModelBinding,
@@ -13,12 +13,18 @@ from db.models import (
)
from db.session import get_session
from fastapi import APIRouter, Depends, HTTPException
from schemas import AssistantOut, AssistantUpsert
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
@@ -73,6 +79,7 @@ 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 = ""
refs = graph_references(body.graph)
body.tool_ids = list(dict.fromkeys([*body.tool_ids, *sorted(refs["tools"])]))
@@ -389,6 +396,7 @@ 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 "",
model_resource_ids=await _resource_ids(session, assistant.id),
knowledge_base_id=assistant.knowledge_base_id,
knowledge_retrieval_config=assistant.knowledge_retrieval_config or {},
@@ -440,6 +448,43 @@ 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)
@@ -468,6 +513,7 @@ 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 "",
knowledge_base_id=source.knowledge_base_id,
knowledge_retrieval_config=dict(source.knowledge_retrieval_config or {}),
prompt=source.prompt,