Skip to content

Providers

A provider sends the conversation to a model API and returns the reply. Core ships three native providers and six OpenAI-compatible presets, with no extra package to install.


Set it on the builder:

use PhpClaw\Claw;
$agent = Claw::builder()
->provider('openai')
->apiKey($key)
->model('gpt-4o')
->build();

Or leave it out and phpClaw picks from the environment. PHPCLAW_PROVIDER wins if set; otherwise the first key found in this order decides:

OrderEnvironment variableProvider
1ANTHROPIC_API_KEYanthropic
2OPENAI_API_KEYopenai
3GROQ_API_KEYgroq
4GEMINI_API_KEYgemini
5MISTRAL_API_KEYmistral
6DEEPSEEK_API_KEYdeepseek
7OLLAMA_HOSTollama

With none of them set, the provider is anthropic, and building fails because there is no key.

When ->model() is not called, PHPCLAW_MODEL is used, then the provider’s default.


SlugClassDefault modelKey
anthropicAnthropicProviderclaude-haiku-4-5-20251001ANTHROPIC_API_KEY
geminiGeminiProvidergemini-3.5-flash-liteGEMINI_API_KEY
openaiOpenAIProvidergpt-4o-miniOPENAI_API_KEY
groqOpenAIProviderllama-3.1-8b-instantGROQ_API_KEY
mistralOpenAIProvidermistral-small-latestMISTRAL_API_KEY
deepseekOpenAIProviderdeepseek-flashDEEPSEEK_API_KEY
ollamaOpenAIProviderqwen2.5:7bnone
customOpenAIProvidernoneOPENAI_API_KEY

The last six share one class and differ only in endpoint, default model and authentication.


AnthropicProvider names three models as constants and picks an output limit for each when you do not set maxTokens():

ConstantModelDefault max_tokens
MODEL_HAIKUclaude-haiku-4-5-202510018192
MODEL_SONNETclaude-sonnet-516000
MODEL_OPUSclaude-opus-4-816000
any other model4096
$agent = Claw::builder()->provider('anthropic')->promptCache(false)->build();

On by default through the builder. It marks the system prompt and the last tool as cacheable and sends the prompt-caching-2024-07-31 beta header. AnthropicProvider’s own constructor defaults it to off, so a provider you construct yourself does not cache unless told to.

Where you runPrompt caching by default
Claw::builder()on
WordPress, Joomla, Drupal, Magento, PrestaShop, OpenCarton: they do not set it, so the builder default applies
Laraveloff, PHPCLAW_PROMPT_CACHE
Symfonyoff, prompt_cache
$agent = Claw::builder()->provider('anthropic')->thinkingBudget(2048)->build();
$response = $agent->send('...');
echo $response->thinking;

A budget of 1024 or more turns it on. It sends thinking: {type: enabled, budget_tokens: N} with the interleaved-thinking-2025-05-14 beta header, and raises max_tokens to at least the budget plus 1000. Below 1024 it is off.

Laravel exposes it as PHPCLAW_THINKING_BUDGET and Symfony as thinking_budget, both default 0. No CMS adapter exposes it.


GeminiProvider sends the key as a ?key= query parameter rather than a header.

When you do not set maxTokens(), a model id starting gemini-3.5-flash-lite, gemini-3.5-flash, gemini-2.5-flash, gemini-2.5-pro or gemini-2 gets an output limit of 8192 tokens. Any other model gets 2048.

It strips JSON Schema keywords that Gemini rejects before sending tool schemas, including additionalProperties, $schema, $ref, const and patternProperties, and converts nullable union types to Gemini’s nullable flag.

Model ids starting gemini-1. are treated as legacy and never receive web search.


openai, groq, mistral, deepseek, ollama and custom all use OpenAIProvider against a Chat Completions endpoint.

No key. The endpoint is http://127.0.0.1:11434/v1/chat/completions, or OLLAMA_HOST followed by /v1/chat/completions when that variable is set.

Terminal window
export OLLAMA_HOST=http://localhost:11434

qwen2.5:7b is the default model, so pull it first or set another:

Terminal window
ollama pull qwen2.5:7b

Any OpenAI-compatible endpoint:

Terminal window
export PHPCLAW_PROVIDER=custom
export OPENAI_BASE_URL=https://llm.internal.example.com/v1/chat/completions
export OPENAI_API_KEY=...
export PHPCLAW_MODEL=my-model

Without OPENAI_BASE_URL, building fails. There is no default model, so set one. A Bearer token is always sent.

An endpoint given with no path, such as https://llm.example.com, gets /v1/chat/completions appended. One with a path is used as written.


Anthropic, Gemini and OpenAI support provider-native web search, and it is attached by default. When you configure none, the builder attaches one with no limits.

ProviderWhat is sent by default
anthropica web_search tool on every request, with no use limit
geminigoogle_search grounding, but only on a request that carries no function tools
openai presetsweb_search_options, but only when the model id contains search-preview

Configure it:

use PhpClaw\Providers\Tools\WebSearch;
$agent = Claw::builder()
->provider('anthropic')
->withProviderTool((new WebSearch)->max(3)->allow(['php.net'])->location(country: 'GB'))
->build();
MethodEffectHonoured by
max(int)maximum searches per requestAnthropic
allow(array)allowed domainsAnthropic
location(city, region, country)approximate user locationAnthropic

Gemini’s grounding ignores all three.

The builder has no switch to remove it: configuring no web search means “use the default”. To send requests without it, build the provider yourself and pass it to providerOverride().

On Gemini, an agent with any tools registered never gets grounding.


$agent->stream($message, $onToken) behaves differently depending on tools:

AgentWhat $onToken receives
no tools registeredtokens as the provider streams them
tools registerednothing until the whole run finishes, then the final answer in 10-byte pieces

With tools, every model call is a normal request so that tool calls can be read, and the finished answer is replayed through $onToken. The output looks streamed, but the first chunk arrives only after the full answer exists.


use PhpClaw\ClawConfig;
use PhpClaw\Providers\ProviderRegistry;
ProviderRegistry::register('myllm', fn (ClawConfig $config) => new MyLlmProvider($config->apiKey, $config->model));
$agent = Claw::builder()->provider('myllm')->apiKey($key)->build();

register() takes a factory, as above, or a class name. A class is constructed as new $class($apiKey, new RawHttpClient, new StreamParser, $model), so its constructor must accept those four positional arguments. Names are case-insensitive.

Your own slug has no key variable of its own, so pass the key with apiKey(). A provider key in the environment, such as ANTHROPIC_API_KEY, is not picked up for it.

The registry is consulted before the presets, so registering openai or ollama replaces that preset.

namespace PhpClaw\Providers\Contracts;
interface ProviderInterface
{
public function send(array $messages, array $tools = []): array;
public function stream(array $messages, callable $onToken): string;
public function name(): string;
public function model(): string;
}

name() decides the tool schema shape, so return one of openai, groq, gemini, mistral or ollama if your API expects OpenAI-style tools, and anything else for the Anthropic shape.


providerOverride() bypasses provider resolution entirely:

use PhpClaw\Claw;
use PhpClaw\Providers\Contracts\ProviderInterface;
$mock = $this->createMock(ProviderInterface::class);
$mock->method('name')->willReturn('anthropic');
$mock->method('model')->willReturn('claude-haiku-4-5-20251001');
$mock->method('send')->willReturn([
'type' => 'text',
'text' => 'Hello from mock',
'input_tokens' => 10,
'output_tokens' => 5,
]);
$agent = Claw::builder()->providerOverride($mock)->build();
$this->assertSame('Hello from mock', $agent->send('Hello')->text);

A mock of ProviderInterface does not implement web search, so no web search is attached.


ProviderCatalogue::keys() lists every provider available by slug: the two native providers carrying a #[Provider] attribute, anthropic and gemini, followed by the six presets. activateFromSettings() registers nothing; the catalogue is for listing providers, for example in an admin dropdown.