Virtual Context can be configured through environment variables, configuration files, or programmatic options when using the Python API. This page documents all available configuration parameters for storage backends, embedding models, compaction models, retrieval tuning, and proxy behavior.

The most common configuration choices are the storage backend (SQLite for single-instance development, PostgreSQL for production multi-tenant setups), the embedding model used for semantic retrieval (defaults to text-embedding-3-small), and the compaction model that generates summaries and extracts structured facts during the hierarchical compression pipeline.

For a broader understanding of how these configuration options affect the system pipeline, see the architecture documentation. For benchmark results showing how different configurations affect recall quality and token efficiency, see the benchmark results.

Configuration

Storage, models and tuning parameters.

Minimal Config

Configuration lives in a YAML file, discovered automatically or named with -c. The dataclass defaults in the engine are the source of truth; this page documents the keys most deployments touch.

version: "0.2"

context_window: 120000

tag_generator:
  type: "llm"
  provider: "anthropic"
  model: "claude-haiku-4-5-20251001"

summarization:
  provider: "anthropic"
  model: "claude-haiku-4-5-20251001"

storage:
  backend: "sqlite"

Defaults That Affect You

These are on out of the box. Each one changes behaviour in a deployment that has configured nothing, so they are worth reading before the reference below.

KeyDefaultWhat it does when you leave it alone
compaction.code_modetrueCoding-oriented summarization prompts, investigatory actions excluded from facts, code references emitted. Applies to non-coding deployments too.
compaction.pre_compaction_filteringaggressiveDrops tag-irrelevant turns from the payload before the first compaction, using the standard protected window.
compaction.skip_active_tagstrueDoes not re-retrieve tags whose content is already in the raw window.
compaction.session_gap_minutes30Silently splits segments at conversational time gaps, so widely spaced messages summarize separately even on the same topic.
tag_generator.temporal_heuristic_enabledtrueMarks a query temporal from date patterns even when the tagger did not, routing it to time-scoped retrieval.
search.tool_guard_enabledtrueStops search-tool bursts past the threshold for the rest of the window.
history_widening_threshold0.10When a client widens its history window backward, clears ingest state and re-ingests the full history.

pre_compaction_filtering takes off, conservative or aggressive: off passes every turn through, conservative drops with a doubled protected window, and the default drops with the standard one. After the first compaction the standard window always applies regardless.

Top-Level

KeyDescription
versionConfig schema version
context_windowTotal token budget for the context window
token_counterCounting mode: anthropic, tiktoken or estimate
storage_rootRoot directory for data files

Tag Generator

tag_generator:
  type: "llm"                       # "llm" or "keyword"
  provider: "anthropic"
  model: "claude-haiku-4-5-20251001"
  max_tags: 10
  min_tags: 5

llm tags completed turns with the configured model and gives the best quality. keyword uses a configured vocabulary: fastest, lowest quality, useful for testing or very cost-sensitive setups.

tag_generator:
  prompt_mode: "detailed"           # or "compact" for cheaper models
  temporal_heuristic_enabled: true  # on by default
  context_lookback_pairs: 5         # recent turn pairs fed as tagger context
  context_bleed_threshold: 0.1      # embedding gate on that context
  disable_thinking: false           # prepend /no_think (local-model workaround)
  keyword_fallback:
    tag_keywords: {}                # tag -> keywords, for type: "keyword"
    tag_patterns: {}                # tag -> regex patterns
  tag_splitting:
    enabled: false                  # auto-split overly broad tags
    frequency_threshold: 15

keyword_fallback is how you shape deterministic tagging: with type: "keyword", tags come only from that vocabulary. disable_thinking is a model workaround for local models that emit thinking blocks and break JSON tagging; leave it off unless you hit that.

Inbound tagging is configured separately under retrieval.inbound_tagger_type. The default embedding assigns tags by vector similarity against the existing vocabulary using a local model, with no model call on the request path. The LLM tagger always tags the completed turn on the background path, so every turn ends up LLM-tagged regardless.

Compaction

