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 +7 -0
- data/CHANGELOG.md +5 -0
- data/README.md +90 -0
- data/lib/agent_ruby/action.rb +22 -0
- data/lib/agent_ruby/agent.rb +50 -0
- data/lib/agent_ruby/configuration.rb +13 -0
- data/lib/agent_ruby/message.rb +23 -0
- data/lib/agent_ruby/prompt/create_task.rb +61 -0
- data/lib/agent_ruby/prompt/summarize.rb +33 -0
- data/lib/agent_ruby/result.rb +16 -0
- data/lib/agent_ruby/step/create_task.rb +55 -0
- data/lib/agent_ruby/step/summarize.rb +34 -0
- data/lib/agent_ruby/task.rb +18 -0
- data/lib/agent_ruby/version.rb +5 -0
- data/lib/agent_ruby.rb +25 -0
- metadata +69 -0
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
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
|
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: []
|