ox-ai-workers 0.5.3.1 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34c154dd79d968e939b91f26e410e0f7a4e51c5099347b3577dc05708ab8b341
4
- data.tar.gz: 294f14527bb5ef82e885ee5c93e6577d93c1820f844baad1139fbd385cb895c2
3
+ metadata.gz: 67dfa0fc5c5cb3c3c223615ec1386324b1a71fdc38d90db9b78c6c94f0136add
4
+ data.tar.gz: 555689bc0e7d83a854f0614d13aed065b2634bdf4501a9aa2025fc40736db398
5
5
  SHA512:
6
- metadata.gz: 1f2c528093acd8ef4a6214ef65898807f72172f10e211096cbd490589d04e55220ff679c57cc04dd677ab04a211445fe4eb8cfefbc0beb1b453fcb7282dfabd0
7
- data.tar.gz: 14f317606790d54f1c735f437145da15243a86af05c28bfcaf63464929c841df1b4de02cc6326b94571ddb0334a3bd13298ffe78078e39e1c24e7314d386fea7
6
+ metadata.gz: 374c51ea659b98605131b46e1ffbffbfc3ec5d9d6c5718f64952e630e2a14adec56412776eadc4c074ead54c8197fc0195b9590dd76d0737dd7fff347b5ab236
7
+ data.tar.gz: 21714fc9c30d3159c756ce53840a598c163bca8547e7425e86ffd6ef86c78b5c3563c3a5b5d415420d3141c45d68c87f289d55436796bfa17cc83e4a4b963ab9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ - def_except and def_only for `Iterator`
4
+ - Renamed on_pack_history to on_summarize
5
+ - Renamed all "pack_history" to "summarize"
6
+
3
7
  ## [0.5.3] - 2024-07-31
4
8
 
