Skip to main content

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:

  1. Semantic search embeddings — upgrades the smart/global search (GET /projects/{slug}/search) from keyword/substring matching to embedding-based semantic ranking.
  2. 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.

Why opt-in

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.

VariableDefaultPurpose
SEARCH_EMBEDDINGS_ENABLEDfalseMaster switch for semantic search. When false, /search uses keyword/substring fallback only.
SEARCH_EMBEDDING_BASE_URLhttps://api.openai.com/v1Base 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_PROVIDERopenaiEmbedding provider.
SEARCH_EMBEDDING_MODELtext-embedding-3-smallEmbedding model used to index and query tracking-plan text.
SEARCH_EMBEDDING_DIMENSIONS1536Vector dimensionality. Must match the chosen model.
SEARCH_EMBEDDING_API_KEYfalls back to OPENAI_API_KEYCredential for the embedding provider.
tip

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.

VariableDefaultPurpose
AI_ENABLEDfalseMaster switch for AI assistance. When false, AI-backed helpers return a "disabled" response.
AI_BASE_URLhttps://api.openai.com/v1Base URL of the OpenAI-compatible API. Point this at a proxy or self-hosted endpoint to use a different backend.
AI_MODELgpt-4o-miniChat/completion model.
AI_API_KEYfalls back to OPENAI_API_KEYCredential for the AI provider.
AI_TIMEOUT_SECONDS30Per-request timeout.
AI_MAX_OUTPUT_TOKENS700Cap on generated output length.
Bring your own endpoint

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.

warning

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.

Repointing it later means re-indexing

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.