feat: prefer base64 inline image for summary, fall back to S3
Send the screenshot to the multimodal model inline as a base64 data URL so summarisation no longer requires S3 to be configured or publicly reachable. Only when the PNG exceeds the provider MaxInlineBytes limit do we upload to S3, verify the public URL is reachable, and delete the temporary object once done. Oversized images without S3 now surface a clear hint instead of a hard S3 requirement.
This commit is contained in:
@@ -2,6 +2,7 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -10,6 +11,7 @@ type fakeOSSProvider struct {
|
||||
data []byte
|
||||
contentType string
|
||||
url string
|
||||
deletedKey string
|
||||
}
|
||||
|
||||
func (f *fakeOSSProvider) Upload(_ context.Context, key string, data []byte, contentType string) (string, error) {
|
||||
@@ -24,6 +26,11 @@ func (f *fakeOSSProvider) Upload(_ context.Context, key string, data []byte, con
|
||||
|
||||
func (f *fakeOSSProvider) Name() string { return "fake" }
|
||||
|
||||
func (f *fakeOSSProvider) Delete(_ context.Context, key string) error {
|
||||
f.deletedKey = key
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeSummarizer struct {
|
||||
prompt string
|
||||
url string
|
||||
@@ -84,3 +91,50 @@ func TestCaptureSummaryServiceUploadsSummarizesAndCopies(t *testing.T) {
|
||||
t.Fatalf("expected summary copied, got %q", clip.text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCaptureSummaryServiceInlineDataURL(t *testing.T) {
|
||||
svc := &CaptureSummaryService{MaxInlineBytes: 10}
|
||||
|
||||
if !svc.CanInline([]byte("small")) {
|
||||
t.Fatalf("expected 5-byte payload to be inlinable under 10-byte cap")
|
||||
}
|
||||
if svc.CanInline([]byte("this is definitely too big")) {
|
||||
t.Fatalf("expected oversized payload to be rejected for inlining")
|
||||
}
|
||||
|
||||
url := svc.InlineDataURL([]byte("png"))
|
||||
if !strings.HasPrefix(url, "data:image/png;base64,") {
|
||||
t.Fatalf("expected base64 data URL prefix, got %q", url)
|
||||
}
|
||||
if strings.Contains(url, "https://") {
|
||||
t.Fatalf("inline URL must not be a remote URL, got %q", url)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCaptureSummaryServiceDeleteUploadedUsesDeleter(t *testing.T) {
|
||||
provider := &fakeOSSProvider{}
|
||||
svc := &CaptureSummaryService{Provider: provider}
|
||||
|
||||
if err := svc.DeleteUploaded(context.Background(), "snapgo/a.png"); err != nil {
|
||||
t.Fatalf("delete uploaded: %v", err)
|
||||
}
|
||||
if provider.deletedKey != "snapgo/a.png" {
|
||||
t.Fatalf("expected object deleted, got %q", provider.deletedKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCaptureSummaryServiceDeleteUploadedNoDeleterIsNoop(t *testing.T) {
|
||||
// A provider without Delete must not error when cleanup is requested.
|
||||
svc := &CaptureSummaryService{Provider: nonDeletingProvider{}}
|
||||
if err := svc.DeleteUploaded(context.Background(), "snapgo/a.png"); err != nil {
|
||||
t.Fatalf("expected no-op delete, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type nonDeletingProvider struct{}
|
||||
|
||||
func (nonDeletingProvider) Upload(_ context.Context, _ string, _ []byte, _ string) (string, error) {
|
||||
return "https://cdn.example.com/x.png", nil
|
||||
}
|
||||
|
||||
func (nonDeletingProvider) Name() string { return "no-delete" }
|
||||
|
||||
Reference in New Issue
Block a user