Skip to main content

API Reference

agrag

Agentic GraphRAG: graph-based RAG with agentic reasoning.

Modules:

  • agents – Agentic layer: planner/researcher/verifier over SearchEngine.
  • chunking – Chunking helpers for the ingestion layer.
  • cypher – Cypher query builders for graph stores.
  • embedding – Text embedding: turn strings into dense vectors.
  • graphdb – Graph storage backends and the build shortcut.
  • ingestion – The ingestion package.
  • observability – OpenTelemetry wiring for the ingestion layer.
  • retrieval – Retrieval package: search engine, fusion, reranking, and retrievers.
  • vectordb – Vector storage backends and the build shortcut.

agrag.agents

Agentic layer: planner/researcher/verifier over SearchEngine.

Modules:

  • build – Build the planner/researcher/verifier agent graph.
  • ledger – Citation ledger: assigns and tracks stable keys for one agent run.
  • middleware – Agent middleware for composing multiple chat models per strategy.
  • model – Translate LLMClientConfig into the matching LangChain chat model.
  • prompts – Agent prompt templates for planner, researcher, verifier.
  • settings – Env-backed LLM and loop config for the agent layer.
  • subagents – Subagent definitions: planner, researcher, verifier.
  • tools – Agent tools: thin wrappers calling SearchEngine with fixed Recipes.

agrag.agents.build

Build the planner/researcher/verifier agent graph.

Functions:

  • build_agent – Build the planner/researcher/verifier agent graph.
agrag.agents.build.build_agent
build_agent(*, engine:SearchEngine, llm_settings:AgentLLMSettings, agent_settings:AgentSettings | None = None, filters:SearchFilters | None = None) -> Any

Build the planner/researcher/verifier agent graph.

Constructs a LangGraph-based agent with three roles: planner (decomposes the question), researcher (has tools), and verifier (judges evidence sufficiency).

Each call to ainvoke creates a fresh Ledger so citation numbering, identity mappings, and retrieved evidence do not leak across runs.

Parameters:

  • engine (SearchEngine) – Retrieval to expose to the researcher subagent's tools.
  • llm_settings (AgentLLMSettings) – The model every subagent role calls, via build_chat_model. With several clients, the remaining ones compose per strategy through agent middleware.
  • agent_settings (AgentSettings | None) – Loop-level configuration; defaults from environment. The recursion limit is enforced as the LangGraph recursion_limit in the invoke config.
  • filters (SearchFilters | None) – Retrieval scope applied to every tool search, e.g. document or tenant constraints. None searches unfiltered.

Returns:

  • Any – A compiled agent graph ready for invoke/ainvoke, or a
  • Any – simple single-search fallback when deepagents is not
  • Any – installed.

agrag.agents.ledger

Citation ledger: assigns and tracks stable keys for one agent run.

Classes:

  • Ledger – Assigns and tracks stable citation keys for one agent run.
agrag.agents.ledger.Ledger
Ledger() -> None

Assigns and tracks stable citation keys for one agent run.

A key (E1, R1, C1 for entities, relations, and chunks) is assigned the first time this run encounters that item, by SearchResult.identity_key, and never reassigned within the run. The agent is shown rendered evidence carrying these keys, never raw SearchResults.

Functions:

  • cite – Return this result's citation key, assigning one if new.
  • render – Return the markdown-with-key text the agent sees.
  • resolve – Return the SearchResult behind a citation key.

Attributes:

  • keys (list[str]) – Return all citation keys assigned so far.
agrag.agents.ledger.Ledger.cite
cite(result:SearchResult) -> str

Return this result's citation key, assigning one if new.

Parameters:

  • result (SearchResult) – The SearchResult to assign a key to.

Returns:

  • str – The citation key (e.g. E1, C3).
agrag.agents.ledger.Ledger.keys
keys: list[str]

Return all citation keys assigned so far.

agrag.agents.ledger.Ledger.render
render(result:SearchResult) -> str

Return the markdown-with-key text the agent sees.

Parameters:

Returns:

  • str – Markdown text with the citation key and item summary.
agrag.agents.ledger.Ledger.resolve
resolve(key:str) -> SearchResult | None

Return the SearchResult behind a citation key.

Parameters:

  • key (str) – The citation key to look up.

Returns:

  • SearchResult | None – The SearchResult, or None if the key is unknown.

agrag.agents.middleware

Agent middleware for composing multiple chat models per strategy.

Classes:

agrag.agents.middleware.RoundRobinModelMiddleware
RoundRobinModelMiddleware(models:list[Any]) -> None

Bases: AgentMiddleware

Rotate across the configured chat models, one model per call.

Overrides the request's model on every model call so requests are distributed across all configured clients in order.

Functions:

Parameters:

  • models (list[Any]) – Chat models to rotate across, in configuration order. Must be non-empty.

Raises:

agrag.agents.middleware.RoundRobinModelMiddleware.awrap_model_call
awrap_model_call(request:ModelRequest, handler:Callable[[ModelRequest], Awaitable[ModelResponse]]) -> Any

Run the call against the next model in rotation.

agrag.agents.middleware.RoundRobinModelMiddleware.wrap_model_call
wrap_model_call(request:ModelRequest, handler:Callable[[ModelRequest], ModelResponse]) -> Any

Run the call against the next model in rotation.

agrag.agents.model

Translate LLMClientConfig into the matching LangChain chat model.

Classes:

Functions:

agrag.agents.model.UnsupportedAgentProviderError

Bases: Exception

Raised when a provider has no agent-side mapping yet.

agrag.agents.model.build_chat_model
build_chat_model(config:LLMClientConfig) -> Any

Translate one LLMClientConfig into a LangChain chat model.

Covers anthropic, openai, openai-generic (mapped to ChatOpenAI with base_url set), and google-ai. The remaining LLMProvider values are valid for BAML but have no agent-side mapping yet.

Parameters:

  • config (LLMClientConfig) – The provider, model, api_key, and base_url to use.

Returns:

  • Any – A constructed, ready-to-call BaseChatModel.

Raises:

agrag.agents.model.build_model_middleware
build_model_middleware(clients:list[LLMClientConfig], *, strategy:Literal['single', 'fallback', 'round_robin'] = 'single') -> list[Any]

Build agent middleware composing multiple clients per strategy.

The agent calls clients[0] as its primary model. With more than one client, the returned middleware teaches the agent loop to use the rest: "fallback" tries the other clients in order when the primary model call fails, and "round_robin" rotates across every client per model call.

Parameters:

  • clients (list[LLMClientConfig]) – The configured clients, in priority order.
  • strategy (Literal['single', 'fallback', 'round_robin']) – How to compose clients. "single" ignores all but the first client.

Returns:

  • list[Any] – Middleware for create_deep_agent/create_agent; empty when there
  • list[Any] – is nothing to compose.

Raises:

agrag.agents.prompts

Agent prompt templates for planner, researcher, verifier.

Attributes:

agrag.agents.prompts.PLANNER_SYSTEM
PLANNER_SYSTEM = 'You are a research planner. Given a user question, decompose it into 2-4 focused sub-questions that a researcher can answer by searching a knowledge graph. Each sub-question should be specific and answerable independently.\n\nReturn your sub-questions as a numbered list.'
agrag.agents.prompts.RESEARCHER_SYSTEM
RESEARCHER_SYSTEM = 'You are a researcher with access to a knowledge graph. Use the available tools to find evidence for each sub-question. Cite every claim with the citation keys (E1, C3, etc.) returned by tools. Base your answer only on evidence found through tools, not on general knowledge.\n\nWhen you have gathered enough evidence, provide a concise answer with citations.'
agrag.agents.prompts.VERIFIER_SYSTEM
VERIFIER_SYSTEM = "You are an evidence verifier. Given a question, a set of sub-questions, and the researcher's evidence, check:\n1. Every sub-question has at least one citation.\n2. Every citation key resolves to real evidence.\n3. The answer directly addresses the original question.\n\nIf evidence is sufficient, say PASS. If not, list what is missing."

agrag.agents.settings

Env-backed LLM and loop config for the agent layer.

Classes:

agrag.agents.settings.AgentLLMSettings

Bases: BaseSettings

LLM client config for the agent's own reasoning turns.

Mirrors ExtractionLLMSettings for the agent role: same shape, same from_openai_compatible_env() convention, because the agent's model and the extraction model are configured the same way even though the agent calls its model through LangChain, not BAML.

Attributes:

  • clients (list[LLMClientConfig]) – The LLM client(s) to use. One element for a single provider; more than one composed per strategy through agent middleware.
  • strategy (Literal['single', 'fallback', 'round_robin']) – How to compose multiple clients. "fallback" tries the other clients in order when a model call fails; "round_robin" rotates across all clients per call. Ignored with one client.

Env prefix: AGENT_LLM_.

Functions:

agrag.agents.settings.AgentLLMSettings.clients
clients: list[LLMClientConfig]
agrag.agents.settings.AgentLLMSettings.from_openai_compatible_env
from_openai_compatible_env() -> AgentLLMSettings

Build settings from OpenAI-compatible env vars.

Loads .env first, then reads AGENT_LLM_BASE_URL, AGENT_LLM_API_KEY, and AGENT_LLM_MODEL_ID. When the agent-specific variables are unset, the shared LLM_* convenience variables used by the extraction role stand in, so one .env can configure every LLM-backed role. The model name defaults to gpt-4o-mini when neither variable names one.

Returns:

agrag.agents.settings.AgentLLMSettings.model_config
model_config = SettingsConfigDict(env_prefix='AGENT_LLM_', env_file='.env', extra='ignore')
agrag.agents.settings.AgentLLMSettings.strategy
strategy: Literal['single', 'fallback', 'round_robin'] = 'single'
agrag.agents.settings.AgentSettings

Bases: BaseSettings

Configuration for the agent loop itself.

Attributes:

  • recursion_limit (int) – The maximum LangGraph step count before the loop stops and reports incomplete progress.

Env prefix: AGENT_.

agrag.agents.settings.AgentSettings.model_config
model_config = SettingsConfigDict(env_prefix='AGENT_', env_file='.env', extra='ignore')
agrag.agents.settings.AgentSettings.recursion_limit
recursion_limit: int = 50

agrag.agents.subagents

Subagent definitions: planner, researcher, verifier.

Functions:

agrag.agents.subagents.make_planner_prompt
make_planner_prompt() -> dict[str, Any]

Return the planner subagent config.

Returns:

  • dict[str, Any] – A dict with system_prompt key for the planner role.
agrag.agents.subagents.make_researcher_prompt
make_researcher_prompt() -> dict[str, Any]

Return the researcher subagent config.

Returns:

  • dict[str, Any] – A dict with system_prompt key for the researcher role.
agrag.agents.subagents.make_verifier_prompt
make_verifier_prompt() -> dict[str, Any]

Return the verifier subagent config.

Returns:

  • dict[str, Any] – A dict with system_prompt key for the verifier role.

agrag.agents.tools

Agent tools: thin wrappers calling SearchEngine with fixed Recipes.

Each tool is a LangChain-compatible callable that deepagents can register. Tools are named for what the agent is trying to find out, not for the retrieval method they use.

Functions:

  • make_tools – Build the agent's tool set over one SearchEngine and Ledger.
agrag.agents.tools.make_tools
make_tools(engine:'SearchEngine', ledger:'Ledger', *, filters:'SearchFilters | None' = None) -> list[Any]

Build the agent's tool set over one SearchEngine and Ledger.

Parameters:

  • engine ('SearchEngine') – The SearchEngine every tool calls.
  • ledger ('Ledger') – The citation ledger for one run.
  • filters ('SearchFilters | None') – Retrieval scope applied to every tool's search. Pass document or tenant constraints here so the agent cannot surface graph data outside them; the LLM never sees or chooses the scope.

Returns:

  • list[Any] – A list of LangChain tool instances: search_source_text,
  • list[Any] – look_up_entity, find_connection, explore_related, and
  • list[Any] – answer_from_graph_structure.

agrag.chunking

Chunking helpers for the ingestion layer.

This module isolates the chonkie dependency to one import site, so the rest of the codebase (and tests) can build a chunker without importing chonkie directly.

Modules:

  • text – Splits a text Document into Chunks with the chonkie chunker.

Classes:

  • RecursiveChunker – Chunker that recursively splits text into smaller chunks, based on the provided RecursiveRules.

Functions:

agrag.chunking.RecursiveChunker

RecursiveChunker(tokenizer:Union[str, TokenizerProtocol] = 'character', chunk_size:int = 2048, rules:RecursiveRules = RecursiveRules(), min_characters_per_chunk:int = 24) -> None

Bases: BaseChunker

Chunker that recursively splits text into smaller chunks, based on the provided RecursiveRules.

Parameters:

  • tokenizer (Union[str, TokenizerProtocol]) – Tokenizer to use
  • chunk_size (int) – Maximum size of each chunk.
  • rules (RecursiveRules) – Recursive rules to use for chunking.
  • min_characters_per_chunk (int) – Minimum number of characters per chunk.

Functions:

Attributes:

Parameters:

  • tokenizer (Union[str, TokenizerProtocol]) – Tokenizer to use
  • chunk_size (int) – Maximum size of each chunk.
  • rules (RecursiveRules) – Recursive rules to use for chunking.
  • min_characters_per_chunk (int) – Minimum number of characters per chunk.

Raises:

agrag.chunking.RecursiveChunker.achunk
achunk(text:str) -> list[Chunk]

Chunk the given text asynchronously.

Parameters:

  • text (str) – The text to chunk.

Returns:

  • list[Chunk] – list[Chunk]: A list of Chunks.
agrag.chunking.RecursiveChunker.achunk_batch
achunk_batch(texts:Sequence[str], show_progress:bool = True) -> list[list[Chunk]]

Chunk a batch of texts asynchronously.

Parameters:

  • texts (Sequence[str]) – The texts to chunk.
  • show_progress (bool) – Whether to show progress.

Returns:

  • list[list[Chunk]] – list[list[Chunk]]: A list of lists of Chunks.
agrag.chunking.RecursiveChunker.achunk_document
achunk_document(document:Document) -> Document

Chunk a document asynchronously.

Parameters:

  • document (Document) – The document to chunk.

Returns:

  • Document – The document with chunks populated.
agrag.chunking.RecursiveChunker.chunk
chunk(text:str) -> list[Chunk]

Recursively chunk text.

Parameters:

  • text (str) – Text to chunk.
agrag.chunking.RecursiveChunker.chunk_batch
chunk_batch(texts:Sequence[str], show_progress:bool = True) -> list[list[Chunk]]

Chunk a batch of texts.

Parameters:

  • texts (Sequence[str]) – The texts to chunk.
  • show_progress (bool) – Whether to show progress.

Returns:

  • list[list[Chunk]] – list[list[Chunk]]: A list of lists of Chunks.
agrag.chunking.RecursiveChunker.chunk_document
chunk_document(document:Document) -> Document

Chunk a document.

After chunking, non-empty document.metadata is shallow-merged into each chunk's :attr:~chonkie.types.Chunk.metadata (chunk keys override on conflict).

Parameters:

  • document (Document) – The document to chunk.

Returns:

  • Document – The document with chunks populated.
agrag.chunking.RecursiveChunker.chunk_size
chunk_size = chunk_size
agrag.chunking.RecursiveChunker.from_recipe
from_recipe(name:Optional[str] = 'default', lang:Optional[str] = 'en', path:str | PathLike | None = None, tokenizer:Union[str, TokenizerProtocol] = 'character', chunk_size:int = 2048, min_characters_per_chunk:int = 24) -> RecursiveChunker

Create a RecursiveChunker object from a recipe.

The recipes are registered in the Chonkie Recipe Store. If the recipe is not there, you can create your own recipe and share it with the community!

Parameters:

  • name (Optional[str]) – The name of the recipe.
  • lang (Optional[str]) – The language that the recursive chunker should support.
  • path (Optional[str]) – The path to the recipe.
  • tokenizer (Union[str, TokenizerProtocol]) – The tokenizer to use.
  • chunk_size (int) – The chunk size.
  • min_characters_per_chunk (int) – The minimum number of characters per chunk.

Returns:

Raises:

agrag.chunking.RecursiveChunker.min_characters_per_chunk
min_characters_per_chunk = min_characters_per_chunk
agrag.chunking.RecursiveChunker.rules
rules = rules
agrag.chunking.RecursiveChunker.sep
sep = '✄'
agrag.chunking.RecursiveChunker.tokenizer
tokenizer: AutoTokenizer

Get the tokenizer instance.

agrag.chunking.default_chunker

default_chunker(chunk_size:int = 1024) -> RecursiveChunker

Build the default text chunker.

Parameters:

  • chunk_size (int) – The maximum number of characters per chunk.

Returns:

agrag.chunking.text

Splits a text Document into Chunks with the chonkie chunker.

Functions:

agrag.chunking.text.chunk_document
chunk_document(document:Document, chunker:RecursiveChunker) -> list[Chunk]

Split a document's text into chunks.

This function computes line_start and line_end from each chunk's character span, because the chunker does not report line numbers. It also sets heading_path from the document's heading outline.

Parameters:

  • document (Document) – The document to split. This function reads only its text and heading_outline fields.
  • chunker (RecursiveChunker) – The chunker to run.

Returns:

  • list[Chunk] – The chunks, in document order.
agrag.chunking.text.iter_chunk_documents
iter_chunk_documents(documents:Iterator[Document], chunker:RecursiveChunker) -> Iterator[Chunk]

Chunk a stream of documents into a flat stream of chunks.

Parameters:

Yields:

  • Chunk – Each chunk, in document then chunk order.

agrag.cypher

Cypher query builders for graph stores.

Leaf modules only: nothing here imports agrag.graphdb, so the dependency points one way (store -> cypher).

Modules:

  • entities – Cypher builders for node writes and filters.
  • merge – Cypher for the tombstone/transfer/dedup merge path.
  • relations – Cypher builders for relationship writes and graph traversal.
  • safety – Safety gate for generated Cypher queries.
  • schema – Cypher builders for constraints and native vector indexes.

agrag.cypher.entities

Cypher builders for node writes and filters.

Leaf module: imports nothing from agrag.graphdb or other store packages, so the dependency points one way (store -> cypher).

Functions:

Attributes:

agrag.cypher.entities.MERGE_ALIAS_LABEL
MERGE_ALIAS_LABEL = '_AgragMergeAlias'
agrag.cypher.entities.NODE_IDENTITY_LABEL
NODE_IDENTITY_LABEL = '_AgragNode'
agrag.cypher.entities.clear_chunk_embedding_query
clear_chunk_embedding_query(vector_property:str) -> str

Build Cypher removing a vector property from Chunk nodes.

Guards on text the same way set_chunk_embedding_query does, so a concurrent update that changed a chunk's text between this call's embed and its clear does not accidentally wipe a newer vector.

Parameters:

  • vector_property (str) – The property to remove. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $records, a list of dicts with
  • str – the keys id and expected_text.
agrag.cypher.entities.clear_property_query
clear_property_query(property_name:str) -> str

Build Cypher removing one property from a batch of nodes, guarded by text.

Used to drop a stale value rather than leave it readable after a write that was supposed to replace it fails partway through, such as an embedding vector left over from before an entity's text changed. The same name/description guard as set_embedding_query applies: a record only clears the property if the node's text still matches what this call started with, so it cannot wipe a vector a newer, still-in- flight call has already written for different text.

Parameters:

  • property_name (str) – The property to remove. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $records, a list of dicts with the keys
  • str – id, expected_name, and expected_description.
agrag.cypher.entities.fetch_all_by_label_query
fetch_all_by_label_query(label:str) -> str

Build Cypher paginating every node with label, for consolidate().

Parameters:

  • label (str) – The node label. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $skip and $limit.
agrag.cypher.entities.fetch_by_merge_keys_query
fetch_by_merge_keys_query() -> str

Build Cypher for a batched exact-match lookup by merge key.

Resolves through the merge-key alias table (MERGE_ALIAS_LABEL) rather than matching each node's own merge_key property directly: that property is cleared when a node is tombstoned (see clear_tombstone_merge_keys_query), so a name it once held would otherwise become unreachable. The alias always points at the entity id that first held the key, which may itself now be a tombstone; the caller follows its merged_into chain to the live survivor.

merge_key is returned alongside n so the caller can map a row back to the mention(s) that queried it without re-deriving a key from the resolved entity's current name: an accepted alias (see upsert_merge_alias_query) can name an entity by something other than its current canonical name, so re-deriving would silently fail to map those mentions back.

Returns:

  • str – Parameterized Cypher expecting $merge_keys (list of strings).
agrag.cypher.entities.fetch_relations_between_query
fetch_relations_between_query(rel_type:str) -> str

Build Cypher for batched lookup of existing relations by endpoints.

Parameters:

  • rel_type (str) – The relationship type. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $pairs (list of
  • str{source_id, target_id}). Returns each match's id and
  • str – source_chunk_ids alongside the pair it matched.
agrag.cypher.entities.filter_clause
filter_clause(filters:dict[str, Any], node_var:str = 'node') -> tuple[str, dict[str, Any]]

Build a Cypher WHERE clause and parameters from a flat-dict filter.

Parameters:

  • filters (dict[str, Any]) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together.
  • node_var (str) – The Cypher variable bound to the node in the surrounding query.

Returns:

  • str – The WHERE clause text (beginning with WHERE when filters is
  • dict[str, Any] – non-empty, otherwise an empty string) and the parameter dict to pass
  • tuple[str, dict[str, Any]] – with it.
agrag.cypher.entities.hydrate_chunks_by_id_query
hydrate_chunks_by_id_query() -> str

Build Cypher fetching chunks by id.

Chunks are never tombstoned, so no merged_into guard is needed. The query filters on the Chunk label for type safety.

Returns:

  • str – Parameterized Cypher expecting $ids (list of string ids).
agrag.cypher.entities.hydrate_entities_by_id_query
hydrate_entities_by_id_query() -> str

Build Cypher fetching entities by id, excluding tombstones.

A tombstoned node is never deleted (ADR 0033) so a naive MATCH (n) WHERE n.id IN $ids would surface one. This query filters on merged_into IS NULL to return only live nodes.

Returns:

  • str – Parameterized Cypher expecting $ids (list of string ids).
agrag.cypher.entities.is_safe_identifier
is_safe_identifier(value:str) -> bool

Report whether a label or relationship type is a safe Cypher identifier.

A non-raising counterpart to validate_identifier, for filtering a batch of names (for example ones read back from the database) rather than validating one name a caller must supply correctly.

Parameters:

  • value (str) – The label or relationship type to check.

Returns:

  • boolTrue if value is a safe identifier.
agrag.cypher.entities.merge_key_index_query
merge_key_index_query(label:str) -> str

Build a CREATE INDEX query on the node merge_key property.

Backs the global exact-match lookup.

Parameters:

  • label (str) – The node label. Must already be validated.

Returns:

  • str – A Cypher query creating the range index if absent.
agrag.cypher.entities.resolve_merged_into_query
resolve_merged_into_query() -> str

Return a node and the id of the node it was merged into.

A tombstoned node is never deleted; it only gains a merged_into property pointing at its survivor (ADR 0033). The pointer is a property, not a relationship, so a chain is followed one hop per call: merged_into is null on a live node and holds the next id on a tombstone.

Returns:

  • str – A parameterized query expecting an $id parameter, returning the
  • str – node as node and its survivor id as merged_into.
agrag.cypher.entities.set_chunk_embedding_query
set_chunk_embedding_query(vector_property:str) -> str

Build Cypher setting a vector property on Chunk nodes.

Similar to set_embedding_query but guards on text instead of name/description, since chunks have no name field. The text guard prevents a stale write from overwriting a newer vector.

Parameters:

  • vector_property (str) – The property to set. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $records, a list of dicts with the
  • str – keys id, vector, and expected_text.
agrag.cypher.entities.set_embedding_query
set_embedding_query(vector_property:str) -> str

Build Cypher setting one vector property per node, guarded by its text.

