feat(faceswap): carry the measured outcome, not just the image
A face swap always returns an image and always looks like success. Whether the likeness actually transferred is a different question, and until now nothing in the response answered it — so a caller wanting to know went and asked a vision model instead. That is wrong in precisely the cases that matter: shown a jogger in a Georgetown cap holding McDonald's cups, a VLM answers "Bill Clinton" whoever's face is on him. In the run that prompted this it reported failure on six consecutive CORRECT swaps (measured afterwards at 0.79-0.84 cosine), and the caller burned 21 minutes chasing a problem that did not exist. Result.SwappedFaces now carries, per replaced face: pixel size, the target image's dimensions, head yaw, and cosine similarity between the source face and the face actually present in the output. Yaw and FractionOfImage are the two that explain the complaint. The swap in question replaced a 138px face in a 1010px-wide photo — 14% of the width, correct and invisible at a glance — and elsewhere a face turned -82 degrees, where the features carrying identity are edge-on and any swap reads as a generic person. Same code on a 168px face in a 385px picture (44%, yaw 2) is unmistakable. None of that was inferable from a bounding box. Typed on Result rather than stuffed into Raw: a caller has to act on this, and a value reachable only by type-asserting an `any` is one nobody finds in time. doRawHeaders is doRaw with the whole header instead of only Content-Type; doRaw delegates to it, so the other 25 call sites are untouched and there is still one place where the status check and the size cap live. A missing or malformed header yields nil, not an error — an older shim sends no header, and a swap that produced a good image must not fail because the diagnostics beside it were unreadable. Covered for absent/garbage/wrong-type, and the parse is break-checked.
This commit is contained in:
@@ -338,27 +338,41 @@ func parseVoices(raw []byte) ([]string, error) {
|
||||
// varies. contentType sets the request Content-Type when body is non-nil.
|
||||
// A response larger than maxBytes is an error, never a silent truncation.
|
||||
func (p *Provider) doRaw(ctx context.Context, method, path, model, contentType string, body io.Reader, maxBytes int64) ([]byte, string, error) {
|
||||
if err := p.requireBaseURL(); err != nil {
|
||||
data, hdr, err := p.doRawHeaders(ctx, method, path, model, contentType, body, maxBytes)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return data, hdr.Get("Content-Type"), nil
|
||||
}
|
||||
|
||||
// doRawHeaders is doRaw with the WHOLE response header rather than just
|
||||
// Content-Type. Only the face swap needs it — the shim reports whether the
|
||||
// likeness actually transferred in X-Swap-Report, and that answer would be
|
||||
// thrown away by a function that keeps one header — so doRaw stays the
|
||||
// signature 25 other call sites use and delegates here. Two bodies would be
|
||||
// two places for the size cap and the status check to drift apart.
|
||||
func (p *Provider) doRawHeaders(ctx context.Context, method, path, model, contentType string, body io.Reader, maxBytes int64) ([]byte, http.Header, error) {
|
||||
if err := p.requireBaseURL(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
req, err := p.newRequest(ctx, method, path, contentType, body)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("llama-swap: do request: %w", err)
|
||||
return nil, nil, fmt.Errorf("llama-swap: do request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return nil, "", p.apiError(resp, model)
|
||||
return nil, nil, p.apiError(resp, model)
|
||||
}
|
||||
data, err := io.ReadAll(io.LimitReader(resp.Body, maxBytes+1))
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("llama-swap: read response: %w", err)
|
||||
return nil, nil, fmt.Errorf("llama-swap: read response: %w", err)
|
||||
}
|
||||
if int64(len(data)) > maxBytes {
|
||||
return nil, "", fmt.Errorf("llama-swap: response exceeds %d bytes", maxBytes)
|
||||
return nil, nil, fmt.Errorf("llama-swap: response exceeds %d bytes", maxBytes)
|
||||
}
|
||||
return data, resp.Header.Get("Content-Type"), nil
|
||||
return data, resp.Header, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user