embedding

package
v2.3.4 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package embedding provides text-to-vector embedding for Cortex.

Embeddings enable semantic search: instead of matching keywords, search by meaning. The service supports multiple backends:

  • ollama: Local Ollama server (default: nomic-embed-text, 768 dims)
  • openai: OpenAI API (text-embedding-3-small, 1536 dims)
  • none: Disabled (default)

HTTP client lifecycle: each backend holds a single reusable *http.Client (constructed once in New) so HTTP keepalive connections are pooled across all Embed() calls rather than re-created per call. The concrete types (*ollamaService, *openAIService) implement io.Closer: Close() calls CloseIdleConnections on the shared client, reaping the Transport's persistConn read/write goroutines. The composition root (app.Close, bench Close) type-asserts to io.Closer to invoke it — the Service interface itself is NOT bloated with Close(), so test fakes and stubs need no changes.

Package embedding also provides the durable embedding worker (ADR-04, W4, REQ-EMB-001).

The worker drains the transactional outbox asynchronously: it leases pending embed+upsert intents, hydrates the observation text, embeds it with the configured model (versioned namespace), upserts the vector, records index namespace coverage, and marks the intent complete. Failures retry with capped exponential backoff up to max_attempts; terminal (non-retryable) failures go to dead-letter. The worker uses a bounded pool, drains on shutdown BEFORE DB.Close, and propagates cancellation — no detached fire-and-forget goroutines.

Index

Constants

View Source
const (
	// MaxSingleEmbeddingLength is ~2000 words / 8000 chars, well within the 8191 token limit of modern models.
	MaxSingleEmbeddingLength = 8000
	// ChunkOverlapChars is the sliding window overlap for paragraph continuity.
	ChunkOverlapChars = 400
)
View Source
const DefaultCacheCapacity = 1000

DefaultCacheCapacity is the default number of embeddings cached in memory.

Variables

This section is empty.

Functions

func ChunkText added in v2.3.0

func ChunkText(text string, maxChars, overlapChars int) []string

ChunkText splits large content into overlapping chunks if it exceeds maxChars.

func PrepareObservationText added in v2.3.0

func PrepareObservationText(obs *domain.Observation) string

PrepareObservationText builds the dense text payload for embedding. It enriches the text with contextual metadata (Anthropic Contextual Retrieval technique) so dense vector search retrieves relevant memories even with brief query keywords.

Types

type CachedService added in v2.3.3

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

CachedService wraps any Service with a high-performance, thread-safe LRU cache.

func NewCachedService added in v2.3.3

func NewCachedService(inner Service, capacity int) *CachedService

NewCachedService creates a new CachedService wrapping inner with the given capacity. If capacity <= 0, DefaultCacheCapacity (1000) is used.

func (*CachedService) Close added in v2.3.3

func (c *CachedService) Close() error

Close closes the underlying service if it implements io.Closer.

func (*CachedService) Dimensions added in v2.3.3

func (c *CachedService) Dimensions() int

Dimensions returns the embedding dimension size.

func (*CachedService) Embed added in v2.3.3

func (c *CachedService) Embed(ctx context.Context, text string) ([]float32, error)

Embed returns a vector embedding for the given text, serving from LRU cache if available.

func (*CachedService) Len added in v2.3.3

func (c *CachedService) Len() int

Len returns current number of cached entries.

func (*CachedService) Model added in v2.3.3

func (c *CachedService) Model() string

Model returns the model identifier.

type Config

type Config struct {
	Provider string // "ollama", "openai", "none"
	APIKey   string // API key (OpenAI only; defaults to env var)
	Model    string // Model name override
	BaseURL  string // Base URL override (Ollama: default http://localhost:11434)
}

Config configures the embedding service.

type OutboundPolicy added in v2.3.0

type OutboundPolicy struct {
	AllowedHosts              []string
	AllowedPorts              []int
	AllowLoopback             bool
	AllowInsecureLoopbackHTTP bool
	// RailwayInternalEmbeddingHost is one exact, administrator-configured
	// *.railway.internal hostname permitted to resolve to a private IP for
	// the embedding provider. All other private HTTP destinations stay denied.
	RailwayInternalEmbeddingHost string
	MaxRedirects                 int
	MaxResponseBodyBytes         int64
	MaxConcurrent                int
	Timeout                      time.Duration
}

