Module: LlmCostTracker::Integrations::Anthropic

Extended by:
Base
Defined in:
lib/llm_cost_tracker/integrations/anthropic.rb

Defined Under Namespace

Modules: MessagesPatch

Class Method Summary collapse

Methods included from Base

active?, elapsed_ms, enforce_budget!, install, minimum_version, object_dig, object_value, patch_target, patch_targets, record_safely, request_params, status, version_constant

Class Method Details

.finish_stream(collector, errored:) ⇒ Object



105
106
107
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 105

def finish_stream(collector, errored:)
  record_safely { collector.finish!(errored: errored) }
end

.integration_nameObject



13
14
15
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 13

def integration_name
  :anthropic
end

.minimum_versionObject



17
18
19
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 17

def minimum_version
  "1.36.0"
end

.patch_targetsObject



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 25

def patch_targets
  [
    patch_target("Anthropic::Resources::Messages", with: MessagesPatch, methods: %i[create stream stream_raw]),
    patch_target(
      "Anthropic::Resources::Beta::Messages",
      with: MessagesPatch,
      methods: %i[create stream stream_raw],
      optional: true
    )
  ]
end

.record_message(message, request:, latency_ms:) ⇒ Object



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
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 37

def record_message(message, request:, latency_ms:)
  return unless active?

  record_safely do
    usage = object_value(message, :usage)
    next unless usage

    input_tokens = object_value(usage, :input_tokens)
    output_tokens = object_value(usage, :output_tokens)
    next if input_tokens.nil? && output_tokens.nil?

    LlmCostTracker::Tracker.record(
      capture: UsageCapture.build(
        provider: "anthropic",
        model: object_value(message, :model) || request[:model],
        pricing_mode: object_value(usage, :service_tier) || object_value(message, :service_tier) ||
          request[:service_tier],
        token_usage: token_usage(usage, input_tokens, output_tokens),
        usage_source: :sdk_response,
        provider_response_id: object_value(message, :id)
      ),
      latency_ms: latency_ms
    )
  end
end

.stream_collector(request) ⇒ Object



98
99
100
101
102
103
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 98

def stream_collector(request)
  LlmCostTracker::Capture::StreamCollector.new(
    provider: "anthropic",
    model: request[:model]
  )
end

.token_usage(usage, input_tokens, output_tokens) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 63

def token_usage(usage, input_tokens, output_tokens)
  cache_write_1h = object_dig(usage, :cache_creation, :ephemeral_1h_input_tokens).to_i
  cache_write_5m = object_dig(usage, :cache_creation, :ephemeral_5m_input_tokens)
  cache_write = if cache_write_5m.nil?
                  total_cache_write = object_value(usage, :cache_creation_input_tokens)
                  [total_cache_write.to_i - cache_write_1h, 0].max
                else
                  cache_write_5m.to_i
                end
  hidden_output = (
    object_value(usage, :thinking_tokens, :thinking_output_tokens) ||
    object_dig(usage, :output_tokens_details, :reasoning_tokens)
  ).to_i

  TokenUsage.build(
    input_tokens: input_tokens.to_i,
    output_tokens: output_tokens.to_i,
    cache_read_input_tokens: object_value(usage, :cache_read_input_tokens).to_i,
    cache_write_input_tokens: cache_write,
    cache_write_1h_input_tokens: cache_write_1h,
    hidden_output_tokens: hidden_output
  )
end

.track_stream(stream, collector:) ⇒ Object



87
88
89
90
91
92
93
94
95
96
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 87

def track_stream(stream, collector:)
  return stream unless active?

  LlmCostTracker::Capture::StreamTracker.new(
    stream,
    collector,
    -> { active? },
    ->(errored:) { finish_stream(collector, errored: errored) }
  ).wrap
end

.version_constantObject



21
22
23
# File 'lib/llm_cost_tracker/integrations/anthropic.rb', line 21

def version_constant
  "Anthropic::VERSION"
end