ruby_llm 2.2.1 → 2.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55db20760e71db4c87a6f71b21ac7eba6b1651838dc6d897397d7d61e8283e14
4
- data.tar.gz: d624ca6c55d349a74876d2ac12609484b7e8a7d28c08f7133e0c827550fd98cd
3
+ metadata.gz: 2922aa0070d452a4fc7c878dccd47bd01d19b35e6f35c0a235d60d82c55a2c9f
4
+ data.tar.gz: 794394420abde21eb9dc76c92016033fa19f8cf2728b622748f82fa29e66eb63
5
5
  SHA512:
6
- metadata.gz: 4ee13ff16e87c7ef85d5c9bea076b7c01dcaf6d8e7d422a632b403e31284c5c4400bd0c7d255fde5ad609e6e42cf57b5e15db514adba86d18bbcd07a41fbb7dc
7
- data.tar.gz: 9988f48c57b5c8603dce67d9ddc098a3be387a9fd478756ac32037c0b8a4a3d7d5929aa60a7c1eba761677e93df980abc0fc6dfc279da9ee1e8a1f4018fa6bfa
6
+ metadata.gz: c5873a40701f252b6e10eff51d7b42aa825a97e825e25376840276a6d0c98e5f0ec819799f24c02fe60ec8b2c3a3bbace142ed5059572a4629a6bef300e6fbb6
7
+ data.tar.gz: 7bd73a7cdc657af2207325b29dad976135280495e43ce6ba5c57a8b7a9d5a383a46a793808f1f34ba195618ebe936fd0ae7f92f73340cb1373091a134fac40ea
data/README.md CHANGED
@@ -179,7 +179,7 @@ Start your Rails server and visit `http://localhost:3000/chats` to try the chat
179
179
 
180
180
  ## Start with one line. Add files, tools, and agents
181
181
 
