Cloudflare Workers AI provider for Prism PHP and Laravel AI SDK — routes through the AI Gateway /compat endpoint.
Works with both Prism::text()->using('workers-ai', ...) and agent()->prompt(provider: 'workers-ai').
Workers AI's OpenAI-compatible /compat endpoint has subtle differences from the OpenAI API that break Prism's built-in xAI driver:
- Array content format — The xAI
MessageMapwraps user content in[{type: "text", text: "..."}]. Workers AI expects a plain string. - Structured output type mismatch —
/compatmay returncontentas a JSON object instead of a string, causing TypeError crashes. - Missing
contentfield — Workers AI requirescontentto always be present on assistant messages, even when empty (tool call responses). - No embeddings — The xAI driver doesn't support embeddings. This package does.
composer require meirdick/prism-workers-aiThe service provider is auto-discovered by Laravel. No additional setup needed.
'providers' => [
'workers-ai' => [
'api_key' => env('CLOUDFLARE_AI_API_TOKEN', ''),
'url' => env('WORKERS_AI_URL'),
],
],'providers' => [
'workers-ai' => [
'driver' => 'workers-ai',
'key' => env('CLOUDFLARE_AI_API_TOKEN'),
'url' => env('WORKERS_AI_URL'),
],
],CLOUDFLARE_AI_API_TOKEN=your-cloudflare-api-token
WORKERS_AI_URL=https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_slug}/compatNote: The URL must end in
/compat, not/workers-ai/v1. The SDK appends/chat/completionsautomatically.
Create a Cloudflare API token with Workers AI: Read permission at dash.cloudflare.com/profile/api-tokens.
The AI Gateway has two OpenAI-compatible endpoints for Workers AI:
| Endpoint | URL | Model format |
|---|---|---|
| Universal (recommended) | .../compat |
workers-ai/@cf/meta/... |
| Provider-specific | .../workers-ai/v1 |
@cf/meta/... |
This package targets the /compat endpoint. The workers-ai/ prefix in model names tells the gateway which provider to route to. The gateway strips the prefix internally — responses return just @cf/....
use Prism\Prism\Facades\Prism;
// Text generation
$response = Prism::text()
->using('workers-ai', 'workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast')
->withPrompt('Hello!')
->asText();
// Structured output
$response = Prism::structured()
->using('workers-ai', 'workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast')
->withSchema($schema)
->withPrompt('Classify this intent.')
->generate();
// Embeddings
$response = Prism::embeddings()
->using('workers-ai', 'workers-ai/@cf/baai/bge-large-en-v1.5')
->fromInput('Hello world')
->generate();
// Streaming
$stream = Prism::text()
->using('workers-ai', 'workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast')
->withPrompt('Tell me a story')
->asStream();
// Tool calling
$response = Prism::text()
->using('workers-ai', 'workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast')
->withTools([$weatherTool])
->withMaxSteps(3)
->withPrompt('What is the weather?')
->asText();
// Reasoning models (Kimi K2.5) — thinking content extracted automatically
$response = Prism::text()
->using('workers-ai', 'workers-ai/@cf/moonshotai/kimi-k2.5')
->withMaxTokens(2000) // reasoning models need higher max_tokens
->withPrompt('What is 15 * 37?')
->asText();
$response->text; // "555"
$response->steps[0]->additionalContent['thinking']; // "The user is asking for the product of 15 and 37..."
// Streaming with thinking events
$stream = Prism::text()
->using('workers-ai', 'workers-ai/@cf/moonshotai/kimi-k2.5')
->withMaxTokens(2000)
->withPrompt('Explain briefly why the sky is blue.')
->asStream();
foreach ($stream as $event) {
// ThinkingStartEvent, ThinkingEvent (deltas), ThinkingCompleteEvent
// then TextStartEvent, TextDeltaEvent, TextCompleteEvent
}
// Session affinity (prefix caching for multi-turn conversations)
$response = Prism::text()
->using('workers-ai', 'workers-ai/@cf/moonshotai/kimi-k2.5')
->withProviderOptions(['session_affinity' => 'ses_' . $conversation->id])
->withMaxTokens(2000)
->withPrompt('Follow-up question...')
->asText();use function Laravel\Ai\agent;
// Text generation via agent
$response = agent(instructions: 'You are a helpful assistant.')
->prompt('Hello!', provider: 'workers-ai');
// With explicit model
$response = agent(instructions: 'Be brief.')
->prompt('Hello!',
provider: 'workers-ai',
model: 'workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast',
);
// Via agent class attributes
#[Provider('workers-ai')]
#[Model('workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast')]
class MyAgent implements Agent, Conversational { ... }| Model | Use Case |
|---|---|
workers-ai/@cf/moonshotai/kimi-k2.5 |
Frontier — smartest (256K context, reasoning, vision, tool calling) |
workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast |
General purpose (best quality/speed) |
workers-ai/@cf/meta/llama-3.1-8b-instruct |
Fast/cheap tasks |
workers-ai/@cf/qwen/qwq-32b |
Reasoning |
workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct |
Code generation |
workers-ai/@cf/baai/bge-large-en-v1.5 |
Embeddings (1024 dimensions) |
Reasoning models: Kimi K2.5 is a thinking model — it reasons before answering. Set
withMaxTokens(2000)or higher, as reasoning tokens count against the limit. The thinking chain is available in$response->steps[0]->additionalContent['thinking'].
All model names must be prefixed with workers-ai/ when routing through AI Gateway, so the gateway knows which provider to route to.
The package registers a workers-ai provider at two levels:
- Prism layer — via
PrismManager::extend(), handling the actual HTTP requests with correct content formatting - Laravel AI SDK layer — via
AiManager::extend(), bridging theworkers-aidriver to the Prism provider
The Laravel AI SDK bridge includes a PrismGateway subclass that overrides configure() to pass the driver name as a string to Prism, bypassing the SDK's hardcoded provider enum mapping. This override auto-disables itself when laravel/ai adds native support for custom Prism providers (laravel/ai#283, laravel/ai#284).
For a fully automated setup — including AI Gateway routing, environment configuration, and Workers AI registration — use the laravel-cloudflare-ai-gateway Claude Code skill:
npx skills add meirdick/laravel-cloudflare-ai-gatewayIt handles installing this package, configuring your gateway URL, and wiring up all providers in a single guided workflow.
composer testMIT