Module: LlmCostTracker::Parsers::OpenaiServiceCharges

Included in:
OpenaiUsage
Defined in:
lib/llm_cost_tracker/parsers/openai_service_charges.rb

Constant Summary collapse

RESPONSE_OUTPUT_COMPONENTS =
{
  "web_search_call" => :web_search_request,
  "file_search_call" => :file_search_call,
  "code_interpreter_call" => :container_session
}.freeze

Class Method Summary collapse

Class Method Details

.billable?(item) ⇒ Boolean

Returns:

  • (Boolean)


22
23
24
25
26
27
28
29
30
31
# File 'lib/llm_cost_tracker/parsers/openai_service_charges.rb', line 22

def billable?(item)
  return false unless item.is_a?(Hash)

  component = RESPONSE_OUTPUT_COMPONENTS[item["type"]]
  return false unless component
  return true unless component == :web_search_request

  action_type = item.dig("action", "type")
  action_type.nil? || action_type == "search"
end

.build_line_item(item) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/llm_cost_tracker/parsers/openai_service_charges.rb', line 45

def build_line_item(item)
  component_key = RESPONSE_OUTPUT_COMPONENTS[item["type"]]
  return nil unless component_key

  provider_item_id = if component_key == :container_session
                       item["container_id"] || item["id"]
                     else
                       item["id"]
                     end
  Billing::LineItem.build(
    component_key: component_key,
    quantity: 1,
    cost_status: Billing::CostStatus::UNKNOWN,
    pricing_basis: :provider_usage,
    provider_field: "response.output.#{item['type']}",
    provider_item_id: provider_item_id,
    details: line_item_details(item)
  )
end

.line_item_details(item) ⇒ Object



65
66
67
68
69
70
71
# File 'lib/llm_cost_tracker/parsers/openai_service_charges.rb', line 65

def line_item_details(item)
  {
    "status" => item["status"],
    "action_type" => item.dig("action", "type"),
    "container_id" => item["container_id"]
  }.compact
end

.line_items_from_output(output_items) ⇒ Object



16
17
18
19
20
# File 'lib/llm_cost_tracker/parsers/openai_service_charges.rb', line 16

def line_items_from_output(output_items)
  deduped = {}
  Array(output_items).each { |item| store_output_item(deduped, item) }
  deduped.values.filter_map { |item| build_line_item(item) }
end

.openai_service_line_items(response) ⇒ Object



73
74
75
# File 'lib/llm_cost_tracker/parsers/openai_service_charges.rb', line 73

def openai_service_line_items(response)
  line_items_from_output(response["output"])
end

.openai_stream_service_line_items(events) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/llm_cost_tracker/parsers/openai_service_charges.rb', line 77

def openai_stream_service_line_items(events)
  output_items = []
  each_event_data(events) do |data|
    output_items.concat(Array(data.dig("response", "output")))
    output_items << data["item"] if data["item"]
  end
  line_items_from_output(output_items)
end

.store_output_item(output_items, item) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/llm_cost_tracker/parsers/openai_service_charges.rb', line 33

def store_output_item(output_items, item)
  return unless billable?(item)

  component = RESPONSE_OUTPUT_COMPONENTS[item["type"]]
  key = if component == :container_session && item["container_id"]
          "#{component}:#{item['container_id']}"
        else
          item["id"] || "#{item['type']}:#{output_items.length}"
        end
  output_items[key] = item
end