Installation
composer require 1tomany/llm-sdk-bundle
Enable the bundle in config/bundles.php:
return [
// ...
OneTomany\LLMSDKBundle\OneTomanyLLMSDKBundle::class => ['all' => true],
];
Configure API Keys
Add environment variables (.env):
ANTHROPIC_API_KEY=your_key
GEMINI_API_KEY=your_key
OPENAI_API_KEY=your_key
Create config/packages/onetomany_llmsdk.yaml with minimal config:
onetomany_llmsdk:
openai: # or anthropic/gemini
api_key: "%env(OPENAI_API_KEY)%"
First Use Case: Chat Completion Inject the service and call:
use OneTomany\LLMSDKBundle\Service\LLMService;
class MyController extends AbstractController
{
public function __invoke(LLMService $llm): string
{
$response = $llm->chat('openai', [
'model' => 'gpt-3.5-turbo',
'messages' => [['role' => 'user', 'content' => 'Hello!']],
]);
return $response['choices'][0]['message']['content'];
}
}
Service Injection
Use dependency injection for LLMService to interact with any configured provider (OpenAI, Anthropic, Gemini):
public function __construct(private LLMService $llm) {}
Provider-Specific Logic Dynamically switch providers based on config or runtime needs:
$provider = $this->getParameter('llm_provider'); // e.g., 'anthropic'
$response = $llm->chat($provider, [...]);
Async Operations
Leverage Symfony’s HttpClient for async calls (if configured):
$response = $llm->asyncChat('openai', [...])->wait();
Mocking in Dev
Enable mocking in config/packages/onetomany_llmsdk.yaml:
when@dev:
onetomany_llmsdk:
mock:
enabled: true
Mock responses are returned instantly for testing.
Event-Driven AI Use Symfony events to trigger LLM calls (e.g., after form submission):
$dispatcher->dispatch(new LLMTriggerEvent('user', 'content'));
Caching Responses Cache frequent queries with Symfony’s cache system:
$cacheKey = md5(serialize($prompt));
$response = $cache->get($cacheKey, fn() => $llm->chat('openai', $prompt));
Validation Layer Validate prompts before sending (e.g., length, toxicity):
if (strlen($prompt) > 2000) {
throw new \RuntimeException('Prompt too long');
}
Rate Limiting Implement a decorator to enforce rate limits:
$llm->withRateLimiter(new RateLimiter(1000, 60))->chat(...);
API Key Exposure
%env() strictly and validate keys are not logged:
onetomany_llmsdk:
openai:
api_key: "%env(OPENAI_API_KEY)%"
Add to monolog.yaml:
handlers:
main:
channels: ["!llm"]
Serialization Mismatches
serializer service config and ensure it matches the provider’s expected format (e.g., JSON for OpenAI).Mocking Quirks
src/OneTomany/LLMSDKBundle/Resources/mocks/.php bin/console config:dump-reference onetomany_llmsdk > config/packages/onetomany_llmsdk.yaml
HTTP Client Scope
onetomany_llmsdk:
openai:
http_client: "llm.http_client.scoped"
Define in services.yaml:
llm.http_client.scoped:
parent: http_client
synthetic: true
Enable Verbose Logging
Add to config/packages/monolog.yaml:
handlers:
llm:
type: stream
path: "%kernel.logs_dir%/llm.log"
level: debug
channels: ["llm"]
Check Response Headers Inspect raw responses for errors:
$response = $llm->chat('openai', [...]);
if (isset($response['error'])) {
throw new \RuntimeException($response['error']['message']);
}
API Version Conflicts
400 Bad Request with "Invalid API version".api_version in config (e.g., "2023-06-01" for Anthropic).Custom Providers Extend the bundle by creating a new service:
services:
App\Service\CustomLLMProvider:
tags: ['onetomany_llm.provider']
Implement OneTomany\LLMSDKBundle\Contract\LLMProviderInterface.
Middleware Add request/response middleware:
$llm->addMiddleware(new AuthMiddleware());
Event Listeners
Listen for LLM events (e.g., LLMBeforeRequestEvent):
use OneTomany\LLMSDKBundle\Event\LLMEvent;
public function onLLMRequest(LLMEvent $event) {
$event->setPrompt(strtoupper($event->getPrompt()));
}
Register in services.yaml:
App\EventListener\LLMListener:
tags:
- { name: kernel.event_listener, event: llm.before_request, method: onLLMRequest }
Docker Integration Use environment variables in Docker:
# docker-compose.yml
services:
app:
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
How can I help you explore Laravel packages today?