Architecture

Module-by-module map of the Pascal units that make up the project, plus the data flow across them.

Source layout

src/
  mormot.db.pgvector.pas             Codec: TPgVector value type,
                                     text + binary wire format,
                                     MRL truncation helper.
  mormot.db.pgvector.binding.pas     Binding: bind/scan against the
                                     mORMot 2 PostgreSQL driver, DDL
                                     emitters, distance op builders,
                                     schema setup helper.
  mormot.db.pgvector.orm.pas         ORM helper: TOrmWithEmbedding +
                                     dim registry + CREATE TABLE
                                     emitter that substitutes
                                     VECTOR(N) for the BYTEA default.
  mormot.ai.embed.pas                Embedding-client interface
                                     (IEmbeddingClient), HTTP
                                     transport interface
                                     (IHttpTransport), live
                                     TMormotHttpTransport with
                                     persistent socket, retry
                                     decorator, concurrency-limit
                                     decorator.
  mormot.ai.embed.ollama.pas         Ollama-native IEmbeddingClient
                                     impl (POST /api/embed,
                                     EmbeddingGemma defaults, MRL
                                     fallback).
  mormot.ai.llm.pas                  ILlmClient interface +
                                     TStubLlmClient (no-network
                                     placeholder).
  mormot.ai.llm.ollama.pas           Ollama-native ILlmClient impl
                                     (POST /api/generate).
  mormot.ai.chunk.pas                Pascal source walker +
                                     decomposer (one chunk per
                                     top-level declaration),
                                     metadata schema, family
                                     derivation, UTF-8 + BOM
                                     handling, large-decl splitter.
  mormot.ai.cli.pas                  Shared CLI harness:
                                     TCliSpec / TCliResult records,
                                     CliParse, CliFormatUsage, exit
                                     code constants.
  mormot.ai.rag.pas                  Orchestrator: TRagOrchestrator
                                     with sequential Ingest,
                                     IngestParallel, Retrieve.

examples/
  01_ingest_mormot2.dpr              Production ingest CLI.
  02_query_cli.dpr                   Production retrieve CLI.
  03_chat_cli.dpr                    Production chat CLI.
  demo_real.dpr                      Quick all-in-one demo runner.

test/
  test.pgvector.pas                  Codec tests.
  test.pgvector.orm.pas              ORM helper tests.
  test.binding.pas                   DDL + operator builder tests.
  test.binding.live.pas              Live PostgreSQL bind/scan tests.
  test.embed.pas                     IEmbeddingClient interface tests.
  test.embed.ollama.pas              Ollama embed-client + retry +
                                     concurrency limit tests.
  test.llm.pas                       Stub LLM tests.
  test.llm.ollama.pas                Ollama generative tests.
  test.chunk.pas                     Walker + decomposer tests.
  test.cli.pas                       CLI harness tests.
  test.cli.binaries.pas              Spawn-the-binary regression
                                     tests for the three CLIs.
  test.rag.live.pas                  Live RAG ingest + retrieve.
  test.rag.smoke.pas                 Canonical mORMot 2 corpus
                                     smoke probes.
  test_runner.dpr                    TSynTests entry point.

Domain dependency graph

                          codec
                         (Domain A)
                            │
                ┌───────────┴───────────┐
                ▼                       ▼
            binding                  embedder
           (Domain B)               (Domain C)
                │                       │
                ▼                       ▼
                │                   ┌───┴────┐
                │                   ▼        ▼
                │                Ollama   Stub /
                │                impl     other
                │                            (interface only)
                │                       │
                └───────────┬───────────┘
                            ▼
                       orchestrator
                       (Domain E) ◀──── chunker (Domain D)
                            │
                            ▼
                         CLIs ◀──── CLI harness
                       (Domain F)   (Domain F shared)

Ingest flow

