Skip to main content

Overview

defineProvider creates a named provider factory from a base provider. Use it when an API is compatible with an existing provider package but needs its own endpoint, key, icon, model list, pricing, or request behavior. Provider files live in agents/providers/ and must default-export defineProvider(...).
agents/providers/darkbloom.ts
import { defineProvider, providerEnv } from '@standardagents/spec';
import { openai } from '@standardagents/openai';

export default defineProvider({
  name: 'darkbloom',
  label: 'Darkbloom',
  baseProvider: openai,
  config: {
    apiKey: providerEnv('DARKBLOOM_API_KEY', { valueType: 'secret' }),
    baseUrl: providerEnv('DARKBLOOM_BASE_URL', {
      valueType: 'url',
      default: 'https://api.darkbloom.dev/v1',
    }),
  },
});
Models import the local provider as a default import:
agents/models/darkbloom_chat.ts
import { defineModel } from '@standardagents/spec';
import darkbloom from '../providers/darkbloom';

export default defineModel({
  name: 'darkbloom_chat',
  provider: darkbloom,
  model: 'darkbloom-chat',
});

Type Definition

function defineProvider<N extends string, Base extends ProviderFactoryWithOptions>(
  definition: ProviderDefinition<N, Base>
): ProviderFactoryWithOptions;

interface ProviderDefinition<N extends string = string> {
  name: N;
  label?: string;
  baseProvider: ProviderFactoryWithOptions;
  config?: Record<string, ProviderConfigValueSource>;
  overrides?: ProviderMethodOverrides;
}

Configuration

Provider config describes values passed to the base provider factory. It is for connection-level values such as keys, endpoints, regions, account IDs, and timeouts. Per-model request options still belong in providerOptions on defineModel.
config: {
  apiKey: providerEnv('DARKBLOOM_API_KEY', {
    valueType: 'secret',
    description: 'Darkbloom API key',
  }),
  baseUrl: providerEnv('DARKBLOOM_BASE_URL', {
    valueType: 'url',
    default: 'https://api.darkbloom.dev/v1',
  }),
}

Value Sources

HelperDescription
providerEnv(name, options)Reads a value from an environment variable, with optional default, required, valueType, and description metadata
providerValue(value)Uses a literal constant value
providerConst(value)Alias for providerValue(value)
If a sub-provider does not override a required base provider slot, the base provider’s defaultEnv is used. For example, an OpenAI-based provider that only overrides baseUrl still requires OPENAI_API_KEY.

Overrides

The overrides object can replace or compose with provider behavior. Each method receives a context object and next(), where next() calls the base provider’s implementation.
export default defineProvider({
  name: 'darkbloom',
  label: 'Darkbloom',
  baseProvider: openai,
  config: {
    apiKey: providerEnv('DARKBLOOM_API_KEY', { valueType: 'secret' }),
    baseUrl: providerValue('https://api.darkbloom.dev/v1'),
  },
  overrides: {
    getIcon() {
      return '/icons/darkbloom.svg';
    },
    async getModels(_context, next) {
      const models = await next();
      return [
        { id: 'darkbloom-chat', name: 'Darkbloom Chat' },
        ...models,
      ];
    },
  },
});
Call next() when the override should extend the base provider. Omit next() when it should fully replace the base behavior. Overrideable methods include generate, stream, supportsModel, getModels, getModelsPage, getModelCapabilities, getTools, getIcon, inspectRequest, and getResponseMetadata.
supportsModel and getIcon are synchronous because the provider interface expects synchronous results for those methods. Use getModels or getModelsPage for asynchronous discovery.

Model Discovery Metadata

getModels and getModelsPage return ProviderModelInfo entries. Providers may include optional pricing metadata when their catalog exposes it:
{
  id: 'vendor/model-id',
  name: 'Model Display Name',
  inputPrice: 1.4,
  outputPrice: 4.4,
}
inputPrice, outputPrice, and cachedPrice use the same units as defineModel: USD per 1M tokens. When present, AgentBuilder can copy those values into generated model definitions so request logs can calculate cost from token usage when the upstream response does not report a dollar cost.