compaction:
  soft_threshold: 0.70
  hard_threshold: 0.85
  protected_recent_turns: 6
  min_summary_tokens: 200
  max_summary_tokens: 2000
  defer_payload_mutation: false     # preserve the prompt-cache prefix while warm
  flush_ttl_seconds: 300            # cache considered cold after this idle time
  fill_pass_enabled: false          # rewrite payload toward a target fill each request
  store_recovery_threshold: 0.70    # recover from store below this fraction of stored turns

Thresholds are fractions of the context window: at 0.70 with a 120K window, compaction starts around 84K tokens in use. Protected turns are never compacted, keeping recent conversation at full fidelity. Setting that too high wastes budget; too low loses recent context.

defer_payload_mutation and the flush gate are covered under engine internals. fill_pass_enabled conflicts with deferral because it rewrites the payload every request, and the proxy warns when both are on.

Segmentation and merge tuning

compaction:
  summary_ratio: 0.15               # target summary size as a fraction of source
  max_segment_tokens: 2000
  max_segment_turns: 20             # 0 = unlimited
  tag_overlap_threshold: 0.5        # min overlap to keep turns in one segment
  tool_result_segment_threshold: 50000   # bytes; larger results get their own segment
  merge_lookback: 5                 # segments checked for a merge candidate
  merge_overlap_threshold: 0.35
  max_concurrent_summaries: 4
  session_gap_minutes: 30           # on by default
  code_mode: true                   # on by default

Summarization

summarization:
  provider: "anthropic"
  model: "claude-haiku-4-5-20251001"
  temperature: 0.3                  # lower is more faithful

The summarization model is independent of your upstream. A small fast model is usually the right choice here even when the upstream is a frontier model.

Storage

storage:
  backend: "sqlite"                 # sqlite | filesystem | postgres | neo4j | falkordb
  sqlite:
    path: ".virtualcontext/store.db"
  postgres:
    dsn: "postgresql://user:pass@host:5432/vc"

SQLite is the default and needs no setup. PostgreSQL, which requires the postgres extra, is the backend for multi-worker deployments. Neo4j and FalkorDB add graph-based fact traversal. The filesystem backend stores segments as Markdown with YAML frontmatter and does not host the full feature set.

Retrieval

retrieval:
  inbound_tagger_type: "embedding"
  embedding_model: "all-MiniLM-L6-v2"
  active_tag_lookback: 4
  anchorless_lookback: 6
  strategy_config:
    default:
      max_results: 10
      max_budget_fraction: 0.25
      include_related: true

Only the default strategy entry is read; per-strategy overrides beyond it have no effect. active_tag_lookback excludes tags from the last N turns because their content is already in raw history; higher values reduce redundancy but risk missing older content under the same tags. max_budget_fraction caps injected context as a fraction of the window.

Scoring

retrieval:
  scoring:
    idf_weight: 0.50
    bm25_weight: 0.30
    embedding_weight: 0.20
    rrf_k: 60
    embedding_context_turns: 0      # 0 = bare query only
    embedding_context_guard: true
    embedding_reserved_seats: 0
    dampening:
      hub_enabled: true             # penalize tags above the p90 segment count
      gravity_enabled: true         # halve embedding scores with no BM25 support
      resolution_enabled: true      # boost fact-bearing tags

embedding_context_turns blends the last N turns into the query vector, so an under-specified question inherits the elided topic from recent context. embedding_context_guard then scores each tag by the better of its bare-query and blended similarity, so irrelevant recent context can never demote a relevant tag. embedding_reserved_seats forces the top embedding-only candidates into the fused results, for queries where the embedding signal is the only one that finds the right topic.

Assembly

assembly:
  tag_context_max_tokens: 30000
  recent_turns_always_included: 3
  context_hint_enabled: true
  context_hint_max_tokens: 2000
  protected_window_db_source: "off"  # "merge" for unified multi-channel conversations
  actor_card_enabled: false          # per-person memory cards; ships dark
  speaker_roster_enabled: false      # participant roster; ships dark

The speaker gates, the actor-card keys and how the attribution subsystem fits together are documented on the attribution page.

