Class RoleRagBinding
forRole(java.lang.Class<?>) call and
cached for the lifetime of the JVM.
Holds:
- A populated all-source
VectorMemoryStoreandBM25Index - Immutable entry partitions keyed by
Knowledge.name() - Lazily cached source-scoped index bundles used when a search covers a subset of the sources
RAGStrategyinstances for SEMANTIC, KEYWORD, HYBRID, GRAPH, HIERARCHICAL, TEMPORAL, and REASONING, built over the selected data capability at retrieval time. MULTI_QUERY is composed byUnifiedRetrievalRuntime; HYBRID RRF-fuses BM25, vector, and aGraphStorewhen one is unambiguous, otherwise the two-stream fuse plus a typed skip diagnostic; GRAPH binds an unambiguousGraphStoreProvideror fails the action; HIERARCHICAL binds a structurally valid hierarchy index or fails the action; TEMPORAL binds a structurally valid temporal index or fails the action; REASONING binds an unambiguousTreeNavigatorfor the declared model or fails the action.
Annotation-only bindings are shared by Role class. Builder-configured bindings are keyed by Role class plus the immutable source template, so agents with identical declarations share ingestion while different builder declarations cannot leak corpora into one another.
Knowledge.Type.FILE is the only type TnsAI
bundles a loader for (LocalFileSourceLoader). The rest are
extension points, not gaps: an application registers a
SourceLoader for them from an optional module, and until it
does, sources of that type are logged and skipped.
Vector-backed strategies and embedding-based deduplication share one
process-wide EmbeddingFunction. Applications install an alternative
by registering exactly one implementation in
META-INF/services/com.tnsai.memory.advanced.EmbeddingFunction.
Without one, the deterministic hash implementation remains the default;
multiple providers fail binding initialization rather than selecting by
classpath order. A vector-dependent strategy
(SEMANTIC,
HYBRID,
HIERARCHICAL,
TEMPORAL) on that default logs
one warning per Role: the hash embedding ranks by token overlap, not
meaning. KEYWORD,
GRAPH,
MULTI_QUERY and
REASONING do not, because their
scoring path does not use the vector store (HYBRID does, even though
BM25 still contributes). Applications that intend the hash embedding
set the tnsai.rag.acknowledgeHashEmbedding system property to
true rather than lowering the log level. Provider calls are
admitted one at a time without a wait queue, so implementations must
return promptly; returned vectors must be finite, non-empty,
dimensionally stable, and no wider than 16,384 values.
- Since:
- 0.5.4 (issue #172, Phase 1)
-
Method Summary
Modifier and TypeMethodDescriptionintNumber of documents successfully ingested for this Role.intdocumentCount(String sourceName) Number of documents the named source contributes to this binding, in the unitdocumentCount()counts.static RoleRagBindingReturns the (lazily computed) binding for the given Role class.static RoleRagBindingReturns a binding isolated by Role class and builder source template.static RoleRagBindingReturns a binding for a Role whose@Knowledgedeclarations are kept, with the locations of the named ones supplied at run time.static voidinvalidate(Class<?> roleClass) Drops every cached binding for the given Role class, so the nextforRole(java.lang.Class<?>)call re-reads the Role's@Knowledgedeclarations from scratch.static voidinvalidate(Class<?> roleClass, String sourceName) Drops cached state whose canonical source set containssourceNamewhile preserving every sibling binding and sequential cursor that does not depend on that source.static List<com.tnsai.rag.KnowledgeSourceConfig> locateDeclaredSources(Class<?> roleClass, Map<String, Path> resolvedPaths) Copies the Role's@Knowledgedeclarations, replacing the path of each named entry.Class<?> The Role class this binding was built for.strategyFor(com.tnsai.annotations.Search.Strategy requested) Builds theRAGStrategymatching the requested@Search.strategy.strategyFor(com.tnsai.annotations.Search.Strategy requested, String navigatorModel) Builds the requested strategy, supplying the tree-navigation modelSearch.Strategy.REASONINGneeds.
-
Method Details
-
forRole
Returns the (lazily computed) binding for the given Role class. Subsequent calls for the same class return the cached instance.If the Role declares no knowledge the returned binding is still valid — it just contains an empty vector store and BM25 index. Retrieval against it produces an empty document list, and the action proceeds without context.
- Parameters:
roleClass- the Role's runtime class- Returns:
- the per-Role binding (never null)
-
forRole
Returns a binding for a Role whose@Knowledgedeclarations are kept, with the locations of the named ones supplied at run time.@Knowledge.path()is a compile-time constant. A corpus that lives somewhere only known once the run starts — a timestamped transcript directory, an episode folder — cannot be named by it. This overload closes that gap without a template language in the annotation: the Role declares everything it can (name, format, selectors, unit), leavespathempty or as a placeholder, and the caller supplies the resolved location here.Each entry names a source declared on
roleClass. The declaration is copied with only its path replaced — seeKnowledgeSourceConfig.withPath(String)— and then takes part in the binding exactly as a builder source does, so the located path becomes part of the binding key. A relative FILE location resolves byKnowledgePaths.resolve(java.lang.String), against the run's base directory when one is set, so the key carries the path resolved when the binding is asked for. Two runs with different folders get two bindings; nothing is shared or stale between them. A FILE location that resolves to the declared path gives the declaration itself, which is not a builder source, so it shares the bindingforRole(Class)gives, as a dispatch given that location does.Naming a source the Role does not declare is an error, not a silent new source: the override exists to locate a declaration, and an unrecognised name almost always means a typo in one of the two places.
- Parameters:
roleClass- the Role's runtime classresolvedPaths- source name to its location for this run- Returns:
- the per-Role binding (never null)
- Throws:
IllegalArgumentException- if a key names no@KnowledgeonroleClass- Since:
- 0.16.0
-
locateDeclaredSources
public static List<com.tnsai.rag.KnowledgeSourceConfig> locateDeclaredSources(Class<?> roleClass, Map<String, Path> resolvedPaths) Copies the Role's@Knowledgedeclarations, replacing the path of each named entry. Same conversionforRole(Class, Map)uses; the SCOP dispatch path feeds the result in as builder sources so its knowledge sees the run-time folder without the action body calling this factory.- Parameters:
roleClass- the Role's runtime classresolvedPaths- source name to its location for this run- Returns:
- located copies, in map-iteration order
- Throws:
IllegalArgumentException- if a key names no@KnowledgeonroleClass- Since:
- 0.16.1
-
forRole
public static RoleRagBinding forRole(Class<?> roleClass, List<com.tnsai.rag.KnowledgeSourceConfig> builderSources) Returns a binding isolated by Role class and builder source template. -
invalidate
Drops every cached binding for the given Role class, so the nextforRole(java.lang.Class<?>)call re-reads the Role's@Knowledgedeclarations from scratch.Call this after the corpus behind a Role's knowledge sources changes — files rewritten, a document added or withdrawn. A binding is built once per Role class per JVM and memoised for the process lifetime, so without this call a running deployment keeps answering from the corpus as it stood at first dispatch, however long ago that was.
@Search.cacheTTLdoes not cover it: the result cache expires against the same immutable index and re-searching it returns the same documents.Invalidation is per Role class and covers every binding that class owns, including the ones keyed by distinct builder-supplied source sets. It drops the loaded documents, the per-source indexes, the resolved graph strategies, and the retrieval-result cache together — they describe one corpus snapshot and are only coherent as a set.
The rebuild reloads and re-chunks every source and rebuilds every index, but it does not embed a chunk again whose content and embedding provider are unchanged: the vectors of the bindings retired here are kept for one generation, keyed by provider compatibility and chunk content, and reused by the next build for this Role. A vector is a function of the provider and the text, not of the snapshot, so reuse leaves the coherence guarantee above intact.
Retrieval that is already in flight is unaffected and completes against the snapshot it started on; the next dispatch rebuilds. The rebuild happens on that dispatch, not here, so the cost of re-ingesting lands on the caller that needs the fresh corpus. Explicit
KnowledgeScopesnapshots and cursors are unaffected; their owner closes them independently.- Parameters:
roleClass- Role whose knowledge binding should be discarded- Since:
- 0.13.0
-
invalidate
Drops cached state whose canonical source set containssourceNamewhile preserving every sibling binding and sequential cursor that does not depend on that source.The source identifier is exact and case-sensitive. It must name an enabled annotation or builder source already known for this Role; null, blank, disabled, and unknown identifiers fail loudly. A ranked binding containing the source is retired as one coherent snapshot, including its result cache, scoped indexes, and graph corpus. Retrieval already in flight completes on its prior snapshot. A concurrent build containing the source is fenced and rebuilt before it can enter the cache.
Sequential readers are narrower: only readers for the named source reset to the first unit. Positions for every sibling source remain unchanged.
invalidate(Class)retains its role-wide behavior. ExplicitKnowledgeScopesnapshots and cursors are unaffected. Deterministic Role-owned remote indexes are not physically cleared by this narrower overload because another process or builder template may share that owner; the rebuilt snapshot filters remote entries that are no longer part of its canonical corpus before ranking. The role-wide overload retains the same non-destructive remote-owner behavior.- Parameters:
roleClass- Role whose source-backed state should be discardedsourceName- exact canonical knowledge-source name- Throws:
IllegalArgumentException- if the source name is null, blank, disabled, or unknown for the Role- Since:
- 0.16.3
-
strategyFor
Builds theRAGStrategymatching the requested@Search.strategy. SEMANTIC, KEYWORD, and HYBRID are base strategies. MULTI_QUERY requires annotation-bound expansion configuration and is composed byUnifiedRetrievalRuntime; GRAPH resolves itsGraphStoreProviderhere, so a missing or ambiguous provider fails the action before the@Search.onFailureguard can observe it. HIERARCHICAL selects child hits semantically and expands their hierarchy parents; its hierarchy index is validated here, for the same reason. TEMPORAL re-ranks semantic child hits through bounded time decay; its temporal index is validated here, for the same reason. REASONING descends the same hierarchy with aTreeNavigator; a blank model or a missing provider fails here, for the same reason. -
strategyFor
public RAGStrategy strategyFor(com.tnsai.annotations.Search.Strategy requested, String navigatorModel) Builds the requested strategy, supplying the tree-navigation modelSearch.Strategy.REASONINGneeds.Other strategies ignore
navigatorModel. REASONING rejects a blank model and an identifier no installed provider serves.- Parameters:
requested- strategy discriminantnavigatorModel- exactTreeNavigatormodel identifier- Returns:
- the constructed strategy
-
documentCount
public int documentCount()Number of documents successfully ingested for this Role. Useful for tests asserting a binding actually loaded its sources, and for the_rag_document_countcontext value when retrieval runs against an empty store. -
documentCount
Number of documents the named source contributes to this binding, in the unitdocumentCount()counts. A source that loaded nothing counts 0, and so does an enabled source of a type no loader is registered for: this binding holds it, but it contributes nothing. A source that did not load is not counted as 0: its recorded failure is thrown, asrequireLoadedreports it. Counting builds no index and loads nothing again.- Parameters:
sourceName- a source this binding holds- Returns:
- the number of documents, 0 or more
- Throws:
com.tnsai.rag.RetrievalException- the source's recorded load failure, with the source and the role in its messageIllegalArgumentException- when this binding holds no source of that name, such as an undeclared or disabled one- Since:
- 0.18.0
-
roleClass
The Role class this binding was built for.
-