ruboty-openai_chat 0.2.0 → 0.3.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/lib/ruboty/openai_chat/actions/base.rb +3 -2
- data/lib/ruboty/openai_chat/actions/chat.rb +45 -48
- data/lib/ruboty/openai_chat/message.rb +47 -0
- data/lib/ruboty/openai_chat/version.rb +1 -1
- data/lib/ruboty/openai_chat.rb +7 -2
- data/ruboty-openai_chat.gemspec +1 -1
- metadata +5 -5
- data/lib/ruboty/openai_chat/dialog.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56d165955c63d1811168c341cc949fa01edef6afefdbee4fca59968f4d45bcc0
|
4
|
+
data.tar.gz: 035fe05f51a35bb5c94bcb2347bf4e3f783455d741d1922ae04e70928c0bd71d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fd13b2d9393cc7971eb13555c98a1ff3c704e01d48cfbace79958f3b5adc3c18a8ecb1f1e3c928e2844a8f6ba22ec5d8dd69df98bafd5c5f39e9b32849b04b2
|
7
|
+
data.tar.gz: 272e9f664c55bf3d46542a06f1360018743a4a01228c92ab32cd27bb1af0157133a839e76912033ff494b500cd87ea262501941636754b5abff0b4ecf285b8a1
|
@@ -31,8 +31,9 @@ module Ruboty
|
|
31
31
|
@memory ||= Memory.new(robot)
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
# @return [Array<String>]
|
35
|
+
def pretexts
|
36
|
+
[ENV["OPENAI_CHAT_PRETEXT"], memory.namespace(NAMESPACE)[PROFILE_KEY]].compact
|
36
37
|
end
|
37
38
|
end
|
38
39
|
end
|
@@ -11,70 +11,71 @@ module Ruboty
|
|
11
11
|
|
12
12
|
def call
|
13
13
|
human_comment = message[:body]
|
14
|
-
response =
|
15
|
-
p response if
|
14
|
+
response = request_chat(human_comment)
|
15
|
+
p response if Ruboty::OpenAIChat.debug_mode?
|
16
16
|
raise response.body if response.code >= 400
|
17
17
|
|
18
|
-
ai_comment = response.dig("choices", 0, "
|
18
|
+
ai_comment = response.dig("choices", 0, "message", "content").gsub(/\A\s+/, "") || ""
|
19
19
|
|
20
|
-
|
20
|
+
remember_messages(
|
21
|
+
Message.new(role: :user, content: human_comment, expire_at: expire_at),
|
22
|
+
Message.new(role: :assistant, content: ai_comment, expire_at: expire_at)
|
23
|
+
)
|
21
24
|
message.reply(ai_comment)
|
22
25
|
rescue StandardError => e
|
23
26
|
forget
|
24
27
|
message.reply(e.message, code: true)
|
25
|
-
raise e if
|
28
|
+
raise e if Ruboty::OpenAIChat.debug_mode?
|
26
29
|
|
27
30
|
true
|
28
31
|
end
|
29
32
|
|
30
33
|
private
|
31
34
|
|
32
|
-
def
|
35
|
+
def request_chat(human_comment)
|
33
36
|
# https://beta.openai.com/examples/default-chat
|
34
|
-
client.
|
37
|
+
client.chat(
|
35
38
|
parameters: {
|
36
|
-
model: "
|
37
|
-
|
38
|
-
|
39
|
-
top_p: 1,
|
40
|
-
frequency_penalty: 0,
|
41
|
-
presence_penalty: 0.6,
|
42
|
-
stop: Dialog::STOP_SEQUENCES,
|
43
|
-
prompt: build_prompt(human_comment)
|
39
|
+
model: "gpt-3.5-turbo",
|
40
|
+
messages: build_messages(human_comment).map(&:to_api_hash),
|
41
|
+
temperature: 0.7
|
44
42
|
}
|
45
43
|
)
|
46
44
|
end
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
# @return [Array<Message>]
|
47
|
+
def build_messages(human_comment)
|
48
|
+
settings = <<~STRING.chomp
|
49
|
+
The following is a conversation with an AI assistant. The assistant is helpful, creative, clever, and very friendly. The AI assistant's name is #{robot.name}.
|
50
|
+
STRING
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
52
|
+
system_messages = [Message.new(role: :system, content: settings)]
|
53
|
+
pretexts.each do |pretext|
|
54
|
+
system_messages << Message.new(role: :system, content: pretext.chomp)
|
55
|
+
end
|
56
56
|
|
57
|
-
|
58
|
-
#{prefix.join(" ")}
|
57
|
+
pre_messages = [*example_dialog, *messages_from_memory]
|
59
58
|
|
60
|
-
|
61
|
-
STRING
|
59
|
+
[*system_messages, *pre_messages, Message.new(role: :user, content: human_comment)]
|
62
60
|
end
|
63
61
|
|
64
62
|
# @return [Array<Dialog>]
|
65
|
-
def
|
66
|
-
|
67
|
-
|
63
|
+
def messages_from_memory
|
64
|
+
current = Time.now
|
65
|
+
raw_messages.reject! { |hash| Message.from_hash(hash).expired?(current) }
|
66
|
+
raw_messages.map { |hash| Message.from_hash(hash) }
|
68
67
|
end
|
69
68
|
|
70
|
-
# @param
|
71
|
-
def
|
72
|
-
|
69
|
+
# @param messages [Array<Message>]
|
70
|
+
def remember_messages(*messages)
|
71
|
+
messages.each do |message|
|
72
|
+
raw_messages << message.to_h
|
73
|
+
end
|
73
74
|
end
|
74
75
|
|
75
76
|
# @return [Array<Hash>]
|
76
|
-
def
|
77
|
-
memory.namespace(NAMESPACE, message.from || "general")[:
|
77
|
+
def raw_messages
|
78
|
+
memory.namespace(NAMESPACE, message.from || "general")[:messages] ||= []
|
78
79
|
end
|
79
80
|
|
80
81
|
def forget
|
@@ -83,26 +84,22 @@ module Ruboty
|
|
83
84
|
|
84
85
|
# @return [Time]
|
85
86
|
def expire_at
|
86
|
-
Time.now + ENV.fetch("OPENAI_CHAT_MEMORIZE_SECONDS") {
|
87
|
-
end
|
88
|
-
|
89
|
-
# @return [String]
|
90
|
-
def prompt_prefix
|
91
|
-
"The following is a conversation with an AI assistant. The assistant is helpful, creative, clever, and very friendly. The AI assistant's name is #{robot.name}."
|
87
|
+
Time.now + ENV.fetch("OPENAI_CHAT_MEMORIZE_SECONDS") { 15 * 60 }.to_i
|
92
88
|
end
|
93
89
|
|
90
|
+
# @return [Array<Message>]
|
94
91
|
def example_dialog
|
95
92
|
case language
|
96
93
|
when :ja
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
94
|
+
[
|
95
|
+
Message.new(role: :user, content: "こんにちは。あなたは誰ですか?"),
|
96
|
+
Message.new(role: :assistant, content: "私は AI アシスタントの #{robot.name} です。なにかお手伝いできることはありますか?")
|
97
|
+
]
|
101
98
|
else
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
99
|
+
[
|
100
|
+
Message.new(role: :user, content: "Hello, who are you?"),
|
101
|
+
Message.new(role: :assistant, content: "I'm #{robot.name}, an AI assistant. How can I help you today?")
|
102
|
+
]
|
106
103
|
end
|
107
104
|
end
|
108
105
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ruboty
|
4
|
+
module OpenAIChat
|
5
|
+
class Message
|
6
|
+
ROLES = %i[system user assistant].freeze
|
7
|
+
|
8
|
+
# @return [:system, :user, :assistant]
|
9
|
+
attr_reader :role
|
10
|
+
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :content
|
13
|
+
|
14
|
+
# @return [Time]
|
15
|
+
attr_reader :expire_at
|
16
|
+
|
17
|
+
def self.from_hash(hash)
|
18
|
+
new(**hash.transform_keys(&:to_sym))
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param role [:system, :user, :assistant]
|
22
|
+
# @param content [String]
|
23
|
+
# @param expire_at [Time, Integer, nil]
|
24
|
+
def initialize(role:, content:, expire_at: nil)
|
25
|
+
@role = role.to_sym
|
26
|
+
raise ArgumentError, "role must be :system, :user, or :assistant" unless ROLES.include?(@role)
|
27
|
+
|
28
|
+
@content = content
|
29
|
+
@expire_at = expire_at&.yield_self { |t| Time.at(t) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Hash]
|
33
|
+
def to_h
|
34
|
+
{ role: role, content: content, expire_at: expire_at&.to_i }
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_api_hash
|
38
|
+
{ role: role, content: content }
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Boolean]
|
42
|
+
def expired?(from = Time.now)
|
43
|
+
expire_at && (expire_at <= from)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/ruboty/openai_chat.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "ruboty"
|
4
|
-
require "
|
4
|
+
require "openai"
|
5
5
|
|
6
6
|
require_relative "openai_chat/version"
|
7
7
|
require_relative "openai_chat/actions/base"
|
8
8
|
require_relative "openai_chat/actions/chat"
|
9
9
|
require_relative "openai_chat/actions/remember_profile"
|
10
10
|
require_relative "openai_chat/actions/show_profile"
|
11
|
-
require_relative "openai_chat/dialog"
|
12
11
|
require_relative "openai_chat/memory"
|
12
|
+
require_relative "openai_chat/message"
|
13
13
|
|
14
14
|
require_relative "handlers/openai_chat"
|
15
15
|
|
@@ -17,5 +17,10 @@ module Ruboty
|
|
17
17
|
module OpenAIChat
|
18
18
|
class Error < StandardError; end
|
19
19
|
# Your code goes here...
|
20
|
+
|
21
|
+
# @return [Boolean]
|
22
|
+
def self.debug_mode?
|
23
|
+
ENV["OPENAI_CHAT_DEBUG"]&.length&.positive?
|
24
|
+
end
|
20
25
|
end
|
21
26
|
end
|
data/ruboty-openai_chat.gemspec
CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
# Uncomment to register a new dependency of your gem
|
32
32
|
# spec.add_dependency "example-gem", "~> 1.0"
|
33
33
|
spec.add_dependency "ruboty"
|
34
|
-
spec.add_dependency "ruby-openai", "~>
|
34
|
+
spec.add_dependency "ruby-openai", "~> 3.5"
|
35
35
|
|
36
36
|
# For more information and examples about making a new gem, check out our
|
37
37
|
# guide at: https://bundler.io/guides/creating_gem.html
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruboty-openai_chat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomoya Chiba
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruboty
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3.5'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '3.5'
|
41
41
|
description:
|
42
42
|
email:
|
43
43
|
- tomo.asleep@gmail.com
|
@@ -61,8 +61,8 @@ files:
|
|
61
61
|
- lib/ruboty/openai_chat/actions/chat.rb
|
62
62
|
- lib/ruboty/openai_chat/actions/remember_profile.rb
|
63
63
|
- lib/ruboty/openai_chat/actions/show_profile.rb
|
64
|
-
- lib/ruboty/openai_chat/dialog.rb
|
65
64
|
- lib/ruboty/openai_chat/memory.rb
|
65
|
+
- lib/ruboty/openai_chat/message.rb
|
66
66
|
- lib/ruboty/openai_chat/version.rb
|
67
67
|
- ruboty-openai_chat.gemspec
|
68
68
|
homepage: https://github.com/tomoasleep/ruboty-openai_chat
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Ruboty
|
4
|
-
module OpenAIChat
|
5
|
-
class Dialog
|
6
|
-
STOP_SEQUENCES = [">> Human: ", ">> AI: "].freeze
|
7
|
-
STOP_SEQUENCE_PATTERN = />> (Human|AI): /.freeze
|
8
|
-
|
9
|
-
# @return [String]
|
10
|
-
attr_reader :human_comment, :ai_comment
|
11
|
-
|
12
|
-
# @return [Time]
|
13
|
-
attr_reader :expire_at
|
14
|
-
|
15
|
-
def self.from_hash(hash)
|
16
|
-
new(**hash.transform_keys(&:to_sym))
|
17
|
-
end
|
18
|
-
|
19
|
-
# @param human_comment [String]
|
20
|
-
# @param ai_comment [String]
|
21
|
-
# @param expire_at [Time, Integer, nil]
|
22
|
-
def initialize(human_comment:, ai_comment:, expire_at: nil)
|
23
|
-
@human_comment = human_comment
|
24
|
-
@ai_comment = ai_comment
|
25
|
-
@expire_at = expire_at&.yield_self { |t| Time.at(t) }
|
26
|
-
end
|
27
|
-
|
28
|
-
# @return [String]
|
29
|
-
def to_prompt
|
30
|
-
<<~STRING
|
31
|
-
>> Human: #{escape(human_comment).chomp}
|
32
|
-
>> AI: #{escape(ai_comment).chomp}
|
33
|
-
STRING
|
34
|
-
end
|
35
|
-
|
36
|
-
# @return [Hash]
|
37
|
-
def to_h
|
38
|
-
{ human_comment: human_comment, ai_comment: ai_comment, expire_at: expire_at&.to_i }
|
39
|
-
end
|
40
|
-
|
41
|
-
# @return [Boolean]
|
42
|
-
def expired?
|
43
|
-
expire_at && (expire_at <= Time.now)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def escape(text)
|
49
|
-
text.gsub(STOP_SEQUENCE_PATTERN, &:downcase)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|