input: source root path, IEmbeddingClient (or factory + worker count)

  ChunkerEnumerate(root)                         walks .pas/.pp/.dpr/.inc
        │
        ▼
  ChunkerDecompose(file)                         one TPascalChunk per
        │                                        top-level declaration
        ▼
  IEmbeddingClient.EmbedBatch(chunk_contents)    Ollama POST /api/embed
        │                                        in batches of 16
        ▼
  TPgVector[]                                    binary wire format
        │
        ▼
  PgVectorBindBinary(stmt, paramIdx, vec)        paramFormat=1 binary
        │
        ▼
  INSERT INTO mormot_chunks                      one row per chunk,
   (... , embedding) VALUES (?, ?, ?::vector)    HNSW + GIN indexes
        │
        ▼
  TRagIngestStats                                files / chunks / rows /
                                                 elapsed_ms

Sequential Ingest runs the loop in the calling thread. IngestParallel(root, factory, workers) distributes files across worker threads via TSynParallelProcess. Each worker calls the factory once at slice start to obtain its own IEmbeddingClient (the default TMormotHttpTransport holds a single persistent socket and is not thread-safe). Database connections come from the thread-safe properties pool automatically.

Retrieval flow

input: query string, top_k, family filter, alpha

  IEmbeddingClient.Embed(query)                  Ollama POST /api/embed
        │
        ▼
  TPgVector                                      same wire format
        │
        ▼
  PgVectorBindBinary(stmt, 1, vec)               binary param
        │
        ▼
  SELECT id, ..., content,
         1.0 - (embedding <=> ?::vector) AS vsim,
         ts_rank_cd(ts, plainto_tsquery('simple', ?)) AS trank,
         α·vsim + (1−α)·trank AS combined
  FROM mormot_chunks
  [WHERE family = ?]
  ORDER BY combined DESC
  LIMIT ?
        │
        ▼
  TRagRetrievalResultDynArray                    sorted, top-k, full
                                                 metadata + 3 score
                                                 components per row

The hybrid blend uses α · cosine_similarity + (1 − α) · ts_rank. By convention α = 1.0 is pure vector and α = 0.0 is pure text. The default is 0.7.

Chat flow

The chat CLI composes the retrieve + LLM steps:

  query
    │
    ▼ Retrieve(query, top_k, family, α)
  top-K chunks
    │
    ▼ BuildRagPrompt
  prompt = [system instructions]
            [chunks as context]
            Question: <query>
            Answer:
    │
    ▼ ILlmClient.Complete(prompt)
  LLM response
    │
    ▼ stdout

Provenance (the file:line ranges of the retrieved chunks) is printed to ErrOutput so the answer on stdout can be piped while the user still sees where the model's facts came from.

Storage schema

The canonical mormot_chunks table layout:

CREATE TABLE mormot_chunks (
  id          BIGSERIAL PRIMARY KEY,
  unit_name   TEXT NOT NULL,
  family      TEXT NOT NULL,             -- core | orm | rest | ...
  symbol      TEXT,
  kind        TEXT,                       -- type | proc | func |
                                          -- comment | unit-header
  file_path   TEXT NOT NULL,
  line_start  INT,
  line_end    INT,
  content     TEXT NOT NULL,
  tokens      INT,
  embedding   VECTOR(768),
  ts          TSVECTOR GENERATED ALWAYS AS
              (to_tsvector('simple', content)) STORED,
  created_at  TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX ON mormot_chunks USING hnsw (embedding vector_cosine_ops)
  WITH (m=16, ef_construction=64);
CREATE INDEX ON mormot_chunks USING gin (ts);
CREATE INDEX ON mormot_chunks (family);
CREATE INDEX ON mormot_chunks (unit_name);

The embedding column dimension is parametrized; PgVectorSchemaConfig defaults to 768 (full EmbeddingGemma) but accepts any of {128, 256, 512, 768} for MRL-truncated indexing.

Production transport stack

For deployments that run hundreds of embed calls in a row, wrap the live transport in two decorators:

inner := TMormotHttpTransport.Create({receive_timeout_ms=}120000);
retrying := TRetryingHttpTransport.Create(inner,
              {max_attempts=}3, {initial_delay_ms=}250);
limited := TConcurrencyLimitedHttpTransport.Create(retrying,
              {limit=}4);
client := TOllamaEmbeddingClient.Create(limited, base_url, model, dim);

The result: a single keep-alive socket reused across requests, automatic exponential-backoff retries on HTTP 5xx / 429 / network errors, and a hard cap on in-flight concurrent calls so the server (typically a single-instance Ollama with one model loaded) is not asked to queue more requests than it can process.