Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Prism Laravel Package

prism-php/prism

Prism is a Laravel package for integrating LLMs into your apps with a fluent interface for text generation, multi-step conversations, and tool use across multiple AI providers, so you can build AI features without wrestling with provider intricacies.

View on GitHub
Deep Wiki
Context7

Getting Started

Prism is a provider-agnostic LLM client for Laravel. The mental model is a fluent builder: pick a driver (using), shape the request (withPrompt, withSystemPrompt, withMessages, withTools), then call generate() or asStream() and read a typed response object. Everything else — OpenAI, Anthropic, Gemini, Ollama, Mistral, Groq, xAI, DeepSeek, OpenRouter — is the same builder shape, which is the entire value proposition.

composer require prism-php/prism
php artisan vendor:publish --tag=prism-config   # current releases

Add the keys you actually use to .env (OPENAI_API_KEY, ANTHROPIC_API_KEY, …), confirm they map correctly in config/prism.php, and make your first call:

use Prism\Prism\Facades\Prism;

$response = Prism::text()
    ->using('openai', 'gpt-4o')
    ->withSystemPrompt('You are terse.')
    ->withPrompt('Summarize the following ticket: ...')
    ->withMaxTokens(500)
    ->generate();

return $response->asText();

Where to look first. The official docs at prismphp.com (not the README — the README is a logo and a license). Then, in your own vendor/prism-php/prism, read src/Prism.php (the builder), src/Providers/ (driver capabilities differ), and the ValueObjects directory, which is where Usage, Meta, ToolCall, and message classes live; most "how do I get X out of the response" questions are answered there faster than in prose docs.

First real use case. Do not start with a chatbot. Start with one structured extraction behind a queue job: take a model you already trust, feed it a document, get a JSON object back, persist it. That single path exercises config, the response DTOs, usage tracking, and error handling — the four things you will otherwise discover in production.

Implementation Patterns

Wrap calls in an action class, never inline in a controller. The builder is fluent and long; controllers become unreadable, and you cannot test or swap providers. One class per prompt is the sustainable pattern:

final class SummarizeTicket
{
    public function __invoke(string $body): string
    {
        return Prism::text()
            ->using(config('ai.provider'), config('ai.fast_model'))
            ->withSystemPrompt(view('prompts.summarize')->render())
            ->withPrompt($body)
            ->withMaxTokens(400)
            ->generate()
            ->asText();
    }
}

Keep provider and model names in config/ai.php, not literals. That is what lets you move a workload from a cheap model to an expensive one, or to a local Ollama instance, without touching business code.

Prefer structured output over parsing prose. Anything that feeds a database, a validation rule, or a branch should go through Prism::structured() with a schema, then ->asStructured():

$result = Prism::structured()
    ->using('openai', 'gpt-4o')
    ->withSchema([
        'type' => 'object',
        'properties' => [
            'priority' => ['type' => 'string', 'enum' => ['low', 'normal', 'urgent']],
            'tags'     => ['type' => 'array', 'items' => ['type' => 'string']],
            'summary'  => ['type' => 'string'],
        ],
        'required' => ['priority', 'tags', 'summary'],
        'additionalProperties' => false,
    ])
    ->withPrompt($ticket)
    ->generate()
    ->asStructured();

Design schemas defensively: mark fields required only if the model can genuinely always fill them, and keep the shape flat. Optional-but-important fields are where providers diverge most.

Tools are where Prism earns its keep, and where bills come from. Define tools as small classes with Tool::make()->withName()->withDescription()->withStringParameter(...)->using($closure), register with ->withTools([...]), and cap the loop with ->withMaxSteps(n) — always. Tool callbacks should be idempotent and fast; the model may call them twice. For multi-step runs, inspect $response->steps, $response->toolCalls, and $response->toolResults rather than re-parsing text.

Conversations: persist messages, not strings. Rebuild history each turn with ->withMessages([...]) from your messages table, and store the assistant reply (plus any tool calls) after each turn. This keeps context windows under your control and makes trimming/summarization possible: when $response->usage->promptTokens starts climbing past a threshold, summarize older turns instead of shipping the whole log.

