ruby_llm 0.1.0.pre4 → 0.1.0.pre6

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.
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.pre4
4
+ version: 0.1.0.pre6
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-30 00:00:00.000000000 Z
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
@@ -301,9 +322,10 @@ dependencies:
301
322
  - - ">="
302
323
  - !ruby/object:Gem::Version
303
324
  version: '0.9'
304
- description: Complete Ruby library for working with Large Language Models (LLMs).
305
- Supports OpenAI, Anthropic, and others with a consistent interface. Features include
306
- tool usage, token tracking, and seamless Rails integration.
325
+ description: A delightful Ruby way to work with AI language models. Provides a unified
326
+ interface to OpenAI and Anthropic models with automatic token counting, proper streaming
327
+ support, and a focus on developer happiness. No wrapping your head around multiple
328
+ APIs - just clean Ruby code that works.
307
329
  email:
308
330
  - carmine@paolino.me
309
331
  executables: []
@@ -322,21 +344,22 @@ files:
322
344
  - bin/console
323
345
  - bin/setup
324
346
  - lib/ruby_llm.rb
325
- - lib/ruby_llm/active_record/acts_as.rb
326
- - lib/ruby_llm/client.rb
347
+ - lib/ruby_llm/chat.rb
348
+ - lib/ruby_llm/chunk.rb
327
349
  - lib/ruby_llm/configuration.rb
328
- - lib/ruby_llm/conversation.rb
329
350
  - lib/ruby_llm/message.rb
330
351
  - lib/ruby_llm/model_capabilities/anthropic.rb
331
- - lib/ruby_llm/model_capabilities/base.rb
332
352
  - lib/ruby_llm/model_capabilities/openai.rb
333
353
  - lib/ruby_llm/model_info.rb
354
+ - lib/ruby_llm/models.json
355
+ - lib/ruby_llm/models.rb
356
+ - lib/ruby_llm/provider.rb
334
357
  - lib/ruby_llm/providers/anthropic.rb
335
- - lib/ruby_llm/providers/base.rb
336
358
  - lib/ruby_llm/providers/openai.rb
337
359
  - lib/ruby_llm/railtie.rb
338
360
  - lib/ruby_llm/tool.rb
339
361
  - lib/ruby_llm/version.rb
362
+ - lib/tasks/models.rake
340
363
  - ruby_llm.gemspec
341
364
  homepage: https://github.com/crmne/ruby_llm
342
365
  licenses:
@@ -344,9 +367,10 @@ licenses:
344
367
  metadata:
345
368
  homepage_uri: https://github.com/crmne/ruby_llm
346
369
  source_code_uri: https://github.com/crmne/ruby_llm
347
- changelog_uri: https://github.com/crmne/ruby_llm/blob/main/CHANGELOG.md
348
- documentation_uri: https://rubydoc.info/gems/ruby_llm
370
+ changelog_uri: https://github.com/crmne/ruby_llm/commits/main
371
+ documentation_uri: https://github.com/crmne/ruby_llm
349
372
  bug_tracker_uri: https://github.com/crmne/ruby_llm/issues
373
+ post_install_message:
350
374
  rdoc_options: []
351
375
  require_paths:
352
376
  - lib
@@ -361,8 +385,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
361
385
  - !ruby/object:Gem::Version
362
386
  version: '0'
363
387
  requirements: []
364
- rubygems_version: 3.6.2
388
+ rubygems_version: 3.5.22
389
+ signing_key:
365
390
  specification_version: 4
366
- summary: The Ruby LLM client library - unified interface for OpenAI, Anthropic, and
367
- other LLM providers
391
+ summary: Clean Ruby interface to modern AI language models
368
392
  test_files: []
@@ -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
@@ -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