defineProvider
Define a provider that extends another provider with custom config and method overrides
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(...).
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:
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, Base extends ProviderFactoryWithOptions = ProviderFactoryWithOptions> {
name: N;
label?: string;
baseProvider: Base;
config?: Partial<Record<string, ProviderConfigEntry>>;
overrides?: ProviderMethodOverrides;
createFetch?: CreateFetch;
}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',
}),
}config also accepts literal strings, numbers, booleans, and nested maps.
A nested defaultHeaders map can mix constants and providerEnv entries.
An unset optional entry is omitted.
createFetch({ providerName, config, state }) returns a fetch-compatible transport.
The runtime constructs the provider for each request when this field is present.
state is available for thread execution.
Value Sources
| Helper | Description |
|---|---|
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().
next() calls the base provider's implementation when it exists.
Optional catalog methods have fallback results when the base method is absent.
Calling next() for an absent inspectRequest method throws an error.
import { defineProvider, providerEnv, providerValue } 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: 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.
Model Discovery Metadata
getModels and getModelsPage return ProviderModelInfo entries. Providers
may include optional pricing metadata when their catalog exposes it. This example uses a Novita catalog entry:
{
id: 'zai-org/glm-5.3',
name: 'GLM 5.3',
// Prices change. Check https://api.novita.ai/openai/v1/models
inputPrice: 1.4,
outputPrice: 4.4,
}inputPrice, outputPrice, and cachedPrice use the same units as
defineModel: USD per 1M tokens. When present, Standard Agents Builder 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.
The admin log inspector validates stored requests before calling inspectRequest.
Malformed stored requests return HTTP 500.
The inspector rehydrates canonical content parts and legacy attachment paths.