omniai-tools 0.5.0 → 0.6.0

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +72 -16
  4. data/lib/omniai/tools/browser/base_driver.rb +78 -0
  5. data/lib/omniai/tools/browser/base_tool.rb +31 -4
  6. data/lib/omniai/tools/browser/button_click_tool.rb +1 -14
  7. data/lib/omniai/tools/browser/element_click_tool.rb +30 -0
  8. data/lib/omniai/tools/browser/elements/element_grouper.rb +73 -0
  9. data/lib/omniai/tools/browser/elements/nearby_element_detector.rb +108 -0
  10. data/lib/omniai/tools/browser/formatters/action_formatter.rb +37 -0
  11. data/lib/omniai/tools/browser/formatters/data_entry_formatter.rb +135 -0
  12. data/lib/omniai/tools/browser/formatters/element_formatter.rb +52 -0
  13. data/lib/omniai/tools/browser/formatters/input_formatter.rb +59 -0
  14. data/lib/omniai/tools/browser/inspect_tool.rb +46 -13
  15. data/lib/omniai/tools/browser/inspect_utils.rb +51 -0
  16. data/lib/omniai/tools/browser/link_click_tool.rb +2 -14
  17. data/lib/omniai/tools/browser/page_inspect/button_summarizer.rb +140 -0
  18. data/lib/omniai/tools/browser/page_inspect/form_summarizer.rb +98 -0
  19. data/lib/omniai/tools/browser/page_inspect/html_summarizer.rb +37 -0
  20. data/lib/omniai/tools/browser/page_inspect/link_summarizer.rb +103 -0
  21. data/lib/omniai/tools/browser/page_inspect_tool.rb +30 -0
  22. data/lib/omniai/tools/browser/page_screenshot_tool.rb +22 -0
  23. data/lib/omniai/tools/browser/selector_generator/base_selectors.rb +28 -0
  24. data/lib/omniai/tools/browser/selector_generator/contextual_selectors.rb +140 -0
  25. data/lib/omniai/tools/browser/selector_generator.rb +73 -0
  26. data/lib/omniai/tools/browser/selector_inspect_tool.rb +44 -0
  27. data/lib/omniai/tools/browser/text_field_area_set_tool.rb +2 -31
  28. data/lib/omniai/tools/browser/visit_tool.rb +1 -1
  29. data/lib/omniai/tools/browser/watir_driver.rb +222 -0
  30. data/lib/omniai/tools/browser_tool.rb +262 -0
  31. data/lib/omniai/tools/computer/base_driver.rb +179 -0
  32. data/lib/omniai/tools/computer/mac_driver.rb +103 -0
  33. data/lib/omniai/tools/computer_tool.rb +189 -0
  34. data/lib/omniai/tools/database/base_driver.rb +17 -0
  35. data/lib/omniai/tools/database/postgres_driver.rb +30 -0
  36. data/lib/omniai/tools/database/sqlite_driver.rb +29 -0
  37. data/lib/omniai/tools/database_tool.rb +100 -0
  38. data/lib/omniai/tools/version.rb +1 -1
  39. metadata +31 -5
  40. data/lib/omniai/tools/database/base_tool.rb +0 -37
  41. data/lib/omniai/tools/database/sqlite_tool.rb +0 -110
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ # A tool for interacting with a computer. Be careful with using as it can perform actions on your computer!
6
+ #
7
+ # @example
8
+ # computer = OmniAI::Tools::Computer::MacTool.new
9
+ # computer.display # { "width": 2560, "height": 1440, "scale": 1 }
10
+ # computer.screenshot
11
+ class ComputerTool < OmniAI::Tool
12
+ description "A tool for interacting with a computer."
13
+
14
+ module Action
15
+ KEY = "key" # press a key
16
+ HOLD_KEY = "hold_key" # hold a key
17
+ MOUSE_POSITION = "mouse_position" # get the current (x, y) pixel coordinate of the cursor on the screen
18
+ MOUSE_MOVE = "mouse_move" # move the cursor to a specific (x, y) pixel coordinate on the screen
19
+ MOUSE_CLICK = "mouse_click" # click at a specific x / y coordinate
20
+ MOUSE_DOWN = "mouse_down" # press the mouse button down
21
+ MOUSE_DRAG = "mouse_drag" # drag the mouse to a specific x / y coordinate
22
+ MOUSE_UP = "mouse_up" # release the mouse button
23
+ MOUSE_DOUBLE_CLICK = "mouse_double_click" # double click at a specific x / y coordinate
24
+ MOUSE_TRIPLE_CLICK = "mouse_triple_click" # triple click at a specific x / y coordinate
25
+ TYPE = "type" # type a string
26
+ SCROLL = "scroll"
27
+ WAIT = "wait"
28
+ end
29
+
30
+ module MouseButton
31
+ LEFT = "left"
32
+ MIDDLE = "middle"
33
+ RIGHT = "right"
34
+ end
35
+
36
+ module ScrollDirection
37
+ UP = "up"
38
+ DOWN = "down"
39
+ LEFT = "left"
40
+ RIGHT = "right"
41
+ end
42
+
43
+ ACTIONS = [
44
+ Action::KEY,
45
+ Action::HOLD_KEY,
46
+ Action::MOUSE_POSITION,
47
+ Action::MOUSE_MOVE,
48
+ Action::MOUSE_CLICK,
49
+ Action::MOUSE_DOWN,
50
+ Action::MOUSE_DRAG,
51
+ Action::MOUSE_UP,
52
+ Action::TYPE,
53
+ Action::SCROLL,
54
+ Action::WAIT,
55
+ ].freeze
56
+
57
+ MOUSE_BUTTON_OPTIONS = [
58
+ MouseButton::LEFT,
59
+ MouseButton::MIDDLE,
60
+ MouseButton::RIGHT,
61
+ ].freeze
62
+
63
+ SCROLL_DIRECTION_OPTIONS = [
64
+ ScrollDirection::UP,
65
+ ScrollDirection::DOWN,
66
+ ScrollDirection::LEFT,
67
+ ScrollDirection::RIGHT,
68
+ ].freeze
69
+
70
+ parameter :action, :string, enum: ACTIONS, description: <<~TEXT
71
+ Options:
72
+ * `#{Action::KEY}`: Press a single key / combination of keys on the keyboard:
73
+ - supports xdotool's `key` syntax (e.g. "alt+Tab", "Return", "ctrl+s", etc)
74
+ * `#{Action::HOLD_KEY}`: Hold down a key or multiple keys for a specified duration (in seconds):
75
+ - supports xdotool's `key` syntax (e.g. "alt+Tab", "Return", "ctrl+s", etc)
76
+ * `#{Action::MOUSE_POSITION}`: Get the current (x,y) pixel coordinate of the cursor on the screen.
77
+ * `#{Action::MOUSE_MOVE}`: Move the cursor to a specified (x,y) pixel coordinate on the screen.
78
+ * `#{Action::MOUSE_CLICK}`: Click the mouse button at the specified (x,y) pixel coordinate on the screen.
79
+ * `#{Action::MOUSE_DOUBLE_CLICK}`: Double click at the specified (x,y) pixel coordinate on the screen.
80
+ * `#{Action::MOUSE_TRIPLE_CLICK}`: Triple click at the specified (x,y) pixel coordinate on the screen.
81
+ * `#{Action::MOUSE_DOWN}`: Press the mouse button at the specified (x,y) pixel coordinate on the screen.
82
+ * `#{Action::MOUSE_DRAG}`: Click and drag the cursor to a specified (x, y) pixel coordinate on the screen.
83
+ * `#{Action::MOUSE_UP}`: Release the mouse button at the specified (x,y) pixel coordinate on the screen.
84
+ * `#{Action::TYPE}`: Type a string of text on the keyboard.
85
+ * `#{Action::SCROLL}`: Scroll the screen in a specified direction by a specified amount of clicks of the scroll wheel.
86
+ * `#{Action::WAIT}`: Wait for a specified duration (in seconds).
87
+ TEXT
88
+
89
+ parameter :coordinate, :object, properties: {
90
+ x: OmniAI::Schema.integer(description: "The x position in pixels"),
91
+ y: OmniAI::Schema.integer(description: "The y position in pixels"),
92
+ }, required: %i[x y], description: <<~TEXT
93
+ An (x,y) coordinate. Required for the following actions:
94
+ * `#{Action::MOUSE_MOVE}`
95
+ * `#{Action::MOUSE_CLICK}`
96
+ * `#{Action::MOUSE_DOWN}`
97
+ * `#{Action::MOUSE_DRAG}`
98
+ * `#{Action::MOUSE_UP}`
99
+ * `#{Action::MOUSE_DOUBLE_CLICK}`
100
+ * `#{Action::MOUSE_TRIPLE_CLICK}`
101
+ TEXT
102
+
103
+ parameter :text, :string, description: <<~TEXT
104
+ The text to type. Required for the following actions:
105
+ * `#{Action::KEY}`
106
+ * `#{Action::HOLD_KEY}`
107
+ * `#{Action::TYPE}`
108
+ TEXT
109
+
110
+ parameter :duration, :integer, description: <<~TEXT
111
+ A duration in seconds. Required for the following actions:
112
+ * `#{Action::HOLD_KEY}`
113
+ * `#{Action::WAIT}`
114
+ TEXT
115
+
116
+ parameter :mouse_button, :string, enum: MOUSE_BUTTON_OPTIONS, description: <<~TEXT
117
+ The mouse button to use. Required for the following actions:
118
+ * `#{Action::MOUSE_CLICK}`
119
+ * `#{Action::MOUSE_DOWN}`
120
+ * `#{Action::MOUSE_DRAG}`
121
+ * `#{Action::MOUSE_UP}`
122
+ * `#{Action::MOUSE_DOUBLE_CLICK}`
123
+ * `#{Action::MOUSE_TRIPLE_CLICK}`
124
+ TEXT
125
+
126
+ parameter :scroll_direction, :string, enum: SCROLL_DIRECTION_OPTIONS, description: <<~TEXT
127
+ The direction to scroll. Required for the following actions:
128
+ * `#{Action::SCROLL}`
129
+ TEXT
130
+
131
+ parameter :scroll_amount, :integer, description: <<~TEXT
132
+ The amount of clicks to scroll. Required for the following actions:
133
+ * `#{Action::SCROLL}`
134
+ TEXT
135
+
136
+ required %i[action]
137
+
138
+ # @param driver [Computer::Driver]
139
+ def initialize(driver:, logger: Logger.new(IO::NULL))
140
+ @driver = driver
141
+ @logger = logger
142
+ super()
143
+ end
144
+
145
+ # @param action [String]
146
+ # @param coordinate [Hash<{ width: Integer, height: Integer }>] the (x,y) coordinate
147
+ # @param text [String]
148
+ # @param duration [Integer] the duration in seconds
149
+ # @param mouse_button [String] e.g. "left", "middle", "right"
150
+ # @param scroll_direction [String] e.g. "up", "down", "left", "right"
151
+ # @param scroll_amount [Integer] the amount of clicks to scroll
152
+ def execute(
153
+ action:,
154
+ coordinate: nil,
155
+ text: nil,
156
+ duration: nil,
157
+ mouse_button: nil,
158
+ scroll_direction: nil,
159
+ scroll_amount: nil
160
+ )
161
+ @logger.info({
162
+ action:,
163
+ coordinate:,
164
+ text:,
165
+ duration:,
166
+ mouse_button:,
167
+ scroll_direction:,
168
+ scroll_amount:,
169
+ }.compact.map { |key, value| "#{key}=#{value.inspect}" }.join(" "))
170
+
171
+ case action
172
+ when Action::KEY then @driver.key(text:)
173
+ when Action::HOLD_KEY then @driver.hold_key(text:, duration:)
174
+ when Action::MOUSE_POSITION then @driver.mouse_position
175
+ when Action::MOUSE_MOVE then @driver.mouse_move(coordinate:)
176
+ when Action::MOUSE_CLICK then @driver.mouse_click(coordinate:, button: mouse_button)
177
+ when Action::MOUSE_DOUBLE_CLICK then @driver.mouse_double_click(coordinate:, button: mouse_button)
178
+ when Action::MOUSE_TRIPLE_CLICK then @driver.mouse_triple_click(coordinate:, button: mouse_button)
179
+ when Action::MOUSE_DOWN then @driver.mouse_down(coordinate:, button: mouse_button)
180
+ when Action::MOUSE_UP then @driver.mouse_up(coordinate:, button: mouse_button)
181
+ when Action::MOUSE_DRAG then @driver.mouse_drag(coordinate:, button: mouse_button)
182
+ when Action::TYPE then @driver.type(text:)
183
+ when Action::SCROLL then @driver.scroll(amount: scroll_amount, direction: scroll_direction)
184
+ when Action::WAIT then @driver.wait(duration:)
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Database
6
+ # Base class for database drivers (e.g. sqlite, postgres, mysql, etc).
7
+ class BaseDriver
8
+ # @param statement [String] e.g. "SELECT * FROM people"
9
+ #
10
+ # @return [Hash] e.g. { status: :ok, result: [["id", "name"], [1, "John"], [2, "Paul"], ...] }
11
+ def perform(statement:)
12
+ raise NotImplementedError, "#{self.class}##{__method__} undefined"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Database
6
+ # @example
7
+ # connection = PG.connect(dbname: "testdb")
8
+ # driver = OmniAI::Tools::Database::PostgresDriver.new
9
+ # driver.perform(statement: "SELECT * FROM people")
10
+ class PostgresDriver < BaseDriver
11
+ # @param connection [Sqlite3::Database]
12
+ def initialize(connection:)
13
+ super()
14
+ @connection = connection
15
+ end
16
+
17
+ # @param statement [String]
18
+ #
19
+ # @return [Hash]
20
+ def perform(statement:)
21
+ @connection.exec(statement) do |result|
22
+ { status: :ok, result: [result.fields] + result.values }
23
+ end
24
+ rescue ::PG::Error => e
25
+ { status: :error, message: e.message }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Database
6
+ # @example
7
+ # driver = OmniAI::Tools::Database::SqliteDriver.new
8
+ # driver.perform(statement: "SELECT * FROM people")
9
+ class SqliteDriver < BaseDriver
10
+ # @param db [Sqlite3::Database]
11
+ def initialize(db:)
12
+ super()
13
+ @db = db
14
+ end
15
+
16
+ # @param statement [String]
17
+ #
18
+ # @return [Hash]
19
+ def perform(statement:)
20
+ result = @db.execute2(statement)
21
+
22
+ { status: :ok, result: }
23
+ rescue ::SQLite3::Exception => e
24
+ { status: :error, message: e.message }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ # @example
6
+ # db = Sqlite3::Database.new("./db.sqlite")
7
+ # driver = OmniAI::Tools::Database::Sqlite.new(db:)
8
+ # tool = OmniAI::Tools::DatabaseTool.new(driver:)
9
+ # tool.execute(statements: ["SELECT * FROM people"])
10
+ class DatabaseTool < OmniAI::Tool
11
+ description <<~TEXT
12
+ Executes SQL commands (INSERT / UPDATE / SELECT / etc) on a database.
13
+
14
+ Example:
15
+
16
+ STATEMENTS:
17
+
18
+ [
19
+ 'CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT NOT NULL)',
20
+ 'INSERT INTO people (name) VALUES ('John')',
21
+ 'INSERT INTO people (name) VALUES ('Paul')',
22
+ 'SELECT * FROM people',
23
+ 'DROP TABLE people'
24
+ ]
25
+
26
+ RESULT:
27
+
28
+ [
29
+ {
30
+ "status": "OK",
31
+ "statement": "CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
32
+ "result": "..."
33
+ },
34
+ {
35
+ "status": "OK",
36
+ "statement": "INSERT INTO people (name) VALUES ('John')"
37
+ "result": "..."
38
+ },
39
+ {
40
+ "status": "OK",
41
+ "statement": "INSERT INTO people (name) VALUES ('Paul')",
42
+ "result": "..."
43
+ },
44
+ {
45
+ "status": "OK",
46
+ "statement": "SELECT * FROM people",
47
+ "result": "..."
48
+ },
49
+ {
50
+ "status": "OK",
51
+ "statement": "DROP TABLE people",
52
+ "result": "..."
53
+ }
54
+ ]
55
+ TEXT
56
+
57
+ parameter(
58
+ :statements,
59
+ :array,
60
+ description: "A list of SQL statements to run sequentially.",
61
+ items: OmniAI::Schema.string(description: 'A SQL statement to run (e.g. "SELECT * FROM ...").')
62
+ )
63
+
64
+ required %i[statements]
65
+
66
+ # @param driver [OmniAI::Tools::Database::BaseDriver]
67
+ # @param logger [IO] An optional logger for debugging executed commands.
68
+ def initialize(driver:, logger: Logger.new(IO::NULL))
69
+ super()
70
+ @driver = driver
71
+ @logger = logger
72
+ end
73
+
74
+ # @example
75
+ # tool = OmniAI::Tools::Database::BaseTool.new
76
+ # tool.execute(statements: ["SELECT * FROM people"])
77
+ #
78
+ # @param statements [Array<String>]
79
+ #
80
+ # @return [Array<Hash>]
81
+ def execute(statements:)
82
+ [].tap do |executions|
83
+ statements.map do |statement|
84
+ execution = perform(statement:).merge(statement:)
85
+ executions << execution
86
+ break unless execution[:status].eql?(:ok)
87
+ end
88
+ end
89
+ end
90
+
91
+ def perform(statement:)
92
+ @logger&.info("#perform statement=#{statement.inspect}")
93
+
94
+ @driver.perform(statement:).tap do |result|
95
+ @logger&.info(JSON.generate(result))
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAI
4
4
  module Tools
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniai-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-05-15 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: omniai
@@ -49,14 +49,40 @@ files:
49
49
  - bin/console