5
9
  - Fixed summarize state
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
- [![Gem Version](https://badge.fury.io/rb/ox-ai-workers.svg)](https://rubygems.org/gems/ox-ai-workers)
2
-
3
1
  # OxAiWorkers (ox-ai-workers)
4
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/ox-ai-workers.svg)](https://rubygems.org/gems/ox-ai-workers)
4
+
5
5
  OxAiWorkers is a Ruby gem that implements a finite state machine (using the `state_machine` gem) to solve tasks using generative intelligence (with the `ruby-openai` gem). This approach enhances the final result by utilizing internal monologue and external tools.
6
6
 
7
7
  ## Installation
@@ -96,7 +96,7 @@ Then you can create an assistant like this:
96
96
 
97
97
  ```ruby
98
98
  assistant = OxAiWorkers::Assistant::Sysop.new()
99
- assistant.task = "your task"
99
+ assistant.task = "Remove all cron jobs."
100
100
 
101
101
  # Provide a response to the assistant's question
102
102
  assistant.add_response("blah-blah-blah")
@@ -115,7 +115,7 @@ iterator = OxAiWorkers::Iterator.new(
115
115
  on_inner_monologue: ->(text:) { puts "monologue: #{text}".colorize(:yellow) },
116
116
  on_outer_voice: ->(text:) { puts "voice: #{text}".colorize(:green) },
117
117
  on_action_request: ->(text:) { puts "action: #{text}".colorize(:red) },
118
- on_pack_history: ->(text:) { puts "summary: #{text}".colorize(:blue) }
118
+ on_summarize: ->(text:) { puts "summary: #{text}".colorize(:blue) }
119
119
  )
120
120
 
121
121
  iterator.add_task("Show files in current directory.")
@@ -130,9 +130,9 @@ This way, you have the flexibility to choose between a higher-level assistant fo
130
130
  ```ruby
131
131
  steps = []
132
132
  steps << 'Step 1. Develop your own solution to the problem, taking initiative and making assumptions.'
133
- steps << 'Step 2. Enclose all your developments from the previous step in the ox_ai_workers_iterator__inner_monologue function.'
133
+ steps << "Step 2. Enclose all your developments from the previous step in the #{OxAiWorkers::Iterator.full_function_name(:inner_monologue)} function."
134
134
  steps << 'Step 3. Call the necessary functions one after another until the desired result is achieved.'
135
- steps << 'Step 4. When all intermediate steps are completed and the exact content of previous messages is no longer relevant, use the ox_ai_workers_iterator__pack_history function.'
135
+ steps << "Step 4. When all intermediate steps are completed and the exact content of previous messages is no longer relevant, use the #{OxAiWorkers::Iterator.full_function_name(:summarize)} function."
136
136
  steps << "Step 5. When the solution is ready, notify about it and wait for the user's response."
137
137
 
138
138
  @iterator = OxAiWorkers::Iterator.new(
@@ -140,10 +140,12 @@ steps << "Step 5. When the solution is ready, notify about it and wait for the u
140
140
  role: 'You are a software agent inside my computer',
141
141
  tools: [MyTool.new],
142
142
  steps: steps,
143
+ # def_except: [:summarize], # It's except steps with that functions
144
+ # def_only: [:inner_monologue, :outer_voice], # Use it only with your steps
143
145
  on_inner_monologue: ->(text:) { puts "monologue: #{text}".colorize(:yellow) },
144
146
  on_outer_voice: ->(text:) { puts "voice: #{text}".colorize(:green) },
145
147
  on_action_request: ->(text:) { puts "action: #{text}".colorize(:red) },
146
- on_pack_history: ->(text:) { puts "summary: #{text}".colorize(:blue) }
148
+ on_summarize: ->(text:) { puts "summary: #{text}".colorize(:blue) }
147
149
  )
148
150
  ```
149
151
 
@@ -165,7 +167,7 @@ As a worker, you can use different classes depending on your needs:
165
167
  oxaiworkers init
166
168
  ```
167
169
 
168
- This will create a `.oxaiworkers-local` directory with the necessary initial source code.
170
+ This will create a `.oxaiworkers-local` directory with the necessary initial source code.
169
171
 
170
172
  Additionally, you can initialize a more comprehensive example using the command:
171
173
 
@@ -199,7 +201,7 @@ OxAiWorkers.logger.level = :debug
199
201
 
200
202
  ## Contributing
201
203
 
202
- Bug reports and pull requests are welcome on GitHub at https://github.com/neonix20b/ox-ai-workers. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/neonix20b/ox-ai-workers/blob/main/CODE_OF_CONDUCT.md).
204
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/neonix20b/ox-ai-workers>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/neonix20b/ox-ai-workers/blob/main/CODE_OF_CONDUCT.md).
203
205
 
204
206
  ## License
205
207
 
@@ -207,4 +209,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
207
209
 
208
210
  ## Code of Conduct
209
211
 
210
- Everyone interacting in the OxAiWorkers project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/neonix20b/ox-ai-workers/blob/main/CODE_OF_CONDUCT.md).
212
+ Everyone interacting in the OxAiWorkers project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/neonix20b/ox-ai-workers/blob/main/CODE_OF_CONDUCT.md).
@@ -13,7 +13,7 @@ module OxAiWorkers
13
13
  on_inner_monologue: ->(text:) { puts "monologue: #{text}".colorize(:yellow) },
14
14
  on_outer_voice: ->(text:) { puts "voice: #{text}".colorize(:green) },
15
15
  on_action_request: ->(text:) { puts "action: #{text}".colorize(:red) },
16
- on_pack_history: ->(text:) { puts "summary: #{text}".colorize(:blue) }
16
+ on_summarize: ->(text:) { puts "summary: #{text}".colorize(:blue) }
17
17
  )
18
18
  end
19
19
  end
@@ -13,7 +13,7 @@ module OxAiWorkers
13
13
  on_inner_monologue: ->(text:) { puts "monologue: #{text}".colorize(:yellow) },
14
14
  on_outer_voice: ->(text:) { puts "voice: #{text}".colorize(:green) },
15
15
  on_action_request: ->(text:) { puts "action: #{text}".colorize(:red) },
