Documentation
¶
Index ¶
- Constants
- Variables
- func SameSpace(colls []*Collection) error
- func ValidateCollectionName(name string) error
- type Attachment
- type Chunk
- type ChunkHit
- type ChunkID
- type ChunkResult
- type Chunker
- type ChunkerSpec
- type Citation
- type Collection
- type ContentHash
- type Document
- type DocumentID
- type EmbeddingSpace
- type FixedChunker
- type MarkdownChunker
- type ParsedDoc
- type Registry
- type TextChunker
- type VectorMatch
Constants ¶
const ( DefaultChunkSize = 512 DefaultChunkOverlap = 76 )
Default chunking parameters: ~512 tokens, ~15% overlap. For the fixed strategy a "token" is a whitespace word; the structure-aware strategies measure real tokens via an injected counter.
const FixedChunkerVersion = 1
FixedChunkerVersion is the algorithm version of the fixed (word-window) strategy, recorded in a Collection's ChunkerSpec. Bump it if the fixed chunker's output could change for the same input.
const StructureChunkerVersion = 1
StructureChunkerVersion is the algorithm version of the structure-aware strategy (markdown sections + token sizing, plus the plain-text strategy that shares its sizing), recorded in a ChunkerSpec. Bump it when any structure chunker's output could change for the same input.
Variables ¶
var ( // ErrInvalidArgument marks construction/validation failures of domain values. ErrInvalidArgument = errors.New("invalid argument") // ErrSpaceMismatch marks a space-coherence violation: // an attempt to mix vectors from a different EmbeddingSpace into a Collection. ErrSpaceMismatch = errors.New("embedding space mismatch") // ErrChunkerMismatch marks an attempt to (re-)ingest into a Collection with a // chunker different from the one it was pinned to (or any chunker, for a // collection that predates pinning). Like a space mismatch it is an invariant // violation; the CLI maps it to exit 4. ErrChunkerMismatch = errors.New("chunker mismatch") )
Sentinel errors for domain invariant violations. Infrastructure-level errors (ErrNotFound etc.) live in internal/app, next to the ports.
Functions ¶
func SameSpace ¶
func SameSpace(colls []*Collection) error
SameSpace enforces space coherence across a set of collections: their vectors are directly comparable only if every collection shares one EmbeddingSpace. It is the precondition for any cross-collection retrieval — feeding one collection's vectors into another (query --from-collection) or merging hits from several (multi-collection ask/query) — and returns ErrSpaceMismatch naming the first offending collection (and both spaces) on a divergence. Zero or one collection trivially shares a space.
func ValidateCollectionName ¶
ValidateCollectionName reports whether name is non-empty, filesystem- and shell-safe, and at most 64 chars. It is the shared rule behind NewCollection and the import path, which reconstructs a (possibly renamed) collection from an artifact and must validate the target name without building a full collection.
Types ¶
type Attachment ¶
type Attachment struct {
MediaType string // e.g. "image/png", "application/pdf"
Name string // original filename, for display/citation; optional
Data []byte
}
Attachment is raw content handed to a Generator alongside retrieved chunks — an image or document the model reads directly. Unlike ingested content it is ephemeral: never chunked, embedded, or stored. How it is encoded for a provider, and whether the provider accepts it, are the Generator adapter's concern.
func NewAttachment ¶
func NewAttachment(mediaType, name string, data []byte) (Attachment, error)
NewAttachment validates and constructs an Attachment. Name may be empty.
type Chunk ¶
type Chunk struct {
ID ChunkID
DocumentID DocumentID
Seq int
Text string
// HeadingPath is the document section a chunk came from, joined with " > "
// (e.g. "Auth > Keys > Rotation"). Set by structure-aware chunkers; empty for
// formats or strategies without headings. It is provenance for display and
// inspection, not part of the chunk's identity.
HeadingPath string
}
Chunk is the unit of retrieval. It belongs to exactly one Document (deleting the Document deletes its Chunks).
type ChunkHit ¶
type ChunkHit struct {
Chunk Chunk
Score float64
Source string // source URI of the chunk's document
// Collection names the collection the chunk came from. It is empty for
// single-collection retrieval (where it is implied by the request) and set
// only for cross-collection results (multi-collection query/ask), so the
// merged hits stay attributable to their origin.
Collection string
RerankScore *float64
}
ChunkHit is a hydrated retrieval result: the chunk itself, its similarity score, and the URI of the source document it came from (provenance for display and citation). RerankScore is set only after a cross-encoder rerank — nil means the hit was never reranked, so it serializes additively (rerank_score omitted) and the original Score is always preserved alongside it.
type ChunkID ¶
type ChunkID string
ChunkID is deterministic, derived from (DocumentID, Seq), so re-chunking an unchanged document yields identical identities, keeping ingestion idempotent.
func DeriveChunkID ¶
func DeriveChunkID(docID DocumentID, seq int) ChunkID
DeriveChunkID computes the deterministic identity of a chunk.
type ChunkResult ¶
type ChunkResult struct {
Text string
// EmbedText is the text to embed when it should differ from the stored Text —
// e.g. a heading-path context prefix prepended so the embedding captures the
// chunk's place in the document. Empty means embed Text as-is; either way the
// stored Text (what citations and inspection show) is the original, never the
// prefixed form.
EmbedText string
// HeadingPath is the document section the chunk came from (e.g.
// "Auth > Keys > Rotation"), set by structure-aware chunkers; empty otherwise.
HeadingPath string
}
ChunkResult is one chunk a Chunker produced: the text to store and cite, plus any section metadata. Identity (ChunkID, Seq) is assigned by the ingestor, not the chunker, since only the ingestor knows the owning Document.
func (ChunkResult) TextToEmbed ¶
func (r ChunkResult) TextToEmbed() string
TextToEmbed returns the text that should be embedded for this chunk: the EmbedText override when set, otherwise the stored Text.
type Chunker ¶
type Chunker interface {
Chunk(doc ParsedDoc) ([]ChunkResult, error)
}
Chunker splits a parsed document into ordered chunks. Implementations are deterministic and pure: same input → identical output, no I/O, no map-iteration-order dependence. A token-aware strategy receives a token-count func at construction rather than importing a tokenizer, so stdlib-only domain stays clean.
type ChunkerSpec ¶
type ChunkerSpec struct {
Strategy string // "structure" | "fixed"
Version int // strategy algorithm version; bump when output could change for the same input
Size int // target chunk size (tokens for structure, words for fixed)
Overlap int // overlap between adjacent chunks, same unit as Size
Tokenizer string // sizing tokenizer identity, e.g. "o200k_base" (structure) or "words" (fixed)
ContextPrefix bool // whether a heading-path context line is prepended to the embedded text
}
ChunkerSpec identifies the chunking configuration a Collection is pinned to: the strategy, its algorithm version, the size/overlap parameters, the tokenizer used for sizing, and whether a heading-path context prefix is embedded. Any of these changing alters chunk output (boundaries, count, or embedded text) for the same input, so a Collection records its spec at creation and refuses re-ingestion under a different one — the chunker analogue of space coherence. It is comparable with ==.
func NewChunkerSpec ¶
func NewChunkerSpec(strategy string, version, size, overlap int, tokenizer string, contextPrefix bool) (ChunkerSpec, error)
NewChunkerSpec validates and constructs a ChunkerSpec.
func (ChunkerSpec) IsZero ¶
func (s ChunkerSpec) IsZero() bool
IsZero reports whether the spec is uninitialized — the state of a Collection created before chunker pinning existed.
func (ChunkerSpec) String ¶
func (s ChunkerSpec) String() string
String renders the spec for human and error output, e.g. "structure/v1 size=512 overlap=64 tok=o200k_base prefix=on".
func (ChunkerSpec) Validate ¶
func (s ChunkerSpec) Validate() error
Validate reports whether the spec is well-formed.
type Citation ¶
type Citation struct {
ChunkID ChunkID
Source string // source URI of the chunk's document
Seq int // the chunk's position within that document
// Collection names the chunk's collection, set only for cross-collection
// answers (multi-collection ask) so a citation stays attributable to its
// origin; empty for single-collection answers.
Collection string
}
Citation references a chunk an answer was grounded in, carrying the provenance needed to display it: the source document's URI and the chunk's position within it.
type Collection ¶
type Collection struct {
Name string
Space EmbeddingSpace
Chunker ChunkerSpec
CreatedAt time.Time
// Sources are the paths ingested into this collection, recorded by add and
// sync so `lore sync` can replay them without a path argument.
Sources []string
}
Collection is the aggregate root: a named corpus bound to exactly one EmbeddingSpace, and pinned to exactly one ChunkerSpec, for its entire lifetime.
func NewCollection ¶
func NewCollection(name string, space EmbeddingSpace, chunker ChunkerSpec, now time.Time) (*Collection, error)
NewCollection validates identity, space, and chunker spec, and constructs a Collection. The clock is injected so the domain stays deterministic and testable. A new collection must be pinned to a valid ChunkerSpec; collections loaded from storage that predate chunker pinning carry a zero spec (built as a literal by the repository, not through this constructor).
func (*Collection) AcceptsChunker ¶
func (c *Collection) AcceptsChunker(spec ChunkerSpec) error
AcceptsChunker enforces chunker coherence: a collection may be (re-)ingested only by the chunker it was pinned to. Mixing chunker configurations within a collection would silently leave it holding chunks of two incompatible layouts (unchanged documents fast-skip re-chunking), so a mismatch is refused — the collection must be rebuilt under the new chunker. A collection that predates chunker pinning (zero spec) is read-only: queryable, but not re-ingestable.
func (*Collection) AcceptsSpace ¶
func (c *Collection) AcceptsSpace(s EmbeddingSpace) error
AcceptsSpace enforces space coherence: vectors may enter the collection only if they were produced in the collection's own space.
type ContentHash ¶
type ContentHash string
ContentHash is the SHA-256 of document content, hex-encoded. Hash equality is what makes ingestion idempotent.
func HashContent ¶
func HashContent(content []byte) ContentHash
HashContent computes the ContentHash of raw content.
type Document ¶
type Document struct {
ID DocumentID
Collection string
SourceURI string
Hash ContentHash
IngestedAt time.Time
// Fingerprint is a cheap source-side signature (e.g. size + sampled-content
// hash) used to skip re-reading unchanged files before extraction. Unlike
// Hash it is a heuristic, not an identity; empty means "unknown".
Fingerprint string
}
Document is source content with identity. Whether its content *changed* is decided by Hash (hash equality => ingestion is a no-op).
func NewDocument ¶
NewDocument constructs a Document with its deterministic ID.
func (*Document) Unchanged ¶
func (d *Document) Unchanged(hash ContentHash) bool
Unchanged reports whether content with the given hash would be a no-op re-ingestion of this document.
type DocumentID ¶
type DocumentID string
DocumentID is deterministic, derived from (collection, source URI): re-ingesting the same source addresses the same identity, which is what makes `lore add` an upsert rather than an append.
func DeriveDocumentID ¶
func DeriveDocumentID(collection, sourceURI string) DocumentID
DeriveDocumentID computes the deterministic identity of a source within a collection. The NUL separator prevents ambiguity between (a,bc) and (ab,c).
type EmbeddingSpace ¶
EmbeddingSpace identifies the vector space a Collection is bound to: the embedding model plus its output dimensionality. Two vectors are comparable only if they come from the same space.
func NewEmbeddingSpace ¶
func NewEmbeddingSpace(model string, dimensions int) (EmbeddingSpace, error)
NewEmbeddingSpace validates and constructs an EmbeddingSpace.
func (EmbeddingSpace) Equal ¶
func (s EmbeddingSpace) Equal(other EmbeddingSpace) bool
Equal reports whether two spaces are the same (and therefore whether their vectors are comparable).
func (EmbeddingSpace) IsZero ¶
func (s EmbeddingSpace) IsZero() bool
IsZero reports whether the space is uninitialized.
func (EmbeddingSpace) String ¶
func (s EmbeddingSpace) String() string
type FixedChunker ¶
type FixedChunker struct {
// contains filtered or unexported fields
}
FixedChunker emits fixed-size, overlapping windows measured in whitespace words — the legacy chunking behavior, preserved verbatim as the `fixed` strategy and the default fallback. Word sizing is dependency-free and deterministic; the structure-aware strategies use real token counts instead. Chunk text is whitespace-normalized as a side effect.
Construct with NewFixedChunker; the zero value is not usable.
func NewFixedChunker ¶
func NewFixedChunker(size, overlap int) (FixedChunker, error)
NewFixedChunker returns a FixedChunker that emits chunks of at most size words, with consecutive chunks sharing overlap words. It requires size > 0 and 0 <= overlap < size, so every window advances.
func (FixedChunker) Chunk ¶
func (c FixedChunker) Chunk(doc ParsedDoc) ([]ChunkResult, error)
Chunk implements Chunker by windowing the document text. ContentType is ignored — fixed sizing is format-agnostic.
func (FixedChunker) Split ¶
func (c FixedChunker) Split(text string) []string
Split divides text into chunks in document order. Consecutive chunks share up to overlap words. Empty or whitespace-only input yields no chunks, and no chunk is ever empty.
type MarkdownChunker ¶
type MarkdownChunker struct {
// contains filtered or unexported fields
}
MarkdownChunker splits markdown into token-sized chunks that respect the document's heading hierarchy: each chunk is a section (the content under a heading), carrying the full heading path as metadata. It never breaks inside a fenced code block; oversized sections split at paragraph (then sentence, then word) boundaries, and undersized adjacent sections merge up to the target size. Deterministic and pure.
Construct with NewMarkdownChunker; the zero value is not usable.
func NewMarkdownChunker ¶
func NewMarkdownChunker(size, overlap int, contextPrefix bool, countTokens func(string) int) (MarkdownChunker, error)
NewMarkdownChunker returns a MarkdownChunker targeting size tokens per chunk with overlap tokens shared between size-driven sub-splits. When contextPrefix is true, each chunk's embedded text is prefixed with its heading path so the embedding captures the chunk's place in the document (the stored text is unchanged). It requires size > 0, 0 <= overlap < size, and a non-nil counter.
func (MarkdownChunker) Chunk ¶
func (c MarkdownChunker) Chunk(doc ParsedDoc) ([]ChunkResult, error)
Chunk splits the document into heading-aware, token-sized chunks.
type ParsedDoc ¶
ParsedDoc is the input to a Chunker: the extracted text of one document plus the context a strategy may need. ContentType lets the Registry dispatch to a per-format strategy; SourceURI is carried for diagnostics.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry selects a Chunker by content type, falling back to a default. It is how per-format strategies (markdown, plain text, ...) plug in without the ingestor knowing which is which; a future code-aware strategy is just another registered entry (though a tree-sitter strategy could not be pure-domain). It also carries the ChunkerSpec the active configuration corresponds to, so the use cases can pin it on new collections and refuse re-ingest under a different one. Construct with NewRegistry; the zero value is not usable.
func NewRegistry ¶
NewRegistry builds a Registry routing each content type in byType to its Chunker and everything else to def, identified by spec. It requires a valid spec and a non-nil default, and rejects nil entries; content-type keys are normalized so dispatch is case- and parameter-insensitive.
func (Registry) Chunk ¶
func (r Registry) Chunk(doc ParsedDoc) ([]ChunkResult, error)
Chunk dispatches to the chunker registered for the document's content type, or the default if none matches.
func (Registry) Spec ¶
func (r Registry) Spec() ChunkerSpec
Spec returns the ChunkerSpec the registry's active configuration corresponds to — what new collections are pinned to and re-ingest is checked against.
type TextChunker ¶
type TextChunker struct {
// contains filtered or unexported fields
}
TextChunker splits plain text into token-sized chunks at paragraph (then sentence, then word) boundaries, never mid-sentence where avoidable. It has no heading awareness: it is the structure strategy's chunker for plain text and the default fallback for formats without their own structure-aware chunker (docx paragraphs; best-effort pdf/xlsx text). Deterministic and pure.
Construct with NewTextChunker; the zero value is not usable.
func NewTextChunker ¶
func NewTextChunker(size, overlap int, countTokens func(string) int) (TextChunker, error)
NewTextChunker returns a TextChunker targeting size tokens per chunk with overlap tokens shared between size-driven splits. It requires size > 0, 0 <= overlap < size, and a non-nil token counter.
func (TextChunker) Chunk ¶
func (c TextChunker) Chunk(doc ParsedDoc) ([]ChunkResult, error)
Chunk packs the document text into token-sized chunks. There is no heading path; ContentType is ignored.
type VectorMatch ¶
VectorMatch is what a VectorIndex returns: an identity and a similarity score (higher is more similar), not yet hydrated with chunk content.