Skip to content

âš¡ Spector Runtime

Composition root — the single entry point for all Spector consumers.

SpectorRuntime creates, wires, and exposes all subsystem services. It is a thin composition root with no business logic — each handler owns its domain.

Architecture

graph TD
    RT["SpectorRuntime<br/><i>Composition Root</i>"]
    SH["SearchHandler<br/><i>mode-aware routing</i>"]
    IH["IngestionHandler<br/><i>pipeline delegation</i>"]
    ENG["spector-engine<br/><i>vector search, RAG</i>"]
    MEM["spector-memory<br/><i>cognitive memory</i>"]
    ING["spector-ingestion<br/><i>IngestionPipeline</i>"]
    CFG["spector-config"]
    EMB["spector-embed-api"]

    RT --> SH
    RT --> IH
    SH --> ENG
    SH --> MEM
    IH --> ING
    ING --> ENG
    ING --> MEM
    RT --> CFG
    RT --> EMB

SpectorRuntime.ingestion() builds the IngestionPipeline with the correct IngestionTarget (engine or cognitive) and reads chunking configuration from spector.yml.

Service Accessors

Accessor Returns Description
runtime.search() SearchHandler Mode-aware search (engine or memory)
runtime.ingestion() IngestionHandler Mode-aware ingestion (text, file, directory)
runtime.engine() SpectorEngine Direct engine access (always available)
runtime.memory() SpectorMemory Direct memory access (null if disabled)
runtime.mode() SpectorMode Current mode (SEARCH or MEMORY)

Usage

Search & Ingest via Handlers

try (var runtime = SpectorRuntime.from(props, embedder, true)) {
    // Search — mode-aware (routes to engine or memory)
    var results = runtime.search().query("query text", 10);

    // Ingest text — pipeline auto-chunks if content > threshold
    runtime.ingestion().ingest("doc-1", "content text");

    // Ingest directory — discovers files, chunks, embeds, and stores
    runtime.ingestion().ingest(Path.of("/docs"), "**/*.md", 800, 100, ".git");

    // Chunked ingestion — returns IngestionResult with chunk count
    var result = runtime.ingestion().ingestChunked("doc-2", longContent);
}

Factory Methods

// Standard — creates engine + optional memory from config
SpectorRuntime.from(props, embedder);

// With writable index (for ingestion)
SpectorRuntime.from(props, embedder, true);

// Engine-only (no memory)
SpectorRuntime.engineOnly(engine, props);

Configuration

Runtime behavior is driven by spector.yml:

spector:
  mode: search              # search or memory
  engine:
    dimensions: 768
    persistence-mode: DISK
    data-directory: .spector/index
  embedding:
    model: nomic-embed-text
    base-url: http://localhost:11434
  memory:
    enabled: true
    persistence-mode: DISK
    persistence-path: .spector-memory
  ingestion:
    root-directory: /path/to/docs
    file-pattern: "**/*.md"
    chunk-size: 800
    chunk-overlap: 100

Consumers

Module How it uses SpectorRuntime
spector-cli SpectorCtl — CLI commands call runtime.search() / runtime.ingestion()
spector-mcp SpectorMcpServer(runtime) — MCP tools call runtime handlers
spector-node SpectorNode(runtime) — Armeria REST + gRPC + SSE endpoints
spector-dist Fat JAR bundles runtime + all modules

Dependencies

<dependency>
    <groupId>com.spectrayan</groupId>
    <artifactId>spector-runtime</artifactId>
    <version>0.1.0-SNAPSHOT</version>
</dependency>