# Configure embedding

> OpenAI-compatible embedding providers, required fields (baseUrl, apiKey, model, dimensions), sendDimensions for BGE-M3-style backends, timeouts, and graceful keyword-only degradation when provider is none.

- Repository: TencentCloud/TencentDB-Agent-Memory
- GitHub: https://github.com/TencentCloud/TencentDB-Agent-Memory
- Human docs: https://grok-wiki.com/public/docs/tencentcloud-tencentdb-agent-memory-5a33bbf5540a
- Complete Markdown: https://grok-wiki.com/public/docs/tencentcloud-tencentdb-agent-memory-5a33bbf5540a/llms-full.txt

## Source Files

- `src/core/store/embedding.ts`
- `src/config.ts`
- `openclaw.plugin.json`
- `README.md`
- `SKILL.md`
- `src/core/store/factory.ts`

---

---
title: "Configure embedding"
description: "OpenAI-compatible embedding providers, required fields (baseUrl, apiKey, model, dimensions), sendDimensions for BGE-M3-style backends, timeouts, and graceful keyword-only degradation when provider is none."
---

`memory-tencentdb` drives dense vector search through the `embedding` config group. By default `provider` is `"none"`: the plugin stays online and degrades to keyword/FTS recall. Any other non-local provider value is treated as an OpenAI-compatible remote API, with an explicit `zeroentropy` branch and a `qclaw` proxy path.

## Default behavior

Zero-config leaves embedding off:

| Setting | Default | Effect |
| --- | --- | --- |
| `embedding.provider` | `"none"` | Vector search disabled |
| `embedding.enabled` | schema default `true`, but forced `false` when `provider` is `"none"` | No remote embed client is created |
| `embedding.dimensions` | `0` when provider is `"none"` | SQLite `vec0` tables are deferred until a real provider is configured |
| `recall.strategy` | `"hybrid"` | Falls back to `"keyword"` when no embedding service is available |

Incomplete remote config does **not** crash the plugin. `parseConfig` sets `embedding.configError`, forces `enabled: false`, and OpenClaw logs `[EMBEDDING CONFIG ERROR] ...` at startup. Search continues on the keyword path.

## Provider modes

| `provider` value | Mode | Required fields | Notes |
| --- | --- | --- | --- |
| `"none"` (default) | Off | none | Keyword/FTS only |
| `"openai"`, `"deepseek"`, `"azure"`, or any other string except specials below | OpenAI-compatible remote | `baseUrl`, `apiKey`, `model`, `dimensions` | `POST {baseUrl}/embeddings` |
| `"zeroentropy"` | ZeroEntropy native | same four fields | `POST {baseUrl}/models/embed` with `input_type: "query"` |
| `"qclaw"` | Proxied OpenAI-compatible | four fields + `proxyUrl` | Request goes to `proxyUrl`; original URL sent as `Remote-URL` header |
| `"local"` | Not user-configurable | — | Treated as disabled; `configError` asks for a remote provider |

With `storeBackend: "tcvdb"`, the store factory installs `NoopEmbeddingService` and relies on TCVDB server-side embedding (`tcvdb.embeddingModel`) instead of the remote client configured under `embedding.*`.

## Config fields

Configured under OpenClaw at `plugins.entries["memory-tencentdb"].config.embedding`, or under Gateway at `memory.embedding` in `tdai-gateway.json` / YAML (same shape; Gateway reuses `parseConfig`).

<ParamField body="enabled" type="boolean" default="true">
Schema default is `true`, but `provider: "none"` forces effective disable. Incomplete remote config also forces `false`.
</ParamField>

<ParamField body="provider" type="string" default="none">
Embedding backend label. `"none"` disables vectors; other values (except `"local"`) select remote clients. `"zeroentropy"` and `"qclaw"` use specialized request paths.
</ParamField>

<ParamField body="baseUrl" type="string" required>
OpenAI-compatible API base (no trailing slash required; service strips trailing `/`). Example: `https://api.openai.com/v1`. Remote calls hit `{baseUrl}/embeddings` unless provider is `zeroentropy` or `qclaw`.
</ParamField>

<ParamField body="apiKey" type="string" required>
Bearer token for the embeddings API (`Authorization: Bearer …`).
</ParamField>

<ParamField body="model" type="string" required>
Model id sent in the request body (for example `text-embedding-3-small`, `bge-m3`, `zembed-1`).
</ParamField>

<ParamField body="dimensions" type="number" required>
Output vector size. Must match the model (and any Matryoshka truncation the backend supports). Stored and used for SQLite `vec0` layout.
</ParamField>

<ParamField body="sendDimensions" type="boolean" default="true">
When `true`, the request body includes `"dimensions": <n>` (OpenAI Matryoshka-style). Set `false` for fixed-dimension OSS backends such as BGE-M3 that reject Matryoshka with HTTP 400.
</ParamField>

<ParamField body="proxyUrl" type="string">
Required only when `provider` is `"qclaw"`. Fetch target becomes `proxyUrl`; header `Remote-URL` is set to `{baseUrl}/embeddings`.
</ParamField>

<ParamField body="maxInputChars" type="number" default="5000">
Per-text character cap before truncation (warning logged). Applies to remote OpenAI-compatible and ZeroEntropy paths.
</ParamField>

<ParamField body="timeoutMs" type="number" default="10000">
Default per-call HTTP timeout (ms). Transient failures retry up to 3 times with exponential backoff (500ms × attempt). HTTP 4xx (except 429) is not retried.
</ParamField>

<ParamField body="recallTimeoutMs" type="number">
Optional override for user-facing recall embeds. Falls back to `timeoutMs`. Prefer a shorter value (for example `3000`).
</ParamField>

<ParamField body="captureTimeoutMs" type="number">
Optional override for background capture/L1-dedup embeds. Falls back to `timeoutMs`. Can be longer (for example `15000`).
</ParamField>

<ParamField body="conflictRecallTopK" type="number" default="5">
Top-K candidates pulled during L1 conflict/dedup detection when vectors are available.
</ParamField>

## Enable a remote provider

### OpenClaw (`~/.openclaw/openclaw.json`)

```json
{
  "plugins": {
    "entries": {
      "memory-tencentdb": {
        "enabled": true,
        "config": {
          "embedding": {
            "enabled": true,
            "provider": "openai",
            "baseUrl": "https://api.openai.com/v1",
            "apiKey": "sk-...",
            "model": "text-embedding-3-small",
            "dimensions": 1536,
            "sendDimensions": true,
            "timeoutMs": 10000,
            "recallTimeoutMs": 3000,
            "captureTimeoutMs": 15000
          },
          "recall": {
            "strategy": "hybrid"
          }
        }
      }
    }
  }
}
```

Restart the OpenClaw gateway after edits so `parseConfig` and the store factory re-run.

### BGE-M3 and other fixed-dimension backends

OpenAI-compatible hosts that reject Matryoshka need `sendDimensions: false`. Keep `dimensions` equal to the model’s fixed size for local SQLite storage:

```json
{
  "embedding": {
    "enabled": true,
    "provider": "openai",
    "baseUrl": "http://your-host:port/v1",
    "apiKey": "<KEY>",
    "model": "bge-m3",
    "dimensions": 1024,
    "sendDimensions": false
  }
}
```

Without `sendDimensions: false`, backends often return HTTP 400 with a body containing `does not support matryoshka representation`.

### ZeroEntropy

```json
{
  "embedding": {
    "provider": "zeroentropy",
    "baseUrl": "https://api.zeroentropy.dev/v1",
    "apiKey": "<KEY>",
    "model": "zembed-1",
    "dimensions": 640,
    "sendDimensions": true
  }
}
```

`dimensions` for `zembed-1` must be in the server Matryoshka set (`2560`, `1280`, `640`, `320`, `160`, `80`, `40`) when sent. Set `sendDimensions: false` to omit the field.

### Gateway / Hermes (`memory-tencentdb-ctl`)

```bash
# Enable OpenAI-compatible embedding
memory-tencentdb-ctl config embedding \
  --provider openai \
  --api-key "sk-xxxx" \
  --base-url "https://api.openai.com/v1" \
  --model "text-embedding-3-small" \
  --dimensions 1536 \
  --restart

# qclaw requires --proxy-url
memory-tencentdb-ctl config embedding \
  --provider qclaw \
  --api-key "..." \
  --base-url "https://api.openai.com/v1" \
  --model "text-embedding-3-small" \
  --dimensions 1536 \
  --proxy-url "http://127.0.0.1:PORT" \
  --restart

# Disable → keyword/BM25-only
memory-tencentdb-ctl config embedding --provider none --restart
```

Writes go to `$TDAI_DATA_DIR/tdai-gateway.json` at `memory.embedding.{provider,baseUrl,apiKey,model,dimensions,enabled,proxyUrl?}`. Validation matches `parseConfig`: non-`none` providers require the four core fields; missing fields abort the write.

## Runtime path (SQLite)

```text
parseConfig(embedding)
        │
        ├─ provider=none / incomplete / local  → enabled=false, dimensions=0
        │                                         no EmbeddingService
        │                                         recall → keyword/FTS only
        │
        └─ remote + valid four-tuple
                  │
                  ▼
         createStoreBundle (sqlite)
                  │
                  ├─ createEmbeddingService(...)
                  │     openai-compatible → OpenAIEmbeddingService
                  │     zeroentropy       → ZeroEntropyEmbeddingService
                  └─ VectorStore(vectors.db, dimensions)
```

TCVDB path: store uses server embedding; client-side `embedding.*` is not used for dense vectors.

### Request body (OpenAI-compatible)

```json
{
  "input": ["text..."],
  "model": "text-embedding-3-small",
  "dimensions": 1536
}
```

`dimensions` is omitted when `sendDimensions` is `false`. Responses are L2-normalized client-side. Batch size caps at 256 texts per request.

## Keyword-only degradation

| Condition | Behavior |
| --- | --- |
| `provider: "none"` | Embedding disabled; SQLite skips `vec0` creation |
| Missing `apiKey` / `baseUrl` / `model` / `dimensions` | `configError` set; plugin continues without vectors |
| `provider: "local"` | Disabled at config entry; error asks for a remote provider |
| `provider: "qclaw"` without `proxyUrl` | Same graceful disable + `configError` |
| `recall.strategy` is `embedding` or `hybrid` but no service | Auto-recall warns and uses `keyword` |
| Embedding API failure at search time | Search logs a warning and returns empty for that path |

FTS5 keyword search remains available on the SQLite store when FTS is healthy. Hybrid RRF on SQLite needs both keyword and embedding; without embedding, only the keyword branch runs.

## Timeouts and retries

| Control | Default | Scope |
| --- | --- | --- |
| `embedding.timeoutMs` | `10000` | Per embedding HTTP call |
| `embedding.recallTimeoutMs` | unset → `timeoutMs` | Auto-recall / user-facing embeds |
| `embedding.captureTimeoutMs` | unset → `timeoutMs` | Background L1 dedup / capture embeds |
| `recall.timeoutMs` | `5000` | Whole auto-recall budget (separate from embed HTTP) |
| Retries | 3 retries after first attempt | Network errors, 5xx, 429, timeouts; not other 4xx |

## Verification

<Steps>
<Step title="Confirm config shape">
Ensure all four remote fields are set and `provider` is not `"none"`. For BGE-M3-style hosts, set `sendDimensions: false`.
</Step>
<Step title="Restart the host">
OpenClaw: restart the gateway. Hermes/standalone: `memory-tencentdb-ctl restart` (or `config embedding ... --restart`).
</Step>
<Step title="Check startup logs">
Absence of `[EMBEDDING CONFIG ERROR]` means parse accepted the remote block. Presence of that tag means vectors are disabled and the missing-field message is in the log.
</Step>
<Step title="Exercise recall">
With `recall.strategy: "hybrid"` (default), semantic queries should return L1 hits after conversations have been captured and extracted. If only keyword matches work, the embed client is still missing or failing.
</Step>
</Steps>

## Troubleshooting

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Startup `[EMBEDDING CONFIG ERROR]` | Incomplete remote quartet or qclaw without `proxyUrl` | Fill `baseUrl`, `apiKey`, `model`, `dimensions` (+ `proxyUrl` for qclaw) |
| HTTP 400 + matryoshka message | Backend rejects `dimensions` body field | `"sendDimensions": false` |
| No vector results, keyword works | `provider: "none"`, disabled service, or API errors | Enable remote provider; inspect embed HTTP errors in logs |
| Dimension mismatch after switching models | SQLite `vec0` built for previous dims | Align `dimensions` with the new model; may require re-index / fresh `vectors.db` for a clean layout |
| Slow or empty auto-recall | Embed latency vs `recall.timeoutMs` | Lower `recallTimeoutMs`, raise overall `recall.timeoutMs`, or fix backend latency |
| Expect client embeds with TCVDB | Wrong mental model | TCVDB uses `NoopEmbeddingService` + `tcvdb.embeddingModel` |

## Related pages

<CardGroup>
<Card title="Configure OpenClaw" href="/configure-openclaw">
Plugin enablement, daily tuning groups, and gateway restart verification.
</Card>
<Card title="Storage backends" href="/storage-backends">
SQLite vs TCVDB, hybrid RRF, and where embedding services attach.
</Card>
<Card title="Use Tencent VectorDB" href="/use-tcvdb">
Server-side embedding model and TCVDB connection fields.
</Card>
<Card title="Plugin configuration reference" href="/plugin-config-reference">
Full `memory-tencentdb` schema, defaults, and `parseConfig` rules.
</Card>
<Card title="Gateway lifecycle" href="/gateway-ops">
`memory-tencentdb-ctl config embedding` and data-dir layout.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Embedding 400 / matryoshka and broader failure checklist.
</Card>
</CardGroup>