16
- on_pack_history: ->(text:) { puts "summary: #{text}".colorize(:blue) }
16
+ on_summarize: ->(text:) { puts "summary: #{text}".colorize(:blue) }
17
17
  )
18
18
  @iterator.add_context(format(I18n.t('oxaiworkers.assistant.localizer.source'), source))
19
19
 
@@ -13,7 +13,7 @@ module OxAiWorkers
13
13
  on_inner_monologue: ->(text:) { puts "monologue: #{text}".colorize(:yellow) },
14
14
  on_outer_voice: ->(text:) { puts "voice: #{text}".colorize(:green) },
15
15
  on_action_request: ->(text:) { puts "action: #{text}".colorize(:red) },
16
- on_pack_history: ->(text:) { puts "summary: #{text}".colorize(:blue) }
16
+ on_summarize: ->(text:) { puts "summary: #{text}".colorize(:blue) }
17
17
  )
18
18
  end
19
19
  end
@@ -2,9 +2,11 @@
2
2
 
3
3
  module OxAiWorkers
4
4
  class Iterator < OxAiWorkers::StateTools
5
+ ITERATOR_FUNCTIONS = %i[inner_monologue outer_voice action_request summarize].freeze
6
+
5
7
  extend OxAiWorkers::ToolDefinition
6
8
  attr_accessor :worker, :role, :messages, :context, :result, :tools, :queue, :monologue, :tasks, :milestones
7
- attr_accessor :on_inner_monologue, :on_outer_voice, :on_action_request, :on_pack_history
9
+ attr_accessor :on_inner_monologue, :on_outer_voice, :on_action_request, :on_summarize, :def_except, :def_only
8
10
 
9
11
  define_function :inner_monologue, description: I18n.t('oxaiworkers.iterator.inner_monologue.description') do
10
12
  property :speach, type: 'string', description: I18n.t('oxaiworkers.iterator.inner_monologue.speach'),
@@ -20,22 +22,24 @@ module OxAiWorkers
20
22
  required: true
21
23
  end
22
24
 
23
- define_function :summarize, description: I18n.t('oxaiworkers.iterator.pack_history.description') do
24
- property :text, type: 'string', description: I18n.t('oxaiworkers.iterator.pack_history.text'), required: true
25
+ define_function :summarize, description: I18n.t('oxaiworkers.iterator.summarize.description') do
26
+ property :text, type: 'string', description: I18n.t('oxaiworkers.iterator.summarize.text'), required: true
25
27
  end
26
28
 
27
29
  def initialize(worker:, role: nil, tools: [], on_inner_monologue: nil, on_outer_voice: nil, on_action_request: nil,
28
- on_pack_history: nil, steps: nil)
30
+ on_summarize: nil, steps: nil, def_except: [], def_only: nil)
29
31
  @worker = worker
30
- @tools = [self] + tools
32
+ @tools = tools
31
33
  @role = role
32
34
  @context = []
35
+ @def_only = def_only || ITERATOR_FUNCTIONS
36
+ @def_except = def_except
33
37
  @monologue = steps || I18n.t('oxaiworkers.iterator.monologue')
34
38
 
35
39
  @on_inner_monologue = on_inner_monologue
36
40
  @on_outer_voice = on_outer_voice
37
41
  @on_action_request = on_action_request
38
- @on_pack_history = on_pack_history
42
+ @on_summarize = on_summarize
39
43
 
40
44
  cleanup
41
45
 
@@ -61,6 +65,7 @@ module OxAiWorkers
61
65
  def outer_voice(text:)
62
66
  # @queue.pop
63
67
  @queue << { role: :assistant, content: text.to_s }
68
+ complete! unless available_defs.include?(:action_request)
64
69
  @on_outer_voice&.call(text: text)
65
70
  nil
66
71
  end
@@ -77,11 +82,11 @@ module OxAiWorkers
77
82
  def summarize(text:)
78
83
  @milestones << text.to_s
79
84
  @messages = []
80
- @queue << { role: :assistant, content: I18n.t('oxaiworkers.iterator.pack_history.result') }
85
+ @queue << { role: :assistant, content: I18n.t('oxaiworkers.iterator.summarize.result') }
81
86
  @worker.finish
