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:
openviking-server init
openviking-server doctoropenviking-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:
{
"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:
{
"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 rewritesov.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:
| Scope | Configuration | Lifecycle | Effective behavior |
|---|---|---|---|
| Cluster | agent_evolution | Dynamic | ROOT can update it through the Admin API; it is used as the cluster default. |
| Account | feishu, agent_evolution | Dynamic | ROOT 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. |
| Account | github, acl | Dynamic | ROOT or the Account ADMIN can update it. These sections have no Cluster fallback. |
| Account | vlm, query_planner | Dynamic | ROOT-only. Each configured section requires model and a non-empty credentials array; timeout is optional. ADMIN callers cannot read or update these sections. |
| Account | embedding | Mixed | ROOT-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. |
| Account | vectordb | Create-only | ROOT-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:
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}/configurationThe request body wraps a PATCH document in settings:
{
"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)
{
"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
{
"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.
{
"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
{
"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
{
"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
{
"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
| Parameter | Type | Description |
|---|---|---|
max_concurrent | int | Maximum concurrent embedding requests (embedding.max_concurrent, default: 10; must be >= 1) |
max_retries | int | Maximum retry attempts for transient embedding provider errors (embedding.max_retries, default: 3; 0 disables retry) |
text_source | str | Text 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_tokens | int | Maximum estimated raw text tokens sent to the embedding model when content is used. Default: 4096 |
provider | str | "openai", "azure", "volcengine", "vikingdb", "jina", "ollama", "gemini", "voyage", "dashscope", "minimax", "cohere", "litellm", or "local" |
api_key | str | API key |
model | str | Model name |
dimension | int | Vector dimension. For Voyage, this maps to output_dimension |
input | str | Input type: "text" or "multimodal" |
batch_size | int | Batch size for embedding requests |
encoding_format | str | (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_body | object | (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).
{
"embedding": {
"circuit_breaker": {
"failure_threshold": 5,
"reset_timeout": 60,
"max_reset_timeout": 600
}
}
}| Parameter | Type | Description |
|---|---|---|
circuit_breaker.failure_threshold | int | Consecutive failures required to open the breaker (default: 5) |
circuit_breaker.reset_timeout | float | Base reset timeout in seconds (default: 60) |
circuit_breaker.max_reset_timeout | float | Maximum reset timeout in seconds when backing off (default: 600) |
Available Models
| Model | Dimension | Input Type | Notes |
|---|---|---|---|
doubao-embedding-vision-251215 | 1024 | multimodal | Recommended |
doubao-embedding-250615 | 1024 | text | Text 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 APIazure: Azure OpenAI Embedding APIvolcengine: Volcengine Embedding APIvikingdb: VikingDB Embedding APIjina: Jina AI Embedding APIollama: Ollama local OpenAI-compatible Embedding APIvoyage: Voyage AI Embedding APIminimax: MiniMax Embedding APIcohere: Cohere Embedding APIgemini: Google Gemini Embedding API (text-only; requiresgoogle-genai>=1.39.0)dashscope: DashScope (Alibaba Tongyi) Embedding APIlitellm: LiteLLM Embedding APIlocal: Local GGUF embedding models
OpenAI-compatible provider example with JSON float embeddings:
{
"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:
{
"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:
{
"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:
{
"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:
{
"embedding": {
"dense": {
"provider": "vikingdb",
"model": "bge_large_zh",
"ak": "your-access-key",
"sk": "your-secret-key",
"region": "cn-beijing",
"dimension": 1024
}
}
}jina provider example:
{
"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:
{
"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:
{
"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.0in 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]" --upgradeorpip install "openviking[gemini-async]".
{
"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:
{
"embedding": {
"dense": {
"provider": "dashscope",
"api_key": "${DASHSCOPE_API_KEY}",
"model": "text-embedding-v4",
"dimension": 1024,
"input": "text"
}
}
}Available DashScope models:
| Model | Dimension | Input Type | Notes |
|---|---|---|---|
text-embedding-v3 | 1024 | text | Optimized for Chinese |
text-embedding-v4 | 1024 | text | Optimized for Chinese |
tongyi-embedding-vision-plus | 1152 | multimodal | Supports fusion via enable_fusion |
tongyi-embedding-vision-flash | 768 | multimodal | Faster, lower cost |
qwen3-vl-embedding | 2560 | multimodal | Text + image + video |
qwen2.5-vl-embedding | 1024 | multimodal | Text + image + video |
Input and multimodal parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
input | str | "multimodal" | Embedding mode: "text" or "multimodal" |
enable_fusion | bool | false | Enable fusion vectors for tongyi-embedding-vision-* models |
res_level | int | 2 | Image resolution level (1=high, 2=medium, 3=low) |
max_video_frames | int | 16 | Maximum video frames to embed |
Endpoint selection — DashScope provides api_base defaults for China (cn) and international (intl) regions:
| Region | api_base | Notes |
|---|---|---|
| China | https://dashscope.aliyuncs.com (default) | Recommended for users in mainland China |
| International | https://dashscope-intl.aliyuncs.com | For 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):
{
"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:
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.
{
"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
{
"embedding": {
"hybrid": {
"provider": "volcengine",
"api_key": "your-api-key",
"model": "doubao-embedding-vision-251215",
"dimension": 1024
}
}
}Option 2: Combine dense + sparse
{
"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).
{
"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
| Parameter | Type | Description |
|---|---|---|
api_key | str | API key. Optional for openai-codex when Codex OAuth is available, and optional for litellm routes that use provider-native credentials |
forward_api_key | bool | LiteLLM 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 |
model | str | Model name |
api_base | str | API endpoint (optional) |
thinking | bool | Enable thinking mode for VolcEngine models (default: false) |
max_concurrent | int | Maximum concurrent semantic LLM calls (default: 32) |
max_retries | int | Maximum retry attempts for transient VLM provider errors (default: 3; 0 disables retry) |
credentials | array | Ordered 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_seconds | float | Time threshold for attempting a step back toward a higher-priority credential after failover (default: 600) |
failback_request_count | int | Successful requests on a lower-priority credential before attempting a step back (default: 50) |
backup | object | Optional 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 |
timeout | float | Per-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_expiry | float | Idle 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_headers | object | Custom HTTP headers for compatible HTTP providers. kimi also accepts header overrides, but already injects the required subscription headers by default |
extra_request_body | object | Extra JSON body fields for OpenAI-compatible completion requests, useful for provider-specific options such as Ollama {"think": false} |
reasoning_effort | str | Reasoning 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 |
media | object | Audio/video runtime controls. Media understanding reuses this VLM's provider, model, credentials, client, timeout, retry, headers, output-token limit, failover, and token accounting |
media.enabled | bool | Enable audio/video understanding (default: false) |
media.max_concurrent | int | Maximum concurrent audio/video calls (default: 2) |
media.file_processing_timeout | float | Maximum provider-side preprocessing wait in seconds (default: 1800) |
media.file_poll_interval | float | Provider-side preprocessing poll interval in seconds (default: 3) |
media.video_fps | float | Video 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
| Model | Notes |
|---|---|
doubao-seed-2-0-lite-260428 | Recommended for semantic extraction |
doubao-pro-32k | For longer context |
When resources are added, VLM generates:
- L0 (Abstract): file or directory summary, with a default limit of 256 characters
- 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 APIopenai: OpenAI-compatible VLM APIopenai-codex: Codex VLM via ChatGPT/Codex OAuthkimi: Kimi Coding subscription endpoint with built-in provider defaultsglm: Z.AI GLM Coding Plan endpoint with OpenAI-compatible requestslitellm: LiteLLM VLM API, including explicit LiteLLM routes such asbedrock/,sagemaker/,vertex_ai/, andazure/
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:
{
"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-RefererandX-OpenRouter-Titleheaders provide app attribution, not API authentication;X-Titleremains 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 withoutx-opencode-sessionfail with HTTP 400MissingSessionID. 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:
{
"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.
{
"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
| Type | Stored by the existing parser | Understood by Ark in this release |
|---|---|---|
| Audio | MP3, WAV, OGG, FLAC, AAC, M4A, OPUS, AC3 | MP3, WAV, AAC, M4A |
| Video | MP4, AVI, MOV, MKV, WEBM, FLV, WMV, TS | MP4, 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:
{
"bot": {
"compile": {
"map_concurrency": 8,
"shuffle_concurrency": 4,
"shuffle_batch_size": 4,
"reduce_concurrency": 6
}
}
}| Field | Default | Description |
|---|---|---|
map_concurrency | Inherits vlm.max_concurrent | Concurrent Map extraction jobs |
shuffle_concurrency | Inherits vlm.max_concurrent | Concurrent Shuffle routing jobs and embedding batches |
shuffle_batch_size | 4 | Maximum primary records per routing request, independent of concurrency and the soft character budget |
reduce_concurrency | Inherits vlm.max_concurrent | Concurrent 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 inityou can optionally enable a local lightweight query planner; the wizard pulls the Ollama model and writes thequery_plannerconfig for you. For recognized query-planner models,search()selects the matching bundled prompt at runtime. Models not in the mapping keep usingretrieval.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:
ollama pull guoxuter/ov_intent_analysis_sft:v7_q8Then add the following to your OpenViking configuration:
{
"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.
{
"feishu": {
"app_id": "",
"app_secret": "",
"domain": "https://open.feishu.cn",
"max_rows_per_sheet": 1000,
"max_records_per_table": 1000
}
}| Parameter | Type | Description |
|---|---|---|
app_id | str | Feishu app ID (can also be set via FEISHU_APP_ID env var) |
app_secret | str | Feishu app secret (can also be set via FEISHU_APP_SECRET env var) |
domain | str | Feishu API domain. Use https://open.larksuite.com for Lark international |
max_rows_per_sheet | int | Maximum rows to import per spreadsheet sheet (default: 1000) |
max_records_per_table | int | Maximum 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:
| Field | Type | Description | Default |
|---|---|---|---|
github_domains | list[str] | Allowed GitHub hosts (add your GitHub Enterprise host here) | ["github.com", "www.github.com"] |
gitlab_domains | list[str] | Allowed GitLab hosts (add your self-hosted GitLab host here) | ["gitlab.com", "www.gitlab.com"] |
azure_devops_domains | list[str] | Allowed Azure DevOps hosts | ["dev.azure.com", "ssh.dev.azure.com", "vs-ssh.visualstudio.com"] |
code_hosting_domains | list[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:
{
"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).
{
"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"
}
}
}| Parameter | Type | Description |
|---|---|---|
strategy | str | Parsing strategy: local / mineru / auto (default auto) |
mineru_endpoint | str | MinerU API base URL (e.g. http://127.0.0.1:8000) |
mineru_timeout | float | Request timeout in seconds (default 300.0) |
mineru_bodys | dict | MinerU 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.
| Field | Default | Meaning |
|---|---|---|
enable | false | Enable 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_url | false | Allow 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_upload | false | Enable resumable uploads. |
upload_simple_max_bytes | 536870912 (512 MiB) | Simple-upload size threshold. |
upload_part_size_bytes | 8388608 (8 MiB) | Resumable-upload part size. |
http_timeout_seconds | 10.0 | Timeout for an individual HTTP request. |
response_timeout_seconds | 1800 | Timeout while waiting for the parsing response. |
poll_interval_ms | 3000 | Response 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):
{
"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):
{
"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:
{
"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:
{
"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 fromvercel 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_basemust include the/typesafesuffix.https://ai-gateway.vercel.sh/v1is 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, withcandidate_{index}mapped to input indices. Scores come fromprobabilitiesin 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
| Parameter | Type | Description |
|---|---|---|
provider | str | "vikingdb", "cohere", "openai", "litellm", or "jev". Auto-detected if omitted. |
ak | str | VikingDB Access Key (vikingdb provider only) |
sk | str | VikingDB Secret Key (vikingdb provider only) |
model_name | str | Model name (vikingdb provider only, default: doubao-seed-rerank) |
api_key | str | API key (for openai, cohere, or jev providers) |
api_base | str | Endpoint URL (for openai or jev; Jev defaults to https://api.typesafe.ai, Vercel uses https://ai-gateway.vercel.sh/typesafe) |
model | str | Model name for OpenAI-compatible, LiteLLM, or Jev providers |
mode | "noul", "choice", or null | Jev rerank mode. null and omission use "noul" |
timeout | float | HTTP request timeout in seconds for HTTP rerank providers, including Jev. Default: 30.0 |
max_input_tokens | int | Maximum 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_payloads | bool | Log complete rerank request and response payloads. May expose query and document content. Default: false |
threshold | float | Score threshold between 0.0 and 1.0; results below this are filtered out. Default: 0.1 |
extra_headers | object | Custom HTTP headers (for OpenAI-compatible providers, optional) |
Supported providers:
vikingdb: Volcengine VikingDB Rerank API (uses AK/SK)cohere: Cohere Rerank APIopenai: OpenAI-compatible Rerank APIlitellm: LiteLLM Rerank APIjev: 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.
{
"retrieval": {
"enable_intent": true,
"recall_intent_timeout_s": 5.0,
"recall_rewrite_timeout_s": 30.0
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
enable_intent | bool | Run 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:
| Parameter | Type | Description | Default |
|---|---|---|---|
recall_intent_timeout_s | float | Timeout for session intent analysis and context query expansion; on timeout or failure the original user query is used | 5.0 |
recall_rewrite_timeout_s | float | Timeout for the digest rewrite; on timeout digest is empty and rendered is returned as usual | 30.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.
{
"grep": {
"engine": "auto",
"switch_to_remote_threshold": 10000
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
engine | str | Search 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_threshold | int | L2 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.
{
"glob": {
"engine": "fs",
"switch_to_remote_threshold": 100
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
engine | str | Path 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_threshold | int | Record-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
| Parameter | Type | Description | Default |
|---|---|---|---|
workspace | str | Local data storage path (main configuration) | "./data" |
skip_process_lock | bool | Skip 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 |
agfs | object | RAGFS (Rust-based AGFS) configuration | {} |
vectordb | object | Vector database storage configuration | {} |
task_store | object | Durable Task/Work backend configuration | {"provider":"file"} |
{
"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:
{
"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)
| Parameter | Type | Description | Default |
|---|---|---|---|
backend | str | "local", "s3", or "memory" | "local" |
timeout | float | Request timeout in seconds | 10.0 |
backups | object | Multi-write storage configuration. When set, the top-level backend acts as the primary backend and backups.items[] defines backup backends | null |
queuefs | object | QueueFS 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_path | str (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 |
s3 | object | S3 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.
{
"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:
| Parameter | Type | Description | Default |
|---|---|---|---|
initial_partitions | int | Initial metadata partitions; changes do not affect existing accounts | 16 |
checkpoint_interval_secs | int | Checkpoint discovery interval; minimum 60 | 86400 |
provider | str | Metadata provider: filesystem or top-level CacheRuntime-backed cache | "filesystem" |
items | array | Backups 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:
| Parameter | Type | Description | Default |
|---|---|---|---|
provider | str | Global Cache Provider. This release supports redis | required |
params | object | Provider-owned parameters; parsed as Redis connection settings when provider=redis | {} |
storage.agfs.cachefs only controls CacheFS behavior:
| Parameter | Type | Description | Default |
|---|---|---|---|
backend | str | local keeps the original filesystem path; cache enables the CacheFS wrapper | local |
max_file_size_bytes | int | Maximum full-file object size admitted to cache | 1048576 |
traversal_mode | str | backend or cached_traversal | backend |
bypass_prefixes | array[str] | Path prefixes that bypass cache | [] |
storage.agfs.pathlock selects the PathLock storage provider:
| Parameter | Type | Description | Default |
|---|---|---|---|
provider | str | filesystem, memory, or cache; cache uses the shared Redis CacheRuntime | filesystem |
lock_expire_secs | float | Seconds before an unrefreshed lock becomes stale; must be at least 1.0 | 30.0 |
lock_timeout_secs | float | Deprecated and ignored; runtime wait timeout remains 0.0 | 0.0 |
{
"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
| Parameter | Type | Description | Default |
|---|---|---|---|
mode | str | QueueFS namespace mode: "shared" uses /queue; "worker" isolates each worker under /queue/worker-<index|pid> | "shared" |
backend | str | QueueFS backend: "memory", "sqlite", "sqlite3", or "cache" | "sqlite" |
db_path | str (optional) | SQLite database path for QueueFS when backend is "sqlite" or "sqlite3" | null |
recover_stale_sec | int | Recover processing queue messages older than this many seconds on startup. 0 means recover all stale processing messages | 0 |
busy_timeout_ms | int | SQLite busy timeout for QueueFS in milliseconds | 5000 |
Notes:
- QueueFS defaults to
sqliteeven if the main AGFS storage backend islocal,s3, ormemory. mode=sharedkeeps the historical global queue namespace at/queue;mode=workerisolates each worker under/queue/worker-<index|pid>.db_pathis only used when QueueFS backend issqliteorsqlite3.backend=cacheautomatically binds the globalcache.provider + cache.paramsconfiguration and uses top-levelservice_nameas the key prefix.recover_stale_secandbusy_timeout_msapply only to SQLite backends.- Redis Cluster endpoints are seed nodes, and
dbmust be0. 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. usernameandpasswordauthenticate data nodes;sentinel_usernameandsentinel_passwordauthenticate Sentinel nodes.- All Redis reads go to the primary. Replica reads are not configurable.
tls_insecure_skip_verify=truerequiresrediss://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
processingmessages 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_pathand legacystorage.agfs.queue_db_pathare set,storage.agfs.queuefs.db_pathwins. - If QueueFS backend is
memory, anydb_pathor legacyqueue_db_pathis ignored.
Examples:
{
"storage": {
"workspace": "./data",
"agfs": {
"backend": "local",
"queuefs": {
"mode": "shared",
"backend": "sqlite",
"db_path": "./data/_system/queue/custom-queue.db"
}
}
}
}{
"storage": {
"workspace": "./data",
"agfs": {
"backend": "local",
"queuefs": {
"mode": "worker",
"backend": "memory"
}
}
}
}Legacy compatibility example:
{
"storage": {
"workspace": "./data",
"agfs": {
"backend": "local",
"queue_db_path": "./data/_system/queue/queue.db"
}
}
}S3 Backend Configuration
| Parameter | Type | Description | Default |
|---|---|---|---|
bucket | str | S3 bucket name | null |
region | str | AWS region where the bucket is located (e.g., us-east-1, cn-beijing) | null |
access_key | str | S3 access key ID | null |
secret_key | str | S3 secret access key corresponding to the access key ID | null |
endpoint | str | Custom 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_ssl | null |
prefix | str | Optional key prefix for namespace isolation | "" |
use_ssl | bool | Enable/disable SSL (HTTPS) for S3 connections. Also controls the scheme auto-prefixed onto bare-hostname endpoint values | true |
use_path_style | bool | true for PathStyle used by MinIO and some S3-compatible services; false for VirtualHostStyle used by TOS and some S3-compatible services | true |
auto_detect_content_type | bool | Automatically infer MIME type from the object key / filename extension and set the S3 object Content-Type header during upload | false |
directory_marker_mode | str | How to persist directory markers: none, empty, or nonempty | "empty" |
normalize_encoding_chars | str | Characters 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:
emptyis the default. RAGFS writes a zero-byte directory marker and preserves empty-directory semantics.nonemptywrites a non-empty marker payload. Use this for S3-compatible services such as TOS that reject zero-byte directory markers.noneswitches 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, whereHHis 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_charsto""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 aContent-Type. - Unknown extensions fall back to
application/octet-stream.
Example:
{
"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.{
"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.{
"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
| Parameter | Type | Description | Default |
|---|---|---|---|
backend | str | VectorDB backend type: 'local' (file-based), 'http' (remote service), 'volcengine' (cloud VikingDB), 'vikingdb' (private deployment), or 'cuvs' (local storage + GPU dense search) | "local" |
name | str | VectorDB collection name | "context" |
url | str | Remote service URL for 'http' type (e.g., http://localhost:5000) | null |
project_name | str | Project name (alias project) | "default" |
distance_metric | str | Distance metric for vector similarity search (e.g., 'cosine', 'l2', 'ip') | "cosine" |
dimension | int | Vector embedding dimension | 0 |
sparse_weight | float | Sparse weight for hybrid vector search, only effective when using hybrid index | 0.0 |
volcengine | object | 'volcengine' type VikingDB configuration | - |
vikingdb | object | 'vikingdb' type private deployment configuration | - |
cuvs | object | NVIDIA 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{
"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:
acl_direct_grants
acl_inherited_grantsEach 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:
| File | Purpose | Default Path |
|---|---|---|
ov.conf | OpenViking Server configuration | ~/.openviking/ov.conf |
ovcli.conf | HTTP 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_keyinov.confis the credential accepted by the server.root_api_keyinovcli.confis the client-side copy used byov --sudo. If that CLI manages this server, keep the two values identical and rotate both files together. The normal tenant-scopedapi_keyremains 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:
# 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.confov.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:
{
"memory": {
"custom_templates_dir": "/path/to/custom-memory"
}
}| Field | Description | Default |
|---|---|---|
version | Deprecated and ignored. OpenViking always uses the v3 memory extraction pipeline; existing configs that set this field still load without error. | "v3" |
custom_templates_dir | Custom memory schema directory. Later definitions replace the same memory_type; new types are added. See the Prompt Guide. | "" |
extraction_enabled | Whether session commit runs long-term memory extraction. | true |
session_skill_extraction_enabled | Whether session commit also extracts reusable skills into the current user's skill directory. | false |
link_enabled | Whether memory extraction writes and resolves memory links. | false |
session_auto_commit | Server-wide automatic session commit controls. This belongs under memory, not under server; see Session Auto Commit Configuration. | See section below |
Memory links (memory.link_enabled)
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.
{
"memory": {
"session_auto_commit": {
"enabled": false,
"idle_backend": "local",
"check_interval_seconds": 600.0,
"scan_rate_limit_files_per_second": 2.0
}
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
enabled | bool | Master 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 happens | false |
idle_backend | string | Idle-timeout discovery backend: local scans session metadata; redis uses distributed expiry scheduling | local |
check_interval_seconds | float | Minimum interval between two idle scan rounds in seconds. Must be greater than 0 | 600.0 |
scan_rate_limit_files_per_second | float | Maximum 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 large | 2.0 |
Notes:
memory.session_auto_commitis 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 withPOST /api/v1/sessions, or partially update it throughPATCH /api/v1/sessions/{session_id}/config. Omittingauto_commit_policyfrom a PATCH preserves it; sendingnulldisables automatic commits. UseGET /api/v1/sessions/{session_id}to inspect the effective policy. - When
enabled=false, sessions created without an explicit orserver.user_config_defaults.auto_commit_policypolicy keep auto commit disabled and returnauto_commit_policy: null;SessionAutoCommitScheduleris not started. - When
enabled=true:- Sessions without an explicit or deployment-default policy get the built-in policy below.
- With
idle_backend="local",SessionAutoCommitSchedulerstarts a scan round immediately, then waits at leastcheck_interval_secondsbefore the next round. Session.meta.jsonfiles are read serially at no more thanscan_rate_limit_files_per_second. - With
idle_backend="redis", the server enables expiry reporting, publishing, and callback consumption together using the top-levelexpiryconfiguration.
- 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.
{
"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"
}
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
service_name | string | Stable document service name shared by all instances using the same expiry data | "" |
publisher_separate_process | bool | Let openviking-server supervise publisher in a dedicated child process; when false, publisher runs in each API process | true |
shard_count | int | Number of logical expiry shards; all instances sharing a namespace must use the same value, in the range 1..512 | 128 |
session_consumer_concurrency | int | Concurrent Session expiry callbacks per API process; must be greater than 0 | 8 |
redis.mode | string | Redis deployment mode: standalone or cluster | standalone |
redis.endpoints | string array | Redis endpoints; a cluster may use one proxy endpoint | [] |
redis.username | string | Redis ACL username | "" |
redis.password_env | string | Environment variable containing the Redis password; startup fails if it is not set | "" |
redis.password | string | Legacy 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.
| Field | Type | Default | Min | Max | Description |
|---|---|---|---|---|---|
pending_token_threshold | int | 150000 | 0 | 1000000 | When uncommitted pending tokens exceed this value (strictly greater-than), an auto commit is triggered after a message write. |
message_count_threshold | int | 100 | 0 | 1000 | When the uncommitted live message count exceeds this value (strictly greater-than), an auto commit is triggered after a message write. |
idle_timeout_seconds | int | 86400 | 0 (off) or 300 | 604800 | New 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_count | int | 0 | 0 | 500 | Number 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_seconds | int | 0 | 0 | 604800 | Minimum 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:
{
"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"
}
}| Field | Description | Default |
|---|---|---|
url | Server address | (required) |
api_key | Normally a user/admin key for data access. A root key here only works for admin APIs in API Key mode | null (no auth) |
root_api_key | Root 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 fallback | null |
account | Optional trusted-mode account identity header value | null |
user | Optional trusted-mode user identity header value | null |
profile | Whether 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_dirs | Default directory ignore list for add-resource (CSV) | null |
upload.include | Default include patterns for add-resource (CSV) | null |
upload.exclude | Default exclude patterns for add-resource (CSV) | null |
upload.mode | Python 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:
ov --account acme --user alice ls viking://For add-resource, upload filter flags are merged additively with ovcli.conf defaults:
# 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:
{
"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
}
}
}| Field | Type | Description | Default |
|---|---|---|---|
host | str | Bind address | 127.0.0.1 |
port | int | Bind port | 1933 |
auth_mode | str / null | Built-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_key | str | Root 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 identity | null |
account_store.provider | str | Complete 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.params | object | Provider-specific startup parameters. Framework-owned filesystem and File-provider API-key hashing dependencies cannot be overridden here. | {} |
profile_enabled | bool | Whether 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_origins | list | Allowed CORS origins | ["*"] |
public_base_url | str | Public-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_seconds | int | TTL 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_mode | str | Server-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_bytes | int | Maximum size accepted in shared mode, in bytes. Requests above this size are rejected before object-store write. | 536870912 (512 MiB) |
temp_upload.ttl_seconds | int | Retention 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_uri | str | Deployment 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_uri | str | Deployment 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_policy | object | Deployment default memory extraction policy used when neither the Session nor the User has an explicit policy. | null |
user_config_defaults.auto_commit_policy | object | Deployment default auto-commit policy for newly created sessions without an explicit policy. | null |
agent_evolution.enabled | bool | Startup 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.
{
"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.
{
"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_uriis used as the defaultadd_resourceparent directory, equivalent toparent=<uri>, create_parent=true. It must be a writable resource directory URI for the request user. Supported forms areviking://resourcesorviking://resources/...,viking://~/resourcesorviking://~/resources/...,viking://user/{user_id}/resourcesorviking://user/{user_id}/resources/..., andviking://user/{user_id}/peers/{peer_id}/resourcesorviking://user/{user_id}/peers/{peer_id}/resources/.... Theviking://~/...home alias resolves per request user.skill_uriis used as the defaultadd_skilltarget root. In v1, onlyviking://~/skillsandviking://agent/skillsare accepted; explicitviking://user/{user_id}/skillsis not accepted.- Legacy spellings:
viking://user/resourcesandviking://user/skillswritten in earlier configs are auto-normalized toviking://~/resourcesandviking://~/skillswhen the config is loaded, and the server logs an info message. Prefer theviking://~/...form in new configs — outsideadd_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.
| Field | Default | Meaning |
|---|---|---|
source | "file" | file stores settings through AGFS; memory keeps them only in this process; a registered source name selects a custom implementation. |
module | null | Optional 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.
| Field | Default | Meaning |
|---|---|---|
enabled | true | Enable externalization. |
threshold_chars | 20000 | Externalize an individual output above this character count. |
preview_chars | 2000 | Preview character target for one output. |
assistant_turn_inline_budget_chars | 100000 | Inline budget across tool outputs in one assistant turn. |
assistant_turn_preview_budget_chars | 10000 | Budget used to allocate previews across the turn. |
min_preview_chars | 1000 | Lower 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:
{
"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:
{"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:
{"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:
{
"storage": {
"agfs": {
"pathlock": {
"provider": "filesystem",
"lock_expire_secs": 30.0
}
}
}
}Legacy compatibility form (not recommended for new deployments):
{
"storage": {
"transaction": {
"lock_expire": 30.0
}
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
lock_timeout | float | Deprecated and ignored. Runtime wait timeout is fixed at 0.0. | 0.0 |
lock_expire | float | Deprecated. Use storage.agfs.pathlock.lock_expire_secs. Automatically mapped when the new field is unset. | 30.0 |
redo_recovery_enabled | bool | Deprecated 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:
/local/{account_id}/_system/tasks/{user_id}/{task_id}.jsonencryption 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.
{
"encryption": {
"enabled": true,
"provider": "local|vault|volcengine_kms"
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
enabled | bool | Whether encryption is enabled | false |
provider | str | Key provider: "local", "vault", or "volcengine_kms" | - |
api_key_hashing.enabled | bool | File-provider only: whether to apply Argon2id one-way hashing to API key values (independent of file-level enabled); see Encryption Guide | false |
Local (File)
Suitable for development environments and single-node deployments:
{
"encryption": {
"enabled": true,
"provider": "local",
"local": {
"key_file": "~/.openviking/master.key"
}
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
local.key_file | str | Root key file path | ~/.openviking/master.key |
Vault (HashiCorp Vault)
Suitable for production and multi-cloud deployments:
{
"encryption": {
"enabled": true,
"provider": "vault",
"vault": {
"address": "https://vault.example.com:8200",
"token": "vault-token-xxx",
"mount_point": "transit",
"key_name": "openviking-root"
}
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
vault.address | str | Vault service address | - |
vault.token | str | Vault access token | - |
vault.mount_point | str | Transit engine mount point | "transit" |
vault.key_name | str | Root key name | "openviking-root" |
Volcengine KMS
Suitable for Volcengine cloud deployments:
{
"encryption": {
"enabled": true,
"provider": "volcengine_kms",
"volcengine_kms": {
"key_id": "kms-key-id-xxx",
"region": "cn-beijing",
"access_key": "AKLTxxxxxxxx",
"secret_key": "Tmpxxxxxxxx"
}
}
}| Parameter | Type | Description | Default |
|---|---|---|---|
volcengine_kms.key_id | str | KMS key ID | - |
volcengine_kms.region | str | Region | "cn-beijing" |
volcengine_kms.access_key | str | Volcengine Access Key | - |
volcengine_kms.secret_key | str | Volcengine 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.
{
"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_weightcontrols 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 keyCheck your API key is correct and has the required permissions.
Vector Dimension Mismatch
Error: Vector dimension mismatchEnsure 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_retriesmoderately - Try a smaller model
- For bulk ingestion, consider lowering
vlm.max_concurrent
Rate Limiting
Error: Rate limit exceededCheck 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_concurrentfirst - Keep a small
max_retriesvalue for occasional429s; set it to0if you prefer fail-fast behavior
Related Documentation
- Volcengine Purchase Guide - API key setup
- API Overview - Client initialization
- Server Deployment - Server configuration
- Context Layers - L0/L1/L2