ox-ai-workers 0.8.7 → 0.9.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 +4 -4
- data/.cursorrules +88 -2
- data/CHANGELOG.md +21 -11
- data/README.md +135 -38
- data/config/locales/en.oxaiworkers.assistant.yml +39 -5
- data/config/locales/en.oxaiworkers.iterator.yml +1 -9
- data/config/locales/en.oxaiworkers.tool.yml +16 -1
- data/config/locales/ru.oxaiworkers.assistant.yml +39 -5
- data/config/locales/ru.oxaiworkers.iterator.yml +2 -11
- data/config/locales/ru.oxaiworkers.tool.yml +16 -1
- data/lib/ox-ai-workers.rb +16 -6
- data/lib/oxaiworkers/assistant/coder.rb +16 -8
- data/lib/oxaiworkers/assistant/localizer.rb +16 -9
- data/lib/oxaiworkers/assistant/module_base.rb +14 -5
- data/lib/oxaiworkers/assistant/orchestrator.rb +44 -0
- data/lib/oxaiworkers/assistant/painter.rb +37 -0
- data/lib/oxaiworkers/assistant/sysop.rb +15 -7
- data/lib/oxaiworkers/delayed_request.rb +2 -2
- data/lib/oxaiworkers/image_iterator.rb +41 -0
- data/lib/oxaiworkers/iterator.rb +34 -54
- data/lib/oxaiworkers/models/deepseek_max.rb +16 -0
- data/lib/oxaiworkers/models/module_base.rb +17 -0
- data/lib/oxaiworkers/models/openai_max.rb +15 -0
- data/lib/oxaiworkers/models/openai_mini.rb +12 -0
- data/lib/oxaiworkers/models/openai_nano.rb +12 -0
- data/lib/oxaiworkers/module_request.rb +11 -15
- data/lib/oxaiworkers/tool/eval.rb +19 -8
- data/lib/oxaiworkers/tool/file_system.rb +30 -15
- data/lib/oxaiworkers/tool/pipeline.rb +106 -0
- data/lib/oxaiworkers/tool/pixels.rb +76 -0
- data/lib/oxaiworkers/version.rb +1 -1
- data/template/start +4 -6
- metadata +11 -1
@@ -2,23 +2,19 @@
|
|
2
2
|
|
3
3
|
module OxAiWorkers
|
4
4
|
class ModuleRequest
|
5
|
-
attr_accessor :result, :client, :messages, :model, :
|
6
|
-
:tool_calls_raw, :tool_calls, :is_truncated, :finish_reason, :
|
5
|
+
attr_accessor :result, :client, :messages, :model, :custom_id, :tools, :errors,
|
6
|
+
:tool_calls_raw, :tool_calls, :is_truncated, :finish_reason, :on_stream_proc
|
7
7
|
|
8
|
-
def initialize_requests(model
|
9
|
-
@max_tokens = max_tokens || OxAiWorkers.configuration.max_tokens
|
8
|
+
def initialize_requests(model:, on_stream: nil)
|
10
9
|
@custom_id = SecureRandom.uuid
|
11
|
-
@model = model
|
12
|
-
@temperature = temperature || OxAiWorkers.configuration.temperature
|
13
|
-
@uri_base = uri_base
|
10
|
+
@model = model
|
14
11
|
@client = nil
|
15
12
|
@is_truncated = false
|
16
13
|
@finish_reason = nil
|
17
14
|
@on_stream_proc = on_stream
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
error_text = 'OpenAi access token missing!'
|
16
|
+
if @model.api_key.nil?
|
17
|
+
error_text = "#{@model.model} access token missing!"
|
22
18
|
raise OxAiWorkers::ConfigurationError, error_text
|
23
19
|
end
|
24
20
|
|
@@ -27,8 +23,8 @@ module OxAiWorkers
|
|
27
23
|
|
28
24
|
def cleanup
|
29
25
|
@client ||= OpenAI::Client.new(
|
30
|
-
access_token:
|
31
|
-
uri_base: @
|
26
|
+
access_token: @model.api_key,
|
27
|
+
uri_base: @model.uri_base,
|
32
28
|
log_errors: true # Highly recommended in development, so you can see what errors OpenAI is returning. Not recommended in production because it could leak private data to your logs.
|
33
29
|
)
|
34
30
|
@result = nil
|
@@ -47,10 +43,10 @@ module OxAiWorkers
|
|
47
43
|
|
48
44
|
def params
|
49
45
|
parameters = {
|
50
|
-
model: @model,
|
46
|
+
model: @model.model,
|
51
47
|
messages: @messages,
|
52
|
-
temperature: @temperature,
|
53
|
-
|
48
|
+
temperature: @model.temperature,
|
49
|
+
max_completion_tokens: @model.max_tokens
|
54
50
|
}
|
55
51
|
if @tools.present?
|
56
52
|
parameters[:tools] = @tools
|
@@ -9,10 +9,13 @@ module OxAiWorkers
|
|
9
9
|
include OxAiWorkers::DependencyHelper
|
10
10
|
include OxAiWorkers::LoadI18n
|
11
11
|
|
12
|
-
|
12
|
+
attr_accessor :current_dir
|
13
|
+
|
14
|
+
def initialize(only: nil, current_dir: nil)
|
13
15
|
store_locale
|
14
16
|
|
15
17
|
init_white_list_with only
|
18
|
+
@current_dir = current_dir
|
16
19
|
|
17
20
|
define_function :ruby, description: I18n.t('oxaiworkers.tool.eval.ruby.description') do
|
18
21
|
property :input, type: 'string', description: I18n.t('oxaiworkers.tool.eval.ruby.input'), required: true
|
@@ -25,20 +28,28 @@ module OxAiWorkers
|
|
25
28
|
|
26
29
|
def ruby(input:)
|
27
30
|
OxAiWorkers.logger.info("Executing ruby: \"#{input}\"", for: self.class)
|
28
|
-
eval(input)
|
31
|
+
execute_in_directory { eval(input) }
|
29
32
|
end
|
30
33
|
|
31
34
|
def sh(input:)
|
32
35
|
OxAiWorkers.logger.info("Executing sh: \"#{input}\"", for: self.class)
|
33
|
-
|
36
|
+
execute_in_directory do
|
34
37
|
stdout_and_stderr_s, status = Open3.capture2e(input)
|
35
|
-
|
38
|
+
stdout_and_stderr_s.present? ? stdout_and_stderr_s : status.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
36
43
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
44
|
+
def execute_in_directory(&block)
|
45
|
+
if @current_dir.present?
|
46
|
+
Dir.chdir(@current_dir, &block)
|
47
|
+
else
|
48
|
+
yield
|
41
49
|
end
|
50
|
+
rescue StandardError => e
|
51
|
+
OxAiWorkers.logger.debug(e.message, for: self.class)
|
52
|
+
e.message
|
42
53
|
end
|
43
54
|
end
|
44
55
|
end
|
@@ -15,7 +15,9 @@ module OxAiWorkers
|
|
15
15
|
include OxAiWorkers::DependencyHelper
|
16
16
|
include OxAiWorkers::LoadI18n
|
17
17
|
|
18
|
-
|
18
|
+
attr_accessor :current_dir
|
19
|
+
|
20
|
+
def initialize(current_dir: nil, only: nil)
|
19
21
|
depends_on 'ptools'
|
20
22
|
|
21
23
|
store_locale
|
@@ -40,38 +42,51 @@ module OxAiWorkers
|
|
40
42
|
property :content, type: 'string', description: I18n.t('oxaiworkers.tool.file_system.write_to_file.content'),
|
41
43
|
required: true
|
42
44
|
end
|
45
|
+
|
46
|
+
@current_dir = current_dir
|
43
47
|
end
|
44
48
|
|
45
49
|
def list_directory(directory_path:)
|
46
|
-
|
47
|
-
|
50
|
+
path = full_path(directory_path)
|
51
|
+
OxAiWorkers.logger.info("Listing directory: #{path}", for: self.class)
|
52
|
+
list = Dir.entries(path)
|
48
53
|
list.delete_if { |f| f.start_with?('.') }
|
49
54
|
if list.present?
|
50
|
-
|
55
|
+
"Contents of directory \"#{path}\":\n #{list.join("\n")}"
|
51
56
|
else
|
52
|
-
|
57
|
+
"Directory is empty: #{path}"
|
53
58
|
end
|
54
59
|
rescue Errno::ENOENT
|
55
|
-
|
60
|
+
"No such directory: #{path}"
|
56
61
|
end
|
57
62
|
|
58
63
|
def read_file(file_path:)
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
path = full_path(file_path)
|
65
|
+
OxAiWorkers.logger.info("Reading file: #{path}", for: self.class)
|
66
|
+
if File.binary?(path)
|
67
|
+
"File is binary: #{path}"
|
62
68
|
else
|
63
|
-
File.read(
|
69
|
+
File.read(path).to_s
|
64
70
|
end
|
65
71
|
rescue Errno::ENOENT
|
66
|
-
|
72
|
+
"No such file: #{path}"
|
67
73
|
end
|
68
74
|
|
69
75
|
def write_to_file(file_path:, content:)
|
70
|
-
|
71
|
-
|
72
|
-
|
76
|
+
path = full_path(file_path)
|
77
|
+
OxAiWorkers.logger.info("Writing to file: #{path}", for: self.class)
|
78
|
+
# Ensure directory exists if using current_dir
|
79
|
+
FileUtils.mkdir_p(File.dirname(path)) if @current_dir.present? && !Dir.exist?(File.dirname(path))
|
80
|
+
File.write(path, content)
|
81
|
+
"Content was successfully written to the file: #{path}"
|
73
82
|
rescue Errno::EACCES
|
74
|
-
|
83
|
+
"Permission denied: #{path}"
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def full_path(path)
|
89
|
+
@current_dir.present? ? File.join(@current_dir, path) : path
|
75
90
|
end
|
76
91
|
end
|
77
92
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module OxAiWorkers
|
2
|
+
module Tool
|
3
|
+
class Pipeline
|
4
|
+
include OxAiWorkers::ToolDefinition
|
5
|
+
include OxAiWorkers::DependencyHelper
|
6
|
+
include OxAiWorkers::LoadI18n
|
7
|
+
|
8
|
+
attr_accessor :assistants, :messages
|
9
|
+
|
10
|
+
def initialize(only: nil, on_message: nil)
|
11
|
+
store_locale
|
12
|
+
|
13
|
+
init_white_list_with only
|
14
|
+
|
15
|
+
define_function :send_message, description: I18n.t('oxaiworkers.tool.pipeline.send_message.description') do
|
16
|
+
property :message, type: 'string', description: I18n.t('oxaiworkers.tool.pipeline.send_message.message'),
|
17
|
+
required: true
|
18
|
+
property :result, type: 'string', description: I18n.t('oxaiworkers.tool.pipeline.send_message.result'),
|
19
|
+
required: true
|
20
|
+
property :example, type: 'string', description: I18n.t('oxaiworkers.tool.pipeline.send_message.example')
|
21
|
+
property :to_id, type: 'string', description: I18n.t('oxaiworkers.tool.pipeline.send_message.to_id'),
|
22
|
+
required: true
|
23
|
+
end
|
24
|
+
|
25
|
+
@messages = []
|
26
|
+
@on_message = on_message
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_message(message:, result:, example:, to_id:)
|
30
|
+
puts "send_message to #{to_id}: #{message}".colorize(:red)
|
31
|
+
puts " Result: #{result}"
|
32
|
+
puts " Example: #{example}"
|
33
|
+
context = context_for(to_id)
|
34
|
+
@assistants[to_id].replace_context(context)
|
35
|
+
@assistants[to_id].add_task message
|
36
|
+
@assistants[to_id].add_task "#{I18n.t('oxaiworkers.tool.pipeline.send_message.result')}: #{result}"
|
37
|
+
@assistants[to_id].add_task "#{I18n.t('oxaiworkers.tool.pipeline.send_message.example')}: #{example}"
|
38
|
+
@assistants[to_id].execute
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_assistant(assistant)
|
43
|
+
unless assistant.id.present? &&
|
44
|
+
assistant.title.present? &&
|
45
|
+
assistant.role.present? &&
|
46
|
+
assistant.description.present? &&
|
47
|
+
assistant.capabilities.present?
|
48
|
+
raise 'Assistant is not valid: id, title, role, description and capabilities are required'
|
49
|
+
end
|
50
|
+
|
51
|
+
assistant.iterator.on_inner_monologue = ->(text:) { recive_monologue(from_id: assistant.id, message: text) }
|
52
|
+
assistant.iterator.on_outer_voice = ->(text:) { recive_voice(from_id: assistant.id, message: text) }
|
53
|
+
|
54
|
+
@assistants ||= {}
|
55
|
+
@assistants[assistant.id] = assistant
|
56
|
+
end
|
57
|
+
|
58
|
+
def recive_monologue(from_id:, message:)
|
59
|
+
recive_message(id: from_id, type: 'monologue', message:, color: :yellow)
|
60
|
+
end
|
61
|
+
|
62
|
+
def recive_voice(from_id:, message:)
|
63
|
+
recive_message(id: from_id, type: 'voice', message:, color: :green)
|
64
|
+
end
|
65
|
+
|
66
|
+
def recive_message(id:, type:, message:, color: :grey)
|
67
|
+
puts "#{id} [#{type}]: #{message}".colorize(color)
|
68
|
+
@messages ||= []
|
69
|
+
m = { id:, type:, message: }
|
70
|
+
@messages << m
|
71
|
+
@on_message.call(text: format_message(m)) if !@on_message.nil? && @assistants.key?(id)
|
72
|
+
# @assistants.each_value do |assistant|
|
73
|
+
# assistant.iterator.add_queue format_message(m), role: :system if assistant.id != id
|
74
|
+
# end
|
75
|
+
end
|
76
|
+
|
77
|
+
def context_for(id)
|
78
|
+
array = @messages.select { |message| message[:id] == id || message[:type] == 'voice' }
|
79
|
+
array.map { |m| format_message(m) }.join("\n\n")
|
80
|
+
end
|
81
|
+
|
82
|
+
def context
|
83
|
+
"#{assistants_info}\n\n#{@messages.map { |m| format_message(m) }.join("\n\n")}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def assistants_info
|
87
|
+
@assistants.values.map { |assistant| format_assistant(assistant) }.join("\n\n")
|
88
|
+
end
|
89
|
+
|
90
|
+
def format_message(message)
|
91
|
+
"**#{message[:id]}**\n: #{message[:message]}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def format_assistant(assistant)
|
95
|
+
with_locale do
|
96
|
+
I18n.t('oxaiworkers.tool.pipeline.assistant_info',
|
97
|
+
id: assistant.id,
|
98
|
+
title: assistant.title,
|
99
|
+
role: assistant.role,
|
100
|
+
description: assistant.description,
|
101
|
+
capabilities: assistant.capabilities.join(', '))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module OxAiWorkers
|
5
|
+
module Tool
|
6
|
+
class Pixels
|
7
|
+
include OxAiWorkers::ToolDefinition
|
8
|
+
include OxAiWorkers::DependencyHelper
|
9
|
+
include OxAiWorkers::LoadI18n
|
10
|
+
|
11
|
+
attr_accessor :worker, :url, :current_dir
|
12
|
+
|
13
|
+
def initialize(worker:, current_dir: nil, only: nil)
|
14
|
+
store_locale
|
15
|
+
|
16
|
+
init_white_list_with only
|
17
|
+
|
18
|
+
define_function :generate_image, description: I18n.t('oxaiworkers.tool.pixels.generate_image.description') do
|
19
|
+
property :prompt, type: 'string', description: I18n.t('oxaiworkers.tool.pixels.generate_image.prompt'),
|
20
|
+
required: true
|
21
|
+
property :size, type: 'string', description: I18n.t('oxaiworkers.tool.pixels.generate_image.size'),
|
22
|
+
enum: %w[1024x1792 1792x1024 1024x1024]
|
23
|
+
if current_dir.present?
|
24
|
+
property :file_name, type: 'string',
|
25
|
+
description: I18n.t('oxaiworkers.tool.pixels.generate_image.file_name'),
|
26
|
+
required: true
|
27
|
+
end
|
28
|
+
property :quality, type: 'string', description: I18n.t('oxaiworkers.tool.pixels.generate_image.quality'),
|
29
|
+
enum: %w[standard hd]
|
30
|
+
end
|
31
|
+
|
32
|
+
@worker = worker
|
33
|
+
@current_dir = current_dir
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_image(prompt:, file_name: nil, size: '1024x1792', quality: 'standard')
|
37
|
+
puts "generate_image: #{prompt}"
|
38
|
+
|
39
|
+
response = @worker.client.images.generate(
|
40
|
+
parameters: {
|
41
|
+
prompt:,
|
42
|
+
model: 'dall-e-3',
|
43
|
+
size:,
|
44
|
+
quality:
|
45
|
+
}
|
46
|
+
)
|
47
|
+
|
48
|
+
@url = response.dig('data', 0, 'url')
|
49
|
+
revised_prompt = response.dig('data', 0, 'revised_prompt')
|
50
|
+
if file_name.present?
|
51
|
+
path = save_generated_image(file_name:)
|
52
|
+
"url: #{@url}\nfile_name: #{path}\n\nrevised_prompt: #{revised_prompt}"
|
53
|
+
else
|
54
|
+
"url: #{@url}\n\nrevised_prompt: #{revised_prompt}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def save_generated_image(file_name:)
|
59
|
+
unless @current_dir.present?
|
60
|
+
return 'Current directory not set for OxAiWorkers::Tool::Pixels. Please set current directory first.'
|
61
|
+
end
|
62
|
+
|
63
|
+
# Ensure current_dir exists
|
64
|
+
FileUtils.mkdir_p(@current_dir) unless Dir.exist?(@current_dir)
|
65
|
+
|
66
|
+
path = File.join(@current_dir, file_name)
|
67
|
+
|
68
|
+
File.open(path, 'wb') do |file|
|
69
|
+
file.write(URI.open(@url).read)
|
70
|
+
end
|
71
|
+
|
72
|
+
file_name
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/oxaiworkers/version.rb
CHANGED
data/template/start
CHANGED
@@ -18,18 +18,16 @@ puts "OxAiWorkers #{OxAiWorkers::VERSION}"
|
|
18
18
|
|
19
19
|
# Configure
|
20
20
|
OxAiWorkers.configure do |config|
|
21
|
-
config.
|
22
|
-
config.model = 'gpt-4o-mini'
|
23
|
-
# config.auto_execute = false
|
21
|
+
config.access_token_openai = ENV.fetch('OPENAI')
|
24
22
|
end
|
25
23
|
|
24
|
+
OxAiWorkers.default_model = OxAiWorkers::Models::OpenaiMini.new
|
25
|
+
|
26
26
|
# OxAiWorkers.logger.level = :debug
|
27
27
|
|
28
28
|
# Main algorithm
|
29
29
|
@assistant = MyAssistant.new
|
30
30
|
@assistant.task = '2 + 2 is ?'
|
31
|
-
|
32
|
-
# Uncomment this if auto_execute is false
|
33
|
-
# @assistant.execute
|
31
|
+
@assistant.execute
|
34
32
|
|
35
33
|
IRB.start(__FILE__)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ox-ai-workers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Smolev
|
@@ -143,14 +143,22 @@ files:
|
|
143
143
|
- lib/oxaiworkers/assistant/coder.rb
|
144
144
|
- lib/oxaiworkers/assistant/localizer.rb
|
145
145
|
- lib/oxaiworkers/assistant/module_base.rb
|
146
|
+
- lib/oxaiworkers/assistant/orchestrator.rb
|
147
|
+
- lib/oxaiworkers/assistant/painter.rb
|
146
148
|
- lib/oxaiworkers/assistant/sysop.rb
|
147
149
|
- lib/oxaiworkers/compatibility.rb
|
148
150
|
- lib/oxaiworkers/contextual_logger.rb
|
149
151
|
- lib/oxaiworkers/delayed_request.rb
|
150
152
|
- lib/oxaiworkers/dependency_helper.rb
|
151
153
|
- lib/oxaiworkers/engine.rb
|
154
|
+
- lib/oxaiworkers/image_iterator.rb
|
152
155
|
- lib/oxaiworkers/iterator.rb
|
153
156
|
- lib/oxaiworkers/load_i18n.rb
|
157
|
+
- lib/oxaiworkers/models/deepseek_max.rb
|
158
|
+
- lib/oxaiworkers/models/module_base.rb
|
159
|
+
- lib/oxaiworkers/models/openai_max.rb
|
160
|
+
- lib/oxaiworkers/models/openai_mini.rb
|
161
|
+
- lib/oxaiworkers/models/openai_nano.rb
|
154
162
|
- lib/oxaiworkers/module_request.rb
|
155
163
|
- lib/oxaiworkers/present_compat.rb
|
156
164
|
- lib/oxaiworkers/request.rb
|
@@ -161,6 +169,8 @@ files:
|
|
161
169
|
- lib/oxaiworkers/tool/database.rb
|
162
170
|
- lib/oxaiworkers/tool/eval.rb
|
163
171
|
- lib/oxaiworkers/tool/file_system.rb
|
172
|
+
- lib/oxaiworkers/tool/pipeline.rb
|
173
|
+
- lib/oxaiworkers/tool/pixels.rb
|
164
174
|
- lib/oxaiworkers/tool_definition.rb
|
165
175
|
- lib/oxaiworkers/version.rb
|
166
176
|
- lib/ruby/ox-ai-workers.rb
|