attachments

package
v0.8.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package attachments provides a content-addressed attachment store backed by SHA-256 hashing and a SQLite metadata index. Duplicate files are stored only once on disk; metadata records track provenance per ingest, enabling queries by sender, channel, conversation, and content hash.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Analyzer

type Analyzer struct {
	// contains filtered or unexported fields
}

Analyzer performs vision analysis on image attachments using an LLM. Results are cached in the attachment metadata index so identical content is only analyzed once (leveraging content-addressed dedup).

func NewAnalyzer

func NewAnalyzer(store *Store, cfg AnalyzerConfig) *Analyzer

NewAnalyzer creates an Analyzer that uses the given store and LLM client for vision analysis.

func (*Analyzer) Analyze

func (a *Analyzer) Analyze(ctx context.Context, rec *Record) (string, error)

Analyze performs vision analysis on an image attachment. It returns the description text, or an empty string if the record is not an image. Results are cached in the attachment store: subsequent calls for the same record (or any record with the same content hash) return the cached description without calling the LLM.

func (*Analyzer) Reanalyze

func (a *Analyzer) Reanalyze(ctx context.Context, rec *Record, model string) (string, error)

Reanalyze forces a fresh vision analysis regardless of any cached result. Use this to upgrade descriptions when better models become available. The model parameter overrides the analyzer's configured model; pass an empty string to use the default.

type AnalyzerConfig

type AnalyzerConfig struct {
	Client  llm.Client    // multi-client routes to the correct provider
	Model   string        // vision model name (must be in models.available)
	Prompt  string        // custom analysis prompt; empty uses default
	Timeout time.Duration // per-analysis timeout; 0 uses defaultVisionTimeout
	Logger  *slog.Logger
}

AnalyzerConfig holds the dependencies for an Analyzer.

type IngestParams

type IngestParams struct {
	Source         io.Reader // attachment data to read
	OriginalName   string    // original filename if known
	ContentType    string    // MIME type
	Size           int64     // declared size in bytes (informational)
	Width          int       // image width in pixels
	Height         int       // image height in pixels
	Channel        string    // source channel ("signal", "email", etc.)
	Sender         string    // channel-specific sender identifier
	ConversationID string    // conversation context
	ReceivedAt     time.Time // when the attachment was received
}

IngestParams describes an attachment to be ingested into the store.

type Record

type Record struct {
	ID             string    // UUIDv4 primary key
	Hash           string    // hex-encoded SHA-256 of file content
	StorePath      string    // relative path in store: ab/cd/abcd...ef.jpg
	OriginalName   string    // filename as provided by sender
	ContentType    string    // MIME type (e.g. "image/jpeg")
	Size           int64     // file size in bytes
	Width          int       // image width in pixels (0 if not applicable)
	Height         int       // image height in pixels (0 if not applicable)
	Channel        string    // ingest channel ("signal", "email", etc.)
	Sender         string    // channel-specific sender identifier
	ConversationID string    // conversation the attachment belongs to
	ReceivedAt     time.Time // when the attachment was received

	// Vision analysis fields (populated by Analyzer).
	Description   string    // vision analysis text (empty = not analyzed)
	AnalyzedAt    time.Time // when analysis was performed (zero = not analyzed)
	AnalysisModel string    // model used for analysis
}

Record is one metadata entry for an ingested attachment.

type SearchParams

type SearchParams struct {
	ConversationID string // filter to a specific conversation
	Channel        string // filter by channel ("signal", "email")
	Sender         string // filter by sender identifier
	ContentType    string // MIME prefix filter (e.g. "image/")
	Query          string // text search across name, description, sender
	Limit          int    // max results; 0 → 20, capped at 50
}

SearchParams controls attachment listing and search queries.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store manages content-addressed file storage with a SQLite metadata index.

func NewStore

func NewStore(dbPath string, rootDir string, logger *slog.Logger) (*Store, error)

NewStore creates an attachment store rooted at rootDir with a SQLite metadata index at dbPath. The root directory is created if it does not exist.

func (*Store) AbsPath

func (s *Store) AbsPath(rec *Record) string

AbsPath returns the absolute filesystem path for a stored attachment.

func (*Store) ByHash

func (s *Store) ByHash(ctx context.Context, hash string) (*Record, error)

ByHash returns the first metadata record matching the given SHA-256 hash, or nil if no record exists.

func (*Store) ByID

func (s *Store) ByID(ctx context.Context, id string) (*Record, error)

ByID returns the metadata record with the given UUID, or nil if not found.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database connection.

func (*Store) Ingest

func (s *Store) Ingest(ctx context.Context, params IngestParams) (*Record, error)

Ingest reads the attachment from params.Source, computes its SHA-256 hash, stores it content-addressed on disk, and records metadata in the index. If a file with the same hash already exists on disk, the file is not rewritten (dedup). A new metadata record is always created so that provenance is tracked per ingest. Returns the created Record.

func (*Store) Search

func (s *Store) Search(ctx context.Context, params SearchParams) ([]*Record, error)

Search returns attachment records matching the given filters, ordered by received_at descending (newest first). All filter fields are optional; an empty SearchParams returns the most recent attachments.

func (*Store) TelemetryStats

func (s *Store) TelemetryStats(ctx context.Context) (total, totalBytes, unique int64, err error)

TelemetryStats returns aggregate attachment statistics for operational dashboards. The three counts (total records, total bytes, unique content hashes) are computed in a single query.

func (*Store) UpdateVision

func (s *Store) UpdateVision(ctx context.Context, id, description, model string) error

UpdateVision stores vision analysis results for an attachment record. An empty description is rejected — callers should only store meaningful analysis results. The analyzed_at timestamp is set automatically.

func (*Store) VisionByHash

func (s *Store) VisionByHash(ctx context.Context, hash string) (description, model string, ok bool)

VisionByHash returns cached vision analysis for any record matching the given content hash. This enables reuse across dedup hits — if the same image was already analyzed for a different sender, the cached description is returned. Returns ok=false if no analyzed record exists for the hash. Non-ErrNoRows database errors are logged and treated as cache misses.

type Tools

type Tools struct {
	// contains filtered or unexported fields
}

Tools provides agent tool handlers for attachment operations. The analyzer is optional — when nil, describe operations return cached descriptions only and cannot trigger new analysis.

func NewTools

func NewTools(store *Store, analyzer *Analyzer) *Tools

NewTools creates attachment tool handlers backed by the given store and optional vision analyzer.

func (*Tools) Describe

func (t *Tools) Describe(ctx context.Context, args map[string]any) (string, error)

Describe returns the vision description for an attachment. When the attachment has not been analyzed and a vision analyzer is available, analysis is triggered automatically. Supports re-analysis with optional model and prompt overrides.

func (*Tools) List

func (t *Tools) List(ctx context.Context, args map[string]any) (string, error)

List returns a formatted list of attachments matching the given filters.

func (*Tools) Search

func (t *Tools) Search(ctx context.Context, args map[string]any) (string, error)

Search performs a text search across attachment metadata and returns matching records.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL