Sidekick
A fluent Laravel package for integrating with OpenAI, Anthropic Claude, Mistral, and Cohere AI services. Features a modern builder API, typed responses, streaming support, database-backed conversations, a RAG knowledge base system, an embeddable chat widget, and first-class testing support.
Install / Use
/learn @PapaRascal2020/SidekickQuality Score
Category
Development & EngineeringSupported Platforms
README
Sidekick v2.0
A fluent Laravel package for integrating with OpenAI, Anthropic Claude, Mistral, and Cohere AI services. Features a modern builder API, typed responses, streaming support, database-backed conversations, an embeddable chat widget, and first-class testing support.
Requirements
- PHP 8.2+
- Laravel 10, 11, or 12
Installation
composer require paparascaldev/sidekick
php artisan sidekick:install
Or install manually:
composer require paparascaldev/sidekick
php artisan vendor:publish --tag=sidekick-config
php artisan migrate
Configuration
Add your API keys to .env (only add the providers you use):
SIDEKICK_OPENAI_TOKEN=your-openai-key
SIDEKICK_CLAUDE_TOKEN=your-anthropic-key
SIDEKICK_MISTRAL_TOKEN=your-mistral-key
SIDEKICK_COHERE_TOKEN=your-cohere-key
Publish and customize the config file:
php artisan vendor:publish --tag=sidekick-config
Config File Overview
The config/sidekick.php file includes:
return [
// Default provider when none specified
'default' => env('SIDEKICK_DEFAULT_PROVIDER', 'openai'),
// Default provider + model per capability
'defaults' => [
'text' => ['provider' => 'openai', 'model' => 'gpt-4o'],
'image' => ['provider' => 'openai', 'model' => 'dall-e-3'],
'audio' => ['provider' => 'openai', 'model' => 'tts-1'],
'transcription' => ['provider' => 'openai', 'model' => 'whisper-1'],
'embedding' => ['provider' => 'openai', 'model' => 'text-embedding-3-small'],
'moderation' => ['provider' => 'openai', 'model' => 'text-moderation-latest'],
],
// Provider API keys and base URLs
'providers' => [
'openai' => ['api_key' => env('SIDEKICK_OPENAI_TOKEN'), 'base_url' => '...'],
'anthropic' => ['api_key' => env('SIDEKICK_CLAUDE_TOKEN'), 'base_url' => '...'],
'mistral' => ['api_key' => env('SIDEKICK_MISTRAL_TOKEN'), 'base_url' => '...'],
'cohere' => ['api_key' => env('SIDEKICK_COHERE_TOKEN'), 'base_url' => '...'],
],
// Register custom providers
'custom_providers' => [],
// Chat widget settings
'widget' => [
'enabled' => env('SIDEKICK_WIDGET_ENABLED', false),
'route_prefix' => 'sidekick',
'middleware' => ['web'],
'provider' => env('SIDEKICK_WIDGET_PROVIDER', 'openai'),
'model' => env('SIDEKICK_WIDGET_MODEL', 'gpt-4o'),
'system_prompt' => env('SIDEKICK_WIDGET_SYSTEM_PROMPT', 'You are a helpful assistant.'),
'max_tokens' => 1024,
],
// HTTP timeout and retry settings
'http' => [
'timeout' => env('SIDEKICK_HTTP_TIMEOUT', 30),
'connect_timeout' => env('SIDEKICK_HTTP_CONNECT_TIMEOUT', 10),
'retry' => ['times' => 0, 'sleep' => 100],
],
];
Quick Start
You can use the Sidekick facade or the sidekick() helper function:
use PapaRascalDev\Sidekick\Facades\Sidekick;
// Using the facade
$response = Sidekick::text()->withPrompt('Hello')->generate();
// Using the helper function
$response = sidekick()->text()->withPrompt('Hello')->generate();
Text Generation
$response = Sidekick::text()
->using('openai', 'gpt-4o')
->withSystemPrompt('You are a helpful assistant.')
->withPrompt('What is Laravel?')
->generate();
echo $response->text; // "Laravel is a PHP web framework..."
echo $response->usage->totalTokens; // 150
echo $response->meta->latencyMs; // 523.4
echo $response->finishReason; // "stop"
echo (string) $response; // Same as $response->text
All TextBuilder methods:
| Method | Description |
|--------|-------------|
| using(string $provider, ?string $model) | Set provider and model |
| withPrompt(string $prompt) | Add a user message |
| withSystemPrompt(string $prompt) | Set the system prompt |
| withMessages(array $messages) | Set the full message history (array of Message objects or arrays) |
| addMessage(Role $role, string $content) | Append a single message with a specific role |
| withMaxTokens(int $maxTokens) | Set max tokens (default: 1024) |
| withTemperature(float $temp) | Set temperature (default: 1.0) |
| generate(): TextResponse | Execute and return a TextResponse |
| stream(): StreamResponse | Execute and return a streamable StreamResponse |
Streaming
$stream = Sidekick::text()
->using('anthropic', 'claude-sonnet-4-20250514')
->withPrompt('Write a haiku about coding')
->stream();
// Iterate over chunks
foreach ($stream as $chunk) {
echo $chunk;
}
// Get the full buffered text after iteration
$fullText = $stream->text();
// Or return as an SSE response from a controller
return $stream->toResponse();
Conversations (with DB persistence)
// Start a conversation
$convo = Sidekick::conversation()
->using('openai', 'gpt-4o')
->withSystemPrompt('You are a travel advisor.')
->withMaxTokens(2048)
->begin();
$response = $convo->send('I want to visit Japan.');
echo $response->text;
// Get the conversation ID to resume later
$conversationId = $convo->getConversation()->id;
// Resume later
$convo = Sidekick::conversation()->resume($conversationId);
$response = $convo->send('What about accommodation?');
// Delete a conversation
$convo->delete();
All ConversationBuilder methods:
| Method | Description |
|--------|-------------|
| using(string $provider, ?string $model) | Set provider and model |
| withSystemPrompt(string $prompt) | Set the system prompt |
| withMaxTokens(int $maxTokens) | Set max tokens (default: 1024) |
| begin(): self | Start a new conversation (persisted to DB) |
| resume(string $id): self | Resume an existing conversation by UUID |
| send(string $message): TextResponse | Send a message and get a response |
| delete(): bool | Delete the conversation and its messages |
| getConversation(): ?Conversation | Get the underlying Eloquent model |
Image Generation
$response = Sidekick::image()
->using('openai', 'dall-e-3')
->withPrompt('A sunset over mountains')
->withSize('1024x1024')
->withQuality('hd')
->count(2)
->generate();
echo $response->url(); // First image URL
echo $response->urls; // Array of all URLs
echo $response->revisedPrompt; // DALL-E's revised prompt (if any)
All ImageBuilder methods:
| Method | Description |
|--------|-------------|
| using(string $provider, ?string $model) | Set provider and model |
| withPrompt(string $prompt) | Set the image prompt |
| withSize(string $size) | Set dimensions (default: 1024x1024) |
| withQuality(string $quality) | Set quality: standard or hd (default: standard) |
| count(int $count) | Number of images to generate (default: 1) |
| generate(): ImageResponse | Execute and return an ImageResponse |
Audio (Text-to-Speech)
$response = Sidekick::audio()
->using('openai', 'tts-1')
->withText('Hello, welcome to Sidekick!')
->withVoice('nova')
->withFormat('mp3')
->generate();
$response->save('audio/welcome.mp3'); // Save to default disk
$response->save('audio/welcome.mp3', 's3'); // Save to specific disk
echo $response->format; // "mp3"
All AudioBuilder methods:
| Method | Description |
|--------|-------------|
| using(string $provider, ?string $model) | Set provider and model |
| withText(string $text) | Set the text to speak |
| withVoice(string $voice) | Set the voice (default: alloy) |
| withFormat(string $format) | Set audio format (default: mp3) |
| generate(): AudioResponse | Execute and return an AudioResponse |
Transcription
$response = Sidekick::transcription()
->using('openai', 'whisper-1')
->withFile('/path/to/audio.mp3')
->withLanguage('en')
->generate();
echo $response->text; // Transcribed text
echo $response->language; // "en"
echo $response->duration; // Duration in seconds
echo (string) $response; // Same as $response->text
All TranscriptionBuilder methods:
| Method | Description |
|--------|-------------|
| using(string $provider, ?string $model) | Set provider and model |
| withFile(string $filePath) | Path to the audio file |
| withLanguage(string $language) | Hint the language (optional) |
| generate(): TranscriptionResponse | Execute and return a TranscriptionResponse |
Embeddings
$response = Sidekick::embedding()
->using('openai', 'text-embedding-3-small')
->withInput('Laravel is a great framework')
->generate();
$vector = $response->vector(); // First embedding vector (array of floats)
$all = $response->embeddings; // All embedding vectors
echo $response->usage->totalTokens; // Token usage
All EmbeddingBuilder methods:
| Method | Description |
|--------|-------------|
| using(string $provider, ?string $model) | Set provider and mod
Related Skills
node-connect
349.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.7kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
