omniai-tools 0.2.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04d1c28a8b8a92c2cc6c0ada9976d58ee2aed291fe25368356ce9a055b14cc7e
4
- data.tar.gz: 5afcc660cd72ef9ed7cea14fb6478a3239af14d8e519e3ba2bb89c32ccfaeb97
3
+ metadata.gz: f361e26584723aa8d30ba41edaefafb44a7e08c5c6345656dbf25e37c965448d
4
+ data.tar.gz: c718feeb39dfdd1c2c5ff9a2976ece26eec48aae12f52405724fc21209101d32
5
5
  SHA512:
6
- metadata.gz: f1331d0fc5ed4b89072e8d793620b7531d240556bca49be4a6a9b16e3368a96df3d8ca7a45c4e50198d97850b50ee13319875d4a8152ea8e7c93676d3eb81653
7
- data.tar.gz: 39ac4a40cbcafe9876e51c9a7a5f8b63c3d51c9bfc7f63afe6a47e209ed0ee6acbdb8d7c5845e3f9b7fab3ed6dac8eb946eda1a69d900f8a3d897cee67bb3b52
6
+ metadata.gz: fa14b3333edc0b235e046d9f408f75f201ea37be76435d897873fb7585999cd0aeac3d36c86c16e72a772ab6a28f48dd40560074767720736583de79a80d8847
7
+ data.tar.gz: 6ec511fc27bae83abdf0d827572ad2784419039b0d37d128d5670b19d58645a8e6efd82bfb7132397473181b56897f966314396a76172aaec33883826ff14f12
data/Gemfile CHANGED
@@ -16,4 +16,5 @@ gem "rubocop-factory_bot"
16
16
  gem "rubocop-rake"
17
17
  gem "rubocop-rspec"
18
18
  gem "simplecov"
19
+ gem "sqlite3"
19
20
  gem "yard"
data/README.md CHANGED
@@ -6,11 +6,59 @@
6
6
  [![Yard](https://img.shields.io/badge/docs-site-blue.svg)](https://omniai-tools.ksylvest.com)
7
7
  [![CircleCI](https://img.shields.io/circleci/build/github/ksylvest/omniai-tools)](https://circleci.com/gh/ksylvest/omniai-tools)
8
8
 
9
- `OmniAI::Tools` is a library of pre-built tools to simplify integrating common tasks w/ [OmniAI](https://github.com/ksylvest/omniai).
9
+ `OmniAI::Tools` is a library of pre-built tools to simplify integrating common tasks with [OmniAI](https://github.com/ksylvest/omniai).
10
+
11
+ ## Database
12
+
13
+ Database tools are focused on running SQL statements:
14
+
15
+ ```ruby
16
+ require "omniai/openai"
17
+ require "omniai/tools"
18
+
19
+ require "sqlite3"
20
+
21
+ db = SQLite3::Database.new(":memory:")
22
+
23
+ client = OmniAI::OpenAI::Client.new
24
+ logger = Logger.new($stdout)
25
+
26
+ tools = [
27
+ OmniAI::Tools::Database::SqliteTool.new(logger:, db:),
28
+ ]
29
+
30
+ puts "Type 'exit' or 'quit' to leave."
31
+
32
+ prompt = OmniAI::Chat::Prompt.build do |builder|
33
+ builder.system "Use tools to manage a database as requested."
34
+ end
35
+
36
+ loop do
37
+ print "# "
38
+ text = gets.strip
39
+ break if %w[exit quit].include?(text)
40
+
41
+ prompt.user(text)
42
+ response = client.chat(prompt, stream: $stdout, tools:)
43
+ prompt.assistant(response.text)
44
+ end
45
+ ```
46
+
47
+ ```
48
+ Type 'exit' or 'quit' to leave.
49
+ # Generate tables for artists, albums, songs, bands, members and populate it with data surrounding the Beatles.
50
+
51
+ CREATE TABLE artists (id INTEGER PRIMARY KEY, name TEXT NOT NULL)
52
+ CREATE TABLE bands (id INTEGER PRIMARY KEY, name TEXT NOT NULL)
53
+ CREATE TABLE members (id INTEGER PRIMARY KEY, artist_id INTEGER, band_id INTEGER, FOREIGN KEY(artist_id) REFERENCES artists(id), FOREIGNKEY(band_id) REFERENCES bands(id))"
54
+ CREATE TABLE albums (id INTEGER PRIMARY KEY, title TEXT NOT NULL, band_id INTEGER, FOREIGN KEY(band_id) REFERENCES bands(id))
55
+ CREATE TABLE songs (id INTEGER PRIMARY KEY, title TEXT NOT NULL, album_id INTEGER, FOREIGN KEY(album_id) REFERENCES albums(id))
56
+ ...
57
+ ```
10
58
 
11
59
  ## Disk
12
60
 
13
- Disk tools are focused on creating / updating / deleting files and directories within a "root":
61
+ Disk tools are focused on creating, updating, and deleting files and directories within a "root":
14
62
 
15
63
  ```ruby
16
64
  require "omniai/openai"
@@ -53,7 +101,7 @@ end
53
101
 
54
102
  ## Docker
55
103
 
56
- Disk tools are focused on running commands through Docker via compose:
104
+ Docker tools are focused on running commands through Docker via Compose:
57
105
 
58
106
  ```ruby
59
107
  require "omniai/openai"
@@ -72,7 +120,7 @@ tools = [
72
120
  puts "Type 'exit' or 'quit' to leave."
73
121
 
74
122
  prompt = OmniAI::Chat::Prompt.build do |builder|
75
- builder.system "Use tools to interact with docker."
123
+ builder.system "Use tools to interact with Docker."
76
124
  end
77
125
 
78
126
  loop do
@@ -0,0 +1,37 @@
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
@@ -0,0 +1,110 @@
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
+ [].tap do |executions|
83
+ statements.map do |statement|
84
+ execution = perform(statement:)
85
+ executions << execution
86
+ break unless execution[:status].eql?(:ok)
87
+ end
88
+ end
89
+ end
90
+
91
+ protected
92
+
93
+ # @param statement [String]
94
+ #
95
+ # @return [Hash]
96
+ def perform(statement:)
97
+ @logger.info(%(#{self.class.name}#perform statement=#{statement.inspect}))
98
+
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
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::DirectoryCreateTool.new(root: "./project")
8
+ # tool.execute(path: "./foo/bar")
9
+ class DirectoryCreateTool < BaseTool
10
+ description "Creates a directory."
11
+
12
+ parameter :old_path, :string, description: "a path (e.g. `./old`)"
13
+ parameter :new_path, :string, description: "a path (e.g. `./new`)"
14
+
15
+ required %i[old_path new_path]
16
+
17
+ # @param old_path [String]
18
+ # @param new_path [String]
19
+ #
20
+ # @return [String]
21
+ def execute(old_path:, new_path:)
22
+ @logger.info("#{self.class.name}#execute path=#{path.inspect}")
23
+
24
+ FileUtils.mv(old_path, new_path)
25
+ rescue SecurityError => e
26
+ @logger.error(e.message)
27
+ raise e
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -7,9 +7,9 @@ module OmniAI
7
7
  # tool = OmniAI::Tools::Disk::FileDeleteTool.new(root: "./project")
8
8
  # tool.execute(path: "./README.md")
9
9
  class FileDeleteTool < BaseTool
10
- description "reads the contents of a file"
10
+ description "Deletes a file."
11
11
 
12
- parameter :path, :string, description: "a path to the directory (e.g. `./foo/bar`)"
12
+ parameter :path, :string, description: "a path to the file (e.g. `./README.md`)"
13
13
 
14
14
  required %i[path]
15
15
 
@@ -4,13 +4,13 @@ module OmniAI
4
4
  module Tools
5
5
  module Disk
6
6
  # @example
7
- # tool = OmniAI::Tools::Disk::FileDeleteTool.new(root: "./project")
7
+ # tool = OmniAI::Tools::Disk::FileMoveTool.new(root: "./project")
8
8
  # tool.execute(
9
9
  # old_path: "./README.txt",
10
10
  # new_path: "./README.md",
11
11
  # )
12
12
  class FileMoveTool < BaseTool
13
- description "moves a file"
13
+ description "Moves a file."
14
14
 
15
15
  parameter :old_path, :string, description: "a path (e.g. `./old.rb`)"
16
16
  parameter :new_path, :string, description: "a path (e.g. `./new.rb`)"
@@ -7,7 +7,7 @@ module OmniAI
7
7
  # tool = OmniAI::Tools::Disk::FileReadTool.new(root: "./project")
8
8
  # tool.execute(path: "./README.md") # => "..."
9
9
  class FileReadTool < BaseTool
10
- description "reads the contents of a file"
10
+ description "Reads the contents of a file."
11
11
 
12
12
  parameter :path, :string, description: "a path (e.g. `./main.rb`)"
13
13
 
@@ -11,7 +11,7 @@ module OmniAI
11
11
  # path: "README.md"
12
12
  # )
13
13
  class FileReplaceTool < BaseTool
14
- description "replace a specific string in a file (old_text => new_text)"
14
+ description "Replaces a specific string in a file (old_text => new_text)."
15
15
 
16
16
  parameter :old_text, :string, description: "the old text (e.g. `puts 'ABC'`)"
17
17
  parameter :new_text, :string, description: "the new text (e.g. `puts 'DEF'`)"
@@ -7,7 +7,7 @@ module OmniAI
7
7
  # tool = OmniAI::Tools::Disk::FileWriteTool.new(root: "./project")
8
8
  # tool.execute(path: "./README.md", text: "Hello World")
9
9
  class FileWriteTool < BaseTool
10
- description "Write the contents of a file."
10
+ description "Writes the contents of a file."
11
11
 
12
12
  parameter :path, :string, description: "a path for the file (e.g. `./main.rb`)"
13
13
  parameter :text, :string, description: "the text to write to the file (e.g. `puts 'Hello World'`)"
@@ -7,7 +7,7 @@ module OmniAI
7
7
  # tool = OmniAI::Tools::Disk::SummaryTool.new(root: "./project")
8
8
  # tool.execute
9
9
  class SummaryTool < BaseTool
10
- description "Summarize the contents (files and directories) of the project."
10
+ description "Summarizes the contents (files and directories) of the project."
11
11
 
12
12
  # @return [String]
13
13
  def execute
@@ -9,12 +9,12 @@ module OmniAI
9
9
  # tool = OmniAI::Tools::Docker::ComposeRunTool.new(root: "./project")
10
10
  # tool.execute(service: "app", command: "rspec" args: ["spec/main_spec.rb"])
11
11
  class ComposeRunTool < BaseTool
12
- description "Run a command via docker with arguments on the project (e.g. `rspec spec/main_spec.rb`)."
12
+ description "Runs a command via Docker with arguments on the project (e.g. `rspec spec/main_spec.rb`)."
13
13
 
14
- parameter :service, :string, description: "the service to run the command against (e.g. `app`)"
15
- parameter :command, :string, description: "the command to run (e.g. `rspec`)"
16
- parameter :args, :array, description: "the arguments for the command",
17
- items: OmniAI::Tool::Property.string(description: "an argument for the command (e.g. `spec/main_spec.rb`)")
14
+ parameter :service, :string, description: "The service to run the command on (e.g. `app`)."
15
+ parameter :command, :string, description: "The command to run (e.g. `rspec`)."
16
+ parameter :args, :array, description: "The arguments for the command.",
17
+ items: OmniAI::Schema.string(description: "An argument for the command (e.g. `spec/main_spec.rb`).")
18
18
 
19
19
  # @param service [String]
20
20
  # @param command [String]
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAI
4
4
  module Tools
5
- VERSION = "0.2.0"
5
+ VERSION = "0.4.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.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-23 00:00:00.000000000 Z
10
+ date: 2025-05-14 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: omniai
@@ -49,9 +49,12 @@ files:
49
49
  - bin/console
50
50
  - bin/setup
51
51
  - lib/omniai/tools.rb
52
+ - lib/omniai/tools/database/base_tool.rb
53
+ - lib/omniai/tools/database/sqlite_tool.rb
52
54
  - lib/omniai/tools/disk/base_tool.rb
53
55
  - lib/omniai/tools/disk/directory_create_tool.rb
54
56
  - lib/omniai/tools/disk/directory_delete_tool.rb
57
+ - lib/omniai/tools/disk/directory_move_tool.rb
55
58
  - lib/omniai/tools/disk/file_create_tool.rb
56
59
  - lib/omniai/tools/disk/file_delete_tool.rb
57
60
  - lib/omniai/tools/disk/file_move_tool.rb