ruby_llm-providers-groq 2.0.0.pre.1

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.flayignore +1 -0
  3. data/.github/workflows/ci.yml +30 -0
  4. data/.github/workflows/gitleaks.yml +22 -0
  5. data/.github/workflows/release.yml +36 -0
  6. data/.overcommit.yml +31 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +29 -0
  9. data/Archspec.rb +14 -0
  10. data/LICENSE +21 -0
  11. data/README.md +57 -0
  12. data/lib/ruby_llm/providers/groq.rb +70 -0
  13. data/models.json +218 -0
  14. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_openai_gpt_oss_120b_can_handle_a_multi_turn_conversation.yml +153 -0
  15. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_openai_gpt_oss_120b_can_have_a_basic_conversation.yml +75 -0
  16. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_openai_gpt_oss_120b_can_use_tools.yml +153 -0
  17. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_openai_gpt_oss_120b_returns_structured_output.yml +80 -0
  18. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_openai_gpt_oss_120b_returns_the_raw_response.yml +79 -0
  19. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_openai_gpt_oss_120b_supports_streaming_responses.yml +252 -0
  20. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_qwen_qwen3_8_27b_can_handle_a_multi_turn_conversation.yml +160 -0
  21. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_qwen_qwen3_8_27b_can_have_a_basic_conversation.yml +77 -0
  22. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_qwen_qwen3_8_27b_can_use_tools.yml +152 -0
  23. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_qwen_qwen3_8_27b_returns_structured_output.yml +77 -0
  24. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_qwen_qwen3_8_27b_returns_the_raw_response.yml +77 -0
  25. data/spec/fixtures/vcr_cassettes/rubyllm_chat_groq_qwen_qwen3_8_27b_supports_streaming_responses.yml +126 -0
  26. data/spec/ruby_llm/chat_schema_spec.rb +30 -0
  27. data/spec/ruby_llm/chat_spec.rb +35 -0
  28. data/spec/ruby_llm/chat_streaming_spec.rb +22 -0
  29. data/spec/ruby_llm/chat_tools_spec.rb +31 -0
  30. data/spec/ruby_llm/embedding_spec.rb +49 -0
  31. data/spec/ruby_llm/image_spec.rb +23 -0
  32. data/spec/ruby_llm/models_spec.rb +11 -0
  33. data/spec/ruby_llm/moderation_spec.rb +22 -0
  34. data/spec/ruby_llm/providers/groq_spec.rb +32 -0
  35. data/spec/ruby_llm/rerank_spec.rb +23 -0
  36. data/spec/ruby_llm/speech_spec.rb +25 -0
  37. data/spec/ruby_llm/video_spec.rb +27 -0
  38. data/spec/spec_helper.rb +26 -0
  39. data/spec/support/models.rb +22 -0
  40. data/spec/support/rubyllm_configuration.rb +14 -0
  41. data/spec/support/vcr_configuration.rb +16 -0
  42. metadata +105 -0
