autoflux 0.3.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 +4 -4
- data/.rubocop.yml +1 -1
- data/README.md +19 -99
- data/lib/autoflux/step/agent.rb +22 -0
- data/lib/autoflux/step/command.rb +1 -7
- data/lib/autoflux/step.rb +1 -2
- data/lib/autoflux/version.rb +1 -1
- data/lib/autoflux/workflow.rb +7 -15
- data/lib/autoflux.rb +0 -6
- data/sig/autoflux/agent.rbs +1 -3
- data/sig/autoflux/step.rbs +4 -12
- data/sig/autoflux/workflow.rbs +2 -4
- data/sig/autoflux.rbs +0 -5
- metadata +4 -9
- data/lib/autoflux/memory.rb +0 -28
- data/lib/autoflux/step/assistant.rb +0 -24
- data/lib/autoflux/step/tool.rb +0 -39
- data/sig/autoflux/event.rbs +0 -27
- data/sig/autoflux/memory.rbs +0 -20
- data/sig/autoflux/tool.rbs +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85b6aa4923d77e37e9e4c0323b9d7b4ea211d1c826b832f439ffef41fc2838a4
|
4
|
+
data.tar.gz: 1f83f0a1a66cca3f03fdd0905625406e87fb5e43f9c8a8a467d4508d1fb2a450
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5014bfa1497302ff5a839f2ed914ca483af355faf5c33f6f5f543e2e3a4729a09515c58e473b6ecb2e6e03dd345a7354371728475f2fbee5998d1db514b7a465
|
7
|
+
data.tar.gz: 95a4935d4ca50f9817412184781ce486b5273ba349be6441f5842f746b8f61543f849d5c3d23fb39375e02f265078e7596809d7599a4a6135d76c2b16725950b
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -28,11 +28,9 @@ Autoflux provides a default state machine for a chat workflow.
|
|
28
28
|
stateDiagram-v2
|
29
29
|
[*] --> Start
|
30
30
|
Start --> Command
|
31
|
-
Command -->
|
32
|
-
Assistant --> Command
|
33
|
-
Assistant --> Tool
|
34
|
-
Tool --> Assistant
|
31
|
+
Command --> Agent
|
35
32
|
Command --> Stop
|
33
|
+
Agent --> Command
|
36
34
|
Stop --> [*]
|
37
35
|
```
|
38
36
|
|
@@ -47,116 +45,38 @@ workflow = Autoflux::Workflow.new(
|
|
47
45
|
workflow.run
|
48
46
|
```
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
```ruby
|
53
|
-
workflow.run(system_prompt: 'Help user to solve the problem')
|
54
|
-
```
|
55
|
-
|
56
|
-
When receive "exit" from the user, the workflow transition to the stop state.
|
48
|
+
When the `io` is `EOF`, the workflow will stop.
|
57
49
|
|
58
50
|
### Agent
|
59
51
|
|
60
|
-
The agent is
|
52
|
+
The agent is a interface implements `call` method to process the command.
|
61
53
|
|
62
54
|
```ruby
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
55
|
+
agent = ->(prompt, **context) {
|
56
|
+
case prompt
|
57
|
+
when 'hello'
|
58
|
+
'Hello, how can I help you?'
|
59
|
+
when 'bye'
|
60
|
+
'Goodbye'
|
61
|
+
else
|
62
|
+
'I do not understand'
|
71
63
|
end
|
72
|
-
|
73
|
-
def call(memory:)
|
74
|
-
msg = client.chat(
|
75
|
-
parameters: {
|
76
|
-
model: model,
|
77
|
-
messages: memory.data.map { |event| convert_message(event) },
|
78
|
-
}
|
79
|
-
).dig('choices', 0, 'message')
|
80
|
-
|
81
|
-
convert_event(msg)
|
82
|
-
end
|
83
|
-
|
84
|
-
# If allow to use the tool, return tool object implements `Autoflux::_Tool` interface
|
85
|
-
def tools?(name) = false
|
86
|
-
def tool(name) = nil
|
87
|
-
|
88
|
-
# Autoflux use a generic event object to represent the message, you have to convert it to the model's format
|
89
|
-
def convert_message(event)
|
90
|
-
{
|
91
|
-
role: event[:role],
|
92
|
-
content: event[:content]
|
93
|
-
}.tap do |evt|
|
94
|
-
evt[:tool_calls] = event[:invocations]&.map { |invocation| convert_tool_call(invocation) }
|
95
|
-
evt[:tool_call_id] = event[:invocation_id] if event[:invocation_id]
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def convert_tool_call(invocation)
|
100
|
-
{
|
101
|
-
type: :function,
|
102
|
-
id: invocation[:id],
|
103
|
-
function: {
|
104
|
-
name: invocation[:name],
|
105
|
-
arguments: invocation[:args]
|
106
|
-
}
|
107
|
-
}
|
108
|
-
end
|
109
|
-
|
110
|
-
def convert_event(msg) # rubocop:disable Metrics/MethodLength
|
111
|
-
{
|
112
|
-
role: msg['role'],
|
113
|
-
content: msg['content'],
|
114
|
-
invocations: msg['tool_calls']&.map do |call|
|
115
|
-
{
|
116
|
-
id: call['id'],
|
117
|
-
name: call.dig('function', 'name'),
|
118
|
-
args: call.dig('function', 'arguments')
|
119
|
-
}
|
120
|
-
end
|
121
|
-
}
|
122
|
-
end
|
123
|
-
end
|
64
|
+
}
|
124
65
|
```
|
125
66
|
|
126
|
-
The
|
127
|
-
|
128
|
-
### Tool
|
129
|
-
|
130
|
-
The tool is a function that can be used in the agent's response.
|
67
|
+
The workflow will pass itself as context to the agent.
|
131
68
|
|
132
69
|
```ruby
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
def initialize # rubocop:disable Metrics/MethodLength
|
138
|
-
@name = 'add_to_cart'
|
139
|
-
@description = 'Add the product to the cart'
|
140
|
-
@parameters = {
|
141
|
-
type: 'object',
|
142
|
-
properties: {
|
143
|
-
name: { type: 'string', description: 'The name of the product' },
|
144
|
-
quantity: { type: 'number', description: 'The quantity of the product' }
|
145
|
-
}
|
146
|
-
}
|
147
|
-
end
|
148
|
-
|
149
|
-
def call(workflow:, params:)
|
150
|
-
{ success: true, content: "Added #{params[:quantity]} #{params[:name]} to the cart" }
|
151
|
-
end
|
152
|
-
end
|
70
|
+
agent = ->(prompt, workflow:, **) {
|
71
|
+
workflow.io.write("User: #{prompt}")
|
72
|
+
}
|
153
73
|
```
|
154
74
|
|
155
|
-
|
75
|
+
Workflow never knows the how the agent works and which tool is used.
|
156
76
|
|
157
77
|
### IO
|
158
78
|
|
159
|
-
The IO is an adapter to the
|
79
|
+
The IO is an adapter to let the workflow interact with the user.
|
160
80
|
|
161
81
|
```ruby
|
162
82
|
# :nodoc:
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Autoflux
|
4
|
+
module Step
|
5
|
+
# The Agent is transfer control to the agent and wait for the next event
|
6
|
+
class Agent
|
7
|
+
attr_reader :prompt
|
8
|
+
|
9
|
+
def initialize(prompt: nil)
|
10
|
+
@prompt = prompt
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s = self.class.name || "Agent"
|
14
|
+
|
15
|
+
def call(workflow:)
|
16
|
+
res = workflow.agent.call(prompt, workflow: workflow)
|
17
|
+
workflow.io.write(res.to_s)
|
18
|
+
Command.new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -4,19 +4,13 @@ module Autoflux
|
|
4
4
|
module Step
|
5
5
|
# The Command step is used to get the user input.
|
6
6
|
class Command
|
7
|
-
EXIT_COMMAND = "exit"
|
8
|
-
|
9
7
|
def to_s = self.class.name || "Command"
|
10
8
|
|
11
9
|
def call(workflow:)
|
12
10
|
input = workflow.io.read
|
13
11
|
return Stop.new if input.nil?
|
14
|
-
return Stop.new if input == EXIT_COMMAND
|
15
12
|
|
16
|
-
|
17
|
-
event = { role: ROLE_USER, content: input }
|
18
|
-
workflow.memory.push(event)
|
19
|
-
Assistant.new
|
13
|
+
Agent.new(prompt: input)
|
20
14
|
end
|
21
15
|
end
|
22
16
|
end
|
data/lib/autoflux/step.rb
CHANGED
data/lib/autoflux/version.rb
CHANGED
data/lib/autoflux/workflow.rb
CHANGED
@@ -18,37 +18,29 @@ module Autoflux
|
|
18
18
|
|
19
19
|
include Enumerable
|
20
20
|
|
21
|
-
attr_reader :id, :agent, :
|
21
|
+
attr_reader :id, :agent, :io
|
22
22
|
|
23
23
|
# @rbs state: State
|
24
|
-
def initialize(agent:, io:, id: nil, step: Step::Start.new
|
24
|
+
def initialize(agent:, io:, id: nil, step: Step::Start.new)
|
25
25
|
@id = id || Workflow.next_id
|
26
26
|
@agent = agent
|
27
27
|
@io = io
|
28
28
|
@step = step
|
29
|
-
@memory = memory
|
30
29
|
end
|
31
30
|
|
32
31
|
def each
|
33
32
|
return to_enum(:each) unless block_given?
|
34
33
|
|
35
|
-
|
36
|
-
|
34
|
+
loop do
|
35
|
+
break unless @step
|
36
|
+
|
37
|
+
yield self
|
37
38
|
@step = step.call(workflow: self)
|
38
|
-
yield self if @step
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
# Run the workflow.
|
43
|
-
|
44
|
-
# @rbs system_prompt: String?
|
45
|
-
def run(system_prompt: nil, &block)
|
46
|
-
if system_prompt
|
47
|
-
# @type var event: Autoflux::event
|
48
|
-
event = { role: ROLE_SYSTEM, content: system_prompt }
|
49
|
-
memory.push(event)
|
50
|
-
end
|
51
|
-
|
43
|
+
def run(&block)
|
52
44
|
each(&block || ->(_workflow) {})
|
53
45
|
end
|
54
46
|
|
data/lib/autoflux.rb
CHANGED
@@ -6,12 +6,6 @@ require_relative "autoflux/version"
|
|
6
6
|
module Autoflux
|
7
7
|
class Error < StandardError; end
|
8
8
|
|
9
|
-
ROLE_SYSTEM = "system"
|
10
|
-
ROLE_ASSISTANT = "assistant"
|
11
|
-
ROLE_TOOL = "tool"
|
12
|
-
ROLE_USER = "user"
|
13
|
-
|
14
|
-
require_relative "autoflux/memory"
|
15
9
|
require_relative "autoflux/step"
|
16
10
|
require_relative "autoflux/workflow"
|
17
11
|
end
|
data/sig/autoflux/agent.rbs
CHANGED
data/sig/autoflux/step.rbs
CHANGED
@@ -5,32 +5,24 @@ module Autoflux
|
|
5
5
|
end
|
6
6
|
|
7
7
|
module Step
|
8
|
-
# The Start step is used to start the workflow.
|
9
8
|
class Start
|
10
9
|
include Autoflux::_Step
|
11
10
|
end
|
12
11
|
|
13
|
-
# The Stop step is used to stop the workflow.
|
14
12
|
class Stop
|
15
13
|
include Autoflux::_Step
|
16
14
|
end
|
17
15
|
|
18
|
-
|
19
|
-
class Assistant
|
16
|
+
class Agent
|
20
17
|
include Autoflux::_Step
|
21
|
-
OUTPUT_ROLE_NAME: "assistant"
|
22
|
-
end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def run: (workflow: Workflow, invocation: invocation) -> ::_ToJson
|
19
|
+
attr_reader prompt: String?
|
20
|
+
|
21
|
+
def initialize: (?prompt: String) -> void
|
28
22
|
end
|
29
23
|
|
30
|
-
# The Command step is used to get the user input.
|
31
24
|
class Command
|
32
25
|
include Autoflux::_Step
|
33
|
-
EXIT_COMMAND: "exit"
|
34
26
|
end
|
35
27
|
end
|
36
28
|
end
|
data/sig/autoflux/workflow.rbs
CHANGED
@@ -7,18 +7,16 @@ module Autoflux
|
|
7
7
|
@agent: _Agent
|
8
8
|
@io: _IO
|
9
9
|
@step: _Step
|
10
|
-
@memory: _Memory
|
11
10
|
|
12
11
|
def self.next_id: () -> String
|
13
12
|
|
14
13
|
attr_reader id: String
|
15
14
|
attr_reader agent: _Agent
|
16
|
-
attr_reader memory: _Memory
|
17
15
|
attr_reader io: _IO
|
18
16
|
|
19
|
-
def initialize: (agent: _Agent, io: _IO, ?id: String?, ?step: _Step
|
17
|
+
def initialize: (agent: _Agent, io: _IO, ?id: String?, ?step: _Step) -> void
|
20
18
|
def each: { (Workflow) -> void } -> void
|
21
|
-
def run: (
|
19
|
+
def run: () ?{ (Workflow) -> void } -> void
|
22
20
|
def stop: () -> void
|
23
21
|
def step: () -> _Step
|
24
22
|
def to_h: () -> { id: String, step: String }
|
data/sig/autoflux.rbs
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: autoflux
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aotokitsuruya
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-11 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A lightweight AI agent framework for Ruby
|
14
14
|
email:
|
@@ -27,26 +27,21 @@ files:
|
|
27
27
|
- Steepfile
|
28
28
|
- commitlint.config.js
|
29
29
|
- lib/autoflux.rb
|
30
|
-
- lib/autoflux/memory.rb
|
31
30
|
- lib/autoflux/stdio.rb
|
32
31
|
- lib/autoflux/step.rb
|
33
|
-
- lib/autoflux/step/
|
32
|
+
- lib/autoflux/step/agent.rb
|
34
33
|
- lib/autoflux/step/command.rb
|
35
34
|
- lib/autoflux/step/start.rb
|
36
35
|
- lib/autoflux/step/stop.rb
|
37
|
-
- lib/autoflux/step/tool.rb
|
38
36
|
- lib/autoflux/version.rb
|
39
37
|
- lib/autoflux/workflow.rb
|
40
38
|
- package-lock.json
|
41
39
|
- package.json
|
42
40
|
- sig/autoflux.rbs
|
43
41
|
- sig/autoflux/agent.rbs
|
44
|
-
- sig/autoflux/event.rbs
|
45
42
|
- sig/autoflux/io.rbs
|
46
|
-
- sig/autoflux/memory.rbs
|
47
43
|
- sig/autoflux/stdio.rbs
|
48
44
|
- sig/autoflux/step.rbs
|
49
|
-
- sig/autoflux/tool.rbs
|
50
45
|
- sig/autoflux/workflow.rbs
|
51
46
|
homepage: https://github.com/elct9620/autoflux
|
52
47
|
licenses:
|
@@ -63,7 +58,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
58
|
requirements:
|
64
59
|
- - ">="
|
65
60
|
- !ruby/object:Gem::Version
|
66
|
-
version: 3.
|
61
|
+
version: 3.2.0
|
67
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
63
|
requirements:
|
69
64
|
- - ">="
|
data/lib/autoflux/memory.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Autoflux
|
4
|
-
# The Memory is a class to store the memory of the workflow.
|
5
|
-
class Memory
|
6
|
-
attr_reader :data, :verbose
|
7
|
-
|
8
|
-
# @rbs data: Array[Hash]
|
9
|
-
def initialize(data: [], verbose: false)
|
10
|
-
@data = data
|
11
|
-
@verbose = verbose
|
12
|
-
freeze
|
13
|
-
end
|
14
|
-
|
15
|
-
# Push the data to the memory.
|
16
|
-
#
|
17
|
-
# @rbs data: Hash
|
18
|
-
def push(event)
|
19
|
-
puts JSON.pretty_generate(event) if verbose
|
20
|
-
@data.push(event)
|
21
|
-
end
|
22
|
-
|
23
|
-
# Get the last data from the memory.
|
24
|
-
def last
|
25
|
-
@data.last
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Autoflux
|
4
|
-
module Step
|
5
|
-
# The Assistant state is used to call the agent.
|
6
|
-
class Assistant
|
7
|
-
def to_s = self.class.name || "Assistant"
|
8
|
-
|
9
|
-
def call(workflow:)
|
10
|
-
event = workflow.agent.call(memory: workflow.memory)
|
11
|
-
workflow.memory.push(event)
|
12
|
-
|
13
|
-
# @type var invocation_event: Autoflux::invocationEvent
|
14
|
-
invocation_event = event
|
15
|
-
return Tool.new if invocation_event[:invocations]&.any?
|
16
|
-
|
17
|
-
# @type var event: Autoflux::textEvent
|
18
|
-
workflow.io.write(event[:content]) if event[:role] == ROLE_ASSISTANT
|
19
|
-
|
20
|
-
Command.new
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/autoflux/step/tool.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Autoflux
|
4
|
-
module Step
|
5
|
-
# The Tool state is used to call the tools provided by the agent.
|
6
|
-
class Tool
|
7
|
-
def to_s = self.class.name || "Tool"
|
8
|
-
|
9
|
-
def call(workflow:)
|
10
|
-
# @type var event: Autoflux::invocationEvent
|
11
|
-
event = workflow.memory.last
|
12
|
-
event[:invocations]&.each do |invocation|
|
13
|
-
# @type: var invocation: Autoflux::invocation
|
14
|
-
# @type: var event: Autoflux::invocationResultEvent
|
15
|
-
event = {
|
16
|
-
role: ROLE_TOOL,
|
17
|
-
content: run(workflow: workflow, invocation: invocation).to_json,
|
18
|
-
invocation_id: invocation[:id]
|
19
|
-
}
|
20
|
-
workflow.memory.push(event)
|
21
|
-
end
|
22
|
-
|
23
|
-
Assistant.new
|
24
|
-
end
|
25
|
-
|
26
|
-
protected
|
27
|
-
|
28
|
-
def run(workflow:, invocation:)
|
29
|
-
name = invocation[:name]
|
30
|
-
params = JSON.parse(invocation[:args], symbolize_names: true)
|
31
|
-
return { status: "error", message: "Tool not found" } unless workflow.agent.tool?(name)
|
32
|
-
|
33
|
-
workflow.agent.tool(name)&.call(workflow: workflow, params: params)
|
34
|
-
rescue JSON::ParserError
|
35
|
-
{ status: "error", message: "Invalid arguments" }
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/sig/autoflux/event.rbs
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Autoflux
|
2
|
-
type jsonString = String
|
3
|
-
|
4
|
-
type invocation = {
|
5
|
-
id: String,
|
6
|
-
name: String,
|
7
|
-
args: jsonString
|
8
|
-
}
|
9
|
-
|
10
|
-
type eventRole = "assistant" | "system" | "user" | "tool" | String
|
11
|
-
type baseEvent = {
|
12
|
-
role: eventRole,
|
13
|
-
agent_id?: String,
|
14
|
-
raw?: Hash[untyped, untyped]
|
15
|
-
}
|
16
|
-
type textEvent = baseEvent & {
|
17
|
-
content: String
|
18
|
-
}
|
19
|
-
type invocationEvent = baseEvent & {
|
20
|
-
invocations: Array[invocation]
|
21
|
-
}
|
22
|
-
type invocationResultEvent = textEvent & {
|
23
|
-
invocation_id: String
|
24
|
-
}
|
25
|
-
|
26
|
-
type event = textEvent | invocationEvent | invocationResultEvent
|
27
|
-
end
|
data/sig/autoflux/memory.rbs
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
module Autoflux
|
2
|
-
interface _Memory
|
3
|
-
def data: () -> Array[event]
|
4
|
-
def push: (event event) -> void
|
5
|
-
def last: () -> event
|
6
|
-
end
|
7
|
-
|
8
|
-
# The Memory is a class to store the memory of the workflow.
|
9
|
-
class Memory
|
10
|
-
include _Memory
|
11
|
-
|
12
|
-
@data: Array[event]
|
13
|
-
@verbose: bool
|
14
|
-
|
15
|
-
attr_reader verbose: bool
|
16
|
-
|
17
|
-
# @rbs data: Array[Hash]
|
18
|
-
def initialize: (?data: Array[event], ?verbose: bool) -> void
|
19
|
-
end
|
20
|
-
end
|