Providers
Provider objects sit between configuration and runtime assembly. They resolve config values from presets, arrays, or explicit objects, and optionally carry an explicit driver instance. Runtimes use providers to determine which driver to build and how to configure it.
LLMProvider¶
LLMProvider is a builder that wraps an LLMConfig and an optional explicit driver. It implements CanResolveLLMConfig and HasExplicitInferenceDriver, which the runtime uses during assembly.
Namespace: Cognesy\Polyglot\Inference\LLMProvider
Creating a Provider¶
use Cognesy\Polyglot\Inference\LLMProvider;
use Cognesy\Polyglot\Inference\Config\LLMConfig;
// From a named preset
$provider = LLMProvider::using('openai');
// With a custom base path for presets
$provider = LLMProvider::using('openai', basePath: '/path/to/presets');
// From an explicit config
$provider = LLMProvider::fromLLMConfig($config);
// From an array
$provider = LLMProvider::fromArray([
'driver' => 'anthropic',
'apiUrl' => 'https://api.anthropic.com/v1',
'apiKey' => getenv('ANTHROPIC_API_KEY'),
'endpoint' => '/messages',
'model' => 'claude-sonnet-4-20250514',
]);
// Default (OpenAI with gpt-4.1-nano)
$provider = LLMProvider::new();
// @doctest id="ac0b"
Customizing a Provider¶
All mutators return a new immutable instance:
// Override specific config values
$provider = LLMProvider::using('openai')
->withModel('gpt-4.1')
->withConfigOverrides(['maxTokens' => 4096]);
// Replace the entire config
$provider = $provider->withLLMConfig($newConfig);
// Inject an explicit driver (bypasses the driver factory)
$provider = $provider->withDriver($customDriver);
// @doctest id="d7b8"
When an explicit driver is set, the runtime uses it directly instead of building one from the config. This is useful for testing or for providers that need custom initialization.
How the Runtime Uses It¶
When you call InferenceRuntime::fromProvider($provider), the runtime:
- Calls
$provider->resolveConfig()to get theLLMConfig - Checks if
$provider->explicitInferenceDriver()returns a driver - If an explicit driver exists, uses it directly
- Otherwise, looks up the driver name from the config and creates one via the
InferenceDriverRegistry
EmbeddingsProvider¶
EmbeddingsProvider serves the same role for embeddings. It wraps an EmbeddingsConfig and an optional explicit driver.
Namespace: Cognesy\Polyglot\Embeddings\EmbeddingsProvider
Creating a Provider¶
use Cognesy\Polyglot\Embeddings\EmbeddingsProvider;
use Cognesy\Polyglot\Embeddings\Config\EmbeddingsConfig;
// Default (empty config)
$provider = EmbeddingsProvider::new();
// From an explicit config
$provider = EmbeddingsProvider::fromEmbeddingsConfig($config);
// From an array
$provider = EmbeddingsProvider::fromArray([
'driver' => 'openai',
'apiUrl' => 'https://api.openai.com/v1',
'apiKey' => getenv('OPENAI_API_KEY'),
'endpoint' => '/embeddings',
'model' => 'text-embedding-3-small',
]);
// @doctest id="fd1a"
Unlike LLMProvider, EmbeddingsProvider does not have a using(...) shortcut for presets. Use Embeddings::using(...) or construct the config explicitly.
Customizing a Provider¶
$provider = EmbeddingsProvider::fromArray([...])
->withConfigOverrides(['dimensions' => 256])
->withDriver($customDriver);
// @doctest id="b1f8"
Driver Factories¶
Inference Driver Registry¶
The InferenceDriverRegistry manages the mapping between driver names and their factory callables. Polyglot ships with a default set of bundled drivers via BundledInferenceDrivers::registry().
A registry entry is one of two things, and the difference is worth understanding before you write your own:
- A spec. Every bundled provider is an
InferenceDriverSpec— a row naming its body, request, response, usage and message collaborators. OpenAI-compatible providers use the defaults; native protocols and providers with custom URLs or headers select bespoke adapters in the same row.SpecifiedInferenceDriveris the single class behind all of them. - A custom driver class. Custom registrations may still use a class-string when they need behavior that is not composition. Bundled providers do not need a driver shell for that purpose because their request adapters own provider-specific URL and header behavior.
Supported inference drivers:
| Driver Name | Built from | Notes |
|---|---|---|
a21 |
spec: A21BodyFormat |
A21 Labs |
anthropic |
spec: AnthropicBodyFormat + AnthropicRequestAdapter |
Anthropic Messages API |
azure |
spec: OpenAIBodyFormat + AzureOpenAIRequestAdapter |
Azure OpenAI |
bedrock-openai |
spec: OpenAICompatibleBodyFormat + BedrockOpenAIRequestAdapter |
AWS Bedrock (OpenAI-compatible) |
cerebras |
spec: CerebrasBodyFormat |
Cerebras |
cohere |
spec: CohereV2BodyFormat + CohereV2RequestAdapter |
Cohere v2 |
deepseek |
spec: DeepseekBodyFormat |
DeepSeek; the one provider whose capabilities depend on the model |
fireworks |
spec: FireworksBodyFormat |
Fireworks AI |
gemini |
spec: GeminiBodyFormat + GeminiRequestAdapter |
Google Gemini native API |
gemini-oai |
spec: GeminiOAIBodyFormat + GeminiOAIRequestAdapter |
Gemini via OpenAI-compatible endpoint |
glm |
spec: GlmBodyFormat |
GLM |
groq |
spec: GroqBodyFormat |
Groq |
huggingface |
spec: HuggingFaceBodyFormat + HuggingFaceRequestAdapter |
Hugging Face |
inception |
spec: InceptionBodyFormat |
Inception |
meta |
spec: MetaBodyFormat |
Meta Llama API |
minimaxi |
spec: MinimaxiBodyFormat |
Minimaxi |
mistral |
spec: MistralBodyFormat |
Mistral |
openai |
spec: OpenAIBodyFormat |
OpenAI Chat Completions API |
openai-responses |
spec: OpenResponsesBodyFormat + OpenAIResponsesRequestAdapter |
OpenAI Responses API |
openresponses |
spec: OpenResponsesBodyFormat + OpenResponsesRequestAdapter |
Open Responses API |
openrouter |
spec: OpenRouterBodyFormat |
OpenRouter |
perplexity |
spec: PerplexityBodyFormat |
Perplexity |
qwen |
spec: QwenBodyFormat |
Alibaba Qwen |
sambanova |
spec: SambaNovaBodyFormat |
SambaNova |
xai |
spec: OpenAICompatibleBodyFormat + XAiMessageFormat |
xAI (Grok) |
moonshot |
spec: OpenAICompatibleBodyFormat |
Moonshot (via OpenAI-compatible) |
ollama |
spec: OpenAICompatibleBodyFormat |
Ollama (via OpenAI-compatible) |
openai-compatible |
spec: OpenAICompatibleBodyFormat |
Generic OpenAI-compatible APIs |
together |
spec: OpenAICompatibleBodyFormat |
Together AI (via OpenAI-compatible) |
The last four names share a single spec object: they are the same provider behaviour under four names, and the requests they emit are asserted byte-identical.
You can extend the registry with custom drivers:
use Cognesy\Polyglot\Inference\Creation\InferenceDriverRegistry;
use Cognesy\Polyglot\Inference\Creation\BundledInferenceDrivers;
$registry = BundledInferenceDrivers::registry()
->withDriver('my-provider', MyCustomDriver::class);
$runtime = InferenceRuntime::fromConfig($config, drivers: $registry);
// @doctest id="d1d3"
A custom driver can be registered as a class name (must accept LLMConfig, CanSendHttpRequests, and CanHandleEvents in its constructor) or as a callable factory:
$registry = $registry->withDriver('my-provider', function ($config, $httpClient, $events) {
return new MyCustomDriver($config, $httpClient, $events);
});
// @doctest id="2f4d"
You can also remove drivers from the registry:
Embeddings Driver Registry¶
The EmbeddingsDriverRegistry follows the same immutable instance-based pattern as InferenceDriverRegistry. Bundled embeddings drivers are provided via BundledEmbeddingsDrivers::registry() and include: openai, azure, cohere, gemini, jina, mistral, and ollama.
Custom embeddings drivers can be registered through the registry:
use Cognesy\Polyglot\Embeddings\Creation\BundledEmbeddingsDrivers;
$registry = BundledEmbeddingsDrivers::registry()
->withDriver('my-provider', MyEmbeddingsDriver::class);
$runtime = EmbeddingsRuntime::fromConfig($config, drivers: $registry);
// @doctest id="fdbe"
Or with a factory callable:
$registry = $registry->withDriver('my-provider', function ($config, $httpClient, $events) {
return new MyEmbeddingsDriver($config, $httpClient, $events);
});
// @doctest id="4506"
Both InferenceDriverRegistry and EmbeddingsDriverRegistry use immutable instance-based registration, so driver registrations can vary per runtime.
Key Contracts¶
The provider system is built on a small set of interfaces:
Provider Contracts¶
| Interface | Purpose |
|---|---|
CanResolveLLMConfig |
Returns an LLMConfig from a provider |
HasExplicitInferenceDriver |
Optionally returns a pre-built inference driver |
CanAcceptLLMConfig |
Allows setting an LLMConfig on a provider |
CanResolveEmbeddingsConfig |
Returns an EmbeddingsConfig from a provider |
HasExplicitEmbeddingsDriver |
Optionally returns a pre-built embeddings driver |
Driver Contracts¶
| Interface | Purpose |
|---|---|
CanProcessInferenceRequest |
Main inference driver contract (make responses, stream deltas, report capabilities) |
CanHandleVectorization |
Main embeddings driver contract; returns a normalized EmbeddingsResponse |
CanProvideInferenceDrivers |
Registry that creates inference drivers by name |
Adapter Contracts¶
| Interface | Purpose |
|---|---|
CanTranslateInferenceRequest |
Converts InferenceRequest to HttpRequest |
CanTranslateInferenceResponse |
Converts HttpResponse to InferenceResponse or stream deltas |
CanMapMessages |
Maps typed Messages to provider format |
CanMapRequestBody |
Assembles the request body |
CanMapUsage |
Extracts token usage from response data |
The driver contract CanProcessInferenceRequest also includes a capabilities() method that reports what features a driver supports (e.g., streaming, tool calls, structured output). This can be used to make runtime decisions about which features to use with a given provider: