Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions Sources/OpenAI/Public/Models/ChatResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -269,24 +269,72 @@ public struct ChatResult: Codable, Equatable, Sendable {
public let totalTokens: Int
/// Breakdown of tokens used in the prompt.
public let promptTokensDetails: PromptTokensDetails?

/// Breakdown of tokens used in the completion.
public let completionTokensDetails: CompletionTokensDetails?

public init(
completionTokens: Int,
promptTokens: Int,
totalTokens: Int,
promptTokensDetails: PromptTokensDetails? = nil,
completionTokensDetails: CompletionTokensDetails? = nil
) {
self.completionTokens = completionTokens
self.promptTokens = promptTokens
self.totalTokens = totalTokens
self.promptTokensDetails = promptTokensDetails
self.completionTokensDetails = completionTokensDetails
}

public struct PromptTokensDetails: Codable, Equatable, Sendable {
/// Audio input tokens present in the prompt.
public let audioTokens: Int
/// Cached tokens present in the prompt.
public let cachedTokens: Int

enum CodingKeys: String, CodingKey {
case audioTokens = "audio_tokens"
case cachedTokens = "cached_tokens"
}
}


public struct CompletionTokensDetails: Codable, Equatable, Sendable {
/// When using Predicted Outputs, the number of tokens in the prediction that appeared in the completion.
public let acceptedPredictionTokens: Int?
/// Audio output tokens generated by the model.
public let audioTokens: Int?
/// Tokens generated by the model for reasoning. Only present for reasoning models.
public let reasoningTokens: Int?
/// When using Predicted Outputs, the number of tokens in the prediction that did not appear in the completion.
/// However, like reasoning tokens, these tokens are still counted in the total completion tokens for purposes of billing, output, and context window limits.
public let rejectedPredictionTokens: Int?

public init(
acceptedPredictionTokens: Int? = nil,
audioTokens: Int? = nil,
reasoningTokens: Int? = nil,
rejectedPredictionTokens: Int? = nil
) {
self.acceptedPredictionTokens = acceptedPredictionTokens
self.audioTokens = audioTokens
self.reasoningTokens = reasoningTokens
self.rejectedPredictionTokens = rejectedPredictionTokens
}

enum CodingKeys: String, CodingKey {
case acceptedPredictionTokens = "accepted_prediction_tokens"
case audioTokens = "audio_tokens"
case reasoningTokens = "reasoning_tokens"
case rejectedPredictionTokens = "rejected_prediction_tokens"
}
}

enum CodingKeys: String, CodingKey {
case completionTokens = "completion_tokens"
case promptTokens = "prompt_tokens"
case totalTokens = "total_tokens"
case promptTokensDetails = "prompt_tokens_details"
case completionTokensDetails = "completion_tokens_details"
}
}
}
Expand Down
49 changes: 48 additions & 1 deletion Tests/OpenAITests/OpenAITestsDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,54 @@ class OpenAITestsDecoder: XCTestCase {
)
try decode(data, expectedValue)
}


func testCompletionUsageWithCompletionTokensDetails() async throws {
let data = """
{
"completion_tokens": 320,
"prompt_tokens": 80,
Comment thread
nezhyborets marked this conversation as resolved.
"total_tokens": 400,
"completion_tokens_details": {
"accepted_prediction_tokens": 12,
"audio_tokens": 0,
"reasoning_tokens": 256,
"rejected_prediction_tokens": 4
}
}
"""

let expectedValue = ChatResult.CompletionUsage(
completionTokens: 320,
promptTokens: 80,
totalTokens: 400,
promptTokensDetails: nil,
completionTokensDetails: .init(
acceptedPredictionTokens: 12,
audioTokens: 0,
reasoningTokens: 256,
rejectedPredictionTokens: 4
)
)
try decode(data, expectedValue)
}

func testCompletionUsageDecodesWithoutCompletionTokensDetails() async throws {
let data = """
{
"completion_tokens": 12,
"prompt_tokens": 9,
"total_tokens": 21
}
"""

let expectedValue = ChatResult.CompletionUsage(
completionTokens: 12,
promptTokens: 9,
totalTokens: 21
)
try decode(data, expectedValue)
}

func testImageQuery() async throws {
let imageQuery = ImagesQuery(
prompt: "test",
Expand Down
Loading