Skip to content

Commit 97e1956

Browse files
authored
Merge pull request #411 from MacPaw/claude/issue-258-completion-tokens-details
feat(chat): add completion_tokens_details to CompletionUsage
2 parents 2324e24 + 9fff161 commit 97e1956

2 files changed

Lines changed: 99 additions & 4 deletions

File tree

Sources/OpenAI/Public/Models/ChatResult.swift

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,24 +269,72 @@ public struct ChatResult: Codable, Equatable, Sendable {
269269
public let totalTokens: Int
270270
/// Breakdown of tokens used in the prompt.
271271
public let promptTokensDetails: PromptTokensDetails?
272-
272+
/// Breakdown of tokens used in the completion.
273+
public let completionTokensDetails: CompletionTokensDetails?
274+
275+
public init(
276+
completionTokens: Int,
277+
promptTokens: Int,
278+
totalTokens: Int,
279+
promptTokensDetails: PromptTokensDetails? = nil,
280+
completionTokensDetails: CompletionTokensDetails? = nil
281+
) {
282+
self.completionTokens = completionTokens
283+
self.promptTokens = promptTokens
284+
self.totalTokens = totalTokens
285+
self.promptTokensDetails = promptTokensDetails
286+
self.completionTokensDetails = completionTokensDetails
287+
}
288+
273289
public struct PromptTokensDetails: Codable, Equatable, Sendable {
274290
/// Audio input tokens present in the prompt.
275291
public let audioTokens: Int
276292
/// Cached tokens present in the prompt.
277293
public let cachedTokens: Int
278-
294+
279295
enum CodingKeys: String, CodingKey {
280296
case audioTokens = "audio_tokens"
281297
case cachedTokens = "cached_tokens"
282298
}
283299
}
284-
300+
301+
public struct CompletionTokensDetails: Codable, Equatable, Sendable {
302+
/// When using Predicted Outputs, the number of tokens in the prediction that appeared in the completion.
303+
public let acceptedPredictionTokens: Int?
304+
/// Audio output tokens generated by the model.
305+
public let audioTokens: Int?
306+
/// Tokens generated by the model for reasoning. Only present for reasoning models.
307+
public let reasoningTokens: Int?
308+
/// When using Predicted Outputs, the number of tokens in the prediction that did not appear in the completion.
309+
/// However, like reasoning tokens, these tokens are still counted in the total completion tokens for purposes of billing, output, and context window limits.
310+
public let rejectedPredictionTokens: Int?
311+
312+
public init(
313+
acceptedPredictionTokens: Int? = nil,
314+
audioTokens: Int? = nil,
315+
reasoningTokens: Int? = nil,
316+
rejectedPredictionTokens: Int? = nil
317+
) {
318+
self.acceptedPredictionTokens = acceptedPredictionTokens
319+
self.audioTokens = audioTokens
320+
self.reasoningTokens = reasoningTokens
321+
self.rejectedPredictionTokens = rejectedPredictionTokens
322+
}
323+
324+
enum CodingKeys: String, CodingKey {
325+
case acceptedPredictionTokens = "accepted_prediction_tokens"
326+
case audioTokens = "audio_tokens"
327+
case reasoningTokens = "reasoning_tokens"
328+
case rejectedPredictionTokens = "rejected_prediction_tokens"
329+
}
330+
}
331+
285332
enum CodingKeys: String, CodingKey {
286333
case completionTokens = "completion_tokens"
287334
case promptTokens = "prompt_tokens"
288335
case totalTokens = "total_tokens"
289336
case promptTokensDetails = "prompt_tokens_details"
337+
case completionTokensDetails = "completion_tokens_details"
290338
}
291339
}
292340
}

Tests/OpenAITests/OpenAITestsDecoder.swift

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,54 @@ class OpenAITestsDecoder: XCTestCase {
7575
)
7676
try decode(data, expectedValue)
7777
}
78-
78+
79+
func testCompletionUsageWithCompletionTokensDetails() async throws {
80+
let data = """
81+
{
82+
"completion_tokens": 320,
83+
"prompt_tokens": 80,
84+
"total_tokens": 400,
85+
"completion_tokens_details": {
86+
"accepted_prediction_tokens": 12,
87+
"audio_tokens": 0,
88+
"reasoning_tokens": 256,
89+
"rejected_prediction_tokens": 4
90+
}
91+
}
92+
"""
93+
94+
let expectedValue = ChatResult.CompletionUsage(
95+
completionTokens: 320,
96+
promptTokens: 80,
97+
totalTokens: 400,
98+
promptTokensDetails: nil,
99+
completionTokensDetails: .init(
100+
acceptedPredictionTokens: 12,
101+
audioTokens: 0,
102+
reasoningTokens: 256,
103+
rejectedPredictionTokens: 4
104+
)
105+
)
106+
try decode(data, expectedValue)
107+
}
108+
109+
func testCompletionUsageDecodesWithoutCompletionTokensDetails() async throws {
110+
let data = """
111+
{
112+
"completion_tokens": 12,
113+
"prompt_tokens": 9,
114+
"total_tokens": 21
115+
}
116+
"""
117+
118+
let expectedValue = ChatResult.CompletionUsage(
119+
completionTokens: 12,
120+
promptTokens: 9,
121+
totalTokens: 21
122+
)
123+
try decode(data, expectedValue)
124+
}
125+
79126
func testImageQuery() async throws {
80127
let imageQuery = ImagesQuery(
81128
prompt: "test",

0 commit comments

Comments
 (0)