1
2
3
4
5
6
7
8
9
10
11
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#!/usr/bin/env ruby
# frozen_string_literal: true
# Sample app: ask the AI to tell you about the man "Standing Sitting".
#
# Usage:
# bundle exec ruby examples/ask_standing_sitting.rb
#
# Authentication (auto-detected, in order):
# 1. ANTHROPIC_API_KEY env var (raw API key)
# 2. Cached OAuth token in ~/.config/dispatch/claude_oauth.json
# 3. Interactive OAuth login (will open your browser)
require "bundler/setup"
require "dispatch/adapter/claude"
PROMPT = "Tell me about the man Standing Sitting."
# ── Pick credentials ─────────────────────────────────────────────────────────
api_key = ENV.fetch("ANTHROPIC_API_KEY", nil)
explicit_key_supplied = api_key && !api_key.strip.empty?
# ── Pick a model ─────────────────────────────────────────────────────────────
known_models = Dispatch::Adapter::Claude::PricingTable.known_ids.sort
puts "\nAvailable models:"
known_models.each_with_index do |id, idx|
puts " #{(idx + 1).to_s.rjust(2)}) #{id}"
end
print "\nSelect a model (1-#{known_models.length}): "
choice = $stdin.gets&.strip
selected_index = begin
Integer(choice, 10) - 1
rescue StandardError
-1
end
if selected_index.negative? || selected_index >= known_models.length
warn "Invalid selection."
exit 1
end
model_id = known_models[selected_index]
puts "→ Using #{model_id}\n\n"
# ── Pick thinking level (configurable; default "high") ───────────────────────
puts "Thinking level (extended/adaptive thinking):"
puts " 1) high (default — adaptive on 4.6+, enabled on older)"
puts " 2) medium"
puts " 3) low"
puts " 4) off (no thinking)"
print "Select [1-4, blank=1]: "
think_choice = $stdin.gets&.strip
thinking = case think_choice
when "2" then "medium"
when "3" then "low"
when "4" then false
else "high" # "", nil, "1", or unrecognised input
end
puts "→ thinking: #{thinking.inspect}\n\n"
# ── Build the adapter ────────────────────────────────────────────────────────
adapter = Dispatch::Adapter::Claude.new(
model: model_id,
api_key: explicit_key_supplied ? api_key : nil,
thinking: thinking
)
# Trigger auth (cached token, refresh, or interactive OAuth login as needed).
status = adapter.authenticate!
puts "Auth: #{status}\n\n"
# ── Send the request ─────────────────────────────────────────────────────────
messages = [
Dispatch::Adapter::Message.new(
role: "user",
content: [Dispatch::Adapter::TextBlock.new(text: PROMPT)]
)
]
puts "Q: #{PROMPT}"
puts "A:"
response = adapter.chat(messages, stream: false)
response.content.each do |block|
case block
when Dispatch::Adapter::TextBlock
puts block.text
when Dispatch::Adapter::ThinkingBlock
# Skip — internal reasoning, not the answer
end
end
# ── Print usage / cost summary ───────────────────────────────────────────────
u = response.usage
puts "\n--- usage ---"
puts "input_tokens: #{u.input_tokens}"
puts "output_tokens: #{u.output_tokens}"
puts "cache_read: #{u.cache_read_tokens}"
puts "cache_create: #{u.cache_creation_tokens}"
puts format("cost (USD): $%.6f", u.cost.total) if u.cost
puts "stop_reason: #{response.stop_reason}"
# ── Print rate-limit header info (per-response real-time quota) ───────────────
rli = adapter.rate_limit_info
if rli
puts "\n--- rate-limit (from response headers) ---"
puts "status: #{rli.status}"
puts "representative window: #{rli.representative_claim}"
puts "fallback available: #{rli.fallback}"
rli.windows.each do |wid, win|
pct = win.utilization ? format("%.2f%%", win.utilization * 100) : "?"
remains = win.utilization ? format("%.2f%%", (1.0 - win.utilization) * 100) : "?"
reset = win.reset_at ? " (resets #{win.reset_at.strftime("%Y-%m-%d %H:%M:%S %Z")})" : ""
puts " #{wid.ljust(12)} used=#{pct} remaining=#{remains} status=#{win.status}#{reset}"
end
log_path = File.expand_path("~/.config/dispatch/claude_ratelimit.jsonl")
puts "\n[Rate-limit log: #{log_path}]"
else
puts "\n(No unified rate-limit headers received.)"
puts "\nDebug: ALL response headers from the last API call:"
hdrs = adapter.last_response_headers
if hdrs && !hdrs.empty?
hdrs.sort.each { |k, v| puts " #{k}: #{v}" }
else
puts " (none captured)"
end
end
|