Skip to content

Configure models and services ​

Use this guide to choose models, combine configuration examples and understand configuration scope. For field lookups, see server configuration or client configuration. For a first deployment, start with deployment paths.

OpenViking uses a JSON configuration file (~/.openviking/ov.conf) for settings.

Use this page when configuring the server. To connect to an existing server, follow CLI Setup; clients do not need model or storage settings.

For a first setup, use the wizard below. For an existing installation, find the relevant section and merge its fields into your JSON configuration; do not replace the whole file with a partial example. Editing ov.conf requires a restart. The limited runtime update surface is described below.

For a first-time setup, the recommended flow is:

bash
openviking-server init
openviking-server doctor

openviking-server init prompts for embedding and VLM settings separately. For API-based VLM choices such as OpenAI, Volcengine, Kimi, and GLM, enter the VLM API key when prompted. If you want to use Codex as the VLM provider, choose OpenAI Codex; the wizard can import existing Codex auth or guide you through login directly.

Configuration File ​

Create ~/.openviking/ov.conf in your home configuration directory:

json
{
  "service_name": "openviking-server",
  "storage": {
    "workspace": "./data",
    "vectordb": {
      "name": "context",
      "backend": "local"
    },
    "agfs": {
      "backend": "local"
    }
  },
  "embedding": {
    "dense": {
      "api_base" : "<api-endpoint>",
      "api_key"  : "<your-api-key>",
      "provider" : "<provider-type>",
      "dimension": 1024,
      "model"    : "<model-name>"
    }
  },
  "vlm": {
    "api_base" : "<api-endpoint>",
    "api_key"  : "<your-api-key>",
    "provider" : "<provider-type>",
    "model"    : "<model-name>"
  }
}

For provider: "openai-codex", vlm.api_key is optional when Codex OAuth is already available.

Top-level service_name defaults to "openviking-server". Cache-backed modules use it as their Redis namespace. OTel service_name fields keep their own value when set and inherit this value when omitted.

Ordered credentials in ov.conf ​

vlm and query_planner support an ordered credentials array. Embedding supports the same array inside each configured dense, sparse, or hybrid section. Index 0 has the highest priority; later entries provide failover credentials. The model sections remain JSON objects, and existing single-credential configurations remain supported.

Merge the following fields into your ov.conf; set PRIMARY_API_KEY and BACKUP_API_KEY before starting the server:

json
{
  "vlm": {
    "model": "your-vlm-model",
    "credentials": [
      {"id": "primary", "provider": "openai", "api_key": "${PRIMARY_API_KEY}"},
      {"id": "backup", "provider": "openai", "api_key": "${BACKUP_API_KEY}"}
    ]
  },
  "embedding": {
    "dense": {
      "model": "text-embedding-3-small",
      "dimension": 1536,
      "credentials": [
        {"id": "primary", "provider": "openai", "api_key": "${PRIMARY_API_KEY}"},
        {"id": "backup", "provider": "openai", "api_key": "${BACKUP_API_KEY}"}
      ]
    }
  }
}

Each credential can specify its own provider, model and connection fields; an omitted model uses the parent section's model. Embedding credentials must remain compatible with the parent model's vector dimension and input semantics. query_planner.credentials follows the VLM format. Rerank remains a single configuration and does not support a credentials array.

Configuration Scope and Update Lifecycle ​

OpenViking configuration has two layers:

  • Startup configuration is read from ov.conf. It defines the process baseline and the runtime configuration source. Changing it requires a service restart; the runtime configuration API never rewrites ov.conf.
  • Runtime settings are stored independently at Cluster and Account scope by the configured runtime configuration source. They can be read and updated through the Admin API.

Only fields explicitly declared as runtime fields are exposed by the runtime configuration API. The current update surface is:

ScopeConfigurationLifecycleEffective behavior
Clusteragent_evolutionDynamicROOT can update it through the Admin API; it is used as the cluster default.
Accountfeishu, agent_evolutionDynamicROOT or the Account ADMIN can update them. Agent Evolution retains deprecated whole-section Cluster fallback for compatibility. Feishu defaulting is implemented by its business resolver: an unset Account section uses Cluster, while a configured section takes only domain from Cluster.
Accountgithub, aclDynamicROOT or the Account ADMIN can update it. These sections have no Cluster fallback.
Accountvlm, query_plannerDynamicROOT-only. Each configured section requires model and a non-empty credentials array; timeout is optional. ADMIN callers cannot read or update these sections.
AccountembeddingMixedROOT-only. Credentials, retries, concurrency, failback and circuit-breaker settings are dynamic; model identity, vector-space fields, text source and input token limit are create-only.
AccountvectordbCreate-onlyROOT-only. Supply it in Account creation settings; later additions, changes and resets are rejected. Only remote backends http, volcengine and vikingdb are supported for Account-owned connections.

Cluster embedding, vlm, query_planner, memory, feishu, storage, parser, retrieval, and other ordinary configuration sections remain startup-only. Account memory is not on the current Account configuration API surface.

Account and Cluster configurations are independently stored and published. Any use of Cluster settings when Account settings are absent is a business default, not implicit configuration inheritance. The VLM resolver implements that policy: without an Account VLM section it uses Cluster VLM; with an Account section, model-service identity comes from Account while selected runtime behavior is composed with Cluster. Account timeout overrides Cluster timeout when set; omitting or resetting it uses Cluster timeout. Query Planner selection is Account query_planner, Account vlm, Cluster query_planner, then Cluster vlm.

The vector resolver validates effective Embedding and VectorDB settings together. Account embedding: {} is valid; without Account model modes, Cluster model bindings are used. Account credentials and explicit VectorDB connections supply their own connection and authentication fields. Remote collections, indexes and authorization must be provisioned externally. Changing embedding credentials does not migrate existing vectors; the caller must preserve model compatibility.

Account-owned business code must resolve model configuration through VLMResolver and pass the resulting VLMConfig to lower-level helpers. ClusterVLMResolver is restricted to startup, diagnostics, and offline evaluation composition roots. A CI architecture test rejects new direct reads of Cluster VLM or Query Planner configuration from other production modules.

For runtime changes, use the following endpoints:

http
GET   /api/v1/admin/configuration
PATCH /api/v1/admin/configuration

GET   /api/v1/admin/accounts/{account_id}/configuration
PATCH /api/v1/admin/accounts/{account_id}/configuration

The request body wraps a PATCH document in settings:

json
{
  "settings": {
    "agent_evolution": {
      "enabled": true
    }
  }
}

PATCH uses three states: an omitted field is unchanged, a concrete value sets or replaces the value, and null removes that value from the addressed scope. Objects merge recursively and arrays replace as a whole. The response contains settings from that scope, not a business-effective Account/Cluster composition. Declarative fallback is deprecated, supports only complete sections, and remains solely for compatibility; new features must implement Cluster defaults in their business resolver. See Admin API - Runtime Configuration for details.

Account Embedding and VectorDB ​

ROOT can configure settings.embedding and settings.vectordb when creating an Account. These sections use Account-specific allowlist schemas. Account and Cluster settings remain independent; the vector resolver applies Cluster defaults for omitted Account values. Provider connections can only be supplied through a complete credentials binding. Configuration reads return only Account values.

VectorDB is immutable after Account creation. Existing Accounts may rotate complete Embedding credential/deployment bindings and update retry, concurrency, failback, and circuit-breaker settings. Outer model identity and other vector-space fields remain create-only. Compatible endpoint updates do not interrupt in-flight calls or rebuild historical vectors.

Account-owned VectorDB configurations use remote backends only. Account configuration cannot select local/cuvs backends, local paths, cuVS tuning, or custom adapter parameters. Accounts with no VectorDB settings continue sharing the Cluster connection with Account data filtering. Remote resources must already exist. See Admin configuration API for permissions and PATCH rules.

Configuration Examples ​

Volcengine (Doubao Models)
json
{
  "embedding": {
    "dense": {
      "api_base" : "https://ark.cn-beijing.volces.com/api/v3",
      "api_key"  : "your-volcengine-api-key",
      "provider" : "volcengine",
      "dimension": 1024,
      "model"    : "doubao-embedding-vision-251215",
      "input": "multimodal",
      "batch_size": 32
    }
  },
  "vlm": {
    "api_base" : "https://ark.cn-beijing.volces.com/api/v3",
    "api_key"  : "your-volcengine-api-key",
    "provider" : "volcengine",
    "model"    : "doubao-seed-2-0-lite-260428"
  }
}
OpenAI Models
json
{
  "embedding": {
    "dense": {
      "api_base" : "https://api.openai.com/v1",
      "api_key"  : "your-openai-api-key",
      "provider" : "openai",
      "dimension": 1536,
      "model"    : "text-embedding-3-small"
    }
  },
  "vlm": {
    "api_base" : "https://api.openai.com/v1",
    "api_key"  : "your-openai-api-key",
    "provider" : "openai",
    "model"    : "gpt-5.4"
  }
}
Volcengine Embedding + Codex VLM

Use openviking-server init to complete the Codex login/import step, then run openviking-server doctor.

json
{
  "embedding": {
    "dense": {
      "api_base" : "https://ark.cn-beijing.volces.com/api/v3",
      "api_key"  : "your-volcengine-api-key",
      "provider" : "volcengine",
      "dimension": 1024,
      "model"    : "doubao-embedding-vision-251215"
    }
  },
  "vlm": {
    "provider" : "openai-codex",
    "model"    : "gpt-5.6-terra",
    "api_base" : "https://chatgpt.com/backend-api/codex",
    "reasoning_effort": "xhigh"
  }
}

OpenAI retired gpt-5.4 from Codex with ChatGPT sign-in on August 31, 2026. For an existing setup, change vlm.model in ov.conf to gpt-5.6-terra and restart the server; upgrading OpenViking does not rewrite saved model settings. This retirement does not affect provider: "openai" with an API key.

Volcengine Embedding + Kimi Coding VLM
json
{
  "embedding": {
    "dense": {
      "api_base" : "https://ark.cn-beijing.volces.com/api/v3",
      "api_key"  : "your-volcengine-api-key",
      "provider" : "volcengine",
      "dimension": 1024,
      "model"    : "doubao-embedding-vision-251215"
    }
  },
  "vlm": {
    "provider" : "kimi",
    "model"    : "kimi-code",
    "api_key"  : "your-kimi-subscription-api-key",
    "api_base" : "https://api.kimi.com/coding"
  }
}

kimi applies the Kimi Coding defaults automatically, including the default Kimi Coding user agent.

Volcengine Embedding + GLM Coding Plan VLM
json
{
  "embedding": {
    "dense": {
      "api_base" : "https://ark.cn-beijing.volces.com/api/v3",
      "api_key"  : "your-volcengine-api-key",
      "provider" : "volcengine",
      "dimension": 1024,
      "model"    : "doubao-embedding-vision-251215"
    }
  },
  "vlm": {
    "provider" : "glm",
    "model"    : "glm-4.6v",
    "api_key"  : "your-zai-api-key",
    "api_base" : "https://api.z.ai/api/coding/paas/v4"
  }
}

Use a vision-capable GLM model such as glm-4.6v or glm-5v-turbo when OpenViking needs image understanding.

Configuration Sections ​

embedding ​

Embedding model configuration for vector search, supporting dense, sparse, and hybrid modes.

Dense Embedding ​

json
{
  "embedding": {
    "max_concurrent": 10,
    "max_retries": 3,
    "text_source": "content_only",
    "max_input_tokens": 4096,
    "dense": {
      "provider": "volcengine",
      "api_key": "your-api-key",
      "model": "doubao-embedding-vision-251215",
      "dimension": 1024,
      "input": "multimodal"
    }
  }
}

Parameters

ParameterTypeDescription
max_concurrentintMaximum concurrent embedding requests (embedding.max_concurrent, default: 10; must be >= 1)
max_retriesintMaximum retry attempts for transient embedding provider errors (embedding.max_retries, default: 3; 0 disables retry)
text_sourcestrText used for vectorizing text files. content_only reads raw content, summary_first uses summary when available and falls back to content, summary_only is a deprecated alias for summary_first; existing configurations still load, log a warning, and normalize to summary_first. Default: content_only
max_input_tokensintMaximum estimated raw text tokens sent to the embedding model when content is used. Default: 4096
providerstr"openai", "azure", "volcengine", "vikingdb", "jina", "ollama", "gemini", "voyage", "dashscope", "minimax", "cohere", "litellm", or "local"
api_keystrAPI key
modelstrModel name
dimensionintVector dimension. For Voyage, this maps to output_dimension
inputstrInput type: "text" or "multimodal"
batch_sizeintBatch size for embedding requests
encoding_formatstr(OpenAI / Azure only) Wire format for embedding values: "float" or "base64". Leave unset to use the OpenAI Python SDK default. Set to "float" when the upstream gateway cannot deserialize base64 embedding payloads correctly.
extra_bodyobject(OpenAI / Azure only) Extra JSON body fields merged into every embeddings request. Useful for OpenAI-compatible gateways that accept vendor-specific fields, e.g. OpenRouter provider routing {"provider": {"sort": "latency"}}. Explicit query_param/document_param keys take precedence on conflict.