82
87
  rebuild_worker
83
- # complete! if can_complete?
84
- @on_pack_history&.call(text: text)
88
+ complete! if can_complete?
89
+ @on_summarize&.call(text: text)
85
90
  nil
86
91
  end
87
92
 
@@ -93,12 +98,21 @@ module OxAiWorkers
93
98
  def rebuild_worker
94
99
  @worker.messages = []
95
100
  @worker.append(role: :system, content: @role) if @role.present?
96
- @worker.append(role: :system, content: @monologue.join("\n"))
101
+ @worker.append(role: :system, content: valid_monologue.join("\n"))
97
102
  @worker.append(messages: @context) if @context.present?
98
103
  @tasks.each { |task| @worker.append(role: :user, content: task) }
99
104
  @milestones.each { |milestone| @worker.append(role: :assistant, content: milestone) }
100
105
  @worker.append(messages: @messages)
101
- @worker.tools = @tools.map { |tool| tool.class.function_schemas.to_openai_format }.flatten if @tools.present?
106
+ @worker.tools = self.class.function_schemas.to_openai_format(only: available_defs)
107
+ @worker.tools += @tools.map { |tool| tool.class.function_schemas.to_openai_format }.flatten if @tools.present?
108
+ end
109
+
110
+ def available_defs
111
+ @def_only - @def_except
112
+ end
113
+
114
+ def valid_monologue
115
+ @monologue.reject { |item| @def_except.any? { |fun| item.include?(self.class.full_function_name(fun)) } }
102
116
  end
103
117
 
104
118
  def next_iteration
@@ -123,12 +137,12 @@ module OxAiWorkers
123
137
  if @worker.tool_calls.present?
124
138
  @queue << { role: :assistant, content: @worker.tool_calls_raw.to_s }
125
139
  @worker.tool_calls.each do |external_call|
126
- tool = @tools.select do |t|
140
+ tool = ([self] + @tools).select do |t|
127
141
  t.class.tool_name == external_call[:class] && t.respond_to?(external_call[:name])
128
142
  end.first
129
143
  unless tool.nil?
130
144
  out = tool.send(external_call[:name], **external_call[:args])
131
- @queue << { role: :user, content: out.to_s } if out.present?
145
+ @queue << { role: :system, content: out.to_s } if out.present?
132
146
  end
133
147
  end
134
148
  @worker.finish
@@ -63,6 +63,10 @@ module OxAiWorkers::ToolDefinition
63
63
  .downcase
64
64
  end
65
65
 
66
+ def full_function_name(fun)
67
+ function_schemas.function_name(fun)
68
+ end
69
+
66
70
  # Manages schemas for functions
67
71
  class FunctionSchemas
68
72
  def initialize(tool_name)
@@ -70,6 +74,10 @@ module OxAiWorkers::ToolDefinition
70
74
  @tool_name = tool_name
71
75
  end
72
76
 
77
+ def function_name method_name
78
+ "#{@tool_name}__#{method_name}"
79
+ end
80
+
73
81
  # Adds a function to the schemas
74
82
  #
75
83
  # @param method_name [Symbol] Name of the method to add
@@ -77,7 +85,7 @@ module OxAiWorkers::ToolDefinition
77
85
  # @yield Block that defines the parameters for the function
78
86
  # @raise [ArgumentError] If a block is defined and no parameters are specified for the function
79
87
  def add_function(method_name:, description:, &)
80
- name = "#{@tool_name}__#{method_name}"
88
+ name = function_name(method_name)
81
89
 
82
90
  if block_given?
83
91
  parameters = ParameterBuilder.new(parent_type: "object").build(&)
@@ -96,15 +104,23 @@ module OxAiWorkers::ToolDefinition
96
104
  # Converts schemas to OpenAI-compatible format
97
105
  #
98
106
  # @return [String] JSON string of schemas in OpenAI format