Core Context

assembly.core_files pins file content into every assembled context, which makes it a capability rather than a tuning knob: a project brief, a style guide or a schema can be made permanently present without relying on retrieval to surface it.

assembly:
  core_files:
    - path: "PROJECT.md"
      priority: 8
  core_context_max_tokens: 18000
  facts_max_tokens: 20000

Each entry takes a path and an optional priority, defaulting to 5, with higher rendering first. Files are read from disk, concatenated with per-file headers and budgeted by core_context_max_tokens. Missing files are skipped silently, so a typo in a path degrades to no pinned content rather than an error.

Paging

paging:
  enabled: false
  auto_promote: true
  auto_evict: true
  max_tool_loops: 10

When enabled, models on the configured autonomous-model list receive the paging tools and drive topic expansion themselves.

Tool Output

tool_output:
  enabled: false                    # intercept and truncate oversized tool results
  default_truncate_threshold: 8192
  default_head_ratio: 0.6
  default_tail_ratio: 0.4

Facts

facts:
  graph_links: true
  link_types: [supersedes, caused_by, part_of, contradicts, same_as, related_to]

Supersession and curation are separate optional passes over the fact store, both disabled by default, each taking its own provider and model.

Providers

providers:
  openrouter:
    base_url: "https://openrouter.ai/api/v1"
    api_key_env: "OPENROUTER_API_KEY"

summarization:
  provider: "openrouter"
  model: "some/model-slug"

The block declares named endpoints that the tagger and summarizer refer to by label, and onboard and init generate it for you. When a providers block is present, summarization.provider must name one of its entries or validation fails. api_key_env names the environment variable holding the key, which is how non-Anthropic keys are wired in.

Proxy

The listen address comes from the CLI, not YAML: --host defaults to 127.0.0.1 and --port to 5757. The block below configures logging, limits, the session cache and multi-instance mode.

proxy:
  request_log_dir: null
  llm_calls_log: null
  upstream_context_limit: null
  passthrough_trim_ratio: 0.40
  redis_url: null                   # or the REDIS_URL environment variable
  redis_history_cap: 200

  instances:
    - port: 5757
      upstream: "https://api.anthropic.com"
      label: "anthropic"
      config: "./virtual-context-proxy-anthropic.yaml"

Each instance may carry its own config file with isolated storage, tagger and summarizer settings. Instances without a config field share the master engine.

Tag Rules

tag_rules:
  - match: "legal-*"
    priority: 1
    summary_prompt: "Summarize precisely, preserving dates, names, and docket numbers."

match is a glob pattern over tag names and priority breaks ties when several rules match, lower winning. Tag rules override the summarization prompt for matching segments; they do not force segments into the assembled context.

Presets and Validation

Two presets ship with the package. coding uses aggressive compaction, code-specific keyword fallbacks, tool output interception and the fact graph, tuned for tool-heavy development sessions. agentic is balanced for autonomous agent conversations across any domain, also with the fact graph enabled.

virtual-context presets list
virtual-context presets show coding      # print the preset’s full config as YAML
virtual-context init coding              # bootstrap a config file from it
virtual-context config validate

presets show prints exactly what a preset sets, so the summaries above never need to be taken on trust.

Validation reports missing required fields, invalid types and cross-field violations such as a soft threshold at or above the hard threshold.

Environment Variables

VariablePurpose
ANTHROPIC_API_KEYKey for the Anthropic provider, read directly
OPENAI_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEYProvider keys, honored through a providers.<label>.api_key_env entry
REDIS_URLRedis session cache URL; overrides proxy.redis_url
VC_DASHBOARD_TOKENRequire a token on dashboard endpoints; unauthenticated when unset
DATABASE_URLPostgres DSN fallback for the CLI, consulted only when neither a storage flag nor -c was given
VC_DATA_DIRData directory for deployments whose store has no local database path. Media originals saved by image compression land beneath it per conversation, and are cleaned up when a conversation is deleted
VIRTUAL_CONTEXT_CONFIGConfig path override, read by the MCP server only