embedding.max_retries only applies to transient errors such as 429, 5xx, timeouts, and connection failures. Permanent errors such as 400, 401, 403, and AccountOverdue are not retried automatically. The backoff strategy is exponential backoff with jitter, starting at 0.5s and capped at 8s.

Embedding Circuit Breaker ​

When the embedding provider experiences consecutive transient failures (e.g. 429, 5xx), OpenViking opens a circuit breaker to temporarily stop calling the provider and re-enqueue embedding tasks. After the base reset_timeout, it allows a probe request (HALF_OPEN). If the probe fails, the next reset_timeout is doubled (capped by max_reset_timeout).

json
{
  "embedding": {
    "circuit_breaker": {
      "failure_threshold": 5,
      "reset_timeout": 60,
      "max_reset_timeout": 600
    }
  }
}
ParameterTypeDescription
circuit_breaker.failure_thresholdintConsecutive failures required to open the breaker (default: 5)
circuit_breaker.reset_timeoutfloatBase reset timeout in seconds (default: 60)
circuit_breaker.max_reset_timeoutfloatMaximum reset timeout in seconds when backing off (default: 600)

Available Models

ModelDimensionInput TypeNotes
doubao-embedding-vision-2512151024multimodalRecommended
doubao-embedding-2506151024textText only

With input: "multimodal", OpenViking can embed text, images (PNG, JPG, etc.), and mixed content. Image-to-image search requires this mode; text-only embedding models continue to index image summaries but cannot accept image queries.

Hybrid image support depends on the dense channel. The complete mixed input goes to dense, while sparse receives only text; image-only inputs skip sparse. Volcengine hybrid keeps one combined request for text-only input and uses separate multimodal dense and text-only sparse requests for mixed input. Keep the dense model and dimension compatible with the vectors already stored in the index.

Supported providers:

  • openai: OpenAI Embedding API
  • azure: Azure OpenAI Embedding API
  • volcengine: Volcengine Embedding API
  • vikingdb: VikingDB Embedding API
  • jina: Jina AI Embedding API
  • ollama: Ollama local OpenAI-compatible Embedding API
  • voyage: Voyage AI Embedding API
  • minimax: MiniMax Embedding API
  • cohere: Cohere Embedding API
  • gemini: Google Gemini Embedding API (text-only; requires google-genai>=1.39.0)
  • dashscope: DashScope (Alibaba Tongyi) Embedding API
  • litellm: LiteLLM Embedding API
  • local: Local GGUF embedding models

OpenAI-compatible provider example with JSON float embeddings:

json
{
  "embedding": {
    "dense": {
      "provider": "openai",
      "api_key": "your-api-key",
      "api_base": "https://your-openai-compatible-endpoint/v1",
      "model": "text-embedding-3-large",
      "dimension": 3072,
      "encoding_format": "float"
    }
  }
}

encoding_format is optional and is only forwarded for provider: "openai" and provider: "azure". Leave it unset for the OpenAI Python SDK default. Set it to "float" when an OpenAI-compatible upstream gateway cannot deserialize base64 embedding payloads correctly.

OpenRouter example with provider routing:

json
{
  "embedding": {
    "dense": {
      "provider": "openai",
      "api_key": "your-openrouter-api-key",
      "api_base": "https://openrouter.ai/api/v1",
      "model": "qwen/qwen3-embedding-8b",
      "dimension": 4096,
      "extra_body": {
        "provider": {
          "sort": "latency"
        }
      }
    }
  }
}

