Class KnowledgePreflight

java.lang.Object
com.tnsai.intelligence.rag.binding.KnowledgePreflight

public final class KnowledgePreflight extends Object
Setup-time checks that a role's knowledge loads, before the first action runs. Each answers one question: would the first call that reads these sources find them loaded? None retrieves anything.

The caller states the mode the same way it states it when it reads:

  • requireLoaded(Object, Collection, Map) checks SEARCH sources in the binding a dispatch of the role's declared knowledge binds, the same cache entry, so the load the preflight does is the load the first dispatch uses. Its key is the role's declared reach, moved to the given run-time locations exactly as the dispatch moves it, with no agent. A dispatch binds a different entry when the agent adds knowledge, or when the agent's retrieval configuration replaces the search of an action that names only SEARCH sources; the preflight does not check that entry.
  • requireSequential(Object, SequentialConfig, List) checks the generation SequentialUnitReader.forTarget(Object, SequentialConfig, List) reads with the same arguments, built on first use exactly as opening the reader builds it, through the same key and lock. No cursor is opened, so no reader's position moves.

The checks load; they do not index. A later dispatch or reader finds the generation the preflight loaded and indexes it, so embedding and the vector backend are first reached by that dispatch, not by the preflight. After RoleRagBinding.invalidate(Class), after RoleRagBinding.invalidate(Class, String) for a checked source, or after ClassConfig.useBaseDirectory(java.nio.file.Path) starts a new run, the next call loads again, and so does a dispatch that was not checked again.

A SEARCH source that did not load is handled as its dispatch handles it: the recorded failure is reported, and loaded again only when the dispatch would load it again, on the process-wide cache once the backoff has passed. A SEQUENTIAL source that did not load is not recorded, so the next check or reader loads it again.

The checks run against KnowledgeScope.current(), as forTarget does. Call them under a lease of the scope the dispatcher holds, or they check the process-wide cache instead of that scope's. On the process-wide cache a concurrent first load fails with PROVIDER_UNAVAILABLE, as a dispatch does. Under a scope a failure stays for the scope.

A target dispatched through com.tnsai.integration.scop.SCOPBridge is checked with SCOPBridge.requireLoaded, not with these methods. The bridge's dispatch falls back to its bridge-level locations when a call gives none, and binds the role to its owner's configuration, which changes the knowledge the role declares; the bridge's preflight does both through the code its dispatch runs, where a caller here would have to repeat them.

Since:
0.18.0
  • Method Details

    • requireLoaded

      public static Map<String,Integer> requireLoaded(Object target, Collection<String> names)
      requireLoaded(Object, Collection, Map) with no run-time locations: every source is checked at its declared path.

      For a target dispatched through com.tnsai.integration.scop.SCOPBridge, call SCOPBridge.requireLoaded instead. That dispatch takes the bridge-level locations when a call gives none, so a check here would miss a location the dispatch applies.

      Parameters:
      target - the role instance, whose class declares the knowledge
      names - declared, enabled sources that the role and its actions read with use SEARCH
      Returns:
      each name's document count, in sorted name order; unmodifiable
      Throws:
      IllegalArgumentException - as requireLoaded(Object, Collection, Map) throws it
      com.tnsai.rag.RetrievalException - when a named source did not load
      Since:
      0.18.0
    • requireLoaded

      public static Map<String,Integer> requireLoaded(Object target, Collection<String> names, Map<String,Path> resolvedPaths)
      Loads the named SEARCH sources of target's role as its first dispatch with these run-time locations would, and returns the number of documents each holds.

      The locations move the declared sources exactly as the dispatch moves them, through the one location rule both use, so the binding checked is the one that dispatch leases. Give the map the dispatch receives under RetrievalSpi.RESOLVED_SOURCE_PATHS_KEY; an empty map is the declared paths, as a dispatch with none.

      For a target dispatched through com.tnsai.integration.scop.SCOPBridge, call SCOPBridge.requireLoaded instead. When a call gives no map, that dispatch takes the bridge-level one, and it binds the role to its owner's configuration first; the bridge's preflight does both through the same code, where a map passed here could differ from the one the dispatch uses.

      Names are deduplicated and checked in sorted order; the first failure ends the call, so the names after it are not checked. Every name is validated, and every FILE source's path checked, before any source is touched. A count of 0 is not a failure: whether an empty source may serve is the caller's rule. A source whose type has no registered loader counts 0, as its dispatch finds it empty.

      A source that did not load throws its recorded failure, SOURCE_LOADING, or CONFIGURATION with the invalid input as its cause, and its message ends with " (source '<name>' on <Role>)". An enabled FILE source with no path, declared or located, throws CONFIGURATION, as its dispatch does. A failure of the binding as a whole names no source: CONFIGURATION for sources it cannot bind, PROVIDER_UNAVAILABLE while another call loads it first, and ENGINE_CLOSED when it was released each time the preflight leased it.

      Parameters:
      target - the role instance, whose class declares the knowledge
      names - declared, enabled sources that the role and its actions read with use SEARCH
      resolvedPaths - source name to its run-time location, as the dispatch receives it; empty for the declared paths
      Returns:
      each name's document count, in sorted name order; unmodifiable
      Throws:
      IllegalArgumentException - when resolvedPaths names a source the role does not declare, with the dispatch's message; when names is empty, or names a source the role does not declare, a disabled one, a GRAPH handle, or one the role or one of its actions reads DIRECT or SEQUENTIAL; check a SEQUENTIAL read with requireSequential(Object, SequentialConfig, List)
      com.tnsai.rag.RetrievalException - when a named source did not load
      Since:
      0.18.0
    • requireSequential

      public static int requireSequential(Object target, String source)
      Parameters:
      target - the role instance whose reader is checked
      source - the source name, as given to forTarget
      Returns:
      the number of units the reader reads
      Since:
      0.18.0
    • requireSequential

      public static int requireSequential(Object target, com.tnsai.rag.SequentialConfig config, List<com.tnsai.rag.KnowledgeSourceConfig> builderSources)
      Loads the generation the reader SequentialUnitReader.forTarget(Object, SequentialConfig, List) opens with the same arguments, and returns the number of units it reads. A later forTarget with the same arguments reads this generation from its start, unless a reader for this target is already open: that reader keeps its position.

      Whatever opening the reader throws, this throws, unchanged: the same RetrievalException for a source that does not load, the same IllegalArgumentException for a source the reader cannot read. A count of 0 is not a failure.

      Parameters:
      target - the role instance whose reader is checked
      config - the reader's configuration, as given to forTarget
      builderSources - the builder sources, as given to forTarget
      Returns:
      the number of units the reader reads
      Throws:
      com.tnsai.rag.RetrievalException - when the source does not load
      Since:
      0.18.0