Touches only vector_property, unlike a full node upsert: another write can update an entity's provenance or properties while its new embedding is being computed, and overwriting the whole node from a snapshot taken before that update would discard it along with delivering the vector. The name/description match is an optimistic-concurrency guard: a record only applies if the node's text still matches what its vector was computed from, so a slower write from an older call cannot overwrite a newer one's vector with a stale one.

Also requires merged_into IS NULL: a concurrent merge can tombstone the node -- clearing this same property -- after this call already read its text and started embedding, but before this write lands. Without this guard, the write would restore a vector on an absorbed entity purely because its name/description happened not to change, putting it back in native vector search. tombstone_query sets merged_into and removes the embedding in the same write, so this guard racing that one always sees them change together.

Parameters:

  • vector_property (str) – The property to set. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $records, a list of dicts with the keys
  • str – id, vector, expected_name, and expected_description.
agrag.cypher.entities.upsert_merge_alias_query
upsert_merge_alias_query() -> str

Build Cypher recording every accepted merge_key's owning entity id.

Every apply_merge call writes one of these for each merge_key the merge accepted -- the survivor's own current name, but also every other name (mention text or absorbed entity's name) resolution folded into it -- so a later mention of any of those names resolves back to this entity instead of creating a duplicate. ON CREATE SET only claims a merge_key that has no alias yet: if resolution in some other merge already accepted this same key for a different entity, that entity keeps it. Without this, a resolution decision in one add() call could silently steal a name an unrelated entity already owns.

Once created, an alias is never rewritten to point elsewhere: if the entity it names is later itself absorbed, fetch_by_merge_keys_query's caller follows that entity's merged_into chain from here instead of this table being kept in sync with every later merge.

The returned rows are what let a caller detect the case ON CREATE SET alone cannot: an accepted merge_key already owned by some other live entity, not one this same merge is writing or absorbing. Neither entity's own node merge_key collides in that case, so nothing at the database level rejects the write; the caller must compare each row's entity_id against its own survivor and tombstone ids itself.

Returns:

  • str – Parameterized Cypher expecting $merge_keys (list of strings) and
  • str – $entity_id. Returns each merge_key alongside the entity_id that now
  • str – owns it -- $entity_id when this call claimed or already owned it,
  • str – another entity's id when a different one claimed it first.
agrag.cypher.entities.upsert_node_query
upsert_node_query(labels:Sequence[str]) -> str

Build the Cypher for an UNWIND-batched node upsert.

MERGE identity is anchored to NODE_IDENTITY_LABEL, not to labels itself, so a node keeps resolving to the same id regardless of what labels it currently carries. labels is then applied additively with SET, which is idempotent (a label the node already has is a no-op) and never removes a label a previous upsert of the same id set but this one omits: labels only ever accumulate. Every node in one call gets the same additive label set, since Cypher requires labels to be literal in the query text rather than a runtime parameter; nodes whose NodeRecord.labels differ need separate calls, one per distinct label set (see Neo4jGraphStore.upsert_nodes for how a mixed batch is grouped and split before reaching this builder).

Identity is reasserted after applying properties, so a caller-supplied properties["id"] cannot overwrite the id used to MERGE and orphan the node from later upserts of the same record.

Parameters:

  • labels (Sequence[str]) – The node's labels to add, in addition to the identity anchor. Must already be validated, and non-empty.

Returns:

  • str – A parameterized Cypher query expecting a $records list parameter.

Raises:

  • ValueErrorlabels is empty, or any label is not a safe identifier.
agrag.cypher.entities.upsert_survivor_query
upsert_survivor_query(label:str) -> str

Build Cypher upserting a merge survivor with atomic accumulation.

Unlike upsert_node_query's plain SET n += record.properties overwrite, source_chunk_ids and merged_from are unioned against whatever the node currently has, and merge_count is incremented by a delta, all read and written inside this one query. Two concurrent callers merging into the same entity each read the node's current accumulator values fresh here, so neither's contribution is lost to whichever write lands second -- unlike overwriting from a full snapshot taken before either write landed. Every other property is still applied as-is (last write wins); resolving a conflict there needs the candidate values, which only a Python-side read can gather, so making that atomic too is out of scope here.

Parameters:

  • label (str) – The node label. Must already be validated.

Returns:

  • str – A parameterized Cypher query expecting a $records list
  • str – parameter whose items carry id, properties (every survivor
  • str – field except source_chunk_ids, merged_from, and
  • strmerge_count), new_source_chunk_ids, new_merged_from, and
  • strmerge_count_delta.
agrag.cypher.entities.validate_identifier
validate_identifier(value:str) -> str

Check that a label or relationship type is a safe Cypher identifier.

Parameters:

  • value (str) – The label or relationship type to check.

Returns:

  • strvalue unchanged, once validated.

Raises:

agrag.cypher.merge

Cypher for the tombstone/transfer/dedup merge path.

Functions:

agrag.cypher.merge.apply_relationship_dedup_delete_query
apply_relationship_dedup_delete_query() -> str

Build Cypher deleting relationships a dedup pass superseded.

Returns:

  • str – Parameterized Cypher expecting $delete_ids (list of
  • str{id, rel_type}).
agrag.cypher.merge.apply_relationship_dedup_update_query
apply_relationship_dedup_update_query() -> str

Build Cypher applying a kept relationship's merged properties.

Returns:

  • str – Parameterized Cypher expecting $updates (list of
  • str{id, rel_type, properties}), where properties is the full merged
  • str – property map -- not just source_chunk_ids -- so a duplicate's other
  • str – fields are not silently dropped when its edge is deleted.
agrag.cypher.merge.clear_tombstone_merge_keys_query
clear_tombstone_merge_keys_query(label:str) -> str

Build Cypher removing merge_key from nodes about to be absorbed.

Must run before upsert_survivor_query writes the survivor, not after tombstone_query: canonical selection can choose a different node as survivor than the one a property rule (e.g. KEEP_FIRST) resolves the name from, so the survivor's resolved merge_key can equal a still-live tombstone's own merge_key. Writing the survivor first would then collide with the per-label merge_key uniqueness constraint, since both nodes would briefly hold the same value.

Parameters:

  • label (str) – The node label. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $tombstone_ids.
agrag.cypher.merge.delete_internal_relationships_query
delete_internal_relationships_query() -> str

Build Cypher deleting edges that would become meaningless self-links.

Covers two cases, both before any transfer runs, inside the same transaction as the merge:

  • An edge between two absorbed nodes: left untransferred, it would become a stale survivor->tombstone edge after the first transfer.
  • An edge directly between an absorbed node and its own survivor: transfer_relationships_query excludes these (other.id <> $survivor_id), since transferring one would create a survivor->survivor self-loop that no relation type's semantics call for. Deleting them here, in both directions, is what keeps them from being silently orphaned on the tombstone instead.

Returns:

  • str – Parameterized Cypher expecting $tombstone_ids (list of strings) and
  • str – $survivor_id.
agrag.cypher.merge.fetch_node_relationships_query
fetch_node_relationships_query(*, outgoing:bool) -> str

Build Cypher fetching one direction of a node's own relationships.

Run against the survivor after every transfer completes, so the dedup pass that follows sees the survivor's whole neighbourhood in that direction -- both freshly transferred edges and ones it already had -- rather than only what one transfer call happened to move.

Parameters:

  • outgoing (bool) – True fetches (node)-[r]->(other) edges. False fetches (other)-[r]->(node) edges.

Returns:

  • str – Parameterized Cypher expecting $node_id.
agrag.cypher.merge.tombstone_query
tombstone_query(label:str, *, vector_property:str) -> str

Build Cypher marking one or more nodes as merged, never deleting them.

Also removes vector_property: a native Neo4j vector index only covers nodes that currently carry the indexed property, so dropping it takes the tombstone out of vector search immediately, with no query-time filter and no dependency on a later re-embed ever running against it.

Parameters:

  • label (str) – The node label. Must already be validated.
  • vector_property (str) – The embedding property to remove. Must already be validated.

Returns:

  • str – Parameterized Cypher expecting $tombstone_ids and $survivor_id.
agrag.cypher.merge.transfer_relationships_query
transfer_relationships_query(*, outgoing:bool) -> str

Build Cypher moving one direction of a tombstoned node's relationships.

Parameters:

  • outgoing (bool) – True moves (tombstone)-[r]->(other) edges. False moves (other)-[r]->(tombstone) edges.

Returns:

  • str – Parameterized Cypher expecting $tombstone_id and $survivor_id.

agrag.cypher.relations

Cypher builders for relationship writes and graph traversal.

Leaf module: imports nothing from agrag.graphdb. See entities.py for the identifier-validation contract shared by every Cypher builder.

Functions:

agrag.cypher.relations.bfs_expand_query
bfs_expand_query(*, depth:int = 2, limit:int = 50, filters:dict[str, Any] | None = None, relation_types:Sequence[str] | None = None) -> tuple[str, dict[str, Any]]

Build Cypher for BFS expansion from seed entity ids.

Traverses outgoing relationships from a set of seed entities, bounded by depth hops and limit total result nodes. The depth is formatted into the query text (not a parameter) because Neo4j does not accept a parameter for a variable-length relationship bound. It must come from RetrievalSettings, never from user input.

relation_types restricts which relationships a traversal may cross. Neo4j does not accept a parameter for relationship types either, so each type is validated and formatted into the pattern.

depth is clamped to [1, 10] and limit to [1, 1000] so misconfigured or malicious settings cannot produce unbounded traversals. The clamp is applied here, closest to the Cypher interpolation, so every caller benefits.

Result nodes are restricted to _AgragNode entities that are not Chunk nodes: chunks are intermediate path nodes only, never returned as BFS results.

Parameters:

  • depth (int) – The maximum BFS hops. Clamped to [1, 10].
  • limit (int) – The maximum number of result nodes. Clamped to [1, 1000].
  • filters (dict[str, Any] | None) – Optional flat-dict filter applied to neighbor nodes. A scalar value means exact match, a list means any of.
  • relation_types (Sequence[str] | None) – Optional relationship types the traversal may cross. None or empty crosses every type.

Returns:

  • str – A (query, params) tuple. The query expects $seed_ids
  • dict[str, Any] – (list of string ids) plus any filter parameters.

Raises:

  • ValueError – A relation type is not a safe Cypher identifier.
agrag.cypher.relations.chunks_mentioning_entities_query
chunks_mentioning_entities_query() -> str

Build Cypher finding chunks that mention given entities.

Walks the MENTIONED_IN edge from Chunk to Entity. Returns chunks that reference any of the given entity ids.

Returns:

  • str – Parameterized Cypher expecting $entity_ids (list of string ids).
agrag.cypher.relations.entities_mentioned_in_chunks_query
entities_mentioned_in_chunks_query() -> str

Build Cypher finding entities mentioned by given chunks.

Walks the MENTIONED_IN edge from Chunk to Entity in reverse. Returns entities referenced by any of the given chunk ids.

Returns:

  • str – Parameterized Cypher expecting $chunk_ids (list of string ids).
agrag.cypher.relations.upsert_relation_query
upsert_relation_query(rel_type:str) -> str

Build the Cypher for an UNWIND-batched relationship upsert.

Relationship identity is record.id, not the (start, end, type) triple: two relationships of this type between the same nodes keep separate identities when their ids differ, so parallel relationships do not collapse into one. When a record's endpoints move, the relationship keeps its id: the stale copy at the old endpoints is deleted before the new one is written, backed by the per-type uniqueness constraint from relation_id_constraint_query. A relationship's type is immutable once written; retyping one requires deleting it under its old type first, since a single upsert call only ever targets one type. Identity is reasserted after applying properties, so a caller-supplied properties["id"] cannot overwrite the id used to MERGE and orphan the relationship from later upserts of the same record.

source_chunk_ids is unioned against whatever is already on the relationship at write time, inside this same query, rather than blindly overwritten: two concurrent callers upserting the same relationship each compute their own union from a read taken before either write lands, so without this, whichever caller's write commits second would silently discard the chunk ids the other one contributed. Reading the current value here, inside the same MERGE, keeps the union correct regardless of which caller's read was stale.

Parameters:

  • rel_type (str) – The relationship type. Must already be validated.

Returns:

  • str – A parameterized Cypher query expecting a $records list parameter whose
  • str – items carry id, start_id, end_id, and properties keys.
  • strproperties may include source_chunk_ids; other keys are
  • str – applied as-is.

agrag.cypher.safety

Safety gate for generated Cypher queries.

Classes:

  • UnsafeCypherError – Raised when a generated Cypher query contains a write clause.

Functions:

agrag.cypher.safety.UnsafeCypherError

Bases: Exception

Raised when a generated Cypher query contains a write clause.

agrag.cypher.safety.reject_write_cypher
reject_write_cypher(query:str) -> None

Raise fast on an obvious write clause, ahead of EXPLAIN.

This is a cheap pre-filter, not the safety boundary: execute_read's read transaction is what actually prevents a write from running, since Neo4j itself rejects one there. This check exists so a write-shaped generated query fails immediately instead of spending an EXPLAIN round trip first.

Keywords are matched case-insensitively, and only outside string literals, comments, and backtick identifiers, so a lowercase delete or a quote inside a comment cannot desync the scan. The check is conservative: a write keyword used as a property name (e.g. RETURN n.set) is also rejected, acceptable for a pre-filter that guards model-generated text.

Parameters:

  • query (str) – The Cypher text a BAML call produced.

Raises:

  • UnsafeCypherError – The query contains a write keyword outside a string literal, comment, or backtick identifier.
agrag.cypher.safety.strip_cypher_syntax
strip_cypher_syntax(query:str) -> str

Blank string literals, comments, and backtick identifiers.

Replaces the contents of string literals, // and /* */ comments, and backtick-quoted identifiers with spaces, keeping every other character intact so token boundaries survive. Scans char by char and honors backslash and doubled-quote escapes, so an escaped quote inside a literal cannot close the scan early and let a real keyword after it hide inside a bogus "string".

Parameters:

  • query (str) – The Cypher text to scrub.

Returns:

  • str – The query with literal, comment, and identifier content blanked,
  • str – layout otherwise unchanged.

agrag.cypher.schema

Cypher builders for constraints and native vector indexes.

Leaf module: imports nothing from agrag.graphdb. See entities.py for the identifier-validation contract shared by every Cypher builder.

Functions:

agrag.cypher.schema.merge_alias_constraint_query
merge_alias_constraint_query() -> str

Build a CREATE CONSTRAINT query making the merge-key alias table unique.

One global constraint, not per label: MERGE_ALIAS_LABEL is shared across every entity type, and merge_key already embeds the label (see Entity.merge_key), so a single uniqueness constraint on it is sufficient.

Returns:

  • str – A Cypher query creating the uniqueness constraint if absent.
agrag.cypher.schema.merge_key_constraint_query
merge_key_constraint_query(label:str) -> str

Build a CREATE CONSTRAINT query making merge_key unique per label.

Backs the concurrent-ingestion safety tier: two concurrent add() calls for the same (label, normalized name) cannot both create a canonical node; the second fails the constraint and is resolved to the canonical via the merge path. A node absorbed by a merge has its merge_key cleared before it is marked merged_into, so the constraint permits one live survivor per key plus any number of tombstones.

Parameters:

  • label (str) – The node label. Must already be validated.

Returns:

  • str – A Cypher query creating the uniqueness constraint if absent.
agrag.cypher.schema.node_id_constraint_query
node_id_constraint_query(label:str) -> str

Build a CREATE CONSTRAINT query making id unique per node.

Neo4j constraint names share one flat, global namespace regardless of whether they apply to a node label or a relationship type, so this name is kind-prefixed and length-prefixed the same way vector_index_name is: label "X_rel" and relationship type "X" would otherwise both produce X_rel_id_unique, and IF NOT EXISTS would then silently leave the second one never created.

Parameters:

  • label (str) – The node label. Must already be validated.

Returns:

  • str – A Cypher query creating the uniqueness constraint if absent.
agrag.cypher.schema.plain_index_query
plain_index_query(label:str) -> str

Build a CREATE INDEX query on the node id property.

Parameters:

  • label (str) – The node label. Must already be validated.

Returns:

  • str – A Cypher query creating the range index if absent.
agrag.cypher.schema.relation_id_constraint_query
relation_id_constraint_query(rel_type:str) -> str

Build a CREATE CONSTRAINT query making id unique per relationship type.

This backs the stale-relationship lookup in upsert_relation_query with an index and guarantees at most one relationship of rel_type carries a given id. See node_id_constraint_query for why the name is kind-prefixed and length-prefixed rather than a plain concatenation.

Parameters:

  • rel_type (str) – The relationship type. Must already be validated.

Returns:

  • str – A Cypher query creating the uniqueness constraint if absent.
agrag.cypher.schema.vector_index_name
vector_index_name(label:str, vector_property:str) -> str

Derive the deterministic name a vector index is created under.

Each component is length-prefixed so the encoding is unambiguous: a plain join like f"{label}_{vector_property}_vector" would let a label and property containing underscores collide, for example ("A_B", "C") and ("A", "B_C") both joining to "A_B_C_vector". A collision would make ensure_vector_index reuse one index for two different label and property pairs, and vector_search would then search the wrong nodes. ensure_vector_index and vector_search both call this function, so they always agree on the name.

Parameters:

  • label (str) – The node label. Must already be validated.
  • vector_property (str) – The vector property name. Must already be validated.

Returns:

  • str – The index name ensure_vector_index and vector_search share.
agrag.cypher.schema.vector_index_query
vector_index_query(label:str, vector_property:str, dimensions:int, distance:Distance) -> str

Build a CREATE VECTOR INDEX query for native vector search.

Parameters:

  • label (str) – The node label. Must already be validated.
  • vector_property (str) – The vector property name. Must already be validated.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric, mapped to Neo4j's similarity function.

Returns:

  • str – A Cypher query creating the vector index if absent.

Raises:

  • ValueErrordistance is not a metric Neo4j vector indexes support.
agrag.cypher.schema.vector_search_query
vector_search_query(index_name:str, filters:dict[str, Any] | None = None) -> tuple[str, dict[str, Any]]

Build a native vector search query and its filter parameters.

Parameters:

  • index_name (str) – The vector index name from vector_index_name.
  • filters (dict[str, Any] | None) – An optional flat-dict filter applied with WHERE.

Returns:

  • str – The Cypher query yielding node and score, and a parameter dict
  • dict[str, Any] – holding only the filter parameters (the caller adds index, k,
  • tuple[str, dict[str, Any]] – and vector).

agrag.embedding

Text embedding: turn strings into dense vectors.

Modules:

  • base – The Embedder and EmbeddingCache protocols.
  • errors – Errors that the embedding layer raises.
  • fastembed_bm25 – BM25 sparse embedder backed by FastEmbed.
  • sentence_transformers – Sentence-transformers embedder implementation.
  • settings – Settings for the sentence-transformers embedder.
  • sparse_base – Sparse lexical vectors and the sparse embedder protocol.

Classes:

Functions:

  • build_embedder – Build an embedder from a model name, or return an embedder unchanged.

agrag.embedding.Embedder

Bases: ABC

A component that turns text into dense embedding vectors.

Functions:

  • dimensions – Return the dimension of the vectors this embedder produces.
  • embed – Embed a batch of texts.
  • embed_one – Embed a single text.

Attributes:

agrag.embedding.Embedder.dimensions
dimensions() -> int

Return the dimension of the vectors this embedder produces.

Async because a lazily-loaded embedder may need to load its model to answer, and that load must go through the same worker-thread/lock path embed uses rather than blocking the event loop.

agrag.embedding.Embedder.distance
distance: Distance

Return the distance metric for vector indexes created for this embedder.

Defaults to cosine, which matches normalized sentence-transformer models. Concrete embedders may override when their vectors use a different metric.

agrag.embedding.Embedder.embed
embed(texts:Sequence[str]) -> list[list[float]]

Embed a batch of texts.

Parameters:

  • texts (Sequence[str]) – The texts to embed, in order.

Returns:

  • list[list[float]] – One vector per input text, in the same order.
agrag.embedding.Embedder.embed_one
embed_one(text:str) -> list[float]

Embed a single text.

Parameters:

  • text (str) – The text to embed.

Returns:

  • list[float] – The text's embedding vector.
agrag.embedding.Embedder.model
model: str

agrag.embedding.EmbeddingSettings

Bases: BaseSettings

Sentence-transformers embedder configuration.

All fields are overridable via environment variables with the EMBEDDING_ prefix.

Attributes:

  • model (str) – The sentence-transformers model name or path. Env: EMBEDDING_MODEL.
  • device (str | None) – The device to load the model on, such as "cpu" or "cuda". None uses sentence-transformers' own default detection. Env: EMBEDDING_DEVICE.
  • normalize (bool) – Whether to L2-normalize output vectors. Env: EMBEDDING_NORMALIZE.
  • batch_size (int) – The number of texts encoded per model.encode call. Env: EMBEDDING_BATCH_SIZE.
  • cache_folder (str | None) – Where sentence-transformers caches downloaded models. None uses the library default. Env: EMBEDDING_CACHE_FOLDER.
agrag.embedding.EmbeddingSettings.batch_size
batch_size: int = 32
agrag.embedding.EmbeddingSettings.cache_folder
cache_folder: str | None = None
agrag.embedding.EmbeddingSettings.device
device: str | None = None
agrag.embedding.EmbeddingSettings.model
model: str = 'ibm-granite/granite-embedding-small-english-r2'
agrag.embedding.EmbeddingSettings.model_config
model_config = SettingsConfigDict(env_prefix='EMBEDDING_', env_file='.env', extra='ignore')
agrag.embedding.EmbeddingSettings.normalize
normalize: bool = True

agrag.embedding.FastEmbedBM25Embedder

FastEmbedBM25Embedder(*, model:str | None = None) -> None

Bases: SparseEmbedder

A sparse BM25 embedder built on FastEmbed.

The model loads lazily on first embed, so constructing the embedder does not download weights. Each blocking call into FastEmbed runs in a worker thread, keeping the event loop free. FastEmbed ships with the qdrant extra, so a clean install without that extra raises EmbeddingMissingExtraError rather than ImportError.

Functions:

  • embed – Embed a batch of documents into BM25 sparse vectors.
  • query_embed – Embed a batch of search queries into BM25 sparse vectors.

Attributes:

  • model (str) – The configured model name, or the FastEmbed default when unset.

Parameters:

  • model (str | None) – The FastEmbed BM25 model name. Defaults to FastEmbed's built-in BM25 model.
agrag.embedding.FastEmbedBM25Embedder.embed
embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of documents into BM25 sparse vectors.

Applies FastEmbed's document-side term-frequency and length normalization weighting. Use query_embed for search queries.

Parameters:

  • texts (Sequence[str]) – The document texts to embed, in order.

Returns:

agrag.embedding.FastEmbedBM25Embedder.model
model: str

The configured model name, or the FastEmbed default when unset.

agrag.embedding.FastEmbedBM25Embedder.query_embed
query_embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of search queries into BM25 sparse vectors.

Uses FastEmbed's query_embed, which assigns each unique query term a uniform weight of 1.0 rather than the document-side term-frequency and length-normalization weighting embed applies; IDF weighting is applied separately by the sparse index's Modifier.IDF at query time.

Parameters:

  • texts (Sequence[str]) – The query texts to embed, in order.

Returns:

agrag.embedding.SentenceTransformerEmbedder

SentenceTransformerEmbedder(*, settings:EmbeddingSettings | None = None, cache:EmbeddingCache | None = None, model:object | None = None) -> None

Bases: Embedder

An embedder backed by sentence-transformers.

The model loads lazily on first embed, so constructing the embedder does not touch the GPU or download weights. Every blocking call into the model runs in a worker thread (asyncio.to_thread), so the event loop stays free for other work while a large batch encodes.

Functions:

  • dimensions – Return the dimension the loaded model produces.
  • embed – Embed a batch of texts, using the cache where possible.
  • embed_one – Embed a single text.

Attributes:

  • distance (Distance) – Return the distance metric for vector indexes created for this embedder.
  • model (str) – The configured model name.

