This document covers the embedding behavior that exists in the current codebase.
| Method | Path | Format |
|---|---|---|
| POST | /v1/embeddings |
OpenAI-compatible |
| POST | /api/embed |
Current Ollama embedding format |
| POST | /api/embeddings |
Legacy Ollama embedding format |
Today that means:
- the server caches embedding engines by resolved model and device
NPU_PROXY_EMBEDDING_MODELandNPU_PROXY_EMBEDDING_DEVICEprovide the default model and device- request
modelfields and device overrides can select another cached engine when a runtime-ready export exists - if the requested model is missing or unusable in the runtime cache path, the request fails by default instead of returning success-shaped fallback embeddings
If you want a different real embedding model, either change the process defaults or provide a request model/device pair that already has a matching exported model on disk.
- The documented default embedding device remains
CPU. - On the validation workstation,
sentence-transformers/all-MiniLM-L6-v2validated onNPUvia a static-shape profile (batch_size=1,max_length=256,pad_to_max_length=true). - On the validation workstation,
BAAI/bge-small-en-v1.5onNPUdid not validate because the Intel NPU plugin failed withcheck_sdpa_nodes(model). - If the configured model is missing or unusable, the service now fails the request unless explicit fallback mode is enabled.
| Variable | Default |
|---|---|
NPU_PROXY_EMBEDDING_MODEL |
BAAI/bge-small-en-v1.5 |
NPU_PROXY_EMBEDDING_DEVICE |
CPU |
NPU_PROXY_LOAD_TIMEOUT |
300 |
NPU_PROXY_EMBED_TIMEOUT |
60 |
NPU_PROXY_EMBEDDING_CACHE_SIZE |
1024 |
The runtime looks for exported models under:
~/.cache/npu-proxy/models/embeddings/<canonical-id-or-sanitized-repo>
Examples:
~/.cache/npu-proxy/models/embeddings/bge-small
~/.cache/npu-proxy/models/embeddings/all-minilm-l6-v2
Known registry-backed models use canonical runtime IDs such as bge-small and all-minilm-l6-v2. Sanitized repo-name paths such as BAAI_bge-small-en-v1.5 are legacy compatibility paths.
See ../guides/MODEL_DOWNLOAD.md for the current setup flow.
Examples of built-in embedding registry IDs include:
all-minilm-l6-v2bge-smallbge-basebge-largee5-smalle5-largenomic-embed-textqwen3-embedding-0.6b-int4-ovqwen3-embedding-8b-int4-ov
The pull/download helpers know additional short aliases such as bge-base, bge-large, e5-small, and nomic-embed-text.
POST /v1/embeddings accepts a single string or a list of strings in input. It validates input before loading the engine. MAX_EMBEDDING_BATCH_SIZE is 128.
| Condition | HTTP status | error.type |
error.code |
Message |
|---|---|---|---|---|
| Empty input list | 400 | invalid_request_error |
empty_input |
Embedding input must contain at least one item |
| Batch larger than 128 | 413 | request_too_large |
embedding_batch_too_large |
Embedding input batch is too large |
| Empty or whitespace-only text | 400 | invalid_request_error |
empty_input |
Embedding input text must not be empty |
| Single text over 8192 characters | 413 | request_too_large |
embedding_input_too_large |
Embedding input text is too large |
| Request total over 65536 characters | 413 | request_too_large |
embedding_request_too_large |
Embedding request is too large |
OpenAI errors use this envelope:
{
"error": {
"message": "Embedding input text must not be empty",
"type": "invalid_request_error",
"param": null,
"code": "empty_input"
}
}The response includes X-Request-ID; a provided X-Request-ID request header is echoed after sanitization, otherwise the server generates one.
For every successful embedding request, the response contains exactly one finite, correctly-dimensioned vector per input, in input order. The service rejects batch results that do not align 1:1 with the input list; it does not return zero/blank placeholder vectors as successful batch items.
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8080/v1",
api_key="not-needed",
)
response = client.embeddings.create(
model="all-minilm",
input=["hello", "world"],
)
for item in response.data:
print(item.index, len(item.embedding))curl -X POST http://127.0.0.1:8080/api/embed \
-H "Content-Type: application/json" \
-d '{"model":"all-minilm","input":["text1","text2"]}'Response shape:
{
"model": "all-minilm",
"embeddings": [[0.1, -0.2], [0.3, -0.4]],
"total_duration": 14143917,
"load_duration": 0,
"prompt_eval_count": 8
}curl -X POST http://127.0.0.1:8080/api/embeddings \
-H "Content-Type: application/json" \
-d '{"model":"all-minilm","prompt":"single text"}'Response shape:
{
"embedding": [0.1, -0.2]
}POST /v1/embeddings follows the OpenAI request/response shape implemented in npu_proxy/api/embeddings.py.
Request:
modelis requiredinputis required and may be a string or list of stringsencoding_formatdefaults tofloat; other values return400with codeunsupported_encoding_format
Successful response:
object: "list"data[]entries containobject: "embedding",embedding: [...], andindexmodelechoes the request fieldusage.prompt_tokensandusage.total_tokensare populated
The model field in the response echoes the request field; use runtime health or engine info when you need to confirm which real engine/device handled a request.
The default runtime behavior is no implicit fallback. If no runtime-ready embedding model is found, the request fails with an embedding error instead of returning success-shaped placeholder vectors.
If you explicitly set NPU_PROXY_EMBEDDING_FALLBACK_MODE=hash, the service can return deterministic hash-based embeddings for operator testing or wiring checks. That mode is opt-in and should not be treated as semantic retrieval quality.
On the validation workstation, all-minilm-l6-v2 worked on NPU through a static-shape profile, while bge-small still failed inside the Intel NPU plugin with check_sdpa_nodes(model).