extra_body is merged into every embeddings request, so OpenAI-compatible gateways that accept vendor-specific fields (such as OpenRouter's provider routing preferences) can be tuned without code changes. It is only forwarded for provider: "openai" and provider: "azure".

Azure OpenAI provider example with JSON float embeddings:

json
{
  "embedding": {
    "dense": {
      "provider": "azure",
      "api_key": "your-azure-api-key",
      "api_base": "https://your-resource-name.openai.azure.com",
      "api_version": "2025-01-01-preview",
      "model": "your-embedding-deployment-name",
      "dimension": 3072,
      "encoding_format": "float"
    }
  }
}

For Azure OpenAI, model must be the embedding deployment name configured in Azure.

minimax provider example:

json
{
  "embedding": {
    "dense": {
      "provider": "minimax",
      "api_key": "your-minimax-api-key",
      "model": "embo-01",
      "dimension": 1536,
      "query_param": "query",
      "document_param": "db",
      "extra_headers": {
        "GroupId": "your-group-id"
      }
    }
  }
}

vikingdb provider example:

json
{
  "embedding": {
    "dense": {
      "provider": "vikingdb",
      "model": "bge_large_zh",
      "ak": "your-access-key",
      "sk": "your-secret-key",
      "region": "cn-beijing",
      "dimension": 1024
    }
  }
}

jina provider example:

json
{
  "embedding": {
    "dense": {
      "provider": "jina",
      "api_key": "jina_xxx",
      "model": "jina-embeddings-v5-text-small",
      "dimension": 1024
    }
  }
}

Available Jina models:

  • jina-embeddings-v5-text-small: 677M params, 1024 dim, max seq 32768 (default)
  • jina-embeddings-v5-text-nano: 239M params, 768 dim, max seq 8192

Get your API key at https://jina.ai

voyage provider example:

json
{
  "embedding": {
    "dense": {
      "provider": "voyage",
      "api_key": "pa-xxx",
      "api_base": "https://api.voyageai.com/v1",
      "model": "voyage-4-lite",
      "dimension": 1024
    }
  }
}

Text models include voyage-4-lite, voyage-4, and voyage-4-large. See Voyage Text Embeddings for available models and output dimensions.

If dimension is omitted, OpenViking uses its built-in model table (voyage-3, voyage-3-large, voyage-3.5, voyage-3.5-lite, voyage-4, voyage-4-lite, voyage-4-large, voyage-code-3, voyage-context-3, voyage-finance-2, voyage-law-2), falling back to 1024 for an unrecognized model. Set a supported output dimension explicitly for a new model. The current adapter sends plain text input; it does not send Voyage's input_type, expose quantized output, or implement specialized contextual embedding calls.

Local deployment (GGUF/MLX): Jina embedding models are open-weight and available in GGUF and MLX formats on Hugging Face. You can run them locally with any OpenAI-compatible server (e.g. llama.cpp, MLX, vLLM) and point the api_base to your local endpoint:

json
{
  "embedding": {
    "dense": {
      "provider": "jina",
      "api_key": "local",
      "api_base": "http://localhost:8080/v1",
      "model": "jina-embeddings-v5-text-nano",
      "dimension": 768
    }
  }
}

gemini provider example:

Version 1.39.0 is the minimum SDK version with the client context managers and close methods used to keep asynchronous embedding requests on their owning event loop. Upgrade older SDK pins before enabling this provider.

Note: Requires google-genai>=1.39.0 in the server environment — uv install: uv tool install openviking --upgrade --with "google-genai>=1.39.0"; pip install: pip install "google-genai>=1.39.0". For async batching use the extra instead: uv tool install "openviking[gemini-async]" --upgrade or pip install "openviking[gemini-async]".

json
{
  "embedding": {
    "dense": {
      "provider": "gemini",
      "api_key": "your-google-api-key",
      "model": "gemini-embedding-2",
      "dimension": 3072
    }
  }
}

Available Gemini embedding models:

  • gemini-embedding-2: 8192 token input limit, 1–3072 output dimension (MRL)
  • gemini-embedding-001: 2048 token input limit, 1–3072 output dimension (MRL)
  • text-embedding-004: 2048 token input limit, 768 output dimension (fixed)

Recommended dimensions: 768, 1536, or 3072 (default: 3072).

Gemini Embedding 2 represents task types as documented text instructions because the stable model does not accept the task_type request field. OpenViking applies these instructions when query_param or document_param is configured. If you change the model for an existing index, rebuild its vectors unless the provider explicitly guarantees that the two embedding spaces are compatible.

Get your API key at https://aistudio.google.com/apikey

DashScope (Alibaba Tongyi) provider:

json
{
  "embedding": {
    "dense": {
      "provider": "dashscope",
      "api_key": "${DASHSCOPE_API_KEY}",
      "model": "text-embedding-v4",
      "dimension": 1024,
      "input": "text"
    }
  }
}

Available DashScope models:

ModelDimensionInput TypeNotes
text-embedding-v31024textOptimized for Chinese
text-embedding-v41024textOptimized for Chinese
tongyi-embedding-vision-plus1152multimodalSupports fusion via enable_fusion
tongyi-embedding-vision-flash768multimodalFaster, lower cost
qwen3-vl-embedding2560multimodalText + image + video
qwen2.5-vl-embedding1024multimodalText + image + video

Input and multimodal parameters:

ParameterTypeDefaultDescription
inputstr"multimodal"Embedding mode: "text" or "multimodal"
enable_fusionboolfalseEnable fusion vectors for tongyi-embedding-vision-* models
res_levelint2Image resolution level (1=high, 2=medium, 3=low)
max_video_framesint16Maximum video frames to embed

Endpoint selection — DashScope provides api_base defaults for China (cn) and international (intl) regions:

Regionapi_baseNotes
Chinahttps://dashscope.aliyuncs.com (default)Recommended for users in mainland China
Internationalhttps://dashscope-intl.aliyuncs.comFor users outside China

For a custom gateway, set api_base to the gateway's base URL. OpenViking appends the mode-specific endpoint path automatically, so do not include /compatible-mode/v1 (text mode) or /api/v1/services/embeddings/multimodal-embedding/multimodal-embedding (multimodal mode) in api_base.

Get your API key at https://dashscope.console.aliyun.com/api-key

Non-symmetric retrieval (different task types for indexing vs. query):

json
{
  "embedding": {
    "dense": {
      "provider": "gemini",
      "api_key": "your-google-api-key",
      "model": "gemini-embedding-2",
      "dimension": 3072,
      "query_param": "RETRIEVAL_QUERY",
      "document_param": "RETRIEVAL_DOCUMENT"
    }
  }
}

Supported task types: RETRIEVAL_QUERY, RETRIEVAL_DOCUMENT, SEMANTIC_SIMILARITY, CLASSIFICATION, CLUSTERING, CODE_RETRIEVAL_QUERY, QUESTION_ANSWERING, FACT_VERIFICATION.

Local provider (local) ​

When no dense, sparse, or hybrid embedding is configured (including when embedding is omitted), OpenViking selects dense: {"provider": "local", "model": "bge-small-zh-v1.5-f16"}. This GGUF model produces 512-dimensional vectors and requires no API key.

Install the local inference dependency:

bash
pip install "openviking[local-embed]"

On first use, OpenViking downloads the model to ~/.cache/openviking/models unless the file is already cached. Set embedding.dense.cache_dir to change the cache directory, or embedding.dense.model_path to use an existing GGUF file without downloading it. A missing explicit model_path is an error; it does not trigger a download. This model's dimension is fixed at 512.

Sparse Embedding ​

This example uses doubao-embedding-vision-251215. Check sparse text-output support for your selected model in the Ark embedding documentation.

json
{
  "embedding": {
    "sparse": {
      "provider": "volcengine",
      "api_key": "your-api-key",
      "model": "doubao-embedding-vision-251215"
    }
  }
}

Sparse output is a provider capability, not an endpoint inferred from storage.vectordb.sparse_weight. OpenViking currently implements sparse and hybrid embedding providers for volcengine and vikingdb. The OpenAI-compatible, Ollama, and built-in local providers are dense-only; a self-hosted /v1/embeddings endpoint is therefore not treated as a sparse endpoint, and OpenViking does not probe a separate /v1/embeddings/sparse route.

There is no automatic BM25 or other sparse-vector fallback when the configured embedder returns only dense vectors. To use hybrid retrieval, configure a supported sparse/hybrid provider and set storage.vectordb.sparse_weight > 0. Model memory requirements are provider/model-specific and are not controlled by OpenViking; size self-hosted models against their provider documentation before enabling them in production.

Hybrid Embedding ​

Two approaches are supported:

Option 1: Single hybrid model

json
{
  "embedding": {
    "hybrid": {
      "provider": "volcengine",
      "api_key": "your-api-key",
      "model": "doubao-embedding-vision-251215",
      "dimension": 1024
    }
  }
}

Option 2: Combine dense + sparse

json
{
  "embedding": {
    "dense": {
      "provider": "volcengine",
      "api_key": "your-api-key",
      "model": "doubao-embedding-vision-251215",
      "dimension": 1024
    },
    "sparse": {
      "provider": "volcengine",
      "api_key": "your-api-key",
      "model": "doubao-embedding-vision-251215"
    }
  }
}

vlm ​

Vision Language Model for semantic extraction (L0/L1 generation).

json
{
  "vlm": {
    "provider": "volcengine",
    "api_key": "your-api-key",
    "model": "doubao-seed-2-0-lite-260428",
    "api_base": "https://ark.cn-beijing.volces.com/api/v3",
    "max_retries": 3,
    "media": {
      "enabled": true,
      "max_concurrent": 2,
      "file_processing_timeout": 1800,
      "file_poll_interval": 3,
      "video_fps": 1.0
    }
  }
}

Parameters

ParameterTypeDescription
api_keystrAPI key. Optional for openai-codex when Codex OAuth is available, and optional for litellm routes that use provider-native credentials
forward_api_keyboolLiteLLM only. Overrides whether api_key is forwarded to LiteLLM. By default, OpenViking does not forward placeholder keys for native AWS/GCP routes such as bedrock/, sagemaker/, and vertex_ai/; set to true when intentionally using a LiteLLM API-key route such as Bedrock bearer-token auth
modelstrModel name
api_basestrAPI endpoint (optional)
thinkingboolEnable thinking mode for VolcEngine models (default: false)
max_concurrentintMaximum concurrent semantic LLM calls (default: 32)
max_retriesintMaximum retry attempts for transient VLM provider errors (default: 3; 0 disables retry)
credentialsarrayOrdered VLM credential/model list, with index 0 having the highest priority. Each item can override provider, model, api_key, api_base, api_version, extra_headers, extra_request_body, reasoning_effort, and keepalive_expiry
failback_timeout_secondsfloatTime threshold for attempting a step back toward a higher-priority credential after failover (default: 600)
failback_request_countintSuccessful requests on a lower-priority credential before attempting a step back (default: 50)
backupobjectOptional backup VLM configuration (same shape as vlm) for automatic failover when the primary fails with retryable errors such as rate limits, 5xx responses, or connection/timeout failures. Only one level of failover is supported — the backup itself cannot define a nested backup
timeoutfloatPer-request HTTP timeout in seconds passed to the underlying OpenAI/LiteLLM client. Increase for slow endpoints (e.g., DashScope, local inference). Must be > 0 (default: 600.0)
keepalive_expiryfloatIdle connection lifetime in seconds for OpenAI-compatible VLM clients. Set to 0 to disable idle connection reuse; unset uses the OpenAI SDK default. Must be >= 0
extra_headersobjectCustom HTTP headers for compatible HTTP providers. kimi also accepts header overrides, but already injects the required subscription headers by default
extra_request_bodyobjectExtra JSON body fields for OpenAI-compatible completion requests, useful for provider-specific options such as Ollama {"think": false}
reasoning_effortstrReasoning effort for openai, azure, kimi, glm, and openai-codex; explicit values are forwarded, and accepted values depend on the model. When unset, GPT-5/o-series names retain low; other models omit the field. For Chat Completions, extra_request_body.reasoning_effort takes precedence
mediaobjectAudio/video runtime controls. Media understanding reuses this VLM's provider, model, credentials, client, timeout, retry, headers, output-token limit, failover, and token accounting
media.enabledboolEnable audio/video understanding (default: false)
media.max_concurrentintMaximum concurrent audio/video calls (default: 2)
media.file_processing_timeoutfloatMaximum provider-side preprocessing wait in seconds (default: 1800)
media.file_poll_intervalfloatProvider-side preprocessing poll interval in seconds (default: 3)
media.video_fpsfloatVideo frame sampling rate when supported by the provider, from 0.2 through 5.0 (default: 1.0)

vlm.max_retries only applies to transient errors such as 429, 5xx, timeouts, and connection failures. Permanent authentication, authorization, and billing errors are not retried automatically. The backoff strategy is exponential backoff with jitter, starting at 0.5s and capped at 8s.

Available Models

ModelNotes
doubao-seed-2-0-lite-260428Recommended for semantic extraction
doubao-pro-32kFor longer context

When resources are added, VLM generates:

  1. L0 (Abstract): file or directory summary, with a default limit of 256 characters
  2. L1 (Overview): directory overview with navigation, with a default limit of 4000 characters

When the VLM configuration reports is_available() == false, generic file summaries are empty and directory overviews fall back to a not-ready placeholder. Paths such as local code-skeleton extraction retain their own behavior. Do not treat this as completed semantic indexing; inspect the model configuration and processing task.

Supported providers:

  • volcengine: Volcengine VLM API
  • openai: OpenAI-compatible VLM API
  • openai-codex: Codex VLM via ChatGPT/Codex OAuth
  • kimi: Kimi Coding subscription endpoint with built-in provider defaults
  • glm: Z.AI GLM Coding Plan endpoint with OpenAI-compatible requests
  • litellm: LiteLLM VLM API, including explicit LiteLLM routes such as bedrock/, sagemaker/, vertex_ai/, and azure/

For openai-codex, authenticate through openviking-server init, then verify with openviking-server doctor.

For litellm, api_key can be omitted when the underlying route authenticates through environment or provider-native credentials, such as AWS IAM/IRSA for Bedrock and SageMaker or ADC/service-account credentials for Vertex AI. Azure routes still use api_key normally. If you intentionally use LiteLLM's Bedrock bearer-token API-key auth, set forward_api_key to true.

Custom HTTP Headers

For OpenAI-compatible providers (e.g., OpenRouter), you can add custom HTTP headers via extra_headers:

json
{
  "vlm": {
    "provider": "openai",
    "api_key": "your-api-key",
    "model": "gpt-4o",
    "api_base": "https://openrouter.ai/api/v1",
    "extra_headers": {
      "HTTP-Referer": "https://your-site.com",
      "X-OpenRouter-Title": "Your App Name"
    }
  }
}

Common use cases:

  • OpenRouter: Optional HTTP-Referer and X-OpenRouter-Title headers provide app attribution, not API authentication; X-Title remains supported for compatibility
  • Kimi Coding: Override or extend the default subscription headers when you need a custom user agent
  • OpenCode Go (https://opencode.ai/zen/go/v1): Requests without x-opencode-session fail with HTTP 400 MissingSessionID. Set a fixed id, e.g. "extra_headers": {"x-opencode-session": "openviking-<your-host>"}. A fixed id works; OpenCode Go only uses it for routing and prompt-cache hints
  • Custom proxies: Add authentication or tracing headers
  • API gateways: Add version or routing identifiers

Custom Request Body

Use extra_request_body for provider-specific JSON body fields. OpenAI-compatible routes use SDK extra_body. LiteLLM Anthropic routes pass native options such as thinking and output_config directly to LiteLLM, so they reach the top-level request body. LiteLLM still validates model support for these options. On Anthropic routes, LiteLLM control names (including api_key, api_base, metadata, and mock_response) and call-owned fields (model, messages, tools, tool_choice, stream, timeout, and headers) are rejected instead of overriding the call. Configure credentials and routing through their dedicated settings.

For example, to disable thinking on Ollama:

json
{
  "vlm": {
    "provider": "litellm",
    "api_key": "ollama",
    "model": "ollama/llama3.1",
    "api_base": "http://127.0.0.1:11434",
    "extra_request_body": {
      "think": false
    }
  }
}

Audio/video understanding

Audio and video understanding is an optional capability of the configured VLM. It uses the same provider, model, credentials, client, request timeout, retries, headers, maximum output tokens, failover chain, and token accounting as other VLM calls. Enable it with the nested vlm.media controls; there is no separate media model configuration.

json
{
  "vlm": {
    "provider": "volcengine",
    "api_key": "${VOLCENGINE_API_KEY}",
    "model": "${VOLCENGINE_MODEL}",
    "api_base": "https://ark.cn-beijing.volces.com/api/v3",
    "timeout": 1200,
    "max_retries": 3,
    "max_tokens": 4096,
    "media": {
      "enabled": true,
      "file_processing_timeout": 1800,
      "file_poll_interval": 3,
      "max_concurrent": 2,
      "video_fps": 1.0
    }
  }
}

vlm.extra_request_body also applies to the Ark Responses request for audio/video, for example {"service_tier": "flex", "thinking": {"type": "disabled"}}. Use fields supported by the selected model and Responses API. Explicit extra body fields override generated request fields through the SDK, including store; response storage defaults to false unless you explicitly override it. These fields are not forwarded to file upload, status, or deletion requests.

The VLM model value is the corresponding Ark model endpoint ID. video_fps applies only to video and controls the frame sampling rate sent to Ark.

The recommended starting models for audio and video understanding are doubao-seed-2-0-lite-260428 and doubao-seed-2-0-mini-260428. These are recommended examples, not an exhaustive compatibility list; Ark continues to update its models and input capabilities. See Ark's official video input capability list and audio input capability list for other supported models. If model is an ep-* inference endpoint ID, verify that its underlying foundation model supports the corresponding media input. OpenViking does not validate audio or video model capabilities while loading configuration.

Ingestible and understandable formats

TypeStored by the existing parserUnderstood by Ark in this release
AudioMP3, WAV, OGG, FLAC, AAC, M4A, OPUS, AC3MP3, WAV, AAC, M4A
VideoMP4, AVI, MOV, MKV, WEBM, FLV, WMV, TSMP4, AVI, MOV

Formats outside the understanding column continue to follow the existing parser and storage behavior; OpenViking does not transcode them or send them to the understanding model. When such a file is recognized as an audio or video leaf, an empty media summary is indexed using its filename.

For a supported file, OpenViking uploads the media to the Ark Files API without explicitly setting expire_at, so file retention follows Ark's default policy. After processing completes, OpenViking references the file's file_id from the Responses API with response storage disabled by default, then attempts to delete the Ark file under a short cleanup deadline. Remote deletion is best-effort and does not replace an otherwise successful result if cleanup fails; a file whose deletion fails or times out continues to follow Ark's default retention policy. Local temporary files are removed independently even when remote cleanup fails or is cancelled.

  • A successful summary for a directory containing exactly one audio or video file becomes that directory's L1 directly, with L0 derived through the existing semantic path. No second generic VLM summarization is performed.
  • Media in a mixed directory contributes its summary to the existing generic VLM aggregation.
  • Disabled media understanding, an unsupported understanding format, or a final model failure yields an empty media summary. Generic directory L0/L1 generation keeps its existing behavior, while a recognized audio or video leaf uses its filename for the DETAIL vector and BM25 content. Provider errors and media-understanding status text are not written to the media summary or leaf index.

Media processing sends file content to the configured external provider. Disabled response storage and best-effort deletion reduce unintended retention but do not replace the provider's own privacy and retention controls; uploaded files do not receive an explicit expiration time, so their retention period is determined by Ark's default policy. Ark Files storage/processing and Responses model tokens can incur provider charges, so review your provider's privacy, retention, and billing terms before enabling this feature. See the official Volcengine Ark documentation for audio understanding and video understanding.

bot.compile ​

Set the number of concurrent workers per Resource Compile task and stage, for both direct model calls and agent execution:

json
{
  "bot": {
    "compile": {
      "map_concurrency": 8,
      "shuffle_concurrency": 4,
      "shuffle_batch_size": 4,
      "reduce_concurrency": 6
    }
  }
}
FieldDefaultDescription
map_concurrencyInherits vlm.max_concurrentConcurrent Map extraction jobs
shuffle_concurrencyInherits vlm.max_concurrentConcurrent Shuffle routing jobs and embedding batches
shuffle_batch_size4Maximum primary records per routing request, independent of concurrency and the soft character budget
reduce_concurrencyInherits vlm.max_concurrentConcurrent Reduce jobs; final same-path candidate merges reuse this value

Concurrency values must be positive integers; omitted or null values inherit the default. shuffle_batch_size must be a positive integer, defaults to 4, and does not accept null. Routing saves valid decisions individually and re-batches only failed records; the final retry uses single-record requests, with at most three rounds per record. Restart VikingBot after changing these settings. All Compile tasks within one VikingBot service still share the model-request capacity set by vlm.max_concurrent, so raising stage concurrency cannot exceed that total limit. Embedding requests also obey the embedding service's own concurrency limit. Recovery uses the same reduce setting by default; an explicit --concurrency overrides recovery concurrency.

Compile inherits the model's thinking and reasoning settings. Model requests use the effective model timeout (bot.agents.timeout, or inherited vlm.timeout).

query_planner ​

Optional lightweight model for retrieval intent analysis and query planning. It uses the same configuration shape as vlm for search() intent analysis, query expansion, and optional server recall-digest rewriting. If query_planner is omitted or empty, OpenViking falls back to vlm for backward compatibility.

In openviking-server init you can optionally enable a local lightweight query planner; the wizard pulls the Ollama model and writes the query_planner config for you. For recognized query-planner models, search() selects the matching bundled prompt at runtime. Models not in the mapping keep using retrieval.intent_analysis.

We recommend the local Ollama model guoxuter/ov_intent_analysis_sft:v7_q8. Fine-tuned from Qwen3.5-0.8B, it can be deployed locally and is well suited to letting a small model handle retrieval planning: for small talk, greetings, or turns where the context is already sufficient, it returns no queries to reduce unnecessary memory injection and token consumption; when retrieval is needed, it emits structured queries targeting skill, resource, and memory. The earlier v4_q8 revision is still supported as an alternative.

Pull the model first and make sure the Ollama service is reachable:

bash
ollama pull guoxuter/ov_intent_analysis_sft:v7_q8

Then add the following to your OpenViking configuration:

json
{
  "query_planner": {
    "provider": "litellm",
    "model": "ollama/guoxuter/ov_intent_analysis_sft:v7_q8",
    "api_base": "http://127.0.0.1:11434",
    "temperature": 0.0,
    "timeout": 60,
    "extra_request_body": {
      "think": false
    }
  }
}

For ollama/guoxuter/ov_intent_analysis_sft:v7_q8 (and v4_q8), OpenViking automatically uses the matching bundled prompt during search (retrieval.ov_intent_analysis_sft_v7 and retrieval.ov_intent_analysis_sft_v4 respectively). No prompt file replacement or prompts.templates_dir override is required. If you use an unmapped model, OpenViking keeps the default retrieval.intent_analysis prompt.

This lets a smaller model handle retrieval planning while a stronger vlm handles semantic extraction, memory extraction, and multimodal processing. Measure latency with your model, hardware, and request load.

feishu ​

Configuration for Feishu/Lark cloud document parsing. See Resources for supported URL patterns.

json
{
  "feishu": {
    "app_id": "",
    "app_secret": "",
    "domain": "https://open.feishu.cn",
    "max_rows_per_sheet": 1000,
    "max_records_per_table": 1000
  }
}
ParameterTypeDescription
app_idstrFeishu app ID (can also be set via FEISHU_APP_ID env var)
app_secretstrFeishu app secret (can also be set via FEISHU_APP_SECRET env var)
domainstrFeishu API domain. Use https://open.larksuite.com for Lark international
max_rows_per_sheetintMaximum rows to import per spreadsheet sheet (default: 1000)
max_records_per_tableintMaximum records to import per bitable table (default: 1000)

Dependency: Included by default in openviking[bot] installation

Lark international: For Lark URLs (*.larksuite.com), set domain to https://open.larksuite.com.

code ​

Code skeleton extraction is built into the code summary pipeline and has no parser-level configuration. OpenViking first uses maintained tags.scm queries when one exists for the language; if no corresponding tags.scm exists, it uses tree-sitter-language-pack.process(); when the current extraction route produces no useful skeleton, it invokes semantic.code_summary as fallback.

The remaining code configuration fields are for remote code resource network guards and code-hosting allowlists. See Code Skeleton Extraction for the extraction route.

Remote resource network guard ​

For URL ingestion, the default network check rejects destinations resolving to non-public addresses. Ordinary public URLs do not need to be on the code-hosting allowlist; trusted code hosts on that list bypass the address-range check. Add self-hosted GitHub Enterprise, GitLab, or Azure DevOps hosts to the corresponding platform list under code:

FieldTypeDescriptionDefault
github_domainslist[str]Allowed GitHub hosts (add your GitHub Enterprise host here)["github.com", "www.github.com"]
gitlab_domainslist[str]Allowed GitLab hosts (add your self-hosted GitLab host here)["gitlab.com", "www.gitlab.com"]
azure_devops_domainslist[str]Allowed Azure DevOps hosts["dev.azure.com", "ssh.dev.azure.com", "vs-ssh.visualstudio.com"]
code_hosting_domainslist[str]Allowed generic code-hosting hosts["github.com", "gitlab.com", "gitcode.com", "gitee.com", "bitbucket.org", "codeberg.org", "gitea.com", "atomgit.com", "git.sr.ht"]

To allow other private-network targets, such as internal mirrors, set the top-level allow_private_networks to true. It is disabled by default; local hostnames such as localhost and its subdomains remain rejected. This example enables private-network access and registers a code host:

json
{
  "allow_private_networks": true,
  "code": {
    "github_domains": ["github.com", "github.example.com"]
  }
}

Use github_domains, gitlab_domains, or azure_devops_domains when the host needs those platform-specific URL semantics. Add other Git hosts to code_hosting_domains.

pdf ​

PDF parsing configuration. Three strategies are supported: local (local pdfplumber), mineru (remote MinerU API), and auto (try local first, fall back to MinerU).

json
{
  "pdf": {
    "strategy": "auto",
    "mineru_endpoint": "http://127.0.0.1:8000",
    "mineru_timeout": 300.0,
    "mineru_bodys": {
      "backend": "hybrid-auto-engine",
      "lang_list": ["ch"],
      "parse_method": "auto"
    }
  }
}
ParameterTypeDescription
strategystrParsing strategy: local / mineru / auto (default auto)
mineru_endpointstrMinerU API base URL (e.g. http://127.0.0.1:8000)
mineru_timeoutfloatRequest timeout in seconds (default 300.0)
mineru_bodysdictMinerU API multipart form fields

MinerU protocol: a synchronous POST {mineru_endpoint}/file_parse request with the PDF as the multipart files field; form parameters are passed through from mineru_bodys.

External parser API (parser_api) ​

The top-level parser_api section routes selected formats to the external Understanding files/responses API. It replaces the Parser stage; TreeBuilder, persistence, and semantic processing remain in OpenViking. See Resource processing for routing and asynchronous behavior.

FieldDefaultMeaning
enablefalseEnable external parsing.
extensions[]File extensions to route externally; normalized to lowercase without the leading dot.
host""API host including scheme, such as https://parser.example.com; required when enabled.
api_key""External API credential; required when enabled.
enable_feishu_urlfalseAllow direct Feishu URL parsing when credentials are available. Feishu documents already normalized to Markdown by the accessor bypass external parsing; file attachments still use extension routing.
enable_resumable_uploadfalseEnable resumable uploads.
upload_simple_max_bytes536870912 (512 MiB)Simple-upload size threshold.
upload_part_size_bytes8388608 (8 MiB)Resumable-upload part size.
http_timeout_seconds10.0Timeout for an individual HTTP request.
response_timeout_seconds1800Timeout while waiting for the parsing response.
poll_interval_ms3000Response polling interval.

All size, timeout, and polling values must be greater than zero.

rerank ​

Reranking model for search result refinement. Supports VikingDB (Volcengine), Cohere, OpenAI-compatible APIs, LiteLLM, and Jev.

Volcengine (VikingDB):

json
{
  "rerank": {
    "provider": "vikingdb",
    "ak": "your-access-key",
    "sk": "your-secret-key",
    "model_name": "doubao-seed-rerank",
    "model_version": "251028"
  }
}

OpenAI-compatible provider (e.g. DashScope):

json
{
  "rerank": {
    "provider": "openai",
    "api_key": "your-api-key",
    "api_base": "https://dashscope.aliyuncs.com/compatible-api/v1/reranks",
    "model": "qwen3-rerank",
    "timeout": 120,
    "max_input_tokens": 2048,
    "threshold": 0.1
  }
}

Jev (TypeSafe System One) provider:

json
{
  "rerank": {
    "provider": "jev",
    "api_key": "your-typesafe-api-key",
    "model": "jev-latest",
    "timeout": 120,
    "log_payloads": false,
    "threshold": 0.1
  }
}

To use Jev through Vercel AI Gateway, point api_base at Vercel's TypeSafe-compatible endpoint and use Vercel's model ID. The request and response formats are identical to TypeSafe direct access; the adapter does not special-case Vercel:

json
{
  "rerank": {
    "provider": "jev",
    "api_key": "your-vercel-ai-gateway-api-key",
    "api_base": "https://ai-gateway.vercel.sh/typesafe",
    "model": "typesafe-ai/jev",
    "threshold": 0.1
  }
}

Notes for Vercel:

  • Use a long-lived AI Gateway API key created with vercel ai-gateway api-keys create, not the OIDC token from vercel env pull, which expires after 12 hours.
  • The Vercel team must have a credit card on file for AI Gateway; otherwise requests fail with 403 customer_verification_required.
  • api_base must include the /typesafe suffix. https://ai-gateway.vercel.sh/v1 is Vercel's own evaluate protocol and is not supported by the adapter.

The Jev adapter sends the query and candidate documents as shared System One state and supports two modes:

  • noul (default): one independent relevance question per document, using its returned yes probability as the score. All questions are sent in one HTTP request, and document scores do not have to sum to 1.
  • choice: one question compares all candidate documents, with candidate_{index} mapped to input indices. Scores come from probabilities in document order.

Select the mode with rerank.mode; omitting it or setting it to null keeps the default noul behavior. Choice scores are relative probabilities summing to 1 within the candidate pool, not absolute relevance probabilities, so they are unsuitable for absolute score filtering. When enabling Choice, set "threshold": 0. The MCP find and search min_score argument overrides that setting, so pass min_score=0 when using Choice through MCP.

Parameters

ParameterTypeDescription
providerstr"vikingdb", "cohere", "openai", "litellm", or "jev". Auto-detected if omitted.
akstrVikingDB Access Key (vikingdb provider only)
skstrVikingDB Secret Key (vikingdb provider only)
model_namestrModel name (vikingdb provider only, default: doubao-seed-rerank)
api_keystrAPI key (for openai, cohere, or jev providers)
api_basestrEndpoint URL (for openai or jev; Jev defaults to https://api.typesafe.ai, Vercel uses https://ai-gateway.vercel.sh/typesafe)
modelstrModel name for OpenAI-compatible, LiteLLM, or Jev providers
mode"noul", "choice", or nullJev rerank mode. null and omission use "noul"
timeoutfloatHTTP request timeout in seconds for HTTP rerank providers, including Jev. Default: 30.0
max_input_tokensintMaximum estimated raw-text tokens in each query-document pair sent to the reranker. Oversized inputs retain their beginning and end. 0 disables. Default: 0
log_payloadsboolLog complete rerank request and response payloads. May expose query and document content. Default: false
thresholdfloatScore threshold between 0.0 and 1.0; results below this are filtered out. Default: 0.1
extra_headersobjectCustom HTTP headers (for OpenAI-compatible providers, optional)

Supported providers:

  • vikingdb: Volcengine VikingDB Rerank API (uses AK/SK)
  • cohere: Cohere Rerank API
  • openai: OpenAI-compatible Rerank API
  • litellm: LiteLLM Rerank API
  • jev: Jev (TypeSafe System One) structured-decision API with Choice comparison and independent Noul scoring

If rerank is not configured, search uses vector similarity only.

retrieval ​

Configuration for session intent analysis and context assembly timeouts.

json
{
  "retrieval": {
    "enable_intent": true,
    "recall_intent_timeout_s": 5.0,
    "recall_rewrite_timeout_s": 30.0
  }
}
ParameterTypeDescriptionDefault
enable_intentboolRun intent analysis and query planning when search() receives a session_id.true

Session intent analysis in ordinary search() and query expansion in mode="context" share recall_intent_timeout_s. Context digest rewriting uses a separate timeout:

ParameterTypeDescriptionDefault
recall_intent_timeout_sfloatTimeout for session intent analysis and context query expansion; on timeout or failure the original user query is used5.0
recall_rewrite_timeout_sfloatTimeout for the digest rewrite; on timeout digest is empty and rendered is returned as usual30.0

Query expansion requires a session_id with content and intent analysis enabled; rewriting requires rewrite. Each model call has its own timeout. On failure, retrieval falls back to the original query or the unrevised result.

grep ​

Grep engine configuration for content pattern search. These settings are server-side only and cannot be overridden per-request.

json
{
  "grep": {
    "engine": "auto",
    "switch_to_remote_threshold": 10000
  }
}
ParameterTypeDescriptionDefault
enginestrSearch engine mode: "auto" uses VikingDB BM25 recall when available and falls back to local filesystem search; "fs" forces local filesystem search only."auto"
switch_to_remote_thresholdintL2 record count threshold to switch to VikingDB BM25 recall. When the number of L2 files under the search scope reaches this threshold, VikingDB BM25 is used for phase-1 recall; otherwise local filesystem search is used. Set to 0 to always use VikingDB BM25. Must be ≥ 0.10000

For VikingDB / Volcengine FullText grep, OpenViking writes a content text field for BM25 recall. The source context keeps the full content, while the vector-store write payload truncates this field to 1 MB at the final adapter boundary to stay within backend payload limits. Only VikingDB-backed backends use content; on all other backends (local, cuvs, http) the field is not written.

glob ​

Glob engine configuration for path pattern matching. These settings are server-side only and cannot be overridden per request.

json
{
  "glob": {
    "engine": "fs",
    "switch_to_remote_threshold": 100
  }
}
ParameterTypeDescriptionDefault
enginestrPath matching engine mode: "auto" uses remote path_glob processing when a VikingDB / Volcengine vector store is available and the search-scope record count reaches the threshold; otherwise it falls back to local filesystem search. "fs" forces local filesystem search only."fs"
switch_to_remote_thresholdintRecord-count threshold at which auto mode switches to remote path_glob. Set to 0 to always use remote path matching. Must be ≥ 0.100

storage ​

Storage configuration for context data, including file storage (RAGFS) and vector database storage (VectorDB).

Root Configuration ​

ParameterTypeDescriptionDefault
workspacestrLocal data storage path (main configuration)"./data"
skip_process_lockboolSkip the exclusive .openviking.lock file lock on storage.workspace for embedded vector backends (local, cuvs). Other backends do not acquire this lock. Skipping it does not make embedded vector storage safe for multiple processes.false
agfsobjectRAGFS (Rust-based AGFS) configuration{}
vectordbobjectVector database storage configuration{}
task_storeobjectDurable Task/Work backend configuration{"provider":"file"}
json
{
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "timeout": 10
    },
    "vectordb": {
      "backend": "local"
    }
  }
}

task_store ​

Task records use the local FileStore by default. To share Task/Work state across OpenViking processes, provision openviking/service/sql/task_store_init.sql in MySQL and select the relational backend:

json
{
  "storage": {
    "task_store": {
      "provider": "mysql",
      "params": {
        "user": "openviking",
        "password": "change-me",
        "database": "openviking",
        "host": "127.0.0.1",
        "port": 3306,
        "table": "ov_tasks"
      }
    }
  }
}

params.table is the table prefix. It must start with an ASCII letter, contain only ASCII letters, digits, or underscores, and be at most 38 characters so derived table, index, and constraint names fit MySQL's 64-character limit. Replace ov_tasks consistently in the provisioning SQL when using a custom prefix.

The MySQL backend requires the openviking[mysql] extra. Its mutations, Task finalization, and execution-event updates commit in one database transaction. task_id is globally unique; account_id and user_id scope authorization and queries. Do not use the FileStore tables and MySQL tables as active writers for the same deployment.

agfs (RAGFS) ​

ParameterTypeDescriptionDefault
backendstr"local", "s3", or "memory""local"
timeoutfloatRequest timeout in seconds10.0
backupsobjectMulti-write storage configuration. When set, the top-level backend acts as the primary backend and backups.items[] defines backup backendsnull
queuefsobjectQueueFS configuration. Controls the namespace mode, backend, and runtime options for /queue{ "mode": "shared", "backend": "sqlite", "recover_stale_sec": 0, "busy_timeout_ms": 5000 }
queue_db_pathstr (optional)Legacy compatibility field for QueueFS sqlite DB path. Superseded by storage.agfs.queuefs.db_path. Defaults to {storage.workspace}/_system/queue/queue.db when not set. Useful when the workspace volume does not support sqlite (e.g. some network filesystems)null
s3objectS3 backend configuration (when backend is 's3')-

Configuration Examples

RAGFS uses Rust binding mode by default, directly accessing the file system through the Rust implementation.

WARNING

storage.agfs no longer supports the AGFS HTTP client mode, and the old HTTP client entry should not be configured anymore. AGFS / RAGFS filesystem access now happens only through the in-process Rust binding (RAGFSBindingClient). This does not affect the OpenViking server HTTP API, the ov CLI, or AsyncHTTPClient / SyncHTTPClient when they connect to an OpenViking server.

Multi-Write Storage Configuration ​

storage.agfs.backups enables asynchronous V2 multi-write storage. The top-level backend remains the only direct read source.

json
{
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "backups": {
        "initial_partitions": 16,
        "checkpoint_interval_secs": 86400,
        "provider": "filesystem",
        "items": [
          {
            "name": "local-backup",
            "backend": "local",
            "params": {
              "workspace": "./data/backup"
            }
          }
        ]
      }
    }
  }
}

