Skip to content

Commit 6fb5e13

Browse files
committed
feature #329 [AI Bundle] Activate token usage output processors (junaidbinfarooq)
This PR was merged into the main branch. Discussion ---------- [AI Bundle] Activate token usage output processors | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Docs? | no | Issues | Fix #208 Continues #311 | License | MIT # Changes proposed: - Adds support to automatically configure the token usage processors so that they are available to end users (devs) when needed - Makes the availability of token usage configurable and enabled by default Commits ------- b426948 feat(ai-bundle): Auto-configure token usage processors - Adds support to automatically configure the token usage processors so that they are available to end users (devs) when needed - Makes the availability of token usage configurable and disabled by default
2 parents b13250f + b426948 commit 6fb5e13

File tree

16 files changed

+163
-18
lines changed

16 files changed

+163
-18
lines changed

examples/bootstrap.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
use Psr\Log\LoggerAwareInterface;
1313
use Psr\Log\LoggerInterface;
14+
use Symfony\AI\Platform\Metadata\Metadata;
15+
use Symfony\AI\Platform\Metadata\TokenUsage;
1416
use Symfony\Component\Console\Logger\ConsoleLogger;
1517
use Symfony\Component\Console\Output\ConsoleOutput;
1618
use Symfony\Component\Dotenv\Dotenv;
@@ -52,3 +54,19 @@ function logger(): LoggerInterface
5254

5355
return new ConsoleLogger(new ConsoleOutput($verbosity));
5456
}
57+
58+
function print_token_usage(Metadata $metadata): void
59+
{
60+
$tokenUsage = $metadata->get('token_usage');
61+
62+
assert($tokenUsage instanceof TokenUsage);
63+
64+
echo 'Prompt tokens: '.$tokenUsage->promptTokens.\PHP_EOL;
65+
echo 'Completion tokens: '.$tokenUsage->completionTokens.\PHP_EOL;
66+
echo 'Thinking tokens: '.$tokenUsage->thinkingTokens.\PHP_EOL;
67+
echo 'Cached tokens: '.$tokenUsage->cachedTokens.\PHP_EOL;
68+
echo 'Remaining tokens minute: '.$tokenUsage->remainingTokensMinute.\PHP_EOL;
69+
echo 'Remaining tokens month: '.$tokenUsage->remainingTokensMonth.\PHP_EOL;
70+
echo 'Remaining tokens: '.$tokenUsage->remainingTokens.\PHP_EOL;
71+
echo 'Utilized tokens: '.$tokenUsage->totalTokens.\PHP_EOL;
72+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\AI\Agent\Agent;
13+
use Symfony\AI\Platform\Bridge\Mistral\Mistral;
14+
use Symfony\AI\Platform\Bridge\Mistral\PlatformFactory;
15+
use Symfony\AI\Platform\Bridge\Mistral\TokenOutputProcessor;
16+
use Symfony\AI\Platform\Message\Message;
17+
use Symfony\AI\Platform\Message\MessageBag;
18+
19+
require_once dirname(__DIR__).'/bootstrap.php';
20+
21+
$platform = PlatformFactory::create(env('MISTRAL_API_KEY'), http_client());
22+
$model = new Mistral();
23+
24+
$agent = new Agent($platform, $model, outputProcessors: [new TokenOutputProcessor()], logger: logger());
25+
26+
$messages = new MessageBag(
27+
Message::forSystem('You are a pirate and you write funny.'),
28+
Message::ofUser('What is the best French cuisine?'),
29+
);
30+
31+
$result = $agent->call($messages, [
32+
'temperature' => 0.3,
33+
'max_tokens' => 500,
34+
]);
35+
36+
print_token_usage($result->getMetadata());

examples/openai/token-metadata.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Symfony\AI\Platform\Bridge\OpenAi\TokenOutputProcessor;
1616
use Symfony\AI\Platform\Message\Message;
1717
use Symfony\AI\Platform\Message\MessageBag;
18-
use Symfony\AI\Platform\Result\Metadata\TokenUsage;
1918

2019
require_once dirname(__DIR__).'/bootstrap.php';
2120

@@ -33,12 +32,4 @@
3332
'max_tokens' => 500, // specific options just for this call
3433
]);
3534

36-
$metadata = $result->getMetadata();
37-
$tokenUsage = $metadata->get('token_usage');
38-
39-
assert($tokenUsage instanceof TokenUsage);
40-
41-
echo 'Utilized Tokens: '.$tokenUsage->totalTokens.\PHP_EOL;
42-
echo '-- Prompt Tokens: '.$tokenUsage->promptTokens.\PHP_EOL;
43-
echo '-- Completion Tokens: '.$tokenUsage->completionTokens.\PHP_EOL;
44-
echo 'Remaining Tokens: '.$tokenUsage->remainingTokens.\PHP_EOL;
35+
print_token_usage($result->getMetadata());

src/ai-bundle/CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,9 @@ CHANGELOG
2525
- Factory services for creating platforms
2626
* Add bundle configuration with semantic validation
2727
* Add support for fault-tolerant tool execution
28-
* Add structured output configuration support
28+
* Add structured output configuration support
29+
* Add token usage tracking:
30+
- `track_token_usage` option for agents to monitor AI model consumption
31+
- Automatic registration of token output processors for Mistral, OpenAI and Vertex AI
32+
- Token usage metadata in agent results including prompt, completion, total, cached, and thinking tokens
33+
- Rate limit information tracking for supported platforms

src/ai-bundle/config/options.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@
103103
->info('Service name of platform')
104104
->defaultValue(PlatformInterface::class)
105105
->end()
106+
->booleanNode('track_token_usage')
107+
->info('Enable tracking of token usage for the agent')
108+
->defaultTrue()
109+
->end()
106110
->arrayNode('model')
107111
->children()
108112
->scalarNode('class')->isRequired()->end()

src/ai-bundle/config/services.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@
2626
use Symfony\AI\AiBundle\Security\EventListener\IsGrantedToolAttributeListener;
2727
use Symfony\AI\Platform\Bridge\Anthropic\Contract\AnthropicContract;
2828
use Symfony\AI\Platform\Bridge\Gemini\Contract\GeminiContract;
29+
use Symfony\AI\Platform\Bridge\Mistral\TokenOutputProcessor as MistralTokenOutputProcessor;
2930
use Symfony\AI\Platform\Bridge\Ollama\Contract\OllamaContract;
3031
use Symfony\AI\Platform\Bridge\OpenAi\Contract\OpenAiContract;
32+
use Symfony\AI\Platform\Bridge\OpenAi\TokenOutputProcessor as OpenAiTokenOutputProcessor;
3133
use Symfony\AI\Platform\Bridge\VertexAi\Contract\GeminiContract as VertexAiGeminiContract;
34+
use Symfony\AI\Platform\Bridge\VertexAi\TokenOutputProcessor as VertexAiTokenOutputProcessor;
3235
use Symfony\AI\Platform\Contract;
3336
use Symfony\AI\Platform\Contract\JsonSchema\DescriptionParser;
3437
use Symfony\AI\Platform\Contract\JsonSchema\Factory as SchemaFactory;
@@ -131,5 +134,10 @@
131134
service('.inner'),
132135
])
133136
->tag('ai.traceable_toolbox')
137+
138+
// token usage processors
139+
->set('ai.platform.token_usage_processor.mistral', MistralTokenOutputProcessor::class)
140+
->set('ai.platform.token_usage_processor.openai', OpenAiTokenOutputProcessor::class)
141+
->set('ai.platform.token_usage_processor.vertexai', VertexAiTokenOutputProcessor::class)
134142
;
135143
};

src/ai-bundle/doc/index.rst

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Configuration
6565
rag:
6666
platform: 'ai.platform.azure.gpt_deployment'
6767
structured_output: false # Disables support for "output_structure" option, default is true
68+
track_token_usage: true # Enable tracking of token usage for the agent, default is true
6869
model:
6970
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
7071
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
@@ -230,6 +231,66 @@ The attribute ``IsGrantedTool`` can be added on class- or method-level - even mu
230231
times. If multiple attributes apply to one tool call, a logical AND is used and all access
231232
decisions have to grant access.
232233

