symfony/ai-deep-seek-platform
Symfony AI bridge for the DeepSeek Platform. Use DeepSeek chat completions with support for multi-round conversations and function calling, following DeepSeek’s API docs. Contribute and report issues via the main symfony/ai repository.
Install the Package:
composer require symfony/ai-deep-seek-platform
Ensure your composer.json includes PHP 8.2+ and Symfony 7.3+ components (or use spatie/laravel-symfony-components for Laravel integration).
Configure API Key:
Add DeepSeek API credentials to .env:
DEEPSEEK_API_KEY=your_api_key_here
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
Register the Client:
In AppServiceProvider or a dedicated service provider:
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Ai\DeepSeek\DeepSeekClient;
public function register()
{
$this->app->singleton(DeepSeekClient::class, function ($app) {
return new DeepSeekClient(
HttpClient::create([
'base_uri' => config('services.deepseek.url'),
'auth_bearer' => config('services.deepseek.key'),
])
);
});
}
First Use Case: Chat Completion Inject the client into a controller or service:
use Symfony\Ai\DeepSeek\DeepSeekClient;
public function askAi(DeepSeekClient $client)
{
$response = $client->completeChat(
"Summarize this user's order history",
['model' => 'deepseek-chat']
);
return $response->getContent();
}
functions, temperature).DeltaInterface for streaming).Provider abstraction for model routing logic.// Single-turn chat
$response = $client->completeChat(
"Explain Laravel's service container",
['model' => 'deepseek-chat', 'temperature' => 0.7]
);
// Multi-turn chat (persist context)
$messages = [
['role' => 'user', 'content' => 'Hello!'],
['role' => 'assistant', 'content' => 'Hi there!'],
['role' => 'user', 'content' => 'How are you?'],
];
$response = $client->completeChat(
"How are you?",
['model' => 'deepseek-chat', 'messages' => $messages]
);
Bridge AI prompts to Laravel methods:
$response = $client->completeChat(
"Generate an invoice for user ID 123",
[
'model' => 'deepseek-chat',
'functions' => [
[
'name' => 'generateInvoice',
'description' => 'Creates an invoice for a user',
'parameters' => [
'type' => 'object',
'properties' => [
'userId' => ['type' => 'integer'],
'amount' => ['type' => 'number'],
],
],
],
],
]
);
// Handle the response (e.g., parse JSON and call Laravel logic)
$functionCall = $response->getFunctionCall();
if ($functionCall) {
$user = User::find($functionCall['arguments']['userId']);
Invoice::generate($user, $functionCall['arguments']['amount']);
}
Use DeltaInterface for real-time updates (e.g., chat UIs):
$stream = $client->streamChat(
"Write a blog post about Laravel AI",
['model' => 'deepseek-chat']
);
foreach ($stream as $delta) {
if ($delta->isContent()) {
echo $delta->content; // Stream chunks incrementally
}
}
Dynamic provider selection:
// Configure in config/services.php
'deepseek' => [
'url' => env('DEEPSEEK_URL'),
'key' => env('DEEPSEEK_KEY'),
'default_model' => 'deepseek-chat',
'providers' => [
'fallback' => 'openai', // Hypothetical fallback
],
],
// Usage
$client->setProvider('deepseek'); // Explicitly route
$response = $client->completeChat("Use the fallback provider if DeepSeek fails");
Facades for Idiomatic Usage:
// Create a facade (e.g., `app/Facades/DeepSeek.php`)
use Illuminate\Support\Facades\Facade;
class DeepSeek extends Facade {
protected static function getFacadeAccessor() {
return 'deepseek.client';
}
}
Then use DeepSeek::completeChat() in controllers.
Config Publishing: Publish the package’s config (if it had one) or create a custom config:
php artisan vendor:publish --provider="Symfony\Ai\DeepSeek\DeepSeekServiceProvider"
(Note: The package may lack built-in config; extend it via config/deepseek.php.)
Request Scoping: Bind the client to a request-specific scope (e.g., for multi-tenancy):
$client = app(DeepSeekClient::class)->withOptions([
'base_uri' => tenant()->deepseekEndpoint,
]);
Leverage Symfony’s uniform errors (v0.8.0) in Laravel’s exception handler:
// app/Exceptions/Handler.php
public function render($request, Throwable $exception)
{
if ($exception instanceof \Symfony\Ai\Exception\AiException) {
return response()->json([
'error' => $exception->getMessage(),
'code' => $exception->getCode(),
], 400);
}
return parent::render($request, $exception);
}
Mock the client in Laravel’s testing suite:
// tests/Feature/AiFeatureTest.php
use Symfony\Ai\DeepSeek\DeepSeekClient;
public function test_chat_completion()
{
$mockClient = Mockery::mock(DeepSeekClient::class);
$mockClient->shouldReceive('completeChat')
->once()
->andReturn(new \Symfony\Ai\Response\ChatCompletionResponse(
json_encode(['choices' => [['message' => ['content' => 'Test response']]]])
));
$this->app->instance(DeepSeekClient::class, $mockClient);
$response = $this->askAi();
$response->assertSee('Test response');
}
Symfony Dependency Overhead:
HttpClient. If your Laravel app uses Guzzle, you’ll need a wrapper:
use Symfony\Component\HttpClient\Psr18Client;
use GuzzleHttp\Client as GuzzleClient;
$guzzle = new GuzzleClient();
$symfonyClient = new Psr18Client($guzzle);
$deepSeekClient = new DeepSeekClient($symfonyClient);
spatie/laravel-symfony-components to avoid conflicts.Streaming Blocking:
DeltaInterface) can block Laravel’s request lifecycle, causing timeouts.// Dispatch a job to handle the stream
StreamChatJob::dispatch($prompt, $model)->onQueue('ai');
Then use Laravel Echo/Pusher to broadcast chunks to the client.Model Routing Complexity:
Provider abstraction (v0.8.0) may require custom Laravel bindings to dynamically switch providers.Provider interface to integrate with Laravel’s service container:
class LaravelProvider implements ProviderInterface {
public function getClient(): DeepSeekClient {
return app(DeepSeekClient::class);
}
}
Authentication Quirks:
HttpClient explicitly:
$client = new DeepSeekClient(HttpClient::create([
'auth_bearer' => config('services.deepseek.key'),
'headers' => ['X-Custom-Header' => 'value'],
]));
Rate Limiting:
AiExceptions.use Symfony\Component\HttpClient\RetryStrategy;
$client = new DeepSeekClient(HttpClient::create([
'
How can I help you explore Laravel packages today?