73 lines
2.2 KiB
Go
73 lines
2.2 KiB
Go
package application
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/mmmy/snapgo/internal/domain"
|
|
"github.com/mmmy/snapgo/internal/infrastructure/clipboard"
|
|
)
|
|
|
|
// VisionSummarizer describes a multimodal model capable of reading a public
|
|
// image URL and returning a textual summary.
|
|
type VisionSummarizer interface {
|
|
SummarizeImage(ctx context.Context, prompt string, imageURL string) (string, error)
|
|
}
|
|
|
|
// CaptureSummaryService wires screenshot bytes -> S3 public URL -> LLM
|
|
// summary -> clipboard. It intentionally does not notify UI state itself:
|
|
// the caller emits fine-grained "uploading / recognizing / done" progress.
|
|
type CaptureSummaryService struct {
|
|
Provider domain.OSSProvider
|
|
Summarizer VisionSummarizer
|
|
Clipboard clipboard.Writer
|
|
Prompt string
|
|
PathPrefix string
|
|
}
|
|
|
|
// UploadImage uploads the screenshot and returns the public URL visible to
|
|
// the multimodal provider.
|
|
func (s *CaptureSummaryService) UploadImage(ctx context.Context, pngBytes []byte) (*domain.UploadResult, error) {
|
|
if s.Provider == nil {
|
|
return nil, fmt.Errorf("s3 is not configured")
|
|
}
|
|
if len(pngBytes) == 0 {
|
|
return nil, fmt.Errorf("empty screenshot")
|
|
}
|
|
key := buildObjectKey(s.PathPrefix)
|
|
start := time.Now()
|
|
url, err := s.Provider.Upload(ctx, key, pngBytes, "image/png")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("s3 put object: %w", err)
|
|
}
|
|
return &domain.UploadResult{
|
|
URL: url,
|
|
Key: key,
|
|
Provider: s.Provider.Name(),
|
|
Elapsed: time.Since(start),
|
|
}, nil
|
|
}
|
|
|
|
// Summarize asks the configured model to summarize the uploaded image URL.
|
|
func (s *CaptureSummaryService) Summarize(ctx context.Context, imageURL string) (string, error) {
|
|
if s.Summarizer == nil {
|
|
return "", fmt.Errorf("llm is not configured")
|
|
}
|
|
return s.Summarizer.SummarizeImage(ctx, s.Prompt, imageURL)
|
|
}
|
|
|
|
// CopySummary writes the final summary text to the clipboard.
|
|
func (s *CaptureSummaryService) CopySummary(_ context.Context, summary string) error {
|
|
if summary == "" {
|
|
return fmt.Errorf("empty summary")
|
|
}
|
|
if s.Clipboard == nil {
|
|
return fmt.Errorf("clipboard is not configured")
|
|
}
|
|
if err := s.Clipboard.WriteText(summary); err != nil {
|
|
return fmt.Errorf("clipboard write failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|