ai-lite 0.6.1 → 1.0.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.
data/test/ai_lite_test.rb CHANGED
@@ -20,8 +20,27 @@ class AiLiteTest < Minitest::Test
20
20
  end
21
21
  end
22
22
 
23
+ def provider
24
+ AiLite::OpenAI
25
+ end
26
+
23
27
  def setup
24
- AiLite.reset_configuration!
28
+ provider.reset_configuration!
29
+ end
30
+
31
+ def test_http_200_failed_or_incomplete_generation_is_not_success
32
+ client = provider.new(api_key: "test-key")
33
+ %w[failed cancelled incomplete].each do |state|
34
+ body = JSON.generate("id" => "resp_partial", "status" => state,
35
+ "incomplete_details" => { "reason" => "max_output_tokens" }, "output" => [])
36
+ with_stubbed_http(FakeResponse.new("200", body)) do
37
+ result = client.chat("Hi", debug: true)
38
+ assert_equal 200, result["status"]
39
+ assert_includes result["error"], state
40
+ assert_equal "resp_partial", result["response_id"]
41
+ refute_nil result["raw"]
42
+ end
43
+ end
25
44
  end
26
45
 
27
46
  def test_has_a_version_number
@@ -30,7 +49,7 @@ class AiLiteTest < Minitest::Test
30
49
 
31
50
  def test_stores_initializer_config_and_headers
32
51
  with_env("OPENAI_API_KEY" => nil, "OPEN_AI_TOKEN" => nil) do