func (*OutboundPolicy) ApproveDestination added in v2.3.0

func (p *OutboundPolicy) ApproveDestination(raw string) error

type Service

type Service interface {
	// Embed returns a vector embedding for the given text.
	Embed(ctx context.Context, text string) ([]float32, error)

	// Dimensions returns the embedding dimension size.
	Dimensions() int

	// Model returns the model identifier.
	Model() string
}

Service generates embeddings from text.

func New

func New(cfg Config) Service

New creates an embedding service from config with a transparent in-memory LRU cache. Returns nil if provider is "none" or empty.

func NewSecure added in v2.3.0

func NewSecure(cfg Config, policy OutboundPolicy) (Service, error)

type Worker

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

Worker is the durable embedding worker. It drains the outbox with a bounded pool of goroutines, retrying failures with capped backoff and dead-lettering terminal failures. Start returns a cancel func that drains on shutdown.

func NewWorker

func NewWorker(outbox outboxQueue, obs observationReader, embeddings Service, vectors vectorWriter, cfg WorkerConfig) *Worker

NewWorker creates an embedding worker. The concrete *sqlitestore.OutboxStore and *sqlitestore.Store satisfy the interface parameters structurally; the vectors parameter accepts any domain.VectorIndex implementation (the sqlite_blob adapter is the W8 default, ADR-05). The worker depends on the domain port, not the concrete vector store.

func (*Worker) IsSaturated

func (w *Worker) IsSaturated(ctx context.Context) (bool, error)

IsSaturated reports whether the outbox backlog exceeds the configured MaxBacklog threshold. The save path calls this before enqueuing to fail-closed under overload (REQ-EMB-001 saturation/overload behavior).

func (*Worker) Start

func (w *Worker) Start(ctx context.Context) context.CancelFunc

Start begins processing outbox intents in a bounded pool of goroutines. It first recovers any intents left 'leased' by a crashed worker (crash recovery, REQ-EMB-001). The returned cancel func is the worker's STOP function and implements explicit cancel/stop/join semantics with TWO bounded contexts:

  • runCtx bounds leasing, hydration, embedding, and upsert work.
  • finalizeCtx bounds outcome-recording operations (MarkComplete/MarkFailed/ DeadLetter/UpdateIndexState).

The stop func executes three phases:

  1. STOP — cancel runCtx: no new leases; in-flight embed/upsert abort and the intent is left leased for crash-recovery on next startup.
  2. JOIN — wait for all worker goroutines to exit, bounded by DrainTimeout. finalizeCtx is still alive, so outcomes of in-flight intents are recorded normally (no silent loss on a graceful drain).
  3. CANCEL FINALIZE — unconditionally cancel finalizeCtx. After this returns, no goroutine can complete a finalize DB write: • joined goroutines have already exited; • a goroutine still unwinding an in-flight finalize observes ctx.Done and the ExecContext aborts BEFORE touching the DB; • a goroutine stuck in an embed/upsert (runCtx already cancelled) cannot reach finalize; its intent remains leased → recovery.

Therefore DB.Close MUST be called only after this stop func returns — and when it does, no goroutine is touching or can touch the DB (no write-after-close, no leaked DB-accessing goroutine).

type WorkerConfig

type WorkerConfig struct {
	// Concurrency is the bounded worker pool size (goroutines). Default 2.
	Concurrency int
	// PollInterval is how long to wait before re-checking for new work when the
	// outbox is empty. Default 100ms.
	PollInterval time.Duration
	// LeaseBatch is how many intents a single worker leases per poll. Default 1.
	LeaseBatch int
	// MaxBacklog is the saturation threshold: when PendingCount exceeds this,
	// the save path fails-closed (REQ-EMB-001 saturation). Default 1000.
	MaxBacklog int
	// DrainTimeout bounds how long Start's cancel func waits for in-flight work
	// on shutdown. Default 30s.
	DrainTimeout time.Duration
}

WorkerConfig configures the embedding worker.

Jump to

Keyboard shortcuts

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