chatgpt-rb 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56bfd3ab0a5f56e05e8da9b5f59f7358383ebb1c78214d1afec32c6dd63fa140
4
- data.tar.gz: 3361ca43ea603a5634e95793cfff46215751a855e2836a92ba6161c4eb07d3fc
3
+ metadata.gz: 50e30e865ade9a6e20cad5680617972114a224bda8bf25d9969b9b57e6236763
4
+ data.tar.gz: e7d1714512f1696b9401018b11292a46ed220c650639521def9b4fa0290303a0
5
5
  SHA512:
6
- metadata.gz: '09ab2d88f40d4b00eed86b3fbab7ab8fcc9b454f98c6f1ab0ac3b7461f97324f9151563540c9700d6e24d2ba47491ada4e611394453bd9b093ed2c026254f4cb'
7
- data.tar.gz: ff8ba450b24f98564a455c5994e8e19a3ad8cc0e85e4e3f14530a65a669e2a964ae8484815c14098fa6160c48e61b24a69f8cb46de4da4b5874d8a191881bb60
6
+ metadata.gz: a42400a64dee74e93b7b27c8d04fc1f8c750bd0b90abc1ba37c33bdd58cbc9a494906c544b2bb0e02585807fffffbaae9ff3488d328b9bdb3051cc4fbfb59c0b
7
+ data.tar.gz: 4f187489399450e128b1f5f1634004be8de0a5f4b71516bff12ec234e3cf5cf3498102d6e44081e0e2bd00dc8ad14c1f75ca68770425eac0a7b1e32d284ee6e4
data/bin/watcher ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "listen"
4
+
5
+ directory_to_watch = Dir.pwd
6
+
7
+ listener = Listen.to(directory_to_watch) do |modified, added, removed|
8
+ system("rspec")
9
+ end
10
+
11
+ listener.start
12
+ puts "Watching #{directory_to_watch} for changes..."
13
+
14
+ # Stop the listener when the process is terminated
15
+ Signal.trap("INT") { listener.stop }
16
+ Signal.trap("TERM") { listener.stop }
17
+ sleep
@@ -1,12 +1,19 @@
1
1
  require "httparty"
2
+ require_relative "./function"
3
+ require_relative "./dsl/conversation"
2
4
 
3
5
  module ChatgptRb
4
6
  class Conversation
5
- attr_reader :api_key, :model, :functions, :temperature, :max_tokens, :top_p, :frequency_penalty, :presence_penalty, :messages
7
+ include HTTParty
8
+
9
+ base_uri "https://api.openai.com"
10
+
11
+ attr_accessor :api_key, :model, :functions, :temperature, :max_tokens, :top_p, :frequency_penalty, :presence_penalty, :prompt
12
+ attr_reader :messages
6
13
 
7
14
  # @param api_key [String]
8
15
  # @param model [String]
9
- # @param functions [Array<Hash>]
16
+ # @param functions [Array<Hash>, Array<ChatgptRb::Function>]
10
17
  # @param temperature [Float]
11
18
  # @param max_tokens [Integer]
12
19
  # @param top_p [Float]
@@ -14,25 +21,43 @@ module ChatgptRb
14
21
  # @param presence_penalty [Float]
15
22
  # @param messages [Array<Hash>]
16
23
  # @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)
24
+ def initialize(api_key: nil, 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, &configuration)
18
25
  @api_key = api_key
19
26
  @model = model
20
- @functions = functions
27
+ @functions = functions.each_with_object({}) do |function, hash|
28
+ func = function.is_a?(ChatgptRb::Function) ? function : ChatgptRb::Function.new(**function)
29
+ hash[func.name] = func
30
+ end
21
31
  @temperature = temperature
22
32
  @max_tokens = max_tokens
23
33
  @top_p = top_p
24
34
  @frequency_penalty = frequency_penalty
25
35
  @presence_penalty = presence_penalty
26
36
  @messages = messages
