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

Deepseek Chat Laravel Package

filaforge/deepseek-chat

Integrate DeepSeek's chat API into your Laravel app with this lightweight package. Send prompts, manage conversations, and handle responses through a clean, configurable interface built for PHP and Laravel developers.

View on GitHub
Deep Wiki
Context7

Getting Started

Start with the install and a single round-trip call before designing anything around it.

composer require filaforge/deepseek-chat
php artisan vendor:publish --tag=deepseek-config

Then set the credential in .env:

DEEPSEEK_API_KEY=sk-...

Look first at config/deepseek.php — that is where the base URL (DeepSeek is OpenAI-compatible, conventionally https://api.deepseek.com), default model (deepseek-chat for general use, deepseek-reasoner for reasoning-heavy prompts), timeouts, and retry settings live. Read the package's facade or manager class next to see what the fluent builder actually supports in this version, because pre-1.0 packages change surface area between minor releases.

The first use case worth wiring is a thin service class around the client rather than calling the facade from controllers:

$reply = DeepSeek::chat()
    ->model('deepseek-chat')
    ->system('You are a concise support assistant.')
    ->user($question)
    ->send();

return $reply->content(); // plus ->usage() for token accounting

Get that working end-to-end (including one deliberate failure, e.g. a bogus key) before you build streaming, tools, or queued jobs on top of it.

Implementation Patterns

Wrap it in one application-owned service. Controllers, jobs, and Artisan commands should all depend on your AssistantService, not on DeepSeek:: directly. That single seam is what lets you swap models, add prompt versioning, or survive a breaking change in a 0.9 release without touching twenty call sites.

Conversation state belongs in your database, not the package. The client is stateless — you send the full message array each turn. Persist turns in a messages table (or a JSON column on a conversations record) and rebuild the payload per request. This also gives you auditability and makes retries safe.

$messages = $conversation->messages()
    ->latest()->limit(20)->get()->reverse()
    ->map(fn ($m) => ['role' => $m->role, 'content' => $m->content])
    ->all();

Stream over SSE for anything user-facing and interactive. A Laravel route returning a StreamedResponse that echoes deltas gives perceived latency far below a blocking call. For long generations, pair it with a queue for the post-processing step rather than doing database writes mid-stream.

Treat long-running calls as jobs. Anything beyond a couple of seconds should be a queued job with an explicit $timeout larger than the HTTP client timeout, plus $tries and backoff() aligned to the rate limits you observe. Returning a job id and polling (or broadcasting on completion) is the reliable pattern.

Budget tokens deliberately. Track usage on every response in a token_usages table keyed by user/feature. Once you know your average cost per call, you can set a per-tenant daily cap before finance notices.

Function/tool calling fits Laravel nicely. Map each tool to an invokable class or a small registry, validate the model's arguments with a FormRequest or Validator::make, and execute. Never call a mutating method straight from model output without validation — the model can and will invent arguments.

Test with Http::fake(). Because the transport is HTTP, you can assert your prompt construction and error branches entirely offline. Keep one recorded real-response fixture for parsing tests (usage shape, finish reasons, streamed chunk framing).

Gotchas and Tips

Release notes for v0.8.5 are effectively empty — the entry is only a compare link against v0.8.3, with no changelog body, no migration note, and no listed fixes. Treat that as "unknown" rather than "safe": read the diff (v0.8.3...v0.8.5) before bumping, and commit your composer.lock so a surprise patch cannot land silently in CI.

Pre-1.0 semver is not a promise. Under 0.x, a patch bump should be compatible but the maintainer makes no guarantee — method signatures, config keys, and the default model string have the most churn historically. Pin with ^0.8.5 and upgrade deliberately.

Config caching bites. php artisan config:cache will freeze your published config; after changing a key in config/deepseek.php in a deployed environment, re-run config:cache. In tests, config()->set() must happen before the client is resolved if the manager caches its configuration in the constructor — bind a fresh instance or use app()->forgetInstance().

Timeouts are two-sided. Your HTTP client timeout must exceed the model's generation time, and your queue worker's --timeout must exceed the HTTP timeout. A mismatch here produces the classic "job killed, request still billing" ghost. Reasoning models are materially slower; give them their own timeout profile.

Rate limits and balance errors need distinct handling. A 429 wants exponential backoff and jitter; a 402-style "insufficient balance" wants to fail loudly and alert an operator, not retry. Catch by status code, log the request id, and surface a friendly message to users. Distinguish these from 5xx, which are safe to retry.

Never log the API key or full headers. Log the model, token counts, latency, and request id. Redact message bodies by default, or you will end up with PII in your log aggregator.

Empty or truncated responses are a real state. Check finish_reason; a length finish means your max_tokens cut the answer off. Surfacing that as a normal success is a common and confusing bug.

Extension points to prefer. Use the container binding / facade swap to inject a fake client in tests, and an Http::macro or Guzzle middleware for retry-and-metrics if the package's own retry config is thin. Avoid subclassing internal client classes — those are the classes most likely to change between 0.8 and 1.0. If you need custom behaviour, wrap rather than extend.

Watch the vendor release feed. With changelogs this terse, subscribing to GitHub releases for this repo (rather than relying on Packagist's summary) is the only practical early warning before a breaking bump.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib