/
githubmirror
/
photoprism
Обзор
Документация
Войти
/
githubmirror
/
photoprism
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
internal/ai/vision/api_response.go
188 строк
6 KB
Michael Mayer
Go: Apply go fix modernizations across backend packages
20 фев 2026, 05:54
20 фев 2026, 05:54
a2b7615
Код
Авторство
О чём код?
package vision import ( "errors" "fmt" "math" "net/http" "github.com/photoprism/photoprism/internal/ai/classify" "github.com/photoprism/photoprism/internal/ai/face" "github.com/photoprism/photoprism/internal/ai/nsfw" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/pkg/clean" ) // ApiResponse represents a Vision API service response. type ApiResponse struct { Id string `yaml:"Id,omitempty" json:"id,omitempty"` Code int `yaml:"Code,omitempty" json:"code,omitempty"` Error string `yaml:"Error,omitempty" json:"error,omitempty"` Model *Model `yaml:"Model,omitempty" json:"model,omitempty"` Result ApiResult `yaml:"Result,omitempty" json:"result"` } // Err returns an error if the request has failed. func (r *ApiResponse) Err() error { if r == nil { return errors.New("response is nil") } if r.Code >= 400 { if r.Error != "" { return errors.New(r.Error) } return fmt.Errorf("error %d", r.Code) } else if r.Result.IsEmpty() { return errors.New("no result") } return nil } // HasResult checks if there is at least one result in the response data. func (r *ApiResponse) HasResult() bool { if r == nil { return false } return !r.Result.IsEmpty() } // ApiResult represents the model response(s) to a Vision API service // request and can optionally include data from multiple models. type ApiResult struct { Labels []LabelResult `yaml:"Labels,omitempty" json:"labels,omitempty"` Nsfw []nsfw.Result `yaml:"Nsfw,omitempty" json:"nsfw,omitempty"` Embeddings []face.Embeddings `yaml:"Embeddings,omitempty" json:"embeddings,omitempty"` Caption *CaptionResult `yaml:"Caption,omitempty" json:"caption,omitempty"` } // IsEmpty checks if there is no result in the response data. func (r *ApiResult) IsEmpty() bool { if r == nil { return false } return len(r.Labels) == 0 && len(r.Nsfw) == 0 && len(r.Embeddings) == 0 && r.Caption == nil } // CaptionResult represents the result generated by a caption generation model. type CaptionResult struct { Text string `yaml:"Text,omitempty" json:"text,omitempty"` Source string `yaml:"Source,omitempty" json:"source,omitempty"` Confidence float32 `yaml:"Confidence,omitempty" json:"confidence,omitempty"` } // LabelResult represents a label generated by an image classification model. type LabelResult struct { Name string `yaml:"Name,omitempty" json:"name"` Source string `yaml:"Source,omitempty" json:"source"` Priority int `yaml:"Priority,omitempty" json:"priority,omitempty"` Confidence float32 `yaml:"Confidence,omitempty" json:"confidence,omitempty"` Topicality float32 `yaml:"Topicality,omitempty" json:"topicality,omitempty"` Categories []string `yaml:"Categories,omitempty" json:"categories,omitempty"` NSFW bool `yaml:"Nsfw,omitempty" json:"nsfw,omitempty"` NSFWConfidence float32 `yaml:"NsfwConfidence,omitempty" json:"nsfw_confidence,omitempty"` } // ToClassify returns the label results as classify.Label. func (r LabelResult) ToClassify(labelSrc string) classify.Label { // Calculate uncertainty from confidence or assume a default of 20%. var uncertainty int if r.Confidence <= 0 { uncertainty = 20 } else { uncertainty = int(math.RoundToEven(float64(100 - r.Confidence*100))) } // Default to "image" if no source name is provided. switch { case labelSrc != entity.SrcAuto: labelSrc = clean.ShortTypeLower(labelSrc) case r.Source != "": labelSrc = clean.ShortTypeLower(r.Source) default: labelSrc = entity.SrcImage } topicality := int(math.RoundToEven(float64(r.Topicality * 100))) if topicality < 0 { topicality = 0 } else if topicality > 100 { topicality = 100 } // Return label. confidenceScaled := int(math.RoundToEven(float64(r.NSFWConfidence * 100))) if confidenceScaled < 0 { confidenceScaled = 0 } else if confidenceScaled > 100 { confidenceScaled = 100 } if r.NSFW && confidenceScaled == 0 { confidenceScaled = 100 } return classify.Label{ Name: r.Name, Source: labelSrc, Priority: r.Priority, Uncertainty: uncertainty, Topicality: topicality, Categories: r.Categories, NSFW: r.NSFW, NSFWConfidence: confidenceScaled, } } // NewApiError generates a Vision API error response based on the specified HTTP status code. func NewApiError(id string, code int) ApiResponse { return ApiResponse{ Id: clean.Type(id), Code: code, Error: http.StatusText(code), } } // NewLabelsResponse generates a new Vision API image classification service response. func NewLabelsResponse(id string, model *Model, results classify.Labels) ApiResponse { if model == nil { model = NasnetModel } var labels = make([]LabelResult, 0, len(results)) for _, label := range results { labels = append(labels, LabelResult{ Name: label.Name, Source: label.Source, Priority: label.Priority, Confidence: label.Confidence(), Topicality: float32(label.Topicality) / 100, Categories: label.Categories, NSFW: label.NSFW, NSFWConfidence: float32(label.NSFWConfidence) / 100, }) } return ApiResponse{ Id: clean.Type(id), Code: http.StatusOK, Model: &Model{Type: ModelTypeLabels, Name: model.Name, Version: model.Version, Resolution: model.Resolution}, Result: ApiResult{Labels: labels}, } } // NewCaptionResponse generates a new Vision API image caption service response. func NewCaptionResponse(id string, model *Model, result *CaptionResult) ApiResponse { return ApiResponse{ Id: clean.Type(id), Code: http.StatusOK, Model: &Model{Type: ModelTypeLabels, Name: model.Name, Version: model.Version, Resolution: model.Resolution}, Result: ApiResult{Caption: result}, } }