37
+ @prompt = prompt
38
+ ChatgptRb::DSL::Conversation.configure(self, &configuration) if block_given?
27
39
  @messages << { role: "system", content: prompt } if prompt
28
40
  end
29
41
 
30
42
  # @param content [String]
43
+ # @yieldparam [String] the response, but streamed
44
+ # @return [String] the response
31
45
  def ask(content, &block)
32
46
  @messages << { role: "user", content: }
33
47
  get_next_response(&block)
34
48
  end
35
49
 
50
+ # @param content [String]
51
+ # @param function [ChatgptRb::Function] temporarily enhance the next response with the provided function
52
+ # @yieldparam [String] the response, but streamed
53
+ # @return [String] the response
54
+ def ask_with_function(content, function, &block)
55
+ function_was = functions[function.name]
56
+ functions[function.name] = function
57
+ get_next_response(content, &block)
58
+ functions[function.name] = function_was
59
+ end
60
+
36
61
  private
37
62
 
38
63
  def <<(message)
@@ -46,23 +71,29 @@ module ChatgptRb
46
71
  streamed_function = ""
47
72
  error_buffer = []
48
73
 
49
- response = HTTParty.post(
50
- "https://api.openai.com/v1/chat/completions",
74
+ body = {
75
+ model:,
76
+ messages: @messages,
77
+ temperature:,
78
+ max_tokens:,
79
+ top_p:,
80
+ frequency_penalty:,
81
+ presence_penalty:,
82
+ stream: block_given?,
83
+ }.tap do |hash|
84
+ hash[:functions] = functions.values.map(&:as_json) unless functions.empty?
85
+ end
86
+
87
+ response = self.class.post(
88
+ "/v1/chat/completions",
51
89
  steam_body: block_given?,
52
90
  headers: {
53
- "Content-Type": "application/json",
54
- "Authorization": "Bearer #{api_key}",
91
+ "Content-Type" => "application/json",
92
+ "Authorization" => "Bearer #{api_key}",
93
+ "Accept" => "application/json",
94
+ "User-Agent" => "Ruby/chatgpt-rb",
55
95
  },
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,
96
+ body: body.to_json,
66
97
  ) do |fragment|
67
98
  if block_given?
68
99
  fragment.each_line do |line|
@@ -113,13 +144,12 @@ module ChatgptRb
113
144
  function_args = @messages.last["function_call"]
114
145
  function_name = function_args.fetch("name")
115
146
  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))
147
+ function = functions[function_name]
148
+ content = function.implementation.call(**arguments.transform_keys(&:to_sym))
119
149
 
120
150
  @messages << { role: "function", name: function_name, content: content.to_json }
121
151
 
122
- get_next_response(functions:, api_key:, model:, temperature:, max_tokens:, top_p:, frequency_penalty:, presence_penalty:, &block)
152
+ get_next_response(&block)
123
153
  end
124
154
  end
125
155
  end
