Skip to content

Commit 8c1ca00

Browse files
authored
Fix OpenAIEmbeddingGenerator to handle missing usage data (#7074)
1 parent 7c6f384 commit 8c1ca00

File tree

2 files changed

+94
-5
lines changed

2 files changed

+94
-5
lines changed

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIEmbeddingGenerator.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,22 @@ public async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(IEnumerab
6868
_embeddingClient.GenerateEmbeddingsAsync(values, openAIOptions, cancellationToken);
6969
var embeddings = (await t.ConfigureAwait(false)).Value;
7070

71+
UsageDetails? usage = embeddings.Usage is not null ?
72+
new()
73+
{
74+
InputTokenCount = embeddings.Usage.InputTokenCount,
75+
TotalTokenCount = embeddings.Usage.TotalTokenCount
76+
} :
77+
null;
78+
7179
return new(embeddings.Select(e =>
7280
new Embedding<float>(e.ToFloats())
7381
{
7482
CreatedAt = DateTimeOffset.UtcNow,
7583
ModelId = embeddings.Model,
7684
}))
7785
{
78-
Usage = new()
79-
{
80-
InputTokenCount = embeddings.Usage.InputTokenCount,
81-
TotalTokenCount = embeddings.Usage.TotalTokenCount
82-
},
86+
Usage = usage,
8387
};
8488
}
8589

test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIEmbeddingGeneratorTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,91 @@ public async Task EmbeddingGenerationOptions_DoNotOverwrite_NotNullPropertiesInR
216216
}
217217
}
218218

219+
[Fact]
220+
public async Task EmbeddingGenerationOptions_MissingUsage_Ignored()
221+
{
222+
const string Input = """
223+
{
224+
"input":["hello, world!"],
225+
"model":"text-embedding-3-small",
226+
"encoding_format":"base64"
227+
}
228+
""";
229+
230+
const string Output = """
231+
{
232+
"object": "list",
233+
"data": [
234+
{
235+
"object": "embedding",
236+
"index": 0,
237+
"embedding": "AAAAAA=="
238+
}
239+
],
240+
"model": "text-embedding-3-small"
241+
}
242+
""";
243+
244+
using VerbatimHttpHandler handler = new(Input, Output);
245+
using HttpClient httpClient = new(handler);
246+
using IEmbeddingGenerator<string, Embedding<float>> generator = CreateEmbeddingGenerator(httpClient, "text-embedding-3-small");
247+
248+
var response = await generator.GenerateAsync(["hello, world!"]);
249+
Assert.NotNull(response);
250+
Assert.Single(response);
251+
Assert.Null(response.Usage);
252+
253+
foreach (Embedding<float> e in response)
254+
{
255+
Assert.Equal("text-embedding-3-small", e.ModelId);
256+
Assert.NotNull(e.CreatedAt);
257+
Assert.Equal(1, e.Vector.Length);
258+
}
259+
}
260+
261+
[Fact]
262+
public async Task EmbeddingGenerationOptions_NullUsage_Ignored()
263+
{
264+
const string Input = """
265+
{
266+
"input":["hello, world!"],
267+
"model":"text-embedding-3-small",
268+
"encoding_format":"base64"
269+
}
270+
""";
271+
272+
const string Output = """
273+
{
274+
"object": "list",
275+
"data": [
276+
{
277+
"object": "embedding",
278+
"index": 0,
279+
"embedding": "AAAAAA=="
280+
}
281+
],
282+
"model": "text-embedding-3-small",
283+
"usage": null
284+
}
285+
""";
286+
287+
using VerbatimHttpHandler handler = new(Input, Output);
288+
using HttpClient httpClient = new(handler);
289+
using IEmbeddingGenerator<string, Embedding<float>> generator = CreateEmbeddingGenerator(httpClient, "text-embedding-3-small");
290+
291+
var response = await generator.GenerateAsync(["hello, world!"]);
292+
Assert.NotNull(response);
293+
Assert.Single(response);
294+
Assert.Null(response.Usage);
295+
296+
foreach (Embedding<float> e in response)
297+
{
298+
Assert.Equal("text-embedding-3-small", e.ModelId);
299+
Assert.NotNull(e.CreatedAt);
300+
Assert.Equal(1, e.Vector.Length);
301+
}
302+
}
303+
219304
[Fact]
220305
public async Task RequestHeaders_UserAgent_ContainsMEAI()
221306
{

0 commit comments

Comments
 (0)