luo 0.1.0 → 0.1.1
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/.idea/inspectionProfiles/Project_Default.xml +9 -0
- data/.idea/luo.iml +26 -1
- data/.rspec +1 -0
- data/Gemfile.lock +62 -1
- data/README.md +3 -0
- data/README.zh.md +9 -0
- data/exe/luo +7 -0
- data/lib/luo/agent.rb +45 -0
- data/lib/luo/agent_runner_base.rb +89 -0
- data/lib/luo/agent_runner_context.rb +33 -0
- data/lib/luo/aiui.rb +41 -0
- data/lib/luo/cli.rb +33 -0
- data/lib/luo/configurable.rb +14 -0
- data/lib/luo/http_client.rb +21 -0
- data/lib/luo/memory_history.rb +55 -0
- data/lib/luo/messages.rb +50 -0
- data/lib/luo/open_ai.rb +48 -0
- data/lib/luo/open_ai_agent_runner.rb +55 -0
- data/lib/luo/prompt_template.rb +39 -0
- data/lib/luo/prompts.rb +32 -0
- data/lib/luo/templates/luo_agent_input.md.erb +39 -0
- data/lib/luo/templates/luo_agent_system.md.erb +4 -0
- data/lib/luo/templates/luo_agent_tool_input.md.erb +10 -0
- data/lib/luo/templates/luo_commit.md.erb +2 -0
- data/lib/luo/templates/luo_xinghuo_agent_input.md.erb +10 -0
- data/lib/luo/templates/luo_xinghuo_agent_tool_input.md.erb +16 -0
- data/lib/luo/templates/prompt.demo.erb +1 -0
- data/lib/luo/version.rb +1 -1
- data/lib/luo/xinghuo.rb +50 -0
- data/lib/luo/xinghuo_agent_runner.rb +56 -0
- data/lib/luo.rb +18 -1
- data/luo.gemspec +9 -0
- data/sig/luo/agent.rbs +21 -0
- data/sig/luo/agent_runner_base.rbs +25 -0
- data/sig/luo/agent_runner_context.rbs +19 -0
- data/sig/luo/aiui.rbs +9 -0
- data/sig/luo/configurable.rbs +5 -0
- data/sig/luo/env.rbs +9 -0
- data/sig/luo/http_client.rbs +9 -0
- data/sig/luo/memory_history.rbs +18 -0
- data/sig/luo/messages.rbs +20 -0
- data/sig/luo/open_ai.rbs +11 -0
- data/sig/luo/prompt_template.rbs +18 -0
- data/sig/luo/prompts.rbs +15 -0
- data/sig/luo/xinghuo.rbs +9 -0
- metadata +186 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7755df3627fd226e48254313905aaaf420d1804bb86d95c67ea0383ca3b6f677
|
4
|
+
data.tar.gz: 17764e813034fd6d9f159fffdc8a954e380aa41df0a1e55b45a8856197343916
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3820ebe256d4c302fac7ee48f784636c666de704fea02d5a8b5a6d12636eb231a406c09ffdfd31f5e9038c6d0c5dd879c9aeab960c72151aae2f6562f5be1a58
|
7
|
+
data.tar.gz: c276f298d2d25b7ced2c6ca27ad427d92964cf1fff3213b5913ee02cdcd9643fdaf4de9db5eca066c77e22bd5ec4302a46eb5a710baa4f1bc8e005d8ce96f08d
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<component name="InspectionProjectProfileManager">
|
2
|
+
<profile version="1.0">
|
3
|
+
<option name="myName" value="Project Default" />
|
4
|
+
<inspection_tool class="RubyClassVariableUsageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
5
|
+
<inspection_tool class="RubyResolve" enabled="false" level="WARNING" enabled_by_default="false">
|
6
|
+
<option name="warnImplicitResults" value="false" />
|
7
|
+
</inspection_tool>
|
8
|
+
</profile>
|
9
|
+
</component>
|
data/.idea/luo.iml
CHANGED
@@ -12,13 +12,38 @@
|
|
12
12
|
<orderEntry type="jdk" jdkName="ruby-3.2.1-p31" jdkType="RUBY_SDK" />
|
13
13
|
<orderEntry type="sourceFolder" forTests="false" />
|
14
14
|
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.4.6, ruby-3.2.1-p31) [gem]" level="application" />
|
15
|
+
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.2.2, ruby-3.2.1-p31) [gem]" level="application" />
|
16
|
+
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.5.0, ruby-3.2.1-p31) [gem]" level="application" />
|
17
|
+
<orderEntry type="library" scope="PROVIDED" name="dotenv (v2.8.1, ruby-3.2.1-p31) [gem]" level="application" />
|
18
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-cli (v1.0.0, ruby-3.2.1-p31) [gem]" level="application" />
|
19
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-configurable (v1.0.1, ruby-3.2.1-p31) [gem]" level="application" />
|
20
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-core (v1.0.0, ruby-3.2.1-p31) [gem]" level="application" />
|
21
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-inflector (v1.0.0, ruby-3.2.1-p31) [gem]" level="application" />
|
22
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-initializer (v3.1.1, ruby-3.2.1-p31) [gem]" level="application" />
|
23
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-logic (v1.5.0, ruby-3.2.1-p31) [gem]" level="application" />
|
24
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-schema (v1.13.1, ruby-3.2.1-p31) [gem]" level="application" />
|
25
|
+
<orderEntry type="library" scope="PROVIDED" name="dry-types (v1.7.1, ruby-3.2.1-p31) [gem]" level="application" />
|
26
|
+
<orderEntry type="library" scope="PROVIDED" name="faraday (v2.7.4, ruby-3.2.1-p31) [gem]" level="application" />
|
27
|
+
<orderEntry type="library" scope="PROVIDED" name="faraday-net_http (v3.0.2, ruby-3.2.1-p31) [gem]" level="application" />
|
28
|
+
<orderEntry type="library" scope="PROVIDED" name="faraday-retry (v2.1.0, ruby-3.2.1-p31) [gem]" level="application" />
|
15
29
|
<orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, ruby-3.2.1-p31) [gem]" level="application" />
|
30
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec (v3.12.0, ruby-3.2.1-p31) [gem]" level="application" />
|
31
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.12.2, ruby-3.2.1-p31) [gem]" level="application" />
|
32
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.12.3, ruby-3.2.1-p31) [gem]" level="application" />
|
33
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.12.5, ruby-3.2.1-p31) [gem]" level="application" />
|
34
|
+
<orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.12.0, ruby-3.2.1-p31) [gem]" level="application" />
|
35
|
+
<orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.6.8, ruby-3.2.1-p31) [gem]" level="application" />
|
16
36
|
</component>
|
17
37
|
<component name="RakeTasksCache">
|
18
38
|
<option name="myRootTask">
|
19
39
|
<RakeTaskImpl id="rake">
|
20
40
|
<subtasks>
|
21
41
|
<RakeTaskImpl description="Build luo-0.1.0.gem into the pkg directory" fullCommand="build" id="build" />
|
42
|
+
<RakeTaskImpl id="build">
|
43
|
+
<subtasks>
|
44
|
+
<RakeTaskImpl description="Generate SHA512 checksum if luo-0.1.0.gem into the checksums directory" fullCommand="build:checksum" id="checksum" />
|
45
|
+
</subtasks>
|
46
|
+
</RakeTaskImpl>
|
22
47
|
<RakeTaskImpl description="Remove any temporary products" fullCommand="clean" id="clean" />
|
23
48
|
<RakeTaskImpl description="Remove any generated files" fullCommand="clobber" id="clobber" />
|
24
49
|
<RakeTaskImpl description="Build and install luo-0.1.0.gem into system gems" fullCommand="install" id="install" />
|
@@ -27,7 +52,7 @@
|
|
27
52
|
<RakeTaskImpl description="Build and install luo-0.1.0.gem into system gems without network access" fullCommand="install:local" id="local" />
|
28
53
|
</subtasks>
|
29
54
|
</RakeTaskImpl>
|
30
|
-
<RakeTaskImpl description="Create tag v0.1.0 and build and push luo-0.1.0.gem to
|
55
|
+
<RakeTaskImpl description="Create tag v0.1.0 and build and push luo-0.1.0.gem to rubygems.org" fullCommand="release[remote]" id="release[remote]" />
|
31
56
|
<RakeTaskImpl description="" fullCommand="default" id="default" />
|
32
57
|
<RakeTaskImpl description="" fullCommand="release" id="release" />
|
33
58
|
<RakeTaskImpl id="release">
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,80 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
luo (0.1.
|
4
|
+
luo (0.1.1)
|
5
|
+
dotenv (~> 2.8, >= 2.8.1)
|
6
|
+
dry-cli (~> 1.0)
|
7
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
8
|
+
dry-schema (~> 1.13, >= 1.13.1)
|
9
|
+
faraday (~> 2.7, >= 2.7.4)
|
10
|
+
faraday-retry (~> 2.1)
|
11
|
+
zeitwerk (~> 2.6, >= 2.6.8)
|
5
12
|
|
6
13
|
GEM
|
7
14
|
remote: https://rubygems.org/
|
8
15
|
specs:
|
16
|
+
concurrent-ruby (1.2.2)
|
17
|
+
diff-lcs (1.5.0)
|
18
|
+
dotenv (2.8.1)
|
19
|
+
dry-cli (1.0.0)
|
20
|
+
dry-configurable (1.0.1)
|
21
|
+
dry-core (~> 1.0, < 2)
|
22
|
+
zeitwerk (~> 2.6)
|
23
|
+
dry-core (1.0.0)
|
24
|
+
concurrent-ruby (~> 1.0)
|
25
|
+
zeitwerk (~> 2.6)
|
26
|
+
dry-inflector (1.0.0)
|
27
|
+
dry-initializer (3.1.1)
|
28
|
+
dry-logic (1.5.0)
|
29
|
+
concurrent-ruby (~> 1.0)
|
30
|
+
dry-core (~> 1.0, < 2)
|
31
|
+
zeitwerk (~> 2.6)
|
32
|
+
dry-schema (1.13.1)
|
33
|
+
concurrent-ruby (~> 1.0)
|
34
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
35
|
+
dry-core (~> 1.0, < 2)
|
36
|
+
dry-initializer (~> 3.0)
|
37
|
+
dry-logic (>= 1.4, < 2)
|
38
|
+
dry-types (>= 1.7, < 2)
|
39
|
+
zeitwerk (~> 2.6)
|
40
|
+
dry-types (1.7.1)
|
41
|
+
concurrent-ruby (~> 1.0)
|
42
|
+
dry-core (~> 1.0)
|
43
|
+
dry-inflector (~> 1.0)
|
44
|
+
dry-logic (~> 1.4)
|
45
|
+
zeitwerk (~> 2.6)
|
46
|
+
faraday (2.7.4)
|
47
|
+
faraday-net_http (>= 2.0, < 3.1)
|
48
|
+
ruby2_keywords (>= 0.0.4)
|
49
|
+
faraday-net_http (3.0.2)
|
50
|
+
faraday-retry (2.1.0)
|
51
|
+
faraday (~> 2.0)
|
9
52
|
rake (13.0.6)
|
53
|
+
rspec (3.12.0)
|
54
|
+
rspec-core (~> 3.12.0)
|
55
|
+
rspec-expectations (~> 3.12.0)
|
56
|
+
rspec-mocks (~> 3.12.0)
|
57
|
+
rspec-core (3.12.2)
|
58
|
+
rspec-support (~> 3.12.0)
|
59
|
+
rspec-expectations (3.12.3)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.12.0)
|
62
|
+
rspec-mocks (3.12.5)
|
63
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
64
|
+
rspec-support (~> 3.12.0)
|
65
|
+
rspec-support (3.12.0)
|
66
|
+
ruby2_keywords (0.0.5)
|
67
|
+
zeitwerk (2.6.8)
|
10
68
|
|
11
69
|
PLATFORMS
|
12
70
|
arm64-darwin-22
|
71
|
+
ruby
|
72
|
+
x86_64-linux
|
13
73
|
|
14
74
|
DEPENDENCIES
|
15
75
|
luo!
|
16
76
|
rake (~> 13.0)
|
77
|
+
rspec (~> 3.12)
|
17
78
|
|
18
79
|
BUNDLED WITH
|
19
80
|
2.4.6
|
data/README.md
CHANGED
@@ -29,3 +29,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
29
29
|
## Contributing
|
30
30
|
|
31
31
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/luo.
|
32
|
+
|
33
|
+
ENV.fetch('OPENAI_HOST', 'https://api.openai.com')
|
34
|
+
ENV.fetch('OPENAI_ACCESS_TOKEN')
|
data/README.zh.md
ADDED
data/exe/luo
ADDED
data/lib/luo/agent.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
class Agent
|
5
|
+
attr_reader :context, :action_input, :client
|
6
|
+
def initialize(context: nil, action_input: nil, client: nil)
|
7
|
+
@context = context
|
8
|
+
@action_input = action_input
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
raise NotImplementedError, "call method must be implemented in subclass"
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def on_call(&block)
|
18
|
+
define_method(:call, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_call_with_final_result(&block)
|
22
|
+
define_method(:call) do
|
23
|
+
context.final_result = instance_eval(&block)
|
24
|
+
context.final_result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.create_parameter_method(method_name, not_provided = Object.new, &block)
|
29
|
+
define_method(method_name.to_sym) do |content = not_provided|
|
30
|
+
if content === not_provided
|
31
|
+
class_variable_get("@@#{method_name}")
|
32
|
+
else
|
33
|
+
content = block.call(content) if block_given?
|
34
|
+
class_variable_set("@@#{method_name}", content)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
create_parameter_method(:agent_desc) { |content| content.gsub(/\n/, "") }
|
40
|
+
create_parameter_method(:agent_name) { |context| context.gsub(/[[:punct:]]/, '') }
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
class AgentRunnerBase
|
5
|
+
|
6
|
+
include Configurable
|
7
|
+
|
8
|
+
setting :language, default: "en"
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
on_init
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_init
|
15
|
+
end
|
16
|
+
|
17
|
+
def context
|
18
|
+
@context ||= Luo::AgentRunnerContext.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset_context
|
22
|
+
histories = context.histories
|
23
|
+
@context = Luo::AgentRunnerContext.new
|
24
|
+
@context.histories = histories
|
25
|
+
@context
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(user_input)
|
29
|
+
context.user_input = user_input
|
30
|
+
on_request
|
31
|
+
on_result
|
32
|
+
on_run
|
33
|
+
after_run
|
34
|
+
rt = Marshal.load(Marshal.dump(context))
|
35
|
+
reset_context
|
36
|
+
rt
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_request
|
40
|
+
raise NotImplementedError, "call method must be implemented in subclass"
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_result
|
44
|
+
raise NotImplementedError, "call method must be implemented in subclass"
|
45
|
+
end
|
46
|
+
|
47
|
+
def after_run
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_run
|
51
|
+
context.have_running_agents.each do |agent|
|
52
|
+
context.agent_results << { agent_name: agent.class.agent_name, agent_response: agent.call }
|
53
|
+
end
|
54
|
+
context.have_running_agents.clear
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_agent(agent)
|
58
|
+
context.have_running_agents << agent
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
|
63
|
+
def agents
|
64
|
+
@agents ||= {}
|
65
|
+
end
|
66
|
+
|
67
|
+
[:on_result, :on_request, :on_init, :after_run].each do |method_name|
|
68
|
+
define_method(method_name) do |&block|
|
69
|
+
define_method(method_name, &block)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def register(agent)
|
74
|
+
agents[agent.agent_name] = agent
|
75
|
+
end
|
76
|
+
|
77
|
+
def language_info
|
78
|
+
if self.config.language == "en"
|
79
|
+
""
|
80
|
+
elsif self.config.language == "zh"
|
81
|
+
"(must be a Chinese string)"
|
82
|
+
else
|
83
|
+
""
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
class AgentRunnerContext
|
5
|
+
attr_accessor :user_input, :action_input, :response, :agent_results, :final_result, :messages, :retries
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@agent_results = []
|
9
|
+
@retries = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def histories
|
13
|
+
@histories ||= MemoryHistory.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def histories=(histories)
|
17
|
+
@histories = histories
|
18
|
+
end
|
19
|
+
|
20
|
+
def client
|
21
|
+
@client
|
22
|
+
end
|
23
|
+
|
24
|
+
def client=(client)
|
25
|
+
@client = client
|
26
|
+
end
|
27
|
+
|
28
|
+
def have_running_agents
|
29
|
+
@running_agents ||= Set.new
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/luo/aiui.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
class AIUI
|
5
|
+
include Configurable
|
6
|
+
|
7
|
+
setting :id, default: ENV.fetch('AIUI_APP_ID')
|
8
|
+
setting :key, default: ENV.fetch('AIUI_APP_KEY')
|
9
|
+
setting :host, default: 'http://api.iflyos.cn'
|
10
|
+
setting :uid, default: -> { SecureRandom.hex(16) }
|
11
|
+
setting :retries, default: ENV.fetch('AIUI_REQUEST_RETRIES', 3).to_i
|
12
|
+
|
13
|
+
include HttpClient.init_client
|
14
|
+
|
15
|
+
PARAMS = Dry::Schema.Params do
|
16
|
+
required(:appid).filled(:string)
|
17
|
+
required(:appkey).filled(:string)
|
18
|
+
required(:uid).filled(:string)
|
19
|
+
required(:text).filled(:string)
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_aiui(params)
|
23
|
+
client.post('/external/ls_log/aiui_request', params.to_h)
|
24
|
+
end
|
25
|
+
|
26
|
+
def chat(messages)
|
27
|
+
if messages.is_a?(Messages)
|
28
|
+
messages = messages.to_a
|
29
|
+
end
|
30
|
+
message = messages.last&.fetch(:content, nil)
|
31
|
+
params = PARAMS.call(
|
32
|
+
appid: config.id,
|
33
|
+
appkey: config.key,
|
34
|
+
uid: config.uid.call,
|
35
|
+
text: message
|
36
|
+
)
|
37
|
+
return params.errors unless params.success?
|
38
|
+
request_aiui(params).body.dig("data", 0, "intent", "answer")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/luo/cli.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
extend Dry::CLI::Registry
|
7
|
+
|
8
|
+
class Version < Dry::CLI::Command
|
9
|
+
desc "Print version"
|
10
|
+
|
11
|
+
def call(*)
|
12
|
+
puts Luo::VERSION
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Commit < Dry::CLI::Command
|
17
|
+
desc "Commit with Luo"
|
18
|
+
|
19
|
+
argument :message, desc: "Commit message", required: true, type: :string
|
20
|
+
|
21
|
+
def call(message:, **)
|
22
|
+
messages = Messages.create.system(prompt: Luo::Prompts.luo_commit, context: {commit: message}).to_a
|
23
|
+
response = OpenAI.new.chat(messages)
|
24
|
+
exec "git commit -m '#{response}'"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
register "version", Version, aliases: %w[v -v --version]
|
29
|
+
register "commit", Commit, aliases: ["c"]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
module HttpClient
|
5
|
+
def self.init_client
|
6
|
+
Module.new do
|
7
|
+
define_method :client do
|
8
|
+
@client ||= Faraday.new(self.class.config.host) { |conn|
|
9
|
+
conn.request :authorization, 'Bearer', self.class.config.access_token if self.class.config.respond_to?(:access_token) && !self.class.config.access_token.nil?
|
10
|
+
conn.request :retry, max: (self.class.config.retries || 3), interval: 0.05,
|
11
|
+
interval_randomness: 0.5, backoff_factor: 2,
|
12
|
+
exceptions: [Faraday::TimeoutError, Faraday::ConnectionFailed]
|
13
|
+
conn.request :json
|
14
|
+
conn.response :json
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
|
5
|
+
##
|
6
|
+
# 用来保存回话历史的简单内存队列
|
7
|
+
class MemoryHistory
|
8
|
+
include Configurable
|
9
|
+
|
10
|
+
setting :max_size, default: 12
|
11
|
+
|
12
|
+
##
|
13
|
+
# 初始化一个队列
|
14
|
+
# @param [Integer] max_size 队列的最大长度
|
15
|
+
def initialize(max_size = config.max_size)
|
16
|
+
@queue = []
|
17
|
+
@max_size = max_size
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# 入队
|
22
|
+
def enqueue(element)
|
23
|
+
if @queue.size == @max_size
|
24
|
+
@queue.shift
|
25
|
+
end
|
26
|
+
@queue << element
|
27
|
+
end
|
28
|
+
|
29
|
+
def user(content)
|
30
|
+
enqueue({role: "user", content: content})
|
31
|
+
end
|
32
|
+
|
33
|
+
def assistant(content)
|
34
|
+
enqueue({role: "assistant", content: content})
|
35
|
+
end
|
36
|
+
|
37
|
+
alias push enqueue
|
38
|
+
|
39
|
+
def dequeue
|
40
|
+
@queue.shift
|
41
|
+
end
|
42
|
+
|
43
|
+
def size
|
44
|
+
@queue.size
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_a
|
48
|
+
@queue
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_json
|
52
|
+
JSON.pretty_generate @queue
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/luo/messages.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
class Messages
|
5
|
+
def initialize(history: [])
|
6
|
+
@history = history
|
7
|
+
@messages = []
|
8
|
+
@system = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_role_message_method(role)
|
12
|
+
define_method(role) do |text: nil, file: '', prompt: nil, context: {}|
|
13
|
+
if prompt.nil?
|
14
|
+
data = text || File.read(file)
|
15
|
+
else
|
16
|
+
data = prompt.render(context)
|
17
|
+
end
|
18
|
+
if role.to_s == "system"
|
19
|
+
@system = {role: role, content: data}
|
20
|
+
else
|
21
|
+
@messages << {role: role, content: data}
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.create_role_message_methods
|
28
|
+
%w(system user assistant).each do |role|
|
29
|
+
create_role_message_method(role)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
create_role_message_methods
|
34
|
+
|
35
|
+
def to_a
|
36
|
+
if @system.empty?
|
37
|
+
@history.to_a + @messages
|
38
|
+
else
|
39
|
+
(@history.to_a + @messages).unshift(@system)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def create(history: [])
|
45
|
+
self.new(history: history)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/luo/open_ai.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
class OpenAI
|
5
|
+
include Configurable
|
6
|
+
|
7
|
+
setting :access_token, default: ENV.fetch('OPENAI_ACCESS_TOKEN')
|
8
|
+
setting :retries, default: ENV.fetch('OPENAI_REQUEST_RETRIES', 3).to_i
|
9
|
+
setting :host, default: ENV.fetch('OPENAI_HOST', 'https://api.openai.com')
|
10
|
+
setting :temperature, default: ENV.fetch('OPENAI_TEMPERATURE', 1).to_i
|
11
|
+
|
12
|
+
include HttpClient.init_client
|
13
|
+
|
14
|
+
PARAMS = Dry::Schema.Params do
|
15
|
+
required(:model).filled(:string)
|
16
|
+
required(:temperature).filled(:float)
|
17
|
+
required(:messages).filled(:array)
|
18
|
+
optional(:top_p).maybe(:float)
|
19
|
+
optional(:n).maybe(:integer)
|
20
|
+
optional(:stream).maybe(:bool)
|
21
|
+
optional(:stop).maybe(:array)
|
22
|
+
optional(:max_tokens).maybe(:integer)
|
23
|
+
optional(:presence_penalty).maybe(:float)
|
24
|
+
optional(:frequency_penalty).maybe(:float)
|
25
|
+
optional(:logit_bias).maybe(:hash)
|
26
|
+
optional(:user).maybe(:string)
|
27
|
+
end
|
28
|
+
|
29
|
+
def chat_completions(params)
|
30
|
+
client.post('/v1/chat/completions', params.to_h)
|
31
|
+
end
|
32
|
+
|
33
|
+
def chat(messages)
|
34
|
+
if messages.is_a?(Messages)
|
35
|
+
messages = messages.to_a
|
36
|
+
end
|
37
|
+
params = PARAMS.call(
|
38
|
+
model: 'gpt-3.5-turbo',
|
39
|
+
temperature: config.temperature,
|
40
|
+
messages: messages
|
41
|
+
)
|
42
|
+
return params.errors unless params.success?
|
43
|
+
chat_completions(params).body.dig("choices", 0, "message", "content")
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Luo
|
4
|
+
class OpenAIAgentRunner < AgentRunnerBase
|
5
|
+
|
6
|
+
include Configurable
|
7
|
+
|
8
|
+
setting :retires, default: 3
|
9
|
+
|
10
|
+
on_init do
|
11
|
+
@openai = OpenAI.new
|
12
|
+
end
|
13
|
+
|
14
|
+
on_request do
|
15
|
+
context.messages = Messages.create(history: context.histories)
|
16
|
+
.system(prompt: Luo::Prompts.luo_agent_system)
|
17
|
+
.user(prompt: Luo::Prompts.luo_agent_input, context: {agents: self.class.agents, last_user_input: context.user_input})
|
18
|
+
context.response = @openai.chat(context.messages)
|
19
|
+
end
|
20
|
+
|
21
|
+
on_result do
|
22
|
+
actions = JSON.parse(context.response)
|
23
|
+
actions = [actions] if actions.is_a?(Hash)
|
24
|
+
actions.each do |action|
|
25
|
+
agent = self.class.agents[action['action']]&.new(
|
26
|
+
context: context,
|
27
|
+
action_input: action['action_input'],
|
28
|
+
client: @openai
|
29
|
+
)
|
30
|
+
add_agent(agent) if agent
|
31
|
+
if action['action'] == "Final Answer"
|
32
|
+
context.final_result = action['action_input']
|
33
|
+
context.histories.user(context.user_input)
|
34
|
+
context.histories.assistant(context.final_result)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
after_run do
|
40
|
+
if context.retries < config.retires && context.final_result.nil?
|
41
|
+
context.messages = context.messages.assistant(
|
42
|
+
prompt: Luo::Prompts.luo_agent_tool_input,
|
43
|
+
context: {
|
44
|
+
tools_response: context.agent_results
|
45
|
+
}
|
46
|
+
)
|
47
|
+
context.response = @openai.chat(context.messages)
|
48
|
+
context.retries += 1
|
49
|
+
on_result
|
50
|
+
on_run
|
51
|
+
after_run
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|