@@ -0,0 +1,29 @@
1
+ module ChatgptRb
2
+ module DSL
3
+ class Base
4
+ # @param object [Chatgpt::Conversation, ChatgptRb::Function, ChatgptRb::Parameter]
5
+ # @param configuration [Block]
6
+ # @return [Chatgpt::Conversation, ChatgptRb::Function, ChatgptRb::Parameter]
7
+ def self.configure(object, &configuration)
8
+ new(object).instance_eval(&configuration)
9
+ object
10
+ end
11
+
12
+ attr_reader :object
13
+
14
+ # @param object [Chatgpt::Conversation, ChatgptRb::Function, ChatgptRb::Parameter]
15
+ def initialize(object)
16
+ @object = object
17
+ end
18
+
19
+ # @param [Array<Symbol>] shorthand for allowing the DSL to set iVars
20
+ def self.supported_fields(fields)
21
+ fields.each do |method_name|
22
+ define_method method_name do |value|
23
+ object.public_send("#{method_name}=", value)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "./base"
2
+ require_relative "./function"
3
+ require_relative "../function"
4
+
5
+ module ChatgptRb
6
+ module DSL
7
+ class Conversation < Base
8
+ supported_fields %i[api_key model functions temperature max_tokens top_p frequency_penalty presence_penalty prompt]
9
+
10
+ # @param name [String] the name of the function
11
+ # @param configuration [Block]
12
+ def function(name, &configuration)
13
+ object.functions[name] = ChatgptRb::DSL::Function.configure(ChatgptRb::Function.new(name:), &configuration)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "../parameter"
2
+ require_relative "./parameter"
3
+
4
+ module ChatgptRb
5
+ module DSL
6
+ class Function < Base
7
+ supported_fields %i[description implementation parameters]
8
+
9
+ def parameter(name, &configuration)
10
+ object.parameters << ChatgptRb::DSL::Parameter.configure(ChatgptRb::Parameter.new(name:), &configuration)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "./base"
2
+
3
+ module ChatgptRb
4
+ module DSL
5
+ class Parameter < Base
6
+ supported_fields %i[description type enum required]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ module ChatgptRb
2
+ class Function
3
+ attr_accessor :name, :description, :parameters, :implementation
4
+
5
+ # @param name [String]
6
+ # @param description [String, nil]
7
+ # @param parameters [Array<ChatgptRb::Parameter>]
8
+ # @param implementation [Lambda, nil]
9
+ def initialize(name:, description: nil, parameters: [], implementation: nil)
10
+ @name = name
11
+ @description = description
12
+ @parameters = parameters
13
+ @implementation = implementation
14
+ end
15
+
16
+ # @return [Hash]
17
+ def as_json
18
+ {
19
+ name:,
20
+ description:,
21
+ parameters: {
22
+ type: "object",
23
+ properties: parameters.each_with_object({}) do |parameter, hash|
24
+ hash[parameter.name] = parameter.as_json
25
+ end,
26
+ },
27
+ required: parameters.select(&:required?).map(&:name),
28
+ }.compact
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module ChatgptRb
2
+ class Parameter
3
+ attr_accessor :name, :enum, :type, :description, :required
4
+
5
+ # @param name [String]
6
+ # @param enum
7
+ # @param type
8
+ # @param description
9
+ # @param required [true, false] whether or not this parameter is required
10
+ def initialize(name:, enum: nil, type: nil, description: nil, required: false)
11
+ @name = name
12
+ @enum = enum
13
+ @type = type
14
+ @description = description
15
+ @required = required
16
+ end
17
+
18
+ def required?
19
+ !!required
20
+ end
21
+
22
+ # @return Hash
23
+ def as_json
24
+ {
25
+ enum:,
26
+ type:,
27
+ description:,
28
+ }.compact
29
+ end
30
+ end
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chatgpt-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Breckenridge
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-07 00:00:00.000000000 Z
11
+ date: 2023-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -66,6 +66,48 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: listen
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  description: Provides libraries for interacting with the ChatGPT API and a CLI program
70
112
  `chatgpt-rb` for live conversations.
71
113
  email:
@@ -76,8 +118,15 @@ extensions: []
76
118
  extra_rdoc_files: []
77
119
  files:
78
120
  - bin/chatgpt-rb
121
+ - bin/watcher
79
122
  - lib/chatgpt_rb.rb
80
123
  - lib/chatgpt_rb/conversation.rb
124
+ - lib/chatgpt_rb/dsl/base.rb
125
+ - lib/chatgpt_rb/dsl/conversation.rb
126
+ - lib/chatgpt_rb/dsl/function.rb
127
+ - lib/chatgpt_rb/dsl/parameter.rb
128
+ - lib/chatgpt_rb/function.rb
129
+ - lib/chatgpt_rb/parameter.rb
81
130
  homepage: https://github.com/breckenedge/chatgpt-rb
82
131
  licenses:
83
132
  - MIT