phaseo_sdk 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,364 @@
1
+ require "minitest/autorun"
2
+ require "tmpdir"
3
+ require_relative "../lib/index"
4
+
5
+ class DevtoolsTest < Minitest::Test
6
+ def test_records_responses_entries
7
+ Dir.mktmpdir("phaseo-devtools-ruby-") do |dir|
8
+ client = PhaseoSdk::Phaseo.new(
9
+ api_key: "test",
10
+ enable_deprecation_warnings: false,
11
+ devtools: PhaseoSdk::Devtools.create(enabled: true, directory: dir)
12
+ )
13
+
14
+ result = client.send(
15
+ :with_lifecycle_and_telemetry,
16
+ endpoint: "responses",
17
+ payload: { model: "openai/gpt-5-nano", input: "hi" },
18
+ check_lifecycle: false
19
+ ) do
20
+ {
21
+ id: "resp_1",
22
+ model: "openai/gpt-5-nano",
23
+ usage: { input_tokens: 2, output_tokens: 1, total_tokens: 3 },
24
+ request_id: "req_ruby_1",
25
+ session_id: "session_ruby_chat_1",
26
+ upstream_request_id: "upstream_ruby_chat_1",
27
+ pricing_lines: [{ provider: "openai", cost_usd: 0.0025 }],
28
+ latency_ms: 120,
29
+ generation_ms: 340,
30
+ provider_attempts: [{ provider: "openai", status_code: 200, duration_ms: 460 }]
31
+ }
32
+ end
33
+
34
+ assert_equal "resp_1", result[:id]
35
+
36
+ generations = File.join(dir, "generations.jsonl")
37
+ metadata = File.join(dir, "metadata.json")
38
+ assert File.exist?(generations), "expected generations.jsonl to exist"
39
+ assert File.exist?(metadata), "expected metadata.json to exist"
40
+
41
+ content = File.read(generations)
42
+ assert_includes content, "\"type\":\"responses\""
43
+ assert_includes content, "\"sdk\":\"ruby\""
44
+
45
+ entry = JSON.parse(content.lines.first)
46
+ assert_equal "req_ruby_1", entry.dig("metadata", "request_id")
47
+ assert_equal "session_ruby_chat_1", entry.dig("metadata", "session_id")
48
+ assert_equal "upstream_ruby_chat_1", entry.dig("metadata", "upstream_request_id")
49
+ assert_equal "openai", entry.dig("metadata", "pricing_lines", 0, "provider")
50
+ assert_equal 120, entry.dig("metadata", "latency_ms")
51
+ assert_equal "openai", entry.dig("metadata", "provider_attempts", 0, "provider")
52
+ end
53
+ end
54
+
55
+ def test_records_structured_error_responses
56
+ Dir.mktmpdir("phaseo-devtools-ruby-") do |dir|
57
+ client = PhaseoSdk::Phaseo.new(
58
+ api_key: "test",
59
+ enable_deprecation_warnings: false,
60
+ devtools: PhaseoSdk::Devtools.create(enabled: true, directory: dir)
61
+ )
62
+
63
+ error = assert_raises(Phaseo::Gen::RequestError) do
64
+ client.send(
65
+ :with_lifecycle_and_telemetry,
66
+ endpoint: "responses",
67
+ payload: { model: "openai/gpt-5-nano", input: "hi" },
68
+ check_lifecycle: false
69
+ ) do
70
+ raise Phaseo::Gen::RequestError.new(
71
+ status_code: 429,
72
+ status_message: "Too Many Requests",
73
+ response_body: {
74
+ request_id: "req_ruby_err_1",
75
+ provider_attempts: [{ provider: "openrouter", status_code: 429, duration_ms: 612 }]
76
+ }.to_json
77
+ )
78
+ end
79
+ end
80
+
81
+ assert_equal 429, error.status_code
82
+
83
+ entry = JSON.parse(File.read(File.join(dir, "generations.jsonl")).lines.first)
84
+ assert_equal "req_ruby_err_1", entry.dig("response", "request_id")
85
+ assert_equal "req_ruby_err_1", entry.dig("metadata", "request_id")
86
+ assert_equal 429, entry.dig("error", "status_code")
87
+ end
88
+ end
89
+
90
+ def test_records_batch_entries
91
+ Dir.mktmpdir("phaseo-devtools-ruby-") do |dir|
92
+ client = PhaseoSdk::Phaseo.new(
93
+ api_key: "test",
94
+ enable_deprecation_warnings: false,
95
+ devtools: PhaseoSdk::Devtools.create(enabled: true, directory: dir)
96
+ )
97
+
98
+ result = client.send(
99
+ :with_lifecycle_and_telemetry,
100
+ endpoint: "batches.create",
101
+ payload: {
102
+ input_file_id: "file_ruby_1",
103
+ endpoint: "/v1/responses",
104
+ completion_window: "24h",
105
+ session_id: "session_ruby_batch_1",
106
+ webhook: { url: "https://example.com/hooks/batch" }
107
+ },
108
+ check_lifecycle: false
109
+ ) do
110
+ {
111
+ id: "batch_ruby_1",
112
+ object: "batch",
113
+ status: "completed",
114
+ endpoint: "/v1/responses",
115
+ provider: "openai",
116
+ request_id: "req_ruby_batch_1",
117
+ session_id: "session_ruby_batch_1",
118
+ pricing_lines: [{ dimension: "batch_requests", units: 2 }],
119
+ request_counts: { total: 2, completed: 1, failed: 1 },
120
+ billing: { charged: true, cost_usd: 0.0025 }
121
+ }
122
+ end
123
+
124
+ assert_equal "batch_ruby_1", result[:id]
125
+
126
+ entry = JSON.parse(File.read(File.join(dir, "generations.jsonl")).lines.first)
127
+ assert_equal "batches.create", entry["type"]
128
+ assert_equal "session_ruby_batch_1", entry.dig("request", "session_id")
129
+ assert_equal "https://example.com/hooks/batch", entry.dig("request", "webhook", "url")
130
+ assert_equal "openai", entry.dig("metadata", "provider")
131
+ assert_equal "session_ruby_batch_1", entry.dig("metadata", "session_id")
132
+ assert_equal 2, entry.dig("metadata", "request_counts", "total")
133
+ assert_equal true, entry.dig("metadata", "billing", "charged")
134
+ end
135
+ end
136
+
137
+ def test_records_generation_lookup_entries
138
+ Dir.mktmpdir("phaseo-devtools-ruby-") do |dir|
139
+ client = PhaseoSdk::Phaseo.new(
140
+ api_key: "test",
141
+ enable_deprecation_warnings: false,
142
+ devtools: PhaseoSdk::Devtools.create(enabled: true, directory: dir)
143
+ )
144
+
145
+ result = client.send(
146
+ :with_lifecycle_and_telemetry,
147
+ endpoint: "generations.retrieve",
148
+ payload: { "id" => "gen_ruby_1" },
149
+ check_lifecycle: false
150
+ ) do
151
+ {
152
+ id: "gen_ruby_1",
153
+ provider: "openai",
154
+ request_id: "req_ruby_generation_1",
155
+ session_id: "session_ruby_generation_1",
156
+ status_code: 200
157
+ }
158
+ end
159
+
160
+ assert_equal "gen_ruby_1", result[:id]
161
+
162
+ entry = JSON.parse(File.read(File.join(dir, "generations.jsonl")).lines.first)
163
+ assert_equal "generations.retrieve", entry["type"]
164
+ assert_equal "gen_ruby_1", entry.dig("request", "id")
165
+ assert_equal "req_ruby_generation_1", entry.dig("metadata", "request_id")
166
+ assert_equal "session_ruby_generation_1", entry.dig("metadata", "session_id")
167
+ assert_equal "openai", entry.dig("metadata", "provider")
168
+ end
169
+ end
170
+
171
+ def test_records_health_entries
172
+ Dir.mktmpdir("phaseo-devtools-ruby-") do |dir|
173
+ client = PhaseoSdk::Phaseo.new(
174
+ api_key: "test",
175
+ enable_deprecation_warnings: false,
176
+ devtools: PhaseoSdk::Devtools.create(enabled: true, directory: dir)
177
+ )
178
+
179
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
180
+ if method == "GET" && path == "/health"
181
+ { "status" => "ok", "timestamp" => "2026-05-05T12:00:00.000Z" }
182
+ else
183
+ raise "unexpected request #{method} #{path}"
184
+ end
185
+ end
186
+
187
+ response = client.health
188
+
189
+ assert_equal "ok", response["status"]
190
+ entry = JSON.parse(File.read(File.join(dir, "generations.jsonl")).lines.first)
191
+ assert_equal "health", entry["type"]
192
+ assert_equal "ok", entry.dig("response", "status")
193
+ end
194
+ end
195
+
196
+ def test_records_video_lifecycle_entries
197
+ Dir.mktmpdir("phaseo-devtools-ruby-") do |dir|
198
+ client = PhaseoSdk::Phaseo.new(
199
+ api_key: "test",
200
+ enable_deprecation_warnings: false,
201
+ devtools: PhaseoSdk::Devtools.create(enabled: true, directory: dir)
202
+ )
203
+
204
+ client.send(
205
+ :with_lifecycle_and_telemetry,
206
+ endpoint: "video.generations",
207
+ payload: { model: "google/veo-3", prompt: "orbital reveal" },
208
+ check_lifecycle: false
209
+ ) do
210
+ {
211
+ id: "video_ruby_1",
212
+ object: "video",
213
+ status: "queued",
214
+ provider: "google",
215
+ model: "google/veo-3",
216
+ request_id: "req_ruby_video_1",
217
+ session_id: "session_ruby_video_1"
218
+ }
219
+ end
220
+
221
+ client.send(
222
+ :with_lifecycle_and_telemetry,
223
+ endpoint: "video.retrieve",
224
+ payload: { "video_id" => "video_ruby_1" },
225
+ check_lifecycle: false
226
+ ) do
227
+ {
228
+ id: "video_ruby_1",
229
+ object: "video",
230
+ status: "completed",
231
+ provider: "google",
232
+ model: "google/veo-3",
233
+ request_id: "req_ruby_video_2",
234
+ session_id: "session_ruby_video_2"
235
+ }
236
+ end
237
+
238
+ client.send(
239
+ :with_lifecycle_and_telemetry,
240
+ endpoint: "video.cancel",
241
+ payload: { "video_id" => "video_ruby_1" },
242
+ check_lifecycle: false
243
+ ) do
244
+ {
245
+ id: "video_ruby_1",
246
+ object: "video",
247
+ status: "cancelled",
248
+ provider: "google",
249
+ model: "google/veo-3",
250
+ request_id: "req_ruby_video_3",
251
+ session_id: "session_ruby_video_3"
252
+ }
253
+ end
254
+
255
+ lines = File.read(File.join(dir, "generations.jsonl")).lines
256
+ assert_equal 3, lines.length
257
+
258
+ create_entry = JSON.parse(lines[0])
259
+ assert_equal "video.generations", create_entry["type"]
260
+ assert_equal "req_ruby_video_1", create_entry.dig("metadata", "request_id")
261
+
262
+ retrieve_entry = JSON.parse(lines[1])
263
+ assert_equal "video.retrieve", retrieve_entry["type"]
264
+ assert_equal "video_ruby_1", retrieve_entry.dig("request", "video_id")
265
+ assert_equal "session_ruby_video_2", retrieve_entry.dig("metadata", "session_id")
266
+
267
+ cancel_entry = JSON.parse(lines[2])
268
+ assert_equal "video.cancel", cancel_entry["type"]
269
+ assert_equal "req_ruby_video_3", cancel_entry.dig("metadata", "request_id")
270
+ end
271
+ end
272
+
273
+ def test_records_control_plane_entries
274
+ Dir.mktmpdir("phaseo-devtools-ruby-") do |dir|
275
+ client = PhaseoSdk::Phaseo.new(
276
+ api_key: "test",
277
+ enable_deprecation_warnings: false,
278
+ devtools: PhaseoSdk::Devtools.create(enabled: true, directory: dir)
279
+ )
280
+
281
+ client.send(:with_lifecycle_and_telemetry, endpoint: "models.list", payload: { limit: "2" }, check_lifecycle: false) do
282
+ { "models" => [{ "model_id" => "openai/gpt-5-mini" }] }
283
+ end
284
+ client.send(:with_lifecycle_and_telemetry, endpoint: "providers", payload: { limit: "2" }, check_lifecycle: false) do
285
+ { "data" => [{ "slug" => "openai" }] }
286
+ end
287
+ client.send(:with_lifecycle_and_telemetry, endpoint: "credits", payload: { team_id: "team_123" }, check_lifecycle: false) do
288
+ { "credits" => { "remaining" => 42 } }
289
+ end
290
+ client.send(:with_lifecycle_and_telemetry, endpoint: "activity", payload: { days: "30" }, check_lifecycle: false) do
291
+ { "data" => [{ "date" => "2026-05-01", "requests" => 12 }] }
292
+ end
293
+ client.send(:with_lifecycle_and_telemetry, endpoint: "analytics", payload: { date: "2026-05-01" }, check_lifecycle: false) do
294
+ { "data" => [{ "endpoint_id" => "responses", "requests" => 9 }] }
295
+ end
296
+ client.send(:with_lifecycle_and_telemetry, endpoint: "endpoints.list", payload: {}, check_lifecycle: false) do
297
+ { "data" => [{ "id" => "responses" }] }
298
+ end
299
+ client.send(:with_lifecycle_and_telemetry, endpoint: "organisations.list", payload: { limit: "2", offset: "3" }, check_lifecycle: false) do
300
+ { "data" => [{ "id" => "org_ruby_1" }] }
301
+ end
302
+ client.send(:with_lifecycle_and_telemetry, endpoint: "pricing.models", payload: { provider: "openai" }, check_lifecycle: false) do
303
+ { "data" => [{ "model_id" => "openai/gpt-5-mini" }] }
304
+ end
305
+ client.send(
306
+ :with_lifecycle_and_telemetry,
307
+ endpoint: "pricing.calculate",
308
+ payload: { provider: "openai", model: "openai/gpt-5-mini", endpoint: "responses" },
309
+ check_lifecycle: false
310
+ ) do
311
+ { "pricing" => { "currency" => "USD", "total_cost_usd" => 0.0031 } }
312
+ end
313
+ client.send(
314
+ :with_lifecycle_and_telemetry,
315
+ endpoint: "provisioning.keys.list",
316
+ payload: { disabled: "true", limit: "2" },
317
+ check_lifecycle: false
318
+ ) do
319
+ { "data" => [{ "id" => "key_ruby_1" }] }
320
+ end
321
+ client.send(:with_lifecycle_and_telemetry, endpoint: "key.current", payload: {}, check_lifecycle: false) do
322
+ { "data" => { "id" => "key_ruby_current_1", "status" => "active" } }
323
+ end
324
+ client.send(:with_lifecycle_and_telemetry, endpoint: "provisioning.workspaces.list", payload: { limit: "2" }, check_lifecycle: false) do
325
+ { "data" => [{ "id" => "ws_ruby_1" }] }
326
+ end
327
+
328
+ lines = File.read(File.join(dir, "generations.jsonl")).lines
329
+ assert_equal 12, lines.length
330
+
331
+ expected_types = [
332
+ "models.list",
333
+ "providers",
334
+ "credits",
335
+ "activity",
336
+ "analytics",
337
+ "endpoints.list",
338
+ "organisations.list",
339
+ "pricing.models",
340
+ "pricing.calculate",
341
+ "provisioning.keys.list",
342
+ "key.current",
343
+ "provisioning.workspaces.list"
344
+ ]
345
+
346
+ expected_types.each_with_index do |expected_type, index|
347
+ entry = JSON.parse(lines[index])
348
+ assert_equal expected_type, entry["type"]
349
+ end
350
+
351
+ analytics_entry = JSON.parse(lines[4])
352
+ assert_equal "responses", analytics_entry.dig("response", "data", 0, "endpoint_id")
353
+
354
+ pricing_entry = JSON.parse(lines[8])
355
+ assert_equal "USD", pricing_entry.dig("response", "pricing", "currency")
356
+
357
+ current_key_entry = JSON.parse(lines[10])
358
+ assert_equal "key_ruby_current_1", current_key_entry.dig("response", "data", "id")
359
+
360
+ workspaces_entry = JSON.parse(lines[11])
361
+ assert_equal "ws_ruby_1", workspaces_entry.dig("response", "data", 0, "id")
362
+ end
363
+ end
364
+ end
@@ -0,0 +1,27 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class EndpointsTest < Minitest::Test
5
+ def test_list_endpoints_returns_payload
6
+ client = PhaseoSdk::Phaseo.new(
7
+ api_key: "test",
8
+ enable_deprecation_warnings: false
9
+ )
10
+
11
+ calls = []
12
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
13
+ calls << [method, path, query, headers, body]
14
+ {
15
+ "ok" => true,
16
+ "endpoints" => ["chat/completions", "responses", "files"],
17
+ "sample_models" => ["openai/gpt-5-nano"]
18
+ }
19
+ end
20
+
21
+ response = client.list_endpoints
22
+
23
+ assert_equal true, response["ok"]
24
+ assert_equal "openai/gpt-5-nano", response["sample_models"][0]
25
+ assert_equal [["GET", "/endpoints", nil, nil, nil]], calls
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class FilesTest < Minitest::Test
5
+ def test_upload_file_returns_payload
6
+ client = PhaseoSdk::Phaseo.new(
7
+ api_key: "test",
8
+ enable_deprecation_warnings: false
9
+ )
10
+
11
+ calls = []
12
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
13
+ calls << [method, path, query, headers, body]
14
+ { "id" => "file_123", "purpose" => "batch", "bytes" => 17 }
15
+ end
16
+
17
+ uploaded = client.upload_file(
18
+ "purpose" => "batch",
19
+ "file" => "data:application/json;base64,eyJ0ZXN0Ijp0cnVlfQ=="
20
+ )
21
+
22
+ assert_equal "file_123", uploaded["id"]
23
+ assert_equal [["POST", "/files", nil, nil, {
24
+ "purpose" => "batch",
25
+ "file" => "data:application/json;base64,eyJ0ZXN0Ijp0cnVlfQ=="
26
+ }]], calls
27
+ end
28
+
29
+ def test_retrieve_file_content_returns_bytes
30
+ client = PhaseoSdk::Phaseo.new(
31
+ api_key: "test",
32
+ enable_deprecation_warnings: false
33
+ )
34
+
35
+ calls = []
36
+ client.raw_client.define_singleton_method(:request_bytes) do |method:, path:, query: nil, headers: nil, body: nil|
37
+ calls << [method, path, query, headers, body]
38
+ "{\"ok\":true}\n"
39
+ end
40
+
41
+ content = client.retrieve_file_content("file_123")
42
+
43
+ assert_equal "{\"ok\":true}\n", content
44
+ assert_equal [["get", "/files/file_123/content", nil, nil, nil]], calls
45
+ end
46
+
47
+ def test_retrieve_file_content_surfaces_request_errors
48
+ client = PhaseoSdk::Phaseo.new(
49
+ api_key: "test",
50
+ enable_deprecation_warnings: false
51
+ )
52
+
53
+ client.raw_client.define_singleton_method(:request_bytes) do |**_args|
54
+ raise Phaseo::Gen::RequestError.new(
55
+ status_code: 404,
56
+ status_message: "Not Found",
57
+ response_body: "{\"error\":\"not found\"}"
58
+ )
59
+ end
60
+
61
+ error = assert_raises(Phaseo::Gen::RequestError) do
62
+ client.retrieve_file_content("file_missing_123")
63
+ end
64
+
65
+ assert_equal 404, error.status_code
66
+ end
67
+ end
@@ -0,0 +1,29 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class GenerationTest < Minitest::Test
5
+ def test_get_generation_returns_payload
6
+ client = PhaseoSdk::Phaseo.new(
7
+ api_key: "test",
8
+ enable_deprecation_warnings: false
9
+ )
10
+
11
+ calls = []
12
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
13
+ calls << [method, path, query, headers, body]
14
+ {
15
+ "id" => "gen_123",
16
+ "provider" => "openai",
17
+ "request_id" => "req_ruby_generation_1",
18
+ "status_code" => 200
19
+ }
20
+ end
21
+
22
+ response = client.get_generation("gen_123")
23
+
24
+ assert_equal "gen_123", response["id"]
25
+ assert_equal "openai", response["provider"]
26
+ assert_equal "req_ruby_generation_1", response["request_id"]
27
+ assert_equal [["GET", "/generations", { "id" => "gen_123" }, nil, nil]], calls
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class HealthTest < Minitest::Test
5
+ def test_health_returns_payload
6
+ client = PhaseoSdk::Phaseo.new(
7
+ api_key: "test",
8
+ enable_deprecation_warnings: false
9
+ )
10
+
11
+ calls = []
12
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
13
+ calls << [method, path, query, headers, body]
14
+ {
15
+ "status" => "ok",
16
+ "timestamp" => "2026-05-05T12:00:00.000Z"
17
+ }
18
+ end
19
+
20
+ response = client.health
21
+
22
+ assert_equal "ok", response["status"]
23
+ assert_equal "2026-05-05T12:00:00.000Z", response["timestamp"]
24
+ assert_equal [["GET", "/health", nil, nil, nil]], calls
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class LifecycleTest < Minitest::Test
5
+ def test_inactive_model_is_blocked_before_dispatch
6
+ warnings = []
7
+ logger = lambda do |level, message, _meta|
8
+ warnings << message if level == "warn"
9
+ end
10
+
11
+ client = PhaseoSdk::Phaseo.new(
12
+ api_key: "test",
13
+ logger: logger,
14
+ lifecycle_resolver: lambda do |model_id|
15
+ {
16
+ model_id: model_id,
17
+ status: "deprecated",
18
+ retirement_date: "2099-01-01T00:00:00Z",
19
+ replacement_model_id: "provider/new-model",
20
+ message: %[ [phaseo] Model "#{model_id}" is deprecated and scheduled for retirement on 2099-01-01T00:00:00Z. Use "provider/new-model" instead. ].strip
21
+ }
22
+ end
23
+ )
24
+
25
+ validation = client.validate_model("provider/old-model")
26
+ assert_equal false, validation[:ok]
27
+ assert_includes validation[:reason], "provider/new-model"
28
+
29
+ error = assert_raises(RuntimeError) do
30
+ client.send(:maybe_warn_for_payload, { model: "provider/old-model" })
31
+ end
32
+ assert_includes error.message, "provider/new-model"
33
+ assert_equal 0, warnings.length
34
+ end
35
+
36
+ def test_retired_model_is_blocked_without_warnings_as_errors
37
+ client = PhaseoSdk::Phaseo.new(
38
+ api_key: "test",
39
+ warnings_as_errors: false,
40
+ lifecycle_resolver: lambda do |model_id|
41
+ {
42
+ model_id: model_id,
43
+ status: "retired",
44
+ retirement_date: "2020-01-01T00:00:00Z",
45
+ message: %[ [phaseo] Model "#{model_id}" is retired as of 2020-01-01T00:00:00Z. ].strip
46
+ }
47
+ end
48
+ )
49
+
50
+ error = assert_raises(RuntimeError) do
51
+ client.send(:maybe_warn_for_payload, { model: "provider/retired-model" })
52
+ end
53
+ assert_includes error.message, "retired"
54
+ end
55
+ end
@@ -0,0 +1,39 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class ModelsTest < Minitest::Test
5
+ def test_list_models_preserves_availability_reason
6
+ client = PhaseoSdk::Phaseo.new(
7
+ api_key: "test",
8
+ enable_deprecation_warnings: false
9
+ )
10
+
11
+ calls = []
12
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
13
+ calls << [method, path, query, headers, body]
14
+ {
15
+ "ok" => true,
16
+ "availability_mode" => "all",
17
+ "models" => [
18
+ {
19
+ "id" => "openai/gpt-5-mini",
20
+ "providers" => [
21
+ {
22
+ "api_provider_id" => "openai",
23
+ "availability_status" => "coming_soon",
24
+ "availability_reason" => "scheduled"
25
+ }
26
+ ]
27
+ }
28
+ ]
29
+ }
30
+ end
31
+
32
+ response = client.list_models("availability" => "all")
33
+
34
+ assert_equal "all", response["availability_mode"]
35
+ assert_equal "coming_soon", response["models"][0]["providers"][0]["availability_status"]
36
+ assert_equal "scheduled", response["models"][0]["providers"][0]["availability_reason"]
37
+ assert_equal [["GET", "/models", {"availability" => "all"}, nil, nil]], calls
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class OrganisationsTest < Minitest::Test
5
+ def test_list_organisations_returns_payload
6
+ client = PhaseoSdk::Phaseo.new(
7
+ api_key: "test",
8
+ enable_deprecation_warnings: false
9
+ )
10
+
11
+ calls = []
12
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
13
+ calls << [method, path, query, headers, body]
14
+ {
15
+ "ok" => true,
16
+ "limit" => 2,
17
+ "offset" => 3,
18
+ "total" => 1,
19
+ "organisations" => [
20
+ {
21
+ "organisation_id" => "org_123",
22
+ "name" => "Anthropic",
23
+ "country_code" => "US",
24
+ "colour" => "#D97706"
25
+ }
26
+ ]
27
+ }
28
+ end
29
+
30
+ response = client.list_organisations("limit" => "2", "offset" => "3")
31
+
32
+ assert_equal true, response["ok"]
33
+ assert_equal "org_123", response["organisations"][0]["organisation_id"]
34
+ assert_equal [["GET", "/organisations", { "limit" => "2", "offset" => "3" }, nil, nil]], calls
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/index"
3
+
4
+ class PricingCalculateTest < Minitest::Test
5
+ def test_calculate_pricing_returns_payload
6
+ client = PhaseoSdk::Phaseo.new(
7
+ api_key: "test",
8
+ enable_deprecation_warnings: false
9
+ )
10
+
11
+ calls = []
12
+ client.raw_client.define_singleton_method(:request) do |method:, path:, query: nil, headers: nil, body: nil|
13
+ calls << [method, path, query, headers, body]
14
+ {
15
+ "ok" => true,
16
+ "pricing" => {
17
+ "total_cost_usd" => 0.00025,
18
+ "currency" => "USD"
19
+ }
20
+ }
21
+ end
22
+
23
+ payload = {
24
+ "provider" => "openai",
25
+ "model" => "openai/gpt-5-mini",
26
+ "endpoint" => "responses",
27
+ "usage" => { "input_tokens" => 1000 }
28
+ }
29
+
30
+ response = client.calculate_pricing(payload)
31
+
32
+ assert_equal true, response["ok"]
33
+ assert_equal 0.00025, response["pricing"]["total_cost_usd"]
34
+ assert_equal [["POST", "/pricing/calculate", nil, nil, payload]], calls
35
+ end
36
+ end