Skip to content

Commit ece3665

Browse files
committed
Add completion_tokens_details with reasoning_tokens to OpenAi Usage
1 parent c67442d commit ece3665

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,12 +929,28 @@ public record TopLogProbs(// @formatter:off
929929
* @param promptTokens Number of tokens in the prompt.
930930
* @param totalTokens Total number of tokens used in the request (prompt +
931931
* completion).
932+
* @param completionTokenDetails Breakdown of tokens used in a completion
932933
*/
933934
@JsonInclude(Include.NON_NULL)
934935
public record Usage(// @formatter:off
935936
@JsonProperty("completion_tokens") Integer completionTokens,
936937
@JsonProperty("prompt_tokens") Integer promptTokens,
937-
@JsonProperty("total_tokens") Integer totalTokens) {// @formatter:on
938+
@JsonProperty("total_tokens") Integer totalTokens,
939+
@JsonProperty("completion_tokens_details") CompletionTokenDetails completionTokenDetails) {// @formatter:on
940+
941+
public Usage(Integer completionTokens, Integer promptTokens, Integer totalTokens) {
942+
this(completionTokens, promptTokens, totalTokens, null);
943+
}
944+
945+
/**
946+
* Breakdown of tokens used in a completion
947+
*
948+
* @param reasoningTokens Number of tokens generated by the model for reasoning.
949+
*/
950+
@JsonInclude(Include.NON_NULL)
951+
public record CompletionTokenDetails(// @formatter:off
952+
@JsonProperty("reasoning_tokens") Integer reasoningTokens) {// @formatter:on
953+
}
938954

939955
}
940956

models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/OpenAiUsage.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ public Long getGenerationTokens() {
5858
return generationTokens != null ? generationTokens.longValue() : 0;
5959
}
6060

61+
public Long getReasoningTokens() {
62+
OpenAiApi.Usage.CompletionTokenDetails completionTokenDetails = getUsage().completionTokenDetails();
63+
Integer reasoningTokens = completionTokenDetails != null ? completionTokenDetails.reasoningTokens() : null;
64+
return reasoningTokens != null ? reasoningTokens.longValue() : 0;
65+
}
66+
6167
@Override
6268
public Long getTotalTokens() {
6369
Integer totalTokens = getUsage().totalTokens();

models/spring-ai-openai/src/test/java/org/springframework/ai/openai/metadata/OpenAiUsageTests.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,28 @@ void whenTotalTokensIsNull() {
5454
assertThat(usage.getTotalTokens()).isEqualTo(300);
5555
}
5656

57+
@Test
58+
void whenCompletionTokenDetailsIsNull() {
59+
OpenAiApi.Usage openAiUsage = new OpenAiApi.Usage(100, 200, 300, null);
60+
OpenAiUsage usage = OpenAiUsage.from(openAiUsage);
61+
assertThat(usage.getTotalTokens()).isEqualTo(300);
62+
assertThat(usage.getReasoningTokens()).isEqualTo(0);
63+
}
64+
65+
@Test
66+
void whenReasoningTokensIsNull() {
67+
OpenAiApi.Usage openAiUsage = new OpenAiApi.Usage(100, 200, 300,
68+
new OpenAiApi.Usage.CompletionTokenDetails(null));
69+
OpenAiUsage usage = OpenAiUsage.from(openAiUsage);
70+
assertThat(usage.getReasoningTokens()).isEqualTo(0);
71+
}
72+
73+
@Test
74+
void whenCompletionTokenDetailsIsPresent() {
75+
OpenAiApi.Usage openAiUsage = new OpenAiApi.Usage(100, 200, 300,
76+
new OpenAiApi.Usage.CompletionTokenDetails(50));
77+
OpenAiUsage usage = OpenAiUsage.from(openAiUsage);
78+
assertThat(usage.getReasoningTokens()).isEqualTo(50);
79+
}
80+
5781
}

0 commit comments

Comments
 (0)