agent-ruby 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9d7ffbd12b2c6b7c1d24f998b6039c1bef1f347c48c4d94ef5d9fed0f23ffa45
4
+ data.tar.gz: 52be74c3c3cede8ea409ab054f3c1f74803f77ec4aa72319bcd2e4cf7e0d7695
5
+ SHA512:
6
+ metadata.gz: d7892d9b9bc99dcc661dd6fab28c9b18254af7559e5a1f055afde012416c73fc07572a7af699123ba721bbc9bb466800fd67aa7ead10a657855965f02790a90d
7
+ data.tar.gz: 61ec01fc12b12e55c41cbc132f0a95b71ff17d006613169eb7c14c0390c971c907aa60451d6f8dfc95e41818721e4f4319350e4ef0036d0f32570708b3052964
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-04-11
4
+
5
+ - Initial release
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # AgentRuby
2
+
3
+ AgentRuby is a Ruby library that allows you to create and manage workflows using LLMs (Large Language Models). It provides a simple and intuitive interface for defining tasks, managing dependencies, and executing workflows.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ ```bash
10
+ bundle add agent-ruby
11
+ ```
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ ```bash
16
+ gem install agent-ruby
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require 'agent-ruby'
23
+
24
+ class CreateNoteTask < AgentRuby::Task
25
+ argument :title, String
26
+ argument :content, String
27
+ argument :folder_id, Integer
28
+
29
+ def perform(title:, content:, folder_id:)
30
+ Post.create(title:, content:, folder_id:)
31
+ end
32
+ end
33
+
34
+ class CreateFolderTask < AgentRuby::Task
35
+ argument :name, String
36
+ argument :path, String
37
+
38
+ def perform(name:, path:)
39
+ Folder.create(name:, path:)
40
+ end
41
+ end
42
+
43
+ class WikiAgent < AgentRuby::Agent
44
+ action CreateNoteAction
45
+ action CreateFolderAction
46
+ end
47
+
48
+ agent = WikiAgent.new("claude-3-7-sonnet-20250219")
49
+ agent.hey(
50
+ <<~TEXT
51
+ - Create a folder named "My Documents"
52
+ - Create a post titled "My First Post" with content "Hello, world!" in the folder "My Documents"
53
+ TEXT
54
+ )
55
+ puts agent.messages.last.content
56
+
57
+ # agent.hey("...") do |chunk|
58
+ # puts chunk
59
+ # end
60
+ ```
61
+
62
+ ## Models
63
+
64
+ ```ruby
65
+ RubyLLM.configure do |config|
66
+ config.openai_api_key = ENV.fetch('OPENAI_API_KEY', nil)
67
+ config.anthropic_api_key = ENV.fetch('ANTHROPIC_API_KEY', nil)
68
+ config.gemini_api_key = ENV.fetch('GEMINI_API_KEY', nil)
69
+ config.deepseek_api_key = ENV.fetch('DEEPSEEK_API_KEY', nil)
70
+
71
+ config.bedrock_api_key = ENV.fetch('AWS_ACCESS_KEY_ID', nil)
72
+ config.bedrock_secret_key = ENV.fetch('AWS_SECRET_ACCESS_KEY', nil)
73
+ config.bedrock_region = ENV.fetch('AWS_REGION', nil)
74
+ config.bedrock_session_token = ENV.fetch('AWS_SESSION_TOKEN', nil)
75
+ end
76
+ ```
77
+
78
+ ## Development
79
+
80
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
81
+
82
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
83
+
84
+ ## Contributing
85
+
86
+ Bug reports and pull requests are welcome on GitHub at https://github.com/moekiorg/agent-ruby. 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/agent-ruby/agent-ruby/blob/main/CODE_OF_CONDUCT.md).
87
+
88
+ ## Code of Conduct
89
+
90
+ Everyone interacting in the He project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/agent-ruby/agent-ruby/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ class Action
5
+ class << self
6
+ attr_accessor :arguments
7
+
8
+ def argument(name, type)
9
+ @arguments ||= []
10
+ @arguments << { name:, type: }
11
+ end
12
+ end
13
+
14
+ def perform(**args)
15
+ raise NotImplementedError, "You must implement the perform method"
16
+ end
17
+
18
+ def key
19
+ self.class.name
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ class Agent
5
+ class << self
6
+ attr_reader :actions
7
+
8
+ def action(klass)
9
+ @actions ||= []
10
+ @actions << klass
11
+ end
12
+ end
13
+
14
+ attr_accessor :chat, :steps
15
+
16
+ def initialize(model = nil)
17
+ @chat = RubyLLM.chat(model:)
18
+ @steps = []
19
+ end
20
+
21
+ def hey(message, &block)
22
+ creation = nil
23
+ results = []
24
+ while 1
25
+ creation = Step::CreateTask.new(
26
+ actions: self.class.actions.map(&:new),
27
+ chat: @chat,
28
+ message:, block:,
29
+ results:,
30
+ )
31
+ created = creation.perform
32
+ task = creation.task
33
+ result = task.result
34
+ results << result
35
+ @steps << created
36
+ if creation.metadata[:continuation].to_s == "false"
37
+ break
38
+ end
39
+ end
40
+ summarize = Step::Summarize.new(
41
+ chat: @chat,
42
+ results:,
43
+ block:,
44
+ )
45
+ summary = summarize.perform
46
+ @steps << summary
47
+ @steps.map(&:content)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ RubyLLM.configure do |config|
4
+ config.openai_api_key = ENV.fetch("OPENAI_API_KEY", nil)
5
+ config.anthropic_api_key = ENV.fetch("ANTHROPIC_API_KEY", nil)
6
+ config.gemini_api_key = ENV.fetch("GEMINI_API_KEY", nil)
7
+ config.deepseek_api_key = ENV.fetch("DEEPSEEK_API_KEY", nil)
8
+
9
+ config.bedrock_api_key = ENV.fetch("AWS_ACCESS_KEY_ID", nil)
10
+ config.bedrock_secret_key = ENV.fetch("AWS_SECRET_ACCESS_KEY", nil)
11
+ config.bedrock_region = ENV.fetch("AWS_REGION", nil)
12
+ config.bedrock_session_token = ENV.fetch("AWS_SESSION_TOKEN", nil)
13
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ class Message
5
+ attr_accessor :messages
6
+
7
+ def initialize
8
+ @messages = []
9
+ end
10
+
11
+ def <<(message)
12
+ @messages << message
13
+ end
14
+
15
+ def to_s
16
+ @messages.join("\n")
17
+ end
18
+
19
+ def clear
20
+ @messages.clear
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ module Prompt
5
+ class CreateTask
6
+ def initialize(actions:, results:)
7
+ @actions = actions
8
+ @results = results
9
+ end
10
+
11
+ def to_s
12
+ <<~XML
13
+ <Instructions>
14
+ You are a workflow agent. Your task is to create a workflow based on the user's instructions.
15
+ `<Message>` tags contain your message to the user.
16
+ `<Metadata>` tags contain metadata about the workflow. If the workflow will continue, set the `continuation` key to `true`. If you think that the workflow is finished when the task is completed, set the `continuation` key to `false`.
17
+ `<Task>` tags contain the tasks to be performed. This tag must be present one time. Task must contain object not array.
18
+ </Instructions>
19
+
20
+ <Options>
21
+ #{
22
+ @actions.map { |action|
23
+ {
24
+ key: action.key,
25
+ params: action.class.arguments.map { _1.transform_values { |v| v.name } }
26
+ }
27
+ }.to_json
28
+ }
29
+ </Options>
30
+
31
+ <Example type="success">
32
+ <Message>
33
+ I will create a folder named "My Folder".
34
+ </Message>
35
+
36
+ <Metadata>
37
+ { "continuation": false | true }
38
+ </Metadata>
39
+
40
+ <Task>
41
+ {
42
+ "id": 1,
43
+ "key": "create_folder",
44
+ "params": {
45
+ "name": "My Folder",
46
+ "path": "My Folder"
47
+ }
48
+ }
49
+ </Task>
50
+ </Example>
51
+
52
+ <Example type="error">
53
+ <Error>
54
+ <Message>There was an error creating the folder</Message>
55
+ </Error>
56
+ </Example>
57
+ XML
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ module Prompt
5
+ class Summarize
6
+ def initialize(results:)
7
+ @results = results
8
+ end
9
+
10
+ def to_s
11
+ <<~XML
12
+ <Instruction>
13
+ <Item>
14
+ The workflow has been executed. Here are the results:
15
+ #{
16
+ @results.map { |r|
17
+ "Task: #{r.action.key}, Params: #{r.action.class.arguments.to_json}, Result: #{r.result}"
18
+ }.join("\n")
19
+ }
20
+ Now, please summarize the results in a single JSON object
21
+ </Item>
22
+ </Instruction>
23
+
24
+ <Example type="success">
25
+ <Message>
26
+ I have created a folder named "My Folder" and a post titled "My Post" with content "This is my post" in the folder "My Folder"
27
+ </Message>
28
+ </Example>
29
+ XML
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ class Result
5
+ attr_reader :action, :result
6
+
7
+ def initialize(action:, result:)
8
+ @action = action
9
+ @result = result
10
+ end
11
+
12
+ def self.find(all, id)
13
+ all.find { |r| r.action.id == id.to_i }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ module Step
5
+ class CreateTask
6
+ def initialize(actions:, chat:, message:, results:, block: nil)
7
+ @message = message
8
+ @results = results
9
+ @block = block
10
+ @actions = actions
11
+ @chat = chat
12
+ @response = nil
13
+ end
14
+
15
+ def perform
16
+ @response = @chat.ask([
17
+ @message,
18
+ Prompt::CreateTask.new(actions: @actions, results: @results).to_s,
19
+ ].join) do |chunk|
20
+ @block.call(chunk) if @block
21
+ end
22
+
23
+ if @response.content.match?(/<Error>/)
24
+ raise Error, "Error: #{@response.content.match(/<Message>(.+)<\/Message>/m)[1]}"
25
+ end
26
+
27
+ unless @response.content.match?(/<Message>(.+)<\/Message>/m)
28
+ raise NoMessageError, "Error: No message found in the response"
29
+ end
30
+
31
+ unless @response.content.match?(/<Task>(.+)<\/Task>/m)
32
+ raise NoTaskError, "Error: No task found in the response"
33
+ end
34
+
35
+ @response
36
+ end
37
+
38
+ def task
39
+ Task.new(**JSON.parse(@response.content.match(/<Task>(.+)<\/Task>/m)[1], symbolize_names: true))
40
+ end
41
+
42
+ def message
43
+ @response.content.match(/<Message>(.+)<\/Message>/m)[1]
44
+ end
45
+
46
+ def metadata
47
+ if @response.content.match(/<Metadata>(.+)<\/Metadata>/m)
48
+ JSON.parse(@response.content.match(/<Metadata>(.+)<\/Metadata>/m)[1], symbolize_names: true)
49
+ else
50
+ {}
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ module Step
5
+ class Summarize
6
+ def initialize(chat:, results:, block: nil)
7
+ @block = block
8
+ @chat = chat
9
+ @results = results
10
+ end
11
+
12
+ def perform
13
+ prompt = Prompt::Summarize.new(results: @results)
14
+ @response = @chat.ask(prompt.to_s) do |chunk|
15
+ @block.call(chunk) if @block
16
+ end
17
+
18
+ if @response.content.match?(/<Error>/)
19
+ raise Error, "Error: #{@response.content.match(/<Message>(.+)<\/Message>/m)[1]}"
20
+ end
21
+
22
+ unless @response.content.match?(/<Message>(.+)<\/Message>/m)
23
+ raise NoMessageError, "Error: No message found in the response"
24
+ end
25
+
26
+ @response
27
+ end
28
+
29
+ def message
30
+ @response.content.match(/<Message>(.+)<\/Message>/m)[1]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ class Task
5
+ attr_accessor :id, :key, :params
6
+
7
+ def initialize(id:, key:, params: {})
8
+ @id = id
9
+ @key = key
10
+ @params = params
11
+ end
12
+
13
+ def result
14
+ action = Object.const_get(key).new
15
+ Result.new(action:, result: action.perform(**@params))
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentRuby
4
+ VERSION = "0.1.0"
5
+ end
data/lib/agent_ruby.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby_llm"
4
+ require "dotenv/load"
5
+ require_relative "agent_ruby/version"
6
+ require_relative "agent_ruby/prompt/summarize"
7
+ require_relative "agent_ruby/step/summarize"
8
+ require_relative "agent_ruby/prompt/create_task"
9
+ require_relative "agent_ruby/step/create_task"
10
+ require_relative "agent_ruby/agent"
11
+ require_relative "agent_ruby/task"
12
+ require_relative "agent_ruby/message"
13
+ require_relative "agent_ruby/configuration"
14
+ require_relative "agent_ruby/result"
15
+ require_relative "agent_ruby/action"
16
+
17
+ module AgentRuby
18
+ class Error < StandardError; end
19
+ class NoMessageError < StandardError; end
20
+ class NoTaskError < StandardError; end
21
+
22
+ def self.new(model = nil)
23
+ AgentRuby::Agent.new(model)
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agent-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - KAWAKAMI Moeki
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-04-12 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ruby_llm
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.0'
26
+ description: AgentRuby is a Ruby library that allows you to create and manage workflows
27
+ using LLMs (Large Language Models). It provides a simple and intuitive interface
28
+ for defining tasks, managing dependencies, and executing workflows.
29
+ email:
30
+ - hi@moeki.org
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - CHANGELOG.md
36
+ - README.md
37
+ - lib/agent_ruby.rb
38
+ - lib/agent_ruby/action.rb
39
+ - lib/agent_ruby/agent.rb
40
+ - lib/agent_ruby/configuration.rb
41
+ - lib/agent_ruby/message.rb
42
+ - lib/agent_ruby/prompt/create_task.rb
43
+ - lib/agent_ruby/prompt/summarize.rb
44
+ - lib/agent_ruby/result.rb
45
+ - lib/agent_ruby/step/create_task.rb
46
+ - lib/agent_ruby/step/summarize.rb
47
+ - lib/agent_ruby/task.rb
48
+ - lib/agent_ruby/version.rb
49
+ homepage: https://github.com/moekiorg/agent-ruby
50
+ licenses: []
51
+ metadata: {}
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubygems_version: 3.6.6
67
+ specification_version: 4
68
+ summary: LLM Workflow Agent for Ruby
69
+ test_files: []