lingoda / langfuse-bundle
Symfony bundle for Langfuse integration with automatic AI tracing
Installs: 23
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
Requires
- php: ^8.3
- dropsolid/langfuse-php-sdk: ^1.2
- lingoda/ai-bundle: ^1.2
- psr/log: ^3.0
- symfony/cache: ^6.4|^7.0
- symfony/clock: ^6.4|^7.0
- symfony/config: ^6.4|^7.0
- symfony/console: ^6.4|^7.0
- symfony/dependency-injection: ^6.4|^7.0
- symfony/expression-language: ^6.4|^7.0
- symfony/http-client: ^6.4|^7.0
- symfony/http-foundation: ^6.4|^7.0
- symfony/http-kernel: ^6.4|^7.0
- webmozart/assert: ^1.11
Requires (Dev)
- dg/bypass-finals: ^1.7
- league/flysystem: ^3.0
- matthiasnoback/symfony-dependency-injection-test: ^6.1
- nyholm/symfony-bundle-test: ^3.1
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-webmozart-assert: ^2.0
- phpunit/phpunit: ^10.5
- symfony/messenger: ^6.4|^7.0
- symplify/easy-coding-standard: ^12.0
Suggests
- league/flysystem: Required for cloud storage integration (S3, GCS, etc.)
- symfony/messenger: Required for async trace processing
README
A Symfony bundle for integrating with Langfuse, providing AI operation tracing, prompt management, and observability for AI applications built with the Lingoda AI SDK.
Features
- 🔍 Automatic AI Tracing: Traces all AI operations (completions, TTS, STT, translations)
- ⚡ Async/Sync Flushing: Configurable sync or async trace processing via Symfony Messenger
- 📝 Prompt Management: Fetch, cache, and manage prompts with intelligent cascade (Cache → API → Storage)
- 💰 Usage Metrics: Automatic token counting and cost tracking with proper generation support
- 🗄️ Flexible Storage: Path-based or Flysystem storage for prompt fallbacks
- 🔧 Console Commands: Connection testing and prompt management tools
Requirements
- PHP 8.3 or higher
- Symfony 6.4 or 7.0+
- Lingoda AI Bundle 1.2+ (the bundle decorates the Lingoda AI SDK)
- Dropsolid Langfuse PHP SDK 1.2+
Installation
- Install the bundle via Composer:
composer require lingoda/langfuse-bundle
- Add the bundle to
config/bundles.php
:
return [ // ... Lingoda\LangfuseBundle\LingodaLangfuseBundle::class => ['all' => true], ];
- Configure the bundle in
config/packages/lingoda_langfuse.yaml
:
lingoda_langfuse: connection: public_key: '%env(LANGFUSE_PUBLIC_KEY)%' secret_key: '%env(LANGFUSE_SECRET_KEY)%' host: '%env(default:LANGFUSE_HOST:https://cloud.langfuse.com)%' timeout: 30 retry: max_attempts: 3 delay: 1000 tracing: enabled: true sampling_rate: 1.0 async_flush: enabled: false message_bus: 'messenger.bus.default' prompts: caching: enabled: false ttl: 3600 service: 'cache.app' fallback: enabled: true storage: path: '%kernel.project_dir%/var/prompts'
Core Features
Automatic AI Operation Tracing
The bundle automatically traces all AI operations by decorating the Lingoda AI SDK's PlatformInterface
:
use Lingoda\AiSdk\PlatformInterface; class ContentService { public function __construct( private PlatformInterface $platform ) {} public function generateContent(string $prompt): string { // This call is automatically traced to Langfuse with proper generation and usage metrics $result = $this->platform->ask($prompt, 'gpt-4'); return $result->getContent(); } public function generateAudio(string $text): string { // TTS operations are also traced $audio = $this->platform->textToSpeech($text); return $audio->getContent(); } }
Automatic trace data includes:
- Operation type (ai-completion, text-to-speech, etc.)
- Model information and provider resolution
- Input/output content and metadata
- Duration and timing information
- Usage metrics (prompt tokens, completion tokens, total tokens)
- Proper Langfuse generation structure for cost tracking
- Error handling and status tracking
Usage Metrics and Cost Tracking
The bundle automatically extracts and sends usage metrics in the proper format for Langfuse:
- Prompt tokens: Input token count
- Completion tokens: Output token count
- Total tokens: Combined count
- Model information: For accurate cost calculation
- Proper generation structure: Trace → Generation hierarchy
This enables accurate cost tracking and usage analysis in the Langfuse dashboard.
Async Trace Flushing
For production applications, enable asynchronous trace processing to eliminate latency impact:
1. Configure Messenger Transport
# config/packages/messenger.yaml framework: messenger: transports: langfuse_traces: dsn: '%env(MESSENGER_TRANSPORT_DSN)%/langfuse-traces' retry_strategy: max_retries: 3 delay: 1000 multiplier: 2 routing: 'Lingoda\LangfuseBundle\Message\FlushLangfuseTrace': langfuse_traces
2. Enable Async Processing
# config/packages/lingoda_langfuse.yaml lingoda_langfuse: tracing: enabled: true sampling_rate: 1.0 async_flush: enabled: true message_bus: 'messenger.bus.default' # optional, defaults to messenger.bus.default
3. Run Message Consumers
php bin/console messenger:consume langfuse_traces --time-limit=3600
# or multiple workers for high throughput
php bin/console messenger:consume langfuse_traces --limit=100
php bin/console messenger:consume langfuse_traces --limit=100
...
Benefits:
- Zero latency impact on AI operations
- Higher throughput for AI-intensive applications
- Reliable delivery with retry mechanisms
- Scalable processing with multiple workers
Prompt Management
The bundle provides a complete prompt management system with intelligent caching and fallback storage:
use Lingoda\LangfuseBundle\Prompt\PromptRegistryInterface; use Lingoda\AiSdk\Prompt\Conversation; class EmailService { public function __construct( private PromptRegistryInterface $prompts ) {} public function generateWelcomeEmail(string $userName): Conversation { // Intelligent cascade: Cache → API → Storage return $this->prompts->get('welcome_email'); } public function generateCustomEmail(string $promptName, array $variables): Conversation { // Get specific version with variables $prompt = $this->prompts->get($promptName, $variables); return $prompt; } }
Prompt Flow:
- Cache lookup - Fast in-memory retrieval
- API call - Fresh fetch from Langfuse if cache miss
- Storage fallback - Local storage if API unavailable
- Automatic caching - Cache API results for future use
Storage Configuration Options
Path-based Storage (Default):
lingoda_langfuse: prompts: fallback: storage: path: '%kernel.project_dir%/var/prompts'
Flysystem Integration (Cloud Storage):
# Configure Flysystem adapter first flysystem: storages: prompts.storage: adapter: 's3' options: bucket: 'my-prompts-bucket' region: 'us-east-1' # Reference in Langfuse config lingoda_langfuse: prompts: fallback: storage: service: 'prompts.storage'
Direct TraceClient Usage
For manual tracing beyond automatic AI operations:
use Lingoda\LangfuseBundle\Client\TraceClient; class AnalyticsService { public function __construct( private TraceClient $traceClient ) {} public function processUserAction(User $user, string $action): void { $trace = $this->traceClient->trace([ 'name' => 'user-action', 'userId' => $user->getId(), 'metadata' => ['action' => $action], 'input' => ['timestamp' => time()] ]); // For AI operations, create a generation if ($action === 'ai-query') { $generation = $trace->createGeneration( name: 'ai-completion', model: 'gpt-4', input: ['query' => 'user question'] ); // Set usage details for cost tracking $generation->withUsageDetails([ 'prompt_tokens' => 15, 'completion_tokens' => 8, 'total_tokens' => 23 ]); $generation->end(['output' => 'AI response']); } $trace->end([ 'output' => ['status' => 'completed'], 'statusMessage' => 'Action processed successfully' ]); // Manually flush if needed (automatic in most cases) $this->traceClient->flush(); } }
Configuration Reference
lingoda_langfuse: # Connection settings connection: public_key: string # Required: Langfuse public key secret_key: string # Required: Langfuse secret key host: string # Default: https://cloud.langfuse.com timeout: int # Default: 30 (seconds) retry: max_attempts: int # Default: 3 delay: int # Default: 1000 (milliseconds) # Tracing configuration tracing: enabled: bool # Default: true sampling_rate: float # Default: 1.0 (0.0-1.0) async_flush: enabled: bool # Default: false message_bus: string # Default: 'messenger.default_bus' # Prompt management prompts: caching: enabled: bool # Default: false ttl: int # Default: 3600 (seconds) service: string # Default: 'cache.app' fallback: enabled: bool # Default: false (auto-enabled with storage config) storage: path: string # File system path # OR service: string # Flysystem service ID
Console Commands
Test Connection
Verify your Langfuse API credentials and connection:
php bin/console langfuse:test-connection
Cache Prompts
Cache specific prompts from Langfuse to local fallback storage:
# Cache specific prompts php bin/console langfuse:cache-prompt --prompt=greeting --prompt=goodbye # Preview without saving php bin/console langfuse:cache-prompt --prompt=greeting --dry-run # Force overwrite existing cached prompts php bin/console langfuse:cache-prompt --prompt=greeting --force
Options:
--prompt|-p
: Specify prompt name(s) to cache (required, can be used multiple times)--dry-run
: Preview what would be cached without saving files--force
: Overwrite existing cached prompts
Performance Optimization
Sync Mode (Default):
- Immediate trace delivery
- ~50-100ms latency per operation
- Guaranteed delivery
- Good for low-traffic applications
Async Mode:
- Zero latency impact on AI operations
- Requires message queue infrastructure
- Higher throughput potential
- Eventual consistency
Sampling Configuration:
lingoda_langfuse: tracing: sampling_rate: 0.1 # Trace 10% of operations
Development
Running Tests
# Install dependencies composer install # Run PHPUnit tests vendor/bin/phpunit # Run static analysis vendor/bin/phpstan analyse # Check code style vendor/bin/ecs check # Fix code style vendor/bin/ecs check --fix
Architecture
The bundle follows clean architecture principles with focused, single-responsibility services:
Core Components
- LangfusePlatformDecorator: Decorates AI SDK to enable automatic tracing
- TraceManager: Coordinates trace data preparation and timing
- SyncTraceFlusher: Handles synchronous flushing with generation support
- AsyncTraceFlusher: Dispatches traces to message queue
- FlushLangfuseTraceHandler: Processes async messages (delegates to sync flusher)
- PromptRegistry: Manages prompt lifecycle with caching and storage
- PromptClient: Handles API communication with Langfuse
- TraceClient: Direct trace creation and management
Trace Processing Architecture
AI Operation → Platform Decorator → TraceManager → FlushService → Langfuse
↓ ↓
TraceFlusherInterface
↓
SyncTraceFlusher
↓
Creates Trace → Generation → Usage Details
↓
Langfuse API
Async Flow:
AI Operation → TraceManager → AsyncTraceFlusher → Message Queue
↓
FlushLangfuseTraceHandler
↓
SyncTraceFlusher
↓
Langfuse API
Prompt Management Architecture
Request → PromptRegistry → Cache → Langfuse API → Storage
↓ ↓ ↓
Fast Return Fresh Data Fallback
Architecture Principles
Design Philosophy
- Direct Langfuse Integration: Clean, direct API communication without intermediary layers
- Simple Trace Structure: AI operations create trace → generation structure
- Async/Sync Flexibility: Choose between immediate or queued processing
Logging
The bundle uses a dedicated langfuse
Monolog channel for all operations. Configure it in your Monolog configuration:
# config/packages/monolog.yaml monolog: channels: - langfuse handlers: langfuse: type: stream path: '%kernel.logs_dir%/langfuse.log' level: debug channels: [langfuse]
Troubleshooting
Common Issues
- Connection failures: Verify your API keys and host URL using
langfuse:test-connection
- Missing prompts: Use
langfuse:cache-prompt
to cache prompts locally for offline access - Async processing not working: Ensure Symfony Messenger is configured and consumers are running
- Token usage not tracked: Verify the AI SDK returns proper usage metrics
Debug Mode
Enable debug logging to troubleshoot issues:
monolog: handlers: langfuse: level: debug # Set to debug level
License
MIT License. See LICENSE for details.