last_llm 0.0.4

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.
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LastLLM
4
+ # Error raised when tool parameter validation fails
5
+ class ToolValidationError < ValidationError; end
6
+
7
+ # Tool class for defining callable functions that can be used with LLM providers
8
+ class Tool
9
+ attr_reader :name, :description, :parameters, :function
10
+
11
+ # Initialize a new tool
12
+ # @param name [String] The name of the tool
13
+ # @param description [String] A description of what the tool does
14
+ # @param parameters [Hash] JSON Schema for the tool parameters
15
+ # @param function [Proc] The function to execute when the tool is called
16
+ def initialize(name:, description:, parameters:, function:)
17
+ validate_initialization_params(name, description, parameters, function)
18
+
19
+ @name = name
20
+ @description = description
21
+ @parameters = parameters
22
+ @function = function
23
+ end
24
+
25
+ # Call the tool with the provided parameters
26
+ # @param params [Hash] Parameters to pass to the tool function
27
+ # @return [Hash] The result of the function call
28
+ def call(params)
29
+ # Convert string keys to symbols
30
+ params = symbolize_keys(params)
31
+
32
+ # Validate parameters against the schema
33
+ validate_parameters(params)
34
+
35
+ # Convert parameter types if needed
36
+ converted_params = convert_parameter_types(params)
37
+
38
+ # Execute the function
39
+ function.call(converted_params)
40
+ end
41
+
42
+ private
43
+
44
+ def validate_initialization_params(name, description, parameters, function)
45
+ missing_params = []
46
+ missing_params << 'name' unless name.is_a?(String) && !name.empty?
47
+ missing_params << 'description' unless description.is_a?(String) && !description.empty?
48
+ missing_params << 'parameters' unless parameters.is_a?(Hash) && !parameters.empty?
49
+ missing_params << 'function' unless function.respond_to?(:call)
50
+
51
+ return if missing_params.empty?
52
+
53
+ raise ArgumentError, "Missing or invalid required attributes: #{missing_params.join(', ')}"
54
+ end
55
+
56
+ def validate_parameters(params)
57
+ # Basic validation for required parameters
58
+ if parameters[:required].is_a?(Array)
59
+ parameters[:required].each do |required_param|
60
+ unless params.key?(required_param.to_sym)
61
+ raise ToolValidationError, "Missing required parameter: #{required_param}"
62
+ end
63
+ end
64
+ end
65
+
66
+ # Validate enum values if present
67
+ return unless parameters[:properties].is_a?(Hash)
68
+
69
+ parameters[:properties].each do |prop_name, prop_schema|
70
+ next unless params.key?(prop_name.to_sym) && prop_schema[:enum].is_a?(Array)
71
+
72
+ param_value = params[prop_name.to_sym]
73
+ unless prop_schema[:enum].include?(param_value)
74
+ raise ToolValidationError, "Invalid value for #{prop_name}: #{param_value}. " \
75
+ "Allowed values: #{prop_schema[:enum].join(', ')}"
76
+ end
77
+ end
78
+ end
79
+
80
+ def convert_parameter_types(params)
81
+ converted_params = params.dup
82
+
83
+ parameters[:properties].each do |prop_name, prop_schema|
84
+ next unless params.key?(prop_name.to_sym)
85
+
86
+ param_value = params[prop_name.to_sym]
87
+ prop_name_sym = prop_name.to_sym
88
+
89
+ case prop_schema[:type]
90
+ when 'number', 'integer'
91
+ converted_params[prop_name_sym] = param_value.to_f if param_value.is_a?(String)
92
+ if prop_schema[:type] == 'integer' && param_value.is_a?(Float)
93
+ converted_params[prop_name_sym] =
94
+ param_value.to_i
95
+ end
96
+ when 'boolean'
97
+ converted_params[prop_name_sym] = (param_value.downcase == 'true') if param_value.is_a?(String)
98
+ end
99
+ end
100
+
101
+ converted_params
102
+ end
103
+
104
+ def symbolize_keys(hash)
105
+ return hash unless hash.is_a?(Hash)
106
+
107
+ hash.transform_keys(&:to_sym)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LastLLM
4
+ VERSION = '0.0.4'
5
+ end
data/lib/last_llm.rb ADDED
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-schema'
4
+ require 'faraday'
5
+ require 'json'
6
+ require 'last_llm/extensions/dry_schema_extensions'
7
+
8
+ # Main module for LastLLM
9
+ module LastLLM
10
+ # Base error class for all LastLLM errors
11
+ class Error < StandardError; end
12
+
13
+ # Error raised when configuration is invalid
14
+ class ConfigurationError < Error; end
15
+
16
+ # Error raised when validation fails
17
+ class ValidationError < Error; end
18
+
19
+ # Error raised when API request fails
20
+ class ApiError < Error
21
+ attr_reader :status
22
+
23
+ def initialize(message, status = nil)
24
+ @status = status
25
+ super(message)
26
+ end
27
+ end
28
+
29
+ # Autoload all required components
30
+ autoload :Configuration, 'last_llm/configuration'
31
+ autoload :Client, 'last_llm/client'
32
+ autoload :Provider, 'last_llm/provider'
33
+ autoload :Schema, 'last_llm/schema'
34
+ autoload :StructuredOutput, 'last_llm/structured_output'
35
+ autoload :Tool, 'last_llm/tool'
36
+ autoload :Railtie, 'last_llm/railtie'
37
+ autoload :Completion, 'last_llm/completion'
38
+ autoload :VERSION, 'last_llm/version'
39
+
40
+ # Provider implementations
41
+ module Providers
42
+ autoload :OpenAI, 'last_llm/providers/openai'
43
+ autoload :Anthropic, 'last_llm/providers/anthropic'
44
+ autoload :GoogleGemini, 'last_llm/providers/google_gemini'
45
+ autoload :Deepseek, 'last_llm/providers/deepseek'
46
+ autoload :Ollama, 'last_llm/providers/ollama'
47
+ end
48
+
49
+ class << self
50
+ # Configure the LastLLM client
51
+ # @yield [config] Configuration instance
52
+ # @return [Configuration] The updated configuration
53
+ def configure
54
+ self.configuration ||= Configuration.new
55
+ yield(configuration) if block_given?
56
+ configuration
57
+ end
58
+
59
+ # Get the current configuration or create a new one
60
+ # @return [Configuration] The current configuration
61
+ def configuration
62
+ @configuration ||= Configuration.new
63
+ end
64
+
65
+ # Create a new client with the current configuration
66
+ # @param options [Hash] Additional options for the client
67
+ # @return [Client] A new client instance
68
+ def client(options = {})
69
+ Client.new(configuration, options)
70
+ end
71
+
72
+ # Reset the configuration to defaults
73
+ # @return [Configuration] A new default configuration
74
+ def reset_configuration!
75
+ @configuration = Configuration.new
76
+ end
77
+
78
+ # Add Rails integration helper
79
+ def setup_rails!
80
+ return unless defined?(Rails)
81
+
82
+ require 'last_llm/railtie'
83
+ end
84
+ end
85
+ end
86
+
87
+ # Rails integration
88
+ LastLLM.setup_rails! if defined?(Rails)
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: last_llm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Sam Obukwelu
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-03-04 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: dry-schema
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.6'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.6'
26
+ - !ruby/object:Gem::Dependency
27
+ name: faraday
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: json
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.12'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.12'
68
+ - !ruby/object:Gem::Dependency
69
+ name: vcr
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '6.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '6.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: webmock
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.18'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.18'
96
+ description: A unified client for interacting with various LLM providers
97
+ email:
98
+ - sam@obukwelu.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - README.md
104
+ - lib/last_llm.rb
105
+ - lib/last_llm/client.rb
106
+ - lib/last_llm/completion.rb
107
+ - lib/last_llm/configuration.rb
108
+ - lib/last_llm/extensions/dry_schema_extensions.rb
109
+ - lib/last_llm/provider.rb
110
+ - lib/last_llm/providers/anthropic.rb
111
+ - lib/last_llm/providers/constants.rb
112
+ - lib/last_llm/providers/deepseek.rb
113
+ - lib/last_llm/providers/google_gemini.rb
114
+ - lib/last_llm/providers/ollama.rb
115
+ - lib/last_llm/providers/openai.rb
116
+ - lib/last_llm/providers/test_provider.rb
117
+ - lib/last_llm/railtie.rb
118
+ - lib/last_llm/schema.rb
119
+ - lib/last_llm/structured_output.rb
120
+ - lib/last_llm/tool.rb
121
+ - lib/last_llm/version.rb
122
+ homepage: https://github.com/joemocha/last_llm
123
+ licenses:
124
+ - MIT
125
+ metadata:
126
+ rubygems_mfa_required: 'true'
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '3.0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubygems_version: 3.6.3
142
+ specification_version: 4
143
+ summary: Last LLM
144
+ test_files: []