Extend the gateway

Custom Provider Guide

Where the provider system lives, and exactly how to plug in any new AI provider — zero-code or with a tiny file. প্রতিটি ধাপের সাথে বাংলায় বিস্তারিত ব্যাখ্যা দেওয়া আছে। New: you can also manage providers visually with full CRUD in the Admin Panel.

1. How providers work

A provider is any upstream AI service the gateway can chat with. Every provider lives in src/providers/ and exposes the same tiny contract, so the rest of the gateway (validation, errors, rate limits, response shape) works identically no matter who answers.

request flow
your app ──▶ POST /api/v1/chat { "provider": "poolside", ... }
                    │
                    ▼
            AI Gateway (src/index.js)
                    │  validate → rate-limit → pick provider (src/providers/registry.js)
                    ▼
   ┌─────────────────┼──────────────────┐
   ▼                 ▼                  ▼
openrouter.js    poolside.js        custom.js
OpenRouter API   Poolside API       ANY OpenAI-compatible URL (env-driven)
বাংলা ব্যাখ্যা

Provider মানে হলো AI সেবা দেওয়া কোম্পানি (OpenRouter, Poolside, OpenAI ইত্যাদি)। এদের সবাইকে গেটওয়েতে যুক্ত করার জায়গা হলো src/providers/ ফোল্ডার। রেজিস্ট্রি ফাইল (src/providers/registry.js) ঠিক করে কোন কোন provider চালু আছে। আপনি চাইলে কোড না লিখেই (Method 1) অথবা ছোট একটি ফাইল বানিয়ে (Method 2) নতুন provider যোগ করতে পারবেন।

2. Three ways to add a provider

1 — Zero code

Reuse the built-in custom provider: just set 3 env vars. Best for quick tests and one extra vendor.

Jump to Method 1 →

2 — Provider file

Add a ~15-line file like poolside.js. First-class id, models list, own env keys. Best for permanence.

Jump to Method 2 →

3 — Template

Copy src/providers/_template.js for a fully annotated skeleton when the provider needs custom logic.

Jump to Method 3 →
Which one? Provider speaks OpenAI protocol and you just want it working → Method 1. You want it permanently, with its own id and model list → Method 2. Custom request/response shape → Method 3.

3. Method 1 — Zero code (the custom provider)

The gateway ships with a universal adapter, src/providers/custom.js. It reads its target from environment variables, so pointing it at a new vendor requires no code and no redeploy of code — only env vars:

Env varRequiredExample
CUSTOM_BASE_URLyeshttps://api.openai.com/v1
CUSTOM_API_KEYusuallysk-… (omit for Ollama/LM Studio)
CUSTOM_MODELyesgpt-4o-mini

Then call it with "provider": "custom":

bash
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "custom",
    "message": "Hello from my own provider!"
  }'

Known-good base URLs

VendorCUSTOM_BASE_URLCUSTOM_MODEL example
OpenAIhttps://api.openai.com/v1gpt-4o-mini
DeepSeekhttps://api.deepseek.comdeepseek-chat
Togetherhttps://api.together.xyz/v1meta-llama/Llama-3.3-70B-Instruct-Turbo
Ollama (local)http://localhost:11434/v1llama3.2 (no key)
LM Studio (local)http://localhost:1234/v1loaded model (no key)
বাংলা ব্যাখ্যা

Method 1 কখন ব্যবহার করবেন: যখন provider-টি OpenAI-এর মতো একই রকম API ব্যবহার করে (বেশিরভাগই করে) এবং আপনি দ্রুত টেস্ট করতে চান। শুধু ৩টি environment variable সেট করুন — CUSTOM_BASE_URL (provider-এর ঠিকানা), CUSTOM_API_KEY (তাদের দেওয়া key), CUSTOM_MODEL (মডেলের নাম)। এরপর রিকোয়েস্টে "provider": "custom" লিখলেই সেই provider উত্তর দেবে। খেয়াল রাখবেন — এই পদ্ধতিতে একসাথে একটি custom provider চলে; একাধিক দরকার হলে Method 2 ব্যবহার করুন।

4. Method 2 — Add a dedicated provider file (recommended)

This is exactly how Poolside AI was added to this project. A provider that speaks the OpenAI protocol needs one tiny file using the factory, then a 2-line registration.

Step 1 — create src/providers/poolside.js

src/providers/poolside.js — real file from this project
import { createOpenAICompatible } from './openai-compatible.js';

export const poolsideProvider = createOpenAICompatible({
  id: 'poolside',                                   // unique, lowercase
  label: 'Poolside AI',                             // shown in /providers
  description: 'Poolside Laguna code-intelligence models…',
  defaultBaseUrl: 'https://inference.poolside.ai/v1',
  envKey: 'POOLSIDE_API_KEY',                       // env var holding the key
  modelEnvKey: 'POOLSIDE_MODEL',                    // optional model override
  defaultModel: 'poolside/laguna-s-2.1',
  models: ['poolside/laguna-s-2.1'],
  supportsMaxTokens: true,
});
বাংলা — ধাপ ১