234+
Token Usage Tracking
235+
--------------------
236+
237+
Token usage tracking is a feature provided by some of the Platform's bridges, for monitoring and analyzing the
238+
consumption of tokens by your agents. This feature is particularly useful for understanding costs and performance.
239+
240+
When enabled, the agent will automatically track token usage information and add it
241+
to the result metadata. The tracked information includes:
242+
243+
* **Prompt tokens**: Number of tokens used in the input/prompt
244+
* **Completion tokens**: Number of tokens generated in the response
245+
* **Total tokens**: Total number of tokens used (prompt + completion)
246+
* **Remaining tokens**: Number of remaining tokens in rate limits (when available)
247+
* **Cached tokens**: Number of cached tokens used (when available)
248+
* **Thinking tokens**: Number of reasoning tokens used (for models that support reasoning)
249+
250+
The token usage information can be accessed from the result metadata::
251+
252+
use Symfony\AI\Agent\AgentInterface;
253+
use Symfony\AI\Platform\Message\Message;
254+
use Symfony\AI\Platform\Message\MessageBag;
255+
use Symfony\AI\Platform\Result\Metadata\TokenUsage\TokenUsage;
256+
257+
final readonly class MyService
258+
{
259+
public function __construct(
260+
private AgentInterface $agent,
261+
) {
262+
}
263+
264+
public function getTokenUsage(string $message): ?TokenUsage
265+
{
266+
$messages = new MessageBag(Message::ofUser($message));
267+
$result = $this->agent->call($messages);
268+
269+
return $result->getMetadata()->get('token_usage');
270+
}
271+
}
272+
273+
**Supported Platforms**
274+
275+
Token usage tracking is currently supported, and by default enabled, for the following platforms:
276+
277+
* **OpenAI**: Tracks all token types including cached and thinking tokens
278+
* **Mistral**: Tracks basic token usage and rate limit information
279+
280+
**Disable Tracking**
281+
282+
To disable token usage tracking for an agent, set the ``track_token_usage`` option to ``false``:
283+
284+
.. code-block:: yaml
285+
286+
ai:
287+
agent:
288+
my_agent:
289+
track_token_usage: false
290+
model:
291+
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
292+
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
293+
233294
Profiler
234295
--------
235296

src/ai-bundle/src/AiBundle.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,27 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
514514
$outputProcessors[] = new Reference('ai.agent.structured_output_processor');
515515
}
516516

517+
// TOKEN USAGE TRACKING
518+
if ($config['track_token_usage'] ?? true) {
519+
$platformServiceId = $config['platform'];
520+
521+
if ($container->hasAlias($platformServiceId)) {
522+
$platformServiceId = (string) $container->getAlias($platformServiceId);
523+
}
524+
525+
if (str_starts_with($platformServiceId, 'ai.platform.')) {
526+
$platform = u($platformServiceId)->after('ai.platform.')->toString();
527+
528+
if (str_contains($platform, 'azure')) {
529+
$platform = 'azure';
530+
}
531+
532+
if ($container->hasDefinition('ai.platform.token_usage_processor.'.$platform)) {
533+
$outputProcessors[] = new Reference('ai.platform.token_usage_processor.'.$platform);
534+
}
535+
}
536+
}
537+
517538
// SYSTEM PROMPT
518539
if (\is_string($config['system_prompt'])) {
519540
$systemPromptInputProcessorDefinition = new Definition(SystemPromptInputProcessor::class, [

src/ai-bundle/tests/DependencyInjection/AiBundleTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ private function getFullConfig(): array
344344
],
345345
],
346346
'structured_output' => false,
347+
'track_token_usage' => true,
347348
'system_prompt' => 'You are a helpful assistant.',
348349
'include_tools' => true,
349350
'tools' => [

src/platform/src/Bridge/Mistral/TokenOutputProcessor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
use Symfony\AI\Agent\Output;
1515
use Symfony\AI\Agent\OutputProcessorInterface;
16-
use Symfony\AI\Platform\Result\Metadata\TokenUsage;
16+
use Symfony\AI\Platform\Metadata\TokenUsage;
1717
use Symfony\AI\Platform\Result\StreamResult;
1818
use Symfony\Contracts\HttpClient\ResponseInterface;
1919

0 commit comments

Comments
 (0)