50
50
  - bin/setup
51
51
  - lib/omniai/tools.rb
52
+ - lib/omniai/tools/browser/base_driver.rb
52
53
  - lib/omniai/tools/browser/base_tool.rb
53
54
  - lib/omniai/tools/browser/button_click_tool.rb
55
+ - lib/omniai/tools/browser/element_click_tool.rb
56
+ - lib/omniai/tools/browser/elements/element_grouper.rb
57
+ - lib/omniai/tools/browser/elements/nearby_element_detector.rb
58
+ - lib/omniai/tools/browser/formatters/action_formatter.rb
59
+ - lib/omniai/tools/browser/formatters/data_entry_formatter.rb
60
+ - lib/omniai/tools/browser/formatters/element_formatter.rb
61
+ - lib/omniai/tools/browser/formatters/input_formatter.rb
54
62
  - lib/omniai/tools/browser/inspect_tool.rb
63
+ - lib/omniai/tools/browser/inspect_utils.rb
55
64
  - lib/omniai/tools/browser/link_click_tool.rb
65
+ - lib/omniai/tools/browser/page_inspect/button_summarizer.rb
66
+ - lib/omniai/tools/browser/page_inspect/form_summarizer.rb
67
+ - lib/omniai/tools/browser/page_inspect/html_summarizer.rb
68
+ - lib/omniai/tools/browser/page_inspect/link_summarizer.rb
69
+ - lib/omniai/tools/browser/page_inspect_tool.rb
70
+ - lib/omniai/tools/browser/page_screenshot_tool.rb
71
+ - lib/omniai/tools/browser/selector_generator.rb
72
+ - lib/omniai/tools/browser/selector_generator/base_selectors.rb
73
+ - lib/omniai/tools/browser/selector_generator/contextual_selectors.rb
74
+ - lib/omniai/tools/browser/selector_inspect_tool.rb
56
75
  - lib/omniai/tools/browser/text_field_area_set_tool.rb
