Files
photoprism/internal/ai/vision/engine.go
2025-12-04 16:10:29 +01:00

117 lines
3.4 KiB
Go

package vision
import (
"context"
"strings"
"sync"
"github.com/photoprism/photoprism/pkg/http/scheme"
)
// ModelEngine represents the canonical identifier for a computer vision service engine.
type ModelEngine = string
const (
// EngineVision represents the default PhotoPrism vision service endpoints.
EngineVision ModelEngine = "vision"
// EngineTensorFlow represents on-device TensorFlow models.
EngineTensorFlow ModelEngine = "tensorflow"
// EngineLocal is used when no explicit engine can be determined.
EngineLocal ModelEngine = "local"
)
// RequestBuilder builds an API request for an engine based on the model configuration and input files.
type RequestBuilder interface {
Build(ctx context.Context, model *Model, files Files) (*ApiRequest, error)
}
// ResponseParser parses a raw engine response into the generic ApiResponse structure.
type ResponseParser interface {
Parse(ctx context.Context, req *ApiRequest, raw []byte, status int) (*ApiResponse, error)
}
// EngineDefaults supplies engine-specific prompt and schema defaults when they are not configured explicitly.
type EngineDefaults interface {
SystemPrompt(model *Model) string
UserPrompt(model *Model) string
SchemaTemplate(model *Model) string
Options(model *Model) *ModelOptions
}
// Engine groups the callbacks required to integrate a third-party vision service.
type Engine struct {
Builder RequestBuilder
Parser ResponseParser
Defaults EngineDefaults
}
var (
engineRegistry = make(map[ApiFormat]Engine)
engineAliasIndex = make(map[string]EngineInfo)
engineMu sync.RWMutex
)
// init wires up the built-in aliases so configuration files can reference the
// human-friendly engine names without duplicating adapter metadata.
func init() {
RegisterEngineAlias(EngineVision, EngineInfo{
RequestFormat: ApiFormatVision,
ResponseFormat: ApiFormatVision,
FileScheme: scheme.Data,
DefaultResolution: DefaultResolution,
})
}
// RegisterEngine adds/overrides an engine implementation for a specific API format.
func RegisterEngine(format ApiFormat, engine Engine) {
engineMu.Lock()
defer engineMu.Unlock()
engineRegistry[format] = engine
}
// EngineInfo describes metadata that can be associated with an engine alias.
type EngineInfo struct {
Uri string
RequestFormat ApiFormat
ResponseFormat ApiFormat
FileScheme string
DefaultModel string
DefaultResolution int
DefaultKey string // Optional placeholder key (e.g., ${OPENAI_API_KEY}); applied only when Service.Key is empty.
}
// RegisterEngineAlias maps a logical engine name (e.g., "ollama") to a
// request/response format pair.
func RegisterEngineAlias(name string, info EngineInfo) {
name = strings.TrimSpace(strings.ToLower(name))
if name == "" || info.RequestFormat == "" {
return
}
if info.ResponseFormat == "" {
info.ResponseFormat = info.RequestFormat
}
engineMu.Lock()
engineAliasIndex[name] = info
engineMu.Unlock()
}
// EngineInfoFor returns the metadata associated with a logical engine name.
func EngineInfoFor(name string) (EngineInfo, bool) {
name = strings.TrimSpace(strings.ToLower(name))
engineMu.RLock()
info, ok := engineAliasIndex[name]
engineMu.RUnlock()
return info, ok
}
// EngineFor returns the registered engine implementation for the given API
// format, if any.
func EngineFor(format ApiFormat) (Engine, bool) {
engineMu.RLock()
defer engineMu.RUnlock()
engine, ok := engineRegistry[format]
return engine, ok
}