Class: OmniAI::Chat::Usage

Inherits:
Object
  • Object
show all
Defined in:
lib/omniai/chat/usage.rb

Overview

The usage of a chat in terms of tokens (input / output / total).

Two invariants hold across every provider:

  • thinking_tokens is a subset of output_tokens, never an addition to it. Providers either fold reasoning into their output count already (reporting the breakdown separately) or report it separately and have it added in by their own serializer. Adding thinking_tokens to output_tokens double counts.
  • total_tokens may exceed input_tokens + output_tokens. Providers count buckets this class does not model — cached input, tool-use prompts — so the reported total is authoritative and is never recomputed from the parts. It may also be nil: some providers report no total at all.

Provider-specific vocabulary is read by that provider's own :usage deserializer, not here. This class reads only its own keys and the flat OpenAI-compatible aliases the base client speaks.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input_tokens:, output_tokens:, total_tokens:, thinking_tokens: nil) ⇒ Usage

Returns a new instance of Usage.

Parameters:

  • input_tokens (Integer, nil)
  • output_tokens (Integer, nil)
  • total_tokens (Integer, nil)
  • thinking_tokens (Integer, nil) (defaults to: nil)

    optional



39
40
41
42
43
44
# File 'lib/omniai/chat/usage.rb', line 39

def initialize(input_tokens:, output_tokens:, total_tokens:, thinking_tokens: nil)
  @input_tokens = input_tokens
  @output_tokens = output_tokens
  @total_tokens = total_tokens
  @thinking_tokens = thinking_tokens
end

Instance Attribute Details

#input_tokensInteger?

Returns:

  • (Integer, nil)


20
21
22
# File 'lib/omniai/chat/usage.rb', line 20

def input_tokens
  @input_tokens
end

#output_tokensInteger?

Returns:

  • (Integer, nil)


23
24
25
# File 'lib/omniai/chat/usage.rb', line 23

def output_tokens
  @output_tokens
end

#thinking_tokensInteger?

The subset of output_tokens a provider attributes to internal reasoning ("thinking"). nil when the provider does not report a breakdown — which is distinct from 0, meaning the provider reported that no reasoning occurred.

Returns:

  • (Integer, nil)


33
34
35
# File 'lib/omniai/chat/usage.rb', line 33

def thinking_tokens
  @thinking_tokens
end

#total_tokensInteger?

Returns:

  • (Integer, nil)


26
27
28
# File 'lib/omniai/chat/usage.rb', line 26

def total_tokens
  @total_tokens
end

Class Method Details

.deserialize(data, context: nil) ⇒ OmniAI::Chat::Usage

Parameters:

Returns:



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/omniai/chat/usage.rb', line 58

def self.deserialize(data, context: nil)
  deserialize = context&.deserializer(:usage)
  return deserialize.call(data, context:) if deserialize

  input_tokens = data["input_tokens"] || data["prompt_tokens"]
  output_tokens = data["output_tokens"] || data["completion_tokens"]
  total_tokens = data["total_tokens"]
  thinking_tokens = data["thinking_tokens"]

  new(input_tokens:, output_tokens:, total_tokens:, thinking_tokens:)
end

Instance Method Details

#inspectString

Returns:

  • (String)


47
48
49
50
51
52
# File 'lib/omniai/chat/usage.rb', line 47

def inspect
  text = "#<#{self.class.name} input_tokens=#{input_tokens} output_tokens=#{output_tokens} " \
    "total_tokens=#{total_tokens}"
  text += " thinking_tokens=#{thinking_tokens}" unless thinking_tokens.nil?
  "#{text}>"
end

#serialize(context: nil) ⇒ Hash

Parameters:

Returns:

  • (Hash)


73
74
75
76
77
78
79
80
81
82
# File 'lib/omniai/chat/usage.rb', line 73

def serialize(context: nil)
  serialize = context&.serializer(:usage)
  return serialize.call(self, context:) if serialize

  {
    input_tokens:,
    output_tokens:,
    total_tokens:,
  }.tap { |data| data[:thinking_tokens] = thinking_tokens unless thinking_tokens.nil? }
end