57
76
  - lib/omniai/tools/browser/visit_tool.rb
58
- - lib/omniai/tools/database/base_tool.rb
59
- - lib/omniai/tools/database/sqlite_tool.rb
77
+ - lib/omniai/tools/browser/watir_driver.rb
78
+ - lib/omniai/tools/browser_tool.rb
79
+ - lib/omniai/tools/computer/base_driver.rb
80
+ - lib/omniai/tools/computer/mac_driver.rb
81
+ - lib/omniai/tools/computer_tool.rb
82
+ - lib/omniai/tools/database/base_driver.rb
83
+ - lib/omniai/tools/database/postgres_driver.rb
84
+ - lib/omniai/tools/database/sqlite_driver.rb
85
+ - lib/omniai/tools/database_tool.rb
60
86
  - lib/omniai/tools/disk/base_tool.rb
61
87
  - lib/omniai/tools/disk/directory_create_tool.rb
62
88
  - lib/omniai/tools/disk/directory_delete_tool.rb
@@ -92,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
118
  - !ruby/object:Gem::Version
93
119
  version: '0'
94
120
  requirements: []
95
- rubygems_version: 3.6.3
121
+ rubygems_version: 3.6.9
96
122
  specification_version: 4
97
123
  summary: A set of tools built for usage with OmniAI.
