Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

97 lines
3.8 KiB
Go

package db
import (
"context"
"errors"
)
// ErrSemanticUnavailable is returned by SearchContent for modes "semantic"
// and "hybrid" when no VectorSearcher has been wired in (SetVectorSearcher
// was never called, or the concrete backend doesn't support semantic search
// at all — PostgreSQL and DuckDB always return it for these modes).
var ErrSemanticUnavailable = errors.New(
"semantic search not available: enable [vector] in config.toml and run 'agentsview embeddings build'")
// ErrSemanticTransient is returned by SearchContent's semantic/hybrid modes
// when the wired VectorSearcher's query-time embed call itself failed (the
// embeddings endpoint is down, slow, or erroring), as distinct from
// ErrSemanticUnavailable's "not configured, or a build has never
// completed" cases: semantic search IS configured and otherwise usable,
// this particular request just failed and can be retried. It is
// deliberately not wrapped by ErrSemanticUnavailable, so
// errors.Is(err, ErrSemanticUnavailable) stays false for it and callers
// don't mistake a transient endpoint outage for "semantic search is
// disabled".
var ErrSemanticTransient = errors.New(
"semantic search embeddings endpoint is unavailable; the request can be retried")
// VectorHit is one unit-level semantic search hit, ranked best first.
// Ordinal is the anchor ordinal: for a run document, the member message
// whose rune span contains the matched chunk's center; for a user document
// it is the message's own ordinal. OrdinalStart/OrdinalEnd span the whole
// unit (both equal Ordinal for user documents), and Subordinate carries the
// unit's sidechain/subagent classification from the vector mirror.
type VectorHit struct {
SessionID string
Ordinal int // anchor ordinal
OrdinalStart int
OrdinalEnd int
Subordinate bool
Score float32
Snippet string
}
// MessageRef identifies one message by its session and ordinal, the shape
// the hybrid FTS leg hands to ResolveMessageUnits.
type MessageRef struct {
SessionID string
Ordinal int
}
// UnitRef locates the embedding unit (user document or assistant run)
// containing a message. The zero value (DocKey == "") means "no containing
// unit": the message lies outside the embeddable universe, and the hybrid
// path keeps such an FTS hit at message granularity rather than dropping it.
type UnitRef struct {
DocKey string
SessionID string
OrdinalStart int
OrdinalEnd int
Subordinate bool
}
// VectorSearcher is the seam through which internal/db reaches the vector
// embedding index without importing internal/vector directly, which would
// create an import cycle (internal/vector depends on internal/db's schema
// helpers). The concrete implementation is internal/vector's Index, wired in
// at startup via SetVectorSearcher.
type VectorSearcher interface {
// SemanticSearch embeds query and returns up to limit unit-level hits,
// best first.
SemanticSearch(ctx context.Context, query string, limit int) ([]VectorHit, error)
// ResolveMessageUnits maps each ref to the unit containing it. The
// result is parallel to refs; a ref with no containing unit yields a
// zero UnitRef (DocKey == "").
ResolveMessageUnits(ctx context.Context, refs []MessageRef) ([]UnitRef, error)
}
// SetVectorSearcher wires (or, with nil, clears) the semantic search
// backend. Safe to call concurrently with SearchContent/HasSemantic.
func (db *DB) SetVectorSearcher(v VectorSearcher) {
db.vectorMu.Lock()
defer db.vectorMu.Unlock()
db.vectorSearcher = v
}
// HasSemantic reports whether a VectorSearcher has been wired in.
func (db *DB) HasSemantic() bool {
return db.getVectorSearcher() != nil
}
// getVectorSearcher returns the currently wired VectorSearcher, or nil.
func (db *DB) getVectorSearcher() VectorSearcher {
db.vectorMu.RLock()
defer db.vectorMu.RUnlock()
return db.vectorSearcher
}