Common backups fields:

ParameterTypeDescriptionDefault
initial_partitionsintInitial metadata partitions; changes do not affect existing accounts16
checkpoint_interval_secsintCheckpoint discovery interval; minimum 6086400
providerstrMetadata provider: filesystem or top-level CacheRuntime-backed cache"filesystem"
itemsarrayBackups with name, backend, params, and optional encryption[]

The Python configuration layer still accepts legacy sync, retry, operations, excludes, and redirects fields. It warns and removes them before calling Rust; the fields have no effect.

See the Multi-Write Storage Guide for more examples.

Global Cache Provider, CacheFS, and PathLock Configuration ​

Top-level service_name names the OpenViking deployment. CacheFS, QueueFS with backend=cache, cache-backed PathLock, and cache-backed multi-write metadata derive their Redis namespaces from it.

The top-level cache section is a sibling of storage. Its public shape is Provider-neutral:

ParameterTypeDescriptionDefault
providerstrGlobal Cache Provider. This release supports redisrequired
paramsobjectProvider-owned parameters; parsed as Redis connection settings when provider=redis{}

storage.agfs.cachefs only controls CacheFS behavior:

ParameterTypeDescriptionDefault
backendstrlocal keeps the original filesystem path; cache enables the CacheFS wrapperlocal
max_file_size_bytesintMaximum full-file object size admitted to cache1048576
traversal_modestrbackend or cached_traversalbackend
bypass_prefixesarray[str]Path prefixes that bypass cache[]