@@ -0,0 +1,152 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.groq.com/openai/v1/responses
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"model":"qwen/qwen3.8-27b","input":[{"role":"user","content":"What''s
9
+ the weather in Berlin? Use 52.5200, 13.4050."}],"stream":false,"store":false,"tools":[{"type":"function","name":"weather","description":"Gets
10
+ current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude"},"longitude":{"type":"string","description":"Longitude"}},"required":["latitude","longitude"],"additionalProperties":false,"strict":true},"strict":false}]}'
11
+ headers:
12
+ User-Agent:
13
+ - Faraday v2.14.4
14
+ Authorization:
15
+ - Bearer <AUTH_TOKEN>
16
+ Content-Type:
17
+ - application/json
18
+ Accept-Encoding:
19
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
20
+ Accept:
21
+ - "*/*"
22
+ response:
23
+ status:
24
+ code: 200
25
+ message: OK
26
+ headers:
27
+ Date:
28
+ - Wed, 07 Oct 2026 16:48:59 GMT
29
+ Content-Type:
30
+ - application/json
31
+ Transfer-Encoding:
32
+ - chunked
33
+ Connection:
34
+ - keep-alive
35
+ Cache-Control:
36
+ - private, max-age=0, no-store, no-cache, must-revalidate
37
+ Server:
38
+ - cloudflare
39
+ Vary:
40
+ - Origin
41
+ X-Groq-Region:
42
+ - dls
43
+ X-Ratelimit-Limit-Requests:
44
+ - '1000'
45
+ X-Ratelimit-Limit-Tokens:
46
+ - '8000'
47
+ X-Ratelimit-Remaining-Requests:
48
+ - '994'
49
+ X-Ratelimit-Remaining-Tokens:
50
+ - '7281'
51
+ X-Ratelimit-Reset-Requests:
52
+ - 8m38.4s
53
+ X-Ratelimit-Reset-Tokens:
54
+ - 23.34s
55
+ X-Request-Id:
56
+ - req_01m4bm9b16ekpr23jjhgq99gdc
57
+ Via:
58
+ - 1.1 google
59
+ Alt-Svc:
60
+ - h3=":443"; ma=86400
61
+ Cf-Cache-Status:
62
+ - DYNAMIC
63
+ Set-Cookie:
64
+ - __cf_bm=6n0BSCZAb4sticznTXikANIWQ4hRD0KAdXlgDEiQg1s-1791391738.8197184-1.0.1.1-5D88MLf22WDRiQhFRiEcyUBCinTMXeWMYkWGMy50.3eUrzi._wMKyeY3A7dYYzOmKtSIMqZPoXBG7d29JP9mB9CnwJtJdaHHSfS.4UK7khx5Hw_t5.J1DSHEBagnmj.x;
65
+ HttpOnly; SameSite=None; Secure; Path=/; Domain=groq.com; Expires=Wed, 07
66
+ Oct 2026 17:18:59 GMT
67
+ Strict-Transport-Security:
68
+ - max-age=15552000
69
+ Cf-Ray:
70
+ - a46e657f9c7102ba-HEL
71
+ body:
72
+ encoding: ASCII-8BIT
73
+ string: '{"id":"resp_01m4bm9b82et4rkabjx8zjy7dg","object":"response","status":"completed","created_at":1791391739,"output":[{"type":"reasoning","id":"resp_01m4bm9b82et58g8q94rw0ejcc","status":"completed","summary":[]},{"type":"function_call","id":"need8cqcy","status":"completed","call_id":"need8cqcy","name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"}],"previous_response_id":null,"model":"qwen/qwen3.8-27b","reasoning":{"effort":"none"},"max_output_tokens":null,"text":{"format":{"type":"text"}},"tools":[{"type":"function","name":"weather","description":"Gets
74
+ current weather for a location","parameters":{"additionalProperties":false,"properties":{"latitude":{"description":"Latitude","type":"string"},"longitude":{"description":"Longitude","type":"string"}},"required":["latitude","longitude"],"strict":true,"type":"object"},"strict":false}],"tool_choice":"auto","truncation":"disabled","metadata":{},"groq":null,"temperature":1,"top_p":1,"user":null,"service_tier":"default","background":false,"error":null,"incomplete_details":null,"usage":{"input_tokens":330,"input_tokens_details":{"cached_tokens":0},"output_tokens":50,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":380},"parallel_tool_calls":true,"store":false,"top_logprobs":null,"max_tool_calls":null}
75
+
76
+ '
77
+ recorded_at: Wed, 07 Oct 2026 16:48:59 GMT
78
+ - request:
79
+ method: post
80
+ uri: https://api.groq.com/openai/v1/responses
81
+ body:
82
+ encoding: UTF-8
83
+ string: '{"model":"qwen/qwen3.8-27b","input":[{"role":"user","content":"What''s
84
+ the weather in Berlin? Use 52.5200, 13.4050."},{"type":"function_call","call_id":"need8cqcy","name":"weather","arguments":"{\"latitude\":\"52.5200\",\"longitude\":\"13.4050\"}"},{"type":"function_call_output","call_id":"need8cqcy","output":"Current
85
+ weather at 52.5200, 13.4050: 15°C, Wind: 10 km/h"}],"stream":false,"store":false,"tools":[{"type":"function","name":"weather","description":"Gets
86
+ current weather for a location","parameters":{"type":"object","properties":{"latitude":{"type":"string","description":"Latitude"},"longitude":{"type":"string","description":"Longitude"}},"required":["latitude","longitude"],"additionalProperties":false,"strict":true},"strict":false}]}'
87
+ headers:
88
+ User-Agent:
89
+ - Faraday v2.14.4
90
+ Authorization:
91
+ - Bearer <AUTH_TOKEN>
92
+ Content-Type:
93
+ - application/json
94
+ Accept-Encoding:
95
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
96
+ Accept:
97
+ - "*/*"
98
+ response:
99
+ status:
100
+ code: 200
101
+ message: OK
102
+ headers:
103
+ Date:
104
+ - Wed, 07 Oct 2026 16:48:59 GMT
105
+ Content-Type:
106
+ - application/json
107
+ Transfer-Encoding:
108
+ - chunked
109
+ Connection:
110
+ - keep-alive
111
+ Cache-Control:
112
+ - private, max-age=0, no-store, no-cache, must-revalidate
113
+ Server:
114
+ - cloudflare
115
+ Vary:
116
+ - Origin
117
+ X-Groq-Region:
118
+ - dls
119
+ X-Ratelimit-Limit-Requests:
120
+ - '1000'
121
+ X-Ratelimit-Limit-Tokens:
122
+ - '8000'
123
+ X-Ratelimit-Remaining-Requests:
124
+ - '993'
125
+ X-Ratelimit-Remaining-Tokens:
126
+ - '6708'
127
+ X-Ratelimit-Reset-Requests:
128
+ - 10m4.8s
129
+ X-Ratelimit-Reset-Tokens:
130
+ - 35.699s
131
+ X-Request-Id:
132
+ - req_01m4bm9bj5e0tbrfxv3ggsc541
133
+ Via:
134
+ - 1.1 google
135
+ Alt-Svc:
136
+ - h3=":443"; ma=86400
137
+ Cf-Cache-Status:
138
+ - DYNAMIC
139
+ Set-Cookie:
140
+ - __cf_bm=sMd2yUwGBBEW3i4mnVSFWKBtbgB1nhkCWBTAqofoNL4-1791391739.3726306-1.0.1.1-_RWoeDDTaqG4pxnQ8oyitUmC_1.IGF8x8EiPyB73b7nG.De3EC_TmPxh_fYBOADgdpgQQb.lhRJdHmHxq2FABtPQDuifhYzQhU9Md76q3xQ04NQHmp5z.C.9AahrVONy;
141
+ HttpOnly; SameSite=None; Secure; Path=/; Domain=groq.com; Expires=Wed, 07
142
+ Oct 2026 17:18:59 GMT
143
+ Strict-Transport-Security:
144
+ - max-age=15552000
145
+ Cf-Ray:
146
+ - a46e65831e0d8d58-HEL
147
+ body:
148
+ encoding: ASCII-8BIT
149
+ string: !binary |-
150
+ eyJpZCI6InJlc3BfMDFtNGJtOWJucmU3ZWJheTFiNnJwbTdnY2IiLCJvYmplY3QiOiJyZXNwb25zZSIsInN0YXR1cyI6ImNvbXBsZXRlZCIsImNyZWF0ZWRfYXQiOjE3OTEzOTE3MzksIm91dHB1dCI6W3sidHlwZSI6InJlYXNvbmluZyIsImlkIjoicmVzcF8wMW00Ym05Ym5yZTdlcmYyMW1zeDgzczl5eSIsInN0YXR1cyI6ImNvbXBsZXRlZCIsInN1bW1hcnkiOltdfSx7InR5cGUiOiJtZXNzYWdlIiwiaWQiOiJtc2dfMDFtNGJtOWJucmU3ZjgxNmM1NnlyYmhycGsiLCJzdGF0dXMiOiJjb21wbGV0ZWQiLCJyb2xlIjoiYXNzaXN0YW50IiwiY29udGVudCI6W3sidHlwZSI6Im91dHB1dF90ZXh0IiwidGV4dCI6IlRoZSBjdXJyZW50IHdlYXRoZXIgaW4gQmVybGluIGlzIDE1wrBDIHdpdGggYSB3aW5kIHNwZWVkIG9mIDEwIGttL2guIiwiYW5ub3RhdGlvbnMiOltdLCJsb2dwcm9icyI6bnVsbH1dfV0sInByZXZpb3VzX3Jlc3BvbnNlX2lkIjpudWxsLCJtb2RlbCI6InF3ZW4vcXdlbjMuOC0yN2IiLCJyZWFzb25pbmciOnsiZWZmb3J0Ijoibm9uZSJ9LCJtYXhfb3V0cHV0X3Rva2VucyI6bnVsbCwidGV4dCI6eyJmb3JtYXQiOnsidHlwZSI6InRleHQifX0sInRvb2xzIjpbeyJ0eXBlIjoiZnVuY3Rpb24iLCJuYW1lIjoid2VhdGhlciIsImRlc2NyaXB0aW9uIjoiR2V0cyBjdXJyZW50IHdlYXRoZXIgZm9yIGEgbG9jYXRpb24iLCJwYXJhbWV0ZXJzIjp7ImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjpmYWxzZSwicHJvcGVydGllcyI6eyJsYXRpdHVkZSI6eyJkZXNjcmlwdGlvbiI6IkxhdGl0dWRlIiwidHlwZSI6InN0cmluZyJ9LCJsb25naXR1ZGUiOnsiZGVzY3JpcHRpb24iOiJMb25naXR1ZGUiLCJ0eXBlIjoic3RyaW5nIn19LCJyZXF1aXJlZCI6WyJsYXRpdHVkZSIsImxvbmdpdHVkZSJdLCJzdHJpY3QiOnRydWUsInR5cGUiOiJvYmplY3QifSwic3RyaWN0IjpmYWxzZX1dLCJ0b29sX2Nob2ljZSI6ImF1dG8iLCJ0cnVuY2F0aW9uIjoiZGlzYWJsZWQiLCJtZXRhZGF0YSI6e30sImdyb3EiOm51bGwsInRlbXBlcmF0dXJlIjoxLCJ0b3BfcCI6MSwidXNlciI6bnVsbCwic2VydmljZV90aWVyIjoiZGVmYXVsdCIsImJhY2tncm91bmQiOmZhbHNlLCJlcnJvciI6bnVsbCwiaW5jb21wbGV0ZV9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjQzMCwiaW5wdXRfdG9rZW5zX2RldGFpbHMiOnsiY2FjaGVkX3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjIyLCJvdXRwdXRfdG9rZW5zX2RldGFpbHMiOnsicmVhc29uaW5nX3Rva2VucyI6MH0sInRvdGFsX3Rva2VucyI6NDUyfSwicGFyYWxsZWxfdG9vbF9jYWxscyI6dHJ1ZSwic3RvcmUiOmZhbHNlLCJ0b3BfbG9ncHJvYnMiOm51bGwsIm1heF90b29sX2NhbGxzIjpudWxsfQo=
151
+ recorded_at: Wed, 07 Oct 2026 16:48:59 GMT
152
+ recorded_with: VCR 6.4.0
@@ -0,0 +1,77 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.groq.com/openai/v1/responses
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"model":"qwen/qwen3.8-27b","input":[{"role":"user","content":"Generate
9
+ a person named John who is 30 years old"}],"stream":false,"store":false,"text":{"format":{"type":"json_schema","name":"response","schema":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"}},"required":["name","age"],"additionalProperties":false},"strict":true}}}'
10
+ headers:
11
+ User-Agent:
12
+ - Faraday v2.14.4
13
+ Authorization:
14
+ - Bearer <AUTH_TOKEN>
15
+ Content-Type:
16
+ - application/json
17
+ Accept-Encoding:
18
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
19
+ Accept:
20
+ - "*/*"
21
+ response:
22
+ status:
23
+ code: 200
24
+ message: OK
25
+ headers:
26
+ Date:
27
+ - Wed, 07 Oct 2026 16:48:53 GMT
28
+ Content-Type:
29
+ - application/json
30
+ Transfer-Encoding:
31
+ - chunked
32
+ Connection:
33
+ - keep-alive
34
+ Cache-Control:
35
+ - private, max-age=0, no-store, no-cache, must-revalidate
36
+ Server:
37
+ - cloudflare
38
+ Vary:
39
+ - Origin
40
+ X-Groq-Region:
41
+ - dls
42
+ X-Ratelimit-Limit-Requests:
43
+ - '1000'
44
+ X-Ratelimit-Limit-Tokens:
45
+ - '8000'
46
+ X-Ratelimit-Remaining-Requests:
47
+ - '999'
48
+ X-Ratelimit-Remaining-Tokens:
49
+ - '7791'
50
+ X-Ratelimit-Reset-Requests:
51
+ - 1m26.4s
52
+ X-Ratelimit-Reset-Tokens:
53
+ - 1.567s
54
+ X-Request-Id:
55
+ - req_01m4bm95h6eknbhd2g57rb86ak
56
+ Via:
57
+ - 1.1 google
58
+ Alt-Svc:
59
+ - h3=":443"; ma=86400
60
+ Cf-Cache-Status:
61
+ - DYNAMIC
62
+ Set-Cookie:
63
+ - __cf_bm=Rp.gZmJcvxYbWZDsbdfQDBTAz7DaFtywcVrwdQQSlXk-1791391733.2010832-1.0.1.1-MBeC0_FXCrPoveYPbgkP024YhTYGhnm2G8ai26jtHdCFBhXKN11v070xGxHdVs6b11ivLdL8VDl_Rkqlhfa4lE0lfIcHCiMUBNgwfgDgZ88op59V9O6YeWQYlBnkTMXH;
64
+ HttpOnly; SameSite=None; Secure; Path=/; Domain=groq.com; Expires=Wed, 07
65
+ Oct 2026 17:18:53 GMT
66
+ Strict-Transport-Security:
67
+ - max-age=15552000
68
+ Cf-Ray:
69
+ - a46e655c799e4825-HEL
70
+ body:
71
+ encoding: ASCII-8BIT
72
+ string: '{"id":"resp_01m4bm95k1ejrt9pm29qkr3r1w","object":"response","status":"completed","created_at":1791391733,"output":[{"type":"reasoning","id":"resp_01m4bm95k1ejs9anac75h9cntj","status":"completed","summary":[]},{"type":"message","id":"msg_01m4bm95k1ejst5m6wcxpz59xh","status":"completed","role":"assistant","content":[{"type":"output_text","text":"{\"age\":
73
+ 30, \"name\": \"John\"}","annotations":[],"logprobs":null}]}],"previous_response_id":null,"model":"qwen/qwen3.8-27b","reasoning":{"effort":"none"},"max_output_tokens":null,"text":{"format":{"type":"json_schema","name":"response","schema":{"additionalProperties":false,"properties":{"age":{"type":"integer"},"name":{"type":"string"}},"required":["name","age"],"type":"object"},"strict":true}},"tools":[],"tool_choice":"auto","truncation":"disabled","metadata":{},"groq":null,"temperature":1,"top_p":1,"user":null,"service_tier":"default","background":false,"error":null,"incomplete_details":null,"usage":{"input_tokens":24,"input_tokens_details":{"cached_tokens":0},"output_tokens":15,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":39},"parallel_tool_calls":true,"store":false,"top_logprobs":null,"max_tool_calls":null}
74
+
75
+ '
76
+ recorded_at: Wed, 07 Oct 2026 16:48:53 GMT
77
+ recorded_with: VCR 6.4.0
@@ -0,0 +1,77 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.groq.com/openai/v1/responses
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"model":"qwen/qwen3.8-27b","input":[{"role":"user","content":"What
9
+ is the capital of France?"}],"stream":false,"store":false}'
10
+ headers:
11
+ User-Agent:
12
+ - Faraday v2.14.4
13
+ Authorization:
14
+ - Bearer <AUTH_TOKEN>
15
+ Content-Type:
16
+ - application/json
17
+ Accept-Encoding:
18
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
19
+ Accept:
20
+ - "*/*"
21
+ response:
22
+ status:
23
+ code: 200
24
+ message: OK
25
+ headers:
26
+ Date:
27
+ - Wed, 07 Oct 2026 16:48:57 GMT
28
+ Content-Type:
29
+ - application/json
30
+ Transfer-Encoding:
31
+ - chunked
32
+ Connection:
33
+ - keep-alive
34
+ Cache-Control:
35
+ - private, max-age=0, no-store, no-cache, must-revalidate
36
+ Server:
37
+ - cloudflare
38
+ Vary:
39
+ - Origin
40
+ X-Groq-Region:
41
+ - dls
42
+ X-Ratelimit-Limit-Requests:
43
+ - '1000'
44
+ X-Ratelimit-Limit-Tokens:
45
+ - '8000'
46
+ X-Ratelimit-Remaining-Requests:
47
+ - '995'
48
+ X-Ratelimit-Remaining-Tokens:
49
+ - '7392'
50
+ X-Ratelimit-Reset-Requests:
51
+ - 7m12s
52
+ X-Ratelimit-Reset-Tokens:
53
+ - 35.34s
54
+ X-Request-Id:
55
+ - req_01m4bm99xzedvt58x4pv2vqdtn
56
+ Via:
57
+ - 1.1 google
58
+ Alt-Svc:
59
+ - h3=":443"; ma=86400
60
+ Cf-Cache-Status:
61
+ - DYNAMIC
62
+ Set-Cookie:
63
+ - __cf_bm=XLgVqOc4BryYpxr3WtbJj06g9ruNoyrH7wPhKXTx4OY-1791391737.6914153-1.0.1.1-fvXlPgyrWk0GhH6hiMdkiKPIMQMcDI6ar4utialeFyFpzwbD6tDVU4IKhZCK6J4.WdVQT.16glOHfj5lwpSsDuVqP_E_HMKTWdaVZpxcEowI2DGTh.aCZt32QFwRjwYK;
64
+ HttpOnly; SameSite=None; Secure; Path=/; Domain=groq.com; Expires=Wed, 07
65
+ Oct 2026 17:18:57 GMT
66
+ Strict-Transport-Security:
67
+ - max-age=15552000
68
+ Cf-Ray:
69
+ - a46e65789a841ede-HEL
70
+ body:
71
+ encoding: ASCII-8BIT
72
+ string: '{"id":"resp_01m4bm99zeeeavesphwjm6thn2","object":"response","status":"completed","created_at":1791391737,"output":[{"type":"reasoning","id":"resp_01m4bm99zeeeb877rssymg8dbc","status":"completed","summary":[]},{"type":"message","id":"msg_01m4bm99zeeebtv8sty8x7x5qf","status":"completed","role":"assistant","content":[{"type":"output_text","text":"The
73
+ capital of France is **Paris**.","annotations":[],"logprobs":null}]}],"previous_response_id":null,"model":"qwen/qwen3.8-27b","reasoning":{"effort":"none"},"max_output_tokens":null,"text":{"format":{"type":"text"}},"tools":[],"tool_choice":"auto","truncation":"disabled","metadata":{},"groq":null,"temperature":1,"top_p":1,"user":null,"service_tier":"default","background":false,"error":null,"incomplete_details":null,"usage":{"input_tokens":19,"input_tokens_details":{"cached_tokens":0},"output_tokens":9,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":28},"parallel_tool_calls":true,"store":false,"top_logprobs":null,"max_tool_calls":null}
74
+
75
+ '
76
+ recorded_at: Wed, 07 Oct 2026 16:48:57 GMT
77
+ recorded_with: VCR 6.4.0
@@ -0,0 +1,126 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.groq.com/openai/v1/responses
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"model":"qwen/qwen3.8-27b","input":[{"role":"user","content":"Count
9
+ from 1 to 3"}],"stream":true,"store":false}'
10
+ headers:
11
+ User-Agent:
12
+ - Faraday v2.14.4
13
+ Authorization:
14
+ - Bearer <AUTH_TOKEN>
15
+ Content-Type:
16
+ - application/json
17
+ Accept-Encoding:
18
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
19
+ Accept:
20
+ - "*/*"
21
+ response:
22
+ status:
23
+ code: 200
24
+ message: OK
25
+ headers:
26
+ Date:
27
+ - Wed, 07 Oct 2026 16:48:53 GMT
28
+ Content-Type:
29
+ - text/event-stream
30
+ Transfer-Encoding:
31
+ - chunked
32
+ Connection:
33
+ - keep-alive
34
+ Cache-Control:
35
+ - no-cache
36
+ Server:
37
+ - cloudflare
38
+ Vary:
39
+ - Origin
40
+ X-Groq-Region:
41
+ - dls
42
+ X-Ratelimit-Limit-Requests:
43
+ - '1000'
44
+ X-Ratelimit-Limit-Tokens:
45
+ - '8000'
46
+ X-Ratelimit-Remaining-Requests:
47
+ - '998'
48
+ X-Ratelimit-Remaining-Tokens:
49
+ - '7637'
50
+ X-Ratelimit-Reset-Requests:
51
+ - 2m52.8s
52
+ X-Ratelimit-Reset-Tokens:
53
+ - 20.639s
54
+ X-Request-Id:
55
+ - req_01m4bm95xdecs8vyvydz11t9f3
56
+ Via:
57
+ - 1.1 google
58
+ Alt-Svc:
59
+ - h3=":443"; ma=86400
60
+ Cf-Cache-Status:
61
+ - DYNAMIC
62
+ Set-Cookie:
63
+ - __cf_bm=dFAMGYsleaUx5TfCxJRPrwOcHlC98BasyB4BcELoUS0-1791391733.5906844-1.0.1.1-VJy8waJsRXkDe2_7PLnqoH6bsxhgpxa9Ljjn1V_9ajfz9zPnXSkn8wH6HaVwA.WBgsiqO38cbaoVujL_7RBJlVJ0WA.DJpY3PRRDXdlX6eSBIcaTDm0uS8nmch_n5hDU;
64
+ HttpOnly; SameSite=None; Secure; Path=/; Domain=groq.com; Expires=Wed, 07
65
+ Oct 2026 17:18:53 GMT
66
+ Strict-Transport-Security:
67
+ - max-age=15552000
68
+ Cf-Ray:
69
+ - a46e655ee85cf78c-HEL
70
+ body:
71
+ encoding: UTF-8
72
+ string: |+
73
+ event: response.created
74
+ data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_01m4bm95y7eb28tghetrch4vgh","object":"response","status":"in_progress","created_at":1791391733,"output":[],"previous_response_id":null,"model":"qwen/qwen3.8-27b","reasoning":{"effort":"none"},"max_output_tokens":null,"text":{"format":{"type":"text"}},"tools":[],"tool_choice":"auto","truncation":"disabled","metadata":{},"groq":null,"temperature":1,"top_p":1,"user":null,"service_tier":"default","background":false,"error":null,"incomplete_details":null,"parallel_tool_calls":true,"store":false,"top_logprobs":null,"max_tool_calls":null}}
75
+
76
+ event: response.in_progress
77
+ data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_01m4bm95y7eb28tghetrch4vgh","object":"response","status":"in_progress","created_at":1791391733,"output":[],"previous_response_id":null,"model":"qwen/qwen3.8-27b","reasoning":{"effort":"none"},"max_output_tokens":null,"text":{"format":{"type":"text"}},"tools":[],"tool_choice":"auto","truncation":"disabled","metadata":{},"groq":null,"temperature":1,"top_p":1,"user":null,"service_tier":"default","background":false,"error":null,"incomplete_details":null,"parallel_tool_calls":true,"store":false,"top_logprobs":null,"max_tool_calls":null}}
78
+
79
+ event: response.output_item.added
80
+ data: {"type":"response.output_item.added","sequence_number":2,"output_index":1,"item":{"type":"message","id":"msg_01m4bm95y8ezvtt1mwca86he3j","role":"assistant","content":[]}}
81
+
82
+ event: response.content_part.added
83
+ data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"part":{"type":"output_text","text":"","annotations":[],"logprobs":null}}
84
+
85
+ event: response.output_text.delta
86
+ data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"delta":"1","logprobs":[]}
87
+
88
+ event: response.output_text.delta
89
+ data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"delta":",","logprobs":[]}
90
+
91
+ event: response.output_text.delta
92
+ data: {"type":"response.output_text.delta","sequence_number":6,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"delta":" ","logprobs":[]}
93
+
94
+ event: response.output_text.delta
95
+ data: {"type":"response.output_text.delta","sequence_number":7,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"delta":"2","logprobs":[]}
96
+
97
+ event: response.output_text.delta
98
+ data: {"type":"response.output_text.delta","sequence_number":8,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"delta":",","logprobs":[]}
99
+
100
+ event: response.output_text.delta
101
+ data: {"type":"response.output_text.delta","sequence_number":9,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"delta":" ","logprobs":[]}
102
+
103
+ event: response.output_text.delta
104
+ data: {"type":"response.output_text.delta","sequence_number":10,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"delta":"3","logprobs":[]}
105
+
106
+ event: response.output_item.added
107
+ data: {"type":"response.output_item.added","sequence_number":11,"output_index":0,"item":{"type":"reasoning","id":"resp_01m4bm95y7eb2tjx9hqns9d3pg","content":[{"type":"reasoning_text","text":""}],"summary":[]}}
108
+
109
+ event: response.output_item.done
110
+ data: {"type":"response.output_item.done","sequence_number":12,"output_index":0,"item":{"type":"reasoning","id":"resp_01m4bm95y7eb2tjx9hqns9d3pg","content":[{"type":"reasoning_text","text":""}],"summary":[]}}
111
+
112
+ event: response.output_text.done
113
+ data: {"type":"response.output_text.done","sequence_number":13,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"text":"1, 2, 3","logprobs":[]}
114
+
115
+ event: response.content_part.done
116
+ data: {"type":"response.content_part.done","sequence_number":14,"item_id":"msg_01m4bm95y8ezvtt1mwca86he3j","output_index":1,"content_index":0,"part":{"type":"output_text","text":"1, 2, 3","annotations":[],"logprobs":null}}
117
+
118
+ event: response.output_item.done
119
+ data: {"type":"response.output_item.done","sequence_number":15,"output_index":1,"item":{"type":"message","id":"msg_01m4bm95y8ezvtt1mwca86he3j","status":"completed","role":"assistant","content":[{"type":"output_text","text":"1, 2, 3","annotations":[],"logprobs":[]}]}}
120
+
121
+ event: response.completed
122
+ data: {"type":"response.completed","sequence_number":16,"response":{"id":"resp_01m4bm95y7eb28tghetrch4vgh","object":"response","status":"completed","created_at":1791391733,"output":[{"type":"reasoning","id":"resp_01m4bm95y7eb2tjx9hqns9d3pg","content":[{"type":"reasoning_text","text":""}],"summary":[]},{"type":"message","id":"msg_01m4bm95y8ezvtt1mwca86he3j","status":"completed","role":"assistant","content":[{"type":"output_text","text":"1, 2, 3","annotations":[],"logprobs":[]}]}],"previous_response_id":null,"model":"qwen/qwen3.8-27b","reasoning":{"effort":"none"},"max_output_tokens":null,"text":{"format":{"type":"text"}},"tools":[],"tool_choice":"auto","truncation":"disabled","metadata":{},"groq":null,"temperature":1,"top_p":1,"user":null,"service_tier":"default","background":false,"error":null,"incomplete_details":null,"usage":{"input_tokens":19,"input_tokens_details":{"cached_tokens":0},"output_tokens":8,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":27},"parallel_tool_calls":true,"store":false,"top_logprobs":null,"max_tool_calls":null}}
123
+
124
+ recorded_at: Wed, 07 Oct 2026 16:48:53 GMT
125
+ recorded_with: VCR 6.4.0
126
+ ...
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyLLM::Chat, :live do
6
+ include_context 'with configured RubyLLM'
7
+
8
+ let(:person_schema) do
9
+ {
10
+ type: 'object',
11
+ properties: {
12
+ name: { type: 'string' },
13
+ age: { type: 'integer' }
14
+ },
15
+ required: %w[name age],
16
+ additionalProperties: false
17
+ }
18
+ end
19
+
20
+ each_model(STRUCTURED_OUTPUT_MODELS) do |provider, model|
21
+ it "#{provider}/#{model} returns structured output" do
22
+ response = RubyLLM.chat(model: model, provider: provider, assume_model_exists: true)
23
+ .with_schema(person_schema)
24
+ .ask('Generate a person named John who is 30 years old')
25
+
26
+ expect(response.content).to be_a(String)
27
+ expect(response.parsed).to include('name' => 'John', 'age' => 30)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyLLM::Chat, :live do
6
+ include_context 'with configured RubyLLM'
7
+
8
+ each_model(CHAT_MODELS) do |provider, model|
9
+ it "#{provider}/#{model} can have a basic conversation" do
10
+ response = RubyLLM.chat(model: model, provider: provider, assume_model_exists: true).ask("What's 2 + 2?")
11
+
12
+ expect(response.content).to include('4')
13
+ expect(response.role).to eq(:assistant)
14
+ expect(response.tokens.input.to_i).to be_positive
15
+ expect(response.tokens.output.to_i).to be_positive
16
+ end
17
+
18
+ it "#{provider}/#{model} returns the raw response" do
19
+ response = RubyLLM.chat(model: model, provider: provider, assume_model_exists: true)
20
+ .ask('What is the capital of France?')
21
+
22
+ expect(response.raw.status).to eq(200)
23
+ expect(response.raw.headers).not_to be_empty
24
+ expect(response.raw.body).not_to be_empty
25
+ expect(response.raw.env.request_body).not_to be_empty
26
+ end
27
+
28
+ it "#{provider}/#{model} can handle a multi-turn conversation" do
29
+ chat = RubyLLM.chat(model: model, provider: provider, assume_model_exists: true)
30
+
31
+ expect(chat.ask('Who created the programming language Ruby?').content).to match(/Matz|Matsumoto/i)
32
+ expect(chat.ask('What year was Ruby first released?').content).to include('199')
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyLLM::Chat, :live do
6
+ include_context 'with configured RubyLLM'
7
+
8
+ each_model(CHAT_MODELS) do |provider, model|
9
+ it "#{provider}/#{model} supports streaming responses" do
10
+ chunks = []
11
+ chat = RubyLLM.chat(model: model, provider: provider, assume_model_exists: true)
12
+
13
+ response = chat.ask('Count from 1 to 3') { |chunk| chunks << chunk }
14
+
15
+ expect(chunks).not_to be_empty
16
+ expect(chunks.first).to be_a(RubyLLM::Chunk)
17
+ expect(response.raw.status).to eq(200)
18
+ expect(response.raw.headers).not_to be_empty
19
+ expect(response.raw.env.request_body).not_to be_empty
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyLLM::Chat, :live do
6
+ include_context 'with configured RubyLLM'
7
+
8
+ let(:weather_tool) do
9
+ Class.new(RubyLLM::Tool) do
10
+ def self.name = 'WeatherTool'
11
+ description 'Gets current weather for a location'
12
+ parameter :latitude, description: 'Latitude'
13
+ parameter :longitude, description: 'Longitude'
14
+
15
+ def execute(latitude:, longitude:)
16
+ "Current weather at #{latitude}, #{longitude}: 15°C, Wind: 10 km/h"
17
+ end
18
+ end
19
+ end
20
+
21
+ each_model(TOOL_MODELS) do |provider, model|
22
+ it "#{provider}/#{model} can use tools" do
23
+ chat = RubyLLM.chat(model: model, provider: provider, assume_model_exists: true).with_tools(weather_tool)
24
+ response = chat.ask("What's the weather in Berlin? Use 52.5200, 13.4050.")
25
+
26
+ expect(response.content).to include('15')
27
+ expect(response.content).to include('10')
28
+ expect(chat.messages.any?(&:tool_call?)).to be(true)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyLLM::Embedding, :live do
6
+ include_context 'with configured RubyLLM'
7
+
8
+ each_model(EMBEDDING_MODELS) do |provider, model, model_info|
9
+ it "#{provider}/#{model} embeds one text" do
10
+ embedding = RubyLLM.embed(
11
+ "Ruby is a programmer's best friend",
12
+ model: model,
13
+ provider: provider,
14
+ assume_model_exists: true
15
+ )
16
+
17
+ expect(embedding.vectors).to be_an(Array)
18
+ expect(embedding.vectors.first).to be_a(Numeric)
19
+ expect(embedding.model).to eq(model)
20
+ expect(embedding.tokens.input.to_i).to be >= 0
21
+ end
22
+
23
+ it "#{provider}/#{model} embeds several texts" do
24
+ embeddings = RubyLLM.embed(
25
+ %w[Ruby Python JavaScript],
26
+ model: model,
27
+ provider: provider,
28
+ assume_model_exists: true
29
+ )
30
+
31
+ expect(embeddings.vectors.size).to eq(3)
32
+ expect(embeddings.vectors).to all(be_an(Array))
33
+ end
34
+
35
+ next unless model_info[:dimensions]
36
+
37
+ it "#{provider}/#{model} supports custom dimensions" do
38
+ embedding = RubyLLM.embed(
39
+ 'Ruby',
40
+ model: model,
41
+ provider: provider,
42
+ assume_model_exists: true,
43
+ dimensions: model_info[:dimensions]
44
+ )
45
+
46
+ expect(embedding.vectors.length).to eq(model_info[:dimensions])
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyLLM::Image, :live do
6
+ include_context 'with configured RubyLLM'
7
+
8
+ each_model(IMAGE_GENERATION_MODELS) do |provider, model, model_info|
9
+ it "#{provider}/#{model} paints an image" do
10
+ image = RubyLLM.paint(
11
+ 'a siamese cat',
12
+ model: model,
13
+ provider: provider,
14
+ assume_model_exists: true,
15
+ provider_options: model_info.fetch(:provider_options, {})
16
+ )
17
+
18
+ expect(image.mime_type).to include('image')
19
+ expect(image.model).to eq(model)
20
+ expect(image.to_blob.bytesize).to be > 1000
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyLLM::Models do
6
+ include_context 'with configured RubyLLM'
7
+
8
+ it 'expects generated providers to add registry metadata before use' do
9
+ expect(RubyLLM::Providers::Groq.assume_models_exist?).to be(false)
10
+ end
11
+ end