Stream to the browser for anything user-facing. ->asStream() yields event objects; a text delta is $event->text, and tool/end events arrive separately. Wire it to an SSE route or Livewire listener, flush per delta, and persist the final assembled message when the stream ends. For anything long-running, batch, or non-interactive, push it to a queue instead — HTTP timeouts are the number one "it works locally" failure.

Operationalize the response, not just the text. $response->usage (prompt/completion tokens), $response->meta (model, id, rate limits) — log these per call into a table keyed by feature. You need per-feature cost attribution on day one; retrofitting it after a surprise invoice is unpleasant.

Test with Prism's fake. Prism ships Prism::fake([...]) so you can assert your code sent the right prompt/model without hitting the network; keep a handful of live smoke tests behind a separate test suite or env flag. Never let a CI suite depend on a provider.

Failover is a try/catch. Catch the package's provider exceptions (including the overloaded variants such as PrismProviderOverloadedException) and retry once on a secondary provider. Combined with ->withClientRetry(n, ms) for transient 429/503s, this covers most real-world flakiness.

Gotchas and Tips

It is 0.x, and it breaks. 0.97.0 renamed \Prism\Prism\Prism to \Prism\Prism\Facades\Prism; 0.99.0 removed the onComplete callback for a new callback handler; 0.100.0 flipped Anthropic structured output to native mode, which also made citations incompatible with structured output. Pin an exact version in composer.json, read the changelog before every bump, and budget an afternoon per minor upgrade.

Token limits have churned and providers default differently. A default cap was removed around 0.99.6, and Anthropic's default max tokens was patched separately. Set ->withMaxTokens() explicitly on every call and treat it as part of the prompt's contract; otherwise the same code behaves differently after a provider swap or a package bump.

withProviderOptions() is not portable. Provider-specific knobs (OpenAI service_tier/store/text_verbosity, Gemini thinking levels, Ollama keep_alive, Anthropic cache TTL) are ignored or rejected by other drivers. Any code that depends on them is not actually provider-agnostic — isolate it, and guard it with a match on the driver.

Structured output + tools is uneven across providers. Support landed incrementally (OpenAI, Gemini, Anthropic at different points). If you need both, test on every provider you intend to ship, and expect JSON-schema subset surprises: Gemini has had raw-schema quirks, some providers reject certain keywords, and Anthropic citations with structured output are still out. Parse defensively even on a schema — a json_decode guard and a single repair retry is cheap insurance.

Streaming tool calls arrive in fragments, and provider tool events differ. Text deltas are safe to render immediately; tool call arguments are assembled across chunks and only meaningful at completion. Don't build logic on partial tool payloads, and don't assume the event sequence is identical between OpenAI, Anthropic, and Gemini.

Provider-specific "bugs" that are really provider behavior. Gemini thinking/thought parts leaking into text, missing thought_signature on Gemini 3 tool calls, OpenRouter returning null in unexpected places — these have all produced patch releases. When something odd only happens on one provider, check the newer releases before debugging your own code.

Timeouts and execution limits. Long generations plus multi-step tools routinely exceed default HTTP timeouts; set ->withClientOptions(['timeout' => 120]) where appropriate and move anything over ~20 seconds to a queue. Streaming into a response that is already buffered or already flushed is a classic silent failure — verify output buffering is off on the route.

Cost control is a design concern, not a config one. Three levers: withMaxTokens, withMaxSteps, and model tier in config. A tool loop with a generous token cap can multiply one user click into dozens of calls. Add a per-request step ceiling and a usage log you can alert on.

Context windows are your problem. Prism gives you withMessages and token counts, not automatic context management. Implement trimming or summarization deliberately, and test with the longest realistic conversation, not the shortest.

Extension points worth knowing. Custom tools (plain classes implementing the tool contract), custom schema/value objects, and custom providers registered through config/prism.php plus the provider contract. That's the escape hatch when you need an internal gateway, a proxy base URL (Anthropic's is configurable now), or a vendor Prism doesn't ship yet.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator