Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9e206f8

Browse files
Amoifrchr-hertel
authored andcommitted
[Platform] Add token usage extraction for embeddings responses
Add TokenUsageExtractor for embeddings in OpenAi, Generic, Scaleway, DockerModelRunner and VertexAi bridges. The extractors parse token usage information from embedding API responses and return a TokenUsage object with prompt and total token counts. VertexAi uses a different response format with per-embedding statistics, so the extractor sums up token_count from all predictions.
1 parent 2f80662 commit 9e206f8

4 files changed

Lines changed: 101 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
0.7
55
---
66

7+
* Add token usage extraction for embeddings responses
78
* [BC BREAK] OpenAI-compatible completion streams now yield `TextDelta`, `ThinkingDelta`, `ThinkingComplete`, `ToolCallStart`, `ToolInputDelta`, `ToolCallComplete`, and streamed `TokenUsage` deltas
89

910
0.4

Embeddings/ResultConverter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ public function convert(RawResultInterface $result, array $options = []): Vector
6868
);
6969
}
7070

71-
public function getTokenUsageExtractor(): ?TokenUsageExtractorInterface
71+
public function getTokenUsageExtractor(): TokenUsageExtractorInterface
7272
{
73-
return null;
73+
return new TokenUsageExtractor();
7474
}
7575
}

Embeddings/TokenUsageExtractor.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
namespace Symfony\AI\Platform\Bridge\Generic\Embeddings;
13+
14+
use Symfony\AI\Platform\Result\RawResultInterface;
15+
use Symfony\AI\Platform\TokenUsage\TokenUsage;
16+
use Symfony\AI\Platform\TokenUsage\TokenUsageExtractorInterface;
17+
use Symfony\AI\Platform\TokenUsage\TokenUsageInterface;
18+
19+
/**
20+
* @author Pascal Cescon <[email protected]>
21+
*/
22+
final class TokenUsageExtractor implements TokenUsageExtractorInterface
23+
{
24+
public function extract(RawResultInterface $rawResult, array $options = []): ?TokenUsageInterface
25+
{
26+
$content = $rawResult->getData();
27+
28+
if (!\array_key_exists('usage', $content)) {
29+
return null;
30+
}
31+
32+
return new TokenUsage(
33+
promptTokens: $content['usage']['prompt_tokens'] ?? null,
34+
totalTokens: $content['usage']['total_tokens'] ?? null,
35+
);
36+
}
37+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
namespace Symfony\AI\Platform\Bridge\Generic\Tests\Embeddings;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\AI\Platform\Bridge\Generic\Embeddings\TokenUsageExtractor;
16+
use Symfony\AI\Platform\Result\InMemoryRawResult;
17+
use Symfony\AI\Platform\TokenUsage\TokenUsage;
18+
19+
final class TokenUsageExtractorTest extends TestCase
20+
{
21+
public function testItReturnsNullWithoutUsageData()
22+
{
23+
$extractor = new TokenUsageExtractor();
24+
25+
$this->assertNull($extractor->extract(new InMemoryRawResult(['some' => 'data'])));
26+
}
27+
28+
public function testItExtractsTokenUsage()
29+
{
30+
$extractor = new TokenUsageExtractor();
31+
$result = new InMemoryRawResult([
32+
'usage' => [
33+
'prompt_tokens' => 20,
34+
'total_tokens' => 20,
35+
],
36+
]);
37+
38+
$tokenUsage = $extractor->extract($result);
39+
40+
$this->assertInstanceOf(TokenUsage::class, $tokenUsage);
41+
$this->assertSame(20, $tokenUsage->getPromptTokens());
42+
$this->assertNull($tokenUsage->getCompletionTokens());
43+
$this->assertSame(20, $tokenUsage->getTotalTokens());
44+
}
45+
46+
public function testItHandlesMissingUsageFields()
47+
{
48+
$extractor = new TokenUsageExtractor();
49+
$result = new InMemoryRawResult([
50+
'usage' => [
51+
'prompt_tokens' => 5,
52+
],
53+
]);
54+
55+
$tokenUsage = $extractor->extract($result);
56+
57+
$this->assertInstanceOf(TokenUsage::class, $tokenUsage);
58+
$this->assertSame(5, $tokenUsage->getPromptTokens());
59+
$this->assertNull($tokenUsage->getTotalTokens());
60+
}
61+
}

0 commit comments

Comments
 (0)