diff --git a/sdk/typescript/src/index.ts b/sdk/typescript/src/index.ts index fc8383f3..3a4f3491 100644 --- a/sdk/typescript/src/index.ts +++ b/sdk/typescript/src/index.ts @@ -9,6 +9,7 @@ export type { ItemCompletedEvent, ThreadError, ThreadErrorEvent, + Usage, } from "./events"; export type { ThreadItem, diff --git a/sdk/typescript/src/thread.ts b/sdk/typescript/src/thread.ts index b710fec3..b31c2c51 100644 --- a/sdk/typescript/src/thread.ts +++ b/sdk/typescript/src/thread.ts @@ -1,5 +1,5 @@ import { CodexOptions } from "./codexOptions"; -import { ThreadEvent } from "./events"; +import { ThreadEvent, Usage } from "./events"; import { CodexExec } from "./exec"; import { ThreadItem } from "./items"; import { ThreadOptions } from "./threadOptions"; @@ -8,6 +8,7 @@ import { ThreadOptions } from "./threadOptions"; export type Turn = { items: ThreadItem[]; finalResponse: string; + usage: Usage | null; }; /** Alias for `Turn` to describe the result of `run()`. */ @@ -85,14 +86,17 @@ export class Thread { const generator = this.runStreamedInternal(input); const items: ThreadItem[] = []; let finalResponse: string = ""; + let usage: Usage | null = null; for await (const event of generator) { if (event.type === "item.completed") { if (event.item.type === "agent_message") { finalResponse = event.item.text; } items.push(event.item); + } else if (event.type === "turn.completed") { + usage = event.usage; } } - return { items, finalResponse }; + return { items, finalResponse, usage }; } } diff --git a/sdk/typescript/tests/responsesProxy.ts b/sdk/typescript/tests/responsesProxy.ts index 7fcdc9c7..9ea9b31c 100644 --- a/sdk/typescript/tests/responsesProxy.ts +++ b/sdk/typescript/tests/responsesProxy.ts @@ -3,6 +3,22 @@ import http from "node:http"; const DEFAULT_RESPONSE_ID = "resp_mock"; const DEFAULT_MESSAGE_ID = "msg_mock"; +type ResponseCompletedUsage = { + input_tokens: number; + input_tokens_details: { cached_tokens: number } | null; + output_tokens: number; + output_tokens_details: { reasoning_tokens: number } | null; + total_tokens: number; +}; + +const DEFAULT_COMPLETED_USAGE: ResponseCompletedUsage = { + input_tokens: 42, + input_tokens_details: { cached_tokens: 12 }, + output_tokens: 5, + output_tokens_details: null, + total_tokens: 47, +}; + type SseEvent = { type: string; [key: string]: unknown; @@ -157,17 +173,26 @@ export function assistantMessage(text: string, itemId: string = DEFAULT_MESSAGE_ }; } -export function responseCompleted(responseId: string = DEFAULT_RESPONSE_ID): SseEvent { +export function responseCompleted( + responseId: string = DEFAULT_RESPONSE_ID, + usage: ResponseCompletedUsage = DEFAULT_COMPLETED_USAGE, +): SseEvent { + const inputDetails = usage.input_tokens_details + ? { ...usage.input_tokens_details } + : null; + const outputDetails = usage.output_tokens_details + ? { ...usage.output_tokens_details } + : null; return { type: "response.completed", response: { id: responseId, usage: { - input_tokens: 0, - input_tokens_details: null, - output_tokens: 0, - output_tokens_details: null, - total_tokens: 0, + input_tokens: usage.input_tokens, + input_tokens_details: inputDetails, + output_tokens: usage.output_tokens, + output_tokens_details: outputDetails, + total_tokens: usage.total_tokens, }, }, }; diff --git a/sdk/typescript/tests/run.test.ts b/sdk/typescript/tests/run.test.ts index 17cdae09..6b577a0b 100644 --- a/sdk/typescript/tests/run.test.ts +++ b/sdk/typescript/tests/run.test.ts @@ -38,6 +38,11 @@ describe("Codex", () => { }, ]; expect(result.items).toEqual(expectedItems); + expect(result.usage).toEqual({ + cached_input_tokens: 12, + input_tokens: 42, + output_tokens: 5, + }); expect(thread.id).toEqual(expect.any(String)); } finally { await close(); diff --git a/sdk/typescript/tests/runStreamed.test.ts b/sdk/typescript/tests/runStreamed.test.ts index f95203ae..f0c4d90b 100644 --- a/sdk/typescript/tests/runStreamed.test.ts +++ b/sdk/typescript/tests/runStreamed.test.ts @@ -52,9 +52,9 @@ describe("Codex", () => { { type: "turn.completed", usage: { - cached_input_tokens: 0, - input_tokens: 0, - output_tokens: 0, + cached_input_tokens: 12, + input_tokens: 42, + output_tokens: 5, }, }, ]);