Skip to content

Determinism Architecture

Overview

  • Determinism matters because the pipeline composes multi-step planning, execution, and aggregation; stable identifiers and ordering are required for reproducible DAGs, consistent merges, and auditability.
  • The system implements determinism in specific places (hashing, sorting, and schema fingerprinting) but also contains explicit nondeterminism (LLM outputs, vector retrieval ranking, timestamps, random retry jitter, and external calls).
  • Scope: planning (IDs + DAG), retrieval (vector ordering), execution ordering (DAG layers), artifacts (content hashing), and state merges.

Determinism Domains

Inputs and Identifier Stability

Planner and DAG Construction

  • Deterministic: Global planner sorts nodes and edges by IDs and roles before constructing the DAG, then hashes a sorted JSON payload to produce a stable dag_id for a given logical plan (pipeline/nodes/global_planner/node.py).
  • Deterministic: DAG layers are computed with a topological sort that sorts ready nodes and dependents, yielding stable layer ordering given the same node/edge sets (pipeline/nodes/global_planner/schemas.py).
  • Non-deterministic: The AST planner is LLM-driven; the PlanModel content is not stabilized inside the node (pipeline/nodes/ast_planner/node.py).

Retrieval Ordering and Chunk Selection

  • Non-deterministic: Vector retrieval uses max marginal relevance search; ranking and ties depend on vector store behavior and similarity scores, with no secondary deterministic tie-breaker in code (indexing/vector_store.py).
  • Non-deterministic: Datasource resolution uses vector retrieval results directly; candidate ordering follows the retrieval output and is not re-sorted (pipeline/nodes/datasource_resolver/node.py).
  • Non-deterministic: Schema retrieval builds the table/column set from vector retrieval metadata and uses dictionary/set iteration without sorting; final relevant_tables ordering follows input ordering (pipeline/nodes/schema_retriever/node.py).
  • Partially deterministic: Table chunk column names are sorted for the table chunk payload, but chunk emission order depends on the schema contract’s insertion order (no sorting over tables.items() or columns.items() for column chunks) (indexing/chunk_builder.py).

Schema Authority and Versioning

  • Deterministic: Schema fingerprints are computed from sorted tables, columns, and foreign keys with JSON serialization; same schema contract yields the same fingerprint (schema/protocol.py).
  • Deterministic: Both in-memory and SQLite schema stores deduplicate by fingerprint, returning an existing version if one matches (schema/in_memory_store.py, schema/sqlite_store.py).
  • Non-deterministic: Schema versions are time-based (YYYYMMDDhhmmss_<fingerprint>). Latest version selection depends on registration time and order (schema/in_memory_store.py, schema/sqlite_store.py).
  • Deterministic (conditional): If schema_version is provided by a sub-query, schema retrieval resolves that exact snapshot; otherwise it uses the latest available version, which is time-ordered (pipeline/nodes/schema_retriever/node.py).

Validation Gates

  • Deterministic: Logical validation normalizes names, enforces ordinal continuity, validates aliases, and uses sorted comparisons for expected schema/alias matching (pipeline/nodes/validator/node.py).
  • Deterministic: Policy enforcement uses explicit namespaced checks and deterministic set membership (pipeline/nodes/validator/node.py).

DAG Execution Order and Node Sequencing

  • Deterministic: Aggregation executes layer-by-layer in DAG order, with ordered inputs computed by role rank and node ID, and terminal nodes sorted by ID (aggregation/aggregator.py).
  • Deterministic: Router selects the next scan layer based on the DAG layers and existing artifact refs (pipeline/graph_utils.py, pipeline/routes.py).

Subgraph Composition and Routing

  • Deterministic: Subgraph selection has a single outcome -- sql_agent when the datasource declares supports_sql, otherwise no subgraph (pipeline/graph_utils.py).
  • Deterministic (conditional): Scan payloads and subgraph outputs are built from explicit state fields; content is deterministic given the input state (pipeline/graph_utils.py).

Retry Mechanisms and Backoff

  • Non-deterministic: Retry backoff includes random jitter; sleep time is a function of random.uniform and wall-clock timing (pipeline/subgraphs/sql_agent.py).
  • Deterministic (conditional): Retry routing decisions depend on current retry count and error retryability flags; deterministic if state is unchanged (pipeline/subgraphs/sql_agent.py).

Artifact Storage and Hashing

  • Deterministic: Artifact content hashes are computed from a sorted JSON payload (columns, row_count, path), giving stable hashes for the same payload (execution/artifacts/store.py).
  • Non-deterministic: Artifact refs include created_at=datetime.utcnow(); this is time-based and changes for each creation (execution/artifacts/store.py).
  • Deterministic (conditional): Upload paths are deterministic given tenant_id and request_id, but the request ID origin is external to this module (execution/artifacts/store.py).

State Mutation and Merge Semantics

  • Deterministic: Decomposer returns sorted sub-queries, combine groups, and post-combine ops by ID to stabilize downstream ordering (pipeline/nodes/decomposer/node.py).
  • Potentially non-deterministic: GraphState merges dict fields with a last-write-wins reducer and concatenates lists; in parallel branches, merge order is not constrained in this code (pipeline/state.py).

Hashing/Fingerprinting

Time-Based Logic and Runtime Controls

External Calls