Parameters:

  • settings (EmbeddingSettings | None) – Embedder configuration. Defaults to EmbeddingSettings().
  • cache (EmbeddingCache | None) – An optional content-addressed cache. Defaults to a no-op cache.
  • model (object | None) – A pre-built sentence-transformers model, for tests. When set, __init__ imports nothing and embed calls this object directly instead of building one.
agrag.embedding.SentenceTransformerEmbedder.dimensions
dimensions() -> int

Return the dimension the loaded model produces.

Calling this loads the model the first time, the same lock-protected, worker-thread path embed uses, so it is safe to call concurrently with embed without stalling the event loop or loading a second copy of the model.

Raises:

agrag.embedding.SentenceTransformerEmbedder.distance
distance: Distance

Return the distance metric for vector indexes created for this embedder.

Defaults to cosine, which matches normalized sentence-transformer models. Concrete embedders may override when their vectors use a different metric.

agrag.embedding.SentenceTransformerEmbedder.embed
embed(texts:Sequence[str]) -> list[list[float]]

Embed a batch of texts, using the cache where possible.

Parameters:

  • texts (Sequence[str]) – The texts to embed, in order.

Returns:

  • list[list[float]] – One vector per input text, in the same order.
agrag.embedding.SentenceTransformerEmbedder.embed_one
embed_one(text:str) -> list[float]

Embed a single text.

Parameters:

  • text (str) – The text to embed.

Returns:

  • list[float] – The text's embedding vector.
agrag.embedding.SentenceTransformerEmbedder.model
model: str

The configured model name.

agrag.embedding.SparseEmbedder

Bases: ABC

A component that turns text into sparse lexical vectors, for hybrid search.

Functions:

  • embed – Embed a batch of documents into sparse vectors.
  • query_embed – Embed a batch of search queries into sparse vectors.

Attributes:

agrag.embedding.SparseEmbedder.embed
embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of documents into sparse vectors.

Parameters:

  • texts (Sequence[str]) – The document texts to embed, in order.

Returns:

agrag.embedding.SparseEmbedder.model
model: str
agrag.embedding.SparseEmbedder.query_embed
query_embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of search queries into sparse vectors.

Query-side sparse embedding is not always the same computation as document-side embedding: BM25, for example, applies term-frequency and document-length normalization on the document side but only a uniform per-term weight on the query side, since IDF weighting is applied by the sparse index at query time instead. Implementations with no such asymmetry may implement this identically to embed.

Parameters:

  • texts (Sequence[str]) – The query texts to embed, in order.

Returns:

agrag.embedding.SparseVector

Bases: BaseModel

A sparse vector: nonzero indices and their values.

Attributes:

agrag.embedding.SparseVector.indices
indices: list[int]
agrag.embedding.SparseVector.values
values: list[float]

agrag.embedding.base

The Embedder and EmbeddingCache protocols.

Classes:

  • Embedder – A component that turns text into dense embedding vectors.
  • EmbeddingCache – A content-addressed cache for embedding vectors.
  • NullEmbeddingCache – A cache that never stores anything. The default when none is injected.
agrag.embedding.base.Embedder

Bases: ABC

A component that turns text into dense embedding vectors.

Functions:

  • dimensions – Return the dimension of the vectors this embedder produces.
  • embed – Embed a batch of texts.
  • embed_one – Embed a single text.

Attributes:

agrag.embedding.base.Embedder.dimensions
dimensions() -> int

Return the dimension of the vectors this embedder produces.

Async because a lazily-loaded embedder may need to load its model to answer, and that load must go through the same worker-thread/lock path embed uses rather than blocking the event loop.

agrag.embedding.base.Embedder.distance
distance: Distance

Return the distance metric for vector indexes created for this embedder.

Defaults to cosine, which matches normalized sentence-transformer models. Concrete embedders may override when their vectors use a different metric.

agrag.embedding.base.Embedder.embed
embed(texts:Sequence[str]) -> list[list[float]]

Embed a batch of texts.

Parameters:

  • texts (Sequence[str]) – The texts to embed, in order.

Returns:

  • list[list[float]] – One vector per input text, in the same order.
agrag.embedding.base.Embedder.embed_one
embed_one(text:str) -> list[float]

Embed a single text.

Parameters:

  • text (str) – The text to embed.

Returns:

  • list[float] – The text's embedding vector.
agrag.embedding.base.Embedder.model
model: str
agrag.embedding.base.EmbeddingCache

Bases: ABC

A content-addressed cache for embedding vectors.

normalize is part of the cache key alongside text and model because it changes the vector an embedder produces for the same text and model: without it, embedders sharing one cache but configured with opposite EmbeddingSettings.normalize values would read back the wrong output mode. Any future embedder setting that changes output values must join this key the same way.

Functions:

  • get – Return the cached vector for (text, model, normalize).
  • set – Store vector under (text, model, normalize).
agrag.embedding.base.EmbeddingCache.get
get(*, text:str, model:str, normalize:bool) -> list[float] | None

Return the cached vector for (text, model, normalize).

Returns:

  • list[float] | None – The cached vector, or None on a miss.
agrag.embedding.base.EmbeddingCache.set
set(*, text:str, model:str, normalize:bool, vector:list[float]) -> None

Store vector under (text, model, normalize).

agrag.embedding.base.NullEmbeddingCache

Bases: EmbeddingCache

A cache that never stores anything. The default when none is injected.

Functions:

  • get – Always miss.
  • set – Do nothing.
agrag.embedding.base.NullEmbeddingCache.get
get(*, text:str, model:str, normalize:bool) -> list[float] | None

Always miss.

agrag.embedding.base.NullEmbeddingCache.set
set(*, text:str, model:str, normalize:bool, vector:list[float]) -> None

Do nothing.

agrag.embedding.build_embedder

build_embedder(value:str | Embedder) -> Embedder

Build an embedder from a model name, or return an embedder unchanged.

Parameters:

  • value (str | Embedder) – A sentence-transformers model name, such as "ibm-granite/granite-embedding-small-english-r2" (the default model), or an already-constructed Embedder for full control over device, batching, or caching.

Returns:

agrag.embedding.errors

Errors that the embedding layer raises.

Classes:

agrag.embedding.errors.EmbeddingDimensionMismatchError
EmbeddingDimensionMismatchError(*, expected:int, actual:int) -> None

Bases: EmbeddingError

A stored collection or index expects a different embedding dimension.

Attributes:

  • expected – The dimension the collection or index was created with.
  • actual – The dimension the embedder actually produces.
agrag.embedding.errors.EmbeddingDimensionMismatchError.actual
actual = actual
agrag.embedding.errors.EmbeddingDimensionMismatchError.expected
expected = expected
agrag.embedding.errors.EmbeddingError

Bases: Exception

The base class for every embedding error.

agrag.embedding.errors.EmbeddingMissingExtraError
EmbeddingMissingExtraError(extra:str) -> None

Bases: EmbeddingError

An embedder exists, but its package extra is not installed.

Attributes:

  • extra – The name of the package extra to install.
agrag.embedding.errors.EmbeddingMissingExtraError.extra
extra = extra

agrag.embedding.fastembed_bm25

BM25 sparse embedder backed by FastEmbed.

Classes:

Attributes:

agrag.embedding.fastembed_bm25.DEFAULT_BM25_MODEL
DEFAULT_BM25_MODEL = 'Qdrant/bm25'
agrag.embedding.fastembed_bm25.FastEmbedBM25Embedder
FastEmbedBM25Embedder(*, model:str | None = None) -> None

Bases: SparseEmbedder

A sparse BM25 embedder built on FastEmbed.

The model loads lazily on first embed, so constructing the embedder does not download weights. Each blocking call into FastEmbed runs in a worker thread, keeping the event loop free. FastEmbed ships with the qdrant extra, so a clean install without that extra raises EmbeddingMissingExtraError rather than ImportError.

Functions:

  • embed – Embed a batch of documents into BM25 sparse vectors.
  • query_embed – Embed a batch of search queries into BM25 sparse vectors.

Attributes:

  • model (str) – The configured model name, or the FastEmbed default when unset.

Parameters:

  • model (str | None) – The FastEmbed BM25 model name. Defaults to FastEmbed's built-in BM25 model.
agrag.embedding.fastembed_bm25.FastEmbedBM25Embedder.embed
embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of documents into BM25 sparse vectors.

Applies FastEmbed's document-side term-frequency and length normalization weighting. Use query_embed for search queries.

Parameters:

  • texts (Sequence[str]) – The document texts to embed, in order.

Returns:

agrag.embedding.fastembed_bm25.FastEmbedBM25Embedder.model
model: str

The configured model name, or the FastEmbed default when unset.

agrag.embedding.fastembed_bm25.FastEmbedBM25Embedder.query_embed
query_embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of search queries into BM25 sparse vectors.

Uses FastEmbed's query_embed, which assigns each unique query term a uniform weight of 1.0 rather than the document-side term-frequency and length-normalization weighting embed applies; IDF weighting is applied separately by the sparse index's Modifier.IDF at query time.

Parameters:

  • texts (Sequence[str]) – The query texts to embed, in order.

Returns:

agrag.embedding.sentence_transformers

Sentence-transformers embedder implementation.

Classes:

agrag.embedding.sentence_transformers.SentenceTransformerEmbedder
SentenceTransformerEmbedder(*, settings:EmbeddingSettings | None = None, cache:EmbeddingCache | None = None, model:object | None = None) -> None

Bases: Embedder

An embedder backed by sentence-transformers.

The model loads lazily on first embed, so constructing the embedder does not touch the GPU or download weights. Every blocking call into the model runs in a worker thread (asyncio.to_thread), so the event loop stays free for other work while a large batch encodes.

Functions:

  • dimensions – Return the dimension the loaded model produces.
  • embed – Embed a batch of texts, using the cache where possible.
  • embed_one – Embed a single text.

Attributes:

  • distance (Distance) – Return the distance metric for vector indexes created for this embedder.
  • model (str) – The configured model name.

Parameters:

  • settings (EmbeddingSettings | None) – Embedder configuration. Defaults to EmbeddingSettings().
  • cache (EmbeddingCache | None) – An optional content-addressed cache. Defaults to a no-op cache.
  • model (object | None) – A pre-built sentence-transformers model, for tests. When set, __init__ imports nothing and embed calls this object directly instead of building one.
agrag.embedding.sentence_transformers.SentenceTransformerEmbedder.dimensions
dimensions() -> int

Return the dimension the loaded model produces.

Calling this loads the model the first time, the same lock-protected, worker-thread path embed uses, so it is safe to call concurrently with embed without stalling the event loop or loading a second copy of the model.

Raises:

agrag.embedding.sentence_transformers.SentenceTransformerEmbedder.distance
distance: Distance

Return the distance metric for vector indexes created for this embedder.

Defaults to cosine, which matches normalized sentence-transformer models. Concrete embedders may override when their vectors use a different metric.

agrag.embedding.sentence_transformers.SentenceTransformerEmbedder.embed
embed(texts:Sequence[str]) -> list[list[float]]

Embed a batch of texts, using the cache where possible.

Parameters:

  • texts (Sequence[str]) – The texts to embed, in order.

Returns:

  • list[list[float]] – One vector per input text, in the same order.
agrag.embedding.sentence_transformers.SentenceTransformerEmbedder.embed_one
embed_one(text:str) -> list[float]

Embed a single text.

Parameters:

  • text (str) – The text to embed.

Returns:

  • list[float] – The text's embedding vector.
agrag.embedding.sentence_transformers.SentenceTransformerEmbedder.model
model: str

The configured model name.

agrag.embedding.settings

Settings for the sentence-transformers embedder.

Classes:

agrag.embedding.settings.EmbeddingSettings

Bases: BaseSettings

Sentence-transformers embedder configuration.

All fields are overridable via environment variables with the EMBEDDING_ prefix.

Attributes:

  • model (str) – The sentence-transformers model name or path. Env: EMBEDDING_MODEL.
  • device (str | None) – The device to load the model on, such as "cpu" or "cuda". None uses sentence-transformers' own default detection. Env: EMBEDDING_DEVICE.
  • normalize (bool) – Whether to L2-normalize output vectors. Env: EMBEDDING_NORMALIZE.
  • batch_size (int) – The number of texts encoded per model.encode call. Env: EMBEDDING_BATCH_SIZE.
  • cache_folder (str | None) – Where sentence-transformers caches downloaded models. None uses the library default. Env: EMBEDDING_CACHE_FOLDER.
agrag.embedding.settings.EmbeddingSettings.batch_size
batch_size: int = 32
agrag.embedding.settings.EmbeddingSettings.cache_folder
cache_folder: str | None = None
agrag.embedding.settings.EmbeddingSettings.device
device: str | None = None
agrag.embedding.settings.EmbeddingSettings.model
model: str = 'ibm-granite/granite-embedding-small-english-r2'
agrag.embedding.settings.EmbeddingSettings.model_config
model_config = SettingsConfigDict(env_prefix='EMBEDDING_', env_file='.env', extra='ignore')
agrag.embedding.settings.EmbeddingSettings.normalize
normalize: bool = True

agrag.embedding.sparse_base

Sparse lexical vectors and the sparse embedder protocol.

Classes:

  • SparseEmbedder – A component that turns text into sparse lexical vectors, for hybrid search.
  • SparseVector – A sparse vector: nonzero indices and their values.
agrag.embedding.sparse_base.SparseEmbedder

Bases: ABC

A component that turns text into sparse lexical vectors, for hybrid search.

Functions:

  • embed – Embed a batch of documents into sparse vectors.
  • query_embed – Embed a batch of search queries into sparse vectors.

Attributes:

agrag.embedding.sparse_base.SparseEmbedder.embed
embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of documents into sparse vectors.

Parameters:

  • texts (Sequence[str]) – The document texts to embed, in order.

Returns:

agrag.embedding.sparse_base.SparseEmbedder.model
model: str
agrag.embedding.sparse_base.SparseEmbedder.query_embed
query_embed(texts:Sequence[str]) -> list[SparseVector]

Embed a batch of search queries into sparse vectors.

Query-side sparse embedding is not always the same computation as document-side embedding: BM25, for example, applies term-frequency and document-length normalization on the document side but only a uniform per-term weight on the query side, since IDF weighting is applied by the sparse index at query time instead. Implementations with no such asymmetry may implement this identically to embed.

Parameters:

  • texts (Sequence[str]) – The query texts to embed, in order.

Returns:

agrag.embedding.sparse_base.SparseVector

Bases: BaseModel

A sparse vector: nonzero indices and their values.

Attributes:

agrag.embedding.sparse_base.SparseVector.indices
indices: list[int]
agrag.embedding.sparse_base.SparseVector.values
values: list[float]

agrag.graphdb

Graph storage backends and the build shortcut.

Modules:

  • base – The GraphStore abstraction and its build shortcut helpers.
  • errors – Errors that the graph-store layer raises.
  • neo4j – Neo4j graph-store backend.
  • serialize – Convert graph records into Neo4j-driver-friendly parameters.
  • settings – Settings for the Neo4j graph-store backend.

Classes:

Functions:

  • build_graph_store – Build a graph store from a backend name, or return one unchanged.

Attributes:

agrag.graphdb.GraphStore

Bases: ABC

A graph database backend: schema, writes, and native vector search.

Functions:

agrag.graphdb.GraphStore.close
close() -> None

Release the backend connection.

agrag.graphdb.GraphStore.connect
connect() -> None

Open the backend connection and verify connectivity.

agrag.graphdb.GraphStore.ensure_vector_index
ensure_vector_index(*, label:str, vector_property:str, dimensions:int, distance:Distance) -> None

Create a native vector index if it does not exist.

Parameters:

  • label (str) – The node label to index.
  • vector_property (str) – The embedding property name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric.
agrag.graphdb.GraphStore.execute_read
execute_read(query:str, parameters:Mapping[str, Any] | None = None, *, timeout:float | None = None) -> list[dict[str, Any]]

Run a read transaction.

Parameters:

  • query (str) – The Cypher query to run.
  • parameters (Mapping[str, Any] | None) – The query parameters.
  • timeout (float | None) – Server-side transaction timeout in seconds. The database terminates the transaction when it runs longer. None uses the server's default timeout. Backends that cannot enforce a timeout ignore it.

Returns:

agrag.graphdb.GraphStore.execute_write
execute_write(query:str, parameters:Mapping[str, Any] | None = None) -> list[dict[str, Any]]

Run a write transaction.

Parameters:

  • query (str) – The Cypher query to run.
  • parameters (Mapping[str, Any] | None) – The query parameters.

Returns:

agrag.graphdb.GraphStore.register_labels
register_labels(labels:Sequence[str]) -> None

Mark labels as known, without writing anything.

setup_constraints()/setup_indexes() only cover labels this instance has already written (or that already exist live in the database) — both empty on a brand-new database. register_labels lets a caller holding a GraphSchema (Graph.open()) provision a fresh database fully before its first write.

Parameters:

  • labels (Sequence[str]) – The labels to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any label is not a safe identifier.
agrag.graphdb.GraphStore.register_relation_types
register_relation_types(types:Sequence[str]) -> None

Mark relationship types as known, without writing anything.

The relationship-type counterpart to register_labels — see its docstring for why this exists.

Parameters:

  • types (Sequence[str]) – The relationship types to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any type is not a safe identifier.
agrag.graphdb.GraphStore.session
session() -> AbstractAsyncContextManager[Any]

Open a session as an async context manager.

Returns:

agrag.graphdb.GraphStore.setup_constraints
setup_constraints() -> None

Create per-label and per-relation-type uniqueness constraints.

Constraints cover every node label and relationship type written through this instance or already present in the database, so a fresh instance can set up an existing database without first rewriting every record.

agrag.graphdb.GraphStore.setup_indexes
setup_indexes() -> None

Create per-label property indexes.

Covers every node label written through this instance or already present in the database, so a fresh instance can set up an existing database without first rewriting every record.

agrag.graphdb.GraphStore.transaction
transaction() -> AsyncIterator[GraphStoreTransaction]

Start an explicit transaction spanning multiple writes.

Every call through the yielded handle should join one backend transaction, committing as a whole on clean exit from the async with block and rolling back as a whole if the block raises. The default here simply yields self and gives no atomicity beyond what each individual call already provides; a backend that can offer real atomicity, such as Neo4jGraphStore, overrides this with a driver transaction.

Use this when a caller must guarantee several writes either all apply or none do, such as apply_merge's tombstone, relationship transfer, and dedup steps.

Returns:

agrag.graphdb.GraphStore.upsert_nodes
upsert_nodes(label:str, nodes:Sequence[NodeRecord], *, batch_size:int = 256) -> None

Write or merge nodes, honoring each record's full label set.

Parameters:

  • label (str) – The label this batch is tracked under for constraint and index bookkeeping.
  • nodes (Sequence[NodeRecord]) – The node records to upsert. Each node's NodeRecord.labels names the full label set actually written to it, which may include labels beyond label.
  • batch_size (int) – Records per backend write call, applied within each distinct label set when nodes mixes more than one. Must be positive.

Raises:

agrag.graphdb.GraphStore.upsert_relations
upsert_relations(relations:Sequence[RelationRecord], *, batch_size:int = 256) -> None

Write or merge relationships between existing nodes.

Parameters:

  • relations (Sequence[RelationRecord]) – The relation records to upsert.
  • batch_size (int) – Records per backend write call. Must be positive.

Raises:

vector_search(*, label:str, vector_property:str, query_vector:Sequence[float], limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search nodes by dense vector.

Parameters:

  • label (str) – The node label to search.
  • vector_property (str) – The embedding property name.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – Maximum number of hits.
  • filters (dict[str, Any] | None) – An optional flat-dict filter on node properties.

Returns:

agrag.graphdb.GraphStoreError

Bases: Exception

The base class for every graph-store error.

agrag.graphdb.GraphStoreMissingExtraError

GraphStoreMissingExtraError(extra:str) -> None

Bases: GraphStoreError

A graph store exists, but its package extra is not installed.

Attributes:

  • extra – The name of the package extra to install.
agrag.graphdb.GraphStoreMissingExtraError.extra
extra = extra

agrag.graphdb.GraphStoreName

GraphStoreName = Literal['neo4j']

agrag.graphdb.Neo4jGraphStore

Neo4jGraphStore(*, settings:Neo4jSettings | None = None, driver:AsyncDriver | None = None) -> None

Bases: GraphStore

A GraphStore backed by Neo4j, using native vector indexes.

The driver connects lazily on first use, so constructing the store does not open a network connection. execute_read/execute_write wrap the driver's managed transactions with no added retry loop, per ADR 0027.

Functions:

  • close – Close the driver, releasing its connection pool.
  • connect – Open the driver and verify connectivity.
  • ensure_vector_index – Create a native vector index if it does not exist.
  • execute_read – Run a read transaction and return its rows.
  • execute_write – Run a write transaction and return its rows.
  • register_labels – Add labels to this instance's known-label set.
  • register_relation_types – Add types to this instance's known-relation-type set.
  • session – Open a session to the configured database.
  • setup_constraints – Create a uniqueness constraint on id for every known label.
  • setup_indexes – Create a range index on id for every known label.
  • transaction – Open one Neo4j explicit transaction spanning multiple writes.
  • upsert_nodes – Write or merge nodes, honoring each record's full label set.
  • upsert_relations – Write or merge relationships between existing nodes.
  • vector_search – Search nodes by dense vector using the native vector index.

Parameters:

  • settings (Neo4jSettings | None) – Neo4j connection settings. Defaults to Neo4jSettings().
  • driver (AsyncDriver | None) – A pre-built AsyncDriver, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
agrag.graphdb.Neo4jGraphStore.close
close() -> None

Close the driver, releasing its connection pool.

agrag.graphdb.Neo4jGraphStore.connect
connect() -> None

Open the driver and verify connectivity.

agrag.graphdb.Neo4jGraphStore.ensure_vector_index
ensure_vector_index(*, label:str, vector_property:str, dimensions:int, distance:Distance) -> None

Create a native vector index if it does not exist.

agrag.graphdb.Neo4jGraphStore.execute_read
execute_read(query:str, parameters:Mapping[str, Any] | None = None, *, timeout:float | None = None) -> list[dict[str, Any]]

Run a read transaction and return its rows.

Parameters:

  • query (str) – The Cypher query to run.
  • parameters (Mapping[str, Any] | None) – The query parameters.
  • timeout (float | None) – Server-side transaction timeout in seconds, applied through the driver's unit_of_work so the database terminates the transaction when it runs longer. None uses the server's default timeout.

Returns:

agrag.graphdb.Neo4jGraphStore.execute_write
execute_write(query:str, parameters:Mapping[str, Any] | None = None) -> list[dict[str, Any]]

Run a write transaction and return its rows.

Raises:

  • GraphStoreConstraintViolationError – The write violated a uniqueness constraint, translated from the driver's own exception so callers do not need a hard dependency on it.
agrag.graphdb.Neo4jGraphStore.register_labels
register_labels(labels:Sequence[str]) -> None

Add labels to this instance's known-label set.

Parameters:

  • labels (Sequence[str]) – The labels to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any label is not a safe identifier.
agrag.graphdb.Neo4jGraphStore.register_relation_types
register_relation_types(types:Sequence[str]) -> None

Add types to this instance's known-relation-type set.

Parameters:

  • types (Sequence[str]) – The relationship types to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any type is not a safe identifier.
agrag.graphdb.Neo4jGraphStore.session
session() -> AbstractAsyncContextManager[Any]

Open a session to the configured database.

agrag.graphdb.Neo4jGraphStore.setup_constraints
setup_constraints() -> None

Create a uniqueness constraint on id for every known label.

"Known" means written by this instance or already present in the database, so a fresh store can set up constraints for an existing database without first rewriting every record. Also creates the global uniqueness constraint on NODE_IDENTITY_LABEL that upsert_node_query's MERGE relies on to resolve a node by id regardless of its other, mutable labels, and a per-type uniqueness constraint on id for every known relationship type, which backs the stale-relationship cleanup upsert_relation_query performs on endpoint changes.

agrag.graphdb.Neo4jGraphStore.setup_indexes
setup_indexes() -> None

Create a range index on id for every known label.

"Known" means written by this instance or already present in the database, so a fresh store can set up indexes for an existing database without first rewriting every record.

agrag.graphdb.Neo4jGraphStore.transaction
transaction() -> AsyncIterator[GraphStoreTransaction]

Open one Neo4j explicit transaction spanning multiple writes.

Commits when the async with block exits cleanly; rolls back and re-raises when it raises. The identity constraint is ensured before opening the transaction, the same way upsert_nodes ensures it before writing, since _Neo4jTransaction.upsert_nodes skips that check to avoid a nested write racing the transaction it belongs to.

Raises:

  • GraphStoreConstraintViolationError – A write inside the block, or the commit itself, violated a uniqueness constraint -- Neo4j validates some constraints only at commit time for an explicit transaction, so this can surface here even when every individual write appeared to succeed.
agrag.graphdb.Neo4jGraphStore.upsert_nodes
upsert_nodes(label:str, nodes:Sequence[NodeRecord], *, batch_size:int = 256) -> None

Write or merge nodes, honoring each record's full label set.

label names the batch for constraint/index bookkeeping, matching every other tracked label; the labels actually written to a node come from NodeRecord.labels, which may name more than one label (for example a node that is both Chunk and Entity). Records with different label sets are grouped and written with separate MERGE queries, since Cypher requires labels to be literal in the query text rather than a runtime parameter, so batch_size chunks apply within each group rather than across the whole call.

Raises:

agrag.graphdb.Neo4jGraphStore.upsert_relations
upsert_relations(relations:Sequence[RelationRecord], *, batch_size:int = 256) -> None

Write or merge relationships between existing nodes.

Relationship identity is each record's id, not its endpoints: see upsert_relation_query for how endpoint changes and same-id parallel relationships are handled.

Raises:

vector_search(*, label:str, vector_property:str, query_vector:Sequence[float], limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search nodes by dense vector using the native vector index.

A label with no provisioned vector index has nothing to search, so an absent index returns an empty result list rather than a driver error.

When filters is set, Neo4j's vector procedure applies the filter only after selecting its top k candidates, so a plain k=limit call can return fewer matches than actually exist. This escalates k and retries until limit filtered hits come back or the escalation reaches _VECTOR_SEARCH_MAX_K.

Raises:

  • ValueErrorlimit is not positive. A non-positive value is not a meaningful request and would send that same non-positive k to Neo4j's native vector procedure, which requires a positive top-k.

agrag.graphdb.Neo4jSettings

Bases: BaseSettings

Neo4j connection configuration.

Attributes:

  • uri (str) – The Bolt connection URI, including scheme (neo4j+s:// for Aura). Env: NEO4J_URI.
  • username (str) – The database username. Env: NEO4J_USERNAME.
  • password (SecretStr) – The database password. Env: NEO4J_PASSWORD.
  • database (str) – The target database name. Env: NEO4J_DATABASE.
  • max_connection_lifetime (int) – The maximum seconds a pooled connection lives, kept well below Aura's roughly five-minute idle timeout. Env: NEO4J_MAX_CONNECTION_LIFETIME.

Raises:

  • ValueErroruri is plaintext (bolt:// or neo4j://) and points at a non-local host. Neo4j always authenticates with a password, so a plaintext scheme always sends it in the clear; use neo4j+s:// (or bolt+s://) for a remote instance.
agrag.graphdb.Neo4jSettings.database
database: str = 'neo4j'
agrag.graphdb.Neo4jSettings.max_connection_lifetime
max_connection_lifetime: int = 240
agrag.graphdb.Neo4jSettings.model_config
model_config = SettingsConfigDict(env_prefix='NEO4J_', env_file='.env', extra='ignore')
agrag.graphdb.Neo4jSettings.password
password: SecretStr = SecretStr('neo4j')
agrag.graphdb.Neo4jSettings.uri
uri: str = 'bolt://localhost:7687'
agrag.graphdb.Neo4jSettings.username
username: str = 'neo4j'

agrag.graphdb.base

The GraphStore abstraction and its build shortcut helpers.

Classes:

  • GraphStore – A graph database backend: schema, writes, and native vector search.
  • GraphStoreTransaction – The read/write/upsert surface available inside a transaction() block.
agrag.graphdb.base.GraphStore

Bases: ABC

A graph database backend: schema, writes, and native vector search.

Functions:

agrag.graphdb.base.GraphStore.close
close() -> None

Release the backend connection.

agrag.graphdb.base.GraphStore.connect
connect() -> None

Open the backend connection and verify connectivity.

agrag.graphdb.base.GraphStore.ensure_vector_index
ensure_vector_index(*, label:str, vector_property:str, dimensions:int, distance:Distance) -> None

Create a native vector index if it does not exist.

Parameters:

  • label (str) – The node label to index.
  • vector_property (str) – The embedding property name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric.
agrag.graphdb.base.GraphStore.execute_read
execute_read(query:str, parameters:Mapping[str, Any] | None = None, *, timeout:float | None = None) -> list[dict[str, Any]]

Run a read transaction.

Parameters:

  • query (str) – The Cypher query to run.
  • parameters (Mapping[str, Any] | None) – The query parameters.
  • timeout (float | None) – Server-side transaction timeout in seconds. The database terminates the transaction when it runs longer. None uses the server's default timeout. Backends that cannot enforce a timeout ignore it.

Returns:

agrag.graphdb.base.GraphStore.execute_write
execute_write(query:str, parameters:Mapping[str, Any] | None = None) -> list[dict[str, Any]]

Run a write transaction.

Parameters:

  • query (str) – The Cypher query to run.
  • parameters (Mapping[str, Any] | None) – The query parameters.

Returns:

agrag.graphdb.base.GraphStore.register_labels
register_labels(labels:Sequence[str]) -> None

Mark labels as known, without writing anything.

setup_constraints()/setup_indexes() only cover labels this instance has already written (or that already exist live in the database) — both empty on a brand-new database. register_labels lets a caller holding a GraphSchema (Graph.open()) provision a fresh database fully before its first write.

Parameters:

  • labels (Sequence[str]) – The labels to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any label is not a safe identifier.
agrag.graphdb.base.GraphStore.register_relation_types
register_relation_types(types:Sequence[str]) -> None

Mark relationship types as known, without writing anything.

The relationship-type counterpart to register_labels — see its docstring for why this exists.

Parameters:

  • types (Sequence[str]) – The relationship types to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any type is not a safe identifier.
agrag.graphdb.base.GraphStore.session
session() -> AbstractAsyncContextManager[Any]

Open a session as an async context manager.

Returns:

agrag.graphdb.base.GraphStore.setup_constraints
setup_constraints() -> None

Create per-label and per-relation-type uniqueness constraints.

Constraints cover every node label and relationship type written through this instance or already present in the database, so a fresh instance can set up an existing database without first rewriting every record.

agrag.graphdb.base.GraphStore.setup_indexes
setup_indexes() -> None

Create per-label property indexes.

Covers every node label written through this instance or already present in the database, so a fresh instance can set up an existing database without first rewriting every record.

agrag.graphdb.base.GraphStore.transaction
transaction() -> AsyncIterator[GraphStoreTransaction]

Start an explicit transaction spanning multiple writes.

Every call through the yielded handle should join one backend transaction, committing as a whole on clean exit from the async with block and rolling back as a whole if the block raises. The default here simply yields self and gives no atomicity beyond what each individual call already provides; a backend that can offer real atomicity, such as Neo4jGraphStore, overrides this with a driver transaction.

Use this when a caller must guarantee several writes either all apply or none do, such as apply_merge's tombstone, relationship transfer, and dedup steps.

Returns:

agrag.graphdb.base.GraphStore.upsert_nodes
upsert_nodes(label:str, nodes:Sequence[NodeRecord], *, batch_size:int = 256) -> None

Write or merge nodes, honoring each record's full label set.

Parameters:

  • label (str) – The label this batch is tracked under for constraint and index bookkeeping.
  • nodes (Sequence[NodeRecord]) – The node records to upsert. Each node's NodeRecord.labels names the full label set actually written to it, which may include labels beyond label.
  • batch_size (int) – Records per backend write call, applied within each distinct label set when nodes mixes more than one. Must be positive.

Raises:

agrag.graphdb.base.GraphStore.upsert_relations
upsert_relations(relations:Sequence[RelationRecord], *, batch_size:int = 256) -> None

Write or merge relationships between existing nodes.

Parameters:

  • relations (Sequence[RelationRecord]) – The relation records to upsert.
  • batch_size (int) – Records per backend write call. Must be positive.

Raises:

vector_search(*, label:str, vector_property:str, query_vector:Sequence[float], limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search nodes by dense vector.

Parameters:

  • label (str) – The node label to search.
  • vector_property (str) – The embedding property name.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – Maximum number of hits.
  • filters (dict[str, Any] | None) – An optional flat-dict filter on node properties.

Returns:

agrag.graphdb.base.GraphStoreTransaction

Bases: Protocol

The read/write/upsert surface available inside a transaction() block.

A structural type, not a base class: GraphStore itself satisfies it (the default transaction() yields self), and a backend's own transaction handle, such as Neo4j's, satisfies it without inheriting from anything here.

Functions:

  • execute_read – Run a read inside the surrounding transaction.
  • execute_write – Run a write inside the surrounding transaction.
  • upsert_nodes – Write or merge nodes inside the surrounding transaction.
agrag.graphdb.base.GraphStoreTransaction.execute_read
execute_read(query:str, parameters:Mapping[str, Any] | None = None) -> list[dict[str, Any]]

Run a read inside the surrounding transaction.

agrag.graphdb.base.GraphStoreTransaction.execute_write
execute_write(query:str, parameters:Mapping[str, Any] | None = None) -> list[dict[str, Any]]

Run a write inside the surrounding transaction.

agrag.graphdb.base.GraphStoreTransaction.upsert_nodes
upsert_nodes(label:str, nodes:Sequence[NodeRecord], *, batch_size:int = 256) -> None

Write or merge nodes inside the surrounding transaction.

agrag.graphdb.build_graph_store

build_graph_store(value:GraphStoreName | GraphStore) -> GraphStore

Build a graph store from a backend name, or return one unchanged.

Parameters:

Returns:

agrag.graphdb.errors

Errors that the graph-store layer raises.

Classes:

agrag.graphdb.errors.GraphStoreAliasConflictError
GraphStoreAliasConflictError(conflicts:dict[str, str]) -> None

Bases: GraphStoreConstraintViolationError

A merge-key alias a merge tried to claim already names another entity.

Unlike the base class, this is not surfaced by the backend's own uniqueness constraint -- claiming an already-owned alias is a silent no-op at the database level (see upsert_merge_alias_query) -- so apply_merge detects it itself from the claim's own return rows and raises this instead. For example, one writer creates a canonical entity named "Bob" while a concurrent writer separately resolves "Bob" as an accepted alias of a different canonical entity named "Robert": neither writer's own node merge_key collides, so recovery must come from here, not from a constraint violation.

Attributes:

  • conflicts – Every accepted merge_key this claim found already owned, mapped to the entity id that owns it.
agrag.graphdb.errors.GraphStoreAliasConflictError.conflicts
conflicts = conflicts
agrag.graphdb.errors.GraphStoreConstraintViolationError

Bases: GraphStoreError

A write violated a uniqueness constraint the backend enforces.

Raised instead of letting the backend's own driver exception propagate, so callers can recognize this specific case -- for example, two concurrent writers both missing an exact-match lookup and racing to create the same merge_key -- and recover by re-resolving to whichever write landed first, rather than treating it as a fatal error.

agrag.graphdb.errors.GraphStoreDataIntegrityError

Bases: GraphStoreError

A read found the graph store in a state its own invariants forbid.

Raised when persisted data cannot be trusted at face value -- for example a merged_into tombstone chain that cycles, points at a missing node, or runs past its expected bound without reaching a live node. Returning the last-seen data in these cases would let a caller silently act on a tombstone instead of the entity it was absorbed into.

agrag.graphdb.errors.GraphStoreError

Bases: Exception

The base class for every graph-store error.

agrag.graphdb.errors.GraphStoreMissingExtraError
GraphStoreMissingExtraError(extra:str) -> None

Bases: GraphStoreError

A graph store exists, but its package extra is not installed.

Attributes:

  • extra – The name of the package extra to install.
agrag.graphdb.errors.GraphStoreMissingExtraError.extra
extra = extra

agrag.graphdb.neo4j

Neo4j graph-store backend.

Classes:

  • Neo4jGraphStore – A GraphStore backed by Neo4j, using native vector indexes.
agrag.graphdb.neo4j.Neo4jGraphStore
Neo4jGraphStore(*, settings:Neo4jSettings | None = None, driver:AsyncDriver | None = None) -> None

Bases: GraphStore

A GraphStore backed by Neo4j, using native vector indexes.

The driver connects lazily on first use, so constructing the store does not open a network connection. execute_read/execute_write wrap the driver's managed transactions with no added retry loop, per ADR 0027.

Functions:

  • close – Close the driver, releasing its connection pool.
  • connect – Open the driver and verify connectivity.
  • ensure_vector_index – Create a native vector index if it does not exist.
  • execute_read – Run a read transaction and return its rows.
  • execute_write – Run a write transaction and return its rows.
  • register_labels – Add labels to this instance's known-label set.
  • register_relation_types – Add types to this instance's known-relation-type set.
  • session – Open a session to the configured database.
  • setup_constraints – Create a uniqueness constraint on id for every known label.
  • setup_indexes – Create a range index on id for every known label.
  • transaction – Open one Neo4j explicit transaction spanning multiple writes.
  • upsert_nodes – Write or merge nodes, honoring each record's full label set.
  • upsert_relations – Write or merge relationships between existing nodes.
  • vector_search – Search nodes by dense vector using the native vector index.

Parameters:

  • settings (Neo4jSettings | None) – Neo4j connection settings. Defaults to Neo4jSettings().
  • driver (AsyncDriver | None) – A pre-built AsyncDriver, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
agrag.graphdb.neo4j.Neo4jGraphStore.close
close() -> None

Close the driver, releasing its connection pool.

agrag.graphdb.neo4j.Neo4jGraphStore.connect
connect() -> None

Open the driver and verify connectivity.

agrag.graphdb.neo4j.Neo4jGraphStore.ensure_vector_index
ensure_vector_index(*, label:str, vector_property:str, dimensions:int, distance:Distance) -> None

Create a native vector index if it does not exist.

agrag.graphdb.neo4j.Neo4jGraphStore.execute_read
execute_read(query:str, parameters:Mapping[str, Any] | None = None, *, timeout:float | None = None) -> list[dict[str, Any]]

Run a read transaction and return its rows.

Parameters:

  • query (str) – The Cypher query to run.
  • parameters (Mapping[str, Any] | None) – The query parameters.
  • timeout (float | None) – Server-side transaction timeout in seconds, applied through the driver's unit_of_work so the database terminates the transaction when it runs longer. None uses the server's default timeout.

Returns:

agrag.graphdb.neo4j.Neo4jGraphStore.execute_write
execute_write(query:str, parameters:Mapping[str, Any] | None = None) -> list[dict[str, Any]]

Run a write transaction and return its rows.

Raises:

  • GraphStoreConstraintViolationError – The write violated a uniqueness constraint, translated from the driver's own exception so callers do not need a hard dependency on it.
agrag.graphdb.neo4j.Neo4jGraphStore.register_labels
register_labels(labels:Sequence[str]) -> None

Add labels to this instance's known-label set.

Parameters:

  • labels (Sequence[str]) – The labels to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any label is not a safe identifier.
agrag.graphdb.neo4j.Neo4jGraphStore.register_relation_types
register_relation_types(types:Sequence[str]) -> None

Add types to this instance's known-relation-type set.

Parameters:

  • types (Sequence[str]) – The relationship types to register. Each must be a safe Cypher identifier.

Raises:

  • ValueError – Any type is not a safe identifier.
agrag.graphdb.neo4j.Neo4jGraphStore.session
session() -> AbstractAsyncContextManager[Any]

Open a session to the configured database.

agrag.graphdb.neo4j.Neo4jGraphStore.setup_constraints
setup_constraints() -> None

Create a uniqueness constraint on id for every known label.

"Known" means written by this instance or already present in the database, so a fresh store can set up constraints for an existing database without first rewriting every record. Also creates the global uniqueness constraint on NODE_IDENTITY_LABEL that upsert_node_query's MERGE relies on to resolve a node by id regardless of its other, mutable labels, and a per-type uniqueness constraint on id for every known relationship type, which backs the stale-relationship cleanup upsert_relation_query performs on endpoint changes.

agrag.graphdb.neo4j.Neo4jGraphStore.setup_indexes
setup_indexes() -> None

Create a range index on id for every known label.

"Known" means written by this instance or already present in the database, so a fresh store can set up indexes for an existing database without first rewriting every record.

agrag.graphdb.neo4j.Neo4jGraphStore.transaction
transaction() -> AsyncIterator[GraphStoreTransaction]

Open one Neo4j explicit transaction spanning multiple writes.

Commits when the async with block exits cleanly; rolls back and re-raises when it raises. The identity constraint is ensured before opening the transaction, the same way upsert_nodes ensures it before writing, since _Neo4jTransaction.upsert_nodes skips that check to avoid a nested write racing the transaction it belongs to.

Raises:

  • GraphStoreConstraintViolationError – A write inside the block, or the commit itself, violated a uniqueness constraint -- Neo4j validates some constraints only at commit time for an explicit transaction, so this can surface here even when every individual write appeared to succeed.
agrag.graphdb.neo4j.Neo4jGraphStore.upsert_nodes
upsert_nodes(label:str, nodes:Sequence[NodeRecord], *, batch_size:int = 256) -> None

Write or merge nodes, honoring each record's full label set.

label names the batch for constraint/index bookkeeping, matching every other tracked label; the labels actually written to a node come from NodeRecord.labels, which may name more than one label (for example a node that is both Chunk and Entity). Records with different label sets are grouped and written with separate MERGE queries, since Cypher requires labels to be literal in the query text rather than a runtime parameter, so batch_size chunks apply within each group rather than across the whole call.

Raises:

agrag.graphdb.neo4j.Neo4jGraphStore.upsert_relations
upsert_relations(relations:Sequence[RelationRecord], *, batch_size:int = 256) -> None

Write or merge relationships between existing nodes.

Relationship identity is each record's id, not its endpoints: see upsert_relation_query for how endpoint changes and same-id parallel relationships are handled.

Raises:

vector_search(*, label:str, vector_property:str, query_vector:Sequence[float], limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search nodes by dense vector using the native vector index.

A label with no provisioned vector index has nothing to search, so an absent index returns an empty result list rather than a driver error.

When filters is set, Neo4j's vector procedure applies the filter only after selecting its top k candidates, so a plain k=limit call can return fewer matches than actually exist. This escalates k and retries until limit filtered hits come back or the escalation reaches _VECTOR_SEARCH_MAX_K.

Raises:

  • ValueErrorlimit is not positive. A non-positive value is not a meaningful request and would send that same non-positive k to Neo4j's native vector procedure, which requires a positive top-k.

agrag.graphdb.serialize

Convert graph records into Neo4j-driver-friendly parameters.

Functions:

  • node_params – Build the $records entry for a node upsert.
  • relation_params – Build the $records entry for a relationship upsert.
agrag.graphdb.serialize.node_params
node_params(record:NodeRecord) -> dict[str, Any]

Build the $records entry for a node upsert.

Parameters:

  • record (NodeRecord) – The node record to serialize.

Returns:

  • dict[str, Any] – A dict with id (string) and properties (converted).
agrag.graphdb.serialize.relation_params
relation_params(record:RelationRecord) -> dict[str, Any]

Build the $records entry for a relationship upsert.

Parameters:

Returns:

  • dict[str, Any] – A dict with id, start_id, end_id, and properties.

agrag.graphdb.settings

Settings for the Neo4j graph-store backend.

Classes:

agrag.graphdb.settings.Neo4jSettings

Bases: BaseSettings

Neo4j connection configuration.

Attributes:

  • uri (str) – The Bolt connection URI, including scheme (neo4j+s:// for Aura). Env: NEO4J_URI.
  • username (str) – The database username. Env: NEO4J_USERNAME.
  • password (SecretStr) – The database password. Env: NEO4J_PASSWORD.
  • database (str) – The target database name. Env: NEO4J_DATABASE.
  • max_connection_lifetime (int) – The maximum seconds a pooled connection lives, kept well below Aura's roughly five-minute idle timeout. Env: NEO4J_MAX_CONNECTION_LIFETIME.

Raises:

  • ValueErroruri is plaintext (bolt:// or neo4j://) and points at a non-local host. Neo4j always authenticates with a password, so a plaintext scheme always sends it in the clear; use neo4j+s:// (or bolt+s://) for a remote instance.
agrag.graphdb.settings.Neo4jSettings.database
database: str = 'neo4j'
agrag.graphdb.settings.Neo4jSettings.max_connection_lifetime
max_connection_lifetime: int = 240
agrag.graphdb.settings.Neo4jSettings.model_config
model_config = SettingsConfigDict(env_prefix='NEO4J_', env_file='.env', extra='ignore')
agrag.graphdb.settings.Neo4jSettings.password
password: SecretStr = SecretStr('neo4j')
agrag.graphdb.settings.Neo4jSettings.uri
uri: str = 'bolt://localhost:7687'
agrag.graphdb.settings.Neo4jSettings.username
username: str = 'neo4j'

agrag.ingestion

The ingestion package.

Modules:

  • extract – The Extractor interface: reads one Chunk and produces an ExtractionResult.
  • graph – The public Graph API for ingestion.
  • merge – Merge mechanics: computing how a resolved group of mentions and entities combine.
  • resolve – Entity resolution: deciding which ExtractedEntity mentions are the same thing.
  • types – Graph.add()'s result and per-stage observability types.

Classes:

  • Graph – A knowledge graph that a caller can open and add content to.

agrag.ingestion.Graph

Graph(*, schema:GraphSchema, graph_store:GraphStore, embedder:Embedder, extractor:Extractor, tracer:Tracer | None = None) -> None

A knowledge graph that a caller can open and add content to.

Functions:

  • add – Add content to the graph.
  • consolidate – Run full tiered resolution against everything persisted.
  • open – Open a graph, connecting and fully provisioning graph_store.

Parameters:

  • schema (GraphSchema) – The entity/relation types this graph validates every extraction against.
  • graph_store (GraphStore) – Where entities, relations, chunks, and MENTIONED_IN edges are written.
  • embedder (Embedder) – Populates entity embeddings for native vector search.
  • extractor (Extractor) – Runs against each chunk.
  • tracer (Tracer | None) – A tracer to record spans for every step. Pass None for none.
agrag.ingestion.Graph.add
add(source:SourcesType | None = None, *, text:str | None = None, documents:Sequence[Document] | None = None, loader:Loader | None = None, error_policy:ErrorPolicy = ErrorPolicy.RAISE, on_progress:Callable[[AddResult], None] | None = None, return_chunks:bool = False) -> AddResult

Add content to the graph.

Give exactly one of source, text, and documents.

Parameters:

  • source (SourcesType | None) – A file path, a directory, a glob, or a list of these.
  • text (str | None) – Raw text to add as one document.
  • documents (Sequence[Document] | None) – Already-built documents to add directly.
  • loader (Loader | None) – A loader to use instead of the registry default. Requires a single-file source; a directory, glob, or list of sources raises an error.
  • error_policy (ErrorPolicy) – The action to take on a per-source error.
  • on_progress (Callable[[AddResult], None] | None) – A callback the call runs after each batch and once more at the end with the fully-populated result.
  • return_chunks (bool) – Whether to include the produced chunks in the returned AddResult. False by default to avoid holding full text for a large corpus when not needed.

Returns:

  • AddResult – A summary of what was added per pipeline stage.

Raises:

  • ValueError – The call got zero, or more than one, of source, text, and documents. Also raised when loader is set without source, or with a source that can match more than one file.
  • UnsupportedFormatError – No loader is registered for a source's format.
  • MissingExtraError – A loader is registered for a source's format, but its package extra is not installed. This error follows error_policy instead of always stopping the call.
agrag.ingestion.Graph.consolidate
consolidate(*, apply:bool = False) -> ConsolidationReport

Run full tiered resolution against everything persisted.

Dry-run by default: produces a report of what would merge before any node is touched. Pass apply=True to write the merges.

For each EntityType label in self._schema, fetches every persisted entity with that label and runs the same comparator sequence add() uses in-batch (ExactMatch, FuzzyMatch, LLMVerify) pairwise across all of them — O(n^2) within each label's population. Confirmed matches become MergePlans via compute_merge.

Parameters:

  • apply (bool) – Write the computed merges. False produces a report only.

Returns:

agrag.ingestion.Graph.open
open(*, schema:GraphSchema, graph_store:GraphStore, embedder:Embedder, extractor:Extractor, tracer:Tracer | None = None) -> Graph

Open a graph, connecting and fully provisioning graph_store.

Provisioning order: connect, then register every label/relation type this graph will ever write (schema's own labels/types plus the fixed system names CHUNK_LABEL/SYSTEM_RELATION_TYPES), then setup_constraints(), then setup_indexes(), then vector indexes for every schema entity label — so a brand-new database is fully ready, including the merge_key index the global exact-match tier needs and the embedding vector indexes native search needs, before this call returns.

Parameters:

  • schema (GraphSchema) – The entity/relation types this graph validates every extraction against.
  • graph_store (GraphStore) – Where entities, relations, chunks, and MENTIONED_IN edges are written.
  • embedder (Embedder) – Populates entity embeddings for native vector search.
  • extractor (Extractor) – Runs against each chunk.
  • tracer (Tracer | None) – A tracer to record spans for every step. Pass None for none.

Returns:

  • Graph – A graph connected to graph_store and ready to accept add() calls.

Raises:

  • Exception – Whatever connect(), registration, constraint/index setup, or vector-index provisioning raises. graph_store is closed first, so a failed open() never leaks a connection.

agrag.ingestion.extract

The Extractor interface: reads one Chunk and produces an ExtractionResult.

Classes:

  • BAMLExtractor – Extracts with an LLM, via a BAML function and a runtime ClientRegistry.
  • EscalatingExtractor – Runs a cheap primary extractor first, escalating per chunk when it's weak.
  • ExtractionLLMSettings – Env-backed LLM client config for the extraction role.
  • Extractor – Reads one Chunk and produces the entities and relations it contains.
  • ExtractorMissingExtraError – An Extractor needs a package extra that is not installed.
  • GlinerExtractor – Extracts locally with a GLiNER2.5 model. No network call.
agrag.ingestion.extract.BAMLExtractor
BAMLExtractor(*, settings:ExtractionLLMSettings | None = None, client:object | None = None) -> None

Bases: Extractor

Extracts with an LLM, via a BAML function and a runtime ClientRegistry.

Functions:

  • extract – Extract with an LLM call through the configured ClientRegistry.

Attributes:

Parameters:

  • settings (ExtractionLLMSettings | None) – LLM client config. Defaults to ExtractionLLMSettings(), loaded from the environment/.env. Ignored when client is given: an injected client also disables settings.retry, since a caller building its own client is assumed to own its own retry behavior too.
  • client (object | None) – An already-built BAML client object exposing ExtractEntitiesAndRelations. Tests inject a fake here.
agrag.ingestion.extract.BAMLExtractor.extract
extract(chunk:Chunk, schema:GraphSchema) -> ExtractionResult

Extract with an LLM call through the configured ClientRegistry.

Raises:

agrag.ingestion.extract.BAMLExtractor.settings
settings = settings
agrag.ingestion.extract.EscalatingExtractor
EscalatingExtractor(primary:Extractor, escalate_to:Extractor, *, min_confidence:float = 0.5, min_chunk_words:int = 8) -> None

Bases: Extractor

Runs a cheap primary extractor first, escalating per chunk when it's weak.

Functions:

  • extract – Extract with the primary extractor, escalating when it's weak.

Attributes:

Parameters:

  • primary (Extractor) – Runs first, for every chunk.
  • escalate_to (Extractor) – Runs instead of, never in addition to, the primary's result, when escalation triggers. Merging both extractors' output would mean reconciling overlapping spans between them, which is what entity resolution is for, not extraction.
  • min_confidence (float) – Escalate when the primary's mean entity confidence falls below this, among entities that report a confidence.
  • min_chunk_words (int) – Below this word count, a zero-entity result from the primary is treated as plausibly correct, not a miss.
agrag.ingestion.extract.EscalatingExtractor.escalate_to
escalate_to = escalate_to
agrag.ingestion.extract.EscalatingExtractor.extract
extract(chunk:Chunk, schema:GraphSchema) -> ExtractionResult

Extract with the primary extractor, escalating when it's weak.

Returns escalate_to's result outright when escalation triggers, never a combination of both extractors' results.

agrag.ingestion.extract.EscalatingExtractor.min_chunk_words
min_chunk_words = min_chunk_words
agrag.ingestion.extract.EscalatingExtractor.min_confidence
min_confidence = min_confidence
agrag.ingestion.extract.EscalatingExtractor.primary
primary = primary
agrag.ingestion.extract.ExtractionLLMSettings

Bases: BaseSettings

Env-backed LLM client config for the extraction role.

Attributes:

  • clients (list[LLMClientConfig]) – The LLM client(s) to use. One element for a single provider; more than one composed per strategy.
  • strategy (Literal['single', 'fallback', 'round_robin']) – How to compose multiple clients. Ignored with one client.
  • retry (RetryConfig) – Retry settings applied to the extraction LLM call.

Env prefix: EXTRACTION_LLM_.

Functions:

agrag.ingestion.extract.ExtractionLLMSettings.clients
clients: list[LLMClientConfig]
agrag.ingestion.extract.ExtractionLLMSettings.from_openai_compatible_env
from_openai_compatible_env() -> ExtractionLLMSettings

Build settings from a generic OpenAI-compatible endpoint.

Reads LLM_BASE_URL, LLM_API_KEY, and LLM_MODEL_ID from the environment or .env, so the model name is never hardcoded. Raises RuntimeError when the required variables are not all set.

Returns:

agrag.ingestion.extract.ExtractionLLMSettings.model_config
model_config = SettingsConfigDict(env_prefix='EXTRACTION_LLM_', env_file='.env', extra='ignore')
agrag.ingestion.extract.ExtractionLLMSettings.retry
retry: RetryConfig = Field(default_factory=RetryConfig)
agrag.ingestion.extract.ExtractionLLMSettings.strategy
strategy: Literal['single', 'fallback', 'round_robin'] = 'single'
agrag.ingestion.extract.Extractor

Bases: ABC

Reads one Chunk and produces the entities and relations it contains.

Functions:

  • extract – Extract entities and relations from one chunk.
agrag.ingestion.extract.Extractor.extract
extract(chunk:Chunk, schema:GraphSchema) -> ExtractionResult

Extract entities and relations from one chunk.

Parameters:

  • chunk (Chunk) – The chunk to read. Only chunk.text and chunk.id are used.
  • schema (GraphSchema) – The entity/relation types to extract. Every returned entity's label and relation's label must be declared in this schema.

Returns:

  • ExtractionResult – The entities and relations this call found, in extraction order.
agrag.ingestion.extract.ExtractorMissingExtraError
ExtractorMissingExtraError(component:str, extra:str) -> None

Bases: IngestionError

An Extractor needs a package extra that is not installed.

Attributes:

  • component – The class name that needs the extra.
  • extra – The package extra to install.
agrag.ingestion.extract.ExtractorMissingExtraError.component
component = component
agrag.ingestion.extract.ExtractorMissingExtraError.extra
extra = extra
agrag.ingestion.extract.GlinerExtractor
GlinerExtractor(*, model_name:str = 'fastino/gliner2.5-small-v1', model:object | None = None) -> None

Bases: Extractor

Extracts locally with a GLiNER2.5 model. No network call.

Functions:

  • extract – Extract with the local GLiNER2.5 model.

Attributes:

Parameters:

  • model_name (str) – The checkpoint to load if model is not given.
  • model (object | None) – An already-built GLiNER2.5 model. Tests inject a fake here to avoid a real model download.
agrag.ingestion.extract.GlinerExtractor.extract
extract(chunk:Chunk, schema:GraphSchema) -> ExtractionResult

Extract with the local GLiNER2.5 model.

Raises:

agrag.ingestion.extract.GlinerExtractor.model_name
model_name = model_name

agrag.ingestion.graph

The public Graph API for ingestion.

Classes:

  • Graph – A knowledge graph that a caller can open and add content to.

Attributes:

agrag.ingestion.graph.Graph
Graph(*, schema:GraphSchema, graph_store:GraphStore, embedder:Embedder, extractor:Extractor, tracer:Tracer | None = None) -> None

A knowledge graph that a caller can open and add content to.

Functions:

  • add – Add content to the graph.
  • consolidate – Run full tiered resolution against everything persisted.
  • open – Open a graph, connecting and fully provisioning graph_store.

Parameters:

  • schema (GraphSchema) – The entity/relation types this graph validates every extraction against.
  • graph_store (GraphStore) – Where entities, relations, chunks, and MENTIONED_IN edges are written.
  • embedder (Embedder) – Populates entity embeddings for native vector search.
  • extractor (Extractor) – Runs against each chunk.
  • tracer (Tracer | None) – A tracer to record spans for every step. Pass None for none.
agrag.ingestion.graph.Graph.add
add(source:SourcesType | None = None, *, text:str | None = None, documents:Sequence[Document] | None = None, loader:Loader | None = None, error_policy:ErrorPolicy = ErrorPolicy.RAISE, on_progress:Callable[[AddResult], None] | None = None, return_chunks:bool = False) -> AddResult

Add content to the graph.

Give exactly one of source, text, and documents.

Parameters:

  • source (SourcesType | None) – A file path, a directory, a glob, or a list of these.
  • text (str | None) – Raw text to add as one document.
  • documents (Sequence[Document] | None) – Already-built documents to add directly.
  • loader (Loader | None) – A loader to use instead of the registry default. Requires a single-file source; a directory, glob, or list of sources raises an error.
  • error_policy (ErrorPolicy) – The action to take on a per-source error.
  • on_progress (Callable[[AddResult], None] | None) – A callback the call runs after each batch and once more at the end with the fully-populated result.
  • return_chunks (bool) – Whether to include the produced chunks in the returned AddResult. False by default to avoid holding full text for a large corpus when not needed.

Returns:

  • AddResult – A summary of what was added per pipeline stage.

Raises:

  • ValueError – The call got zero, or more than one, of source, text, and documents. Also raised when loader is set without source, or with a source that can match more than one file.
  • UnsupportedFormatError – No loader is registered for a source's format.
  • MissingExtraError – A loader is registered for a source's format, but its package extra is not installed. This error follows error_policy instead of always stopping the call.
agrag.ingestion.graph.Graph.consolidate
consolidate(*, apply:bool = False) -> ConsolidationReport

Run full tiered resolution against everything persisted.

Dry-run by default: produces a report of what would merge before any node is touched. Pass apply=True to write the merges.

For each EntityType label in self._schema, fetches every persisted entity with that label and runs the same comparator sequence add() uses in-batch (ExactMatch, FuzzyMatch, LLMVerify) pairwise across all of them — O(n^2) within each label's population. Confirmed matches become MergePlans via compute_merge.

Parameters:

  • apply (bool) – Write the computed merges. False produces a report only.

Returns:

agrag.ingestion.graph.Graph.open
open(*, schema:GraphSchema, graph_store:GraphStore, embedder:Embedder, extractor:Extractor, tracer:Tracer | None = None) -> Graph

Open a graph, connecting and fully provisioning graph_store.

Provisioning order: connect, then register every label/relation type this graph will ever write (schema's own labels/types plus the fixed system names CHUNK_LABEL/SYSTEM_RELATION_TYPES), then setup_constraints(), then setup_indexes(), then vector indexes for every schema entity label — so a brand-new database is fully ready, including the merge_key index the global exact-match tier needs and the embedding vector indexes native search needs, before this call returns.

Parameters:

  • schema (GraphSchema) – The entity/relation types this graph validates every extraction against.
  • graph_store (GraphStore) – Where entities, relations, chunks, and MENTIONED_IN edges are written.
  • embedder (Embedder) – Populates entity embeddings for native vector search.
  • extractor (Extractor) – Runs against each chunk.
  • tracer (Tracer | None) – A tracer to record spans for every step. Pass None for none.

Returns:

  • Graph – A graph connected to graph_store and ready to accept add() calls.

Raises:

  • Exception – Whatever connect(), registration, constraint/index setup, or vector-index provisioning raises. graph_store is closed first, so a failed open() never leaks a connection.
agrag.ingestion.graph.SYSTEM_RELATION_TYPES
SYSTEM_RELATION_TYPES = ['MENTIONED_IN']
agrag.ingestion.graph.SourceType
SourceType = Union[str, Path]
agrag.ingestion.graph.SourcesType
SourcesType = Union[SourceType, Sequence[SourceType]]

agrag.ingestion.merge

Merge mechanics: computing how a resolved group of mentions and entities combine.

This module is storage-agnostic: it decides what a merge should look like, but never touches GraphStore itself. Applying a computed MergePlan is a separate step.

Classes:

  • ConflictRecord – One property that had more than one candidate value.
  • MergePlan – Computed result of merging zero or more entities and mentions.
  • PropertyRules – Per-property conflict resolution, with a default for unlisted properties.
  • PropertyStrategy – Fallback rule for a property with no entry in PropertyRules.

Functions:

  • apply_merge – Write a computed MergePlan to storage.
  • compute_merge – Compute how existing_entities and mentions combine into one Entity.
  • mentioned_in_id – Return the deterministic id for a new Chunk -[:MENTIONED_IN]-> Entity edge.
  • relation_id – Return the deterministic id for a domain relationship triple.

Attributes:

agrag.ingestion.merge.ConflictRecord

Bases: BaseModel

One property that had more than one candidate value.

Attributes:

agrag.ingestion.merge.ConflictRecord.candidates
candidates: list[object]
agrag.ingestion.merge.ConflictRecord.field
field: str
agrag.ingestion.merge.ConflictRecord.resolved
resolved: object
agrag.ingestion.merge.MergePlan

Bases: BaseModel

Computed result of merging zero or more entities and mentions.

Attributes:

  • survivor (Entity) – The resulting Entity. Its merge_count, source_chunk_ids, and merged_from are this call's best local computation, for reporting; apply_merge writes new_source_chunk_ids and merge_count_delta atomically instead, so a concurrent writer's own contribution to the same node is never overwritten.
  • tombstone_ids (list[UUID]) – Ids of entities absorbed into survivor. Also this call's new contribution to the survivor's merged_from, applied atomically.
  • conflicts (list[ConflictRecord]) – Every field that had more than one candidate value.
  • accepted_merge_keys (list[str]) – Every normalized merge_key this merge accepted -- from existing_entities and mentions alike, not only the survivor's own chosen name -- so a later mention of any accepted name resolves back to this entity instead of creating a duplicate.
  • new_source_chunk_ids (list[UUID]) – The chunk ids this call's mentions and absorbed entities contribute, applied as an atomic union against whatever the survivor's node currently has.
  • merge_count_delta (int) – The amount to atomically add to whatever merge_count the survivor's node currently has.
agrag.ingestion.merge.MergePlan.accepted_merge_keys
accepted_merge_keys: list[str] = []
agrag.ingestion.merge.MergePlan.conflicts
conflicts: list[ConflictRecord] = []
agrag.ingestion.merge.MergePlan.merge_count_delta
merge_count_delta: int = 0
agrag.ingestion.merge.MergePlan.new_source_chunk_ids
new_source_chunk_ids: list[UUID] = []
agrag.ingestion.merge.MergePlan.survivor
survivor: Entity
agrag.ingestion.merge.MergePlan.tombstone_ids
tombstone_ids: list[UUID] = []
agrag.ingestion.merge.PropertyRule
PropertyRule = Callable[[list[object]], object]

Per-property conflict resolver.

Takes every candidate value for one property, in encounter order, already filtered to exclude None, and returns the resolved value.

agrag.ingestion.merge.PropertyRules
PropertyRules(rules:dict[str, PropertyRule] = dict(), default:PropertyStrategy = PropertyStrategy.KEEP_FIRST) -> None

Per-property conflict resolution, with a default for unlisted properties.

Attributes:

agrag.ingestion.merge.PropertyRules.default
default: PropertyStrategy = PropertyStrategy.KEEP_FIRST
agrag.ingestion.merge.PropertyRules.rules
rules: dict[str, PropertyRule] = field(default_factory=dict)
agrag.ingestion.merge.PropertyStrategy

Bases: StrEnum

Fallback rule for a property with no entry in PropertyRules.

Attributes:

agrag.ingestion.merge.PropertyStrategy.KEEP_FIRST
KEEP_FIRST = 'keep_first'
agrag.ingestion.merge.PropertyStrategy.KEEP_LAST
KEEP_LAST = 'keep_last'
agrag.ingestion.merge.PropertyStrategy.MERGE_ALL
MERGE_ALL = 'merge_all'
agrag.ingestion.merge.apply_merge
apply_merge(plan:MergePlan, *, graph_store:GraphStore, schema:GraphSchema) -> None

Write a computed MergePlan to storage.

Every call runs inside one GraphStore transaction: when tombstone_ids is non-empty, it first clears merge_key from every entity about to be absorbed, since canonical selection can pick a different node as survivor than the one a property rule (e.g. KEEP_FIRST) resolves the name from, so the survivor's resolved merge_key can equal a still-live tombstone's own merge_key -- writing the survivor before clearing that would collide with the per-label merge_key uniqueness constraint. It then always upserts the survivor and records a merge-key alias for its current name, and, when tombstone_ids is non-empty, also tombstones, deletes edges that would become meaningless self-links, transfers what remains, and dedupes the survivor's resulting neighbourhood. A failure partway through leaves no half-written state: no survivor without its alias, no tombstone without its edges transferred, no transferred edge without its duplicate cleaned up.

Parameters:

  • plan (MergePlan) – The merge to write.
  • graph_store (GraphStore) – Where the merge is written.
  • schema (GraphSchema) – The schema the survivor's label belongs to.

Raises:

  • GraphStoreAliasConflictError – An accepted merge_key is already owned by a live entity outside this merge's own survivor and tombstone ids -- a concurrent writer accepted that name as an alias of, or created it as the canonical name of, a different entity.
  • GraphStoreDataIntegrityError – A candidate conflicting alias owner's merged_into chain cycles, points at a missing node, or does not reach a live node within the hop limit.
agrag.ingestion.merge.compute_merge
compute_merge(*, existing_entities:list[Entity], mentions:list[ExtractedEntity], schema:GraphSchema, rules:PropertyRules | None = None, description_settings:Any | None = None, description_client:Any | None = None) -> tuple[MergePlan, list[Any]]

Compute how existing_entities and mentions combine into one Entity.

No storage is touched. Zero existing entities produces a brand-new Entity. One produces an updated copy folding in the mentions. Two or more picks a canonical survivor and marks the rest for tombstoning.

Parameters:

  • existing_entities (list[Entity]) – Already-persisted entities this call reconciles.
  • mentions (list[ExtractedEntity]) – Fresh ExtractedEntity mentions to fold in.
  • schema (GraphSchema) – Used to look up the entity type's declared properties for the canonical-id schema-completeness check.
  • rules (PropertyRules | None) – Per-property conflict resolution. Defaults to keep_first.
  • description_settings (Any | None) – LLM settings for description summarization.
  • description_client (Any | None) – Injected LLM client for tests.

Returns:

Raises:

  • ValueError – existing_entities and mentions are both empty, or their labels disagree.
agrag.ingestion.merge.mentioned_in_id
mentioned_in_id(chunk_id:UUID, entity_id:UUID) -> UUID

Return the deterministic id for a new Chunk -[:MENTIONED_IN]-> Entity edge.

Only a fresh id for a pair with no persisted edge yet is guaranteed to equal this. An entity merge can transfer an existing edge onto a new entity id while keeping its old id (see transfer_relationships_query), so a caller writing to an already-persisted pair should look up the edge by its endpoints first and fall back to this id only when none is found.

Parameters:

  • chunk_id (UUID) – The Chunk's id.
  • entity_id (UUID) – The Entity's id.

Returns:

  • UUID – The edge id. Deterministic: same pair always returns same id.
agrag.ingestion.merge.relation_id
relation_id(source_id:UUID, target_id:UUID, rel_type:str) -> UUID

Return the deterministic id for a domain relationship triple.

Two concurrent add() calls resolving the same (source_id, target_id, rel_type) triple can both miss the existing-relation lookup and each try to create it; since this id depends only on the triple, both writers compute the same one, so upsert_relation_query's MERGE converges to a single edge instead of two parallel ones with unrelated random ids. Mirrors mentioned_in_id.

Parameters:

  • source_id (UUID) – The relationship's source Entity id.
  • target_id (UUID) – The relationship's target Entity id.
  • rel_type (str) – The relationship's type.

Returns:

  • UUID – The relationship id. Same triple always returns the same id.

agrag.ingestion.resolve

Entity resolution: deciding which ExtractedEntity mentions are the same thing.

Classes:

  • CandidateSource – Narrows which entity pairs resolution compares — the blocking step.
  • Comparator – One matching strategy a Resolver runs against a candidate pair.
  • ComparisonVerdict – A Comparator's verdict on one entity pair.
  • ExactMatch – Matches when normalized text is identical. Never returns NO_MATCH.
  • FuzzyMatch – Matches by string similarity, within a confident-match/distinct band.
  • InBatchCandidateSource – Blocks by label: only entities sharing a label are ever compared.
  • LLMVerify – Asks an LLM to verify an ambiguous pair. Last resort; never UNCERTAIN.
  • ResolutionGroup – One set of ExtractedEntity indices resolution decided are the same entity.
  • Resolver – Runs an ordered comparator sequence over blocked candidate pairs.
agrag.ingestion.resolve.CandidateSource

Bases: ABC

Narrows which entity pairs resolution compares — the blocking step.

Functions:

  • candidates_for – Return indices worth comparing against entities[index].
agrag.ingestion.resolve.CandidateSource.candidates_for
candidates_for(index:int, entities:list[ExtractedEntity]) -> list[int]

Return indices worth comparing against entities[index].

Parameters:

  • index (int) – The entity to find candidates for.
  • entities (list[ExtractedEntity]) – The full entity list this call is scoped to.

Returns:

  • list[int] – Indices into entities, excluding index itself. Order does
  • list[int] – not matter; duplicates are harmless but wasteful.
agrag.ingestion.resolve.Comparator

Bases: ABC

One matching strategy a Resolver runs against a candidate pair.

Functions:

  • compare – Compare two entities.
agrag.ingestion.resolve.Comparator.compare
compare(a:ExtractedEntity, b:ExtractedEntity) -> ComparisonVerdict

Compare two entities.

Parameters:

Returns:

  • ComparisonVerdict – This comparator's verdict. UNCERTAIN defers to the next comparator.
agrag.ingestion.resolve.ComparisonVerdict

Bases: StrEnum

A Comparator's verdict on one entity pair.

Attributes:

  • MATCH – The comparator is confident these are the same entity.
  • NO_MATCH – The comparator is confident these are different entities.
  • UNCERTAIN – This comparator can't decide; the next one gets a turn.
agrag.ingestion.resolve.ComparisonVerdict.MATCH
MATCH = 'match'
agrag.ingestion.resolve.ComparisonVerdict.NO_MATCH
NO_MATCH = 'no_match'
agrag.ingestion.resolve.ComparisonVerdict.UNCERTAIN
UNCERTAIN = 'uncertain'
agrag.ingestion.resolve.ExactMatch

Bases: Comparator

Matches when normalized text is identical. Never returns NO_MATCH.

Functions:

  • compare – Return MATCH on identical normalized text, else UNCERTAIN.
agrag.ingestion.resolve.ExactMatch.compare
compare(a:ExtractedEntity, b:ExtractedEntity) -> ComparisonVerdict

Return MATCH on identical normalized text, else UNCERTAIN.

agrag.ingestion.resolve.FuzzyMatch
FuzzyMatch(*, match_above:float = 0.92, no_match_below:float = 0.7) -> None

Bases: Comparator

Matches by string similarity, within a confident-match/distinct band.

Attributes:

  • match_above – A similarity score at or above this is a confident match.
  • no_match_below – A similarity score below this is a confident non-match. A score in between is UNCERTAIN and defers to the next comparator.

Functions:

  • compare – Return a verdict from token-sort-ratio similarity.
agrag.ingestion.resolve.FuzzyMatch.compare
compare(a:ExtractedEntity, b:ExtractedEntity) -> ComparisonVerdict

Return a verdict from token-sort-ratio similarity.

agrag.ingestion.resolve.FuzzyMatch.match_above
match_above = match_above
agrag.ingestion.resolve.FuzzyMatch.no_match_below
no_match_below = no_match_below
agrag.ingestion.resolve.InBatchCandidateSource

Bases: CandidateSource

Blocks by label: only entities sharing a label are ever compared.

Scoped to whatever entity list a caller passes to candidates_for — today, always the current extraction batch. A future graph-backed candidate source can replace this without changing any Comparator, since comparators only ever see the pairs a CandidateSource proposes.

Functions:

  • candidates_for – Return every other entity sharing entities[index]'s label.
agrag.ingestion.resolve.InBatchCandidateSource.candidates_for
candidates_for(index:int, entities:list[ExtractedEntity]) -> list[int]

Return every other entity sharing entities[index]'s label.

agrag.ingestion.resolve.LLMVerify
LLMVerify(*, chunks_by_id:dict[UUID, Chunk], settings:ExtractionLLMSettings | None = None, client:object | None = None) -> None

Bases: Comparator

Asks an LLM to verify an ambiguous pair. Last resort; never UNCERTAIN.

Never raises from an LLM-call failure: it resolves to NO_MATCH instead, by the same fail-safe design as every comparator a Resolver runs — an ambiguous or failed comparison never merges two entities. A missing package extra is a configuration error, not an ambiguous judgment call, and is raised outright instead (see compare's Raises section).

Functions:

  • compare – Return the LLM's verdict, or NO_MATCH if the call itself fails.

Attributes:

Parameters:

  • chunks_by_id (dict[UUID, Chunk]) – Maps a Chunk id to the Chunk, for prompt context.
  • settings (ExtractionLLMSettings | None) – LLM client config. Defaults to ExtractionLLMSettings(). Ignored when client is given: an injected client also disables settings.retry, since a caller building its own client is assumed to own its own retry behavior too.
  • client (object | None) – An already-built BAML client. Tests inject a fake here.
agrag.ingestion.resolve.LLMVerify.chunks_by_id
chunks_by_id = chunks_by_id
agrag.ingestion.resolve.LLMVerify.compare
compare(a:ExtractedEntity, b:ExtractedEntity) -> ComparisonVerdict

Return the LLM's verdict, or NO_MATCH if the call itself fails.

Raises:

agrag.ingestion.resolve.LLMVerify.settings
settings = settings
agrag.ingestion.resolve.ResolutionGroup

Bases: BaseModel

One set of ExtractedEntity indices resolution decided are the same entity.

Attributes:

  • entity_indices (list[int]) – Indices into the entity list passed to Resolver.resolve. A group of one means resolution found no match for that entity.
agrag.ingestion.resolve.ResolutionGroup.entity_indices
entity_indices: list[int]
agrag.ingestion.resolve.Resolver
Resolver(*, comparators:list[Comparator], candidate_source:CandidateSource) -> None

Runs an ordered comparator sequence over blocked candidate pairs.

Groups every pair a comparator confirms as a match into a ResolutionGroup.

Functions:

  • resolve – Group entities that resolution decided are the same thing.

Attributes:

Parameters:

  • comparators (list[Comparator]) – Tried in order per candidate pair. The first non-UNCERTAIN verdict wins; if every comparator is UNCERTAIN, the pair does not merge.
  • candidate_source (CandidateSource) – Narrows which pairs get compared at all.
agrag.ingestion.resolve.Resolver.candidate_source
candidate_source = candidate_source
agrag.ingestion.resolve.Resolver.comparators
comparators = comparators
agrag.ingestion.resolve.Resolver.resolve
resolve(entities:list[ExtractedEntity]) -> list[ResolutionGroup]

Group entities that resolution decided are the same thing.

Parameters:

  • entities (list[ExtractedEntity]) – The entities to resolve. Only entities passed in the same call are ever compared against each other — resolving against previously-resolved entities from an earlier call is not supported by this Resolver.

Returns:

agrag.ingestion.types

Graph.add()'s result and per-stage observability types.

Classes:

agrag.ingestion.types.AddResult

Bases: BaseModel

Graph.add()'s return type — one summary per pipeline stage.

Attributes:

agrag.ingestion.types.AddResult.chunks
chunks: list[Chunk] = Field(default_factory=list)
agrag.ingestion.types.AddResult.documents
documents: int

Proxy to ingestion.documents for backward compatibility.

agrag.ingestion.types.AddResult.extraction
extraction: ExtractionStats = Field(default_factory=ExtractionStats)
agrag.ingestion.types.AddResult.ingestion
ingestion: IngestStats = Field(default_factory=IngestStats)
agrag.ingestion.types.AddResult.merge
merge: MergeStats = Field(default_factory=MergeStats)
agrag.ingestion.types.AddResult.quarantined
quarantined: int

Proxy to ingestion.quarantined for backward compatibility.

agrag.ingestion.types.AddResult.quarantined_items
quarantined_items: list[StageFailure]

Proxy to ingestion.quarantined_items for backward compatibility.

agrag.ingestion.types.AddResult.resolution
resolution: ResolutionStats = Field(default_factory=ResolutionStats)
agrag.ingestion.types.AddResult.skipped
skipped: int

Proxy to ingestion.skipped for backward compatibility.

agrag.ingestion.types.AddResult.sources
sources: int

Proxy to ingestion.sources for backward compatibility.

agrag.ingestion.types.AddResult.storage
storage: StorageStats = Field(default_factory=StorageStats)
agrag.ingestion.types.ConsolidationReport

Bases: BaseModel

Report from Graph.consolidate().

Attributes:

agrag.ingestion.types.ConsolidationReport.applied
applied: bool = False
agrag.ingestion.types.ConsolidationReport.failures
failures: list[StageFailure] = Field(default_factory=list)
agrag.ingestion.types.ConsolidationReport.would_merge
would_merge: list[MergePlan] = Field(default_factory=list)
agrag.ingestion.types.ExtractionStats

Bases: BaseModel

Extraction-stage results.

Attributes:

agrag.ingestion.types.ExtractionStats.chunks_processed
chunks_processed: int = 0
agrag.ingestion.types.ExtractionStats.entities_extracted
entities_extracted: int = 0
agrag.ingestion.types.ExtractionStats.failures
failures: list[StageFailure] = Field(default_factory=list)
agrag.ingestion.types.ExtractionStats.relations_extracted
relations_extracted: int = 0
agrag.ingestion.types.IngestStats

Bases: BaseModel

Ingestion-stage results. Renamed from IngestResult (ADR 0031).

Attributes:

agrag.ingestion.types.IngestStats.documents
documents: int = 0
agrag.ingestion.types.IngestStats.quarantined
quarantined: int = 0
agrag.ingestion.types.IngestStats.quarantined_items
quarantined_items: list[StageFailure] = Field(default_factory=list)
agrag.ingestion.types.IngestStats.skipped
skipped: int = 0
agrag.ingestion.types.IngestStats.sources
sources: int = 0
agrag.ingestion.types.MergeStats

Bases: BaseModel

Merge-stage results.

Attributes:

  • nodes_created (int) – Brand-new entities materialized this call.
  • nodes_updated (int) – Existing entities that absorbed new mention data without tombstoning anything.
  • nodes_merged (int) – Entities tombstoned into a survivor this call.
  • conflicts_resolved (int) – Total property/description conflicts resolved across every merge this call performed.
  • failures (list[StageFailure]) – Includes an LLM failure during description summarization (ADR 0033's fallback-to-concatenation path still records one here, even though it didn't block the merge).
agrag.ingestion.types.MergeStats.conflicts_resolved
conflicts_resolved: int = 0
agrag.ingestion.types.MergeStats.failures
failures: list[StageFailure] = Field(default_factory=list)
agrag.ingestion.types.MergeStats.nodes_created
nodes_created: int = 0
agrag.ingestion.types.MergeStats.nodes_merged
nodes_merged: int = 0
agrag.ingestion.types.MergeStats.nodes_updated
nodes_updated: int = 0
agrag.ingestion.types.ResolutionStats

Bases: BaseModel

Resolution-stage results.

Attributes:

  • exact_match_hits (int) – Mentions that matched an already-persisted entity via the global exact-match tier.
  • in_batch_groups (int) – Resolution groups the in-batch fuzzy/LLM tier found.
  • ambiguous_count (int) – Comparisons no comparator could confidently decide (ADR 0013's fail-safe: never merged).
agrag.ingestion.types.ResolutionStats.ambiguous_count
ambiguous_count: int = 0
agrag.ingestion.types.ResolutionStats.exact_match_hits
exact_match_hits: int = 0
agrag.ingestion.types.ResolutionStats.in_batch_groups
in_batch_groups: int = 0
agrag.ingestion.types.StageFailure

Bases: BaseModel

One item's failure within a pipeline stage.

Attributes:

  • item_id (str) – The chunk id, mention id, or batch id — whichever unit the stage failed on.
  • error_type (str) – The exception's class name.
  • error_message (str) – The exception's message.
  • trace_id (str | None) – The OTel trace id correlating to the full span detail, when tracing is configured.
  • span_id (str | None) – The OTel span id within that trace.
agrag.ingestion.types.StageFailure.error_message
error_message: str
agrag.ingestion.types.StageFailure.error_type
error_type: str
agrag.ingestion.types.StageFailure.item_id
item_id: str
agrag.ingestion.types.StageFailure.span_id
span_id: str | None = None
agrag.ingestion.types.StageFailure.trace_id
trace_id: str | None = None
agrag.ingestion.types.StorageStats

Bases: BaseModel

Storage-write-stage results.

Attributes:

  • nodes_written (int) – Chunk and Entity nodes together, one aggregate count rather than a sub-count per kind — both are written in the same final phase, so there is one natural accounting point.
  • relationships_written (int) – Domain Relation and MENTIONED_IN edges together, for the same reason.
  • failures (list[StageFailure]) – One record per batch write that failed, capped per call. A GraphStore write is a single managed transaction, so a failure here means the whole batch did not land, not a partial subset of it.
agrag.ingestion.types.StorageStats.failures
failures: list[StageFailure] = Field(default_factory=list)
agrag.ingestion.types.StorageStats.nodes_written
nodes_written: int = 0
agrag.ingestion.types.StorageStats.relationships_written
relationships_written: int = 0

agrag.observability

OpenTelemetry wiring for the ingestion layer.

This module imports only opentelemetry-api. The SDK and exporters stay in the optional observability extra and are never imported here; a caller wires them before opening a graph. The tracer is constructor-injected, never ambient.

Functions:

  • get_tracer – Return a usable tracer.
  • traced – Wrap a call in a span on the given tracer.

agrag.observability.get_tracer

get_tracer(tracer:Tracer | None) -> Tracer

Return a usable tracer.

Parameters:

  • tracer (Tracer | None) – A caller-supplied tracer, or None to use OpenTelemetry's global no-op tracer.

Returns:

  • Tracer – The supplied tracer, or the global no-op tracer when the caller passed None.

agrag.observability.traced

traced(tracer:Tracer | None) -> Callable[[Callable], Callable]

Wrap a call in a span on the given tracer.

Use this at each pipeline call site (loader, chunker). It works on both sync and async functions; the span name is the wrapped callable's qualified name.

Parameters:

  • tracer (Tracer | None) – The tracer to record on, or None for a no-op span.

Returns:

agrag.retrieval

Retrieval package: search engine, fusion, reranking, and retrievers.

Modules:

  • errors – Errors that the retrieval layer raises.
  • filters – Constraints applied across every retrieval method in one call.
  • fusion – Reciprocal Rank Fusion: combine ranked results from multiple methods.
  • identity – Shared identity resolution for merged_into chains.
  • methods – Low-level search method helpers shared by retrievers.
  • recipes – Named, data-only configurations of what SearchEngine runs.
  • rerank – Rerankers that reorder fused search results.
  • retrievers – Retriever implementations for entity, chunk, BFS, and text2cypher search.
  • search_engine – Retrieval's public entry point, independent of Graph (ADR 0035).
  • settings – Env-backed configuration for retrieval methods and fusion.

agrag.retrieval.errors

Errors that the retrieval layer raises.

Classes:

agrag.retrieval.errors.AllRetrievalMethodsFailedError
AllRetrievalMethodsFailedError(failures:dict[str, BaseException]) -> None

Bases: RetrievalError

Every retrieval method a Recipe named failed.

Raised instead of returning an empty result list so a total retrieval outage is not mistaken for a query with no matches.

Attributes:

  • failures – Each failed method name mapped to the exception it raised.
agrag.retrieval.errors.AllRetrievalMethodsFailedError.failures
failures = failures
agrag.retrieval.errors.RetrievalError

Bases: Exception

The base class for every retrieval error.

agrag.retrieval.errors.UnknownRecipeMethodError
UnknownRecipeMethodError(unknown:list[str], known:list[str]) -> None

Bases: RetrievalError

A Recipe named a method SearchEngine does not know how to run.

A misspelled method name is a configuration error and must be raised at search time so an empty successful search cannot silently hide a typo.

Attributes:

  • unknown – The method names the recipe listed that are not in the retriever registry.
  • known – The method names this SearchEngine can run.
agrag.retrieval.errors.UnknownRecipeMethodError.known
known = list(known)
agrag.retrieval.errors.UnknownRecipeMethodError.unknown
unknown = list(unknown)

agrag.retrieval.filters

Constraints applied across every retrieval method in one call.

Classes:

  • SearchFilters – Constraints applied across every retrieval method in one call.
agrag.retrieval.filters.SearchFilters

Bases: BaseModel

Constraints applied across every retrieval method in one call.

Attributes:

  • labels (list[str]) – Entity labels a result must have, when searching entities.
  • relation_types (list[str]) – Relation types a traversal may cross.
  • document_ids (list[str]) – Restrict chunk results to these source documents.
  • properties (dict[str, Any]) – Exact-match property filters, applied identically to vector-store payload filters and Cypher WHERE clauses.

Functions:

agrag.retrieval.filters.SearchFilters.document_ids
document_ids: list[str] = Field(default_factory=list)
agrag.retrieval.filters.SearchFilters.labels
labels: list[str] = Field(default_factory=list)
agrag.retrieval.filters.SearchFilters.properties
properties: dict[str, Any] = Field(default_factory=dict)
agrag.retrieval.filters.SearchFilters.relation_types
relation_types: list[str] = Field(default_factory=list)
agrag.retrieval.filters.SearchFilters.to_cypher_where
to_cypher_where(node_var:str = 'node') -> tuple[str, dict[str, Any]]

Return a parameterized WHERE clause fragment.

Labels are emitted as native Cypher node labels (node:Label) rather than property filters, since Neo4j represents entity types as labels on nodes. Document-id and property filters go through filter_clause as before.

Parameters:

  • node_var (str) – The Cypher variable bound to the node.

Returns:

agrag.retrieval.filters.SearchFilters.to_payload_filter
to_payload_filter() -> dict[str, Any]

Return a flat-dict filter for VectorStore search calls.

Labels become a label payload key, which is how a VectorStore records the graph label a record came from. A GraphStore holds labels on the node itself, not as a property, so the native path uses to_property_filter instead and selects labels by the index it searches.

Returns:

  • dict[str, Any] – A dict suitable for VectorStore.search/hybrid_search
  • dict[str, Any] – filters parameter.
agrag.retrieval.filters.SearchFilters.to_property_filter
to_property_filter() -> dict[str, Any]

Return a flat-dict filter over node properties only.

Excludes labels, which are node labels rather than properties on every graph backend this project supports.

Returns:

  • dict[str, Any] – A dict of property name to expected value, where a list
  • dict[str, Any] – value means any of.

agrag.retrieval.fusion

Reciprocal Rank Fusion: combine ranked results from multiple methods.

Functions:

  • fuse – Combine every method's ranked results into one deduplicated list.
agrag.retrieval.fusion.fuse
fuse(results_by_method:dict[str, list[SearchResult]], *, rrf_k:int = 60) -> list[SearchResult]

Combine every method's ranked results into one deduplicated list.

Runs unconditionally, even for a single method, so a Rerank pass never sees duplicates. Uses Reciprocal Rank Fusion: an item's fused score is the sum of 1 / (rrf_k + rank) across every method that returned it.

Each method contributes at most one vote per item, scored at the item's best (lowest) rank within that method. A multi-label entity that surfaces in two positions of one method's output, or a pre-fusion merged_into collapse, only adds one vote from that method, so duplicate hits from a single retriever cannot unfairly promote an item over a single best hit from another method.

Deduplication uses SearchResult.identity_key, which is (type, id) after hydration has already resolved any merged_into chain to the live survivor. Fusion does not re-resolve identity; it trusts that every SearchResult it receives already carries a live id.

Parameters:

  • results_by_method (dict[str, list[SearchResult]]) – Each method's own ranked output, keyed by method name.
  • rrf_k (int) – The RRF constant; higher values flatten the influence of rank position.

Returns:

agrag.retrieval.identity

Shared identity resolution for merged_into chains.

Functions:

  • resolve_entity – Return the live Entity behind an id, following merged_into.

Attributes:

agrag.retrieval.identity.MAX_MERGE_HOPS
MAX_MERGE_HOPS = 32
agrag.retrieval.identity.resolve_entity
resolve_entity(graph_store:GraphStore, entity_id:UUID) -> Entity

Return the live Entity behind an id, following merged_into.

Every retrieval path that can produce an entity id must call this before wrapping the id in a SearchResult. This is the single place the merged_into invariant is enforced.

A merge writes a merged_into property on the tombstone rather than a relationship, so the chain is walked one hop per query.

Parameters:

  • graph_store (GraphStore) – Where the entity and its possible tombstone chain live.
  • entity_id (UUID) – The id a retrieval method found, which may or may not still be live.

Returns:

  • Entity – The live Entity, after resolving zero or more hops.

Raises:

  • ValueError – The id does not exist, its node cannot be parsed, the chain points at a missing node, the chain cycles, or it is longer than MAX_MERGE_HOPS.

agrag.retrieval.methods

Low-level search method helpers shared by retrievers.

Modules:

  • vector – Shared vector search helper for GraphStore and VectorStore.
agrag.retrieval.methods.vector

Shared vector search helper for GraphStore and VectorStore.

Functions:

  • vector_search – Embed query and search on whichever store is configured.
vector_search(query:str, *, embedder:Embedder, graph_store:GraphStore, vector_store:VectorStore | None, collection:str, labels:Sequence[str], limit:int, filters:SearchFilters | None, settings:RetrievalSettings) -> list[VectorHit]

Embed query and search on whichever store is configured.

When vector_store is set, runs hybrid_search there (dense plus BM25, blended by settings.hybrid_alpha) against collection. When it is None, runs GraphStore's native vector_search once per label in labels and merges the hits, ignoring hybrid_alpha since that path is dense-only. One native vector index exists per label, so a search over several labels is several searches.

Parameters:

  • query (str) – The natural-language query text to embed.
  • embedder (Embedder) – Produces the query's dense vector.
  • graph_store (GraphStore) – The GraphStore-native fallback target.
  • vector_store (VectorStore | None) – The optional VectorStore target; None selects the GraphStore-native path.
  • collection (str) – The VectorStore collection name.
  • labels (Sequence[str]) – The node labels to search on the GraphStore-native path, each backed by its own vector index.
  • limit (int) – Maximum hits to return.
  • filters (SearchFilters | None) – Constraints translated to whichever store is searched. Labels are a payload key on the VectorStore path and choose the searched indexes on the native path, so they are not sent as node property filters.
  • settings (RetrievalSettings) – Supplies hybrid_alpha for the VectorStore path.

Returns:

  • list[VectorHit] – Ranked VectorHits, from whichever store was searched.

Raises:

  • ValueError – The native path was selected with no labels to search.

agrag.retrieval.recipes

Named, data-only configurations of what SearchEngine runs.

Classes:

  • Recipe – A named configuration of what SearchEngine runs for a query.

Attributes:

agrag.retrieval.recipes.CHUNK
CHUNK = Recipe(methods=['chunk'], limit=10)
agrag.retrieval.recipes.ENTITY
ENTITY = Recipe(methods=['entity'], limit=10)
agrag.retrieval.recipes.GRAPH_EXPAND
GRAPH_EXPAND = Recipe(methods=['entity'], bfs=True, limit=20)
agrag.retrieval.recipes.HYBRID
HYBRID = Recipe(methods=['entity', 'chunk'], limit=10)
agrag.retrieval.recipes.HYBRID_RERANKED
HYBRID_RERANKED = Recipe(methods=['entity', 'chunk'], reranker='cross_encoder', limit=10)
agrag.retrieval.recipes.Recipe

Bases: BaseModel

A named configuration of what SearchEngine runs for a query.

Attributes:

  • methods (list[str]) – Which retrieval methods to fan out to concurrently, by name.
  • bfs (bool) – Whether to run a BFS expansion after methods complete, seeded from their entity results. BFS needs seed ids methods produce, so it cannot run concurrently with them.
  • bfs_depth (int | None) – Traversal depth when bfs is true. None uses RetrievalSettings.traversal_depth.
  • reranker (Literal['cross_encoder', 'node_distance'] | None) – The optional Rerank pass to run after Fusion. None skips reranking.
  • limit (int) – The maximum number of results SearchEngine returns.
agrag.retrieval.recipes.Recipe.bfs
bfs: bool = False
agrag.retrieval.recipes.Recipe.bfs_depth
bfs_depth: int | None = None
agrag.retrieval.recipes.Recipe.limit
limit: int = 10
agrag.retrieval.recipes.Recipe.methods
methods: list[str]
agrag.retrieval.recipes.Recipe.reranker
reranker: Literal['cross_encoder', 'node_distance'] | None = None

agrag.retrieval.rerank

Rerankers that reorder fused search results.

Modules:

  • cross_encoder – Cross-encoder reranker using sentence-transformers.
  • node_distance – Node distance reranker: reorder by graph proximity to seeds.
agrag.retrieval.rerank.cross_encoder

Cross-encoder reranker using sentence-transformers.

Functions:

agrag.retrieval.rerank.cross_encoder.cross_encoder_rerank
cross_encoder_rerank(query:str, results:list[SearchResult], *, min_score:float | None = None) -> list[SearchResult]

Rerank results using a cross-encoder model.

Requires the embed-local extra (sentence-transformers). Scores (query, text) pairs and reorders by relevance. Drops results scoring below min_score when set.

Parameters:

  • query (str) – The natural-language query text.
  • results (list[SearchResult]) – The fused results to rerank.
  • min_score (float | None) – Optional minimum score threshold. Results below this are dropped.

Returns:

agrag.retrieval.rerank.node_distance

Node distance reranker: reorder by graph proximity to seeds.

Functions:

agrag.retrieval.rerank.node_distance.node_distance_rerank
node_distance_rerank(results:list[SearchResult], *, graph_store:GraphStore, seed_ids:list[UUID]) -> list[SearchResult]

Rerank results by graph proximity to seed entity ids.

Uses shortest-path distance from each result entity to the closest seed entity. Entities closer to seeds rank higher. Results without an entity item (chunks, relations) are placed at the end with a high distance penalty.

Parameters:

  • results (list[SearchResult]) – The fused results to rerank.
  • graph_store (GraphStore) – The graph store for shortest-path queries.
  • seed_ids (list[UUID]) – The seed entity ids to measure distance from. Seeds are the query's direct hits, not the whole candidate list: a candidate that is its own seed measures distance zero, so seeding with every candidate leaves the order unchanged.

Returns:

agrag.retrieval.retrievers

Retriever implementations for entity, chunk, BFS, and text2cypher search.

Modules:

  • base – Abstract base class for retrieval methods.
  • bfs – BFS retriever: graph traversal from seed entity ids.
  • chunk – Chunk retriever: dense vector search over chunks.
  • entity – Entity retriever: dense vector search over entities.
  • text2cypher – Text2Cypher retriever: generate Cypher from natural language.
agrag.retrieval.retrievers.base

Abstract base class for retrieval methods.

Classes:

  • Retriever – One retrieval method: given a query, return SearchResults.
agrag.retrieval.retrievers.base.Retriever

Bases: ABC

One retrieval method: given a query, return SearchResults.

Subclasses own exactly one strategy (dense entity search, chunk search, BFS expansion). SearchEngine fans a query out to every Retriever a Recipe names and hands the combined output to Fusion.

Functions:

  • retrieve – Run this retrieval method and return hydrated results.

Attributes:

####### agrag.retrieval.retrievers.base.Retriever.name

name: str

####### agrag.retrieval.retrievers.base.Retriever.retrieve

retrieve(query:str, *, filters:SearchFilters | None = None, limit:int = 10) -> list[SearchResult]

Run this retrieval method and return hydrated results.

agrag.retrieval.retrievers.bfs

BFS retriever: graph traversal from seed entity ids.

Classes:

agrag.retrieval.retrievers.bfs.BFSRetriever
BFSRetriever(*, graph_store:GraphStore, settings:RetrievalSettings | None = None) -> None

Bases: Retriever

Graph traversal from seed entity ids.

Takes seed entity ids (from a prior EntityRetriever call, or supplied directly), runs bfs_expand_query, and hydrates the returned entities through resolve_entity and relations directly. Degree-capped by RetrievalSettings.traversal_limit.

Functions:

  • retrieve – Run BFS expansion from seed entity ids.

Attributes:

Parameters:

  • graph_store (GraphStore) – The graph store to traverse.
  • settings (RetrievalSettings | None) – Retrieval configuration; defaults from environment.

####### agrag.retrieval.retrievers.bfs.BFSRetriever.name

name = 'bfs'

####### agrag.retrieval.retrievers.bfs.BFSRetriever.retrieve

retrieve(query:str, *, filters:SearchFilters | None = None, limit:int | None = None, seed_ids:list[UUID] | None = None, depth:int | None = None) -> list[SearchResult]

Run BFS expansion from seed entity ids.

Parameters:

  • query (str) – The natural-language query text (unused for BFS, kept for interface consistency).
  • filters (SearchFilters | None) – Constraints applied to traversal. relation_types restrict which relationships the traversal crosses; property filters apply to neighbor nodes.
  • limit (int | None) – Maximum results. None uses traversal_limit.
  • seed_ids (list[UUID] | None) – The entity ids to expand from. If None, BFS returns empty.
  • depth (int | None) – BFS hops. None uses RetrievalSettings.traversal_depth.

Returns:

  • list[SearchResult] – SearchResults with entities and relations found via BFS.
agrag.retrieval.retrievers.chunk

Chunk retriever: dense vector search over chunks.

Classes:

agrag.retrieval.retrievers.chunk.ChunkRetriever
ChunkRetriever(*, graph_store:GraphStore, embedder:Embedder, vector_store:VectorStore | None = None, settings:RetrievalSettings | None = None) -> None

Bases: Retriever

Dense chunk search via vector similarity.

Chunks are never tombstoned, so no merged_into resolution is needed. Embeds the query, searches via the GraphStore-native or VectorStore path, then hydrates each hit into a Chunk. The native path searches the Chunk vector index ingestion provisions; the VectorStore path searches chunk_collection.

Functions:

  • retrieve – Run chunk search and return hydrated results.

Attributes:

Parameters:

  • graph_store (GraphStore) – Backs chunk search when vector_store is absent.
  • embedder (Embedder) – Produces query vectors.
  • vector_store (VectorStore | None) – Optional VectorStore for hybrid search.
  • settings (RetrievalSettings | None) – Retrieval configuration; defaults from environment.

####### agrag.retrieval.retrievers.chunk.ChunkRetriever.name

name = 'chunk'

####### agrag.retrieval.retrievers.chunk.ChunkRetriever.retrieve

retrieve(query:str, *, filters:SearchFilters | None = None, limit:int | None = None) -> list[SearchResult]

Run chunk search and return hydrated results.

Parameters:

  • query (str) – The natural-language query text.
  • filters (SearchFilters | None) – Constraints applied to the search.
  • limit (int | None) – Maximum results. None uses settings.chunk_top_k.

Returns:

agrag.retrieval.retrievers.entity

Entity retriever: dense vector search over entities.

Classes:

agrag.retrieval.retrievers.entity.EntityRetriever
EntityRetriever(*, graph_store:GraphStore, embedder:Embedder, vector_store:VectorStore | None = None, settings:RetrievalSettings | None = None, entity_labels:Sequence[str] | None = None) -> None

Bases: Retriever

Dense entity search via vector similarity.

Embeds the query, searches via the GraphStore-native or VectorStore path, then resolves every hit through resolve_entity so the caller can trust item.id is live.

The native path searches one vector index per entity label, so it needs the labels ingestion provisioned indexes for: the label filter when the caller sets one, otherwise entity_labels.

Functions:

  • retrieve – Run entity search and return hydrated results.

Attributes:

Parameters:

  • graph_store (GraphStore) – Backs entity search when vector_store is absent.
  • embedder (Embedder) – Produces query vectors.
  • vector_store (VectorStore | None) – Optional VectorStore for hybrid search.
  • settings (RetrievalSettings | None) – Retrieval configuration; defaults from environment.
  • entity_labels (Sequence[str] | None) – The schema entity labels native search runs against. None uses settings.entity_labels.

####### agrag.retrieval.retrievers.entity.EntityRetriever.name

name = 'entity'

####### agrag.retrieval.retrievers.entity.EntityRetriever.retrieve

retrieve(query:str, *, filters:SearchFilters | None = None, limit:int | None = None) -> list[SearchResult]

Run entity search and return hydrated results.

Parameters:

  • query (str) – The natural-language query text.
  • filters (SearchFilters | None) – Constraints applied to the search.
  • limit (int | None) – Maximum results. None uses settings.entity_top_k.

Returns:

Raises:

  • ValueError – Native search was selected and neither the filter nor the configuration names an entity label.
agrag.retrieval.retrievers.text2cypher

Text2Cypher retriever: generate Cypher from natural language.

Classes:

Attributes:

agrag.retrieval.retrievers.text2cypher.Text2CypherRetriever
Text2CypherRetriever(*, graph_store:GraphStore, settings:RetrievalSettings | None = None) -> None

Bases: Retriever

Let the agent ask structured questions via generated Cypher.

Calls a BAML function to generate a read-only Cypher query, runs reject_write_cypher as a safety pre-filter, then bounds the query with a row limit and a server-side transaction timeout before EXPLAIN and execution. Rows that carry an entity id are resolved through resolve_entity before becoming a SearchResult; relationship and chunk rows are parsed directly. Scalar rows (for example counts or property values) cannot become a SearchResult and are logged instead of being silently dropped.

Functions:

  • retrieve – Generate and execute a Cypher query for the question.

Attributes:

Parameters:

  • graph_store (GraphStore) – Where the generated query runs.
  • settings (RetrievalSettings | None) – Retrieval configuration; defaults from environment.

####### agrag.retrieval.retrievers.text2cypher.Text2CypherRetriever.name

name = 'text2cypher'

####### agrag.retrieval.retrievers.text2cypher.Text2CypherRetriever.retrieve

retrieve(query:str, *, filters:SearchFilters | None = None, limit:int = 10) -> list[SearchResult]

Generate and execute a Cypher query for the question.

Parameters:

  • query (str) – The natural-language question.
  • filters (SearchFilters | None) – Ignored; text2cypher applies its own filters.
  • limit (int) – Maximum results to return.

Returns:

  • list[SearchResult] – SearchResults from the generated query: entity results resolved through resolve_entity; relation and chunk rows parsed directly. Rows with no entity, relation, or chunk item are logged and skipped.
agrag.retrieval.retrievers.text2cypher.logger
logger = logging.getLogger(__name__)

agrag.retrieval.search_engine

Retrieval's public entry point, independent of Graph (ADR 0035).

Classes:

  • SearchEngine – Retrieval's public entry point, independent of Graph.

Attributes:

agrag.retrieval.search_engine.SearchEngine
SearchEngine(*, graph_store:GraphStore, embedder:Embedder, vector_store:VectorStore | None = None, settings:RetrievalSettings | None = None, entity_labels:Sequence[str] | None = None) -> None

Retrieval's public entry point, independent of Graph.

Fans a query out to every method a Recipe names, fuses the results, and optionally reranks them. Constructed from its own stores; does not depend on a Graph instance existing.

Functions:

  • search – Run recipe's methods, fuse, expand, and optionally rerank.

Parameters:

  • graph_store (GraphStore) – Always required; backs entity/chunk search when vector_store is absent, and always backs BFS.
  • embedder (Embedder) – Produces query vectors for dense and hybrid search.
  • vector_store (VectorStore | None) – Optional. When set, entity and chunk search run hybrid_search there instead of GraphStore's native search. Configuring one without a dual-write ingestion change gets an empty result set, not an error.
  • settings (RetrievalSettings | None) – Retrieval configuration; defaults from environment.
  • entity_labels (Sequence[str] | None) – The schema entity labels native entity search runs against, one vector index each, as provisioned by Graph.open. Pass [entity.label for entity in schema.entities]. None uses settings.entity_labels. Ignored when a vector_store is configured.
agrag.retrieval.search_engine.SearchEngine.search
search(query:str, recipe:Recipe, *, filters:SearchFilters | None = None) -> list[SearchResult]

Run recipe's methods, fuse, expand, and optionally rerank.

Runs recipe.methods concurrently and fuses their output first. When recipe.bfs is set, BFS runs as a second, sequential step seeded from the fused entity results. BFS results are fused into the same list a second time before reranking.

Parameters:

  • query (str) – The natural-language query text.
  • recipe (Recipe) – Which methods to run, whether to expand via BFS afterward, and which reranker, if any, follows.
  • filters (SearchFilters | None) – Constraints applied identically to every method.

Returns:

Raises:

  • AllRetrievalMethodsFailedError – Every method the recipe names failed. A method failing while others succeed is logged and its results are simply absent.
  • UnknownRecipeMethodError – The recipe names one or more methods that are not in the retriever registry. A misspelled method name is a configuration error and is reported instead of silently returning no results.
agrag.retrieval.search_engine.logger
logger = logging.getLogger(__name__)

agrag.retrieval.settings

Env-backed configuration for retrieval methods and fusion.

Classes:

agrag.retrieval.settings.RetrievalSettings

Bases: BaseSettings

Configuration for retrieval methods and fusion.

Attributes:

  • entity_collection (str) – The VectorStore collection name for entity search. Only read when a VectorStore is configured on SearchEngine; ignored on the GraphStore-native path.
  • chunk_collection (str) – The VectorStore collection name for chunk search. Same condition as entity_collection.
  • entity_labels (list[str]) – The graph labels native entity search runs against, one vector index each. These are the schema's entity labels, never a VectorStore collection name. Only read when no VectorStore is configured and the caller passes no label filter.
  • node_distance_seed_top_k (int) – How many of the highest-ranked entity hits seed the node-distance reranker. Candidates are ordered by graph distance to those seeds.
  • entity_top_k (int) – Results requested per entity search call.
  • chunk_top_k (int) – Results requested per chunk search call.
  • hybrid_alpha (float) – Dense-versus-keyword blend for hybrid search, 0 to 1. Only meaningful on the VectorStore path; GraphStore-native search is dense-only and ignores this.
  • traversal_depth (int) – Maximum BFS hops from a seed entity.
  • traversal_limit (int) – Maximum nodes a BFS expansion can return.
  • rrf_k (int) – The RRF constant controlling how much rank position matters.
  • reranker_min_score (float | None) – Results scoring below this after rerank are dropped. None disables the threshold.
  • text2cypher_max_retries (int) – Maximum retry attempts for a text2cypher generation that produces invalid Cypher.
  • text2cypher_timeout_seconds (float | None) – Server-side transaction timeout applied to generated read queries. The database terminates a generated query that runs longer, so a pathological query cannot hold server resources indefinitely. None uses the server's default timeout.
  • text2cypher_max_rows (int) – Maximum rows a generated read query may return. Appended as a LIMIT clause when the generated query declares none of its own.

Env prefix: RETRIEVAL_.

agrag.retrieval.settings.RetrievalSettings.chunk_collection
chunk_collection: str = 'agrag_chunks'
agrag.retrieval.settings.RetrievalSettings.chunk_top_k
chunk_top_k: int = 10
agrag.retrieval.settings.RetrievalSettings.entity_collection
entity_collection: str = 'agrag_entities'
agrag.retrieval.settings.RetrievalSettings.entity_labels
entity_labels: list[str] = []
agrag.retrieval.settings.RetrievalSettings.entity_top_k
entity_top_k: int = 10
agrag.retrieval.settings.RetrievalSettings.hybrid_alpha
hybrid_alpha: float = 0.5
agrag.retrieval.settings.RetrievalSettings.model_config
model_config = SettingsConfigDict(env_prefix='RETRIEVAL_', env_file='.env', extra='ignore')
agrag.retrieval.settings.RetrievalSettings.node_distance_seed_top_k
node_distance_seed_top_k: int = 3
agrag.retrieval.settings.RetrievalSettings.reranker_min_score
reranker_min_score: float | None = None
agrag.retrieval.settings.RetrievalSettings.rrf_k
rrf_k: int = 60
agrag.retrieval.settings.RetrievalSettings.text2cypher_max_retries
text2cypher_max_retries: int = 3
agrag.retrieval.settings.RetrievalSettings.text2cypher_max_rows
text2cypher_max_rows: int = 1000
agrag.retrieval.settings.RetrievalSettings.text2cypher_timeout_seconds
text2cypher_timeout_seconds: float | None = 10.0
agrag.retrieval.settings.RetrievalSettings.traversal_depth
traversal_depth: int = 2
agrag.retrieval.settings.RetrievalSettings.traversal_limit
traversal_limit: int = 50

agrag.vectordb

Vector storage backends and the build shortcut.

Modules:

  • base – The VectorStore abstraction and its build shortcut.
  • errors – Errors that the vector-store layer raises.
  • milvus – Milvus vector-store backend.
  • qdrant – Qdrant vector-store backend.
  • settings – Settings for vector-store backends.
  • weaviate – Weaviate vector-store backend.

Classes:

Functions:

  • build_vector_store – Build a vector store from a backend name, or return one unchanged.

agrag.vectordb.CollectionDimensionMismatchError

CollectionDimensionMismatchError(*, expected:int, actual:int) -> None

Bases: VectorStoreError

A collection already exists with a different embedding dimension.

Attributes:

  • expected – The dimension the collection was created with.
  • actual – The dimension the caller requested.
agrag.vectordb.CollectionDimensionMismatchError.actual
actual = actual
agrag.vectordb.CollectionDimensionMismatchError.expected
expected = expected

agrag.vectordb.MilvusSettings

Bases: BaseSettings

Milvus connection configuration.

Attributes:

  • uri (str) – The Milvus endpoint URI. Env: MILVUS_URI.
  • token (str) – The Milvus auth token. Empty string for an unauthenticated instance. Env: MILVUS_TOKEN.
  • require_tls (bool) – When True, reject a plaintext uri to a non-local host even with no token configured. Off by default since many deployments run an unauthenticated Milvus on a private network and rely on network segmentation rather than transport encryption. Env: MILVUS_REQUIRE_TLS.

Raises:

  • ValueErroruri is plaintext (http), points at a non-local host, and either token is set or require_tls is True. Use https for a remote Milvus instance.
agrag.vectordb.MilvusSettings.model_config
model_config = SettingsConfigDict(env_prefix='MILVUS_', env_file='.env', extra='ignore')
agrag.vectordb.MilvusSettings.require_tls
require_tls: bool = False
agrag.vectordb.MilvusSettings.token
token: str = ''
agrag.vectordb.MilvusSettings.uri
uri: str = 'http://localhost:19530'

agrag.vectordb.MilvusVectorStore

MilvusVectorStore(*, settings:MilvusSettings | None = None, client:Any | None = None) -> None

Bases: VectorStore

A VectorStore backed by Milvus, including native hybrid search.

The client connects lazily on first use, so constructing the store does not open a network connection. Milvus performs BM25 server-side, so hybrid search needs no client-side sparse embedder; the sparse vector is computed by a Milvus Function from the text field on write and at query time.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its entities.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text in one fused call.
  • initialize – Check connectivity and authentication.
  • invalidate_collection – Drop cached distance-metric knowledge of a collection.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.

Parameters:

  • settings (MilvusSettings | None) – Milvus connection settings. Defaults to MilvusSettings().
  • client (Any | None) – A pre-built AsyncMilvusClient, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
agrag.vectordb.MilvusVectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.MilvusVectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.MilvusVectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.

Returns:

  • int – The number of matching records.
agrag.vectordb.MilvusVectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.MilvusVectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its entities.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.MilvusVectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Milvus performs BM25 server-side, so the sparse field and its Function are always provisioned; the hybrid flag is accepted for interface parity but is a no-op here. An existing collection must already carry this same fixed schema, since upsert and hybrid_search always read and write every field regardless of hybrid.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – Accepted for interface parity; ignored by Milvus.

Raises:

hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text in one fused call.

Fusion uses Milvus's native weighted reranker, which normalizes each request's scores before applying alpha.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0].
agrag.vectordb.MilvusVectorStore.initialize
initialize() -> None

Check connectivity and authentication.

agrag.vectordb.MilvusVectorStore.invalidate_collection
invalidate_collection(name:str) -> None

Drop cached distance-metric knowledge of a collection.

This store caches a collection's distance metric after the first call that resolves it, on the assumption that it alone (via ensure_collection/delete_collection) owns the collection's lifecycle for as long as this instance is in use. If something outside this instance deletes and recreates a collection under the same name with a different metric, call this first so the next call re-resolves that collection's metric from the backend instead of trusting the stale cache.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.MilvusVectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Requests at most MAX_RESPONSE_LIMIT ids per call, so a large ids list cannot exceed Milvus's response-size ceiling in one request the way sending every id at once would.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.MilvusVectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Milvus rejects a query whose offset + limit exceeds MAX_RESPONSE_LIMIT, so a numeric offset cannot page past that many total records. Pages instead cursor on the id primary key: each page filters on id > page_offset and orders by id ascending, which needs no offset at all and so never hits that window regardless of collection size. The explicit order is load bearing: without it, an unordered query result could omit rows at or below the next cursor, permanently skipping them on the next page.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The id cursor from a previous scroll call.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page cursor, or None at the
  • str | None – end.
agrag.vectordb.MilvusVectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT.
agrag.vectordb.MilvusVectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises:

agrag.vectordb.QdrantSettings

Bases: BaseSettings

Qdrant connection configuration.

Attributes:

  • url (str) – The Qdrant endpoint URL. Env: QDRANT_URL.
  • api_key (str) – The Qdrant API key. Env: QDRANT_API_KEY.
  • require_tls (bool) – When True, reject a plaintext url to a non-local host even with no api_key configured. Off by default since many deployments run an unauthenticated Qdrant on a private network and rely on network segmentation rather than transport encryption. Env: QDRANT_REQUIRE_TLS.

Raises:

  • ValueErrorurl is plaintext (http), points at a non-local host, and either api_key is set or require_tls is True. Use https for a remote Qdrant instance.
agrag.vectordb.QdrantSettings.api_key
api_key: str = ''
agrag.vectordb.QdrantSettings.model_config
model_config = SettingsConfigDict(env_prefix='QDRANT_', env_file='.env', extra='ignore')
agrag.vectordb.QdrantSettings.require_tls
require_tls: bool = False
agrag.vectordb.QdrantSettings.url
url: str = 'http://localhost:6333'

agrag.vectordb.QdrantVectorStore

QdrantVectorStore(*, settings:QdrantSettings | None = None, sparse_embedder:SparseEmbedder | None = None, client:Any | None = None, models:Any | None = None) -> None

Bases: VectorStore

A VectorStore backed by Qdrant, including native hybrid search.

The client connects lazily on first use, so constructing the store does not open a network connection. Hybrid search builds its sparse query with a SparseEmbedder that defaults to FastEmbed BM25 and loads only when a hybrid call first runs, not at construction.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its points.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text, fused by a weighted blend.
  • initialize – Check connectivity and authentication.
  • invalidate_collection – Drop cached hybrid-state and distance-metric knowledge of a collection.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.

Parameters:

  • settings (QdrantSettings | None) – Qdrant connection settings. Defaults to QdrantSettings().
  • sparse_embedder (SparseEmbedder | None) – The sparse embedder hybrid search uses. Defaults to a lazily-built FastEmbedBM25Embedder.
  • client (Any | None) – A pre-built AsyncQdrantClient, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
  • models (Any | None) – The qdrant_client.models module, for tests. Pair with client so filter/payload helpers work without needing the real qdrant_client package installed at all.
agrag.vectordb.QdrantVectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.QdrantVectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.QdrantVectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

  • int – The number of matching records.
agrag.vectordb.QdrantVectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.QdrantVectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its points.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.QdrantVectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – Whether to provision the named sparse vector hybrid search needs.

Raises:

hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text, fused by a weighted blend.

Qdrant's native fusion methods (RRF, DBSF) have no continuous dense/keyword weight, so this runs the dense and sparse (BM25) searches independently, min-max normalizes each result set's scores to [0, 1], then combines them per id as alpha * dense + (1 - alpha) * sparse. Each side fetches a wider candidate pool than limit so a document strong on only one signal still has a chance to reach the blended top results.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword.

Returns:

  • list[VectorHit] – The blended hits, highest combined score first.

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0].
agrag.vectordb.QdrantVectorStore.initialize
initialize() -> None

Check connectivity and authentication.

agrag.vectordb.QdrantVectorStore.invalidate_collection
invalidate_collection(name:str) -> None

Drop cached hybrid-state and distance-metric knowledge of a collection.

This store caches a collection's hybrid support and distance metric after the first call that resolves them, on the assumption that it alone (via ensure_collection/delete_collection) owns the collection's lifecycle for as long as this instance is in use. If something outside this instance deletes and recreates a collection under the same name with different config, call this first so the next call re-resolves that collection's state from the backend instead of trusting the stale cache.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.QdrantVectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.QdrantVectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The offset from a previous scroll call.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page offset, or None at the
  • str | None – end.
agrag.vectordb.QdrantVectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT.
agrag.vectordb.QdrantVectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

When collection has sparse-vector support (created or previously seen with ensure_collection(..., hybrid=True)), each record's payload["text"] is also sparse-embedded and stored under the named sparse vector, so hybrid_search's keyword arm has real vectors to match. A record with no text payload key gets an empty sparse vector and only ever surfaces through the dense side of a hybrid search.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises:

agrag.vectordb.VectorStore

Bases: ABC

A vector database backend: collection lifecycle, writes, and search.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its points.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text in one fused call.
  • initialize – Check connectivity and authentication.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.
agrag.vectordb.VectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.VectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.VectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.

Returns:

  • int – The number of matching records.
agrag.vectordb.VectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.VectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its points.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.VectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension. If the collection already exists with a different dimension, this raises.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – Whether to additionally provision the sparse-vector configuration hybrid search needs. Ignored by backends that need no such provisioning.

Raises:

hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text in one fused call.

Parameters:

  • collection (str) – The collection to search. Must have been created with ensure_collection(..., hybrid=True).
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by keyword/BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword. Weaviate and Milvus apply this weight natively. Qdrant's native fusion (Reciprocal Rank Fusion) has no continuous weight, so it applies alpha by blending two independently-scored, min-max normalized result sets instead of a single native fused call.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds agrag.common.validation.MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0]. Enforced uniformly across backends since they otherwise fail differently outside that range.
agrag.vectordb.VectorStore.initialize
initialize() -> None

Check connectivity and authentication.

Raises:

  • VectorStoreError – The backend is unreachable, or the credentials are rejected.
agrag.vectordb.VectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.VectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The offset from a previous scroll call, or None to start at the beginning.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page offset, or None at the
  • str | None – end.
agrag.vectordb.VectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds agrag.common.validation.MAX_SEARCH_LIMIT. Enforced uniformly across backends since they otherwise fail differently outside that range.
agrag.vectordb.VectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises:

agrag.vectordb.VectorStoreError

Bases: Exception

The base class for every vector-store error.

agrag.vectordb.VectorStoreMissingExtraError

VectorStoreMissingExtraError(extra:str) -> None

Bases: VectorStoreError

A vector store exists, but its package extra is not installed.

Attributes:

  • extra – The name of the package extra to install.
agrag.vectordb.VectorStoreMissingExtraError.extra
extra = extra

agrag.vectordb.WeaviateSettings

Bases: BaseSettings

Weaviate connection configuration.

Attributes:

  • mode (Literal['cloud', 'custom']) – "cloud" connects to Weaviate Cloud. "custom" connects to a self-hosted instance (used by integration tests against the local Docker Compose instance) — an explicit field, not inferred from the URL, since inference caused real connection bugs in surveyed reference implementations. Env: WEAVIATE_MODE.
  • url (str) – The Weaviate endpoint URL. For "cloud", the cluster URL. For "custom", the full host URL. Env: WEAVIATE_URL.
  • api_key (str) – The Weaviate API key. Env: WEAVIATE_API_KEY.
  • grpc_port (int) – The gRPC port, used by "custom" mode only ("cloud" mode infers it). Env: WEAVIATE_GRPC_PORT.
  • require_tls (bool) – When True, reject a plaintext url to a non-local host even with no api_key configured. Off by default since many deployments run an unauthenticated Weaviate on a private network and rely on network segmentation rather than transport encryption. Env: WEAVIATE_REQUIRE_TLS.

Raises:

  • ValueErrorurl is plaintext (http), points at a non-local host, and either api_key is set or require_tls is True. Use https for a remote Weaviate instance.
agrag.vectordb.WeaviateSettings.api_key
api_key: str = ''
agrag.vectordb.WeaviateSettings.grpc_port
grpc_port: int = 50051
agrag.vectordb.WeaviateSettings.mode
mode: Literal['cloud', 'custom'] = 'custom'
agrag.vectordb.WeaviateSettings.model_config
model_config = SettingsConfigDict(env_prefix='WEAVIATE_', env_file='.env', extra='ignore')
agrag.vectordb.WeaviateSettings.require_tls
require_tls: bool = False
agrag.vectordb.WeaviateSettings.url
url: str = 'http://localhost:8080'

agrag.vectordb.WeaviateVectorStore

WeaviateVectorStore(*, settings:WeaviateSettings | None = None, client:Any | None = None) -> None

Bases: VectorStore

A VectorStore backed by Weaviate, including native hybrid search.

The client connects lazily on first use, so constructing the store does not open a network connection. Weaviate does its own server-side BM25, so hybrid search needs no client-side sparse embedder.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its objects.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text in one fused call.
  • initialize – Open the connection and check authentication.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.

Parameters:

  • settings (WeaviateSettings | None) – Weaviate connection settings. Defaults to WeaviateSettings().
  • client (Any | None) – A pre-built Weaviate async client, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
agrag.vectordb.WeaviateVectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.WeaviateVectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.WeaviateVectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

  • int – The number of matching records.
agrag.vectordb.WeaviateVectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.WeaviateVectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its objects.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.WeaviateVectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – No-op for Weaviate, which needs no sparse provisioning.

Raises:

  • CollectionDimensionMismatchError – An existing object in the collection carries a vector of a different dimension. Weaviate keeps no schema-level dimension for self-provided vectors, so an existing collection with no vector-bearing object cannot be checked this way.
hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text in one fused call.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by keyword/BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0].
agrag.vectordb.WeaviateVectorStore.initialize
initialize() -> None

Open the connection and check authentication.

agrag.vectordb.WeaviateVectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.WeaviateVectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The cursor id from a previous scroll call.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page cursor, or None at the
  • str | None – end.
agrag.vectordb.WeaviateVectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT.
agrag.vectordb.WeaviateVectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

Uses Weaviate's batch import, which replaces an existing object sharing a written id instead of rejecting it, giving real insert-or-replace semantics and per-call batching in one request.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises:

agrag.vectordb.base

The VectorStore abstraction and its build shortcut.

Classes:

  • VectorStore – A vector database backend: collection lifecycle, writes, and search.
agrag.vectordb.base.VectorStore

Bases: ABC

A vector database backend: collection lifecycle, writes, and search.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its points.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text in one fused call.
  • initialize – Check connectivity and authentication.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.
agrag.vectordb.base.VectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.base.VectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.base.VectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.

Returns:

  • int – The number of matching records.
agrag.vectordb.base.VectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.base.VectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its points.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.base.VectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension. If the collection already exists with a different dimension, this raises.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – Whether to additionally provision the sparse-vector configuration hybrid search needs. Ignored by backends that need no such provisioning.

Raises:

hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text in one fused call.

Parameters:

  • collection (str) – The collection to search. Must have been created with ensure_collection(..., hybrid=True).
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by keyword/BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword. Weaviate and Milvus apply this weight natively. Qdrant's native fusion (Reciprocal Rank Fusion) has no continuous weight, so it applies alpha by blending two independently-scored, min-max normalized result sets instead of a single native fused call.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds agrag.common.validation.MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0]. Enforced uniformly across backends since they otherwise fail differently outside that range.
agrag.vectordb.base.VectorStore.initialize
initialize() -> None

Check connectivity and authentication.

Raises:

  • VectorStoreError – The backend is unreachable, or the credentials are rejected.
agrag.vectordb.base.VectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.base.VectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The offset from a previous scroll call, or None to start at the beginning.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page offset, or None at the
  • str | None – end.
agrag.vectordb.base.VectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter: a scalar value means exact match, a list value means any of, and all keys are AND-ed together. Keys must be valid identifiers (letters, digits, underscore, not starting with a digit) to stay portable: Milvus compiles them into a filter expression and Neo4j's GraphStore counterpart compiles them into Cypher, so both reject other characters, while Qdrant and Weaviate accept arbitrary payload keys.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds agrag.common.validation.MAX_SEARCH_LIMIT. Enforced uniformly across backends since they otherwise fail differently outside that range.
agrag.vectordb.base.VectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises:

agrag.vectordb.build_vector_store

build_vector_store(value:VectorStoreName | VectorStore) -> VectorStore

Build a vector store from a backend name, or return one unchanged.

Parameters:

  • value (VectorStoreName | VectorStore) – "qdrant" or "weaviate", or an already-constructed VectorStore for full control over settings.

Returns:

agrag.vectordb.errors

Errors that the vector-store layer raises.

Classes:

agrag.vectordb.errors.CollectionDimensionMismatchError
CollectionDimensionMismatchError(*, expected:int, actual:int) -> None

Bases: VectorStoreError

A collection already exists with a different embedding dimension.

Attributes:

  • expected – The dimension the collection was created with.
  • actual – The dimension the caller requested.
agrag.vectordb.errors.CollectionDimensionMismatchError.actual
actual = actual
agrag.vectordb.errors.CollectionDimensionMismatchError.expected
expected = expected
agrag.vectordb.errors.VectorStoreError

Bases: Exception

The base class for every vector-store error.

agrag.vectordb.errors.VectorStoreMissingExtraError
VectorStoreMissingExtraError(extra:str) -> None

Bases: VectorStoreError

A vector store exists, but its package extra is not installed.

Attributes:

  • extra – The name of the package extra to install.
agrag.vectordb.errors.VectorStoreMissingExtraError.extra
extra = extra

agrag.vectordb.milvus

Milvus vector-store backend.

Classes:

  • MilvusVectorStore – A VectorStore backed by Milvus, including native hybrid search.

Attributes:

agrag.vectordb.milvus.MAX_RESPONSE_LIMIT
MAX_RESPONSE_LIMIT = 16384
agrag.vectordb.milvus.MilvusVectorStore
MilvusVectorStore(*, settings:MilvusSettings | None = None, client:Any | None = None) -> None

Bases: VectorStore

A VectorStore backed by Milvus, including native hybrid search.

The client connects lazily on first use, so constructing the store does not open a network connection. Milvus performs BM25 server-side, so hybrid search needs no client-side sparse embedder; the sparse vector is computed by a Milvus Function from the text field on write and at query time.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its entities.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text in one fused call.
  • initialize – Check connectivity and authentication.
  • invalidate_collection – Drop cached distance-metric knowledge of a collection.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.

Parameters:

  • settings (MilvusSettings | None) – Milvus connection settings. Defaults to MilvusSettings().
  • client (Any | None) – A pre-built AsyncMilvusClient, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
agrag.vectordb.milvus.MilvusVectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.milvus.MilvusVectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.milvus.MilvusVectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.

Returns:

  • int – The number of matching records.
agrag.vectordb.milvus.MilvusVectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.milvus.MilvusVectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its entities.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.milvus.MilvusVectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Milvus performs BM25 server-side, so the sparse field and its Function are always provisioned; the hybrid flag is accepted for interface parity but is a no-op here. An existing collection must already carry this same fixed schema, since upsert and hybrid_search always read and write every field regardless of hybrid.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – Accepted for interface parity; ignored by Milvus.

Raises:

hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text in one fused call.

Fusion uses Milvus's native weighted reranker, which normalizes each request's scores before applying alpha.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0].
agrag.vectordb.milvus.MilvusVectorStore.initialize
initialize() -> None

Check connectivity and authentication.

agrag.vectordb.milvus.MilvusVectorStore.invalidate_collection
invalidate_collection(name:str) -> None

Drop cached distance-metric knowledge of a collection.

This store caches a collection's distance metric after the first call that resolves it, on the assumption that it alone (via ensure_collection/delete_collection) owns the collection's lifecycle for as long as this instance is in use. If something outside this instance deletes and recreates a collection under the same name with a different metric, call this first so the next call re-resolves that collection's metric from the backend instead of trusting the stale cache.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.milvus.MilvusVectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Requests at most MAX_RESPONSE_LIMIT ids per call, so a large ids list cannot exceed Milvus's response-size ceiling in one request the way sending every id at once would.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.milvus.MilvusVectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Milvus rejects a query whose offset + limit exceeds MAX_RESPONSE_LIMIT, so a numeric offset cannot page past that many total records. Pages instead cursor on the id primary key: each page filters on id > page_offset and orders by id ascending, which needs no offset at all and so never hits that window regardless of collection size. The explicit order is load bearing: without it, an unordered query result could omit rows at or below the next cursor, permanently skipping them on the next page.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The id cursor from a previous scroll call.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page cursor, or None at the
  • str | None – end.
agrag.vectordb.milvus.MilvusVectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on scalar fields.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT.
agrag.vectordb.milvus.MilvusVectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises:

agrag.vectordb.qdrant

Qdrant vector-store backend.

Classes:

  • QdrantVectorStore – A VectorStore backed by Qdrant, including native hybrid search.
agrag.vectordb.qdrant.QdrantVectorStore
QdrantVectorStore(*, settings:QdrantSettings | None = None, sparse_embedder:SparseEmbedder | None = None, client:Any | None = None, models:Any | None = None) -> None

Bases: VectorStore

A VectorStore backed by Qdrant, including native hybrid search.

The client connects lazily on first use, so constructing the store does not open a network connection. Hybrid search builds its sparse query with a SparseEmbedder that defaults to FastEmbed BM25 and loads only when a hybrid call first runs, not at construction.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its points.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text, fused by a weighted blend.
  • initialize – Check connectivity and authentication.
  • invalidate_collection – Drop cached hybrid-state and distance-metric knowledge of a collection.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.

Parameters:

  • settings (QdrantSettings | None) – Qdrant connection settings. Defaults to QdrantSettings().
  • sparse_embedder (SparseEmbedder | None) – The sparse embedder hybrid search uses. Defaults to a lazily-built FastEmbedBM25Embedder.
  • client (Any | None) – A pre-built AsyncQdrantClient, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
  • models (Any | None) – The qdrant_client.models module, for tests. Pair with client so filter/payload helpers work without needing the real qdrant_client package installed at all.
agrag.vectordb.qdrant.QdrantVectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.qdrant.QdrantVectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.qdrant.QdrantVectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

  • int – The number of matching records.
agrag.vectordb.qdrant.QdrantVectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.qdrant.QdrantVectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its points.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.qdrant.QdrantVectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – Whether to provision the named sparse vector hybrid search needs.

Raises:

hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text, fused by a weighted blend.

Qdrant's native fusion methods (RRF, DBSF) have no continuous dense/keyword weight, so this runs the dense and sparse (BM25) searches independently, min-max normalizes each result set's scores to [0, 1], then combines them per id as alpha * dense + (1 - alpha) * sparse. Each side fetches a wider candidate pool than limit so a document strong on only one signal still has a chance to reach the blended top results.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword.

Returns:

  • list[VectorHit] – The blended hits, highest combined score first.

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0].
agrag.vectordb.qdrant.QdrantVectorStore.initialize
initialize() -> None

Check connectivity and authentication.

agrag.vectordb.qdrant.QdrantVectorStore.invalidate_collection
invalidate_collection(name:str) -> None

Drop cached hybrid-state and distance-metric knowledge of a collection.

This store caches a collection's hybrid support and distance metric after the first call that resolves them, on the assumption that it alone (via ensure_collection/delete_collection) owns the collection's lifecycle for as long as this instance is in use. If something outside this instance deletes and recreates a collection under the same name with different config, call this first so the next call re-resolves that collection's state from the backend instead of trusting the stale cache.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.qdrant.QdrantVectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.qdrant.QdrantVectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The offset from a previous scroll call.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page offset, or None at the
  • str | None – end.
agrag.vectordb.qdrant.QdrantVectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT.
agrag.vectordb.qdrant.QdrantVectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

When collection has sparse-vector support (created or previously seen with ensure_collection(..., hybrid=True)), each record's payload["text"] is also sparse-embedded and stored under the named sparse vector, so hybrid_search's keyword arm has real vectors to match. A record with no text payload key gets an empty sparse vector and only ever surfaces through the dense side of a hybrid search.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises:

agrag.vectordb.settings

Settings for vector-store backends.

Classes:

agrag.vectordb.settings.MilvusSettings

Bases: BaseSettings

Milvus connection configuration.

Attributes:

  • uri (str) – The Milvus endpoint URI. Env: MILVUS_URI.
  • token (str) – The Milvus auth token. Empty string for an unauthenticated instance. Env: MILVUS_TOKEN.
  • require_tls (bool) – When True, reject a plaintext uri to a non-local host even with no token configured. Off by default since many deployments run an unauthenticated Milvus on a private network and rely on network segmentation rather than transport encryption. Env: MILVUS_REQUIRE_TLS.

Raises:

  • ValueErroruri is plaintext (http), points at a non-local host, and either token is set or require_tls is True. Use https for a remote Milvus instance.
agrag.vectordb.settings.MilvusSettings.model_config
model_config = SettingsConfigDict(env_prefix='MILVUS_', env_file='.env', extra='ignore')
agrag.vectordb.settings.MilvusSettings.require_tls
require_tls: bool = False
agrag.vectordb.settings.MilvusSettings.token
token: str = ''
agrag.vectordb.settings.MilvusSettings.uri
uri: str = 'http://localhost:19530'
agrag.vectordb.settings.QdrantSettings

Bases: BaseSettings

Qdrant connection configuration.

Attributes:

  • url (str) – The Qdrant endpoint URL. Env: QDRANT_URL.
  • api_key (str) – The Qdrant API key. Env: QDRANT_API_KEY.
  • require_tls (bool) – When True, reject a plaintext url to a non-local host even with no api_key configured. Off by default since many deployments run an unauthenticated Qdrant on a private network and rely on network segmentation rather than transport encryption. Env: QDRANT_REQUIRE_TLS.

Raises:

  • ValueErrorurl is plaintext (http), points at a non-local host, and either api_key is set or require_tls is True. Use https for a remote Qdrant instance.
agrag.vectordb.settings.QdrantSettings.api_key
api_key: str = ''
agrag.vectordb.settings.QdrantSettings.model_config
model_config = SettingsConfigDict(env_prefix='QDRANT_', env_file='.env', extra='ignore')
agrag.vectordb.settings.QdrantSettings.require_tls
require_tls: bool = False
agrag.vectordb.settings.QdrantSettings.url
url: str = 'http://localhost:6333'
agrag.vectordb.settings.WeaviateSettings

Bases: BaseSettings

Weaviate connection configuration.

Attributes:

  • mode (Literal['cloud', 'custom']) – "cloud" connects to Weaviate Cloud. "custom" connects to a self-hosted instance (used by integration tests against the local Docker Compose instance) — an explicit field, not inferred from the URL, since inference caused real connection bugs in surveyed reference implementations. Env: WEAVIATE_MODE.
  • url (str) – The Weaviate endpoint URL. For "cloud", the cluster URL. For "custom", the full host URL. Env: WEAVIATE_URL.
  • api_key (str) – The Weaviate API key. Env: WEAVIATE_API_KEY.
  • grpc_port (int) – The gRPC port, used by "custom" mode only ("cloud" mode infers it). Env: WEAVIATE_GRPC_PORT.
  • require_tls (bool) – When True, reject a plaintext url to a non-local host even with no api_key configured. Off by default since many deployments run an unauthenticated Weaviate on a private network and rely on network segmentation rather than transport encryption. Env: WEAVIATE_REQUIRE_TLS.

Raises:

  • ValueErrorurl is plaintext (http), points at a non-local host, and either api_key is set or require_tls is True. Use https for a remote Weaviate instance.
agrag.vectordb.settings.WeaviateSettings.api_key
api_key: str = ''
agrag.vectordb.settings.WeaviateSettings.grpc_port
grpc_port: int = 50051
agrag.vectordb.settings.WeaviateSettings.mode
mode: Literal['cloud', 'custom'] = 'custom'
agrag.vectordb.settings.WeaviateSettings.model_config
model_config = SettingsConfigDict(env_prefix='WEAVIATE_', env_file='.env', extra='ignore')
agrag.vectordb.settings.WeaviateSettings.require_tls
require_tls: bool = False
agrag.vectordb.settings.WeaviateSettings.url
url: str = 'http://localhost:8080'

agrag.vectordb.weaviate

Weaviate vector-store backend.

Classes:

agrag.vectordb.weaviate.WeaviateVectorStore
WeaviateVectorStore(*, settings:WeaviateSettings | None = None, client:Any | None = None) -> None

Bases: VectorStore

A VectorStore backed by Weaviate, including native hybrid search.

The client connects lazily on first use, so constructing the store does not open a network connection. Weaviate does its own server-side BM25, so hybrid search needs no client-side sparse embedder.

Functions:

  • close – Release the backend connection.
  • collection_exists – Report whether a collection exists.
  • count – Count records in a collection.
  • delete – Delete records by id.
  • delete_collection – Delete a collection and all its objects.
  • ensure_collection – Create the collection if it does not exist.
  • hybrid_search – Search by dense vector and keyword text in one fused call.
  • initialize – Open the connection and check authentication.
  • retrieve – Fetch records by id.
  • scroll – Iterate records in a collection, in batches.
  • search – Search by dense vector only.
  • upsert – Write or overwrite records in a collection.

Parameters:

  • settings (WeaviateSettings | None) – Weaviate connection settings. Defaults to WeaviateSettings().
  • client (Any | None) – A pre-built Weaviate async client, for tests. When set, __init__ imports nothing and the store calls this object directly instead of building one.
agrag.vectordb.weaviate.WeaviateVectorStore.close
close() -> None

Release the backend connection.

agrag.vectordb.weaviate.WeaviateVectorStore.collection_exists
collection_exists(name:str) -> bool

Report whether a collection exists.

Parameters:

  • name (str) – The collection name.

Returns:

  • boolTrue if the collection exists.
agrag.vectordb.weaviate.WeaviateVectorStore.count
count(collection:str, *, filters:dict[str, Any] | None = None) -> int

Count records in a collection.

Parameters:

  • collection (str) – The collection to count.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

  • int – The number of matching records.
agrag.vectordb.weaviate.WeaviateVectorStore.delete
delete(collection:str, ids:Sequence[UUID]) -> None

Delete records by id.

Parameters:

  • collection (str) – The collection to delete from.
  • ids (Sequence[UUID]) – The ids to delete.
agrag.vectordb.weaviate.WeaviateVectorStore.delete_collection
delete_collection(name:str) -> None

Delete a collection and all its objects.

Parameters:

  • name (str) – The collection name.
agrag.vectordb.weaviate.WeaviateVectorStore.ensure_collection
ensure_collection(name:str, *, dimensions:int, distance:Distance, hybrid:bool = False) -> None

Create the collection if it does not exist.

Parameters:

  • name (str) – The collection name.
  • dimensions (int) – The embedding dimension.
  • distance (Distance) – The distance metric new collections use.
  • hybrid (bool) – No-op for Weaviate, which needs no sparse provisioning.

Raises:

  • CollectionDimensionMismatchError – An existing object in the collection carries a vector of a different dimension. Weaviate keeps no schema-level dimension for self-provided vectors, so an existing collection with no vector-bearing object cannot be checked this way.
hybrid_search(collection:str, query_vector:Sequence[float], query_text:str, *, limit:int = 10, filters:dict[str, Any] | None = None, alpha:float = 0.5) -> list[VectorHit]

Search by dense vector and keyword text in one fused call.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • query_text (str) – The query text, matched by keyword/BM25.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • alpha (float) – The dense/keyword balance. 1.0 is pure dense, 0.0 is pure keyword.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT, or alpha is outside [0.0, 1.0].
agrag.vectordb.weaviate.WeaviateVectorStore.initialize
initialize() -> None

Open the connection and check authentication.

agrag.vectordb.weaviate.WeaviateVectorStore.retrieve
retrieve(collection:str, ids:Sequence[UUID]) -> list[VectorRecord]

Fetch records by id.

Parameters:

  • collection (str) – The collection to read.
  • ids (Sequence[UUID]) – The ids to fetch.

Returns:

agrag.vectordb.weaviate.WeaviateVectorStore.scroll
scroll(collection:str, *, limit:int = 100, page_offset:str | None = None, filters:dict[str, Any] | None = None, with_vectors:bool = False) -> tuple[list[VectorRecord], str | None]

Iterate records in a collection, in batches.

Parameters:

  • collection (str) – The collection to read.
  • limit (int) – The maximum number of records per page.
  • page_offset (str | None) – The cursor id from a previous scroll call.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.
  • with_vectors (bool) – Whether to return each record's vector.

Returns:

  • list[VectorRecord] – The page of records and the next page cursor, or None at the
  • str | None – end.
agrag.vectordb.weaviate.WeaviateVectorStore.search
search(collection:str, query_vector:Sequence[float], *, limit:int = 10, filters:dict[str, Any] | None = None) -> list[VectorHit]

Search by dense vector only.

Parameters:

  • collection (str) – The collection to search.
  • query_vector (Sequence[float]) – The dense query embedding.
  • limit (int) – The maximum number of hits to return.
  • filters (dict[str, Any] | None) – A flat-dict filter on payload fields.

Returns:

Raises:

  • ValueErrorlimit is not positive, or exceeds MAX_SEARCH_LIMIT.
agrag.vectordb.weaviate.WeaviateVectorStore.upsert
upsert(collection:str, records:Sequence[VectorRecord], *, batch_size:int = 256) -> None

Write or overwrite records in a collection.

Uses Weaviate's batch import, which replaces an existing object sharing a written id instead of rejecting it, giving real insert-or-replace semantics and per-call batching in one request.

Parameters:

  • collection (str) – The collection to write to.
  • records (Sequence[VectorRecord]) – The records to upsert, in order.
  • batch_size (int) – The number of records per backend write call. Must be positive.

Raises: