Skip to Content
GuidesConfiguration

Configuration Reference

PRME uses pydantic-settings  for configuration. All settings can be set via:

  1. Constructor arguments
  2. Environment variables (with PRME_ prefix)
  3. .env files

Nested settings use double underscores: PRME_EMBEDDING__PROVIDER=openai.

Quick Setup

Minimal .env for LLM-powered ingestion:

# Required for ingest() — pick one provider OPENAI_API_KEY=sk-... # or ANTHROPIC_API_KEY=sk-ant-... # Optional: extraction model PRME_EXTRACTION__PROVIDER=openai PRME_EXTRACTION__MODEL=gpt-4o-mini

For store-only usage (no LLM), no configuration is needed.

Storage Paths

VariableDefaultDescription
PRME_DB_PATH./memory.duckdbDuckDB database file
PRME_VECTOR_PATH./vectors.usearchusearch HNSW index file
PRME_LEXICAL_PATH./lexical_indexTantivy full-text index directory
PRME_PACK_PATHNoneOpt-in single-file pack, conventionally a .prme file: one DuckDB database that also holds the vector snapshot and the pack manifest, with the lexical index rebuilt in memory on open. When set, PRME_DB_PATH, PRME_VECTOR_PATH and PRME_LEXICAL_PATH are ignored. It cannot be combined with PRME_DATABASE_URL, and an empty value means unset. See single-file packs
PRME_DATABASE_URLNonePostgreSQL URL (overrides DuckDB)

Without PRME_PACK_PATH, a local pack is the directory layout of the three paths above, which is the default.

Embedding

VariableDefaultDescription
PRME_EMBEDDING__PROVIDERfastembedfastembed (local) or openai
PRME_EMBEDDING__MODEL_NAMEBAAI/bge-small-en-v1.5Embedding model
PRME_EMBEDDING__DIMENSION384Embedding dimension
PRME_EMBEDDING__API_KEYNoneAPI key for OpenAI embeddings

The default fastembed provider runs locally with no API key needed. It downloads the model on first use (~130MB).

Extraction (LLM)

VariableDefaultDescription
PRME_EXTRACTION__PROVIDERopenaiopenai, anthropic, or ollama
PRME_EXTRACTION__MODELgpt-4o-miniModel name
PRME_EXTRACTION__MAX_RETRIES3Retry count for API failures
PRME_EXTRACTION__TIMEOUT30.0Timeout in seconds
PRME_EXTRACTION__API_KEYNoneCredential; defaults to the provider’s own (OPENAI_API_KEY, ANTHROPIC_API_KEY)
PRME_EXTRACTION__BASE_URLNoneEndpoint; defaults to the provider’s own (OPENAI_BASE_URL, ANTHROPIC_BASE_URL, or a local Ollama)

The extraction provider is used by ingest() and ingest_batch(), and by retrieve() only when PRME_ENABLE_QUERY_REFORMULATION=true: query reformulation calls the same provider, model, endpoint and credential, with the same timeout. The store() method does not call an LLM.

Extracted claims (ingest())

These settings change what ingest() stores from an extraction. They do not affect store(), and records stored before a change keep their text.

