Module: Clacky::Agent::CostTracker

Included in:
Clacky::Agent
Defined in:
lib/clacky/agent/cost_tracker.rb

Overview

Cost tracking and token usage statistics Manages cost calculation, token estimation, and usage display

Instance Method Summary collapse

Instance Method Details

#collect_iteration_tokens(usage, cost) ⇒ Hash

Collect token usage data for current iteration and return it. Does NOT call @ui directly — the caller is responsible for displaying at the right moment (e.g. after show_assistant_message).

Parameters:

  • usage (Hash)

    Usage data from API

  • cost (Float)

    Cost for this iteration

Returns:

  • (Hash)

    token_data ready for show_token_usage



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/clacky/agent/cost_tracker.rb', line 94

def collect_iteration_tokens(usage, cost)
  prompt_tokens = usage[:prompt_tokens] || 0
  completion_tokens = usage[:completion_tokens] || 0
  total_tokens = usage[:total_tokens] || (prompt_tokens + completion_tokens)
  cache_write = usage[:cache_creation_input_tokens] || 0
  cache_read = usage[:cache_read_input_tokens] || 0

  # Calculate token delta from previous iteration
  delta_tokens = total_tokens - @previous_total_tokens
  @previous_total_tokens = total_tokens  # Update for next iteration

  {
    delta_tokens: delta_tokens,
    prompt_tokens: prompt_tokens,
    completion_tokens: completion_tokens,
    total_tokens: total_tokens,
    cache_write: cache_write,
    cache_read: cache_read,
    cost: cost,
    cost_source: @cost_source
  }
end

#track_cost(usage, raw_api_usage: nil) ⇒ Object

Track cost from API usage Updates total cost and displays iteration statistics

Parameters:

  • usage (Hash)

    Usage data from API response

  • raw_api_usage (Hash, nil) (defaults to: nil)

    Raw API usage data for debugging



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/clacky/agent/cost_tracker.rb', line 12

def track_cost(usage, raw_api_usage: nil)
  # Priority 1: Use API-provided cost if available (OpenRouter, LiteLLM, etc.)
  iteration_cost = nil
  if usage[:api_cost]
    @total_cost += usage[:api_cost]
    @cost_source = :api
    @task_cost_source = :api
    iteration_cost = usage[:api_cost]
    @ui&.log("Using API-provided cost: $#{usage[:api_cost]}", level: :debug) if @config.verbose
  else
    # Priority 2: Calculate from tokens using ModelPricing
    result = ModelPricing.calculate_cost(model: current_model, usage: usage)
    cost = result[:cost]
    pricing_source = result[:source]

    @total_cost += cost
    iteration_cost = cost
    # Map pricing source to cost source: :price or :default
    @cost_source = pricing_source
    @task_cost_source = pricing_source

    if @config.verbose
      source_label = pricing_source == :price ? "model pricing" : "default pricing"
      @ui&.log("Calculated cost for #{@config.model_name} using #{source_label}: $#{cost.round(6)}", level: :debug)
      @ui&.log("Usage breakdown: prompt=#{usage[:prompt_tokens]}, completion=#{usage[:completion_tokens]}, cache_write=#{usage[:cache_creation_input_tokens] || 0}, cache_read=#{usage[:cache_read_input_tokens] || 0}", level: :debug)
    end
  end

  # Collect token usage data for this iteration (returned to caller for deferred display)
  token_data = collect_iteration_tokens(usage, iteration_cost)

  # Update session bar cost in real-time (don't wait for agent.run to finish)
  @ui&.update_sessionbar(cost: @total_cost)

  # Track cache usage statistics (global)
  @cache_stats[:total_requests] += 1

  if usage[:cache_creation_input_tokens]
    @cache_stats[:cache_creation_input_tokens] += usage[:cache_creation_input_tokens]
  end

  if usage[:cache_read_input_tokens]
    @cache_stats[:cache_read_input_tokens] += usage[:cache_read_input_tokens]
    @cache_stats[:cache_hit_requests] += 1
  end

  # Store raw API usage samples (keep last 3 for debugging)
  if raw_api_usage
    @cache_stats[:raw_api_usage_samples] ||= []
    @cache_stats[:raw_api_usage_samples] << raw_api_usage
    @cache_stats[:raw_api_usage_samples] = @cache_stats[:raw_api_usage_samples].last(3)
  end

  # Track cache usage for current task
  if @task_cache_stats
    @task_cache_stats[:total_requests] += 1

    if usage[:cache_creation_input_tokens]
      @task_cache_stats[:cache_creation_input_tokens] += usage[:cache_creation_input_tokens]
    end

    if usage[:cache_read_input_tokens]
      @task_cache_stats[:cache_read_input_tokens] += usage[:cache_read_input_tokens]
      @task_cache_stats[:cache_hit_requests] += 1
    end
  end

  # Return token_data so the caller can display it at the right moment
  token_data
end