Embedders
Choose and configure the embedding provider grepai uses to turn code into vectors (Ollama, LM Studio, OpenAI, Synthetic, OpenRouter, Requesty).
An embedder turns each code chunk (and each search query) into a vector. This page helps you pick a provider and shows the configuration for each one.
Available Embedders
| Provider | Runs | Default model | Default dimensions | API key |
|---|---|---|---|---|
ollama (default) | Local | nomic-embed-text | 768 | No |
lmstudio | Local | text-embedding-nomic-embed-text-v1.5 | 768 | No |
openai | Cloud | text-embedding-3-small | 1536 | OPENAI_API_KEY |
synthetic | Cloud | hf:nomic-ai/nomic-embed-text-v1.5 | 768 | SYNTHETIC_API_KEY |
openrouter | Cloud | openai/text-embedding-3-small | 1536 | OPENROUTER_API_KEY |
requesty | Cloud | openai/text-embedding-3-small | 1536 | REQUESTY_API_KEY |
How to choose:
- Keep code on your machine: use
ollamaorlmstudio. - Large repository on a cloud API: consider
openai. It is the only provider that sends batches in parallel and retries on rate limits. - One key for many models: use
openrouterorrequesty.
Pick the provider during grepai init, or pass it directly:
grepai init --provider openai
All providers share the same embedder keys in .grepai/config.yaml:
| Key | Used by | Description |
|---|---|---|
provider | all | ollama, lmstudio, openai, synthetic, openrouter or requesty |
model | all | Model name exactly as the provider expects it |
endpoint | all | Base URL of the API (each provider has a default) |
api_key | cloud providers | API key. If empty, grepai reads the provider’s environment variable, but only when endpoint is the provider’s official endpoint. A custom endpoint requires api_key |
dimensions | all | Vector size. See Changing Embedding Models |
request_timeout_seconds | all except requesty | HTTP timeout per request. Default: 60 (90 for synthetic) |
parallelism | openai | Concurrent requests. Default: 4 |
max_retries | openai | Retries on HTTP 429 and 5xx. Default: 5 |
max_batch_size | openai | Maximum inputs per request. Default: 2000 |
max_batch_tokens | openai | Maximum estimated tokens per request. Default: 280000 |
Note: grepai does not expand environment variables in
config.yaml. A value likeapi_key: ${OPENAI_API_KEY}is sent as that literal text. To use an environment variable, leaveapi_keyout and export the variable.
Custom endpoints need
api_key: grepai sends a key read from the environment (OPENAI_API_KEY,OPENROUTER_API_KEY,SYNTHETIC_API_KEY,REQUESTY_API_KEY) only to the provider’s official host over HTTPS:api.openai.com,openrouter.ai,api.synthetic.neworrouter.requesty.ai. A.grepai/config.yamlcan come with a cloned repository, so itsendpointis not trusted with your environment keys. For any other endpoint (Azure, LiteLLM, vLLM, a proxy…), setapi_keyin the config. If you leave it empty while an environment key is set, grepai stops with an error that names the endpoint instead of sending the key. With theopenaiprovider, a local server that needs no key (vLLM, TEI…) still needs a placeholder, for exampleapi_key: unused: grepai refuses to start without a key.
Ollama (Local)
Setup
-
Install Ollama:
# macOS brew install ollama # Linux curl -fsSL https://ollama.com/install.sh | sh -
Start the server:
ollama serve -
Pull an embedding model:
ollama pull nomic-embed-text
Configuration
grepai init asks for the Ollama endpoint (default http://localhost:11434), so you can point it at a remote server, a Docker container or a custom port. It uses nomic-embed-text as the model. Edit model and dimensions to use another one.
embedder:
provider: ollama
model: nomic-embed-text
endpoint: http://localhost:11434
dimensions: 768
grepai sends up to 64 chunks per request to Ollama’s /api/embed endpoint. If the server does not have that endpoint (older Ollama versions), grepai switches to the legacy /api/embeddings endpoint and sends one chunk per request.
If a chunk is longer than the model’s context window, grepai splits that chunk into smaller pieces and tries again.
Available Models
Any Ollama embedding model works. Common choices:
| Model | Dimensions | Languages |
|---|---|---|
nomic-embed-text | 768 | English |
nomic-embed-text-v2-moe | 768 | Multilingual |
bge-m3 | 1024 | Multilingual |
mxbai-embed-large | 1024 | English |
all-minilm | 384 | English |
Set dimensions to match the model you choose.
Multilingual Support
nomic-embed-text is trained mostly on English. For comments and identifiers in other languages (Korean, Chinese, French, and so on), use a multilingual model such as nomic-embed-text-v2-moe. It has the same dimensions (768) as the default, so you only change model:
ollama pull nomic-embed-text-v2-moe
embedder:
provider: ollama
model: nomic-embed-text-v2-moe
dimensions: 768
You still need to re-index after switching.
Troubleshooting
# Check that Ollama is running
curl http://localhost:11434/api/tags
# Test an embedding
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "Hello world",
"truncate": false
}'
If large batches time out on slow hardware, raise request_timeout_seconds:
embedder:
provider: ollama
request_timeout_seconds: 300
LM Studio (Local)
LM Studio runs embedding models locally behind an OpenAI-compatible API.
Setup
- Download and install LM Studio.
- Load an embedding model (for example, a Nomic embed model).
- Start the local server (default:
http://127.0.0.1:1234).
Configuration
grepai init asks for the LM Studio endpoint (default http://127.0.0.1:1234).
embedder:
provider: lmstudio
model: text-embedding-nomic-embed-text-v1.5
endpoint: http://127.0.0.1:1234
dimensions: 768
grepai calls /v1/embeddings and sends all chunks of a file in one request. Chunks that exceed the model’s context window are split and retried.
Available Models
Any embedding model loaded in LM Studio works. Use the model identifier that LM Studio reports, and set dimensions to match it:
curl http://127.0.0.1:1234/v1/models
Troubleshooting
# Check that the LM Studio server is running
curl http://127.0.0.1:1234/v1/models
# Test an embedding
curl http://127.0.0.1:1234/v1/embeddings \
-H "Content-Type: application/json" \
-d '{
"model": "text-embedding-nomic-embed-text-v1.5",
"input": ["Hello world"]
}'
OpenAI (Cloud)
Setup
-
Get an API key from the OpenAI Platform.
-
Export it:
export OPENAI_API_KEY=sk-...
Configuration
embedder:
provider: openai
model: text-embedding-3-small
endpoint: https://api.openai.com/v1
parallelism: 4
grepai init leaves dimensions unset, so the model returns its native size. If you set dimensions, grepai sends it to the API as the dimensions parameter, which asks the model for shorter vectors.
Azure OpenAI / Microsoft Foundry
Any OpenAI-compatible API works with provider: openai and a custom endpoint. grepai sends POST {endpoint}/embeddings with an Authorization: Bearer header. With a custom endpoint, the key must be in api_key: OPENAI_API_KEY is only sent to api.openai.com.
embedder:
provider: openai
model: text-embedding-3-small # on Azure, use your deployment name
endpoint: https://YOUR-RESOURCE.openai.azure.com/openai/v1
api_key: your-azure-key
Leave dimensions unset for models that do not accept the parameter, such as text-embedding-ada-002.
Available Models
| Model | Dimensions |
|---|---|
text-embedding-3-small (default) | 1536 |
text-embedding-3-large | 3072 |
grepai init --provider openai --model text-embedding-3-large
For current prices, see OpenAI pricing.
Parallelism & Rate Limiting
During indexing, grepai groups chunks from many files into batches (up to 2,000 inputs and about 280,000 estimated tokens each) and sends up to parallelism batches at the same time.
embedder:
provider: openai
parallelism: 4 # concurrent requests (default: 4)
max_retries: 5 # retries per batch on 429 or 5xx (default: 5)
OpenAI-compatible endpoints (self-hosted vLLM, TEI, proxies…) often accept much smaller requests and time out or fail on the defaults. Lower the batch limits for them; they also apply to the watcher, which embeds one file per request:
embedder:
provider: openai
endpoint: https://embeddings.example.com/v1
max_batch_size: 100 # inputs per request (default: 2000)
max_batch_tokens: 50000 # estimated tokens per request (default: 280000)
Tokens are estimated at about 4 characters per token. A single chunk larger than max_batch_tokens is still sent alone.
How errors are handled:
- 429 and 5xx: the batch is retried with exponential backoff (1s, doubling, capped at 32s, plus random jitter). If the response has a
Retry-Afterheader, grepai waits that long instead (at most 60s). - Repeated 429s: after 3 consecutive 429 responses, grepai halves the number of workers (minimum 1). After 10 consecutive successes, it adds one worker back, up to
parallelism. - Batch too large: if OpenAI rejects a batch for exceeding the maximum request size, grepai splits it in half and retries.
- Other 4xx errors: not retried.
Recommended Parallelism by Tier
Your rate limits depend on your OpenAI account tier. Start with the default (4). If indexing runs without 429 errors, raise it; if you see many retries, lower it.
To see your limits, inspect the rate limit headers of a test request:
curl -s -D - https://api.openai.com/v1/embeddings \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "text-embedding-3-small", "input": ["test"]}' \
-o /dev/null 2>&1 | grep -i x-ratelimit
x-ratelimit-limit-requests is your requests-per-minute cap and x-ratelimit-limit-tokens is your tokens-per-minute cap.
Cost Estimation
You pay for the tokens you embed. As a rough guide, grepai estimates about 4 characters per token, so a 1 MB codebase is about 250,000 tokens for the first full index. After that, only changed files are embedded again, and chunks whose content is already in the index reuse their stored vector.
Synthetic (Cloud)
Synthetic serves open embedding models through an OpenAI-compatible API.
export SYNTHETIC_API_KEY=your-key
embedder:
provider: synthetic
model: hf:nomic-ai/nomic-embed-text-v1.5
endpoint: https://api.synthetic.new/openai/v1
dimensions: 768
- If
api_keyandSYNTHETIC_API_KEYare both empty, grepai falls back toOPENAI_API_KEY. - grepai always sends
dimensionsin the request (768 by default). - The default request timeout is 90 seconds.
OpenRouter (Cloud)
OpenRouter is a gateway to embedding models from several vendors.
export OPENROUTER_API_KEY=your-key
embedder:
provider: openrouter
model: openai/text-embedding-3-small
endpoint: https://openrouter.ai/api/v1
grepai init offers three models. grepai infers the dimensions for them when dimensions is unset:
| Model | Dimensions | --model shortcut |
|---|---|---|
openai/text-embedding-3-small (default) | 1536 | text-embedding-3-small |
openai/text-embedding-3-large | 3072 | text-embedding-3-large |
qwen/qwen3-embedding-8b | 4096 | qwen3-embedding-8b |
grepai init --provider openrouter --model qwen3-embedding-8b
For any other model, set dimensions yourself. If api_key and OPENROUTER_API_KEY are both empty, grepai falls back to OPENAI_API_KEY.
Requesty (Cloud)
Requesty is another multi-vendor gateway.
export REQUESTY_API_KEY=your-key
embedder:
provider: requesty
model: openai/text-embedding-3-small
endpoint: https://router.requesty.ai/v1
- grepai infers 3072 dimensions for
openai/text-embedding-3-largeand 1536 for other models. Setdimensionsfor models with another size. - If
api_keyandREQUESTY_API_KEYare both empty, grepai falls back toOPENAI_API_KEY. - The request timeout is fixed at 60 seconds (
request_timeout_secondsis ignored).
Changing Embedding Models
You can use any embedding model your provider offers. Two keys matter:
| Key | Description |
|---|---|
model | The exact model name the provider expects |
dimensions | The size of the vectors the model produces |
When dimensions is unset, grepai assumes 768 for ollama, lmstudio and synthetic, and 1536 for openai, openrouter and requesty (3072 or 4096 for the models listed above). PostgreSQL and Qdrant use this value to size the vector column or collection, so it must match the model.
Finding Model Dimensions
Ollama:
curl -s http://localhost:11434/api/embed \
-d '{"model": "MODEL_NAME", "input": "test"}' | jq '.embeddings[0] | length'
LM Studio:
curl -s http://127.0.0.1:1234/v1/embeddings \
-H "Content-Type: application/json" \
-d '{"model": "MODEL_NAME", "input": ["test"]}' | jq '.data[0].embedding | length'
Re-indexing After Model Change
Vectors from different models are not compatible. grepai records which embedder built each index (provider, model and dimensions, in .grepai/index-meta.json; the endpoint is not part of it), so changing one of them is detected:
grepai search(CLI and MCP) refuses to run on the old index and tells you to rebuild it.grepai indexandgrepai watchremove the project’s old vectors and re-embed every file.
So after changing provider, model or dimensions, run:
grepai watch --stop # if a background watcher is running
grepai index # or: grepai watch
An index built by a version older than this check has no metadata file: it is adopted as is the first time it is indexed again.
Requesting a New Embedder
grepai works issue first and does not accept pull requests. To ask for another provider, open a feature request with the provider, the model you want to use and your use case.
Many services expose an OpenAI-compatible API: they usually work today with the openai provider and a custom endpoint.