chatgpt-rb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/chatgpt-rb +34 -0
- data/lib/chatgpt_rb/conversation.rb +126 -0
- data/lib/chatgpt_rb.rb +1 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 56bfd3ab0a5f56e05e8da9b5f59f7358383ebb1c78214d1afec32c6dd63fa140
|
4
|
+
data.tar.gz: 3361ca43ea603a5634e95793cfff46215751a855e2836a92ba6161c4eb07d3fc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '09ab2d88f40d4b00eed86b3fbab7ab8fcc9b454f98c6f1ab0ac3b7461f97324f9151563540c9700d6e24d2ba47491ada4e611394453bd9b093ed2c026254f4cb'
|
7
|
+
data.tar.gz: ff8ba450b24f98564a455c5994e8e19a3ad8cc0e85e4e3f14530a65a669e2a964ae8484815c14098fa6160c48e61b24a69f8cb46de4da4b5874d8a191881bb60
|
data/bin/chatgpt-rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "dotenv/load"
|
4
|
+
require "colorize"
|
5
|
+
require "reline"
|
6
|
+
require_relative "./../lib/chatgpt_rb"
|
7
|
+
|
8
|
+
begin
|
9
|
+
stty_save = `stty -g`.chomp
|
10
|
+
rescue
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
conversation = ChatgptRb::Conversation.new(api_key: ENV.fetch("OPEN_AI_KEY"), model: ENV.fetch("OPEN_AI_MODEL", "gpt-3.5-turbo"))
|
15
|
+
|
16
|
+
puts "Type any message to talk with ChatGPT. Type 'exit' to quit. Type 'dump' to dump this conversation to JSON."
|
17
|
+
|
18
|
+
while message = Reline.readline("me> ".colorize(:red), true) do
|
19
|
+
case message.chomp
|
20
|
+
when "exit", "quit", "q", "\\q"
|
21
|
+
exit
|
22
|
+
when "dump"
|
23
|
+
puts "dump> ".colorize(:blue) + conversation.messages.to_json
|
24
|
+
else
|
25
|
+
print("ai> ".colorize(:yellow))
|
26
|
+
conversation.ask(message) { |fragment| print(fragment) }
|
27
|
+
puts
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue Interrupt
|
31
|
+
puts "^C"
|
32
|
+
`stty #{stty_save}` if stty_save
|
33
|
+
exit
|
34
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require "httparty"
|
2
|
+
|
3
|
+
module ChatgptRb
|
4
|
+
class Conversation
|
5
|
+
attr_reader :api_key, :model, :functions, :temperature, :max_tokens, :top_p, :frequency_penalty, :presence_penalty, :messages
|
6
|
+
|
7
|
+
# @param api_key [String]
|
8
|
+
# @param model [String]
|
9
|
+
# @param functions [Array<Hash>]
|
10
|
+
# @param temperature [Float]
|
11
|
+
# @param max_tokens [Integer]
|
12
|
+
# @param top_p [Float]
|
13
|
+
# @param frequency_penalty [Float]
|
14
|
+
# @param presence_penalty [Float]
|
15
|
+
# @param messages [Array<Hash>]
|
16
|
+
# @param prompt [String, nil] instructions that the model can use to inform its responses, for example: "Act like a sullen teenager."
|
17
|
+
def initialize(api_key:, model: "gpt-3.5-turbo", functions: [], temperature: 0.7, max_tokens: 1024, top_p: 1.0, frequency_penalty: 0.0, presence_penalty: 0.0, messages: [], prompt: nil)
|
18
|
+
@api_key = api_key
|
19
|
+
@model = model
|
20
|
+
@functions = functions
|
21
|
+
@temperature = temperature
|
22
|
+
@max_tokens = max_tokens
|
23
|
+
@top_p = top_p
|
24
|
+
@frequency_penalty = frequency_penalty
|
25
|
+
@presence_penalty = presence_penalty
|
26
|
+
@messages = messages
|
27
|
+
@messages << { role: "system", content: prompt } if prompt
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param content [String]
|
31
|
+
def ask(content, &block)
|
32
|
+
@messages << { role: "user", content: }
|
33
|
+
get_next_response(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def <<(message)
|
39
|
+
@messages << message
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_next_response(&block)
|
43
|
+
streamed_content = ""
|
44
|
+
streamed_arguments = ""
|
45
|
+
streamed_role = ""
|
46
|
+
streamed_function = ""
|
47
|
+
error_buffer = []
|
48
|
+
|
49
|
+
response = HTTParty.post(
|
50
|
+
"https://api.openai.com/v1/chat/completions",
|
51
|
+
steam_body: block_given?,
|
52
|
+
headers: {
|
53
|
+
"Content-Type": "application/json",
|
54
|
+
"Authorization": "Bearer #{api_key}",
|
55
|
+
},
|
56
|
+
body: {
|
57
|
+
model:,
|
58
|
+
messages: @messages,
|
59
|
+
temperature:,
|
60
|
+
max_tokens:,
|
61
|
+
top_p:,
|
62
|
+
frequency_penalty:,
|
63
|
+
presence_penalty:,
|
64
|
+
stream: block_given?,
|
65
|
+
}.tap { |hash| hash[:functions] = functions.map { |hash| hash.except(:implementation) } unless functions.empty? }.to_json,
|
66
|
+
) do |fragment|
|
67
|
+
if block_given?
|
68
|
+
fragment.each_line do |line|
|
69
|
+
next if line.nil?
|
70
|
+
next if line == "\n"
|
71
|
+
break if line == "data: [DONE]\n"
|
72
|
+
|
73
|
+
line_without_prefix = line.gsub(/^data: /, "")
|
74
|
+
json = JSON.parse(line_without_prefix)
|
75
|
+
|
76
|
+
break if json.dig("choices", 0, "finish_reason")
|
77
|
+
|
78
|
+
if (function_name = json.dig("choices", 0, "delta", "function_call", "name"))
|
79
|
+
streamed_function = function_name
|
80
|
+
next
|
81
|
+
end
|
82
|
+
|
83
|
+
if (role = json.dig("choices", 0, "delta", "role"))
|
84
|
+
streamed_role = role
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
if content = json.dig("choices", 0, "delta", "content")
|
89
|
+
yield content unless line_without_prefix.empty?
|
90
|
+
streamed_content << content
|
91
|
+
elsif arguments = json.dig("choices", 0, "delta", "function_call", "arguments")
|
92
|
+
streamed_arguments << arguments
|
93
|
+
end
|
94
|
+
rescue => e
|
95
|
+
error_buffer << "Error: #{e}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
error_buffer.each { |e| $stderr.puts("Error: #{e}") }
|
101
|
+
|
102
|
+
@messages << if block_given? && streamed_content != ""
|
103
|
+
{ "content" => streamed_content, "role" => streamed_role }
|
104
|
+
elsif block_given? && streamed_arguments != ""
|
105
|
+
{ "role" => "assistant", "content" => nil, "function_call" => { "name" => streamed_function, "arguments" => streamed_arguments } }
|
106
|
+
else
|
107
|
+
response.dig("choices", 0, "message")
|
108
|
+
end
|
109
|
+
|
110
|
+
if @messages.last["content"]
|
111
|
+
@messages.last["content"]
|
112
|
+
elsif @messages.last["function_call"]
|
113
|
+
function_args = @messages.last["function_call"]
|
114
|
+
function_name = function_args.fetch("name")
|
115
|
+
arguments = JSON.parse(function_args.fetch("arguments"))
|
116
|
+
|
117
|
+
function = functions.find { |function| function[:name] == function_name }
|
118
|
+
content = function.fetch(:implementation).call(**arguments.transform_keys(&:to_sym))
|
119
|
+
|
120
|
+
@messages << { role: "function", name: function_name, content: content.to_json }
|
121
|
+
|
122
|
+
get_next_response(functions:, api_key:, model:, temperature:, max_tokens:, top_p:, frequency_penalty:, presence_penalty:, &block)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/chatgpt_rb.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "./chatgpt_rb/conversation"
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chatgpt-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aaron Breckenridge
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.21'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.21'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dotenv
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: reline
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.3'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.3'
|
69
|
+
description: Provides libraries for interacting with the ChatGPT API and a CLI program
|
70
|
+
`chatgpt-rb` for live conversations.
|
71
|
+
email:
|
72
|
+
- aaron@breckridge.dev
|
73
|
+
executables:
|
74
|
+
- chatgpt-rb
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- bin/chatgpt-rb
|
79
|
+
- lib/chatgpt_rb.rb
|
80
|
+
- lib/chatgpt_rb/conversation.rb
|
81
|
+
homepage: https://github.com/breckenedge/chatgpt-rb
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubygems_version: 3.4.10
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: A gem for interacting with the ChatGPT API
|
104
|
+
test_files: []
|