blob: 7e27d45619a5ef66938d2c027681aa182045e5d2 (
plain)
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
|
# frozen_string_literal: true
RSpec.describe Dispatch::Adapter::Claude::ModelCatalog do
describe ".build" do
let(:model_id) { "claude-sonnet-4-5-20250929" }
subject(:info) { described_class.build(model_id) }
it "returns a ModelInfo" do
expect(info).to be_a(Dispatch::Adapter::ModelInfo)
end
it "sets id correctly" do
expect(info.id).to eq(model_id)
end
it "sets name to the id (no separate label in JSON)" do
expect(info.name).to eq(model_id)
end
it "sets max_context_tokens from the pricing table" do
expect(info.max_context_tokens).to eq(
Dispatch::Adapter::Claude::PricingTable.context_window(model_id)
)
end
it "sets supports_vision to true" do
expect(info.supports_vision).to be(true)
end
it "sets supports_tool_use to true" do
expect(info.supports_tool_use).to be(true)
end
it "sets supports_streaming to true" do
expect(info.supports_streaming).to be(true)
end
it "sets premium_request_multiplier to nil" do
expect(info.premium_request_multiplier).to be_nil
end
it "sets pricing from the pricing table" do
expect(info.pricing).to be_a(Dispatch::Adapter::ModelPricing)
end
it "falls back to 200_000 for context_window when table has no entry" do
# Build with an id that is not in the table — simulate by using an
# id absent from the table; ModelCatalog falls back to 200_000.
# (We can't do that cleanly without a stub, so test the fallback
# directly via a non-existent id.)
unknown_info = described_class.build("some-future-model-not-in-table")
expect(unknown_info.max_context_tokens).to eq(200_000)
end
it "returns nil pricing for an id not in the pricing table" do
unknown_info = described_class.build("some-future-model-not-in-table")
expect(unknown_info.pricing).to be_nil
end
end
end
RSpec.describe Dispatch::Adapter::Claude, "#list_models" do
subject(:adapter) { described_class.allocate }
it "returns a non-empty array" do
expect(adapter.list_models).to be_an(Array)
expect(adapter.list_models).not_to be_empty
end
it "every entry is a ModelInfo" do
adapter.list_models.each do |info|
expect(info).to be_a(Dispatch::Adapter::ModelInfo)
end
end
it "every entry has pricing populated" do
adapter.list_models.each do |info|
expect(info.pricing).to be_a(Dispatch::Adapter::ModelPricing),
"expected pricing on #{info.id}"
end
end
it "Pricing.calculate works with every entry" do
usage = Dispatch::Adapter::Usage.new(
input_tokens: 1_000,
output_tokens: 500
)
adapter.list_models.each do |info|
cost = Dispatch::Adapter::Pricing.calculate(usage, info)
expect(cost).to be_a(Dispatch::Adapter::UsageCost),
"expected UsageCost for #{info.id}"
expect(cost.total).to be >= 0
end
end
it "includes the three required models" do
ids = adapter.list_models.map(&:id)
expect(ids).to include("claude-opus-4-7-20251018")
expect(ids).to include("claude-sonnet-4-5-20250929")
expect(ids).to include("claude-haiku-4-5-20251001")
end
end
|