storage.agfs.pathlock selects the PathLock storage provider:

ParameterTypeDescriptionDefault
providerstrfilesystem, memory, or cache; cache uses the shared Redis CacheRuntimefilesystem
lock_expire_secsfloatSeconds before an unrefreshed lock becomes stale; must be at least 1.030.0
lock_timeout_secsfloatDeprecated and ignored; runtime wait timeout remains 0.00.0
json
{
  "service_name": "production",
  "cache": {
    "provider": "redis",
    "params": {
      "mode": "sentinel",
      "endpoints": [
        "redis://sentinel-1:26379",
        "redis://sentinel-2:26379"
      ],
      "master_name": "mymaster",
      "password_env": "OPENVIKING_REDIS_PASSWORD",
      "connect_timeout_ms": 1000,
      "command_timeout_ms": 1000
    }
  },
  "storage": {
    "agfs": {
      "cachefs": {
        "backend": "cache"
      },
      "queuefs": {
        "backend": "cache"
      },
      "pathlock": {
        "provider": "cache",
        "lock_expire_secs": 30.0
      }
    }
  }
}

The canonical configuration has no global cache.enabled and no per-module Redis namespace field. CacheRuntime is initialized when CacheFS or QueueFS selects backend=cache, PathLock selects provider=cache, or multi-write selects backups.provider=cache. Cache-backed PathLock currently requires cache.provider=redis; DynamicProvider is not supported for PathLock. When all modules use local providers, cache.params is not parsed and no Provider connection is opened.

Move Provider settings to top-level cache.provider/cache.params, set top-level service_name for shared Redis names, select cachefs.backend="cache", queuefs.backend="cache", or backups.provider="cache", use Redis mode="standalone" instead of singleton, and use rediss:// instead of tls_enabled.

QueueFS Configuration ​
ParameterTypeDescriptionDefault
modestrQueueFS namespace mode: "shared" uses /queue; "worker" isolates each worker under /queue/worker-<index|pid>"shared"
backendstrQueueFS backend: "memory", "sqlite", "sqlite3", or "cache""sqlite"
db_pathstr (optional)SQLite database path for QueueFS when backend is "sqlite" or "sqlite3"null
recover_stale_secintRecover processing queue messages older than this many seconds on startup. 0 means recover all stale processing messages0
busy_timeout_msintSQLite busy timeout for QueueFS in milliseconds5000

Notes:

  • QueueFS defaults to sqlite even if the main AGFS storage backend is local, s3, or memory.
  • mode=shared keeps the historical global queue namespace at /queue; mode=worker isolates each worker under /queue/worker-<index|pid>.
  • db_path is only used when QueueFS backend is sqlite or sqlite3.
  • backend=cache automatically binds the global cache.provider + cache.params configuration and uses top-level service_name as the key prefix.
  • recover_stale_sec and busy_timeout_ms apply only to SQLite backends.
  • Redis Cluster endpoints are seed nodes, and db must be 0. Slot routing, MOVED/ASK, topology refresh, and reconnects are handled by Fred RedisProvider.
  • Redis Sentinel endpoints are Sentinel nodes and require a non-empty master_name; the provider handles master discovery and reconnects.
  • username and password authenticate data nodes; sentinel_username and sentinel_password authenticate Sentinel nodes.
  • All Redis reads go to the primary. Replica reads are not configurable.
  • tls_insecure_skip_verify=true requires rediss:// endpoints.
  • QueueFS cache keys use {service_name}:ov:*; use different service names for deployments sharing one Redis cluster.
  • Cache instances renew a 30-second heartbeat every 10 seconds. One live instance per QueueFS cache namespace holds a token-checked 30-second leader lease and scans for stale processing messages every 10 seconds. If that instance stops, another instance takes over after the lease expires; a normal shutdown removes its heartbeat so recovery can begin immediately.
  • If both storage.agfs.queuefs.db_path and legacy storage.agfs.queue_db_path are set, storage.agfs.queuefs.db_path wins.
  • If QueueFS backend is memory, any db_path or legacy queue_db_path is ignored.

Examples:

json
{
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "queuefs": {
        "mode": "shared",
        "backend": "sqlite",
        "db_path": "./data/_system/queue/custom-queue.db"
      }
    }
  }
}
json
{
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "queuefs": {
        "mode": "worker",
        "backend": "memory"
      }
    }
  }
}

Legacy compatibility example:

json
{
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "queue_db_path": "./data/_system/queue/queue.db"
    }
  }
}
S3 Backend Configuration ​
ParameterTypeDescriptionDefault
bucketstrS3 bucket namenull
regionstrAWS region where the bucket is located (e.g., us-east-1, cn-beijing)null
access_keystrS3 access key IDnull
secret_keystrS3 secret access key corresponding to the access key IDnull
endpointstrCustom S3 endpoint, required for S3-compatible services like MinIO or LocalStack. Accepts a full URL (https://... or http://...) or a bare hostname; bare hostnames are auto-prefixed with https:// or http:// based on use_sslnull
prefixstrOptional key prefix for namespace isolation""
use_sslboolEnable/disable SSL (HTTPS) for S3 connections. Also controls the scheme auto-prefixed onto bare-hostname endpoint valuestrue
use_path_stylebooltrue for PathStyle used by MinIO and some S3-compatible services; false for VirtualHostStyle used by TOS and some S3-compatible servicestrue
auto_detect_content_typeboolAutomatically infer MIME type from the object key / filename extension and set the S3 object Content-Type header during uploadfalse
directory_marker_modestrHow to persist directory markers: none, empty, or nonempty"empty"
normalize_encoding_charsstrCharacters to escape in S3 object keys as !HH hexadecimal bytes; empty string disables normalization"?#%+@"

directory_marker_mode controls how RAGFS materializes directory objects in S3:

  • empty is the default. RAGFS writes a zero-byte directory marker and preserves empty-directory semantics.
  • nonempty writes a non-empty marker payload. Use this for S3-compatible services such as TOS that reject zero-byte directory markers.
  • none switches RAGFS to prefix-style S3 semantics. RAGFS does not create directory marker objects, so empty directories are not persisted and may not be discoverable until they contain at least one child object.

Typical choices:

  • For MinIO, SeaweedFS, and most PathStyle backends, keep the default empty.
  • For TOS or other VirtualHostStyle backends that reject zero-byte directory markers, use nonempty.
  • If you want pure prefix-style behavior and do not need persisted empty directories, use none.

normalize_encoding_chars controls which characters RAGFS rewrites before issuing S3 requests:

  • The default value is "?#%+@", so only ?, #, %, +, and @ are escaped.
  • Escaped bytes are encoded as !HH, where HH is the uppercase hexadecimal value of the byte.
  • Characters not listed in normalize_encoding_chars, including Chinese and other Unicode characters, remain unchanged.
  • Set normalize_encoding_chars to "" to keep original path segments in object keys.

auto_detect_content_type is disabled by default for backward compatibility. When enabled, RAGFS infers the MIME type from the object key / filename extension and writes it to the S3 object Content-Type:

  • Detection is based on the object key / filename extension, not file content sniffing.
  • Directory markers whose keys end with / do not get a Content-Type.
  • Unknown extensions fall back to application/octet-stream.

Example:

json
{
  "storage": {
    "agfs": {
      "backend": "s3",
      "s3": {
        "bucket": "my-bucket",
        "endpoint": "s3.amazonaws.com",
        "region": "us-east-1",
        "access_key": "your-ak",
        "secret_key": "your-sk",
        "auto_detect_content_type": true
      }
    }
  }
}
PathStyle S3 Supports S3 storage in PathStyle mode, such as MinIO, SeaweedFS.
json
{
  "storage": {
    "agfs": {
      "backend": "s3",
      "s3": {
        "bucket": "my-bucket",
        "endpoint": "s3.amazonaws.com",
        "region": "us-east-1",
        "access_key": "your-ak",
        "secret_key": "your-sk",
        "normalize_encoding_chars": "?#%+@"
      }
    }
  }
}
VirtualHostStyle S3 Supports S3 storage in VirtualHostStyle mode, such as TOS.
json
{
  "storage": {
    "agfs": {
      "backend": "s3",
      "s3": {
        "bucket": "my-bucket",
        "endpoint": "s3.amazonaws.com",
        "region": "us-east-1",
        "access_key": "your-ak",
        "secret_key": "your-sk",
        "use_path_style": false,
        "directory_marker_mode": "nonempty",
        "normalize_encoding_chars": "?#%+@"
      }
    }
  }
}

vectordb ​

Vector database storage configuration

ParameterTypeDescriptionDefault
backendstrVectorDB backend type: 'local' (file-based), 'http' (remote service), 'volcengine' (cloud VikingDB), 'vikingdb' (private deployment), or 'cuvs' (local storage + GPU dense search)"local"
namestrVectorDB collection name"context"
urlstrRemote service URL for 'http' type (e.g., http://localhost:5000)null
project_namestrProject name (alias project)"default"
distance_metricstrDistance metric for vector similarity search (e.g., 'cosine', 'l2', 'ip')"cosine"
dimensionintVector embedding dimension0
sparse_weightfloatSparse weight for hybrid vector search, only effective when using hybrid index0.0
volcengineobject'volcengine' type VikingDB configuration-
vikingdbobject'vikingdb' type private deployment configuration-
cuvsobjectNVIDIA cuVS configuration for the 'cuvs' backend and the opt-in memory-aware auto mode on 'local'; see the cuVS guide-

Default local mode

{
  "storage": {
    "vectordb": {
      "backend": "local"
    }
  }
}
volcengine vikingDB Supports cloud-deployed VikingDB on Volcengine
json
{
  "storage": {
    "vectordb": {
      "name": "context",
      "backend": "volcengine",
      "project": "default",
      "volcengine": {
        "region": "cn-beijing",
        "ak": "your-access-key",
        "sk": "your-secret-key"
      }
    }
  }
}
ACL schema ​

ACL data exists only in the context collection. In addition to acl_mode: string (none, inherit, or restricted), add these scalar-indexed list<string> fields:

text
acl_direct_grants
acl_inherited_grants

Each element uses {mask}:{principal}: 1 means read, 3 means write, and 7 means manage.

Local backends add the fields to an existing collection and rebuild the scalar index during startup. Existing records are not rewritten; missing ACL fields read as acl_mode=none and empty lists.

For existing remote collections, including Volcengine VikingDB, provision these fields and scalar indexes before startup; OpenViking validates but does not alter the remote schema. Volcengine API-key data-plane mode also requires the context collection and configured index to exist. See Resource Access Control (ACL) for permission semantics.

Config Files ​

OpenViking uses two config files:

FilePurposeDefault Path
ov.confOpenViking Server configuration~/.openviking/ov.conf
ovcli.confHTTP client and CLI connection to remote server~/.openviking/ovcli.conf

When config files are at the default path, OpenViking loads them automatically — no additional setup needed.

Root-key two-file rule: server.root_api_key in ov.conf is the credential accepted by the server. root_api_key in ovcli.conf is the client-side copy used by ov --sudo. If that CLI manages this server, keep the two values identical and rotate both files together. The normal tenant-scoped api_key remains a separate user/admin credential.

Reload boundary ​

The server reads ov.conf during process startup and does not watch the file for changes. Editing embedding, vlm, rerank, retrieval, storage, or server settings requires restarting the OpenViking server. Queue work that is already running is not migrated to the new configuration, so use the normal service-manager restart procedure and verify with openviking-server doctor after the process comes back.

ovcli.conf is client-side configuration. A new ov command or newly created HTTP client reads the current file; an already-running client or plugin may keep the values it loaded at construction time and should be restarted when its connection or credential settings change.

If config files are at a different location, there are two ways to specify:

bash
# Option 1: Environment variable
export OPENVIKING_CONFIG_FILE=/path/to/ov.conf
export OPENVIKING_CLI_CONFIG_FILE=/path/to/ovcli.conf

# Option 2: Command-line argument (serve command only)
openviking-server --config /path/to/ov.conf

ov.conf ​

The config sections documented above (embedding, vlm, rerank, retrieval, grep, storage) all belong to the server's ov.conf.

For memory-related settings, add a memory section in ov.conf:

json
{
  "memory": {
    "custom_templates_dir": "/path/to/custom-memory"
  }
}
FieldDescriptionDefault
versionDeprecated and ignored. OpenViking always uses the v3 memory extraction pipeline; existing configs that set this field still load without error."v3"
custom_templates_dirCustom memory schema directory. Later definitions replace the same memory_type; new types are added. See the Prompt Guide.""
extraction_enabledWhether session commit runs long-term memory extraction.true
session_skill_extraction_enabledWhether session commit also extracts reusable skills into the current user's skill directory.false
link_enabledWhether memory extraction writes and resolves memory links.false
session_auto_commitServer-wide automatic session commit controls. This belongs under memory, not under server; see Session Auto Commit Configuration.See section below

memory.link_enabled defaults to false. When enabled, extraction resolves temporary page IDs into URIs and stores links in memory metadata. Each link contains from_uri, to_uri, link_type (default related_to), weight (default 0.5, extraction clamps it to 0–1), match_text (a word from the conversation, or null), description, and created_at.

link_type is a relation label rather than a closed enum. Extraction normalizes it to lowercase snake_case with one to three alphabetic words; invalid labels fall back to related_to. These links describe relationships between memories; the current retrieval pipeline does not traverse them or use their weights for ranking.

Session Auto Commit Configuration ​

memory.session_auto_commit controls server-wide automatic session commit behavior.

json
{
  "memory": {
    "session_auto_commit": {
      "enabled": false,
      "idle_backend": "local",
      "check_interval_seconds": 600.0,
      "scan_rate_limit_files_per_second": 2.0
    }
  }
}
ParameterTypeDescriptionDefault
enabledboolMaster switch for automatic session commits. When enabled, newly created sessions without an explicit auto_commit_policy get a default policy, and the idle-timeout background scheduler is started. When disabled, neither happensfalse
idle_backendstringIdle-timeout discovery backend: local scans session metadata; redis uses distributed expiry schedulinglocal
check_interval_secondsfloatMinimum interval between two idle scan rounds in seconds. Must be greater than 0600.0
scan_rate_limit_files_per_secondfloatMaximum number of session .meta.json files read per second during an idle scan. Must be greater than 0. Bounds background storage IO when the sessions tree is very large2.0

Notes:

  • memory.session_auto_commit is a server-wide control surface, not a per-session business policy.
  • Per-session auto-commit behavior is configured through the session-level auto_commit_policy (see the table below). Set it when creating a session with POST /api/v1/sessions, or partially update it through PATCH /api/v1/sessions/{session_id}/config. Omitting auto_commit_policy from a PATCH preserves it; sending null disables automatic commits. Use GET /api/v1/sessions/{session_id} to inspect the effective policy.
  • When enabled=false, sessions created without an explicit or server.user_config_defaults.auto_commit_policy policy keep auto commit disabled and return auto_commit_policy: null; SessionAutoCommitScheduler is not started.
  • When enabled=true:
    • Sessions without an explicit or deployment-default policy get the built-in policy below.
    • With idle_backend="local", SessionAutoCommitScheduler starts a scan round immediately, then waits at least check_interval_seconds before the next round. Session .meta.json files are read serially at no more than scan_rate_limit_files_per_second.
    • With idle_backend="redis", the server enables expiry reporting, publishing, and callback consumption together using the top-level expiry configuration.
  • Token- and message-count auto commit run inline after message writes, do not depend on the scheduler, but still require the session to carry an auto_commit_policy.

Distributed Expiry Configuration ​

All API, publisher, and callback instances that share expiry state must use the same stable service_name. It identifies the deployed document service, not an individual pod.

json
{
  "expiry": {
    "service_name": "openviking-documents",
    "publisher_separate_process": true,
    "shard_count": 128,
    "session_consumer_concurrency": 8,
    "redis": {
      "mode": "cluster",
      "endpoints": ["redis://redis-proxy.example.com:6379"],
      "username": "openviking",
      "password_env": "OPENVIKING_EXPIRY_REDIS_PASSWORD"
    }
  }
}
ParameterTypeDescriptionDefault
service_namestringStable document service name shared by all instances using the same expiry data""
publisher_separate_processboolLet openviking-server supervise publisher in a dedicated child process; when false, publisher runs in each API processtrue
shard_countintNumber of logical expiry shards; all instances sharing a namespace must use the same value, in the range 1..512128
session_consumer_concurrencyintConcurrent Session expiry callbacks per API process; must be greater than 08
redis.modestringRedis deployment mode: standalone or clusterstandalone
redis.endpointsstring arrayRedis endpoints; a cluster may use one proxy endpoint[]
redis.usernamestringRedis ACL username""
redis.password_envstringEnvironment variable containing the Redis password; startup fails if it is not set""
redis.passwordstringLegacy plaintext password; prefer password_env; mutually exclusive with it""

service_name and Redis endpoints are required when Redis idle auto commit is enabled. Credentials must use the dedicated fields above and cannot be embedded in endpoint URIs. With the default publisher_separate_process=true, openviking-server starts one internal publisher before serving traffic, stops it during shutdown, and exits if that child fails unexpectedly. This publisher is not exposed as a public CLI command.

Per-session Auto Commit Policy ​

When a session carries an auto_commit_policy, any field you omit falls back to the built-in default below. Sessions without a stored policy keep auto commit disabled. Except for the idle-timeout write minimum, values are clamped into [0, max]; unknown keys are rejected with InvalidArgumentError. See Sessions API for how to set and view it.

FieldTypeDefaultMinMaxDescription
pending_token_thresholdint15000001000000When uncommitted pending tokens exceed this value (strictly greater-than), an auto commit is triggered after a message write.
message_count_thresholdint10001000When the uncommitted live message count exceeds this value (strictly greater-than), an auto commit is triggered after a message write.
idle_timeout_secondsint864000 (off) or 300604800New positive inputs have a five-minute minimum; existing stored positive values below 300 continue to be read unchanged. Idle-timeout commits archive the full backlog and ignore keep_recent_count.
keep_recent_countint00500Number of recent live messages to keep (not archived) on a threshold-triggered auto commit. Idle-timeout commits ignore this and commit everything.
min_commit_interval_secondsint00604800Minimum seconds between two automatic commits (throttle).

Code entry: openviking/session/auto_commit_policy.py:AutoCommitPolicy.

ovcli.conf ​

You can edit this file by hand, or generate it interactively with ov config. If you maintain configurations for multiple servers, switch between them with ov config switch.

For the guided CLI setup flow, see OpenViking CLI Setup.

Config file for the HTTP client (SyncHTTPClient / AsyncHTTPClient) and CLI to connect to a remote server:

json
{
  "url": "http://localhost:1933",
  "api_key": "your-secret-key",
  "profile": false,
  "upload": {
    "mode": "local",
    "ignore_dirs": "node_modules,.cache,.nx",
    "include": "*.md,*.pdf",
    "exclude": "*.tmp,*.log"
  }
}
FieldDescriptionDefault
urlServer address(required)
api_keyNormally a user/admin key for data access. A root key here only works for admin APIs in API Key modenull (no auth)
root_api_keyRoot key used by the ov CLI with --sudo, or as a fallback when api_key is unset. The Python HTTP SDK does not use this fallbacknull
accountOptional trusted-mode account identity header valuenull
userOptional trusted-mode user identity header valuenull
profileWhether to append profile=1 to HTTP requests by default. Applies to both the Python HTTP client and the ov CLI; ov --profile can enable it per invocation. Actual effect still depends on the server enabling server.profile_enabled.false
upload.ignore_dirsDefault directory ignore list for add-resource (CSV)null
upload.includeDefault include patterns for add-resource (CSV)null
upload.excludeDefault exclude patterns for add-resource (CSV)null
upload.modePython HTTP-client temporary upload backend: "local" (per-instance disk) or "shared" (distributed shared store). The Rust ov CLI does not read this field; set OPENVIKING_UPLOAD_MODE=shared for shared uploads.null (server's temp_upload.default_mode, which itself defaults to "local")

Local directory uploads respect .gitignore files (root and nested). ignore_dirs/include/exclude apply on top of that.

For trusted gateway deployments, CLI flags can override these identity fields per command:

bash
ov --account acme --user alice ls viking://

For add-resource, upload filter flags are merged additively with ovcli.conf defaults:

bash
# ovcli.conf: upload.exclude="*.log"
ov add-resource ./docs --exclude "*.tmp"
# effective exclude sent to server: "*.log,*.tmp"

See Deployment for details.

server Section ​

When running OpenViking as an HTTP service, add a server section to ov.conf:

json
{
  "server": {
    "host": "127.0.0.1",
    "port": 1933,
    "auth_mode": "api_key",
    "root_api_key": "your-secret-root-key",
    "account_store": {
      "provider": "file",
      "params": {}
    },
    "profile_enabled": false,
    "cors_origins": ["*"],
    "public_base_url": "https://ov.example.com",
    "upload_signed_ttl_seconds": 600,
    "temp_upload": {
      "default_mode": "local",
      "shared_max_size_bytes": 536870912,
      "ttl_seconds": 43200
    },
    "user_config_defaults": {
      "add_targets": {
        "resource_uri": "viking://~/resources",
        "skill_uri": "viking://~/skills"
      },
      "memory_policy": {
        "memory_types": ["profile", "preferences", "events", "entities", "experiences"]
      }
    },
    "agent_evolution": {
      "enabled": false
    }
  }
}
FieldTypeDescriptionDefault
hoststrBind address127.0.0.1
portintBind port1933
auth_modestr / nullBuilt-in modes: "dev", "api_key", "trusted", "oidc", "ldap". When omitted/null, infer api_key from a non-empty root_api_key; otherwise infer dev.null
root_api_keystrRoot API key for multi-tenant auth in api_key mode. In trusted mode it is required on every deployment, including localhost; it does not become the source of user identitynull
account_store.providerstrComplete Account store provider. Built-in values are "file", "mysql", and "redis_mysql"; it owns account, user, group, and API-key records. "file" preserves the existing AGFS JSON registry and owns its process-local lookup indexes. "redis_mysql" keeps MySQL authoritative and adds a shared Redis cache for authentication and request identity reads."file"
account_store.paramsobjectProvider-specific startup parameters. Framework-owned filesystem and File-provider API-key hashing dependencies cannot be overridden here.{}
profile_enabledboolWhether to allow request-scoped cProfile via profile=1 on HTTP requests. When disabled, the server ignores that query parameter. When enabled, the CLI can display the returned profile, while the Python HTTP client currently triggers profiling but does not automatically attach the top-level profile field to most SDK return values.false
cors_originslistAllowed CORS origins["*"]
public_base_urlstrPublic-facing base URL emitted in MCP-issued upload instructions. Resolution order: env var OPENVIKING_PUBLIC_BASE_URL → this field → X-Forwarded-Host/X-Forwarded-Proto request headers → Host request header → listen-address fallback. Set this (or the env var) when the server runs behind a reverse proxy that does not forward X-Forwarded-* headers.null
upload_signed_ttl_secondsintTTL in seconds for the one-shot tokens minted by the MCP add_resource and add_skill tools for local-file uploads via POST /api/v1/resources/temp_upload?token=..., and by the MCP download tool for GET /api/v1/content/download?token=....600 (10 minutes)
temp_upload.default_modestrServer-side default for POST /api/v1/resources/temp_upload when the client does not send upload_mode: "local" (per-instance disk, current single-node behavior) or "shared" (distributed shared store usable across replicas). New shared uploads are stored in internal viking://upload/<created_at_ms>-<uuid>/content and meta objects, and can be consumed repeatedly for ttl_seconds."local"
temp_upload.shared_max_size_bytesintMaximum size accepted in shared mode, in bytes. Requests above this size are rejected before object-store write.536870912 (512 MiB)
temp_upload.ttl_secondsintRetention time shared by local and shared temporary uploads, in seconds. Each upload cleans files older than this for its mode; shared cleanup uses one upload-root listing, parses creation time from each first-level directory name, and recursively removes expired directories without filesystem modification times. Set to 0 to disable automatic cleanup.43200 (12 hours)
user_config_defaults.add_targets.resource_uristrDeployment default resource add directory used when add_resource omits both to and parent. viking://~/... resolves per request user.null
user_config_defaults.add_targets.skill_uristrDeployment default skill add root used when add_skill omits target_uri. Only viking://~/skills and viking://agent/skills are accepted.null
user_config_defaults.memory_policyobjectDeployment default memory extraction policy used when neither the Session nor the User has an explicit policy.null
user_config_defaults.auto_commit_policyobjectDeployment default auto-commit policy for newly created sessions without an explicit policy.null
agent_evolution.enabledboolStartup cluster default for Agent Evolution. Account and Cluster Admin settings may override it at runtime. When enabled, session commits may generate or update cases, trajectories, and experiences according to the session memory_policy. Existing memories remain readable and searchable when disabled.false

The MySQL provider stores a versioned SHA-256 digest of each high-entropy API key by default and uses a separate digest index for constant-cost lookup. Set credential_storage to "encrypted" to retain keys in AES-GCM encrypted form instead; this requires credential_encryption_key, a base64-encoded 32-byte key. MySQL never persists API-key plaintext. Hash mode exposes only key prefixes in user listings, while encrypted mode can decrypt the key for the same listing workflow. credential_storage controls the format of newly written credentials; existing rows are decoded from their material prefix. When migrating from encrypted storage to hash storage, retain credential_encryption_key until all encrypted credentials have been rotated. Replacing that encryption key directly is not supported because aesgcm:v1 records do not carry a key identifier.

Install the MySQL extra and configure the top-level service_name. Its value is stored in the service_name column on every account-store table and is the deployment boundary when multiple OpenViking clusters share a database. Changing service_name selects a different logical account-store partition.

The provider never creates or migrates database tables. Create the normalized tables in openviking/server/account_stores/sql/init.sql through your normal DBA or deployment process before starting the server. Future reviewed schema changes are stored as ordered SQL files in openviking/server/account_stores/sql/changes/. The five ov_accounts* table names are fixed; use distinct top-level service_name values rather than separate table prefixes to isolate OpenViking clusters that share a database.

json
{
  "service_name": "production",
  "server": {
    "account_store": {
      "provider": "mysql",
      "params": {
        "host": "db.example.com",
        "user": "openviking",
        "password": "secret",
        "database": "openviking",
        "credential_storage": "encrypted",
        "credential_encryption_key": "base64-encoded-32-byte-key"
      }
    }
  }
}

redis_mysql requires the redis-mysql extra and wraps the same MySQL parameters. Its cache block follows the top-level cache provider/params shape. The Python client currently supports standalone, cluster, and sentinel Redis deployments. It caches API-key bindings, users, per-user group IDs, and deletion records for a fixed 60 seconds by default. Cache fills and account mutations share an account-scoped Redis lease; normal cache hits do not acquire that lease. Mutations commit to MySQL first, then invalidate affected cache entries. If Redis is unavailable, the MySQL mutation still succeeds and stale cache entries remain valid only until their fixed TTL expires. Cache fills also maintain an account-scoped, expiry-scored key index. Each indexed cache fill removes expired index members, allowing account deletion or recreation to clear only that account's current cached users, groups, deletion records, and credential data. API-key bindings are checked against account and user credential fences, so normal key replacement, revocation, and user deletion invalidate old bindings. Redis automatically removes the index when its last sorted-set member is removed. Cleanup does not issue a separate index deletion, which could remove a newer fill's index after lease loss. Provisioning and key replacement clear negative lookups both with and without a user hint. Redis connection and command timeouts are configured with cache.params.connect_timeout_ms and cache.params.command_timeout_ms; both default to 5000 milliseconds when omitted. These are socket timeouts, not an overall request deadline.

json
{
  "server": {
    "account_store": {
      "provider": "redis_mysql",
      "params": {
        "mysql": {
          "host": "db.example.com",
          "user": "openviking",
          "password": "secret",
          "database": "openviking"
        },
        "cache": {
          "provider": "redis",
          "params": {
            "mode": "sentinel",
            "endpoints": [
              "rediss://sentinel-1.example.com:26379",
              "rediss://sentinel-2.example.com:26379",
              "rediss://sentinel-3.example.com:26379"
            ],
            "master_name": "openviking",
            "username": "openviking",
            "password_env": "OPENVIKING_REDIS_PASSWORD",
            "sentinel_password_env": "OPENVIKING_SENTINEL_PASSWORD",
            "key_prefix": "ov:account-auth",
            "ttl_seconds": 60,
            "lock_ttl_seconds": 60,
            "lock_wait_seconds": 30
          }
        }
      }
    }
  }
}

Omitting auth_mode (or setting it to null) selects api_key when a non-empty root_api_key is configured, and dev otherwise. dev is allowed only on localhost and accepts requests without authentication. An empty-string root_api_key is invalid.

Explicit auth_mode: "api_key" requires a non-empty root_api_key, including on localhost; without it, startup fails instead of falling back to development mode. Use the root key with the Admin API to create accounts and user/admin keys; use those tenant-bound keys for data access. trusted mode accepts account/user identity headers from a trusted gateway and does not require user-key provisioning first. Its root key is required for startup and every authenticated request, including on localhost. For role resolution, OIDC/LDAP setup, and gateway requirements, see Authentication.

user_config_defaults provides deployment defaults for add targets and memory extraction. For add operations, explicit request targets still win: add_resource.to / add_resource.parent take precedence over user defaults, and add_skill.target_uri takes precedence over user defaults. Memory policy precedence is Session policy > User settings/user_config.json policy > server.user_config_defaults.memory_policy > kernel default. server.agent_evolution.enabled supplies the startup default. Runtime resolution is Account override > Cluster runtime override > that startup value. Use the Admin settings APIs for changes without restarting; editing ov.conf directly takes effect after restart.

Supported add target URIs:

  • resource_uri is used as the default add_resource parent directory, equivalent to parent=<uri>, create_parent=true. It must be a writable resource directory URI for the request user. Supported forms are viking://resources or viking://resources/..., viking://~/resources or viking://~/resources/..., viking://user/{user_id}/resources or viking://user/{user_id}/resources/..., and viking://user/{user_id}/peers/{peer_id}/resources or viking://user/{user_id}/peers/{peer_id}/resources/.... The viking://~/... home alias resolves per request user.
  • skill_uri is used as the default add_skill target root. In v1, only viking://~/skills and viking://agent/skills are accepted; explicit viking://user/{user_id}/skills is not accepted.
  • Legacy spellings: viking://user/resources and viking://user/skills written in earlier configs are auto-normalized to viking://~/resources and viking://~/skills when the config is loaded, and the server logs an info message. Prefer the viking://~/... form in new configs — outside add_targets, the uid-less spelling is rejected at the request boundary.

Runtime configuration source ​

The top-level runtime_config section selects the source of runtime settings at startup; it is not nested under server.

FieldDefaultMeaning
source"file"file stores settings through AGFS; memory keeps them only in this process; a registered source name selects a custom implementation.
modulenullOptional Python module imported to register a custom source. Sources can also register through the openviking.config_source entry-point group.
params{}Parameters passed to the custom source factory as ConfigSourceContext.params.

A custom source implements ConfigSource.load(scope), update(scope, mutate), and delete(scope). It must guard read-modify-write against concurrent updates, including calls from different threads or event loops. The mutation callback can be retried and must have no side effects.

The file source stores Account settings at /local/{account_id}/_system/setting.json and Cluster settings at /local/_system/runtime_config/cluster.json in AGFS. It retains the previous bytes as setting.backup.json and cluster.backup.json beside those files and can read the backup if the primary file is missing or invalid. Existing Account settings keep their original path.

The manager polls the Cluster and cached Accounts every 30 seconds. Accounts unused for 24 hours are evicted from the cache and stop being polled; their persisted settings remain. A refresh failure keeps the last valid configuration. Admin settings GET returns the stored override, not a merged effective configuration or proof that every derived client has switched. Use the Admin API to update settings; see Reload boundary for consumer behavior.

Tool output externalization ​

server.tool_output_externalization moves oversized session tool outputs to the session's tool-results/ store and leaves a preview stub plus tool_output_ref. Retrieve the full output through the session tool-result read, search, and list APIs in Session management.

FieldDefaultMeaning
enabledtrueEnable externalization.
threshold_chars20000Externalize an individual output above this character count.
preview_chars2000Preview character target for one output.
assistant_turn_inline_budget_chars100000Inline budget across tool outputs in one assistant turn.
assistant_turn_preview_budget_chars10000Budget used to allocate previews across the turn.
min_preview_chars1000Lower bound when allocating per-output previews.
aggregate_selection_strategy"largest_first"Only supported strategy: select larger outputs first when reducing the turn's inline size.
failure_mode"preserve_raw"On storage failure: preserve_raw keeps the original output, reject raises an error, and preview_only keeps only a preview without a recoverable reference.

The budgets guide output selection and preview allocation; preview metadata and the minimum preview size can prevent a strict cap. With preserve_raw, failed writes can also leave the turn above its budget.

Usage Reporter ​

The optional Usage Reporter extracts memory usage events from committed session tool parts. The built-in file log sink writes each event as one flat JSON object to a dedicated hourly rotating file:

json
{
  "server": {
    "usage_reporter": {
      "enabled": true,
      "extractors": ["memory_usage"],
      "sinks": [
        {
          "type": "file_log",
          "config": {
            "path": "/var/log/openviking_usage/usage.log",
            "resource_id_env": "OV_RESOURCE_ID",
            "rotation_interval_hours": 1,
            "backup_count": 168
          }
        }
      ]
    }
  }
}

The built-in file_log sink replaces the earlier http sink. Deployments using "type": "http" must migrate to file_log and collect the dedicated log files, or configure a custom sink that implements their delivery contract.

Set the environment variable named by resource_id_env before starting the server. Its value identifies the deployed OpenViking resource and isolates otherwise identical account, user, and URI combinations. The sink creates the parent directory, appends events immediately, rotates the active file every UTC hour, and retains backup_count rotated files. It does not write to the default OpenViking stdout log.

Each line has the following form:

json
{"event_time":"2026-08-05 11:30:00","tenant_id":"resource_id:ov-example;account_id:default;user_id:default;resource_uri:viking://user/default/memories/experiences/example.md","event_name":"experience.recall.count","object_id":"ue_<sha256>","count":1,"tags":{"resource_type":"experience"}}

event_time is UTC. tenant_id combines the deployment resource ID, event account, user, and Experience URI. memory.recalled maps to experience.recall.count, while memory.injected maps to experience.inject.count. object_id is the stable Usage Event ID. Downstream consumers must deduplicate by the composite (tenant_id, object_id) key rather than by object_id globally. Aggregate usage with sum(count) after filtering by tenant_id, event_name, and the desired event_time range. File collection and downstream delivery remain best-effort.

Custom sink ​

Add a sink entry under server.usage_reporter.sinks:

json
{"type": "custom", "class_path": "my_app.usage.MySink", "config": {}}

The class must be importable by the server. config is passed as keyword arguments to its constructor. Implement async def write(self, *, events: list[UsageEvent]) -> None; UsageEvent is defined in openviking.usage_reporter.models and exposes event_id and to_dict(). The input is a list of events, not the flat file_log JSON rows.

Delivery is best-effort: sink errors and timeouts are logged, and phase-2 replay can send an event again. A custom sink must define its own retry and deduplication policy. If it emits the file-log schema, use object_id = event.event_id and deduplicate by (tenant_id, object_id) within the deployment/account/user/resource scope described above. This is not an exactly-once delivery contract.

For startup and deployment details see Deployment, for authentication see Authentication.

storage.transaction Section ​

storage.transaction is deprecated and kept only for legacy compatibility. Use storage.agfs.pathlock for the active PathLock provider and expiry configuration; Redis key names come from top-level service_name. When legacy fields are still present, OpenViking logs a warning at runtime; lock_timeout is deprecated and ignored, lock_expire is automatically mapped when the new field is unset, and redo_recovery_enabled is ignored.

Recommended configuration:

json
{
  "storage": {
    "agfs": {
      "pathlock": {
        "provider": "filesystem",
        "lock_expire_secs": 30.0
      }
    }
  }
}

Legacy compatibility form (not recommended for new deployments):

json
{
  "storage": {
    "transaction": {
      "lock_expire": 30.0
    }
  }
}
ParameterTypeDescriptionDefault
lock_timeoutfloatDeprecated and ignored. Runtime wait timeout is fixed at 0.0.0.0
lock_expirefloatDeprecated. Use storage.agfs.pathlock.lock_expire_secs. Automatically mapped when the new field is unset.30.0
redo_recovery_enabledboolDeprecated and ignored. Session commit phase-2 recovery now resumes from the persistent session_commit queue.true

For details on the lock mechanism, see Path Locks and Crash Recovery.

Task Tracker Persistence ​

The task tracker records async task state for endpoints that return a task_id (task types include session_commit, add_resource, add_skill, and admin_reindex). Task records are written to AGFS. Instances sharing the same persistent storage can query the same task_id, and task history survives a restart subject to retention cleanup. Records on the memory filesystem backend do not survive a process restart.

No storage.task_tracker configuration is required. If an older configuration still includes storage.task_tracker, OpenViking logs a warning and ignores it.

Task record files are stored under the owning account's system directory:

text
/local/{account_id}/_system/tasks/{user_id}/{task_id}.json

encryption Section ​

At-rest encryption protects newly written files with account-specific keys through the existing APIs. Existing plaintext files are not encrypted automatically; migrate or rewrite them separately.

json
{
  "encryption": {
    "enabled": true,
    "provider": "local|vault|volcengine_kms"
  }
}
ParameterTypeDescriptionDefault
enabledboolWhether encryption is enabledfalse
providerstrKey provider: "local", "vault", or "volcengine_kms"-
api_key_hashing.enabledboolFile-provider only: whether to apply Argon2id one-way hashing to API key values (independent of file-level enabled); see Encryption Guidefalse

Local (File) ​

Suitable for development environments and single-node deployments:

json
{
  "encryption": {
    "enabled": true,
    "provider": "local",
    "local": {
      "key_file": "~/.openviking/master.key"
    }
  }
}
ParameterTypeDescriptionDefault
local.key_filestrRoot key file path~/.openviking/master.key

Vault (HashiCorp Vault) ​

Suitable for production and multi-cloud deployments:

json
{
  "encryption": {
    "enabled": true,
    "provider": "vault",
    "vault": {
      "address": "https://vault.example.com:8200",
      "token": "vault-token-xxx",
      "mount_point": "transit",
      "key_name": "openviking-root"
    }
  }
}
ParameterTypeDescriptionDefault
vault.addressstrVault service address-
vault.tokenstrVault access token-
vault.mount_pointstrTransit engine mount point"transit"
vault.key_namestrRoot key name"openviking-root"

Volcengine KMS ​

Suitable for Volcengine cloud deployments:

json
{
  "encryption": {
    "enabled": true,
    "provider": "volcengine_kms",
    "volcengine_kms": {
      "key_id": "kms-key-id-xxx",
      "region": "cn-beijing",
      "access_key": "AKLTxxxxxxxx",
      "secret_key": "Tmpxxxxxxxx"
    }
  }
}
ParameterTypeDescriptionDefault
volcengine_kms.key_idstrKMS key ID-
volcengine_kms.regionstrRegion"cn-beijing"
volcengine_kms.access_keystrVolcengine Access Key-
volcengine_kms.secret_keystrVolcengine Secret Key-

For detailed encryption explanations, see Data Encryption. For complete usage instructions, see Encryption Guide.

Configuration Structure ​

This illustrates the main sections, not a complete runnable configuration or JSON Schema. Replace string and |-separated options with actual values; see the module sections for fields omitted here.

json
{
  "embedding": {
    "max_concurrent": 10,
    "max_retries": 3,
    "text_source": "content_only",
    "max_input_tokens": 4096,
    "dense": {
      "provider": "volcengine",
      "api_key": "string",
      "model": "string",
      "dimension": 1024,
      "input": "multimodal",
      "encoding_format": "float|base64"
    }
  },
  "vlm": {
    "provider": "string",
    "api_key": "string",
    "model": "string",
    "api_base": "string",
    "thinking": false,
    "max_concurrent": 32,
    "max_retries": 3,
    "extra_headers": {},
    "extra_request_body": {}
  },
  "rerank": {
    "provider": "vikingdb|cohere|openai|litellm|jev",
    "api_key": "string",
    "model": "string",
    "api_base": "string",
    "max_input_tokens": 0,
    "threshold": 0.1,
    "extra_headers": {}
  },
  "retrieval": {
    "enable_intent": true
  },
  "encryption": {
    "enabled": false,
    "provider": "local|vault|volcengine_kms",
    "local": {
      "key_file": "~/.openviking/master.key"
    },
    "vault": {
      "address": "https://vault.example.com:8200",
      "token": "string",
      "mount_point": "transit",
      "key_name": "openviking-root"
    },
    "volcengine_kms": {
      "key_id": "string",
      "region": "cn-beijing",
      "access_key": "string",
      "secret_key": "string"
    }
  },
  "storage": {
    "workspace": "string",
    "agfs": {
      "backend": "local|s3|memory",
      "timeout": 10,
      "pathlock": {
        "provider": "filesystem",
        "lock_expire_secs": 30.0
      }
    },
    "vectordb": {
      "backend": "local|cuvs|http|volcengine|vikingdb",
      "url": "string",
      "project": "string"
    }
  },
  "server": {
    "host": "127.0.0.1",
    "port": 1933,
    "root_api_key": "string",
    "account_store": {
      "provider": "file",
      "params": {}
    },
    "cors_origins": ["*"]
  }
}

Notes:

  • storage.vectordb.sparse_weight controls hybrid (dense + sparse) indexing/search. It only takes effect when you use a hybrid index; set it > 0 to enable sparse signals.

Troubleshooting ​

API Key Error ​

Error: Invalid API key

Check your API key is correct and has the required permissions.

Vector Dimension Mismatch ​

Error: Vector dimension mismatch

Ensure the dimension in config matches the model's output dimension.

VLM Timeout ​

Error: VLM request timeout
  • Check network connectivity
  • Increase timeout in config
  • For intermittent timeouts, increase vlm.max_retries moderately
  • Try a smaller model
  • For bulk ingestion, consider lowering vlm.max_concurrent

Rate Limiting ​

Error: Rate limit exceeded

Check the provider error to determine whether the limit applies to request count, token usage, or account quota, then adjust request pacing or request the corresponding quota.

  • Lower embedding.max_concurrent / vlm.max_concurrent first
  • Keep a small max_retries value for occasional 429s; set it to 0 if you prefer fail-fast behavior

Open source under the AGPL-3.0 License. Font licenses