src/providers/ ফোল্ডারে নতুন ফাইল বানান, যেমন poolside.js। ফাইলে উপরের মতো createOpenAICompatible ফ্যাক্টরি কল করুন এবং আপনার provider-এর তথ্য বসান: id (ছোট হাতের অক্ষরে ইউনিক নাম — রিকোয়েস্টে এটাই ব্যবহার হবে), defaultBaseUrl (provider-এর API ঠিকানা, শেষে /v1 থাকে সাধারণত), envKey (যে environment variable-এ API key থাকবে), আর defaultModel (মডেলের নাম)। এটাই মূলত পুরো কাজ!

Step 2 — register it in src/providers/registry.js

src/providers/registry.js — only 2 changed lines
import { openrouterProvider } from './openrouter.js';
import { poolsideProvider } from './poolside.js';    // ← 1. import
import { customProvider } from './custom.js';

const BUILT_IN_PROVIDERS = [
  openrouterProvider,
  poolsideProvider,                                  // ← 2. add to the list
  customProvider,
];
বাংলা — ধাপ ২

registry.js ফাইলে গিয়ে আপনার ফাইলটি import করুন (লাইন ১) এবং BUILT_IN_PROVIDERS তালিকায় যোগ করুন (লাইন ২)। এই তালিকার ক্রম অনুযায়ই GET /api/v1/providers-এ provider দেখা যাবে।

Step 3 — set the API key as an env var

Locally: add POOLSIDE_API_KEY=your-key to .env (Node) or .dev.vars (wrangler). On Cloudflare: see section 7. Never hard-code the key in the file itself.

Step 4 — restart / redeploy

Local: stop (Ctrl+C) and re-run npm start. Cloudflare: push to GitHub and Pages redeploys automatically.

Step 5 — call it

bash
curl https://YOUR-DOMAIN/api/v1/chat \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{ "provider": "poolside",
        "model": "poolside/laguna-s-2.1",
        "messages": [{ "role": "user", "content": "What are channels in Go?" }] }'
বাংলা — ধাপ ৩–৫

ধাপ ৩: আপনার provider-এর key-টি environment variable-এ রাখুন (নামটি হবে ফাইলে envKey-তে যা লিখেছেন, যেমন POOLSIDE_API_KEY)। Cloudflare-এ দিলে অবশ্যই রিডিপ্লয় লাগবে। ধাপ ৪: লোকালে সার্ভার রিস্টার্ট করুন, Cloudflare-এ push করলেই হবে। ধাপ ৫: এবার রিকোয়েস্টে "provider": "poolside" (আপনার দেওয়া id) পাঠিয়ে চেক করুন। GET /api/v1/providers-এ নতুন provider "configured" দেখালে বুঝবেন সব ঠিক আছে।

5. Method 3 — the template (full control)

If the provider does not speak the OpenAI protocol (different request shape, different auth, custom response format), copy src/providers/_template.js. It is a fully annotated skeleton of the provider contract — you implement isConfigured(env) and chat(payload, ctx) yourself, throw typed ApiErrors, and register the result exactly like Step 2 above.

the essential contract (from _template.js)
export const myProvider = {
  id: 'myprovider',
  label: 'My Provider',
  description: '…',
  defaultModel: 'my-model-v1',
  models: ['my-model-v1'],
  requiredEnvKeys: ['MYPROVIDER_API_KEY'],

  isConfigured(env) {
    return Boolean(env.MYPROVIDER_API_KEY);
  },

  async chat(payload, { env, config }) {
    // build YOUR request shape, fetch with utils/http.js postJson(),
    // map failures with upstreamError() from core/errors.js,
    // then return { reply, model, usage } — the gateway does the rest.
  },
};
বাংলা ব্যাখ্যা

যদি provider-এর API OpenAI-এর মতো না হয় (যেমন Google Gemini-এর পুরনো ফরম্যাট), তাহলে _template.js ফাইলটি কপি করে নিজের মতো লিখুন। দুটি ফাংশন বানাতে হবে — isConfigured() (key আছে কি না) আর chat() (আসল রিকোয়েস্ট)। এরর দিতে হবে core/errors.js-এর ApiError দিয়ে, যাতে সব এরর একই ফরম্যাটে যায়। এরপর registry-তে যোগ করুন — ব্যস।

6. Provider contract reference

Every option accepted by createOpenAICompatible() — the factory used by OpenRouter, Poolside and custom:

FieldRequiredPurpose
idyesUnique lowercase id — the value clients pass as "provider".
labelyesHuman name shown in GET /api/v1/providers.
descriptionyesOne-liner for the provider list.
defaultBaseUrlyes*e.g. https://inference.poolside.ai/v1 — or use baseUrlEnvKey to read it from env (how custom.js works).
envKeyyesName of the env var holding the API key.
apiKeyOptionalnotrue for key-less runtimes (Ollama, LM Studio).
modelEnvKeyyesEnv var that overrides defaultModel.
defaultModelyesFallback model id.
modelsnoInformational list (shown in the Playground's model picker).
extraHeadersFnno(env) => headers for vendor-specific headers (OpenRouter uses it for attribution).
supportsMaxTokensnoSet false if the vendor rejects max_tokens.

The factory handles everything else: auth header, timeout, error mapping (401→UPSTREAM_AUTH_ERROR, 429→UPSTREAM_RATE_LIMIT…), usage normalization and the success envelope.

7. Setting provider keys on Cloudflare Pages

  1. Open the Cloudflare dashboard → Workers & Pages → click your Pages project.
  2. Go to Settings → Variables and Secrets.
  3. Fastest: click Import .env and upload a prepared env file (all variables at once) — or click + Add, choose type Secret, and enter the name (e.g. POOLSIDE_API_KEY) and the key value.
  4. Add it to both environments: Production and Preview (tabs at the top of the section).
  5. Redeploy — Deployments → latest deployment → Retry deployment, or just push any commit. New variables only take effect on a fresh deployment.
Security: never commit keys to the repo. If a key ever leaks, rotate it at the vendor and update the env var. Keep .env / .dev.vars git-ignored (already configured).
বাংলা — ধাপে ধাপে

① Cloudflare ড্যাশবোর্ডে ঢুকুন → Workers & Pages → নিজের প্রজেক্টে ক্লিক করুন। ② Settings ট্যাব → Variables and Secrets অংশে যান। ③ Import .env বাটন দিয়ে প্রস্তুত করা env ফাইল আপলোড করুন (একবারে সব), অথবা + Add চাপুন → টাইপ Secret → নাম দিন (যেমন POOLSIDE_API_KEY) এবং key পেস্ট করুন। ④ Production এবং Preview — দুই জায়গাতেই যোগ করতে ভুলবেন না। ⑤ নতুন variable কাজ করার জন্য একটি নতুন deployment লাগবেই — Deployments থেকে Retry deployment করুন বা ছোট একটি commit push করুন।

8. Verify it works

Two quick checks after deploying:

1 — is the provider listed & configured?
curl https://YOUR-DOMAIN/api/v1/providers
# → find your provider in the list; "configured": true is required
2 — does it answer?
curl -X POST https://YOUR-DOMAIN/api/v1/chat \
  -H "Content-Type: application/json" \
  -d '{ "provider": "poolside", "failover": false, "message": "ping" }'

Prefer clicking over curl? The API Status page has a per-provider Test button that runs this check from the browser.

9. FAQ & troubleshooting

Can I add more than one custom provider?

Yes — Method 1's custom slot holds one vendor, but Method 2 lets you add unlimited providers (one file each). Many teams keep custom as their staging playground and promote serious vendors to dedicated files.

How do I know if a vendor is OpenAI-compatible?

If this curl returns JSON with choices[0].message.content, it is compatible:

compatibility probe
curl https://VENDOR-URL/v1/chat/completions \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{ "model": "their-model", "messages": [{ "role": "user", "content": "hi" }] }'

Troubleshooting table

SymptomCodeFix
New provider id gets UNKNOWN_PROVIDER400Not registered — check registry.js and that you redeployed.
"not configured on the server"PROVIDER_NOT_CONFIGUREDEnv var missing or named differently than envKey; remember the redeploy rule.
"rejected the API key"UPSTREAM_AUTH_ERRORWrong/revoked key in the env var (no quotes, no spaces).
"could not find the requested model"UPSTREAM_ERRORModel id typo in defaultModel or the request.
"Cannot reach …"NETWORK_ERRORWrong defaultBaseUrl (protocol, path, trailing /v1).
Times out on big promptsUPSTREAM_TIMEOUTRaise PROVIDER_TIMEOUT_MS.
Provider ignores temperature—Some vendors only accept specific ranges; set temperature per request or extend the factory call.
বাংলা — শেষ কথা

সংক্ষেপে: নতুন provider যোগ করার সবচেয়ে সহজ পথ হলো Method 2 — src/providers/-এ ছোট একটি ফাইল + registry.js-এ ২ লাইন + Cloudflare-এ key। কোনো সমস্যা হলে প্রথমে GET /api/v1/providers দেখুন (provider তালিকায় আছে? configured?), তারপর উপরের টেবিলের এরর কোডের সাথে মেলান। Status পেজের Test বাটন দিয়েও যাচাই করতে পারেন।