VariableDefaultDescription
PRME_ENABLE_CLAIM_MERGEtrueMerge an exactly repeated extracted claim into one current record: the new fact keeps the earlier copies’ evidence and supersedes them (issue #209). Quantities, conditional claims, unresolved relative dates and claims that are not observed or asserted stay separate. false stores one fact per extraction
PRME_ENABLE_CLAIM_SENTENCE_TEXTtrueStore a claim’s own sentences as its text: the shortest run of whole sentences of its passage that mentions its subject and object, with a following sentence that qualifies it (only if, unless, however). The paragraph stays its evidence_quote, so a qualifier elsewhere in the paragraph stays in the evidence but not in the text. false gives every claim from a one-paragraph message the whole message as its text. Default since 2026-10-06; provisional
PRME_ENABLE_SPEAKER_REFERENCEStrueIn a turn that names its speaker (ingest(speaker=...)), bind I, me, my, mine and myself in its claims to that speaker’s entity, so the speaker’s claims connect across messages and a repeated or replaced claim can merge with or supersede the earlier one. Plural, second- and third-person references stay local to their message. Only the caller knows that the named speaker, and not someone the turn quotes, is who says I, so set it to false when a speaker’s turns quote others in the first person. Default since 2026-10-06; provisional. See entity identity
PRME_ENABLE_SPEAKER_GROUNDINGfalseLet a named speaker’s own I, me and my count as a mention of the speaker when grounding checks a claim’s subject and object, so a claim such as “Dana, lives in, Denver” for “I live in Denver” is kept. It completes the graph, but the extra claims cost evidence on the offline evidence gate, so it stays off. Provisional
PRME_ENABLE_WINDOWED_EXTRACTIONfalseShow the extractor the preceding turns of the same session, so it can read what the new turn refers to (issue #91). Only the new turn is extracted and grounded. Needs a session_id; provisional
PRME_EXTRACTION_WINDOW_TURNS4How many preceding turns the window shows, from 0 to 32; 0 turns the window off. Each turn adds prompt tokens to every extraction. Provisional
PRME_ENABLE_FACT_TEXT_RESOLUTIONfalseAsk the extractor for fact text that stands alone: a pronoun for a named person becomes the name and a relative date the date it means (issue #91). A deterministic check keeps that text only when those replacements are its sole changes; otherwise the fact keeps its source passage. Names from earlier turns need PRME_ENABLE_WINDOWED_EXTRACTION. Provisional

Scoring Weights

These control the hybrid retrieval scoring formula. The six additive weights must sum to 1.0. The default fusion is rrf, which ranks by semantic and lexical rank and does not use the additive weights; set PRME_SCORING__FUSION=weighted to score with them. Setting any other PRME_SCORING__* variable keeps rrf and its recency boost and tie-break unless PRME_SCORING__FUSION is set too.

VariableDefaultSignal
PRME_SCORING__W_SEMANTIC0.25Vector cosine similarity
PRME_SCORING__W_LEXICAL0.20BM25 full-text score
PRME_SCORING__W_GRAPH0.20Graph proximity
PRME_SCORING__W_RECENCY0.10Time decay: exp(-lambda * days)
PRME_SCORING__W_SALIENCE0.10Virtual salience with decay
PRME_SCORING__W_CONFIDENCE0.15Virtual confidence with decay
PRME_SCORING__W_EPISTEMIC0.05Epistemic type multiplier
PRME_SCORING__W_PATHS0.00Path count tiebreaker
PRME_SCORING__RECENCY_LAMBDA0.02Decay rate for recency
PRME_SCORING__TEMPORAL_BOOST0.15Bonus for temporal queries
PRME_SCORING__FUSIONrrfrrf: reciprocal rank fusion of semantic and lexical ranks, which ignores the additive weights (RFC-0005 Section 7.2), or weighted: the sum above. A ScoringWeights object built in code defaults to weighted; PRMEConfig().scoring is rrf. Under rrf a result’s score is rank-based, so a request’s min_score is compared with its semantic_relevance, the semantic cosine similarity, instead; a floor tuned on weighted scores does not carry over, and a low-cosine exact keyword match is filtered out when a floor is set. If vector search fails or detects an embedding model mismatch and no result has a cosine, the floor is skipped and the response metadata sets min_score_skipped
PRME_SCORING__RRF_K60Rank constant for rrf; ignored, with a warning, under PRME_SCORING__FUSION=weighted
PRME_SCORING__RRF_RECENCY_BOOST0.25rrf only: on current-state questions, multiplies each fused score by 1 + boost x recency relative to the pool’s largest value, where recency is the weighted formula’s current-state recency (exp decay from the newest candidate, doubled for update wording), so the newer of two conflicting memories can rank first. Above 0 and at most 4. 0.25 passed the simulations with no significant change on the offline evidence gate; 1.0 lost LoCoMo evidence (RFC-0005 Section 7.2); provisional. Dropped when PRME_SCORING__FUSION=weighted is set; a ScoringWeights object built in code leaves it unset
PRME_SCORING__RRF_TIE_BREAKevent_timerrf only: event_time orders equal fused scores newest first instead of by path count and node ID. Dropped when PRME_SCORING__FUSION=weighted is set; a ScoringWeights object built in code leaves it unset
PRME_SCORING__RECENCY_TIMEunsetweighted only: event_time dates every memory by when it was stated (its event time, else when it was stored; never when it was last updated, and never its validity start) and measures recency from that against the request’s reference time. Unset, questions that are not about the current state measure it from when the memory was last updated or created, so history imported with a past event time and retrieved at a past reference time gets full recency everywhere. Current-state questions keep measuring back from the newest candidate. Memories without an event time, including entity, consolidation and profile nodes, count from when they were stored. On the offline evidence gate it lost LoCoMo evidence, so it stays off (RFC-0005 Section 7). Needs PRME_SCORING__FUSION=weighted; rank fusion’s recency boost and tie-break already use this clock, and rank fusion drops the setting with a warning. Turning it on or off makes a learned ranking profile built under the other setting inapplicable

Context Packing

The defaults below are PRMEConfig’s, and environment variables that set only some of them keep the others. A PackingConfig built in code starts from its own field defaults instead (auditable, balanced, no rank fusion session decay and no folding), which stored receipts and configurations rely on.

VariableDefaultDescription
PRME_PACKING__TOKEN_BUDGET4096Max tokens in packed context
PRME_PACKING__OVERHEAD_TOKENS100Reserved for formatting overhead
PRME_PACKING__CHARS_PER_TOKEN4.2Estimated chars per token
PRME_PACKING__CONTEXT_FORMATreaderreader (one plain line per record), auditable (one JSON object per record) or compact. Answerability and claim verification need PRME_PACKING__CONTEXT_CITATIONS=true with reader
PRME_PACKING__MULTIPATH_ORDERINGbalancedOrder within the multi-path tier: balanced, score (briefly the default with the reader format) or density
PRME_PACKING__CONTEXT_CITATIONSfalseReader format only: add [m3] references and fill context_references
PRME_PACKING__FOLD_REPEATED_TEXTtrueReader format only: pack each source text once. A record is left out when a packed record from one of its source events already shows its text, unless its line shows another date, tag or speaker, and its budget goes to other records; the auditable and compact formats ignore the setting. Default since 2026-10-06. Receipts that record it use schema version 22, and with false default retrievals write version 19. See folding repeated text; provisional
PRME_PACKING__MIN_FIDELITYreferenceLowest representation a record may fall back to. full, prose or structured keeps the text-free key_value and reference fallbacks, and blank records, out of every context format
PRME_PACKING__VECTOR_K500Vector search candidates. Count and list questions multiply it by PRME_PACKING__AGGREGATION_K_MULTIPLIER, up to PRME_PACKING__AGGREGATION_K_MAX. At 500 the saved benchmark packs return nearly every stored turn; the offline evidence gate reports each channel’s recall at smaller limits (issue #87)
PRME_PACKING__LEXICAL_K500BM25 search candidates, widened the same way. The entity-name and aggregation keyword scans add hits beyond this limit
PRME_PACKING__GRAPH_MAX_CANDIDATES150Graph search candidates, widened the same way
PRME_PACKING__GRAPH_MAX_HOPS3Max graph traversal depth
PRME_PACKING__SESSION_CONTEXT_WINDOW3Adjacent turns to include
PRME_PACKING__SESSION_CONTEXT_TOP_K20Top results for session expansion
PRME_PACKING__SESSION_CONTEXT_SCORE_DECAY0.85Fraction of the triggering result’s score that an adjacent turn inherits
PRME_PACKING__SESSION_CONTEXT_RANK_FUSION_SCORE_DECAY0.6Fraction for results scored with PRME_SCORING__FUSION=rrf, between 0 (exclusive) and 1, in place of the decay above; weighted scoring never uses it. Fused scores are compressed, so at 0.85 adjacent turns can crowd primary evidence out of the context. The offline evidence gate favored 0.6 (RFC-0005 Section 7.2); provisional
PRME_PACKING__SESSION_CONTEXT_PACKINGunsetOpt-in (issue #86). trigger_tier counts session expansion as a path on a turn another search found and gives a turn it added the multi-path tier of the match that brought it in; adjacent packs the same records and places each neighbor beside that match in session order. Needs session expansion. Receipts that record it use schema version 21; remove the variable (an empty value is invalid) to turn it off. Provisional
PRME_PACKING__AGGREGATION_K_MULTIPLIER3.0Multiplier applied to VECTOR_K, LEXICAL_K and GRAPH_MAX_CANDIDATES for count and list questions
PRME_PACKING__AGGREGATION_K_MAX2000Cap on each widened limit

Organizer

VariableDefaultDescription
PRME_ORGANIZER__OPPORTUNISTIC_ENABLEDtrueRun maintenance during retrieve/ingest
PRME_ORGANIZER__OPPORTUNISTIC_COOLDOWN3600Seconds between opportunistic runs
PRME_ORGANIZER__OPPORTUNISTIC_BUDGET_MS200Budget for opportunistic runs
PRME_ORGANIZER__DEFAULT_ORGANIZE_BUDGET_MS5000Default budget for explicit organize()
PRME_ORGANIZER__PROMOTION_AGE_DAYS7.0Days before auto-promotion
PRME_ORGANIZER__PROMOTION_EVIDENCE_COUNT1Min evidence refs for promotion
PRME_ORGANIZER__ARCHIVE_SALIENCE_THRESHOLD0.10Salience below this → archive candidate
PRME_ORGANIZER__ARCHIVE_CONFIDENCE_THRESHOLD0.40Confidence below this → archive candidate
PRME_ORGANIZER__DEDUP_SIMILARITY_THRESHOLD0.92Vector similarity for dedup
PRME_ORGANIZER__ALIAS_SIMILARITY_THRESHOLD0.85Similarity for entity alias detection
PRME_ORGANIZER__CONSOLIDATION_MIN_CLUSTER_SIZE3Min cluster size for consolidation
PRME_ORGANIZER__CONSOLIDATION_SIMILARITY_THRESHOLD0.80Similarity for consolidation

Optional Features

VariableDefaultDescription
PRME_ENABLE_STORE_SUPERSEDENCEfalseAuto-detect supersedence on store
PRME_REINFORCE_SIMILARITY_THRESHOLDNoneAuto-reinforce similar existing nodes
PRME_ENABLE_SURPRISE_GATINGfalseNovelty-based salience adjustment
PRME_ENABLE_RERANKERfalseRerank the top scored candidates with a local cross-encoder after scoring. Needs pip install prme[reranker] and the model files. Experimental: it has not improved answer scores so far, and on the offline evidence gate no cross-encoder rank-order variant beat rank fusion alone on both benchmarks at both 4K and 8K (issue #88)
PRME_RERANKER_MODELcross-encoder/ms-marco-MiniLM-L-6-v2Hugging Face cross-encoder the reranker loads
PRME_RERANKER_TOP_K100How many of the top scored candidates the reranker scores; the rest keep their order
PRME_RERANKER_POLICYlegacyscore_envelope gives the reranked candidates their original scores in the new order, so later stages see the same score scale; anchored_score_envelope also keeps the original top multi-path record first. legacy puts the blended scores on them. See experimental retrieval policies
PRME_RERANKER_PRIOR_WEIGHT0.3Weight of a reranked candidate’s own score in the new order; the cross-encoder gets the rest. 0.0 orders the reranked candidates by the cross-encoder alone (issue #88). A value other than 0.3 needs PRME_RERANKER_POLICY=score_envelope or anchored_score_envelope (the configuration is refused with legacy), is recorded in retrieval receipts and makes a learned ranking profile built under another value inapplicable
PRME_QUERY_INTENT_ORDERentity_firsttemporal_first treats a question with temporal wording (when, last, before, since and similar) or a date in it as temporal even when it names a person, place or organization, so it gets temporal affinity scoring. A present-tense question that only its wording put on the current-state path (“Who is Caroline dating since the breakup?”) leaves it unless it says current, now, latest or similar, and the temporal context guidance is added where the question’s wording does not already select it. A lone capitalized month or weekday word that is part of a name (“Who is June dating?”) is read as the name, not a date. entity_first checks the entity patterns first, so a temporal question that names someone gets no temporal affinity. Experimental (RFC-0005 Section 3); changing it makes a learned ranking profile built under the other order inapplicable

Encryption

VariableDefaultDescription
PRME_ENCRYPTION_ENABLEDfalseEnable encryption at rest. Takes effect only with PRME_ENCRYPTION_KEY set
PRME_ENCRYPTION_KEYNonePassphrase or raw key. The raw_key: prefix marks a raw Fernet key and passphrase: forces derivation; an unprefixed 44-character url-safe base64 value is read as a raw Fernet key, and anything else as a passphrase

A directory pack uses Fernet file encryption. A single-file pack (PRME_PACK_PATH) uses DuckDB’s native encryption instead: DuckDB encrypts the file and its write-ahead log with AES-256-GCM, and PRME turns on DuckDB’s temp_file_encryption, which is off by default even for an encrypted database, with temporary files in <pack>.tmp beside the pack. For a single-file pack, a passphrase becomes the DuckDB key through PBKDF2-HMAC-SHA256 (600,000 iterations) with the fixed salt prme-single-file-pack-v1, so the same passphrase gives the same key for every pack; a raw key is used as given. A missing or wrong key, or encryption configured for an unencrypted pack, raises EncryptionError and leaves the file unchanged.

See Deployment for details.

Write Queue

VariableDefaultDescription
PRME_WRITE_QUEUE_SIZE1000DuckDB write queue size
PRME_MATERIALIZATION_QUEUE_SIZE500Graph materialization queue
PRME_MATERIALIZATION_BUDGET_MS100Budget per materialization batch

Programmatic Configuration

from prme.config import PRMEConfig config = PRMEConfig( db_path="./custom.duckdb", vector_path="./custom.usearch", lexical_path="./custom_lexical", extraction={"provider": "anthropic", "model": "claude-sonnet-4-5-20250514"}, embedding={"provider": "fastembed", "model_name": "BAAI/bge-small-en-v1.5"}, scoring={"w_semantic": 0.30, "w_lexical": 0.15}, ) from prme import MemoryClient client = MemoryClient(config=config)

Source: documentation/configuration.md  in PRME at 58caddb1.