AI & search providers
tripl ships with two optional feature groups that reach out to an external, OpenAI-compatible provider. Both are OFF by default and must be explicitly enabled — tripl runs fully without either:
- Semantic search embeddings — upgrades the smart/global search
(
GET /projects/{slug}/search) from keyword/substring matching to embedding-based semantic ranking. - AI assistance — powers in-app helpers such as event-description suggestions on the event form.
A single shared variable, OPENAI_API_KEY, acts as the credential fallback for
both groups, so a minimal setup only needs one key.
Both groups send tracking-plan text to a third-party provider. They stay off until an operator turns them on, so a default deployment never transmits your catalog anywhere. See the privacy trade-off below.
Semantic search embeddings
When enabled, tripl indexes your tracking-plan text (event names, descriptions,
field and meta values, and related entities) as vector embeddings and uses them
to rank smart-search results by meaning rather than literal substring overlap.
The /search response then reports semantic_used: true.
When disabled — the default — /search still works. It transparently falls
back to keyword/substring matching and returns semantic_used: false. No text
leaves the instance. The one exception is the demo project: it ships with
precomputed embedding vectors for its own content, so demo searches can report
semantic_used: true without any provider configured — still with no text
leaving the instance, since those vectors are computed by maintainers ahead of
time and bundled with the release.
The semantic_used above is the flag on the envelope, and it is the one to
read when diagnosing configuration: it says the semantic leg ran. Each hit in
items carries its own semantic_used, which is narrower — that hit came from
the vector leg alone — so a correctly configured instance routinely answers
semantic_used: true on the envelope with every row reading false. Never
diagnose an instance from a row.
| Variable | Default | Purpose |
|---|---|---|
SEARCH_EMBEDDINGS_ENABLED | false | Master switch for semantic search. When false, /search uses keyword/substring fallback only. |
SEARCH_EMBEDDING_BASE_URL | https://api.openai.com/v1 | Base URL of the OpenAI-compatible embeddings endpoint; /embeddings is appended. Env-only — no instance-settings override, see the re-indexing warning below — but shown read-only, with its source badge, under Settings → Instance → AI. |
SEARCH_EMBEDDING_PROVIDER | openai | Embedding provider. |
SEARCH_EMBEDDING_MODEL | text-embedding-3-small | Embedding model used to index and query tracking-plan text. |
SEARCH_EMBEDDING_DIMENSIONS | 1536 | Vector dimensionality. Must match the chosen model. |
SEARCH_EMBEDDING_API_KEY | falls back to OPENAI_API_KEY | Credential for the embedding provider. |
Leave SEARCH_EMBEDDING_API_KEY unset and just provide OPENAI_API_KEY if the
same credential serves both embeddings and AI assistance.
AI assistance
AI assistance drives generative helpers in the app — for example, suggesting an event description on the event form. It targets an OpenAI-compatible chat endpoint.
| Variable | Default | Purpose |
|---|---|---|
AI_ENABLED | false | Master switch for AI assistance. When false, AI-backed helpers return a "disabled" response. |
AI_BASE_URL | https://api.openai.com/v1 | Base URL of the OpenAI-compatible API. Point this at a proxy or self-hosted endpoint to use a different backend. |
AI_MODEL | gpt-4o-mini | Chat/completion model. |
AI_API_KEY | falls back to OPENAI_API_KEY | Credential for the AI provider. |
AI_TIMEOUT_SECONDS | 30 | Per-request timeout. |
AI_MAX_OUTPUT_TOKENS | 700 | Cap on generated output length. |
Because AI_BASE_URL speaks the OpenAI-compatible protocol, any compatible
gateway, proxy, or self-hosted model server works in place of OpenAI — set
AI_BASE_URL to its address and AI_API_KEY to whatever credential it expects.
The privacy trade-off
Enabling either group means tripl transmits indexed tracking-plan text to the configured provider:
- Embeddings send event names, descriptions, field/meta values and related entity text to the embedding provider so they can be turned into vectors for semantic ranking.
- AI assistance sends the relevant event context to the chat model to generate suggestions.
This is precisely why both default to OFF: you opt in knowingly.
With embeddings disabled, search is not broken — it degrades gracefully to
keyword/substring matching (semantic_used: false, except in the demo project,
which uses bundled precomputed vectors). Many deployments run this way
indefinitely. Only enable embeddings if you accept sending tracking-plan text
to the provider in exchange for semantic relevance.
If your provider is a self-hosted or in-VPC OpenAI-compatible endpoint, you can
keep both features on while keeping all text inside your own infrastructure —
point SEARCH_EMBEDDING_BASE_URL and AI_BASE_URL at that endpoint. Both take a
base, not a full path: tripl appends /embeddings and /chat/completions
respectively, the way an OpenAI-compatible server lays them out.
SEARCH_EMBEDDING_BASE_URL is env-only on purpose, and so is
SEARCH_EMBEDDING_DIMENSIONS. Vectors already stored came from whatever endpoint
produced them, and a similarity score between two different embedding spaces is
meaningless — so changing either after documents have been indexed quietly
degrades every result involving an older vector, with nothing logged. Set it
before you enable embeddings; if you change it afterwards, re-index the project
so the whole corpus is embedded by one provider.
How to set these
Compose / environment
Like all backend settings, these are read from the process environment or a
.env file. In Docker Compose, add them to the shared x-app-environment
anchor at the top of compose.yaml, not to a single service: app serves
the API while celery-worker runs the embedding task, and both read these
settings. Every service that runs the app image inherits the anchor.
The anchor is an explicit allowlist, not a mount of your .env: a variable it
does not name reaches nothing inside the container, the application default wins
instead, and nothing is logged about it. Check the anchor before concluding that
a value you put in .env took effect.
x-app-environment: &app-environment
# …existing entries…
# Semantic search embeddings (opt-in)
SEARCH_EMBEDDINGS_ENABLED: "true"
SEARCH_EMBEDDING_BASE_URL: https://api.openai.com/v1
SEARCH_EMBEDDING_PROVIDER: openai
SEARCH_EMBEDDING_MODEL: text-embedding-3-small
SEARCH_EMBEDDING_DIMENSIONS: "1536"
# AI assistance (opt-in)
AI_ENABLED: "true"
AI_BASE_URL: https://api.openai.com/v1
AI_MODEL: gpt-4o-mini
AI_TIMEOUT_SECONDS: "30"
AI_MAX_OUTPUT_TOKENS: "700"
# Shared credential fallback for both groups
OPENAI_API_KEY: ${OPENAI_API_KEY}
Provide the key through your secret mechanism rather than committing it. See the full Configuration reference for how settings are loaded.
Settings → Instance → AI
Operators can also review and adjust this configuration from the running
instance under Settings → Instance → AI (for example, at
https://tripl.example.com/settings/instance/ai). The same page governs both
the AI-assistance settings and the embeddings toggle, with the environment
variables above acting as defaults that the stored overrides can replace at
runtime — so you can confirm the active model and flip features on or off
without redeploying.
Embeddings base URL and Embedding dimensions appear there too, read-only:
neither takes an override, because the vectors already in the index were written
against one endpoint at one width and similarity across two embedding spaces is
meaningless. They are shown because their source badge answers a question
nothing else in the running system did. Env on the base URL means something
delivered SEARCH_EMBEDDING_BASE_URL to this container; Default means the
value equals the built-in https://api.openai.com/v1, which is either because
nothing delivered it or because what was delivered says the same thing.
That is how you verify from a browser that a self-hosted
SEARCH_EMBEDDING_BASE_URL actually reached the process, which is the failure
the x-app-environment allowlist warning above describes and which is otherwise
silent. A base URL you pointed at a local endpoint and that reads Default in
the browser did not arrive, and every indexed event name, description and field
value is going to OpenAI instead.
Related
- Configuration reference — every environment variable.
- Admin guide — instance settings and operator tasks.
- Searching events — how smart search and the structured listing differ in practice.