package main import ( "path/filepath" "strings" "git.yeij.top/AskaEth/Cyrene_v2/internal/logger" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/multimodal/vision" ) // visionToolName 与 ocrToolName 是 vision 与 OCR 工具的注册名。它们在此与工具注册 // 绑定,并由工具分发器通告给 agent。 const ( visionToolName = "vision" ocrToolName = "ocr" ) // wireVision 注册 vision 与 OCR 工具以及 vision 模型路由。 // // vision 适配器是 OpenAI 兼容的 /chat/completions 端点,与聊天 LLM 共用同一 base // URL(LLM_BASE_URL),配置了专用模型(LLM_VISION_MODEL)时使用该模型。未配置 // base URL 时只告警并跳过这些工具,启动因此绝不阻塞;只要配置了 vision 模型 // (在 wireRuntime 中),模型路由器仍会绑定到它。 func (a *app) wireVision() { log := logger.WithSource(a.log, "cmd/cyrene") if strings.TrimSpace(a.cfg.LLM.BaseURL) == "" { log.Warn("vision/ocr tools skipped: no LLM base url configured") return } adapter := vision.NewOpenAICompatible(a.cfg.LLM, vision.WithModel(a.cfg.LLM.VisionModel), // 同源 upload 产物(`/api/v1/files/`)走**本地读取**:与自己取回相比更快, // 且在「服务端路由不到自己的广告 host」(如安卓模拟器 10.0.2.2、反向代理改写) // 的部署下才取得到图。目录必须与 upload 的落盘位置一致 = `/uploads`。 vision.WithUploadsDir(filepath.Join(resolveDataDir(a.cfg), "uploads")), vision.WithLogger(a.log)) if err := vision.RegisterTools(a.caps, vision.NewVisionTool(visionToolName, adapter), vision.NewOCRTool(ocrToolName, adapter), ); err != nil { log.Warn("vision/ocr tool register skipped", "error", err) return } log.Info("vision/ocr tools registered", "vision_model", a.cfg.LLM.VisionModel) }