ruby_llm 1.6.4 → 1.7.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.
- checksums.yaml +4 -4
- data/README.md +6 -3
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +115 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +10 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +30 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
- data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +4 -4
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +8 -7
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +43 -0
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +6 -5
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +10 -4
- data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -3
- data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +3 -0
- data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/install_generator.rb +129 -33
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +137 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb +160 -0
- data/lib/ruby_llm/active_record/acts_as.rb +111 -327
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +398 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +336 -0
- data/lib/ruby_llm/active_record/message_methods.rb +72 -0
- data/lib/ruby_llm/active_record/model_methods.rb +84 -0
- data/lib/ruby_llm/aliases.json +54 -13
- data/lib/ruby_llm/attachment.rb +20 -0
- data/lib/ruby_llm/chat.rb +5 -5
- data/lib/ruby_llm/configuration.rb +9 -0
- data/lib/ruby_llm/connection.rb +4 -4
- data/lib/ruby_llm/model/info.rb +12 -0
- data/lib/ruby_llm/models.json +3579 -2029
- data/lib/ruby_llm/models.rb +51 -22
- data/lib/ruby_llm/provider.rb +3 -3
- data/lib/ruby_llm/providers/anthropic/chat.rb +2 -2
- data/lib/ruby_llm/providers/anthropic/media.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/chat.rb +2 -2
- data/lib/ruby_llm/providers/bedrock/models.rb +19 -1
- data/lib/ruby_llm/providers/gemini/chat.rb +1 -1
- data/lib/ruby_llm/providers/gemini/media.rb +1 -1
- data/lib/ruby_llm/providers/gpustack/chat.rb +11 -0
- data/lib/ruby_llm/providers/gpustack/media.rb +45 -0
- data/lib/ruby_llm/providers/gpustack/models.rb +44 -8
- data/lib/ruby_llm/providers/gpustack.rb +1 -0
- data/lib/ruby_llm/providers/ollama/media.rb +2 -6
- data/lib/ruby_llm/providers/ollama/models.rb +36 -0
- data/lib/ruby_llm/providers/ollama.rb +1 -0
- data/lib/ruby_llm/providers/openai/chat.rb +1 -1
- data/lib/ruby_llm/providers/openai/media.rb +4 -4
- data/lib/ruby_llm/providers/openai/tools.rb +11 -6
- data/lib/ruby_llm/providers/openai.rb +2 -2
- data/lib/ruby_llm/providers/vertexai/chat.rb +14 -0
- data/lib/ruby_llm/providers/vertexai/embeddings.rb +32 -0
- data/lib/ruby_llm/providers/vertexai/models.rb +130 -0
- data/lib/ruby_llm/providers/vertexai/streaming.rb +14 -0
- data/lib/ruby_llm/providers/vertexai.rb +55 -0
- data/lib/ruby_llm/railtie.rb +20 -3
- data/lib/ruby_llm/streaming.rb +1 -1
- data/lib/ruby_llm/utils.rb +5 -9
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +4 -3
- data/lib/tasks/models.rake +39 -28
- data/lib/tasks/ruby_llm.rake +15 -0
- data/lib/tasks/vcr.rake +2 -2
- metadata +36 -2
- data/lib/generators/ruby_llm/install/templates/INSTALL_INFO.md.tt +0 -108
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module Providers
|
5
|
+
class VertexAI
|
6
|
+
# Models methods for the Vertex AI integration
|
7
|
+
module Models
|
8
|
+
# Gemini and other Google models that aren't returned by the API
|
9
|
+
KNOWN_GOOGLE_MODELS = %w[
|
10
|
+
gemini-2.5-flash-lite
|
11
|
+
gemini-2.5-pro
|
12
|
+
gemini-2.5-flash
|
13
|
+
gemini-2.0-flash-lite-001
|
14
|
+
gemini-2.0-flash-001
|
15
|
+
gemini-2.0-flash
|
16
|
+
gemini-2.0-flash-exp
|
17
|
+
gemini-1.5-pro-002
|
18
|
+
gemini-1.5-pro
|
19
|
+
gemini-1.5-flash-002
|
20
|
+
gemini-1.5-flash
|
21
|
+
gemini-1.5-flash-8b
|
22
|
+
gemini-pro
|
23
|
+
gemini-pro-vision
|
24
|
+
gemini-exp-1206
|
25
|
+
gemini-exp-1121
|
26
|
+
gemini-embedding-001
|
27
|
+
text-embedding-005
|
28
|
+
text-embedding-004
|
29
|
+
text-multilingual-embedding-002
|
30
|
+
].freeze
|
31
|
+
|
32
|
+
def list_models
|
33
|
+
all_models = []
|
34
|
+
page_token = nil
|
35
|
+
|
36
|
+
all_models.concat(build_known_models)
|
37
|
+
|
38
|
+
loop do
|
39
|
+
response = @connection.get('publishers/google/models') do |req|
|
40
|
+
req.headers['x-goog-user-project'] = @config.vertexai_project_id
|
41
|
+
req.params = { pageSize: 100 }
|
42
|
+
req.params[:pageToken] = page_token if page_token
|
43
|
+
end
|
44
|
+
|
45
|
+
publisher_models = response.body['publisherModels'] || []
|
46
|
+
publisher_models.each do |model_data|
|
47
|
+
next if model_data['launchStage'] == 'DEPRECATED'
|
48
|
+
|
49
|
+
model_id = extract_model_id_from_path(model_data['name'])
|
50
|
+
all_models << build_model_from_api_data(model_data, model_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
page_token = response.body['nextPageToken']
|
54
|
+
break unless page_token
|
55
|
+
end
|
56
|
+
|
57
|
+
all_models
|
58
|
+
rescue StandardError => e
|
59
|
+
RubyLLM.logger.debug "Error fetching Vertex AI models: #{e.message}"
|
60
|
+
build_known_models
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def build_known_models
|
66
|
+
KNOWN_GOOGLE_MODELS.map do |model_id|
|
67
|
+
Model::Info.new(
|
68
|
+
id: model_id,
|
69
|
+
name: model_id,
|
70
|
+
provider: slug,
|
71
|
+
family: determine_model_family(model_id),
|
72
|
+
created_at: nil,
|
73
|
+
context_window: nil,
|
74
|
+
max_output_tokens: nil,
|
75
|
+
modalities: nil,
|
76
|
+
capabilities: %w[streaming function_calling],
|
77
|
+
pricing: nil,
|
78
|
+
metadata: {
|
79
|
+
source: 'known_models'
|
80
|
+
}
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_model_from_api_data(model_data, model_id)
|
86
|
+
Model::Info.new(
|
87
|
+
id: model_id,
|
88
|
+
name: model_id,
|
89
|
+
provider: slug,
|
90
|
+
family: determine_model_family(model_id),
|
91
|
+
created_at: nil,
|
92
|
+
context_window: nil,
|
93
|
+
max_output_tokens: nil,
|
94
|
+
modalities: nil,
|
95
|
+
capabilities: extract_capabilities(model_data),
|
96
|
+
pricing: nil,
|
97
|
+
metadata: {
|
98
|
+
version_id: model_data['versionId'],
|
99
|
+
open_source_category: model_data['openSourceCategory'],
|
100
|
+
launch_stage: model_data['launchStage'],
|
101
|
+
supported_actions: model_data['supportedActions'],
|
102
|
+
publisher_model_template: model_data['publisherModelTemplate']
|
103
|
+
}
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
def extract_model_id_from_path(path)
|
108
|
+
path.split('/').last
|
109
|
+
end
|
110
|
+
|
111
|
+
def determine_model_family(model_id)
|
112
|
+
case model_id
|
113
|
+
when /^gemini-2\.\d+/ then 'gemini-2'
|
114
|
+
when /^gemini-1\.\d+/ then 'gemini-1.5'
|
115
|
+
when /^text-embedding/ then 'text-embedding'
|
116
|
+
when /bison/ then 'palm'
|
117
|
+
else 'gemini'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def extract_capabilities(model_data)
|
122
|
+
capabilities = ['streaming']
|
123
|
+
model_name = model_data['name']
|
124
|
+
capabilities << 'function_calling' if model_name.include?('gemini')
|
125
|
+
capabilities.uniq
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module Providers
|
5
|
+
class VertexAI
|
6
|
+
# Streaming methods for the Vertex AI implementation
|
7
|
+
module Streaming
|
8
|
+
def stream_url
|
9
|
+
"projects/#{@config.vertexai_project_id}/locations/#{@config.vertexai_location}/publishers/google/models/#{@model}:streamGenerateContent?alt=sse" # rubocop:disable Layout/LineLength
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module Providers
|
5
|
+
# Google Vertex AI implementation
|
6
|
+
class VertexAI < Gemini
|
7
|
+
include VertexAI::Chat
|
8
|
+
include VertexAI::Streaming
|
9
|
+
include VertexAI::Embeddings
|
10
|
+
include VertexAI::Models
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
super
|
14
|
+
@authorizer = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def api_base
|
18
|
+
"https://#{@config.vertexai_location}-aiplatform.googleapis.com/v1beta1"
|
19
|
+
end
|
20
|
+
|
21
|
+
def headers
|
22
|
+
{
|
23
|
+
'Authorization' => "Bearer #{access_token}"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def configuration_requirements
|
29
|
+
%i[vertexai_project_id vertexai_location]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def access_token
|
36
|
+
return 'test-token' if defined?(VCR) && !VCR.current_cassette.recording?
|
37
|
+
|
38
|
+
initialize_authorizer unless @authorizer
|
39
|
+
@authorizer.fetch_access_token!['access_token']
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize_authorizer
|
43
|
+
require 'googleauth'
|
44
|
+
@authorizer = ::Google::Auth.get_application_default(
|
45
|
+
scope: [
|
46
|
+
'https://www.googleapis.com/auth/cloud-platform',
|
47
|
+
'https://www.googleapis.com/auth/generative-language.retriever'
|
48
|
+
]
|
49
|
+
)
|
50
|
+
rescue LoadError
|
51
|
+
raise Error, 'The googleauth gem is required for Vertex AI. Please add it to your Gemfile: gem "googleauth"'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/ruby_llm/railtie.rb
CHANGED
@@ -3,14 +3,31 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
# Rails integration for RubyLLM
|
5
5
|
class Railtie < Rails::Railtie
|
6
|
+
initializer 'ruby_llm.inflections' do
|
7
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
8
|
+
inflect.acronym 'LLM'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
6
12
|
initializer 'ruby_llm.active_record' do
|
7
13
|
ActiveSupport.on_load :active_record do
|
8
|
-
|
14
|
+
if RubyLLM.config.use_new_acts_as
|
15
|
+
require 'ruby_llm/active_record/acts_as'
|
16
|
+
::ActiveRecord::Base.include RubyLLM::ActiveRecord::ActsAs
|
17
|
+
else
|
18
|
+
require 'ruby_llm/active_record/acts_as_legacy'
|
19
|
+
::ActiveRecord::Base.include RubyLLM::ActiveRecord::ActsAsLegacy
|
20
|
+
|
21
|
+
Rails.logger.warn(
|
22
|
+
"\n!!! RubyLLM's legacy acts_as API is deprecated and will be removed in RubyLLM 2.0.0. " \
|
23
|
+
"Please consult the migration guide at https://rubyllm.com/upgrading-to-1-7/\n"
|
24
|
+
)
|
25
|
+
end
|
9
26
|
end
|
10
27
|
end
|
11
28
|
|
12
|
-
|
13
|
-
|
29
|
+
rake_tasks do
|
30
|
+
load 'tasks/ruby_llm.rake'
|
14
31
|
end
|
15
32
|
end
|
16
33
|
end
|
data/lib/ruby_llm/streaming.rb
CHANGED
data/lib/ruby_llm/utils.rb
CHANGED
@@ -5,10 +5,6 @@ module RubyLLM
|
|
5
5
|
module Utils
|
6
6
|
module_function
|
7
7
|
|
8
|
-
def format_text_file_for_llm(text_file)
|
9
|
-
"<file name='#{text_file.filename}' mime_type='#{text_file.mime_type}'>#{text_file.content}</file>"
|
10
|
-
end
|
11
|
-
|
12
8
|
def hash_get(hash, key)
|
13
9
|
hash[key.to_sym] || hash[key.to_s]
|
14
10
|
end
|
@@ -36,12 +32,12 @@ module RubyLLM
|
|
36
32
|
value.is_a?(Date) ? value : Date.parse(value.to_s)
|
37
33
|
end
|
38
34
|
|
39
|
-
def deep_merge(
|
40
|
-
|
41
|
-
if
|
42
|
-
deep_merge(
|
35
|
+
def deep_merge(original, overrides)
|
36
|
+
original.merge(overrides) do |_key, original_value, overrides_value|
|
37
|
+
if original_value.is_a?(Hash) && overrides_value.is_a?(Hash)
|
38
|
+
deep_merge(original_value, overrides_value)
|
43
39
|
else
|
44
|
-
|
40
|
+
overrides_value
|
45
41
|
end
|
46
42
|
end
|
47
43
|
end
|
data/lib/ruby_llm/version.rb
CHANGED
data/lib/ruby_llm.rb
CHANGED
@@ -21,11 +21,11 @@ loader.inflector.inflect(
|
|
21
21
|
'openrouter' => 'OpenRouter',
|
22
22
|
'gpustack' => 'GPUStack',
|
23
23
|
'mistral' => 'Mistral',
|
24
|
-
'
|
24
|
+
'vertexai' => 'VertexAI',
|
25
|
+
'pdf' => 'PDF',
|
26
|
+
'UI' => 'UI'
|
25
27
|
)
|
26
28
|
loader.ignore("#{__dir__}/tasks")
|
27
|
-
loader.ignore("#{__dir__}/ruby_llm/railtie")
|
28
|
-
loader.ignore("#{__dir__}/ruby_llm/active_record")
|
29
29
|
loader.ignore("#{__dir__}/generators")
|
30
30
|
loader.setup
|
31
31
|
|
@@ -88,6 +88,7 @@ RubyLLM::Provider.register :ollama, RubyLLM::Providers::Ollama
|
|
88
88
|
RubyLLM::Provider.register :openai, RubyLLM::Providers::OpenAI
|
89
89
|
RubyLLM::Provider.register :openrouter, RubyLLM::Providers::OpenRouter
|
90
90
|
RubyLLM::Provider.register :perplexity, RubyLLM::Providers::Perplexity
|
91
|
+
RubyLLM::Provider.register :vertexai, RubyLLM::Providers::VertexAI
|
91
92
|
|
92
93
|
if defined?(Rails::Railtie)
|
93
94
|
require 'ruby_llm/railtie'
|
data/lib/tasks/models.rake
CHANGED
@@ -46,6 +46,8 @@ def configure_from_env
|
|
46
46
|
config.perplexity_api_key = ENV.fetch('PERPLEXITY_API_KEY', nil)
|
47
47
|
config.openrouter_api_key = ENV.fetch('OPENROUTER_API_KEY', nil)
|
48
48
|
config.mistral_api_key = ENV.fetch('MISTRAL_API_KEY', nil)
|
49
|
+
config.vertexai_location = ENV.fetch('GOOGLE_CLOUD_LOCATION', nil)
|
50
|
+
config.vertexai_project_id = ENV.fetch('GOOGLE_CLOUD_PROJECT', nil)
|
49
51
|
configure_bedrock(config)
|
50
52
|
config.request_timeout = 30
|
51
53
|
end
|
@@ -74,7 +76,7 @@ def refresh_models
|
|
74
76
|
validate_models!(models)
|
75
77
|
|
76
78
|
puts "Saving models.json (#{models.all.size} models)"
|
77
|
-
models.
|
79
|
+
models.save_to_json
|
78
80
|
end
|
79
81
|
|
80
82
|
@models = models
|
@@ -152,28 +154,11 @@ def generate_models_markdown
|
|
152
154
|
|
153
155
|
---
|
154
156
|
|
155
|
-
|
157
|
+
## Model Data Sources
|
156
158
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
* How to use model aliases for simpler configuration
|
161
|
-
|
162
|
-
## How Model Data Works
|
163
|
-
|
164
|
-
RubyLLM's model registry combines data from multiple sources:
|
165
|
-
|
166
|
-
- **OpenAI, Anthropic, DeepSeek, Gemini**: Data from [Parsera](https://api.parsera.org/v1/llm-specs)
|
167
|
-
- **OpenRouter**: Direct from OpenRouter's API
|
168
|
-
- **Other providers**: Defined in `capabilities.rb` files
|
169
|
-
|
170
|
-
## Contributing Model Updates
|
171
|
-
|
172
|
-
**For major providers** (OpenAI, Anthropic, DeepSeek, Gemini): File issues with [Parsera](https://github.com/parsera-labs/api-llm-specs/issues) for public model data corrections.
|
173
|
-
|
174
|
-
**For other providers**: Edit `lib/ruby_llm/providers/<provider>/capabilities.rb` then run `rake models:update`.
|
175
|
-
|
176
|
-
See the [Contributing Guide](https://github.com/crmne/ruby_llm/blob/main/CONTRIBUTING.md) for details.
|
159
|
+
- **OpenAI, Anthropic, DeepSeek, Gemini, VertexAI**: Enriched by [🚀 Parsera](https://parsera.org/) *([free LLM metadata API](https://api.parsera.org/v1/llm-specs) - [go say thanks!](https://github.com/parsera-labs/api-llm-specs))*
|
160
|
+
- **OpenRouter**: Direct API
|
161
|
+
- **Others**: Local capabilities files
|
177
162
|
|
178
163
|
## Last Updated
|
179
164
|
{: .d-inline-block }
|
@@ -369,7 +354,7 @@ def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
369
354
|
|
370
355
|
models['bedrock'].each do |bedrock_model|
|
371
356
|
next unless bedrock_model.start_with?('anthropic.')
|
372
|
-
next unless bedrock_model =~ /anthropic\.(claude-[\d
|
357
|
+
next unless bedrock_model =~ /anthropic\.(claude-[\d.]+-[a-z]+)/
|
373
358
|
|
374
359
|
base_name = Regexp.last_match(1)
|
375
360
|
anthropic_name = base_name.tr('.', '-')
|
@@ -388,6 +373,7 @@ def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
388
373
|
aliases[anthropic_name]['openrouter'] = openrouter_model if openrouter_model
|
389
374
|
end
|
390
375
|
|
376
|
+
# Gemini models (also map to vertexai)
|
391
377
|
models['gemini'].each do |model|
|
392
378
|
openrouter_variants = [
|
393
379
|
"google/#{model}",
|
@@ -396,13 +382,38 @@ def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
396
382
|
]
|
397
383
|
|
398
384
|
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
399
|
-
|
385
|
+
vertexai_model = models['vertexai'].include?(model) ? model : nil
|
386
|
+
|
387
|
+
next unless openrouter_model || vertexai_model
|
400
388
|
|
401
389
|
alias_key = model.gsub('-latest', '')
|
402
|
-
aliases[alias_key] = {
|
403
|
-
|
404
|
-
|
405
|
-
|
390
|
+
aliases[alias_key] = { 'gemini' => model }
|
391
|
+
aliases[alias_key]['openrouter'] = openrouter_model if openrouter_model
|
392
|
+
aliases[alias_key]['vertexai'] = vertexai_model if vertexai_model
|
393
|
+
end
|
394
|
+
|
395
|
+
# VertexAI models that aren't in Gemini (e.g. older models like text-bison)
|
396
|
+
models['vertexai'].each do |model|
|
397
|
+
# Skip if already handled above
|
398
|
+
next if models['gemini'].include?(model)
|
399
|
+
|
400
|
+
# Check if OpenRouter has this Google model
|
401
|
+
openrouter_variants = [
|
402
|
+
"google/#{model}",
|
403
|
+
"google/#{model.tr('.', '-')}"
|
404
|
+
]
|
405
|
+
|
406
|
+
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
407
|
+
gemini_model = models['gemini'].include?(model) ? model : nil
|
408
|
+
|
409
|
+
next unless openrouter_model || gemini_model
|
410
|
+
|
411
|
+
alias_key = model.gsub('-latest', '')
|
412
|
+
next if aliases[alias_key] # Skip if already created
|
413
|
+
|
414
|
+
aliases[alias_key] = { 'vertexai' => model }
|
415
|
+
aliases[alias_key]['openrouter'] = openrouter_model if openrouter_model
|
416
|
+
aliases[alias_key]['gemini'] = gemini_model if gemini_model
|
406
417
|
end
|
407
418
|
|
408
419
|
models['deepseek'].each do |model|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :ruby_llm do
|
4
|
+
desc 'Load models from models.json into the database'
|
5
|
+
task load_models: :environment do
|
6
|
+
if RubyLLM.config.model_registry_class
|
7
|
+
RubyLLM.models.load_from_json!
|
8
|
+
model_class = RubyLLM.config.model_registry_class.constantize
|
9
|
+
model_class.save_to_database
|
10
|
+
puts "✅ Loaded #{model_class.count} models into database"
|
11
|
+
else
|
12
|
+
puts 'Model registry not configured. Run rails generate ruby_llm:install'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/tasks/vcr.rake
CHANGED
@@ -75,11 +75,11 @@ end
|
|
75
75
|
|
76
76
|
namespace :vcr do
|
77
77
|
desc 'Record VCR cassettes (rake vcr:record[all] or vcr:record[openai,anthropic])'
|
78
|
-
task :record,
|
78
|
+
task :record, :providers do |_, args|
|
79
79
|
require 'fileutils'
|
80
80
|
require 'ruby_llm'
|
81
81
|
|
82
|
-
providers = (args[:providers]
|
82
|
+
providers = args.extras.unshift(args[:providers]).compact.map(&:downcase)
|
83
83
|
cassette_dir = 'spec/fixtures/vcr_cassettes'
|
84
84
|
FileUtils.mkdir_p(cassette_dir)
|
85
85
|
|
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: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
@@ -136,17 +136,40 @@ extra_rdoc_files: []
|
|
136
136
|
files:
|
137
137
|
- LICENSE
|
138
138
|
- README.md
|
139
|
-
- lib/generators/ruby_llm/
|
139
|
+
- lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb
|
140
|
+
- lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt
|
141
|
+
- lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt
|
142
|
+
- lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt
|
143
|
+
- lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt
|
144
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt
|
145
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt
|
146
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt
|
147
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt
|
148
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt
|
149
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt
|
150
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt
|
151
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt
|
152
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt
|
153
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt
|
154
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt
|
140
155
|
- lib/generators/ruby_llm/install/templates/chat_model.rb.tt
|
141
156
|
- lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt
|
142
157
|
- lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt
|
158
|
+
- lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt
|
143
159
|
- lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt
|
144
160
|
- lib/generators/ruby_llm/install/templates/initializer.rb.tt
|
145
161
|
- lib/generators/ruby_llm/install/templates/message_model.rb.tt
|
162
|
+
- lib/generators/ruby_llm/install/templates/model_model.rb.tt
|
146
163
|
- lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt
|
147
164
|
- lib/generators/ruby_llm/install_generator.rb
|
165
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt
|
166
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb
|
148
167
|
- lib/ruby_llm.rb
|
149
168
|
- lib/ruby_llm/active_record/acts_as.rb
|
169
|
+
- lib/ruby_llm/active_record/acts_as_legacy.rb
|
170
|
+
- lib/ruby_llm/active_record/chat_methods.rb
|
171
|
+
- lib/ruby_llm/active_record/message_methods.rb
|
172
|
+
- lib/ruby_llm/active_record/model_methods.rb
|
150
173
|
- lib/ruby_llm/aliases.json
|
151
174
|
- lib/ruby_llm/aliases.rb
|
152
175
|
- lib/ruby_llm/attachment.rb
|
@@ -205,6 +228,7 @@ files:
|
|
205
228
|
- lib/ruby_llm/providers/gemini/tools.rb
|
206
229
|
- lib/ruby_llm/providers/gpustack.rb
|
207
230
|
- lib/ruby_llm/providers/gpustack/chat.rb
|
231
|
+
- lib/ruby_llm/providers/gpustack/media.rb
|
208
232
|
- lib/ruby_llm/providers/gpustack/models.rb
|
209
233
|
- lib/ruby_llm/providers/mistral.rb
|
210
234
|
- lib/ruby_llm/providers/mistral/capabilities.rb
|
@@ -214,6 +238,7 @@ files:
|
|
214
238
|
- lib/ruby_llm/providers/ollama.rb
|
215
239
|
- lib/ruby_llm/providers/ollama/chat.rb
|
216
240
|
- lib/ruby_llm/providers/ollama/media.rb
|
241
|
+
- lib/ruby_llm/providers/ollama/models.rb
|
217
242
|
- lib/ruby_llm/providers/openai.rb
|
218
243
|
- lib/ruby_llm/providers/openai/capabilities.rb
|
219
244
|
- lib/ruby_llm/providers/openai/chat.rb
|
@@ -229,6 +254,11 @@ files:
|
|
229
254
|
- lib/ruby_llm/providers/perplexity/capabilities.rb
|
230
255
|
- lib/ruby_llm/providers/perplexity/chat.rb
|
231
256
|
- lib/ruby_llm/providers/perplexity/models.rb
|
257
|
+
- lib/ruby_llm/providers/vertexai.rb
|
258
|
+
- lib/ruby_llm/providers/vertexai/chat.rb
|
259
|
+
- lib/ruby_llm/providers/vertexai/embeddings.rb
|
260
|
+
- lib/ruby_llm/providers/vertexai/models.rb
|
261
|
+
- lib/ruby_llm/providers/vertexai/streaming.rb
|
232
262
|
- lib/ruby_llm/railtie.rb
|
233
263
|
- lib/ruby_llm/stream_accumulator.rb
|
234
264
|
- lib/ruby_llm/streaming.rb
|
@@ -238,6 +268,7 @@ files:
|
|
238
268
|
- lib/ruby_llm/version.rb
|
239
269
|
- lib/tasks/models.rake
|
240
270
|
- lib/tasks/release.rake
|
271
|
+
- lib/tasks/ruby_llm.rake
|
241
272
|
- lib/tasks/vcr.rake
|
242
273
|
homepage: https://rubyllm.com
|
243
274
|
licenses:
|
@@ -249,6 +280,9 @@ metadata:
|
|
249
280
|
documentation_uri: https://rubyllm.com
|
250
281
|
bug_tracker_uri: https://github.com/crmne/ruby_llm/issues
|
251
282
|
rubygems_mfa_required: 'true'
|
283
|
+
post_install_message: |
|
284
|
+
Upgrading from RubyLLM <= 1.6.x? Check the upgrade guide for new features and migration instructions
|
285
|
+
--> https://rubyllm.com/upgrading-to-1-7/
|
252
286
|
rdoc_options: []
|
253
287
|
require_paths:
|
254
288
|
- lib
|
@@ -1,108 +0,0 @@
|
|
1
|
-
# RubyLLM Rails Setup Complete!
|
2
|
-
|
3
|
-
Thanks for installing RubyLLM in your Rails application. Here's what was created:
|
4
|
-
|
5
|
-
## Models
|
6
|
-
|
7
|
-
- `<%= options[:chat_model_name] %>` - Stores chat sessions and their associated model ID
|
8
|
-
- `<%= options[:message_model_name] %>` - Stores individual messages in a chat
|
9
|
-
- `<%= options[:tool_call_model_name] %>` - Stores tool calls made by language models
|
10
|
-
|
11
|
-
**Note:** Do not add `validates :content, presence: true` to your Message model - RubyLLM creates empty assistant messages before API calls for streaming support.
|
12
|
-
|
13
|
-
## Configuration Options
|
14
|
-
|
15
|
-
The generator supports the following options to customize model names:
|
16
|
-
|
17
|
-
```bash
|
18
|
-
rails generate ruby_llm:install \
|
19
|
-
--chat-model-name=Conversation \
|
20
|
-
--message-model-name=ChatMessage \
|
21
|
-
--tool-call-model-name=FunctionCall
|
22
|
-
```
|
23
|
-
|
24
|
-
This is useful when you need to avoid namespace collisions with existing models in your application. Table names will be automatically derived from the model names following Rails conventions.
|
25
|
-
|
26
|
-
## Next Steps
|
27
|
-
|
28
|
-
1. **Run migrations:**
|
29
|
-
```bash
|
30
|
-
rails db:migrate
|
31
|
-
```
|
32
|
-
|
33
|
-
**Database Note:** The migrations use `jsonb` for PostgreSQL and `json` for MySQL/SQLite automatically.
|
34
|
-
|
35
|
-
2. **Set your API keys** in `config/initializers/ruby_llm.rb` or using environment variables:
|
36
|
-
```ruby
|
37
|
-
# config/initializers/ruby_llm.rb
|
38
|
-
RubyLLM.configure do |config|
|
39
|
-
config.openai_api_key = ENV['OPENAI_API_KEY']
|
40
|
-
config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']
|
41
|
-
config.gemini_api_key = ENV['GEMINI_API_KEY']
|
42
|
-
# ... add other providers as needed
|
43
|
-
end
|
44
|
-
```
|
45
|
-
|
46
|
-
3. **Start using RubyLLM in your code:**
|
47
|
-
```ruby
|
48
|
-
# Basic usage
|
49
|
-
chat = <%= options[:chat_model_name] %>.create!(model_id: 'gpt-4.1-nano')
|
50
|
-
response = chat.ask("What is Ruby on Rails?")
|
51
|
-
|
52
|
-
# With file attachments (requires ActiveStorage setup)
|
53
|
-
chat.ask("What's in this file?", with: "report.pdf")
|
54
|
-
chat.ask("Analyze these files", with: ["image.jpg", "data.csv", "notes.txt"])
|
55
|
-
```
|
56
|
-
|
57
|
-
4. **For streaming responses** with Hotwire/Turbo:
|
58
|
-
```ruby
|
59
|
-
# app/models/<%= options[:message_model_name].underscore %>.rb
|
60
|
-
class <%= options[:message_model_name] %> < ApplicationRecord
|
61
|
-
acts_as_message
|
62
|
-
|
63
|
-
# Helper to broadcast chunks during streaming
|
64
|
-
def broadcast_append_chunk(chunk_content)
|
65
|
-
broadcast_append_to [ chat, "messages" ],
|
66
|
-
target: dom_id(self, "content"),
|
67
|
-
html: chunk_content
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# app/jobs/chat_stream_job.rb
|
72
|
-
class ChatStreamJob < ApplicationJob
|
73
|
-
def perform(chat_id, user_content)
|
74
|
-
chat = <%= options[:chat_model_name] %>.find(chat_id)
|
75
|
-
chat.ask(user_content) do |chunk|
|
76
|
-
assistant_message = chat.messages.last
|
77
|
-
if chunk.content && assistant_message
|
78
|
-
assistant_message.broadcast_append_chunk(chunk.content)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# In your controller
|
85
|
-
ChatStreamJob.perform_later(@chat.id, params[:content])
|
86
|
-
```
|
87
|
-
|
88
|
-
## Optional: ActiveStorage for Attachments
|
89
|
-
|
90
|
-
If you want to use file attachments (PDFs, images, etc.), set up ActiveStorage:
|
91
|
-
|
92
|
-
```bash
|
93
|
-
rails active_storage:install
|
94
|
-
rails db:migrate
|
95
|
-
```
|
96
|
-
|
97
|
-
Then add to your Message model:
|
98
|
-
```ruby
|
99
|
-
class <%= options[:message_model_name] %> < ApplicationRecord
|
100
|
-
acts_as_message
|
101
|
-
has_many_attached :attachments
|
102
|
-
end
|
103
|
-
```
|
104
|
-
|
105
|
-
## Learn More
|
106
|
-
|
107
|
-
- See the [Rails Integration Guide](https://rubyllm.com/guides/rails) for detailed examples
|
108
|
-
- Visit the [RubyLLM Documentation](https://rubyllm.com) for full API reference
|