simple-openai-client 1.0.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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/openai-client.rb +163 -0
  3. data/openai-client.gemspec +24 -0
  4. metadata +164 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: efa6dcdade7db76ddf992abe323c53b86f773bc6e1955caf03b3fbe5411b8910
4
+ data.tar.gz: 1851ceaf75dae20d8a1eb6f4f08ecd71b3b0002faf1e921d32aa0f03110e9480
5
+ SHA512:
6
+ metadata.gz: b161ea843f3c55e500d303c18b51cd9a081c9d3250a5b70380fc8fdfac27ee506c640132094f834b2d64eced2fca2a10d25d7a2e1eb338fe7a8bbb5d70632951
7
+ data.tar.gz: e8e3f0e4b51c6c9f540dccc3eda4c91026bba5ee2ca53774296e756a71fd4b1ea34744379e60d78ff2171f638ac636c3e25bb7c92f244c1073d594354ba85cd8
@@ -0,0 +1,163 @@
1
+ # Reference:
2
+ # - https://platform.openai.com/docs/guides/function-calling/supported-models
3
+ #
4
+
5
+ require 'net/http'
6
+ require 'uri'
7
+ require 'json'
8
+
9
+ class OpenAIClient
10
+ attr_accessor :api_key, :model, :messages, :functions, :callbacks, :version
11
+
12
+ def initialize(api_key:, model:, messages: [], functions: [], callbacks: [], version: 'v2')
13
+ self.api_key = api_key
14
+ self.model = model
15
+ self.messages = messages
16
+ self.functions = functions
17
+ self.callbacks = callbacks
18
+ self.version = version
19
+ end
20
+
21
+ # Return an array with the available models
22
+ def models
23
+ uri = URI("https://api.openai.com/v1/models")
24
+ http = Net::HTTP.new(uri.host, uri.port)
25
+ http.use_ssl = true
26
+
27
+ # Create a GET request instead of POST
28
+ request = Net::HTTP::Get.new(uri.request_uri, {
29
+ "Authorization" => "Bearer #{self.api_key}",
30
+ "Content-Type" => "application/json"
31
+ # Removed "OpenAI-Beta" header as it's typically not required
32
+ })
33
+
34
+ response = http.request(request)
35
+
36
+ if response.is_a?(Net::HTTPSuccess)
37
+ parsed_response = JSON.parse(response.body)
38
+ # Extract and return the list of models
39
+ parsed_response['data'].map { |model| model['id'] }
40
+ else
41
+ raise "Error: #{response.code} #{response.message}"
42
+ #puts response.body
43
+ #[]
44
+ end
45
+ rescue JSON::ParserError => e
46
+ raise "Failed to parse JSON response: #{e.message}"
47
+ #[]
48
+ rescue StandardError => e
49
+ raise "An error occurred: #{e.message}"
50
+ #[]
51
+ end # models
52
+
53
+ # Ask something to GPT.
54
+ # Return the response.
55
+ def ask(s, context: [])
56
+ # Use v1 chat completions endpoint (with functions support)
57
+ uri = URI("https://api.openai.com/v1/chat/completions")
58
+
59
+ # add contenxt to the history of messages
60
+ self.messages += context
61
+
62
+ # add new question asked by the user to the history of messages
63
+ self.messages << { "role" => "user", "content" => s }
64
+
65
+ request_body = {
66
+ "model" => self.model, # A known model that supports function calling; update as needed
67
+ "messages" => self.messages
68
+ # To let the model decide if and when to call a function, omit "function_call"
69
+ # If you want the model to call a function explicitly, you can add: "function_call" => "auto"
70
+ }
71
+
72
+ request_body["functions"] = self.functions if self.functions.size > 0
73
+
74
+ http = Net::HTTP.new(uri.host, uri.port)
75
+ http.use_ssl = true
76
+
77
+ request = Net::HTTP::Post.new(uri.path, {
78
+ "Content-Type" => "application/json",
79
+ "Authorization" => "Bearer #{self.api_key}",
80
+ "OpenAI-Beta" => "assistants=#{version}"
81
+ })
82
+ request.body = JSON.dump(request_body)
83
+
84
+ response = http.request(request)
85
+
86
+ if response.is_a?(Net::HTTPSuccess)
87
+ response_json = JSON.parse(response.body)
88
+
89
+ # Check if the assistant decided to call a function
90
+ function_call = response_json.dig("choices", 0, "message", "function_call")
91
+
92
+ unless function_call
93
+ # add new response from AI to the history of messages
94
+ assistant_reply = response_json.dig("choices", 0, "message", "content")
95
+ self.messages << { "role" => "assistant", "content" => assistant_reply }
96
+ # return the resonse from AI
97
+ return assistant_reply
98
+ else
99
+ function_call_name = function_call["name"]
100
+ function_call_args = JSON.parse(function_call["arguments"]) rescue {}
101
+
102
+ # Handle the function call
103
+ result = self.callbacks[function_call_name.to_sym].call(function_call_args);
104
+
105
+ # Now we send the function result back to the assistant as another message:
106
+ follow_up_uri = URI("https://api.openai.com/v1/chat/completions")
107
+ follow_up_messages = messages.dup
108
+ follow_up_messages << {
109
+ "role" => "function",
110
+ "name" => function_call_name,
111
+ "content" => JSON.dump(result)
112
+ }
113
+
114
+ follow_up_request_body = {
115
+ "model" => self.model,
116
+ "messages" => follow_up_messages
117
+ }
118
+
119
+ follow_up_http = Net::HTTP.new(follow_up_uri.host, follow_up_uri.port)
120
+ follow_up_http.use_ssl = true
121
+
122
+ follow_up_request = Net::HTTP::Post.new(follow_up_uri.path, {
123
+ "Content-Type" => "application/json",
124
+ "Authorization" => "Bearer #{OPENAI_API_KEY}",
125
+ "OpenAI-Beta" => "assistants=#{version}"
126
+ })
127
+ follow_up_request.body = JSON.dump(follow_up_request_body)
128
+
129
+ follow_up_response = follow_up_http.request(follow_up_request)
130
+ if follow_up_response.is_a?(Net::HTTPSuccess)
131
+ follow_up_response_json = JSON.parse(follow_up_response.body)
132
+ final_reply = follow_up_response_json.dig("choices", 0, "message", "content")
133
+ # add new response from AI to the history of messages
134
+ self.messages << { "role" => "assistant", "content" => final_reply }
135
+ # return the response form the AI.
136
+ return final_reply
137
+ else
138
+ raise "Error after function call: #{follow_up_response.code} - #{follow_up_response.message} - #{follow_up_response.body}"
139
+ end
140
+ end
141
+ else
142
+ raise "Error: #{response.code} - #{response.message} - #{response.body}"
143
+ end
144
+ end # def ask
145
+
146
+ # manage copilot from terminal
147
+ def console
148
+ puts "Mass-Copilot Console".blue
149
+ puts "Type 'exit' to quit.".blue
150
+ while true
151
+ print "You: ".green
152
+ prompt = gets.chomp
153
+ break if prompt.downcase.strip == 'exit'
154
+ begin
155
+ puts "Mass-Copilot: #{ask(prompt)}".blue
156
+ rescue => e
157
+ puts "Error: #{e.to_console}".red
158
+ end
159
+ end
160
+ end # def console
161
+
162
+ end # class OpenAIClient
163
+
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'simple-openai-client'
3
+ s.version = '1.0.1'
4
+ s.date = '2025-01-05'
5
+ s.summary = "Very simple Ruby library for operating OpenAI API for building Agents."
6
+ s.description = "Very simple Ruby library for operating OpenAI API for building Agents."
7
+ s.authors = ["Leandro Daniel Sardi"]
8
+ s.email = 'leandro@massprospecting.com'
9
+ s.files = [
10
+ 'lib/openai-client.rb',
11
+ 'openai-client.gemspec'
12
+ ]
13
+ s.homepage = 'https://github.com/leandrosardi/openai-client'
14
+ s.license = 'MIT'
15
+ s.add_runtime_dependency 'uri', '~> 0.11.2', '>= 0.11.2'
16
+ s.add_runtime_dependency 'net-http', '~> 0.2.0', '>= 0.2.0'
17
+ s.add_runtime_dependency 'json', '~> 2.6.3', '>= 2.6.3'
18
+ s.add_runtime_dependency 'blackstack-core', '~> 1.2.15', '>= 1.2.15'
19
+ #s.add_runtime_dependency 'selenium-webdriver', '~> 4.10.0', '>= 4.10.0'
20
+ #s.add_runtime_dependency 'watir', '~> 7.3.0', '>= 7.3.0'
21
+ #s.add_runtime_dependency 'sequel', '~> 5.75.0', '>= 5.75.0'
22
+ s.add_runtime_dependency 'colorize', '~> 0.8.1', '>= 0.8.1'
23
+ s.add_runtime_dependency 'simple_cloud_logging', '~> 1.2.2', '>= 1.2.2'
24
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple-openai-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Leandro Daniel Sardi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-01-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: uri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.11.2
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.11.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 0.11.2
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.11.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: net-http
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.2.0
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.2.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 0.2.0
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.2.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: json
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 2.6.3
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.6.3
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 2.6.3
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.6.3
73
+ - !ruby/object:Gem::Dependency
74
+ name: blackstack-core
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 1.2.15
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.2.15
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.2.15
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.2.15
93
+ - !ruby/object:Gem::Dependency
94
+ name: colorize
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: 0.8.1
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 0.8.1
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 0.8.1
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 0.8.1
113
+ - !ruby/object:Gem::Dependency
114
+ name: simple_cloud_logging
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: 1.2.2
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 1.2.2
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: 1.2.2
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 1.2.2
133
+ description: Very simple Ruby library for operating OpenAI API for building Agents.
134
+ email: leandro@massprospecting.com
135
+ executables: []
136
+ extensions: []
137
+ extra_rdoc_files: []
138
+ files:
139
+ - lib/openai-client.rb
140
+ - openai-client.gemspec
141
+ homepage: https://github.com/leandrosardi/openai-client
142
+ licenses:
143
+ - MIT
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubygems_version: 3.3.7
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: Very simple Ruby library for operating OpenAI API for building Agents.
164
+ test_files: []