Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"open-telemetry/gen-otlp-protobuf": "^1.0",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.5",
"symfony/ai-agent": "^0.6",
"symfony/ai-platform": "^0.6",
"symfony/framework-bundle": "^7.4 || ^8.0",
"symfony/http-client": "^7.4 || ^8.0",
"symfony/messenger": "^7.4 || ^8.0",
Expand All @@ -39,6 +41,8 @@
},
"suggest": {
"open-telemetry/exporter-otlp": "To export telemetry data to an OTLP compatible ingester (Jaeger, Tempo, Loki, etc.)",
"symfony/ai-agent": "To enable OpenTelemetry tracing for Symfony AI agents (gen_ai.* invoke_agent and execute_tool spans)",
"symfony/ai-platform": "To enable OpenTelemetry tracing for Symfony AI platform calls (gen_ai.* attributes, token usage)",
"symfony/monolog-bundle": "To enable open-telemetry logging"
},
"autoload": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/*
* This file is part of the worldia/instrumentation-bundle package.
* (c) Worldia <developers@worldia.com>
*/

namespace Instrumentation\DependencyInjection\CompilerPass;

use Instrumentation\Semantics\Attribute\AgentAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\ToolAttributeProviderInterface;
use Instrumentation\Semantics\OperationName\AgentOperationNameResolverInterface;
use Instrumentation\Semantics\OperationName\ToolOperationNameResolverInterface;
use Instrumentation\Tracing\AI\Agent\TracingAgent;
use Instrumentation\Tracing\AI\Sampling\OperationNameVoter;
use Instrumentation\Tracing\AI\Toolbox\TracingToolbox;
use OpenTelemetry\API\Trace\TracerProviderInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class AIAgentTracingCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
/** @var array<string> $agentBlacklist */
$agentBlacklist = $container->hasParameter('tracing.ai.agent.blacklist') ? $container->getParameter('tracing.ai.agent.blacklist') : [];
/** @var array<string> $toolBlacklist */
$toolBlacklist = $container->hasParameter('tracing.ai.tool.blacklist') ? $container->getParameter('tracing.ai.tool.blacklist') : [];

// Low priority ensures the tracing decorator is the outermost one, so the
// span covers the full call including any inner decorators (e.g. the
// profiler's traceable decorators registered at -1024).
foreach ($container->findTaggedServiceIds('ai.agent') as $agentId => $tags) {
$name = $tags[0]['name'] ?? $agentId;

$definition = (new Definition(TracingAgent::class))
->setDecoratedService($agentId, priority: -512)
->setArguments([
new Reference('.inner'),
new Reference(TracerProviderInterface::class),
new Reference(AgentOperationNameResolverInterface::class),
new Reference(AgentAttributeProviderInterface::class),
new Definition(OperationNameVoter::class, [$agentBlacklist]),
]);

$container->setDefinition('instrumentation.tracing.ai.agent.'.$name, $definition);
}

foreach ($container->findTaggedServiceIds('ai.toolbox') as $toolboxId => $tags) {
$name = $tags[0]['name'] ?? $toolboxId;

$definition = (new Definition(TracingToolbox::class))
->setDecoratedService($toolboxId, priority: -512)
->setArguments([
new Reference('.inner'),
new Reference(TracerProviderInterface::class),
new Reference(ToolOperationNameResolverInterface::class),
new Reference(ToolAttributeProviderInterface::class),
new Definition(OperationNameVoter::class, [$toolBlacklist]),
]);

$container->setDefinition('instrumentation.tracing.ai.toolbox.'.$name, $definition);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

/*
* This file is part of the worldia/instrumentation-bundle package.
* (c) Worldia <developers@worldia.com>
*/

namespace Instrumentation\DependencyInjection\CompilerPass;

use Instrumentation\Metrics\AI\Platform\MeteringPlatform;
use OpenTelemetry\API\Metrics\MeterProviderInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class AIPlatformMetricsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
foreach ($container->findTaggedServiceIds('ai.platform') as $platformId => $tags) {
$system = $tags[0]['name'] ?? $platformId;

$definition = (new Definition(MeteringPlatform::class))
->setDecoratedService($platformId, priority: -512)
->setArguments([
new Reference('.inner'),
new Reference(MeterProviderInterface::class),
$system,
]);

$container->setDefinition('instrumentation.metrics.ai.platform.'.$system, $definition);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

/*
* This file is part of the worldia/instrumentation-bundle package.
* (c) Worldia <developers@worldia.com>
*/

namespace Instrumentation\DependencyInjection\CompilerPass;

use Instrumentation\Semantics\Attribute\PlatformAttributeProviderInterface;
use Instrumentation\Semantics\OperationName\PlatformOperationNameResolverInterface;
use Instrumentation\Tracing\AI\Platform\TracingPlatform;
use Instrumentation\Tracing\AI\Sampling\OperationNameVoter;
use OpenTelemetry\API\Trace\TracerProviderInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class AIPlatformTracingCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
/** @var array<string> $blacklist */
$blacklist = $container->hasParameter('tracing.ai.platform.blacklist') ? $container->getParameter('tracing.ai.platform.blacklist') : [];

foreach ($container->findTaggedServiceIds('ai.platform') as $platformId => $tags) {
$system = $tags[0]['name'] ?? $platformId;

// Low priority ensures TracingPlatform is the outermost decorator,
// so the span covers the full call including any inner decorators (retry, cache, etc.).
$definition = (new Definition(TracingPlatform::class))
->setDecoratedService($platformId, priority: -512)
->setArguments([
new Reference('.inner'),
new Reference(TracerProviderInterface::class),
new Reference(PlatformOperationNameResolverInterface::class),
new Reference(PlatformAttributeProviderInterface::class),
$system,
new Definition(OperationNameVoter::class, [$blacklist]),
]);

$container->setDefinition('instrumentation.tracing.ai.platform.'.$system, $definition);
}
}
}
42 changes: 42 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,42 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->end()
->end()
->arrayNode('ai')
->addDefaultsIfNotSet()
->children()
->booleanNode('enabled')->defaultFalse()->end()
->arrayNode('platform')
->addDefaultsIfNotSet()
->children()
->arrayNode('blacklist')
->info('Models (regex) to exclude from tracing')
->defaultValue([])
->scalarPrototype()->end()
->end()
->end()
->end()
->arrayNode('agent')
->addDefaultsIfNotSet()
->children()
->arrayNode('blacklist')
->info('Agent names (regex) to exclude from tracing')
->defaultValue([])
->scalarPrototype()->end()
->end()
->end()
->end()
->arrayNode('tool')
->addDefaultsIfNotSet()
->children()
->arrayNode('blacklist')
->info('Tool names (regex) to exclude from tracing')
->defaultValue([])
->scalarPrototype()->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('doctrine')
->addDefaultsIfNotSet()
->children()
Expand Down Expand Up @@ -195,6 +231,12 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->end()
->end()
->arrayNode('ai')
->addDefaultsIfNotSet()
->children()
->booleanNode('enabled')->defaultFalse()->end()
->end()
->end()
->end()
->end()

Expand Down
27 changes: 26 additions & 1 deletion src/DependencyInjection/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@

namespace Instrumentation\DependencyInjection;

use Instrumentation\DependencyInjection\CompilerPass\AIAgentTracingCompilerPass;
use Instrumentation\DependencyInjection\CompilerPass\AIPlatformMetricsCompilerPass;
use Instrumentation\DependencyInjection\CompilerPass\AIPlatformTracingCompilerPass;
use Instrumentation\DependencyInjection\CompilerPass\DoctrineTracingCompilerPass;
use Instrumentation\Metrics\AI\Platform\MeteringPlatform;
use Instrumentation\Tracing\AI\Agent\TracingAgent;
use Instrumentation\Tracing\AI\Platform\TracingPlatform;
use Instrumentation\Tracing\Bridge\TraceUrlGenerator;
use Instrumentation\Tracing\Bridge\TraceUrlGeneratorInterface;
use Instrumentation\Tracing\Request\EventListener\AddUserEventSubscriber;
Expand Down Expand Up @@ -71,6 +77,18 @@ public function prepend(ContainerBuilder $container): void
public function process(ContainerBuilder $container): void
{
(new DoctrineTracingCompilerPass())->process($container);

if ($container->hasDefinition(TracingPlatform::class)) {
(new AIPlatformTracingCompilerPass())->process($container);
}

if ($container->hasDefinition(TracingAgent::class)) {
(new AIAgentTracingCompilerPass())->process($container);
}

if ($container->hasDefinition(MeteringPlatform::class)) {
(new AIPlatformMetricsCompilerPass())->process($container);
}
}

/**
Expand Down Expand Up @@ -126,7 +144,7 @@ protected function loadTracing(array $config, ContainerBuilder $container): void

$config['doctrine']['enabled'] = $config['doctrine']['instrumentation'] || $config['doctrine']['propagation'];

foreach (['request', 'command', 'message', 'doctrine'] as $feature) {
foreach (['request', 'command', 'message', 'ai', 'doctrine'] as $feature) {
if (!$this->isConfigEnabled($container, $config[$feature])) {
continue;
}
Expand All @@ -140,6 +158,10 @@ protected function loadTracing(array $config, ContainerBuilder $container): void
}
}

$container->setParameter('tracing.ai.platform.blacklist', $config['ai']['platform']['blacklist']);
$container->setParameter('tracing.ai.agent.blacklist', $config['ai']['agent']['blacklist']);
$container->setParameter('tracing.ai.tool.blacklist', $config['ai']['tool']['blacklist']);

$container->setParameter('tracing.doctrine.connections', $config['doctrine']['connections']);
$container->setParameter('tracing.doctrine.log_queries', $config['doctrine']['log_queries']);
$container->setParameter('tracing.doctrine.propagation', $config['doctrine']['propagation']);
Expand Down Expand Up @@ -182,6 +204,9 @@ protected function loadMetrics(array $config, ContainerBuilder $container): void
$container->setParameter('metrics.request.blacklist', $config['request']['blacklist']);
$loader->load('request.php');
}
if ($this->isConfigEnabled($container, $config['ai'])) {
$loader->load('ai.php');
}
}

private function getLoader(string $component, ContainerBuilder $container): PhpFileLoader
Expand Down
17 changes: 17 additions & 0 deletions src/DependencyInjection/config/metrics/ai.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

/*
* This file is part of the worldia/instrumentation-bundle package.
* (c) Worldia <developers@worldia.com>
*/

use Instrumentation\Metrics\AI\Platform\MeteringPlatform;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $container) {
$container->services()
->set(MeteringPlatform::class)
->abstract();
};
18 changes: 18 additions & 0 deletions src/DependencyInjection/config/semconv/semconv.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,39 @@
* (c) Worldia <developers@worldia.com>
*/

use Instrumentation\Semantics\Attribute\AgentAttributeProvider;
use Instrumentation\Semantics\Attribute\AgentAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\ClientRequestAttributeProvider;
use Instrumentation\Semantics\Attribute\ClientRequestAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\DoctrineConnectionAttributeProvider;
use Instrumentation\Semantics\Attribute\DoctrineConnectionAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\MessageAttributeProvider;
use Instrumentation\Semantics\Attribute\MessageAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\PlatformAttributeProvider;
use Instrumentation\Semantics\Attribute\PlatformAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\ServerRequestAttributeProvider;
use Instrumentation\Semantics\Attribute\ServerRequestAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\ServerResponseAttributeProvider;
use Instrumentation\Semantics\Attribute\ServerResponseAttributeProviderInterface;
use Instrumentation\Semantics\Attribute\ToolAttributeProvider;
use Instrumentation\Semantics\Attribute\ToolAttributeProviderInterface;
use Instrumentation\Semantics\OperationName\AgentOperationNameResolver;
use Instrumentation\Semantics\OperationName\AgentOperationNameResolverInterface;
use Instrumentation\Semantics\OperationName\ClientRequestOperationNameResolver;
use Instrumentation\Semantics\OperationName\ClientRequestOperationNameResolverInterface;
use Instrumentation\Semantics\OperationName\CommandOperationNameResolver;
use Instrumentation\Semantics\OperationName\CommandOperationNameResolverInterface;
use Instrumentation\Semantics\OperationName\MessageOperationNameResolver;
use Instrumentation\Semantics\OperationName\MessageOperationNameResolverInterface;
use Instrumentation\Semantics\OperationName\PlatformOperationNameResolver;
use Instrumentation\Semantics\OperationName\PlatformOperationNameResolverInterface;
use Instrumentation\Semantics\OperationName\RoutePath\RouteCacheWarmer;
use Instrumentation\Semantics\OperationName\RoutePath\RoutePathResolver;
use Instrumentation\Semantics\OperationName\RoutePath\RoutePathResolverInterface;
use Instrumentation\Semantics\OperationName\RoutePathServerRequestOperationNameResolver;
use Instrumentation\Semantics\OperationName\ServerRequestOperationNameResolverInterface;
use Instrumentation\Semantics\OperationName\ToolOperationNameResolver;
use Instrumentation\Semantics\OperationName\ToolOperationNameResolverInterface;
use Instrumentation\Semantics\ResourceInfoProvider;
use Instrumentation\Semantics\ResourceInfoProviderInterface;
use OpenTelemetry\SDK\Resource\ResourceInfo;
Expand Down Expand Up @@ -59,10 +71,16 @@
param('tracing.http.attributes.request_headers'),
])
->set(DoctrineConnectionAttributeProviderInterface::class, DoctrineConnectionAttributeProvider::class)
->set(PlatformAttributeProviderInterface::class, PlatformAttributeProvider::class)
->set(AgentAttributeProviderInterface::class, AgentAttributeProvider::class)
->set(ToolAttributeProviderInterface::class, ToolAttributeProvider::class)

->set(ClientRequestOperationNameResolverInterface::class, ClientRequestOperationNameResolver::class)
->set(MessageOperationNameResolverInterface::class, MessageOperationNameResolver::class)
->set(CommandOperationNameResolverInterface::class, CommandOperationNameResolver::class)
->set(PlatformOperationNameResolverInterface::class, PlatformOperationNameResolver::class)
->set(AgentOperationNameResolverInterface::class, AgentOperationNameResolver::class)
->set(ToolOperationNameResolverInterface::class, ToolOperationNameResolver::class)
->set(ServerRequestOperationNameResolverInterface::class, RoutePathServerRequestOperationNameResolver::class)
->args([
service(RoutePathResolverInterface::class),
Expand Down
23 changes: 23 additions & 0 deletions src/DependencyInjection/config/tracing/ai.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

/*
* This file is part of the worldia/instrumentation-bundle package.
* (c) Worldia <developers@worldia.com>
*/

use Instrumentation\Tracing\AI\Agent\TracingAgent;
use Instrumentation\Tracing\AI\Platform\TracingPlatform;
use Instrumentation\Tracing\AI\Toolbox\TracingToolbox;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $container) {
$container->services()
->set(TracingPlatform::class)
->abstract()
->set(TracingAgent::class)
->abstract()
->set(TracingToolbox::class)
->abstract();
};
Loading
Loading