package analysis import ( "context" "fmt" "go-places-api/internal/domain" ) // Composite runs the local analyzer first and then, when a remote service is // configured, feeds the locally derived scene labels to it as hints. Running // the local pass first is what makes the hints available, and it also means the // picture still gets badges if the remote service is unreachable. type Composite struct { local Analyzer remote Analyzer } var _ Analyzer = (*Composite)(nil) // NewComposite chains two analyzers. Either may be nil, in which case it is // skipped. func NewComposite(local, remote Analyzer) *Composite { return &Composite{local: local, remote: remote} } // Name implements Analyzer. func (c *Composite) Name() string { if c.remote == nil { return SourceLocal } return SourceLocal + "+" + SourceRemote } // Analyze measures locally, then augments with model labels when available. // // A failure in the remote service is reported as a non-fatal note rather than // an error: the picture was still understood, just less precisely. Callers can // inspect Note to surface the degradation. func (c *Composite) Analyze(ctx context.Context, in Input) (*Result, error) { var ( base *Result local []domain.Badge note string ) if c.local != nil { res, err := c.local.Analyze(ctx, in) if err != nil { return nil, err } base = res local = res.Badges } else { base = &Result{} } if c.remote != nil { hinted := in hinted.Hints = labelsOf(local) res, err := c.remote.Analyze(ctx, hinted) switch { case err != nil: note = fmt.Sprintf("%s analyzer unavailable: %v", SourceRemote, err) if ctx.Err() != nil { return nil, ctx.Err() } default: base.Width, base.Height = pickDimension(base.Width, res.Width), pickDimension(base.Height, res.Height) base.Badges = Combine(local, res.Badges) } } else { base.Badges = local } base.Note = note return base, nil } // labelsOf extracts badge labels to use as upstream hints. func labelsOf(badges []domain.Badge) []string { if len(badges) == 0 { return nil } out := make([]string, 0, len(badges)) for _, b := range badges { if b.Confidence >= 0.5 { out = append(out, b.Label) } } return out } func pickDimension(current, candidate int) int { if candidate > 0 { return candidate } return current }