ruby_llm 0.1.0.pre4 → 0.1.0.pre5
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/.github/workflows/gem-push.yml +1 -1
- data/.github/workflows/test.yml +5 -2
- data/.overcommit.yml +1 -1
- data/README.md +56 -181
- data/bin/console +6 -0
- data/lib/ruby_llm/chat.rb +95 -0
- data/lib/ruby_llm/chunk.rb +6 -0
- data/lib/ruby_llm/configuration.rb +2 -4
- data/lib/ruby_llm/message.rb +26 -18
- data/lib/ruby_llm/model_capabilities/anthropic.rb +43 -48
- data/lib/ruby_llm/model_capabilities/openai.rb +82 -89
- data/lib/ruby_llm/model_info.rb +26 -17
- data/lib/ruby_llm/models.json +686 -0
- data/lib/ruby_llm/models.rb +52 -0
- data/lib/ruby_llm/provider.rb +99 -0
- data/lib/ruby_llm/providers/anthropic.rb +92 -243
- data/lib/ruby_llm/providers/openai.rb +130 -174
- data/lib/ruby_llm/tool.rb +71 -50
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +35 -37
- data/lib/tasks/models.rake +25 -0
- data/ruby_llm.gemspec +1 -0
- metadata +32 -8
- data/lib/ruby_llm/active_record/acts_as.rb +0 -115
- data/lib/ruby_llm/client.rb +0 -70
- data/lib/ruby_llm/conversation.rb +0 -19
- data/lib/ruby_llm/model_capabilities/base.rb +0 -35
- data/lib/ruby_llm/providers/base.rb +0 -67
metadata
CHANGED
@@ -1,14 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_llm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
8
|
+
autorequire:
|
8
9
|
bindir: exe
|
9
10
|
cert_chain: []
|
10
|
-
date: 2025-01
|
11
|
+
date: 2025-02-01 00:00:00.000000000 Z
|
11
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: event_stream_parser
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.0.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.3.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.0.0
|
12
33
|
- !ruby/object:Gem::Dependency
|
13
34
|
name: faraday
|
14
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -322,21 +343,22 @@ files:
|
|
322
343
|
- bin/console
|
323
344
|
- bin/setup
|
324
345
|
- lib/ruby_llm.rb
|
325
|
-
- lib/ruby_llm/
|
326
|
-
- lib/ruby_llm/
|
346
|
+
- lib/ruby_llm/chat.rb
|
347
|
+
- lib/ruby_llm/chunk.rb
|
327
348
|
- lib/ruby_llm/configuration.rb
|
328
|
-
- lib/ruby_llm/conversation.rb
|
329
349
|
- lib/ruby_llm/message.rb
|
330
350
|
- lib/ruby_llm/model_capabilities/anthropic.rb
|
331
|
-
- lib/ruby_llm/model_capabilities/base.rb
|
332
351
|
- lib/ruby_llm/model_capabilities/openai.rb
|
333
352
|
- lib/ruby_llm/model_info.rb
|
353
|
+
- lib/ruby_llm/models.json
|
354
|
+
- lib/ruby_llm/models.rb
|
355
|
+
- lib/ruby_llm/provider.rb
|
334
356
|
- lib/ruby_llm/providers/anthropic.rb
|
335
|
-
- lib/ruby_llm/providers/base.rb
|
336
357
|
- lib/ruby_llm/providers/openai.rb
|
337
358
|
- lib/ruby_llm/railtie.rb
|
338
359
|
- lib/ruby_llm/tool.rb
|
339
360
|
- lib/ruby_llm/version.rb
|
361
|
+
- lib/tasks/models.rake
|
340
362
|
- ruby_llm.gemspec
|
341
363
|
homepage: https://github.com/crmne/ruby_llm
|
342
364
|
licenses:
|
@@ -347,6 +369,7 @@ metadata:
|
|
347
369
|
changelog_uri: https://github.com/crmne/ruby_llm/blob/main/CHANGELOG.md
|
348
370
|
documentation_uri: https://rubydoc.info/gems/ruby_llm
|
349
371
|
bug_tracker_uri: https://github.com/crmne/ruby_llm/issues
|
372
|
+
post_install_message:
|
350
373
|
rdoc_options: []
|
351
374
|
require_paths:
|
352
375
|
- lib
|
@@ -361,7 +384,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
361
384
|
- !ruby/object:Gem::Version
|
362
385
|
version: '0'
|
363
386
|
requirements: []
|
364
|
-
rubygems_version: 3.
|
387
|
+
rubygems_version: 3.5.22
|
388
|
+
signing_key:
|
365
389
|
specification_version: 4
|
366
390
|
summary: The Ruby LLM client library - unified interface for OpenAI, Anthropic, and
|
367
391
|
other LLM providers
|
@@ -1,115 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyLLM
|
4
|
-
module ActiveRecord
|
5
|
-
# Provides ActsAs functionality for LLM-related models
|
6
|
-
module ActsAs
|
7
|
-
def acts_as_llm_model(_options = {})
|
8
|
-
include ModelMethods
|
9
|
-
end
|
10
|
-
|
11
|
-
def acts_as_llm_conversation(_options = {})
|
12
|
-
include ConversationMethods
|
13
|
-
has_many :messages, -> { order(created_at: :asc) }
|
14
|
-
end
|
15
|
-
|
16
|
-
def acts_as_llm_message(_options = {})
|
17
|
-
include MessageMethods
|
18
|
-
belongs_to :conversation
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Methods for LLM model functionality
|
23
|
-
module ModelMethods
|
24
|
-
extend ActiveSupport::Concern
|
25
|
-
|
26
|
-
included do
|
27
|
-
validates :name, presence: true
|
28
|
-
validates :provider, presence: true
|
29
|
-
end
|
30
|
-
|
31
|
-
class_methods do
|
32
|
-
def sync_models
|
33
|
-
# Logic to sync models from providers
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Methods for LLM conversation handling
|
39
|
-
module ConversationMethods
|
40
|
-
extend ActiveSupport::Concern
|
41
|
-
|
42
|
-
included do
|
43
|
-
before_create :set_default_model
|
44
|
-
end
|
45
|
-
|
46
|
-
def send_message(content, model: nil)
|
47
|
-
transaction do
|
48
|
-
create_user_message(content)
|
49
|
-
create_assistant_response(model)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def create_user_message(content)
|
56
|
-
messages.create!(
|
57
|
-
role: :user,
|
58
|
-
content: content
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
def create_assistant_response(model)
|
63
|
-
response = RubyLLM.client.chat(
|
64
|
-
conversation_messages,
|
65
|
-
model: model || current_model
|
66
|
-
)
|
67
|
-
|
68
|
-
messages.create!(
|
69
|
-
role: :assistant,
|
70
|
-
content: response.content,
|
71
|
-
token_count: response.token_count
|
72
|
-
)
|
73
|
-
end
|
74
|
-
|
75
|
-
def conversation_messages
|
76
|
-
messages.map(&:to_llm_format)
|
77
|
-
end
|
78
|
-
|
79
|
-
def set_default_model
|
80
|
-
self.current_model ||= RubyLLM.configuration.default_model
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# Methods for LLM message handling
|
85
|
-
module MessageMethods
|
86
|
-
extend ActiveSupport::Concern
|
87
|
-
|
88
|
-
included do
|
89
|
-
validates :role, presence: true, inclusion: { in: RubyLLM::Message::VALID_ROLES.map(&:to_s) }
|
90
|
-
validates :content, presence: true, unless: :tool_call?
|
91
|
-
|
92
|
-
before_save :calculate_tokens
|
93
|
-
end
|
94
|
-
|
95
|
-
def to_llm_format
|
96
|
-
RubyLLM::Message.new(
|
97
|
-
role: role.to_sym,
|
98
|
-
content: content,
|
99
|
-
tool_calls: tool_calls,
|
100
|
-
tool_results: tool_results
|
101
|
-
)
|
102
|
-
end
|
103
|
-
|
104
|
-
private
|
105
|
-
|
106
|
-
def calculate_tokens
|
107
|
-
# Logic to calculate tokens
|
108
|
-
end
|
109
|
-
|
110
|
-
def tool_call?
|
111
|
-
tool_calls.present? || tool_results.present?
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
data/lib/ruby_llm/client.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyLLM
|
4
|
-
# Client class for handling LLM provider interactions
|
5
|
-
class Client
|
6
|
-
def initialize
|
7
|
-
@providers = {}
|
8
|
-
end
|
9
|
-
|
10
|
-
def chat(messages, model: nil, temperature: 0.7, stream: false, tools: nil, &block)
|
11
|
-
# Convert any hash messages to Message objects
|
12
|
-
formatted_messages = messages.map do |msg|
|
13
|
-
msg.is_a?(Message) ? msg : Message.new(**msg)
|
14
|
-
end
|
15
|
-
|
16
|
-
provider = provider_for(model)
|
17
|
-
response_messages = provider.chat(
|
18
|
-
formatted_messages,
|
19
|
-
model: model,
|
20
|
-
temperature: temperature,
|
21
|
-
stream: stream,
|
22
|
-
tools: tools,
|
23
|
-
&block
|
24
|
-
)
|
25
|
-
|
26
|
-
# Always return an array of messages, even for single responses
|
27
|
-
response_messages.is_a?(Array) ? response_messages : [response_messages]
|
28
|
-
end
|
29
|
-
|
30
|
-
def list_models(provider = nil)
|
31
|
-
if provider
|
32
|
-
provider_for(nil, provider).list_models
|
33
|
-
else
|
34
|
-
all_providers.flat_map(&:list_models)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def all_providers
|
41
|
-
[
|
42
|
-
provider_for(nil, :openai),
|
43
|
-
provider_for(nil, :anthropic)
|
44
|
-
]
|
45
|
-
end
|
46
|
-
|
47
|
-
def provider_for(model, specific_provider = nil)
|
48
|
-
provider_name = specific_provider || detect_provider(model)
|
49
|
-
@providers[provider_name] ||= case provider_name
|
50
|
-
when :openai then Providers::OpenAI.new
|
51
|
-
when :anthropic then Providers::Anthropic.new
|
52
|
-
else
|
53
|
-
raise Error, "Unsupported provider: #{provider_name}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def detect_provider(model)
|
58
|
-
return RubyLLM.configuration.default_provider unless model
|
59
|
-
|
60
|
-
case model
|
61
|
-
when /^gpt-/, /^text-davinci/
|
62
|
-
:openai
|
63
|
-
when /^claude/
|
64
|
-
:anthropic
|
65
|
-
else
|
66
|
-
RubyLLM.configuration.default_provider
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyLLM
|
4
|
-
# Represents a conversation with an LLM
|
5
|
-
class Conversation
|
6
|
-
attr_reader :id, :messages, :tools
|
7
|
-
|
8
|
-
def initialize(tools: [])
|
9
|
-
@id = SecureRandom.uuid
|
10
|
-
@messages = []
|
11
|
-
@tools = tools
|
12
|
-
end
|
13
|
-
|
14
|
-
def add_message(message)
|
15
|
-
@messages << message
|
16
|
-
message
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyLLM
|
4
|
-
module ModelCapabilities
|
5
|
-
class Base
|
6
|
-
def determine_context_window(_model_id)
|
7
|
-
raise NotImplementedError
|
8
|
-
end
|
9
|
-
|
10
|
-
def determine_max_tokens(_model_id)
|
11
|
-
raise NotImplementedError
|
12
|
-
end
|
13
|
-
|
14
|
-
def get_input_price(_model_id)
|
15
|
-
raise NotImplementedError
|
16
|
-
end
|
17
|
-
|
18
|
-
def get_output_price(_model_id)
|
19
|
-
raise NotImplementedError
|
20
|
-
end
|
21
|
-
|
22
|
-
def supports_vision?(_model_id)
|
23
|
-
raise NotImplementedError
|
24
|
-
end
|
25
|
-
|
26
|
-
def supports_functions?(_model_id)
|
27
|
-
raise NotImplementedError
|
28
|
-
end
|
29
|
-
|
30
|
-
def supports_json_mode?(_model_id)
|
31
|
-
raise NotImplementedError
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyLLM
|
4
|
-
module Providers
|
5
|
-
# Base provider class for LLM interactions
|
6
|
-
class Base
|
7
|
-
attr_reader :connection
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@connection = build_connection
|
11
|
-
end
|
12
|
-
|
13
|
-
def chat(messages, **options, &block)
|
14
|
-
raise NotImplementedError
|
15
|
-
end
|
16
|
-
|
17
|
-
protected
|
18
|
-
|
19
|
-
def check_for_api_error(response)
|
20
|
-
return unless response.body.is_a?(Hash) && response.body['type'] == 'error'
|
21
|
-
|
22
|
-
error_msg = response.body.dig('error', 'message') || 'Unknown API error'
|
23
|
-
raise RubyLLM::Error, "API error: #{error_msg}"
|
24
|
-
end
|
25
|
-
|
26
|
-
def build_connection
|
27
|
-
Faraday.new(url: api_base) do |f|
|
28
|
-
f.options.timeout = RubyLLM.configuration.request_timeout
|
29
|
-
f.request :json
|
30
|
-
f.response :json
|
31
|
-
f.adapter Faraday.default_adapter
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def handle_error(error)
|
36
|
-
case error
|
37
|
-
when Faraday::TimeoutError
|
38
|
-
raise RubyLLM::Error, 'Request timed out'
|
39
|
-
when Faraday::ConnectionFailed
|
40
|
-
raise RubyLLM::Error, 'Connection failed'
|
41
|
-
when Faraday::ClientError
|
42
|
-
handle_api_error(error)
|
43
|
-
else
|
44
|
-
raise error
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def handle_api_error(error)
|
49
|
-
raise RubyLLM::Error, "API error: #{error.response[:status]}"
|
50
|
-
end
|
51
|
-
|
52
|
-
def parse_error_message(response)
|
53
|
-
return "HTTP #{response.status}" unless response.body
|
54
|
-
|
55
|
-
if response.body.is_a?(String)
|
56
|
-
begin
|
57
|
-
JSON.parse(response.body).dig('error', 'message')
|
58
|
-
rescue StandardError
|
59
|
-
"HTTP #{response.status}"
|
60
|
-
end
|
61
|
-
else
|
62
|
-
response.body.dig('error', 'message') || "HTTP #{response.status}"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|