Integration guides ยท 2026-08-29
Running the Open SEO SAM agent through an LLMTR gateway
How a self-hosted Open SEO install adds an OpenAI-compatible gateway beside its default provider: the LLM_API_KEY precedence rule, the environment files involved, and what model choice does to cost on a content generation workload.
Beside the default provider, not in place of it
Open SEO is an open source SEO analysis platform. Its agent features, notably SAM, the in-app SEO agent, and the onboarding chat, are optional: with no key configured a setup gate appears and the rest of the platform keeps working.
The change sits on the feat/openai-compatible-llm-provider branch of the knowhycodata/open-seo repository and removes nothing. What it does is open a second path, so any endpoint speaking the chat completions API can now feed the same agents. That branch was not taken into Open SEO's own release.
The distinction matters because of the self-hosting scenario. A team running it on its own server has already chosen to keep analysis data on its own side, and wants to decide by the same reasoning which door the model call leaves through. The second path makes exactly that decision available without breaking existing installs.
Which key selects which path
The selection resolves in a single function and the rule is short: if the gateway key is set it wins, otherwise the default provider's key is consulted, and if neither exists the result is empty and the interface shows a setup gate. An empty result is an expected state, not an error.
Giving the gateway key precedence is deliberate. A migrating install does not have to delete the old secret first; adding the new key and restarting is enough. Reverting is just as simple, since removing the new key hands the decision back.
The table below shows what comes out for each combination present in the environment. Both paths share the same default model, minimax/minimax-m3, an identifier that is live in the catalog and served on the chat completions endpoint.
| Present in the environment | Selected path | Resulting configuration |
|---|---|---|
| Nothing | None | Setup gate appears, rest of the platform works |
| Default provider key only | openrouter | Model minimax/minimax-m3 |
| Default key plus model override | openrouter | The model identifier you wrote is used |
| LLM_API_KEY only | openai-compatible | Base URL https://llmtr.com/v1, model minimax/minimax-m3 |
| LLM_API_KEY, LLM_BASE_URL, LLM_MODEL | openai-compatible | The endpoint and model you specified |
| Both keys | openai-compatible | LLM_API_KEY wins, no need to delete the old secret |
What goes into which file on a self-hosted install
All three environment examples carry the same trio: development, preview and production. The deployment script draws a sharper line, registering the key as a secret while the base URL and model identifier are plain variables. That split means changing the model identifier never involves secret management.
On installs deploying to Cloudflare the key is written from the command line. The ready-made command on the help page still names the default provider's variable; on the gateway path, write LLM_API_KEY in the same command instead. The help page itself was renamed on this branch and now presents both options side by side.
After writing the values, Open SEO has to be restarted, because the setup gate only sees the key after a restart.
Environment values, and writing the secret on a Cloudflare deployment
# .env
LLM_API_KEY=llmtr-your_key
LLM_BASE_URL=https://llmtr.com/v1
LLM_MODEL=minimax/minimax-m3
# On installs deploying to Cloudflare the key is written as a secret
npx wrangler secret put LLM_API_KEY
The provider resolves in one place
The branch removes the old provider file and puts a single module in its place, and both agents draw from it: the onboarding chat and SAM. No model construction logic remains in the agent files themselves; they take a resolved provider and build the model from it.
SAM's model hook runs synchronously on every turn, so there is a separate synchronous resolution path, while the onboarding chat reads its values asynchronously and uses the other one. Both apply the same rule, so the two agents cannot end up on different providers.
On the gateway path the client is constructed plainly: base URL, key, and the flag that asks for token counts in the stream. Nothing provider-specific is sent beyond that, because those options are not portable.
The gateway branch of provider resolution and the client it constructs
const DEFAULT_CHAT_AGENT_MODEL = "minimax/minimax-m3";
const DEFAULT_LLM_BASE_URL = "https://llmtr.com/v1";
if (gatewayKey) {
return {
kind: "openai-compatible",
apiKey: gatewayKey,
baseUrl: read("LLM_BASE_URL") ?? DEFAULT_LLM_BASE_URL,
modelId: read("LLM_MODEL") ?? DEFAULT_CHAT_AGENT_MODEL,
};
}
// The client stays plain: nothing provider-specific is attached.
createOpenAICompatible({
name: "llm-gateway",
baseURL: provider.baseUrl,
apiKey: provider.apiKey,
includeUsage: true,
}).chatModel(provider.modelId);
What the second path does not carry
The default path attaches several provider-specific settings to each request: usage accounting that returns the real cost of a response, an ordering that decides which upstream is preferred, a constraint limiting routing to endpoints that retain no data, and a reasoning channel. None of these belong to the OpenAI-compatible API.
On the gateway path the request therefore stays plain chat completions. Routing and data retention behaviour become properties of the endpoint itself, and reasoning output arrives on its own channel only if the endpoint publishes a separate reasoning field.
The most visible consequence is in accounting. There is no cost field in the OpenAI-compatible API, so installs on this path meter zero spend inside Open SEO. The production environment example states this outright. Spend tracking is not lost, it moves: to the gateway's own dashboard.
- Usage accounting, upstream ordering and the retention constraint do not travel to this path.
- Reasoning output arrives separately only when the endpoint publishes its own reasoning field.
- The spend counter inside Open SEO stays at zero on this path, and that is expected.
- You follow spend in the gateway dashboard instead; the measurement moves rather than disappears.
Model choice on a content generation workload
SAM runs its research through tools, so whichever model you pick has to support tool calling. If the supported parameter field in the model list carries no tool entries, that identifier is unsuitable for the job, and since the list is readable without a key you can check this before creating one.
Because the model identifier is an environment value, changing it needs no code change. Content generation and analysis runs work with long contexts and multi-step tool use, so consumption is driven by the number of turns and how many tokens the model produces per step. Billing is token based and draws down a credit balance.
The LLMTR platform margin applies only when credits are topped up and is 8 percent; no margin is added to model prices. In practice that means a saving made by switching models lands directly in consumption, with no extra layer in between. For the same reason, pointing the base URL at your own vLLM or Ollama server works through exactly the same mechanism: the setup is a question of endpoint address, not provider name.
- The model identifier changes through an environment value, with no code change.
- A model that cannot call tools is unsuitable for SAM.
- Both paths use the same provider/model form, so switching is a key swap.
- The base URL can also point at your own vLLM or Ollama server.
Frequently asked questions
Which key is used when both are set?
LLM_API_KEY is used. The rule exists so a migrating install does not have to delete the old secret first. To go back, removing LLM_API_KEY is enough.
Why does spend show as zero in the Open SEO panel?
Cost metering relies on the default provider's usage accounting, and that field is not part of the OpenAI-compatible API. Responses on this path carry no cost field, so the counter stays at zero. You follow spend in the gateway's own dashboard, and the production environment example notes this explicitly.
Can I connect my own vLLM or Ollama server?
Yes. The base URL can point at any address speaking the OpenAI chat completions API, and the branch's tests demonstrate exactly that with a local endpoint and a local model identifier. The one condition stays the same: the model you choose has to support tool calling.
Can I use Open SEO without enabling the agent features at all?
Yes. With no key configured the setup gate appears and the rest of the platform keeps working. The gate is only shown once the check confirms the key is genuinely missing, so the chat screen is not blocked while the check is in flight.