N
Nyxis
getting started

Custom providers

Register any AI provider with @nyxis/core — Cohere, Groq, Together, AWS Bedrock, internal proxies.

@nyxis/core ships with five providers built in — Anthropic, OpenAI, Google, Mistral, and Ollama — registered automatically when you import the package. For anything else (Cohere, Groq, Together, AWS Bedrock, Replicate, an internal proxy, a self-hosted gateway), use the custom-provider API to plug in at runtime without forking Nyxis.

The three pieces

import { registerProvider, createModel, listProviders } from '@nyxis/core';
FunctionPurpose
registerProvider(id, registration)Add (or replace) a provider adapter.
createModel({ provider: id, model })Build a LanguageModel for that provider.
listProviders()Inspect every registered provider id (built-in + custom).

A registration is just an object with a loadModel function:

interface ProviderRegistration {
  loadModel: (opts: CreateModelOptions) => unknown | Promise<unknown>;
  label?: string; // optional, for UI pickers
  docsUrl?: string;
}

loadModel receives the full CreateModelOptions (provider, model, apiKey, baseURL, settings) and returns a Vercel AI SDK LanguageModel. Sync or async, your choice.

Register Cohere

import { registerProvider } from '@nyxis/core';
import { createCohere } from '@ai-sdk/cohere';

registerProvider('cohere', {
  label: 'Cohere',
  docsUrl: 'https://docs.cohere.com',
  loadModel: ({ apiKey, model }) => createCohere({ apiKey })(model),
});

Import this file once at the top of your server entry point (e.g. an instrumentation.ts in Next.js, or the first line of your Hono / Express app). After that, anywhere in your code:

import { createModel } from '@nyxis/core';

const model = await createModel({
  provider: 'cohere',
  model: 'command-r-plus',
  apiKey: process.env.COHERE_API_KEY,
});

The Nyxis backend recipes (api-chat, api-tools, api-rag) honour custom providers automatically — they just call createModel under the hood.

Register Groq

import { registerProvider } from '@nyxis/core';
import { createGroq } from '@ai-sdk/groq';

registerProvider('groq', {
  label: 'Groq',
  loadModel: ({ apiKey, model, baseURL }) => createGroq({ apiKey, baseURL })(model),
});

Register Together

import { registerProvider } from '@nyxis/core';
import { createTogetherAI } from '@ai-sdk/togetherai';

registerProvider('together', {
  label: 'Together',
  loadModel: ({ apiKey, model }) => createTogetherAI({ apiKey })(model),
});

Register an internal proxy

You don’t have to depend on a real @ai-sdk/<x> package — anything that returns a LanguageModel works. The OpenAI SDK with a custom baseURL is a common pattern for OpenAI-compatible proxies:

import { registerProvider } from '@nyxis/core';
import { createOpenAI } from '@ai-sdk/openai';

registerProvider('internal-proxy', {
  label: 'Internal AI gateway',
  loadModel: ({ apiKey, model }) =>
    createOpenAI({
      apiKey,
      baseURL: 'https://ai.internal.acme.corp/v1',
    })(model),
});

Replacing a built-in

Re-registering an existing id replaces the loader without warning. This is how you’d wrap Anthropic with a custom logger, retry policy, or header injection:

import { registerProvider } from '@nyxis/core';
import { createAnthropic } from '@ai-sdk/anthropic';

registerProvider('anthropic', {
  label: 'Anthropic (logged)',
  loadModel: ({ apiKey, model }) => {
    const provider = createAnthropic({
      apiKey,
      headers: { 'x-internal-trace': crypto.randomUUID() },
    });
    return provider(model);
  },
});

Tests

For test isolation between specs, use resetProviderRegistry:

import { afterEach } from 'vitest';
import { resetProviderRegistry } from '@nyxis/core';

afterEach(() => {
  resetProviderRegistry();
});

This wipes everything and re-registers the five built-ins, leaving each test file in a known state.

Type safety

AIProviderId is a string union with autocomplete: the five built-ins appear in IDE suggestions, and (string & {}) widens the type so any slug compiles without complaint:

const providerId: AIProviderId = 'anthropic'; // ✓ autocomplete
const providerId: AIProviderId = 'cohere'; // ✓ no complaint
const providerId: AIProviderId = 'gibberish'; // ✓ technically valid

If you want stricter typing for your app, narrow it yourself:

import type { AIProviderId } from '@nyxis/core';

type AppProviderId = Extract<AIProviderId, 'anthropic' | 'openai'> | 'cohere';