Skip to content

Commit d93c8e1

Browse files
jgpruittclaude
andcommitted
feat(embedding): surface token usage from AI SDK
Wire up usage.tokens from embed/embedMany return values so callers can track actual token consumption for billing and observability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 270b544 commit d93c8e1

4 files changed

Lines changed: 28 additions & 9 deletions

File tree

packages/embedding/generate.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ const ollamaConfig: EmbeddingConfig = {
2222

2323
describe.skipIf(!RUN_INTEGRATION)("embedding integration (ollama)", () => {
2424
test("generateEmbedding returns correct dimensions", async () => {
25-
const embedding = await generateEmbedding("test text", ollamaConfig);
25+
const result = await generateEmbedding("test text", ollamaConfig);
2626

27-
expect(embedding).toBeInstanceOf(Array);
28-
expect(embedding.length).toBe(768);
29-
expect(typeof embedding[0]).toBe("number");
27+
expect(result.embedding).toBeInstanceOf(Array);
28+
expect(result.embedding.length).toBe(768);
29+
expect(typeof result.embedding[0]).toBe("number");
30+
expect(result.tokens).toBeGreaterThan(0);
3031
});
3132

3233
test("generateEmbedding handles long text with truncation", async () => {
@@ -36,8 +37,9 @@ describe.skipIf(!RUN_INTEGRATION)("embedding integration (ollama)", () => {
3637
options: { maxTokens: 8000 },
3738
};
3839

39-
const embedding = await generateEmbedding(longText, configWithTruncation);
40-
expect(embedding.length).toBe(768);
40+
const result = await generateEmbedding(longText, configWithTruncation);
41+
expect(result.embedding.length).toBe(768);
42+
expect(result.tokens).toBeGreaterThan(0);
4143
});
4244

4345
test("generateEmbeddings returns results for batch", async () => {

packages/embedding/generate.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ function prepareText(text: string, config: EmbeddingConfig): string {
4848
// Single Embedding
4949
// =============================================================================
5050

51+
export interface SingleEmbedResult {
52+
embedding: number[];
53+
/** Tokens consumed by the embedding API call */
54+
tokens: number;
55+
}
56+
5157
/**
5258
* Generate a single embedding for text.
5359
*
@@ -58,7 +64,7 @@ function prepareText(text: string, config: EmbeddingConfig): string {
5864
export async function generateEmbedding(
5965
text: string,
6066
config: EmbeddingConfig,
61-
): Promise<number[]> {
67+
): Promise<SingleEmbedResult> {
6268
const model = getEmbeddingModel(config);
6369
const preparedText = prepareText(text, config);
6470

@@ -74,7 +80,10 @@ export async function generateEmbedding(
7480
);
7581
}
7682

77-
return result.embedding;
83+
return {
84+
embedding: result.embedding,
85+
tokens: result.usage.tokens,
86+
};
7887
}
7988

8089
// =============================================================================
@@ -107,12 +116,15 @@ export async function generateEmbeddings(
107116

108117
try {
109118
// Try batch API first
110-
const { embeddings } = await embedMany({
119+
const { embeddings, usage } = await embedMany({
111120
model,
112121
values: texts,
113122
...getEmbedOptions(config),
114123
});
115124

125+
// embedMany returns aggregate token count — distribute evenly
126+
const tokensPerRow = Math.floor(usage.tokens / rows.length);
127+
116128
for (let i = 0; i < rows.length; i++) {
117129
const embedding = embeddings[i];
118130
const row = rows[i];
@@ -128,6 +140,7 @@ export async function generateEmbeddings(
128140
results.push({
129141
id: row.id,
130142
embedding,
143+
tokens: tokensPerRow,
131144
});
132145
}
133146
}
@@ -155,6 +168,7 @@ export async function generateEmbeddings(
155168
results.push({
156169
id: row.id,
157170
embedding: result.embedding,
171+
tokens: result.usage.tokens,
158172
});
159173
}
160174
} catch (err) {

packages/embedding/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export {
2020
generateEmbedding,
2121
generateEmbeddings,
2222
validateConfig,
23+
type SingleEmbedResult,
2324
} from "./generate";
2425

2526
// =============================================================================

packages/embedding/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export interface MemoryRow {
5151
export interface EmbedResult {
5252
id: string;
5353
embedding: number[];
54+
/** Tokens consumed by the embedding API call */
55+
tokens?: number;
5456
error?: string;
5557
}
5658

0 commit comments

Comments
 (0)