182
- These examples use **2.2.1**. Follow [Getting Started](https://rubyllm.com/getting-started/) to install it and configure the providers you want to try. For 1.x, use the [1.x docs](https://rubyllm.com/v1/).
182
+ These examples use **2.2.2**. Follow [Getting Started](https://rubyllm.com/getting-started/) to install it and configure the providers you want to try. For 1.x, use the [1.x docs](https://rubyllm.com/v1/).
183
183
 
184
184
  ```ruby
185
185
  # Just ask
@@ -309,7 +309,7 @@ response.parsed
309
309
  Install RubyLLM 2.2:
310
310
 
311
311
  ```bash
312
- bundle add ruby_llm --version 2.2.1
312
+ bundle add ruby_llm --version 2.2.2
313
313
  ```
314
314
 
315
315
  Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
@@ -1,7 +1,7 @@
1
1
  <%% answers_path = <%= chat_variable_name %>_answers_path(<%= chat_model_name.demodulize.underscore %>) %>
2
2
  <%% card = "question_#{question.id.parameterize}" %>
3
3
  <%% single = question.fields.first if question.fields.one? %>
4
- <%% choices = single&.choices&.to_h { |choice| [choice, choice] } %>
4
+ <%% choices = single&.choice_titles&.map(&:reverse) %>
5
5
  <%% choices = { "Yes" => true, "No" => false } if single&.type == "boolean" %>
6
6
  <%% steps = { "integer" => 1, "number" => "any" } %>
7
7
  <%% primary = "w-full sm:w-auto rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white font-medium cursor-pointer" %>
@@ -63,7 +63,7 @@
63
63
  <%% else %>
64
64
  <%%= label_tag field_id, caption, class: "block text-sm font-medium" %>
65
65
  <%% if field.choices %>
66
- <%%= form.select "answer[#{field.name}]", field.choices, { selected: field.default, include_blank: !field.required? && !list },
66
+ <%%= form.select "answer[#{field.name}]", field.choice_titles.map(&:reverse), { selected: field.default, include_blank: !field.required? && !list },
67
67
  id: field_id, multiple: list, required: list && field.required?, class: "block shadow-sm rounded-md border border-gray-400 px-3 py-2 mt-1 w-full" %>
68
68
  <%% else %>
69
69
  <%%= form.text_field "answer[#{field.name}]#{"[]" if list}", value: (field.default unless list),
@@ -1,7 +1,7 @@
1
1
  <%% answers_path = <%= chat_variable_name %>_answers_path(<%= chat_model_name.demodulize.underscore %>) %>
2
2
  <%% card = "question_#{question.id.parameterize}" %>
3
3
  <%% single = question.fields.first if question.fields.one? %>
4
- <%% choices = single&.choices&.to_h { |choice| [choice, choice] } %>
4
+ <%% choices = single&.choice_titles&.map(&:reverse) %>
5
5
  <%% choices = { "Yes" => true, "No" => false } if single&.type == "boolean" %>
6
6
  <%% steps = { "integer" => 1, "number" => "any" } %>
7
7
  <div id="<%%= card %>" class="question"
@@ -47,7 +47,7 @@
47
47
  <%% else %>
48
48
  <%%= label_tag field_id, caption, style: "display: block;" %>
49
49
  <%% if field.choices %>
50
- <%%= form.select "answer[#{field.name}]", field.choices, { selected: field.default, include_blank: !field.required? && !list },
50
+ <%%= form.select "answer[#{field.name}]", field.choice_titles.map(&:reverse), { selected: field.default, include_blank: !field.required? && !list },
51
51
  id: field_id, multiple: list, required: list && field.required? %>
52
52
  <%% else %>
53
53
  <%%= form.text_field "answer[#{field.name}]#{"[]" if list}", value: (field.default unless list),
@@ -77,16 +77,41 @@ module RubyLLM
77
77
  # dangle the reference. That row is stamped unlisted instead, keeping
78
78
  # the chats that use it resolvable while the application migrates them.
79
79
  def unlist(dropped_ids)
80
+ referenced = referenced_ids(dropped_ids)
80
81
  stayed = []
81
82
  where(id: dropped_ids).find_each do |model|
82
- transaction(requires_new: true) { model.destroy! }
83
- rescue ::ActiveRecord::InvalidForeignKey
83
+ next if referenced.exclude?(model.id) && destroy_unreferenced(model)
84
+
84
85
  model.update!(unlisted_at: Time.current) unless model.unlisted_at
85
86
  stayed << "#{model.provider}/#{model.model_id}"
86
87
  end
87
88
  warn_unlisted(stayed)
88
89
  end
89
90
 
91
+ # Not every schema has the foreign key that refuses the delete, and the
92
+ # application's chat classes may not be loaded yet, so this checks
93
+ # every table with the ruby_llm_model_id column acts_as_chat uses.
94
+ def referenced_ids(ids)
95
+ return [] if ids.empty?
96
+
97
+ referencing_tables.flat_map do |name|
98
+ table = Arel::Table.new(name)
99
+ where(id: ids).where(arel_table[:id].in(table.project(table[:ruby_llm_model_id]))).pluck(:id)
100
+ end
101
+ end
102
+
103
+ def referencing_tables
104
+ connection_pool.with_connection do |connection|
105
+ connection.tables.select { |table| connection.column_exists?(table, :ruby_llm_model_id) }
106
+ end
107
+ end
108
+
109
+ def destroy_unreferenced(model)
110
+ transaction(requires_new: true) { model.destroy! }
111
+ rescue ::ActiveRecord::InvalidForeignKey
112
+ false
113
+ end
114
+
90
115
  def warn_unlisted(names)
91
116
  return if names.empty?
92
117
 
@@ -20394,8 +20394,7 @@
20394
20394
  "modalities": {
20395
20395
  "input": [
20396
20396
  "text",
20397
- "image",
20398
- "pdf"
20397
+ "image"
20399
20398
  ],
20400
20399
  "output": [
20401
20400
  "text"
@@ -20491,8 +20490,7 @@
20491
20490
  "modalities": {
20492
20491
  "input": [
20493
20492
  "text",
20494
- "image",
20495
- "pdf"
20493
+ "image"
20496
20494
  ],
20497
20495
  "output": [
20498
20496
  "text"
@@ -41972,7 +41970,7 @@
41972
41970
  "family": "gemini-pro",
41973
41971
  "created_at": "2026-06-09 00:00:00 UTC",
41974
41972
  "context_window": 16384,
41975
- "max_output_tokens": 32768,
41973
+ "max_output_tokens": 16384,
41976
41974
  "knowledge_cutoff": null,
41977
41975
  "modalities": {
41978
41976
  "input": [
@@ -42024,7 +42022,7 @@
42024
42022
  },
42025
42023
  "limit": {
42026
42024
  "context": 16384,
42027
- "output": 32768
42025
+ "output": 16384
42028
42026
  },
42029
42027
  "knowledge": "2025-01"
42030
42028
  }
@@ -45840,7 +45838,7 @@
45840
45838
  "family": "ministral",
45841
45839
  "created_at": "2025-12-02 00:00:00 UTC",
45842
45840
  "context_window": 131072,
45843
- "max_output_tokens": 262144,
45841
+ "max_output_tokens": 131072,
45844
45842
  "knowledge_cutoff": null,
45845
45843
  "modalities": {
45846
45844
  "input": [
@@ -45892,7 +45890,7 @@
45892
45890
  },
45893
45891
  "limit": {
45894
45892
  "context": 131072,
45895
- "output": 262144
45893
+ "output": 131072
45896
45894
  }
45897
45895
  }
45898
45896
  },
@@ -49510,7 +49508,7 @@
49510
49508
  "family": "deepseek-thinking",
49511
49509
  "created_at": "2026-04-24 00:00:00 UTC",
49512
49510
  "context_window": 1048576,
49513
- "max_output_tokens": 1048576,
49511
+ "max_output_tokens": 65536,
49514
49512
  "knowledge_cutoff": null,
49515
49513
  "modalities": {
49516
49514
  "input": [
@@ -49563,7 +49561,7 @@
49563
49561
  },
49564
49562
  "limit": {
49565
49563
  "context": 1048576,
49566
- "output": 1048576
49564
+ "output": 65536
49567
49565
  },
49568
49566
  "knowledge": "2025-05"
49569
49567
  }
@@ -49575,7 +49573,7 @@
49575
49573
  "family": "deepseek-thinking",
49576
49574
  "created_at": "2026-08-13 00:00:00 UTC",
49577
49575
  "context_window": 1048576,
49578
- "max_output_tokens": 1048576,
49576
+ "max_output_tokens": 65536,
49579
49577
  "knowledge_cutoff": null,
49580
49578
  "modalities": {
49581
49579
  "input": [
@@ -49630,7 +49628,7 @@
49630
49628
  },
49631
49629
  "limit": {
49632
49630
  "context": 1048576,
49633
- "output": 1048576
49631
+ "output": 65536
49634
49632
  }
49635
49633
  }
49636
49634
  },
@@ -50173,12 +50171,12 @@
50173
50171
  },
50174
50172
  {
50175
50173
  "id": "inkling-2-small",
50176
- "name": "inkling-2-small",
50174
+ "name": "Inkling 2 Small",
50177
50175
  "provider": "ollama_cloud",
50178
- "family": "ollama",
50179
- "created_at": "2026-11-12 17:00:00 UTC",
50180
- "context_window": null,
50181
- "max_output_tokens": null,
50176
+ "family": "ling",
50177
+ "created_at": "2026-11-05 00:00:00 UTC",
50178
+ "context_window": 1048576,
50179
+ "max_output_tokens": 131072,
50182
50180
  "knowledge_cutoff": null,
50183
50181
  "modalities": {
50184
50182
  "input": [
@@ -50190,14 +50188,54 @@
50190
50188
  ]
50191
50189
  },
50192
50190
  "capabilities": [
50193
- "streaming",
50194
50191
  "function_calling",
50192
+ "reasoning",
50195
50193
  "vision",
50196
- "reasoning"
50194
+ "streaming"
50197
50195
  ],
50198
- "pricing": {},
50196
+ "pricing": {
50197
+ "text_tokens": {
50198
+ "standard": {
50199
+ "input_per_million": 0.3,
50200
+ "output_per_million": 1,
50201
+ "cache_read_input_per_million": 0.03
50202
+ }
50203
+ }
50204
+ },
50199
50205
  "metadata": {
50200
- "owned_by": "ollama"
50206
+ "owned_by": "ollama",
50207
+ "source": "models.dev",
50208
+ "provider_id": "ollama-cloud",
50209
+ "open_weights": true,
50210
+ "attachment": true,
50211
+ "temperature": true,
50212
+ "last_updated": "2026-11-12",
50213
+ "tool_call": true,
50214
+ "structured_output": false,
50215
+ "reasoning": true,
50216
+ "reasoning_options": [
50217
+ {
50218
+ "type": "toggle"
50219
+ },
50220
+ {
50221
+ "type": "effort",
50222
+ "values": [
50223
+ "low",
50224
+ "medium",
50225
+ "high",
50226
+ "max"
50227
+ ]
50228
+ }
50229
+ ],
50230
+ "cost": {
50231
+ "input": 0.3,
50232
+ "output": 1,
50233
+ "cache_read": 0.03
50234
+ },
50235
+ "limit": {
50236
+ "context": 1048576,
50237
+ "output": 131072
50238
+ }
50201
50239
  }
50202
50240
  },
50203
50241
  {
@@ -59530,7 +59568,7 @@
59530
59568
  "top_provider": {
59531
59569
  "context_length": 1000000,
59532
59570
  "max_completion_tokens": 128000,
59533
- "is_moderated": true
59571
+ "is_moderated": false
59534
59572
  },
59535
59573
  "per_request_limits": null,
59536
59574
  "supported_parameters": [
@@ -64725,7 +64763,7 @@
64725
64763
  },
64726
64764
  "top_provider": {
64727
64765
  "context_length": 1048576,
64728
- "max_completion_tokens": 943718,
64766
+ "max_completion_tokens": 384000,
64729
64767
  "is_moderated": false
64730
64768
  },
64731
64769
  "per_request_limits": null,
@@ -65418,7 +65456,7 @@
65418
65456
  "family": "deepseek",
65419
65457
  "created_at": "2026-10-17 08:27:04 UTC",
65420
65458
  "context_window": 1048576,
65421
- "max_output_tokens": 384000,
65459
+ "max_output_tokens": 943718,
65422
65460
  "knowledge_cutoff": null,
65423
65461
  "modalities": {
65424
65462
  "input": [
@@ -65439,9 +65477,9 @@
65439
65477
  "pricing": {
65440
65478
  "text_tokens": {
65441
65479
  "standard": {
65442
- "input_per_million": 0.049999999999999996,
65443
- "output_per_million": 0.15,
65444
- "cache_read_input_per_million": 0.004
65480
+ "input_per_million": 0.048,
65481
+ "output_per_million": 0.27,
65482
+ "cache_read_input_per_million": 0.002
65445
65483
  }
65446
65484
  }
65447
65485
  },
@@ -65461,7 +65499,7 @@
65461
65499
  },
65462
65500
  "top_provider": {
65463
65501
  "context_length": 1048576,
65464
- "max_completion_tokens": 384000,
65502
+ "max_completion_tokens": 943718,
65465
65503
  "is_moderated": false
65466
65504
  },
65467
65505
  "per_request_limits": null,
@@ -65497,7 +65535,7 @@
65497
65535
  "family": "deepseek",
65498
65536
  "created_at": "2026-09-10 06:21:25 UTC",
65499
65537
  "context_window": 1048576,
65500
- "max_output_tokens": 131072,
65538
+ "max_output_tokens": 943718,
65501
65539
  "knowledge_cutoff": null,
65502
65540
  "modalities": {
65503
65541
  "input": [
@@ -65518,9 +65556,9 @@
65518
65556
  "pricing": {
65519
65557
  "text_tokens": {
65520
65558
  "standard": {
65521
- "input_per_million": 0.112,
65522
- "output_per_million": 0.33599999999999997,
65523
- "cache_read_input_per_million": 0.00336
65559
+ "input_per_million": 0.048,
65560
+ "output_per_million": 0.27,
65561
+ "cache_read_input_per_million": 0.002
65524
65562
  }
65525
65563
  }
65526
65564
  },
@@ -65540,7 +65578,7 @@
65540
65578
  },
65541
65579
  "top_provider": {
65542
65580
  "context_length": 1048576,
65543
- "max_completion_tokens": 131072,
65581
+ "max_completion_tokens": 943718,
65544
65582
  "is_moderated": false
65545
65583
  },
65546
65584
  "per_request_limits": null,
@@ -81424,9 +81462,9 @@
81424
81462
  "pricing": {
81425
81463
  "text_tokens": {
81426
81464
  "standard": {
81427
- "input_per_million": 0.7,
81428
- "output_per_million": 6.0,
81429
- "cache_read_input_per_million": 0.09999999999999999
81465
+ "input_per_million": 0.346,
81466
+ "output_per_million": 4.41,
81467
+ "cache_read_input_per_million": 0.111
81430
81468
  }
81431
81469
  }
81432
81470
  },
@@ -81454,14 +81492,18 @@
81454
81492
  "supported_parameters": [
81455
81493
  "frequency_penalty",
81456
81494
  "include_reasoning",
81495
+ "logit_bias",
81457
81496
  "max_tokens",
81497
+ "min_p",
81458
81498
  "presence_penalty",
81459
81499
  "reasoning",
81460
81500
  "reasoning_effort",
81501
+ "repetition_penalty",
81461
81502
  "seed",
81462
81503
  "stop",
81463
81504
  "temperature",
81464
- "top_k"
81505
+ "top_k",
81506
+ "top_p"
81465
81507
  ],
81466
81508
  "expiration_date": null
81467
81509
  }
@@ -81473,7 +81515,7 @@
81473
81515
  "family": "moonshotai",
81474
81516
  "created_at": "2026-07-16 15:30:58 UTC",
81475
81517
  "context_window": 1048576,
81476
- "max_output_tokens": 16384,
81518
+ "max_output_tokens": 943718,
81477
81519
  "knowledge_cutoff": null,
81478
81520
  "modalities": {
81479
81521
  "input": [
@@ -81496,9 +81538,9 @@
81496
81538
  "pricing": {
81497
81539
  "text_tokens": {
81498
81540
  "standard": {
81499
- "input_per_million": 2.2800000000000002,
81500
- "output_per_million": 11.399999999999999,
81501
- "cache_read_input_per_million": 0.228
81541
+ "input_per_million": 0.883,
81542
+ "output_per_million": 6.0,
81543
+ "cache_read_input_per_million": 0.218
81502
81544
  }
81503
81545
  }
81504
81546
  },
@@ -81519,7 +81561,7 @@
81519
81561
  },
81520
81562
  "top_provider": {
81521
81563
  "context_length": 1048576,
81522
- "max_completion_tokens": 16384,
81564
+ "max_completion_tokens": 943718,
81523
81565
  "is_moderated": false
81524
81566
  },
81525
81567
  "per_request_limits": null,
@@ -81527,6 +81569,7 @@
81527
81569
  "frequency_penalty",
81528
81570
  "include_reasoning",
81529
81571
  "logit_bias",
81572
+ "logprobs",
81530
81573
  "max_tokens",
81531
81574
  "min_p",
81532
81575
  "presence_penalty",
@@ -81541,6 +81584,7 @@
81541
81584
  "tool_choice",
81542
81585
  "tools",
81543
81586
  "top_k",
81587
+ "top_logprobs",
81544
81588
  "top_p"
81545
81589
  ],
81546
81590
  "expiration_date": null
@@ -82663,7 +82707,7 @@
82663
82707
  "provider": "openrouter",
82664
82708
  "family": "nvidia",
82665
82709
  "created_at": "2026-07-16 12:01:34 UTC",
82666
- "context_window": 32768,
82710
+ "context_window": 4096,
82667
82711
  "max_output_tokens": null,
82668
82712
  "knowledge_cutoff": null,
82669
82713
  "modalities": {
@@ -82692,7 +82736,7 @@
82692
82736
  "instruct_type": null
82693
82737
  },
82694
82738
  "top_provider": {
82695
- "context_length": 32768,
82739
+ "context_length": 4096,
82696
82740
  "max_completion_tokens": null,
82697
82741
  "is_moderated": false
82698
82742
  },
@@ -87984,7 +88028,7 @@
87984
88028
  "top_provider": {
87985
88029
  "context_length": 1050000,
87986
88030
  "max_completion_tokens": 128000,
87987
- "is_moderated": true
88031
+ "is_moderated": false
87988
88032
  },
87989
88033
  "per_request_limits": null,
87990
88034
  "supported_parameters": [
@@ -96797,7 +96841,7 @@
96797
96841
  "family": "qwen",
96798
96842
  "created_at": "2026-08-13 05:08:04 UTC",
96799
96843
  "context_window": 40960,
96800
- "max_output_tokens": 36864,
96844
+ "max_output_tokens": 0,
96801
96845
  "knowledge_cutoff": null,
96802
96846
  "modalities": {
96803
96847
  "input": [
@@ -96825,8 +96869,8 @@
96825
96869
  "instruct_type": null
96826
96870
  },
96827
96871
  "top_provider": {
96828
- "context_length": 40960,
96829
- "max_completion_tokens": 36864,
96872
+ "context_length": 32768,
96873
+ "max_completion_tokens": 0,
96830
96874
  "is_moderated": false
96831
96875
  },
96832
96876
  "per_request_limits": null,
@@ -104924,10 +104968,10 @@
104924
104968
  "expiration_date": null,
104925
104969
  "source": "models.dev",
104926
104970
  "provider_id": "openrouter",
104927
- "open_weights": false,
104971
+ "open_weights": true,
104928
104972
  "attachment": true,
104929
104973
  "temperature": true,
104930
- "last_updated": "2026-11-05",
104974
+ "last_updated": "2026-11-12",
104931
104975
  "tool_call": true,
104932
104976
  "structured_output": true,
104933
104977
  "reasoning": true,
@@ -105020,10 +105064,10 @@
105020
105064
  "expiration_date": null,
105021
105065
  "source": "models.dev",
105022
105066
  "provider_id": "openrouter",
105023
- "open_weights": false,
105067
+ "open_weights": true,
105024
105068
  "attachment": true,
105025
105069
  "temperature": true,
105026
- "last_updated": "2026-11-05",
105070
+ "last_updated": "2026-11-12",
105027
105071
  "tool_call": true,
105028
105072
  "structured_output": false,
105029
105073
  "reasoning": true,
@@ -108562,7 +108606,7 @@
108562
108606
  "instruct_type": null
108563
108607
  },
108564
108608
  "top_provider": {
108565
- "context_length": 204800,
108609
+ "context_length": 202752,
108566
108610
  "max_completion_tokens": 131072,
108567
108611
  "is_moderated": false
108568
108612
  },
@@ -109121,82 +109165,6 @@
109121
109165
  }
109122
109166
  }
109123
109167
  },
109124
- {
109125
- "id": "z-ai/glm-5.2:batch",
109126
- "name": "Z.ai: GLM 5.2 (batch)",
109127
- "provider": "openrouter",
109128
- "family": "z-ai",
109129
- "created_at": "2026-06-16 17:45:30 UTC",
109130
- "context_window": 1048576,
109131
- "max_output_tokens": 943718,
109132
- "knowledge_cutoff": null,
109133
- "modalities": {
109134
- "input": [
109135
- "text"
109136
- ],
109137
- "output": [
109138
- "text"
109139
- ]
109140
- },
109141
- "capabilities": [
109142
- "streaming",
109143
- "function_calling",
109144
- "tool_choice",
109145
- "structured_output"
109146
- ],
109147
- "pricing": {
109148
- "text_tokens": {
109149
- "standard": {
109150
- "input_per_million": 0.322,
109151
- "output_per_million": 0.989,
109152
- "cache_read_input_per_million": 0.109
109153
- }
109154
- }
109155
- },
109156
- "metadata": {
109157
- "description": "GLM 5.2 is a large-scale reasoning model from Z.ai. It supports text input and output with a 1M-token context window, and is suited for long-horizon agent workflows, project-level software engineering,...",
109158
- "architecture": {
109159
- "modality": "text->text",
109160
- "input_modalities": [
109161
- "text"
109162
- ],
109163
- "output_modalities": [
109164
- "text"
109165
- ],
109166
- "tokenizer": "Other",
109167
- "instruct_type": null
109168
- },
109169
- "top_provider": {
109170
- "context_length": 1048576,
109171
- "max_completion_tokens": 943718,
109172
- "is_moderated": false
109173
- },
109174
- "per_request_limits": null,
109175
- "supported_parameters": [
109176
- "frequency_penalty",
109177
- "include_reasoning",
109178
- "logit_bias",
109179
- "logprobs",
109180
- "max_tokens",
109181
- "min_p",
109182
- "presence_penalty",
109183
- "reasoning",
109184
- "reasoning_effort",
109185
- "repetition_penalty",
109186
- "response_format",
109187
- "seed",
109188
- "stop",
109189
- "structured_outputs",
109190
- "temperature",
109191
- "tool_choice",
109192
- "tools",
109193
- "top_k",
109194
- "top_logprobs",
109195
- "top_p"
109196
- ],
109197
- "expiration_date": null
109198
- }
109199
- },
109200
109168
  {
109201
109169
  "id": "z-ai/glm-5.3",
109202
109170
  "name": "GLM-5.3",
@@ -109810,7 +109778,7 @@
109810
109778
  "family": "z-ai",
109811
109779
  "created_at": "2026-08-18 20:57:35 UTC",
109812
109780
  "context_window": 1048576,
109813
- "max_output_tokens": 131072,
109781
+ "max_output_tokens": 943718,
109814
109782
  "knowledge_cutoff": null,
109815
109783
  "modalities": {
109816
109784
  "input": [
@@ -109829,9 +109797,9 @@
109829
109797
  "pricing": {
109830
109798
  "text_tokens": {
109831
109799
  "standard": {
109832
- "input_per_million": 0.44999999999999996,
109833
- "output_per_million": 2.0,
109834
- "cache_read_input_per_million": 0.09999999999999999
109800
+ "input_per_million": 0.322,
109801
+ "output_per_million": 0.989,
109802
+ "cache_read_input_per_million": 0.109
109835
109803
  }
109836
109804
  }
109837
109805
  },
@@ -109850,7 +109818,7 @@
109850
109818
  },
109851
109819
  "top_provider": {
109852
109820
  "context_length": 1048576,
109853
- "max_completion_tokens": 131072,
109821
+ "max_completion_tokens": 943718,
109854
109822
  "is_moderated": false
109855
109823
  },
109856
109824
  "per_request_limits": null,
@@ -120347,7 +120315,7 @@
120347
120315
  "family": "gemini-pro",
120348
120316
  "created_at": "2026-06-09 00:00:00 UTC",
120349
120317
  "context_window": 16384,
120350
- "max_output_tokens": 32768,
120318
+ "max_output_tokens": 16384,
120351
120319
  "knowledge_cutoff": null,
120352
120320
  "modalities": {
120353
120321
  "input": [
@@ -120398,7 +120366,7 @@
120398
120366
  },
120399
120367
  "limit": {
120400
120368
  "context": 16384,
120401
- "output": 32768
120369
+ "output": 16384
120402
120370
  },
120403
120371
  "knowledge": "2025-01"
120404
120372
  }
@@ -38,8 +38,16 @@ module RubyLLM
38
38
  'boolean' => [TrueClass, FalseClass], 'array' => [Array] }.freeze
39
39
  private_constant :BOOLEANS, :TYPES, :CLASSES
40
40
 
41
- # A value a question asks for.
42
- Field = Struct.new(:name, :type, :title, :description, :required, :choices, :default, keyword_init: true) do
41
+ # A value a question asks for. +choices+ lists the values a field
42
+ # offers, and +choice_titles+ maps each one to the title to show for
43
+ # it, which is the value itself when the field gives it none:
44
+ #
45
+ # field.choices # => ["s", "l"]
46
+ # field.choice_titles # => {"s" => "Small", "l" => "Large"}
47
+ #
48
+ # Both are +nil+ for a field that takes any value.
49
+ Field = Struct.new(:name, :type, :title, :description, :required, :choices, :default, :choice_titles,
50
+ keyword_init: true) do
43
51
  # Returns whether the question needs this value.
44
52
  def required?
45
53
  required == true
@@ -370,16 +378,25 @@ module RubyLLM
370
378
  def fields_from(schema)
371
379
  required = Array(schema['required'])
372
380
  schema.fetch('properties', {}).map do |name, property|
381
+ titled = titled_choices(property)
373
382
  Field.new(name: name.to_sym, type: property['type'], title: property['title'],
374
383
  description: property['description'], required: required.include?(name),
375
- choices: choices(property), default: property['default'])
384
+ choices: titled&.map(&:first), default: property['default'], choice_titles: titled&.to_h)
376
385
  end
377
386
  end
378
387
 
379
- def choices(property)
380
- options = property['enum'] || property.dig('items', 'enum') ||
381
- Array(property['oneOf'] || property.dig('items', 'anyOf')).map { |o| o['const'] }
382
- options unless options.empty?
388
+ def titled_choices(property)
389
+ values = property['enum'] || property.dig('items', 'enum')
390
+ titled = values ? named(values, Array(property['enumNames'])) : options(property)
391
+ titled unless titled.empty?
392
+ end
393
+
394
+ def named(values, names)
395
+ values.each_with_index.map { |value, index| [value, names[index] || value] }
396
+ end
397
+
398
+ def options(property)
399
+ Array(property['oneOf'] || property.dig('items', 'anyOf')).map { |o| [o['const'], o['title'] || o['const']] }
383
400
  end
384
401
 
385
402
  def inspect_attributes
@@ -16,8 +16,16 @@ if defined?(Rails::Railtie)
16
16
  RubyLLM.config.instrumenter ||= ActiveSupport::Notifications
17
17
  end
18
18
 
19
- initializer 'ruby_llm.skills', before: :setup_main_autoloader do
20
- Rails.autoloaders.main.ignore(*RubyLLM::Skill.roots)
19
+ initializer 'ruby_llm.skills', before: :setup_main_autoloader do |app|
20
+ manifests = ["**/#{RubyLLM::Skill::FILE}", "**/*/#{RubyLLM::Skill::FILE}"]
21
+ ignore_skills = lambda do
22
+ folders = RubyLLM::Skill.roots.flat_map do |root|
23
+ Dir.glob(manifests, base: root.to_s).map { |file| File.join(root.to_s, File.dirname(file)) }
24
+ end
25
+ Rails.autoloaders.main.ignore(*folders.uniq)
26
+ end
27
+ ignore_skills.call
28
+ app.reloader.before_class_unload(&ignore_skills)
21
29
  end
22
30
 
23
31
  initializer 'ruby_llm.active_record' do
@@ -2,5 +2,5 @@
2
2
 
3
3
  module RubyLLM
4
4
  # The version of the ruby_llm gem, as a string.
5
- VERSION = '2.2.1'
5
+ VERSION = '2.2.2'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carmine Paolino
@@ -884,7 +884,7 @@ metadata:
884
884
  funding_uri: https://github.com/sponsors/crmne
885
885
  rubygems_mfa_required: 'true'
886
886
  post_install_message: |
887
- RubyLLM 2.2.1
887
+ RubyLLM 2.2.2
888
888
 
889
889
  Upgrading? Read the upgrade guide before you boot:
890
890