99
- def to_openai_format
100
- @schemas.values#.map { |schema| schema[:function] }
107
+ def to_openai_format(only: nil)
108
+ valid_schemas(only: only).values
109
+ end
110
+
111
+ def valid_schemas(only: nil)
112
+ if only.nil?
113
+ @schemas
114
+ else
115
+ @schemas.select { |name, schema| only.include?(name) }
116
+ end
101
117
  end
102
118
 
103
119
  # Converts schemas to Anthropic-compatible format
104
120
  #
105
121
  # @return [String] JSON string of schemas in Anthropic format
106
- def to_anthropic_format
107
- @schemas.values.map do |schema|
122
+ def to_anthropic_format(only: nil)
123
+ valid_schemas(only: only).values.map do |schema|
108
124
  schema[:function].transform_keys("parameters" => "input_schema")
109
125
  end
110
126
  end
@@ -112,8 +128,8 @@ module OxAiWorkers::ToolDefinition
112
128
  # Converts schemas to Google Gemini-compatible format
113
129
  #
114
130
  # @return [String] JSON string of schemas in Google Gemini format
115
- def to_google_gemini_format
116
- @schemas.values.map { |schema| schema[:function] }
131
+ def to_google_gemini_format(only: nil)
132
+ valid_schemas(only: only).values.map { |schema| schema[:function] }
117
133
  end
118
134
  end
119
135
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OxAiWorkers
4
- VERSION = '0.5.3.1'
4
+ VERSION = '0.5.4'
5
5
  end
@@ -10,7 +10,7 @@ en:
10
10
  action_request:
11
11
  description: A function for interactive interaction with the user. Allows you to ask a clarifying question, request actions, or complete the current step. The function waits for the user's response and returns it.
12
12
  action: Text of the request or action
13
- pack_history:
13
+ summarize:
14
14
  description: The function saves key facts, nuances, and actions from previous messages, including the provided response. After calling this function, all previous messages will be deleted. Use it only after all intermediate steps are completed and when the exact content of previous messages is no longer relevant.
15
15
  text: Enumeration of important facts and nuances
16
16
  result: Messages deleted
@@ -18,5 +18,5 @@ en:
18
18
  - Step 1. Develop your own solution to the problem, taking initiative and making assumptions.
19
19
  - Step 2. Enclose all your developments from the previous step in the ox_ai_workers_iterator__inner_monologue function.
20
20
  - Step 3. Call the necessary functions one after another until the desired result is achieved.
21
- - Step 4. When all intermediate steps are completed and the exact content of previous messages is no longer relevant, use the ox_ai_workers_iterator__pack_history function.
21
+ - Step 4. When all intermediate steps are completed and the exact content of previous messages is no longer relevant, use the ox_ai_workers_iterator__summarize function.
22
22
  - Step 5. When the solution is ready, notify about it and wait for the user's response.
@@ -10,7 +10,7 @@ ru:
10
10
  action_request:
11
11
  description: Функция для интерактивного взаимодействия с пользователем. Позволяет задать уточняющий вопрос, запросить выполнение действий или завершить текущий шаг. Функция ожидает ответ пользователя и возвращает его.
12
12
  action: Текст запроса или действия
13
- pack_history:
13
+ summarize:
14
14
  description: Функция сохраняет ключевые факты, нюансы и действия из предыдущих сообщений, включая предоставленный ответ. После вызова этой функции все предыдущие сообщения будут удалены. Используйте её только после завершения всех промежуточных шагов и когда точное содержание предыдущих сообщений больше не имеет значения.
15
15
  text: Перечисление важных фактов и нюансов
16
16
  result: Сообщения удалены
@@ -18,5 +18,5 @@ ru:
18
18
  - Шаг 1. Разработай собственное решение проблемы, проявляя инициативу и делая предположения.
19
19
  - Шаг 2. Заключи все свои наработки из предыдущего шага в функцию ox_ai_workers_iterator__inner_monologue.
20
20
  - Шаг 3. Вызывай необходимые функции друг за другом, пока желаемый результат не будет достигнут.