98
124
  test_files: []
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sqlite3"
4
-
5
- module OmniAI
6
- module Tools
7
- module Database
8
- # @example
9
- # tool = OmniAI::Tools::Database::SqliteTool.new
10
- # tool.execute(path: "./foo/bar")
11
- class BaseTool < OmniAI::Tool
12
- # @param logger [IO] An optional logger for debugging executed commands.
13
- def initialize(logger: Logger.new(IO::NULL))
14
- super()
15
- @logger = logger
16
- end
17
-
18
- # @example
19
- # tool = OmniAI::Tools::Database::BaseTool.new
20
- # tool.execute(statements: ["SELECT * FROM people"])
21
- #
22
- # @param statements [Array<String>]
23
- #
24
- # @return [Array<Hash>]
25
- def execute(statements:)
26
- [].tap do |executions|
27
- statements.map do |statement|
28
- execution = perform(statement:)
29
- executions << execution
30
- break unless execution[:status].eql?(:ok)
31
- end
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sqlite3"
4
-
5
- module OmniAI
6
- module Tools
7
- module Database
8
- # @example
9
- # tool = OmniAI::Tools::Database::SqliteTool.new
10
- # tool.execute(path: "./foo/bar")
11
- class SqliteTool < BaseTool
12
- description <<~TEXT
13
- Executes SQL commands (INSERT / UPDATE / SELECT / etc) on a database.
14
-
15
- Example:
16
-
17
- STATEMENTS:
18
-
19
- [
20
- 'CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT NOT NULL)',
21
- 'INSERT INTO people (name) VALUES ('John')',
22
- 'INSERT INTO people (name) VALUES ('Paul')',
23
- 'SELECT * FROM people',
24
- 'DROP TABLE people'
25
- ]
26
-
27
- RESULT:
28
-
29
- [
30
- {
31
- "status": "OK",
32
- "statement": "CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
33
- "result": "..."
34
- },
35
- {
36
- "status": "OK",
37
- "statement": "INSERT INTO people (name) VALUES ('John')"
38
- "result": "..."
39
- },
40
- {
41
- "status": "OK",
42
- "statement": "INSERT INTO people (name) VALUES ('Paul')",
43
- "result": "..."
44
- },
45
- {
46
- "status": "OK",
47
- "statement": "SELECT * FROM people",
48
- "result": "..."
49
- },
50
- {
51
- "status": "OK",
52
- "statement": "DROP TABLE people",
53
- "result": "..."
54
- }
55
- ]
56
- TEXT
57
-
58
- parameter(
59
- :statements,
60
- :array,
61
- description: "A list of SQL statements to run sequentially.",
62
- items: OmniAI::Schema.string(description: 'A SQL statement to run (e.g. "SELECT * FROM ...").')
63
- )
64
-
65
- required %i[statements]
66
-
67
- # @param logger [IO] An optional logger for debugging executed commands.
68
- # @param db [SQLite3::Database] A sqlite database.
69
- def initialize(db:, logger: Logger.new(IO::NULL))
70
- super(logger:)
71
- @db = db
72
- end
73
-
74
- # @example
75
- # tool = OmniAI::Tools::Database::BaseTool.new
76
- # tool.execute(statements: ["SELECT * FROM people"])
77
- #
78
- # @param statements [Array<String>]
79
- #
80
- # @return [Array<Hash>]
81
- def execute(statements:)
82
- @logger.info("#{self.class.name}#{__method__} statements=#{statements.inspect}")
83
-
84
- [].tap do |executions|
85
- statements.map do |statement|
86
- execution = perform(statement:)
87
- executions << execution
88
- break unless execution[:status].eql?(:ok)
89
- end
90
- end
91
- end
92
-
93
- protected
94
-
95
- # @param statement [String]
96
- #
97
- # @return [Hash]
98
- def perform(statement:)
99
- result = @db.execute2(statement)
100
-
101
- { status: :ok, statement:, result: }
102
- rescue ::SQLite3::Exception => e
103
- @logger.warn("ERROR: #{e.message}")
104
-
105
- { status: :error, statement:, result: e.message }
106
- end
107
- end
108
- end
109
- end
110
- end