33
- client = AiLite.new(
52
+ client = provider.new(
34
53
  api_key: "explicit-key",
35
54
  model: "gpt-test",
36
55
  image_model: "gpt-image-test",
@@ -57,7 +76,7 @@ class AiLiteTest < Minitest::Test
57
76
 
58
77
  def test_configure_sets_defaults_for_client
59
78
  with_env("OPENAI_API_KEY" => nil, "OPEN_AI_TOKEN" => nil) do
60
- AiLite.configure do |config|
79
+ provider.configure do |config|
61
80
  config.api_key = "configured-key"
62
81
  config.model = "gpt-config"
63
82
  config.moderation_model = "omni-moderation-test"
@@ -70,7 +89,7 @@ class AiLiteTest < Minitest::Test
70
89
  config.max_output_tokens = 750
71
90
  end
72
91
 
73
- client = AiLite.client
92
+ client = provider.client
74
93
 
75
94
  assert_equal "configured-key", client.api_key
76
95
  assert_equal "gpt-config", client.model
@@ -82,23 +101,23 @@ class AiLiteTest < Minitest::Test
82
101
  assert_equal "gpt-transcribe-test", client.transcription_model
83
102
  assert_equal 15, client.timeout
84
103
  assert_equal 750, client.max_output_tokens
85
- assert_same client, AiLite.client
104
+ assert_same client, provider.client
86
105
  end
87
106
  end
88
107
 
89
108
  def test_configure_resets_cached_client
90
- AiLite.configure { |config| config.api_key = "first-key" }
91
- first_client = AiLite.client
109
+ provider.configure { |config| config.api_key = "first-key" }
110
+ first_client = provider.client
92
111
 
93
- AiLite.configure { |config| config.api_key = "second-key" }
94
- second_client = AiLite.client
112
+ provider.configure { |config| config.api_key = "second-key" }
113
+ second_client = provider.client
95
114
 
96
115
  refute_same first_client, second_client
97
116
  assert_equal "second-key", second_client.api_key
98
117
  end
99
118
 
100
119
  def test_instance_arguments_override_configuration
101
- AiLite.configure do |config|
120
+ provider.configure do |config|
102
121
  config.api_key = "configured-key"
103
122
  config.model = "gpt-config"
104
123
  config.moderation_model = "omni-moderation-config"
@@ -111,7 +130,7 @@ class AiLiteTest < Minitest::Test
111
130
  config.max_output_tokens = 750
112
131
  end
113
132
 
114
- client = AiLite.new(
133
+ client = provider.new(
115
134
  api_key: "explicit-key",
116
135
  model: "gpt-explicit",
117
136
  moderation_model: "omni-moderation-explicit",
@@ -138,32 +157,32 @@ class AiLiteTest < Minitest::Test
138
157
 
139
158
  def test_api_key_fallback_order
140
159
  with_env("OPENAI_API_KEY" => "openai-key", "OPEN_AI_TOKEN" => "legacy-key") do
141
- assert_equal "openai-key", AiLite.new.api_key
142
- assert_equal "explicit-key", AiLite.new(api_key: "explicit-key").api_key
160
+ assert_equal "openai-key", provider.new.api_key
161
+ assert_equal "explicit-key", provider.new(api_key: "explicit-key").api_key
143
162
  end
144
163
 
145
164
  with_env("OPENAI_API_KEY" => nil, "OPEN_AI_TOKEN" => "legacy-key") do
146
- assert_equal "legacy-key", AiLite.new.api_key
165
+ assert_equal "legacy-key", provider.new.api_key
147
166
  end
148
167
  end
149
168
 
150
169
  def test_configured_api_key_takes_precedence_over_env
151
- AiLite.configure { |config| config.api_key = "configured-key" }
170
+ provider.configure { |config| config.api_key = "configured-key" }
152
171
 
153
172
  with_env("OPENAI_API_KEY" => "openai-key", "OPEN_AI_TOKEN" => "legacy-key") do
154
- assert_equal "configured-key", AiLite.new.api_key
155
- assert_equal "explicit-key", AiLite.new(api_key: "explicit-key").api_key
173
+ assert_equal "configured-key", provider.new.api_key
174
+ assert_equal "explicit-key", provider.new(api_key: "explicit-key").api_key
156
175
  end
157
176
  end
158
177
 
159
178
  def test_missing_api_key_raises
160
179
  with_env("OPENAI_API_KEY" => nil, "OPEN_AI_TOKEN" => nil) do
161
- assert_raises(ArgumentError) { AiLite.new }
180
+ assert_raises(ArgumentError) { provider.new }
162
181
  end
163
182
  end
164
183
 
165
184
  def test_chat_sends_post_to_responses_with_default_payload
166
- client = AiLite.new(api_key: "token-abc")
185
+ client = provider.new(api_key: "token-abc")
167
186
 
168
187
  with_stubbed_http(success_response("Hello!")) do |captured, _response|
169
188
  result = client.chat("Say hello")
@@ -189,7 +208,7 @@ class AiLiteTest < Minitest::Test
189
208
  end
190
209
 
191
210
  def test_chat_allows_overriding_max_output_tokens
192
- client = AiLite.new(api_key: "token-abc")
211
+ client = provider.new(api_key: "token-abc")
193
212
 
194
213
  with_stubbed_http(success_response("Short")) do |captured, _response|
195
214
  client.chat("Summarize", max_output_tokens: 500)
@@ -200,13 +219,13 @@ class AiLiteTest < Minitest::Test
200
219
  end
201
220
 
202
221
  def test_chat_uses_configured_max_output_tokens
203
- AiLite.configure do |config|
222
+ provider.configure do |config|
204
223
  config.api_key = "configured-key"
205
224
  config.max_output_tokens = 750
206
225
  end
207
226
 
208
227
  with_stubbed_http(success_response("Configured")) do |captured, _response|
209
- AiLite.chat("Use configured defaults")
228
+ provider.chat("Use configured defaults")
210
229
  payload = JSON.parse(captured[:http].last_request.body)
211
230
 
212
231
  assert_equal 750, payload["max_output_tokens"]
@@ -215,7 +234,7 @@ class AiLiteTest < Minitest::Test
215
234
  end
216
235
 
217
236
  def test_chat_includes_instructions_model_and_options
218
- client = AiLite.new(api_key: "token-abc")
237
+ client = provider.new(api_key: "token-abc")
219
238
 
220
239
  with_stubbed_http(success_response("Aye")) do |captured, _response|
221
240
  client.chat(
@@ -239,7 +258,7 @@ class AiLiteTest < Minitest::Test
239
258
  end
240
259
 
241
260
  def test_chat_supports_previous_response_id
242
- client = AiLite.new(api_key: "token-abc")
261
+ client = provider.new(api_key: "token-abc")
243
262
 
244
263
  with_stubbed_http(success_response("Follow-up")) do |captured, _response|
245
264
  result = client.chat(
@@ -255,7 +274,7 @@ class AiLiteTest < Minitest::Test
255
274
  end
256
275
 
257
276
  def test_chat_supports_message_array_input
258
- client = AiLite.new(api_key: "token-abc")
277
+ client = provider.new(api_key: "token-abc")
259
278
  messages = [
260
279
  { role: "developer", content: "Be concise." },
261
280
  { role: "user", content: "Say hello." }
@@ -276,7 +295,7 @@ class AiLiteTest < Minitest::Test
276
295
  end
277
296
 
278
297
  def test_chat_first_class_previous_response_id_overrides_options
279
- client = AiLite.new(api_key: "token-abc")
298
+ client = provider.new(api_key: "token-abc")
280
299
 
281
300
  with_stubbed_http(success_response("Follow-up")) do |captured, _response|
282
301
  client.chat(
@@ -291,7 +310,7 @@ class AiLiteTest < Minitest::Test
291
310
  end
292
311
 
293
312
  def test_chat_keeps_supporting_previous_response_id_through_options
294
- client = AiLite.new(api_key: "token-abc")
313
+ client = provider.new(api_key: "token-abc")
295
314
 
296
315
  with_stubbed_http(success_response("Follow-up")) do |captured, _response|
297
316
  client.chat("Continue", options: { previous_response_id: "resp_options" })
@@ -302,7 +321,7 @@ class AiLiteTest < Minitest::Test
302
321
  end
303
322
 
304
323
  def test_extracts_text_from_nested_output_text_items
305
- client = AiLite.new(api_key: "token-abc")
324
+ client = provider.new(api_key: "token-abc")
306
325
  body = JSON.generate(
307
326
  "id" => "resp_nested_123",
308
327
  "output" => [
@@ -328,7 +347,7 @@ class AiLiteTest < Minitest::Test
328
347
  end
329
348
 
330
349
  def test_parses_json_looking_output_with_string_keys
331
- client = AiLite.new(api_key: "token-abc")
350
+ client = provider.new(api_key: "token-abc")
332
351
 
333
352
  with_stubbed_http(success_response(JSON.generate("valid" => true))) do |_captured, _response|
334
353
  result = client.chat("Validate")
@@ -341,7 +360,7 @@ class AiLiteTest < Minitest::Test
341
360
  end
342
361
 
343
362
  def test_debug_true_returns_raw_success_response
344
- client = AiLite.new(api_key: "token-abc")
363
+ client = provider.new(api_key: "token-abc")
345
364
 
346
365
  with_stubbed_http(success_response("Hello!")) do |_captured, _response|
347
366
  result = client.chat("Say hello", debug: true)
@@ -364,7 +383,7 @@ class AiLiteTest < Minitest::Test
364
383
  end
365
384
 
366
385
  def test_moderate_sends_post_to_moderations_with_default_payload
367
- client = AiLite.new(api_key: "token-abc")
386
+ client = provider.new(api_key: "token-abc")
368
387
 
369
388
  with_stubbed_http(moderation_response) do |captured, _response|
370
389
  result = client.moderate("Some user submitted text")
@@ -389,13 +408,13 @@ class AiLiteTest < Minitest::Test
389
408
  end
390
409
 
391
410
  def test_moderate_uses_class_level_configured_client
392
- AiLite.configure do |config|
411
+ provider.configure do |config|
393
412
  config.api_key = "configured-key"
394
413
  config.moderation_model = "omni-moderation-config"
395
414
  end
396
415
 
397
416
  with_stubbed_http(moderation_response) do |captured, _response|
398
- AiLite.moderate("Use configured defaults")
417
+ provider.moderate("Use configured defaults")
399
418
  payload = JSON.parse(captured[:http].last_request.body)
400
419
 
401
420
  assert_equal "omni-moderation-config", payload["model"]
@@ -404,7 +423,7 @@ class AiLiteTest < Minitest::Test
404
423
  end
405
424
 
406
425
  def test_moderate_accepts_text_and_image_url_keywords
407
- client = AiLite.new(api_key: "token-abc")
426
+ client = provider.new(api_key: "token-abc")
408
427
 
409
428
  with_stubbed_http(moderation_response) do |captured, _response|
410
429
  client.moderate(
@@ -427,7 +446,7 @@ class AiLiteTest < Minitest::Test
427
446
  end
428
447
 
429
448
  def test_moderate_accepts_raw_openai_input_shape
430
- client = AiLite.new(api_key: "token-abc")
449
+ client = provider.new(api_key: "token-abc")
431
450
  input = [
432
451
  { type: "text", text: "Caption" },
433
452
  {
@@ -456,7 +475,7 @@ class AiLiteTest < Minitest::Test
456
475
  end
457
476
 
458
477
  def test_moderate_reads_image_path_as_data_url
459
- client = AiLite.new(api_key: "token-abc")
478
+ client = provider.new(api_key: "token-abc")
460
479
 
461
480
  Tempfile.create(["moderation", ".png"]) do |file|
462
481
  file.binmode
@@ -481,7 +500,7 @@ class AiLiteTest < Minitest::Test
481
500
  end
482
501
 
483
502
  def test_moderate_returns_all_results_for_multiple_inputs
484
- client = AiLite.new(api_key: "token-abc")
503
+ client = provider.new(api_key: "token-abc")
485
504
  safe_result = moderation_result(flagged: false)
486
505
  flagged_result = moderation_result(flagged: true)
487
506
 
@@ -495,7 +514,7 @@ class AiLiteTest < Minitest::Test
495
514
  end
496
515
 
497
516
  def test_moderate_local_input_errors_return_standard_envelope
498
- client = AiLite.new(api_key: "token-abc")
517
+ client = provider.new(api_key: "token-abc")
499
518
 
500
519
  result = client.moderate
501
520
 
@@ -507,7 +526,7 @@ class AiLiteTest < Minitest::Test
507
526
  end
508
527
 
509
528
  def test_embed_sends_post_to_embeddings_with_default_payload
510
- client = AiLite.new(api_key: "token-abc")
529
+ client = provider.new(api_key: "token-abc")
511
530
 
512
531
  with_stubbed_http(embedding_response([[0.12, -0.34, 0.56]])) do |captured, _response|
513
532
  result = client.embed("Text to vectorize")
@@ -532,7 +551,7 @@ class AiLiteTest < Minitest::Test
532
551
  end
533
552
 
534
553
  def test_embed_returns_vector_list_for_multiple_inputs
535
- client = AiLite.new(api_key: "token-abc")
554
+ client = provider.new(api_key: "token-abc")
536
555
  embeddings = [
537
556
  [0.11, 0.22, 0.33],
538
557
  [0.44, 0.55, 0.66]
@@ -550,7 +569,7 @@ class AiLiteTest < Minitest::Test
550
569
  end
551
570
 
552
571
  def test_embed_includes_options_dimensions_encoding_format_and_model
553
- client = AiLite.new(api_key: "token-abc")
572
+ client = provider.new(api_key: "token-abc")
554
573
 
555
574
  with_stubbed_http(embedding_response([[0.12, -0.34]])) do |captured, _response|
556
575
  client.embed(
@@ -573,13 +592,13 @@ class AiLiteTest < Minitest::Test
573
592
  end
574
593
 
575
594
  def test_embed_uses_class_level_configured_client
576
- AiLite.configure do |config|
595
+ provider.configure do |config|
577
596
  config.api_key = "configured-key"
578
597
  config.embedding_model = "text-embedding-config"
579
598
  end
580
599
 
581
600
  with_stubbed_http(embedding_response([[0.12, -0.34, 0.56]])) do |captured, _response|
582
- AiLite.embed("Use configured defaults")
601
+ provider.embed("Use configured defaults")
583
602
  payload = JSON.parse(captured[:http].last_request.body)
584
603
 
585
604
  assert_equal "text-embedding-config", payload["model"]
@@ -588,7 +607,7 @@ class AiLiteTest < Minitest::Test
588
607
  end
589
608
 
590
609
  def test_embed_debug_true_returns_raw_usage
591
- client = AiLite.new(api_key: "token-abc")
610
+ client = provider.new(api_key: "token-abc")
592
611
 
593
612
  with_stubbed_http(embedding_response([[0.12, -0.34, 0.56]])) do |_captured, _response|
594
613
  result = client.embed("Text to vectorize", debug: true)
@@ -599,7 +618,7 @@ class AiLiteTest < Minitest::Test
599
618
  end
600
619
 
601
620
  def test_image_sends_post_to_images_with_default_payload
602
- client = AiLite.new(api_key: "token-abc")
621
+ client = provider.new(api_key: "token-abc")
603
622
  image_data = Base64.strict_encode64("fake image")
604
623
 
605
624
  with_stubbed_http(image_response(b64_json: image_data)) do |captured, _response|
@@ -625,7 +644,7 @@ class AiLiteTest < Minitest::Test
625
644
  end
626
645
 
627
646
  def test_image_includes_options_size_quality_background_output_format_and_model
628
- client = AiLite.new(api_key: "token-abc")
647
+ client = provider.new(api_key: "token-abc")
629
648
 
630
649
  with_stubbed_http(image_response) do |captured, _response|
631
650
  client.image(
@@ -654,13 +673,13 @@ class AiLiteTest < Minitest::Test
654
673
  end
655
674
 
656
675
  def test_image_uses_class_level_configured_client
657
- AiLite.configure do |config|
676
+ provider.configure do |config|
658
677
  config.api_key = "configured-key"
659
678
  config.image_model = "gpt-image-config"
660
679
  end
661
680
 
662
681
  with_stubbed_http(image_response) do |captured, _response|
663
- AiLite.image("Use configured defaults")
682
+ provider.image("Use configured defaults")
664
683
  payload = JSON.parse(captured[:http].last_request.body)
665
684
 
666
685
  assert_equal "gpt-image-config", payload["model"]
@@ -669,7 +688,7 @@ class AiLiteTest < Minitest::Test
669
688
  end
670
689
 
671
690
  def test_image_writes_decoded_content_to_output_path
672
- client = AiLite.new(api_key: "token-abc")
691
+ client = provider.new(api_key: "token-abc")
673
692
  image_data = Base64.strict_encode64("fake image")
674
693
 
675
694
  Tempfile.create(["generated", ".png"]) do |file|
@@ -683,7 +702,7 @@ class AiLiteTest < Minitest::Test
683
702
  end
684
703
 
685
704
  def test_image_debug_true_returns_raw_usage
686
- client = AiLite.new(api_key: "token-abc")
705
+ client = provider.new(api_key: "token-abc")
687
706
  image_data = Base64.strict_encode64("fake image")
688
707
 
689
708
  with_stubbed_http(image_response(b64_json: image_data)) do |_captured, _response|
@@ -695,7 +714,7 @@ class AiLiteTest < Minitest::Test
695
714
  end
696
715
 
697
716
  def test_image_output_path_without_image_data_returns_standard_envelope
698
- client = AiLite.new(api_key: "token-abc")
717
+ client = provider.new(api_key: "token-abc")
699
718
 
700
719
  Tempfile.create(["generated", ".png"]) do |file|
701
720
  with_stubbed_http(image_response(b64_json: nil)) do |_captured, _response|
@@ -711,7 +730,7 @@ class AiLiteTest < Minitest::Test
711
730
  end
712
731
 
713
732
  def test_speak_sends_post_to_audio_speech_with_default_payload
714
- client = AiLite.new(api_key: "token-abc")
733
+ client = provider.new(api_key: "token-abc")
715
734
 
716
735
  with_stubbed_http(speech_response("fake audio")) do |captured, _response|
717
736
  result = client.speak("Read this aloud")
@@ -737,7 +756,7 @@ class AiLiteTest < Minitest::Test
737
756
  end
738
757
 
739
758
  def test_speak_includes_options_response_format_speed_instructions_model_and_voice
740
- client = AiLite.new(api_key: "token-abc")
759
+ client = provider.new(api_key: "token-abc")
741
760
 
742
761
  with_stubbed_http(speech_response) do |captured, _response|
743
762
  client.speak(
@@ -764,14 +783,14 @@ class AiLiteTest < Minitest::Test
764
783
  end
765
784
 
766
785
  def test_speak_uses_class_level_configured_client
767
- AiLite.configure do |config|
786
+ provider.configure do |config|
768
787
  config.api_key = "configured-key"
769
788
  config.speech_model = "gpt-speech-config"
770
789
  config.speech_voice = "marin"
771
790
  end
772
791
 
773
792
  with_stubbed_http(speech_response) do |captured, _response|
774
- AiLite.speak("Use configured defaults")
793
+ provider.speak("Use configured defaults")
775
794
  payload = JSON.parse(captured[:http].last_request.body)
776
795
 
777
796
  assert_equal "gpt-speech-config", payload["model"]
@@ -781,7 +800,7 @@ class AiLiteTest < Minitest::Test
781
800
  end
782
801
 
783
802
  def test_speak_writes_audio_to_output_path_and_returns_metadata
784
- client = AiLite.new(api_key: "token-abc")
803
+ client = provider.new(api_key: "token-abc")
785
804
 
786
805
  Tempfile.create(["speech", ".mp3"]) do |file|
787
806
  with_stubbed_http(speech_response("fake audio")) do |_captured, _response|
@@ -801,7 +820,7 @@ class AiLiteTest < Minitest::Test
801
820
  end
802
821
 
803
822
  def test_speak_base64_true_returns_encoded_audio
804
- client = AiLite.new(api_key: "token-abc")
823
+ client = provider.new(api_key: "token-abc")
805
824
 
806
825
  with_stubbed_http(speech_response("fake audio")) do |_captured, _response|
807
826
  result = client.speak("Encode this", base64: true)
@@ -812,7 +831,7 @@ class AiLiteTest < Minitest::Test
812
831
  end
813
832
 
814
833
  def test_speak_debug_true_returns_raw_audio
815
- client = AiLite.new(api_key: "token-abc")
834
+ client = provider.new(api_key: "token-abc")
816
835
 
817
836
  with_stubbed_http(speech_response("fake audio")) do |_captured, _response|
818
837
  result = client.speak("Debug this", debug: true)
@@ -823,7 +842,7 @@ class AiLiteTest < Minitest::Test
823
842
  end
824
843
 
825
844
  def test_speak_http_errors_return_standard_envelope
826
- client = AiLite.new(api_key: "token-abc")
845
+ client = provider.new(api_key: "token-abc")
827
846
  body = JSON.generate("error" => { "message" => "Unsupported voice" })
828
847
 
829
848
  with_stubbed_http(FakeResponse.new("400", body)) do |_captured, _response|
@@ -838,7 +857,7 @@ class AiLiteTest < Minitest::Test
838
857
  end
839
858
 
840
859
  def test_transcribe_sends_post_to_audio_transcriptions_with_default_multipart_payload
841
- client = AiLite.new(api_key: "token-abc")
860
+ client = provider.new(api_key: "token-abc")
842
861
 
843
862
  Tempfile.create(["meeting", ".mp3"]) do |file|
844
863
  file.binmode
@@ -868,7 +887,7 @@ class AiLiteTest < Minitest::Test
868
887
  end
869
888
 
870
889
  def test_transcribe_includes_options_and_optional_fields
871
- client = AiLite.new(api_key: "token-abc")
890
+ client = provider.new(api_key: "token-abc")
872
891
 
873
892
  Tempfile.create(["meeting", ".wav"]) do |file|
874
893
  file.binmode
@@ -908,7 +927,7 @@ class AiLiteTest < Minitest::Test
908
927
  end
909
928
 
910
929
  def test_transcribe_uses_class_level_configured_client
911
- AiLite.configure do |config|
930
+ provider.configure do |config|
912
931
  config.api_key = "configured-key"
913
932
  config.transcription_model = "gpt-transcription-config"
914
933
  end
@@ -919,7 +938,7 @@ class AiLiteTest < Minitest::Test
919
938
  file.flush
920
939
 
921
940
  with_stubbed_http(transcription_response) do |captured, _response|
922
- AiLite.transcribe(file.path)
941
+ provider.transcribe(file.path)
923
942
  request = captured[:http].last_request
924
943
 
925
944
  assert_multipart_field request.body, "model", "gpt-transcription-config"
@@ -929,7 +948,7 @@ class AiLiteTest < Minitest::Test
929
948
  end
930
949
 
931
950
  def test_transcribe_returns_plain_text_response_formats
932
- client = AiLite.new(api_key: "token-abc")
951
+ client = provider.new(api_key: "token-abc")
933
952
 
934
953
  Tempfile.create(["meeting", ".webm"]) do |file|
935
954
  file.binmode
@@ -948,7 +967,7 @@ class AiLiteTest < Minitest::Test
948
967
  end
949
968
 
950
969
  def test_transcribe_debug_true_returns_raw_response
951
- client = AiLite.new(api_key: "token-abc")
970
+ client = provider.new(api_key: "token-abc")
952
971
 
953
972
  Tempfile.create(["meeting", ".ogg"]) do |file|
954
973
  file.binmode
@@ -965,7 +984,7 @@ class AiLiteTest < Minitest::Test
965
984
  end
966
985
 
967
986
  def test_transcribe_http_errors_return_standard_envelope
968
- client = AiLite.new(api_key: "token-abc")
987
+ client = provider.new(api_key: "token-abc")
969
988
  body = JSON.generate("error" => { "message" => "Unsupported audio format" })
970
989
 
971
990
  Tempfile.create(["meeting", ".mp3"]) do |file|
@@ -986,7 +1005,7 @@ class AiLiteTest < Minitest::Test
986
1005
  end
987
1006
 
988
1007
  def test_transcribe_local_file_errors_return_standard_envelope
989
- client = AiLite.new(api_key: "token-abc")
1008
+ client = provider.new(api_key: "token-abc")
990
1009
 
991
1010
  missing_result = client.transcribe("tmp/missing-audio.mp3")
992
1011
  assert_nil missing_result["content"]
@@ -1003,7 +1022,7 @@ class AiLiteTest < Minitest::Test
1003
1022
  end
1004
1023
 
1005
1024
  def test_http_errors_return_standard_envelope
1006
- client = AiLite.new(api_key: "token-abc")
1025
+ client = provider.new(api_key: "token-abc")
1007
1026
  body = JSON.generate("error" => { "message" => "Invalid API key" })
1008
1027
 
1009
1028
  with_stubbed_http(FakeResponse.new("401", body)) do |_captured, _response|
@@ -1018,7 +1037,7 @@ class AiLiteTest < Minitest::Test
1018
1037
  end
1019
1038
 
1020
1039
  def test_debug_true_returns_raw_http_error_response
1021
- client = AiLite.new(api_key: "token-abc")
1040
+ client = provider.new(api_key: "token-abc")
1022
1041
  body = JSON.generate("error" => { "message" => "Invalid API key" })
1023
1042
 
1024
1043
  with_stubbed_http(FakeResponse.new("401", body)) do |_captured, _response|
@@ -1032,7 +1051,7 @@ class AiLiteTest < Minitest::Test
1032
1051
  end
1033
1052
 
1034
1053
  def test_response_parse_errors_return_standard_envelope
1035
- client = AiLite.new(api_key: "token-abc")
1054
+ client = provider.new(api_key: "token-abc")
1036
1055
 
1037
1056
  with_stubbed_http(FakeResponse.new("200", "not-json")) do |_captured, _response|
1038
1057
  result = client.chat("Say hello")
@@ -1046,7 +1065,7 @@ class AiLiteTest < Minitest::Test
1046
1065
  end
1047
1066
 
1048
1067
  def test_debug_true_returns_raw_response_parse_error_body
1049
- client = AiLite.new(api_key: "token-abc")
1068
+ client = provider.new(api_key: "token-abc")
1050
1069
 
1051
1070
  with_stubbed_http(FakeResponse.new("200", "not-json")) do |_captured, _response|
1052
1071
  result = client.chat("Say hello", debug: true)
@@ -1059,7 +1078,7 @@ class AiLiteTest < Minitest::Test
1059
1078
  end
1060
1079
 
1061
1080
  def test_network_errors_return_standard_envelope
1062
- client = AiLite.new(api_key: "token-abc")
1081
+ client = provider.new(api_key: "token-abc")
1063
1082
  original_start = Net::HTTP.method(:start)
1064
1083
 
1065
1084
  Net::HTTP.singleton_class.send(:define_method, :start) do |_host, _port, use_ssl:, &_block|
@@ -1208,3 +1227,108 @@ class AiLiteTest < Minitest::Test
1208
1227
  Net::HTTP.singleton_class.send(:define_method, :start, original_start)
1209
1228
  end
1210
1229
  end
1230
+
1231
+ # Run the same HTTP and result contract against every legacy entry point.
1232
+ class AiLiteLegacyTest < AiLiteTest
1233
+ def provider
1234
+ AiLite
1235
+ end
1236
+ end
1237
+
1238
+ class AiLiteNamespaceTest < Minitest::Test
1239
+ def setup
1240
+ AiLite::OpenAI.reset_configuration!
1241
+ end
1242
+
1243
+ def test_provider_calls_are_quiet_and_constants_remain_compatible
1244
+ _stdout, stderr = capture_io do
1245
+ AiLite::OpenAI.configure { |config| config.api_key = "test-key" }
1246
+ assert_instance_of AiLite::OpenAI, AiLite::OpenAI.client
1247
+ AiLite::OpenAI.new
1248
+ end
1249
+
1250
+ assert_empty stderr
1251
+ assert_same AiLite::OpenAI::Configuration, AiLite::Configuration
1252
+ assert_equal AiLite::OpenAI::API_BASE_URL, AiLite::API_BASE_URL
1253
+ refute AiLite::OpenAI < AiLite
1254
+ end
1255
+
1256
+ def test_legacy_configuration_and_client_are_shared_with_openai
1257
+ capture_io do
1258
+ AiLite.configure { |config| config.api_key = "legacy-key" }
1259
+ assert_same AiLite.configuration, AiLite::OpenAI.configuration
1260
+ assert_same AiLite.client, AiLite::OpenAI.client
1261
+ assert_equal "legacy-key", AiLite::OpenAI.new.api_key
1262
+ assert_instance_of AiLite::OpenAI, AiLite.new
1263
+
1264
+ first_client = AiLite.client
1265
+ AiLite::OpenAI.configure { |config| config.api_key = "provider-key" }
1266
+ refute_same first_client, AiLite.client
1267
+ assert_equal "provider-key", AiLite.new.api_key
1268
+
1269
+ second_client = AiLite::OpenAI.client
1270
+ AiLite.reset_client!
1271
+ refute_same second_client, AiLite::OpenAI.client
1272
+ AiLite.reset_configuration!
1273
+ assert_nil AiLite::OpenAI.configuration.api_key
1274
+ end
1275
+ end
1276
+
1277
+ def test_each_legacy_entry_point_warns_only_once_and_preserves_arguments
1278
+ previous = AiLite.instance_variable_get(:@deprecated_entry_points)
1279
+ AiLite.instance_variable_set(:@deprecated_entry_points, {})
1280
+ methods = %i[new configuration configure reset_configuration! client reset_client!
1281
+ chat chat_stream moderate embed image speak transcribe
1282
+ available_models model_available? configured_models check_configured_models]
1283
+
1284
+ stdout, stderr = capture_io do
1285
+ methods.each do |name|
1286
+ test_case = self
1287
+ handler = lambda do |*args, **kwargs, &block|
1288
+ test_case.assert_equal ["input"], args
1289
+ test_case.assert_equal({ debug: true }, kwargs)
1290
+ test_case.assert_equal :block_result, block.call
1291
+ :result
1292
+ end
1293
+ next if name == :new
1294
+
1295
+ original = AiLite::OpenAI.method(name)
1296
+ begin
1297
+ AiLite::OpenAI.define_singleton_method(name, &handler)
1298
+ 2.times do
1299
+ assert_equal :result, AiLite.public_send(name, "input", debug: true) { :block_result }
1300
+ end
1301
+ ensure
1302
+ AiLite::OpenAI.define_singleton_method(name, original)
1303
+ end
1304
+ end
1305
+ 2.times { AiLite.new(api_key: "test-key") }
1306
+ end
1307
+
1308
+ assert_empty stdout
1309
+ assert_equal methods.length, stderr.lines.length
1310
+ methods.each do |name|
1311
+ assert_includes stderr, "AiLite.#{name} is deprecated. Use AiLite::OpenAI.#{name} instead."
1312
+ end
1313
+ assert_includes stderr, "removed in v2.0"
1314
+ ensure
1315
+ AiLite.instance_variable_set(:@deprecated_entry_points, previous)
1316
+ end
1317
+
1318
+ def test_provider_can_be_required_directly_in_a_fresh_process
1319
+ require "open3"
1320
+ require "rbconfig"
1321
+ script = <<~'CODE'
1322
+ require "ai_lite/openai"
1323
+ abort unless AiLite::OpenAI.new(api_key: "test-key").model == AiLite::OpenAI::DEFAULT_MODEL
1324
+ require "ai_lite"
1325
+ abort unless AiLite::Configuration.equal?(AiLite::OpenAI::Configuration)
1326
+ CODE
1327
+ stdout, stderr, status = Open3.capture3(
1328
+ RbConfig.ruby, "-I#{File.expand_path('../lib', __dir__)}", "-e", script
1329
+ )
1330
+ assert status.success?, stderr
1331
+ assert_empty stdout
1332
+ assert_empty stderr
1333
+ end
1334
+ end