21
- - Шаг 4. Когда все промежуточные шаги завершены и точное содержание предыдущих сообщений больше не имеет значения, используй функцию ox_ai_workers_iterator__pack_history.
21
+ - Шаг 4. Когда все промежуточные шаги завершены и точное содержание предыдущих сообщений больше не имеет значения, используй функцию ox_ai_workers_iterator__summarize.
22
22
  - Шаг 5. Когда решение готово, сообщи об этом и ожидай ответ пользователя.
@@ -10,9 +10,9 @@ class MyAssistant
10
10
  # Optional instructions
11
11
  # steps = []
12
12
  # steps << 'Step 1. Develop your own solution to the problem, taking initiative and making assumptions.'
13
- # steps << 'Step 2. Enclose all your developments from the previous step in the ox_ai_workers_iterator__inner_monologue function.'
13
+ # steps << "Step 2. Enclose all your developments from the previous step in the #{OxAiWorkers::Iterator.full_function_name(:inner_monologue)} function."
14
14
  # steps << 'Step 3. Call the necessary functions one after another until the desired result is achieved.'
15
- # steps << 'Step 4. When all intermediate steps are completed and the exact content of previous messages is no longer relevant, use the ox_ai_workers_iterator__pack_history function.'
15
+ # steps << "Step 4. When all intermediate steps are completed and the exact content of previous messages is no longer relevant, use the #{OxAiWorkers::Iterator.full_function_name(:summarize)} function."
16
16
  # steps << "Step 5. When the solution is ready, notify about it and wait for the user's response."
17
17
 
18
18
  @iterator = OxAiWorkers::Iterator.new(
@@ -23,7 +23,7 @@ class MyAssistant
23
23
  on_inner_monologue: ->(text:) { puts "monologue: #{text}".colorize(:yellow) },
24
24
  on_outer_voice: ->(text:) { puts "voice: #{text}".colorize(:green) },
25
25
  on_action_request: ->(text:) { puts "action: #{text}".colorize(:red) },
26
- on_pack_history: ->(text:) { puts "summary: #{text}".colorize(:blue) }
26
+ on_summarize: ->(text:) { puts "summary: #{text}".colorize(:blue) }
27
27
  )
28
28
  end
29
29
  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.3.1
4
+ version: 0.5.4
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-07-31 00:00:00.000000000 Z
11
+ date: 2024-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -108,17 +108,17 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '1'
111
- description: |
112
- OxAiWorkers (ox-ai-workers) is a Ruby gem that provides a powerful and flexible state machine with
113
- integration of generative intelligence using the ruby-openai gem. This gem allows you to create state
114
- machines for solving complex tasks, enhancing the final result by leveraging internal logic (state machine)
115
- and external tools (OpenAI generative intelligence).
111
+ description: |2
112
+ OxAiWorkers (ox-ai-workers) is a Ruby gem that provides a powerful and flexible state machine with
113
+ integration of generative intelligence using the ruby-openai gem. This gem allows you to create state
114
+ machines for solving complex tasks, enhancing the final result by leveraging internal logic (state machine)
115
+ and external tools (OpenAI generative intelligence).
116
116
 
117
- Features:
118
- - State Machine: Easily create and manage state machines to model various states and transitions in your application.
119
- - OpenAI Integration: Utilize the capabilities of generative intelligence to make decisions and perform tasks, improving the quality and accuracy of results.
120
- - Flexibility and Extensibility: Customize the behavior of the state machine and OpenAI integration according to your needs.
121
- - Ease of Use: Intuitive syntax and documentation make it easy to get started with the gem.
117
+ Features:
118
+ - State Machine: Easily create and manage state machines to model various states and transitions in your application.
119
+ - OpenAI Integration: Utilize the capabilities of generative intelligence to make decisions and perform tasks, improving the quality and accuracy of results.
120
+ - Flexibility and Extensibility: Customize the behavior of the state machine and OpenAI integration according to your needs.
121
+ - Ease of Use: Intuitive syntax and documentation make it easy to get started with the gem.
122
122
  email:
123
123
  - smolev@me.com
124
124
  executables: