ox-ai-workers 0.5.8 → 0.6.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: 722b00ed63bf9ef2340a61bb1465192a50377ad0a572f1b2765037132c5de18f
4
- data.tar.gz: afb338f8edc2e2621a8f559bc50f9b5f8c29f3f25e9fe5b8bcac95eae95fe988
3
+ metadata.gz: 811df931f990a04c2764d1e5dc952e69e1dfc409bfe942ff87822ef8b755a6c9
4
+ data.tar.gz: 1d591cc02bec476f76707de96acadd58bb0cda60a1af0bf6657150e9360e02e0
5
5
  SHA512:
6
- metadata.gz: d02f038e7e84323debec83b19323a5a91d34b7f4145e709cdb9462ecdfa471aad224bb5c9b489d7803fff94a14bf10eb17d29e9c0862cba6785c5a1a46e006a2
7
- data.tar.gz: a34bee96ff8cd78a93c1a2da63150dd48a266945a3b9ea891840a3354d6e104aae37c3a659d7327709f544975249b24e377b5f23137600f5f0eb242d27c6d865
6
+ metadata.gz: e4629630ea656bb07ab2da027852ebc262901e7dfb09d27b73071727eed149c850a3b4f40c03e094792c630f32642f9dc2c8c5a6786b5283e4a03916b8c47f8b
7
+ data.tar.gz: c57849c113e05f41fad7a51ab17b86878a27b61b4f7abff08dbac478114efe2ba697fce94ebfff23551931e982d07ae7e2d808d54adb3f28424bc29381b574d7
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.4
data/CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
- ## [Unreleased]
1
+ ## [0.6.2] - 2024-08-024
2
+
3
+ - Rails ActiveRecord support
4
+
5
+ ## [0.6.0] - 2024-08-02
6
+
7
+ - Added rails compatibility
8
+ - Added `wait_for_complete` option
2
9
 
3
10
  ## [0.5.8] - 2024-08-02
4
11
 
data/README.md CHANGED
@@ -86,9 +86,10 @@ For a more robust setup, you can configure the gem with your API keys, for examp
86
86
  OxAiWorkers.configure do |config|
87
87
  config.access_token = ENV.fetch("OPENAI")
88
88
  config.model = "gpt-4o"
89
- config.max_tokens = 4096 # Default
90
- config.temperature = 0.7 # Default
89
+ config.max_tokens = 4096 # Default
90
+ config.temperature = 0.7 # Default
91
91
  config.auto_execute = true # Default
92
+ config.wait_for_complete = true # Default
92
93
  end
93
94
  ```
94
95
 
@@ -178,6 +179,53 @@ As a worker, you can use different classes depending on your needs:
178
179
 
179
180
  - `OxAiWorkers::DelayedRequest`: This class is used for batch API requests, ideal for operations that do not require immediate execution. Using `DelayedRequest` can save up to 50% on costs as requests are executed when the remote server is less busy, but no later than within 24 hours.
180
181
 
182
+ ### Rails Projects with DelayedRequest
183
+
184
+ Generate your model to store the `batch_id` in the database:
185
+
186
+ ```bash
187
+ rails generate model MyRequestWithStore batch_id:string
188
+ ```
189
+
190
+ In your `app/models/my_request_with_store.rb` file, add the following code:
191
+
192
+ ```ruby
193
+ class MyRequestWithStore < ApplicationRecord
194
+ def delayed_request
195
+ @worker ||= OxAiWorkers::DelayedRequest.new(batch_id: self.batch_id)
196
+ end
197
+ end
198
+ ```
199
+
200
+ Then you can use the iterator like this:
201
+
202
+ ```ruby
203
+ # Fetch the first stored batch
204
+ my_store = MyRequestWithStore.first
205
+
206
+ # Get the worker
207
+ my_worker = my_store.delayed_request
208
+
209
+ # Create the iterator
210
+ iterator = OxAiWorkers::Iterator.new(worker: my_worker)
211
+ # ... use the iterator
212
+
213
+ # Destroy the store after completion
214
+ my_store.destroy if my_worker.completed?
215
+ ```
216
+
217
+ To store your batches in the database, use the following code:
218
+
219
+ ```ruby
220
+ # Get the worker from the iterator
221
+ my_worker = iterator.worker
222
+
223
+ # Store the batch_id if it's not completed
224
+ unless my_worker.completed?
225
+ my_store = MyRequestWithStore.create!(batch_id: my_worker.batch_id)
226
+ end
227
+ ```
228
+
181
229
  ## Command Line Interface (CLI)
182
230
 
183
231
  1. Navigate to the required directory.
data/lib/ox-ai-workers.rb CHANGED
@@ -31,6 +31,8 @@ require_relative 'oxaiworkers/assistant/sysop'
31
31
  require_relative 'oxaiworkers/assistant/coder'
32
32
  require_relative 'oxaiworkers/assistant/localizer'
33
33
 
34
+ require_relative 'oxaiworkers/engine' if defined?(Rails)
35
+
34
36
  module OxAiWorkers
35
37
  DEFAULT_MODEL = 'gpt-4o-mini'
36
38
  DEFAULT_MAX_TOKEN = 4096
@@ -40,7 +42,7 @@ module OxAiWorkers
40
42
  class ConfigurationError < Error; end
41
43
 
42
44
  class Configuration
43
- attr_accessor :model, :max_tokens, :temperature, :access_token, :auto_execute
45
+ attr_accessor :model, :max_tokens, :temperature, :access_token, :auto_execute, :wait_for_complete
44
46
 
45
47
  def initialize
46
48
  @access_token = nil
@@ -48,6 +50,7 @@ module OxAiWorkers
48
50
  @max_tokens = DEFAULT_MAX_TOKEN
49
51
  @temperature = DEFAULT_TEMPERATURE
50
52
  @auto_execute = true
53
+ @wait_for_complete = true
51
54
 
52
55
  [Array, NilClass, String, Symbol, Hash].each do |c|
53
56
  c.send(:include, OxAiWorkers::PresentCompat) unless c.method_defined?(:present?)
@@ -3,7 +3,7 @@
3
3
  module OxAiWorkers
4
4
  class DelayedRequest < OxAiWorkers::StateBatch
5
5
  def initialize(batch_id: nil, model: nil, max_tokens: nil, temperature: nil)
6
- initialize_requests(model: model, max_tokens: max_tokens, temperature: temperature)
6
+ initialize_requests(model:, max_tokens:, temperature:)
7
7
  @custom_id = nil if batch_id.present?
8
8
  @batch_id = batch_id
9
9
  @file_id = nil
@@ -0,0 +1,11 @@
1
+ module OxAiWorkers
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace OxAiWorkers # this is generally recommended
4
+
5
+ # no other special configuration needed. But maybe you want
6
+ # an initializer to be added to the app? Easy!
7
+ # initializer 'yourgem.boot_stuff_up' do
8
+ # OxAiWorkers.boot_something_up!
9
+ # end
10
+ end
11
+ end
@@ -52,8 +52,15 @@ module OxAiWorkers
52
52
  cleanup
53
53
 
54
54
  super()
55
+
56
+ tick_or_wait if requested?
55
57
  end
56
58
 
59
+ #
60
+ # Resets the state of the object by setting all instance variables to their initial values.
61
+ #
62
+ # Returns nothing.
63
+ #
57
64
  def cleanup
58
65
  @result = nil
59
66
  @queue = []
@@ -63,6 +70,11 @@ module OxAiWorkers
63
70
  complete_iteration
64
71
  end
65
72
 
73
+ # Updates the internal state of the iterator by adding the given `speach` to the `@queue` and calling the `@on_inner_monologue` callback with the `speach` text.
74
+ #
75
+ # @param speach [String] The text to be added to the `@queue` and passed to the `@on_inner_monologue` callback.
76
+ #
77
+ # @return [nil] This method does not return a value.
66
78
  def inner_monologue(speach:)
67
79
  # @queue.pop
68
80
  @queue << { role: :assistant, content: speach.to_s }
@@ -74,7 +86,7 @@ module OxAiWorkers
74
86
  # @queue.pop
75
87
  @queue << { role: :assistant, content: text.to_s }
76
88
  complete! unless available_defs.include?(:action_request)
77
- @on_outer_voice&.call(text: text)
89
+ @on_outer_voice&.call(text:)
78
90
  nil
79
91
  end
80
92
 
@@ -96,7 +108,7 @@ module OxAiWorkers
96
108
  @worker.finish
97
109
  rebuild_worker
98
110
  complete! if can_complete?
99
- @on_summarize&.call(text: text)
111
+ @on_summarize&.call(text:)
100
112
  nil
101
113
  end
102
114
 
@@ -158,11 +170,28 @@ module OxAiWorkers
158
170
 
159
171
  def external_request
160
172
  @worker.request!
161
- ticker
173
+ tick_or_wait
174
+ end
175
+
176
+ def tick_or_wait
177
+ if OxAiWorkers.configuration.wait_for_complete
178
+ wait_for_complete
179
+ else
180
+ ticker
181
+ end
162
182
  end
163
183
 
164
184
  def ticker
165
- sleep(60) until @worker.completed?
185
+ return false unless @worker.completed?
186
+
187
+ analyze!
188
+ true
189
+ end
190
+
191
+ def wait_for_complete
192
+ return unless requested?
193
+
194
+ sleep(60) unless ticker
166
195
  analyze!
167
196
  end
168
197
 
@@ -199,7 +228,7 @@ module OxAiWorkers
199
228
  end
200
229
 
201
230
  def add_context(text, role: :system)
202
- @context << { role: role, content: text }
231
+ @context << { role:, content: text }
203
232
  end
204
233
 
205
234
  def execute
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n'
4
- I18n.load_path += Dir["#{File.expand_path('../../locales', __dir__)}/*.yml"]
3
+ unless defined?(Rails)
4
+ require 'i18n'
5
+ I18n.load_path += Dir["#{File.expand_path('../../config/locales', __dir__)}/*.yml"]
6
+ end
5
7
 
6
8
  module OxAiWorkers
7
9
  module LoadI18n
@@ -7,7 +7,7 @@ module OxAiWorkers
7
7
  include OxAiWorkers::StateHelper
8
8
  extend StateMachines::MacroMethods
9
9
 
10
- state_machine :state, initial: :idle do
10
+ state_machine :state, initial: ->(t) { t.worker.requested? ? :requested : :idle } do
11
11
  before_transition from: any, do: :log_me
12
12
 
13
13
  after_transition on: :iterate, do: :next_iteration
@@ -0,0 +1,35 @@
1
+ # def getDocument doc1, &block
2
+ # document = getFileBin(doc1)
3
+ # content = nil
4
+ # unless document.nil?
5
+ # extension = doc1['file_name'].split('.').last.downcase
6
+ # if extension == "docx"
7
+ # docx = Docx::Document.open document#.read
8
+ # content = docx.instance_variable_get("@doc").xpath('//w:document//w:body').children.map{ |c|
9
+ # if c.name == 'p' # paragraph
10
+ # paragraph = docx.send(:parse_paragraph_from, c)
11
+ # ReverseMarkdown.convert(paragraph.to_html).strip
12
+ # elsif c.name = 'tbl' # table
13
+ # table = docx.send(:parse_table_from, c)
14
+ # table.rows.map { |row| row.cells.map { |cell| cell.paragraphs.map(&:text).reject { |c| c.empty? }.join("\\n ") }.join(" | ") }.join("\n")
15
+ # else # other types?
16
+ # c.content
17
+ # end
18
+ # }.reject { |c| c.empty? }.join("\n\n")
19
+ # #content = ReverseMarkdown.convert(docx.to_html)
20
+ # makeTempFileWithContent(content, ["file_#{@current_user.id}", ".txt"]) do |file|
21
+ # yield(content, doc1['file_name'], file)
22
+ # end
23
+ # elsif %w[xlsx xls ods].include?(extension)
24
+ # xlsx = Roo::Spreadsheet.open document, extension: extension.to_sym
25
+ # content = xlsx.to_csv
26
+ # makeTempFileWithContent(content, ["file_#{@current_user.id}", ".csv"]) do |file|
27
+ # yield(content, doc1['file_name'], file)
28
+ # end
29
+ # elsif %w[csv txt ini asc log kicad_sch cpp h rb c hpp erb md yml].include?(extension)
30
+ # yield(document.read.force_encoding("UTF-8"), doc1['file_name'], nil)
31
+ # else
32
+ # yield(nil, doc1['file_name'], nil)
33
+ # end
34
+ # end
35
+ # end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
3
+ require 'json'
4
4
 
5
5
  #
6
6
  # Extends a class to be used as a tool in the assistant.
@@ -36,201 +36,220 @@ require "json"
36
36
  # end
37
37
  # end
38
38
  #
39
- module OxAiWorkers::ToolDefinition
40
- attr_accessor :white_list
39
+ module OxAiWorkers
40
+ module ToolDefinition
41
+ attr_accessor :white_list
41
42
 
42
- def init_white_list_with only
43
- @white_list = only.is_a?(Array) ? only : [only]
44
- end
45
-
46
- # Defines a function for the tool
47
- #
48
- # @param method_name [Symbol] Name of the method to define
49
- # @param description [String] Description of the function
50
- # @yield Block that defines the parameters for the function
51
- def define_function(method_name, description:, &)
52
- if @white_list.nil? || @white_list == method_name || @white_list.include?(method_name)
53
- function_schemas.add_function(method_name:, description:, &)
54
- end
55
- end
56
-
57
- # Returns the FunctionSchemas instance for this tool
58
- #
59
- # @return [FunctionSchemas] The FunctionSchemas instance
60
- def function_schemas
61
- @function_schemas ||= FunctionSchemas.new(tool_name)
62
- end
63
-
64
- # Returns the snake_case version of the class name as the tool's name
65
- #
66
- # @return [String] The snake_case version of the class name
67
- def tool_name
68
- @tool_name ||= (self.respond_to?(:name) ? name : self.class.name)
69
- .gsub("::", "_")
70
- .gsub(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
71
- .downcase
72
- end
73
-
74
- def full_function_name(fun)
75
- function_schemas.function_name(fun)
76
- end
77
-
78
- # Manages schemas for functions
79
- class FunctionSchemas
80
- def initialize(tool_name)
81
- @schemas = {}
82
- @tool_name = tool_name
83
- end
84
-
85
- def function_name method_name
86
- "#{@tool_name}__#{method_name}"
43
+ # Initializes the white list with the given `only` parameter.
44
+ #
45
+ # @param only [Object, Array] The object or array to initialize the white list with.
46
+ # @return [Array] The initialized white list.
47
+ def init_white_list_with(only)
48
+ @white_list = only.is_a?(Array) ? only : [only]
87
49
  end
88
50
 
89
- # Adds a function to the schemas
51
+ # Defines a function for the tool
90
52
  #
91
- # @param method_name [Symbol] Name of the method to add
53
+ # @param method_name [Symbol] Name of the method to define
92
54
  # @param description [String] Description of the function
93
55
  # @yield Block that defines the parameters for the function
94
- # @raise [ArgumentError] If a block is defined and no parameters are specified for the function
95
- def add_function(method_name:, description:, &)
96
- name = function_name(method_name)
56
+ def define_function(method_name, description:, &)
57
+ return unless @white_list.nil? || @white_list == method_name || @white_list.include?(method_name)
97
58
 
98
- if block_given?
99
- parameters = ParameterBuilder.new(parent_type: "object").build(&)
59
+ function_schemas.add_function(method_name:, description:, &)
60
+ end
100
61
 
101
- if parameters[:properties].empty?
102
- raise ArgumentError, "Function parameters must have at least one property defined within it, if a block is provided"
103
- end
104
- end
62
+ # Returns the FunctionSchemas instance for this tool
63
+ #
64
+ # @return [FunctionSchemas] The FunctionSchemas instance
65
+ def function_schemas
66
+ @function_schemas ||= FunctionSchemas.new(tool_name)
67
+ end
105
68
 
106
- @schemas[method_name] = {
107
- type: "function",
108
- function: {name:, description:, parameters:}.compact
109
- }
69
+ # Returns the snake_case version of the class name as the tool's name
70
+ #
71
+ # @return [String] The snake_case version of the class name
72
+ def tool_name
73
+ @tool_name ||= (respond_to?(:name) ? name : self.class.name)
74
+ .gsub('::', '_')
75
+ .gsub(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, '_')
76
+ .downcase
110
77
  end
111
78
 
112
- # Converts schemas to OpenAI-compatible format
79
+ # Returns the full function name for the given function.
113
80
  #
114
- # @return [String] JSON string of schemas in OpenAI format
115
- def to_openai_format(only: nil)
116
- valid_schemas(only: only).values
81
+ # @param fun [Symbol] The function name.
82
+ # @return [String] The full function name, which is the tool name concatenated with the function name.
83
+ def full_function_name(fun)
84
+ function_schemas.function_name(fun)
117
85
  end
118
86
 
119
- def valid_schemas(only: nil)
120
- if only.nil?
121
- @schemas
122
- else
123
- @schemas.select { |name, schema| only.include?(name) }
87
+ # Manages schemas for functions
88
+ class FunctionSchemas
89
+ def initialize(tool_name)
90
+ @schemas = {}
91
+ @tool_name = tool_name
124
92
  end
125
- end
126
93
 
127
- # Converts schemas to Anthropic-compatible format
128
- #
129
- # @return [String] JSON string of schemas in Anthropic format
130
- def to_anthropic_format(only: nil)
131
- valid_schemas(only: only).values.map do |schema|
132
- schema[:function].transform_keys("parameters" => "input_schema")
94
+ # Returns the full function name for the given method name.
95
+ #
96
+ # @param method_name [Symbol] The name of the method.
97
+ # @return [String] The full function name, which is the tool name concatenated with the method name.
98
+ def function_name(method_name)
99
+ "#{@tool_name}__#{method_name}"
133
100
  end
134
- end
135
101
 
136
- # Converts schemas to Google Gemini-compatible format
137
- #
138
- # @return [String] JSON string of schemas in Google Gemini format
139
- def to_google_gemini_format(only: nil)
140
- valid_schemas(only: only).values.map { |schema| schema[:function] }
141
- end
142
- end
102
+ # Adds a function to the schemas
103
+ #
104
+ # @param method_name [Symbol] Name of the method to add
105
+ # @param description [String] Description of the function
106
+ # @yield Block that defines the parameters for the function
107
+ # @raise [ArgumentError] If a block is defined and no parameters are specified for the function
108
+ def add_function(method_name:, description:, &)
109
+ name = function_name(method_name)
110
+
111
+ if block_given?
112
+ parameters = ParameterBuilder.new(parent_type: 'object').build(&)
113
+
114
+ if parameters[:properties].empty?
115
+ raise ArgumentError,
116
+ 'Function parameters must have at least one property defined within it, if a block is provided'
117
+ end
118
+ end
143
119
 
144
- # Builds parameter schemas for functions
145
- class ParameterBuilder
146
- VALID_TYPES = %w[object array string number integer boolean].freeze
120
+ @schemas[method_name] = {
121
+ type: 'function',
122
+ function: { name:, description:, parameters: }.compact
123
+ }
124
+ end
147
125
 
148
- def initialize(parent_type:)
149
- @schema = (parent_type == "object") ? {type: "object", properties: {}, required: []} : {}
150
- @parent_type = parent_type
151
- end
126
+ # Converts schemas to OpenAI-compatible format
127
+ #
128
+ # @return [String] JSON string of schemas in OpenAI format
129
+ def to_openai_format(only: nil)
130
+ valid_schemas(only:).values
131
+ end
152
132
 
153
- # Builds the parameter schema
154
- #
155
- # @yield Block that defines the properties of the schema
156
- # @return [Hash] The built schema
157
- def build(&)
158
- instance_eval(&)
159
- @schema
160
- end
133
+ # Returns a subset of schemas based on the provided filter.
134
+ #
135
+ # @param only [Array<Symbol>] An optional array of schema names to filter by.
136
+ # @return [Hash<Symbol, Hash>] A hash of schemas with their corresponding names as keys.
137
+ def valid_schemas(only: nil)
138
+ if only.nil?
139
+ @schemas
140
+ else
141
+ @schemas.select { |name, _schema| only.include?(name) }
142
+ end
143
+ end
161
144
 
162
- # Defines a property in the schema
163
- #
164
- # @param name [Symbol] Name of the property (required only for a parent of type object)
165
- # @param type [String] Type of the property
166
- # @param description [String] Description of the property
167
- # @param enum [Array] Array of allowed values
168
- # @param required [Boolean] Whether the property is required
169
- # @yield [Block] Block for nested properties (only for object and array types)
170
- # @raise [ArgumentError] If any parameter is invalid
171
- def property(name = nil, type:, description: nil, enum: nil, required: false, &)
172
- validate_parameters(name:, type:, enum:, required:)
173
-
174
- prop = {type:, description:, enum:}.compact
175
-
176
- if block_given?
177
- nested_schema = ParameterBuilder.new(parent_type: type).build(&)
178
-
179
- case type
180
- when "object"
181
- if nested_schema[:properties].empty?
182
- raise ArgumentError, "Object properties must have at least one property defined within it"
183
- end
184
- prop = nested_schema
185
- when "array"
186
- if nested_schema.empty?
187
- raise ArgumentError, "Array properties must have at least one item defined within it"
188
- end
189
- prop[:items] = nested_schema
145
+ # Converts schemas to Anthropic-compatible format
146
+ #
147
+ # @return [String] JSON string of schemas in Anthropic format
148
+ def to_anthropic_format(only: nil)
149
+ valid_schemas(only:).values.map do |schema|
150
+ schema[:function].transform_keys('parameters' => 'input_schema')
190
151
  end
191
152
  end
192
153
 
193
- if @parent_type == "object"
194
- @schema[:properties][name] = prop
195
- @schema[:required] << name.to_s if required
196
- else
197
- @schema = prop
154
+ # Converts schemas to Google Gemini-compatible format
155
+ #
156
+ # @return [String] JSON string of schemas in Google Gemini format
157
+ def to_google_gemini_format(only: nil)
158
+ valid_schemas(only:).values.map { |schema| schema[:function] }
198
159
  end
199
160
  end
200
161
 
201
- # Alias for property method, used for defining array items
202
- alias_method :item, :property
162
+ # Builds parameter schemas for functions
163
+ class ParameterBuilder
164
+ VALID_TYPES = %w[object array string number integer boolean].freeze
203
165
 
204
- private
166
+ def initialize(parent_type:)
167
+ @schema = parent_type == 'object' ? { type: 'object', properties: {}, required: [] } : {}
168
+ @parent_type = parent_type
169
+ end
205
170
 
206
- # Validates the parameters for a property
207
- #
208
- # @param name [Symbol] Name of the property
209
- # @param type [String] Type of the property
210
- # @param enum [Array] Array of allowed values
211
- # @param required [Boolean] Whether the property is required
212
- # @raise [ArgumentError] If any parameter is invalid
213
- def validate_parameters(name:, type:, enum:, required:)
214
- if @parent_type == "object"
215
- if name.nil?
216
- raise ArgumentError, "Name must be provided for properties of an object"
171
+ # Builds the parameter schema
172
+ #
173
+ # @yield Block that defines the properties of the schema
174
+ # @return [Hash] The built schema
175
+ def build(&)
176
+ instance_eval(&)
177
+ @schema
178
+ end
179
+
180
+ # Defines a property in the schema
181
+ #
182
+ # @param name [Symbol] Name of the property (required only for a parent of type object)
183
+ # @param type [String] Type of the property
184
+ # @param description [String] Description of the property
185
+ # @param enum [Array] Array of allowed values
186
+ # @param required [Boolean] Whether the property is required
187
+ # @yield [Block] Block for nested properties (only for object and array types)
188
+ # @raise [ArgumentError] If any parameter is invalid
189
+ def property(name = nil, type:, description: nil, enum: nil, required: false, &)
190
+ validate_parameters(name:, type:, enum:, required:)
191
+
192
+ prop = { type:, description:, enum: }.compact
193
+
194
+ if block_given?
195
+ nested_schema = ParameterBuilder.new(parent_type: type).build(&)
196
+
197
+ case type
198
+ when 'object'
199
+ if nested_schema[:properties].empty?
200
+ raise ArgumentError, 'Object properties must have at least one property defined within it'
201
+ end
202
+
203
+ prop = nested_schema
204
+ when 'array'
205
+ if nested_schema.empty?
206
+ raise ArgumentError,
207
+ 'Array properties must have at least one item defined within it'
208
+ end
209
+
210
+ prop[:items] = nested_schema
211
+ end
217
212
  end
218
- unless name.is_a?(Symbol)
219
- raise ArgumentError, "Invalid name '#{name}'. Name must be a symbol"
213
+
214
+ if @parent_type == 'object'
215
+ @schema[:properties][name] = prop
216
+ @schema[:required] << name.to_s if required
217
+ else
218
+ @schema = prop
220
219
  end
221
220
  end
222
221
 
223
- unless VALID_TYPES.include?(type)
224
- raise ArgumentError, "Invalid type '#{type}'. Valid types are: #{VALID_TYPES.join(", ")}"
225
- end
222
+ # Alias for property method, used for defining array items
223
+ alias item property
224
+
225
+ private
226
+
227
+ # Validates the parameters for a property
228
+ #
229
+ # @param name [Symbol] Name of the property
230
+ # @param type [String] Type of the property
231
+ # @param enum [Array] Array of allowed values
232
+ # @param required [Boolean] Whether the property is required
233
+ # @raise [ArgumentError] If any parameter is invalid
234
+ def validate_parameters(name:, type:, enum:, required:)
235
+ if @parent_type == 'object'
236
+ raise ArgumentError, 'Name must be provided for properties of an object' if name.nil?
237
+ raise ArgumentError, "Invalid name '#{name}'. Name must be a symbol" unless name.is_a?(Symbol)
238
+ end
226
239
 
227
- unless enum.nil? || enum.is_a?(Array)
228
- raise ArgumentError, "Invalid enum '#{enum}'. Enum must be nil or an array"
229
- end
240
+ unless VALID_TYPES.include?(type)
241
+ raise ArgumentError, "Invalid type '#{type}'. Valid types are: #{VALID_TYPES.join(', ')}"
242
+ end
243
+
244
+ unless enum.nil? || enum.is_a?(Array)
245
+ raise ArgumentError,
246
+ "Invalid enum '#{enum}'. Enum must be nil or an array"
247
+ end
248
+
249
+ return if [true, false].include?(required)
230
250
 
231
- unless [true, false].include?(required)
232
251
  raise ArgumentError, "Invalid required '#{required}'. Required must be a boolean"
233
252
  end
234
253
  end
235
254
  end
236
- end
255
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OxAiWorkers
4
- VERSION = '0.5.8'
4
+ VERSION = '0.6.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ox-ai-workers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.8
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Smolev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-05 00:00:00.000000000 Z
11
+ date: 2024-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -124,11 +124,18 @@ executables:
124
124
  extensions: []
125
125
  extra_rdoc_files: []
126
126
  files:
127
+ - ".ruby-version"
127
128
  - CHANGELOG.md
128
129
  - CODE_OF_CONDUCT.md
129
130
  - LICENSE
130
131
  - README.md
131
132
  - Rakefile
133
+ - config/locales/en.oxaiworkers.assistant.yml
134
+ - config/locales/en.oxaiworkers.iterator.yml
135
+ - config/locales/en.oxaiworkers.tool.yml
136
+ - config/locales/ru.oxaiworkers.assistant.yml
137
+ - config/locales/ru.oxaiworkers.iterator.yml
138
+ - config/locales/ru.oxaiworkers.tool.yml
132
139
  - exe/oxaiworkers
133
140
  - exe/start
134
141
  - lib/ox-ai-workers.rb
@@ -140,6 +147,7 @@ files:
140
147
  - lib/oxaiworkers/contextual_logger.rb
141
148
  - lib/oxaiworkers/delayed_request.rb
142
149
  - lib/oxaiworkers/dependency_helper.rb
150
+ - lib/oxaiworkers/engine.rb
143
151
  - lib/oxaiworkers/iterator.rb
144
152
  - lib/oxaiworkers/load_i18n.rb
145
153
  - lib/oxaiworkers/module_request.rb
@@ -148,18 +156,13 @@ files:
148
156
  - lib/oxaiworkers/state_batch.rb
149
157
  - lib/oxaiworkers/state_helper.rb
150
158
  - lib/oxaiworkers/state_tools.rb
159
+ - lib/oxaiworkers/tool/converter.rb
151
160
  - lib/oxaiworkers/tool/database.rb
152
161
  - lib/oxaiworkers/tool/eval.rb
153
162
  - lib/oxaiworkers/tool/file_system.rb
154
163
  - lib/oxaiworkers/tool_definition.rb
155
164
  - lib/oxaiworkers/version.rb
156
165
  - lib/ruby/ox-ai-workers.rb
157
- - locales/en.assistant.yml
158
- - locales/en.iterator.yml
159
- - locales/en.tool.yml
160
- - locales/ru.assistant.yml
161
- - locales/ru.iterator.yml
162
- - locales/ru.tool.yml
163
166
  - template/my_assistant.rb
164
167
  - template/start
165
168
  - template/tools/my_tool.rb