rubygpt 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/.rspec +3 -0
- data/.rubocop.yml +21 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +8 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/MIT-LICENSE +20 -0
- data/README.md +236 -0
- data/Rakefile +12 -0
- data/SECURITY.md +7 -0
- data/lib/rubygpt/client/configuration.rb +70 -0
- data/lib/rubygpt/client.rb +29 -0
- data/lib/rubygpt/common/message.rb +73 -0
- data/lib/rubygpt/connection/faraday.rb +31 -0
- data/lib/rubygpt/connection.rb +18 -0
- data/lib/rubygpt/requester/chat_requester.rb +84 -0
- data/lib/rubygpt/requester.rb +26 -0
- data/lib/rubygpt/response/chat_completion.rb +87 -0
- data/lib/rubygpt/response.rb +56 -0
- data/lib/rubygpt/version.rb +5 -0
- data/lib/rubygpt.rb +40 -0
- data/sig/rubygpt.rbs +4 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 80d7d5153895b17a6dc4bd74f3a55ad90a80e8b1a991256d270dbce2813c46af
|
4
|
+
data.tar.gz: 0aee0ce7e85e9a93f05bd26c114bd5cd536cd0bdf70c07e587e5bd751735d82a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ec76d8aa68dffa1584357eac6a8ebf8c75fef4238a9413f190abd688cf095a8d4cc9e1cf5588f6c11fb9f333e88c8325f2b26f698311d63232ea9830cb9e58ed
|
7
|
+
data.tar.gz: 8b128b1f7901e72369d3323d521b9c49b00260fa92a2ac1ccca11b36bce72f0f7e78c0f3876bdb2c01d2cc8e491032599cefc2cd7e022749c25040a11b6948d0
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.2.0
|
3
|
+
|
4
|
+
Style/StringLiterals:
|
5
|
+
Enabled: true
|
6
|
+
EnforcedStyle: double_quotes
|
7
|
+
|
8
|
+
Style/StringLiteralsInInterpolation:
|
9
|
+
Enabled: true
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Max: 120
|
14
|
+
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Exclude:
|
17
|
+
- 'spec/**/*'
|
18
|
+
- 'config/**/*'
|
19
|
+
- 'db/**/*'
|
20
|
+
- 'lib/tasks/**/*'
|
21
|
+
- 'rubygpt.gemspec'
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-3.2.0
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
6
|
+
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
8
|
+
|
9
|
+
## Our Standards
|
10
|
+
|
11
|
+
Examples of behavior that contributes to a positive environment for our community include:
|
12
|
+
|
13
|
+
* Demonstrating empathy and kindness toward other people
|
14
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
15
|
+
* Giving and gracefully accepting constructive feedback
|
16
|
+
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
17
|
+
* Focusing on what is best not just for us as individuals, but for the overall community
|
18
|
+
|
19
|
+
Examples of unacceptable behavior include:
|
20
|
+
|
21
|
+
* The use of sexualized language or imagery, and sexual attention or
|
22
|
+
advances of any kind
|
23
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
24
|
+
* Public or private harassment
|
25
|
+
* Publishing others' private information, such as a physical or email
|
26
|
+
address, without their explicit permission
|
27
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
28
|
+
professional setting
|
29
|
+
|
30
|
+
## Enforcement Responsibilities
|
31
|
+
|
32
|
+
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
33
|
+
|
34
|
+
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
35
|
+
|
36
|
+
## Scope
|
37
|
+
|
38
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
39
|
+
|
40
|
+
## Enforcement
|
41
|
+
|
42
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at feapaydin@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
|
43
|
+
|
44
|
+
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
45
|
+
|
46
|
+
## Enforcement Guidelines
|
47
|
+
|
48
|
+
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
49
|
+
|
50
|
+
### 1. Correction
|
51
|
+
|
52
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
53
|
+
|
54
|
+
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
55
|
+
|
56
|
+
### 2. Warning
|
57
|
+
|
58
|
+
**Community Impact**: A violation through a single incident or series of actions.
|
59
|
+
|
60
|
+
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
61
|
+
|
62
|
+
### 3. Temporary Ban
|
63
|
+
|
64
|
+
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
65
|
+
|
66
|
+
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
67
|
+
|
68
|
+
### 4. Permanent Ban
|
69
|
+
|
70
|
+
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
71
|
+
|
72
|
+
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
73
|
+
|
74
|
+
## Attribution
|
75
|
+
|
76
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
|
77
|
+
available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
78
|
+
|
79
|
+
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
80
|
+
|
81
|
+
[homepage]: https://www.contributor-covenant.org
|
82
|
+
|
83
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
84
|
+
https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2024 - Furkan Enes Apaydın
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/rubygpt.svg)](https://badge.fury.io/rb/rubygpt)
|
2
|
+
[![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
|
3
|
+
[![Spec](https://github.com/feapaydin/rubygpt/actions/workflows/spec.yml/badge.svg?branch=main)](https://github.com/feapaydin/rubygpt/actions/workflows/spec.yml)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/55d19b1c7fbe8c48e9ca/maintainability)](https://codeclimate.com/github/feapaydin/rubygpt/maintainability)
|
5
|
+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
6
|
+
|
7
|
+
# RubyGPT
|
8
|
+
|
9
|
+
This gem aims to provide an easy-to-use Ruby wrapper for all modules of OpenAI's ChatGPT API. It is designed to be simple and easy to use, while also providing a high level of customization. It is also aiming to work efficiently in Ruby on Rails applications.
|
10
|
+
|
11
|
+
### Capabilities
|
12
|
+
|
13
|
+
- [Chat Completions API](#chat-completions-api)
|
14
|
+
- [JSON Mode Support](#json-mode-messages)
|
15
|
+
|
16
|
+
The gem is in the early stages. It's designed to be easily extendable for other modules of the OpenAI APIs and products in the future. See [Contributing](#contributing) section for more details.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Install the gem directly via bundler:
|
21
|
+
|
22
|
+
```sh
|
23
|
+
$ bundle install rubygpt
|
24
|
+
```
|
25
|
+
|
26
|
+
Or add it to your `Gemfile` for your project:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem "rubygpt"
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
```sh
|
35
|
+
$ bundle install
|
36
|
+
```
|
37
|
+
|
38
|
+
## Configuration
|
39
|
+
|
40
|
+
In order to access the OpenAI APIs, you must configure the Rubygpt client with your API key and the preferred ChatGPT model to use. This can be done globally for the entire application, or on a per-request basis.
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
Rubygpt.configure(api_key: 'YOUR_API_KEY', model: 'gpt-3.5-turbo')
|
44
|
+
```
|
45
|
+
|
46
|
+
Alternatively, you can provide a block to set the configuration options:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Rubygpt.configure do |config|
|
50
|
+
config.api_key = 'YOUR_API_KEY'
|
51
|
+
config.model = 'gpt-3.5-turbo'
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
The above examples will create a singleton client that works across the entire application.
|
56
|
+
|
57
|
+
If you'd like to use different configurations for different parts of your application, you can manually create client instances and configure them separately:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# Setup different client objects with different configurations
|
61
|
+
client_gpt3 = Rubygpt::Client.new(api_key: 'YOUR_API_KEY', model: 'gpt-3.5-turbo')
|
62
|
+
client_gpt4 = Rubygpt::Client.new do |config|
|
63
|
+
config.api_key = 'YOUR_SECOND_API_KEY'
|
64
|
+
config.model = 'gpt-4'
|
65
|
+
config.organization_id = 'OPENAI_ORG_ID'
|
66
|
+
end
|
67
|
+
|
68
|
+
# Use the client objects to create different requesters
|
69
|
+
chat_requester_gpt3 = Rubygpt::Requester::ChatRequester.new(client_gpt3)
|
70
|
+
chat_requester_gpt4 = Rubygpt::Requester::ChatRequester.new(client_gpt4)
|
71
|
+
```
|
72
|
+
|
73
|
+
The following attributes can be configured when initializing the client:
|
74
|
+
|
75
|
+
- `api_key` (required): Your OpenAI API key
|
76
|
+
- `model` (required): The model to use for the API requests.
|
77
|
+
- `api_url`: The base URL for the API requests. The default is `https://api.openai.com/v1`
|
78
|
+
- `organization_id`: The organization ID to use for the API requests.
|
79
|
+
- `connection_adapter`: The HTTP connection adapter to use for the API requests. The default is `:faraday`
|
80
|
+
|
81
|
+
#### Connection Adapters
|
82
|
+
|
83
|
+
The Rubygpt client uses [Faraday](https://github.com/lostisland/faraday) to manage HTTP connections. This allows the entire power of Faraday to be used for the API requests, including diverse HTTP adapters and features like streaming.
|
84
|
+
|
85
|
+
## Chat Completions API
|
86
|
+
|
87
|
+
Chat Completions is a Text Completion feature provided by OpenAI's ChatGPT. It can be used to generate human-like responses to a given prompt. It is one of the core features of the ChatGPT.
|
88
|
+
|
89
|
+
See the [OpenAI Chat Completions API documentation](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) and related [Chat API reference](https://platform.openai.com/docs/api-reference/chat/create) for more information.
|
90
|
+
|
91
|
+
### Sending Messages
|
92
|
+
|
93
|
+
After configuring the Rubygpt client, you can perform requests to the Chat Completions API directly.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# Send a message to GPT
|
97
|
+
Rubygpt.chat.create("A system of cells interlinked.") # any message you'd like to send
|
98
|
+
|
99
|
+
# Send multiple messages
|
100
|
+
Rubygpt.chat.create(["Within cells interlinked", "Within cells interlinked", "Within one stem"])
|
101
|
+
```
|
102
|
+
|
103
|
+
To use the received responses, refer to the [Using Chat Completion Responses](#using-chat-completion-responses) section.
|
104
|
+
|
105
|
+
### Customizing the Messages
|
106
|
+
|
107
|
+
By default each message is sent with `system` role and the provided contents in the call.
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
Rubygpt.chat.create("Test message.") # { role: "system", content: "Test message." }
|
111
|
+
```
|
112
|
+
|
113
|
+
You can customize the request by providing additional parameters to the `create` method.
|
114
|
+
|
115
|
+
A `Message` object consist of following attributes:
|
116
|
+
- `role`: The role of the messages author.
|
117
|
+
- `content`: The content of the message.
|
118
|
+
- `name`: The name of the messages author or function name. (has multiple use cases, see OpenAI docs for details)
|
119
|
+
- `tool_calls`: The tool calls generated by the model, such as function calls. (role: assistant only)
|
120
|
+
- `tool_call_id`: Tool call that this message is responding to. (role: tool only)
|
121
|
+
|
122
|
+
For extended details on the attributes, visit the [OpenAI Chat API reference](https://platform.openai.com/docs/api-reference/chat/create).
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
Rubygpt.chat.create(role: 'user', content: "What is Ruby?")
|
126
|
+
Rubygpt.chat.create(role: 'assistant', name: 'furkan', content: "Ruby is a...")
|
127
|
+
```
|
128
|
+
It is also possible to send multiple message objects with a single request.
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
messages = [
|
132
|
+
{ role: 'user', content: "foo" },
|
133
|
+
{ role: 'assistant', name: 'johndoe', content: "bar" }
|
134
|
+
]
|
135
|
+
Rubygpt.chat.create(messages) # send the array/hash directly
|
136
|
+
Rubygpt.chat.create(messages:) # also works as a keyword argument
|
137
|
+
```
|
138
|
+
|
139
|
+
### Customizing The Requests
|
140
|
+
|
141
|
+
You can send any available request body parameter supported by OpenAI Chat Completion API to the `Rubygpt.chat.create` method. Just send the messages in the `messages:` keyword argument. Any additional parameters you'll provide will be passed-through to the request body directly.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
Rubygpt.chat.create(
|
145
|
+
n: 5,
|
146
|
+
messages: ["What time is it?"],
|
147
|
+
model: 'gpt-4-turbo-preview', # overrides your client config when provided explicity here
|
148
|
+
max_tokens: 100,
|
149
|
+
frequency_penalty: 1.0,
|
150
|
+
tempature: 1,
|
151
|
+
user: 'feapaydin',
|
152
|
+
json: true # DO NOT provide response_format for JSON mode, use this flag
|
153
|
+
)
|
154
|
+
```
|
155
|
+
|
156
|
+
### JSON Mode Messages
|
157
|
+
|
158
|
+
The JSON Mode is a feature of the Chat Completions API that forces the model to generate a JSON response.
|
159
|
+
|
160
|
+
> A common way to use Chat Completions is to instruct the model to always return a JSON object that makes sense for your use case, by specifying this in the system message. While this does work in some cases, occasionally the models may generate output that does not parse to valid JSON objects.
|
161
|
+
|
162
|
+
See [JSON Mode official docs](https://platform.openai.com/docs/guides/text-generation/json-mode) for more details.
|
163
|
+
|
164
|
+
To send a message in JSON mode, you can simply send `json: true` option along with your message.
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
# Single message with JSON mode
|
168
|
+
Rubygpt.chat.create(content: "List all programming languages by their creation date.", json: true)
|
169
|
+
|
170
|
+
# Multiple messages with JSON mode
|
171
|
+
messages = [
|
172
|
+
{ role: 'user', content: "List all programming languages by their creation date." },
|
173
|
+
{ role: 'user', content: "Also add their creator's name to the objects." }
|
174
|
+
]
|
175
|
+
Rubygpt.chat.create(messages:, json: true)
|
176
|
+
```
|
177
|
+
|
178
|
+
### Stream Mode
|
179
|
+
|
180
|
+
Streaming mode is a feature of the Chat Completions API that allows the model to generate a continuous stream of messages. This is useful for chat applications where the model is expected to generate multiple or longer responses to a single prompt.
|
181
|
+
|
182
|
+
Stream mode is not supported at the moment, but it's planned to be implemented in the future versions.
|
183
|
+
|
184
|
+
### Using Chat Completion Responses
|
185
|
+
|
186
|
+
Regardless of the amount of messages sent, the response will be an instance of `Response::ChatCompletion` object.
|
187
|
+
The object wraps a set of methods to easily access the response data provided by the [OpenAI Chat API: Create](https://platform.openai.com/docs/api-reference/chat/create).
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
response = Rubygpt.chat.create("What time is it?", "Also tell me the date.")
|
191
|
+
response.messages # ["It's 12:00 PM.", "Today is 2032-01-01."]
|
192
|
+
response.read # => "It's 12:00 PM. Today is 2032-01-01."
|
193
|
+
response.failed? # => true if any message (choice) have finish_reason other than "stop"
|
194
|
+
response.cost # => Total cost of the request (usage.total_tokens)
|
195
|
+
response.to_h # => Hash representation of the response
|
196
|
+
|
197
|
+
# Full attributes:
|
198
|
+
# :id, :object, :created, :model, :system_fingerprint, :usage, :choices
|
199
|
+
```
|
200
|
+
|
201
|
+
Each Choice in the response is an instance of `Response::ChatCompletion::Choice` object.
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
response = Rubygpt.chat.create("What time is it?", "Also tell me the date.")
|
205
|
+
response.choices # => [Response::ChatCompletion::Choice, Response::ChatCompletion::Choice]
|
206
|
+
response.choices.first.index # => 0
|
207
|
+
response.choices.first.message # => <#Common::Message>
|
208
|
+
response.choices.first.content # => "It's 12:00 PM." - delegated from message
|
209
|
+
response.choices.first.role # => "system" - delegated from message
|
210
|
+
response.choices.first.finish_reason # => "stop"
|
211
|
+
response.choices.first.failed? # => false, unless finish_reason is not "stop"
|
212
|
+
response.choices.first.to_h # => Hash representation of the choice
|
213
|
+
response.choices.first.logprobs # => Log probabilities of the tokens, hash or null
|
214
|
+
```
|
215
|
+
|
216
|
+
## Development
|
217
|
+
|
218
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
219
|
+
|
220
|
+
The console contains a pre-configured Rubygpt client with a test API key. See [bin/console](bin/console) for more details.
|
221
|
+
|
222
|
+
To set the API key for the tests, set the environment variable `OPENAI_API_KEY` to access the APIs.
|
223
|
+
|
224
|
+
```sh
|
225
|
+
export OPENAI_API_KEY=your_api_key
|
226
|
+
```
|
227
|
+
|
228
|
+
## Contributing
|
229
|
+
|
230
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/feapaydin/rubygpt. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/feapaydin/rubygpt/blob/main/CODE_OF_CONDUCT.md).
|
231
|
+
|
232
|
+
Participations are much welcome in this project as it's still in the early stages of development. You can contribute by addressing the issues flagged as `good first issue` or `help wanted` in the issues section. You can also contribute by opening new issues, suggesting new features, or reporting bugs.
|
233
|
+
|
234
|
+
## Code of Conduct
|
235
|
+
|
236
|
+
Everyone interacting in the Rubygpt project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/feapaydin/rubygpt/blob/main/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/SECURITY.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# Security Policy
|
2
|
+
|
3
|
+
## Reporting a Vulnerability
|
4
|
+
|
5
|
+
We're running this project solely on GitHub. Please report any vulnerability issues that you encounter by creating a new issue on GitHub. Please be careful not to cause any possible abuses when sharing details.
|
6
|
+
|
7
|
+
For more delicate vulnerabilities, please contact me directly at feapaydin@gmail.com
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubygpt
|
4
|
+
class Client
|
5
|
+
# Handles the configuration options when initializing a new Rubygpt::Client object
|
6
|
+
class Configuration
|
7
|
+
# Initializes a new Rubygpt::Client::Configuration object from a hash or another Configuration object
|
8
|
+
def self.from(configuration_input)
|
9
|
+
case configuration_input
|
10
|
+
when Configuration then configuration_input
|
11
|
+
when Hash, nil then Configuration.new(configuration_input || {})
|
12
|
+
else raise InvalidConfigurationError, "Invalid configuration provided for client."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvalidConfigurationError < StandardError; end
|
17
|
+
|
18
|
+
# The connection adapter to use for the client
|
19
|
+
# Defaults to :faraday
|
20
|
+
attr_accessor :connection_adapter
|
21
|
+
|
22
|
+
# The API URL to use for the client
|
23
|
+
attr_accessor :api_url
|
24
|
+
|
25
|
+
# The API key to use for the client
|
26
|
+
attr_accessor :api_key
|
27
|
+
|
28
|
+
# The OpenAI OrganizationID that will be sent when making requests (optional)
|
29
|
+
# https://platform.openai.com/docs/api-reference/organization-optional
|
30
|
+
attr_accessor :organization_id
|
31
|
+
|
32
|
+
# The GPT model to use for the client
|
33
|
+
# Sample values: gpt-4, gpt-4-turbo-preview, gpt-3.5-turbo, gpt-3.5-turbo-instruct
|
34
|
+
# Refer to https://platform.openai.com/docs/models
|
35
|
+
attr_accessor :model
|
36
|
+
|
37
|
+
# The base URL for the OpenAI API
|
38
|
+
DEFAULT_API_URL = "https://api.openai.com/v1"
|
39
|
+
DEFAULT_CONNECTION_ADAPTER = :faraday
|
40
|
+
|
41
|
+
# Initializes new Rubygpt::Client::Configuration object
|
42
|
+
#
|
43
|
+
# @param [Hash] options
|
44
|
+
# @option options [String] :api_url required
|
45
|
+
# @option options [String] :api_key
|
46
|
+
# @option options [String] :organization_id
|
47
|
+
# @option options [String] :model required
|
48
|
+
# @option options [String] :connection_adapter
|
49
|
+
def initialize(options = {})
|
50
|
+
@connection_adapter = options[:connection_adapter] || DEFAULT_CONNECTION_ADAPTER
|
51
|
+
@api_key = options[:api_key]
|
52
|
+
@api_url = options[:api_url] || DEFAULT_API_URL
|
53
|
+
@organization_id = options[:organization_id]
|
54
|
+
@model = options[:model]
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate!
|
58
|
+
raise(InvalidConfigurationError, "model is required") unless model
|
59
|
+
raise(InvalidConfigurationError, "api_key is required") unless api_key
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_headers
|
63
|
+
{
|
64
|
+
"Authorization" => "Bearer #{api_key}",
|
65
|
+
"OpenAI-Organization" => organization_id
|
66
|
+
}.compact
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubygpt
|
4
|
+
# Main class that issues the connection to OpenAI APIs
|
5
|
+
class Client
|
6
|
+
# The configuration object for the client
|
7
|
+
attr_reader :configuration
|
8
|
+
|
9
|
+
# Initializes new Rubygpt::Client object
|
10
|
+
#
|
11
|
+
# @param [Hash] configuration
|
12
|
+
# @param [Configuration] configuration
|
13
|
+
def initialize(configuration = nil)
|
14
|
+
@configuration = Configuration.from(configuration)
|
15
|
+
yield @configuration if block_given?
|
16
|
+
@configuration.validate!
|
17
|
+
end
|
18
|
+
|
19
|
+
def post(*args)
|
20
|
+
connection.post(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def connection
|
26
|
+
@connection ||= Connection.new(configuration)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Common
|
6
|
+
# Represents a Message object that is used in OpenAI Chat API requests
|
7
|
+
# This object is referenced by both ChatRequester and ChatCompletion objects
|
8
|
+
class Message
|
9
|
+
# The role of the author of this message.
|
10
|
+
# One of: user, assistant, system
|
11
|
+
# Default: system
|
12
|
+
attr_reader :role
|
13
|
+
|
14
|
+
# The contents of the message.
|
15
|
+
attr_reader :content
|
16
|
+
|
17
|
+
# Provides the model information to differentiate between participants of the same role.
|
18
|
+
attr_reader :name
|
19
|
+
|
20
|
+
# The tool calls generated by the model, such as function calls. (assistant msg only)
|
21
|
+
attr_reader :tool_calls
|
22
|
+
|
23
|
+
# Tool call that this message is responding to. (tool msg only)
|
24
|
+
attr_reader :tool_call_id
|
25
|
+
|
26
|
+
# Initializes the Message object
|
27
|
+
#
|
28
|
+
# @param [String, Hash] options The message content
|
29
|
+
def initialize(options = {})
|
30
|
+
if options.is_a?(String)
|
31
|
+
@role = "system"
|
32
|
+
@content = options
|
33
|
+
else
|
34
|
+
attributes_from_options(options)
|
35
|
+
end
|
36
|
+
json_content_parse
|
37
|
+
end
|
38
|
+
|
39
|
+
def json?
|
40
|
+
!!@json_content
|
41
|
+
end
|
42
|
+
|
43
|
+
def empty?
|
44
|
+
content.nil? || content.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_h
|
48
|
+
{ role:, content:, name:, tool_calls:, tool_call_id: }.compact
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def attributes_from_options(options)
|
54
|
+
@role = options[:role] || "system"
|
55
|
+
@content = options[:content]
|
56
|
+
@name = options[:name]
|
57
|
+
@tool_calls = options[:tool_calls]
|
58
|
+
@tool_call_id = options[:tool_call_id]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Parses the content if it is a JSON string
|
62
|
+
# This is used when the request is made with json: true flag
|
63
|
+
# https://platform.openai.com/docs/guides/text-generation/json-mode
|
64
|
+
def json_content_parse
|
65
|
+
return unless @content.is_a?(String) && @content.start_with?("{", "[")
|
66
|
+
|
67
|
+
@content = JSON.parse(@content, symbolize_names: true)
|
68
|
+
@json_content = true
|
69
|
+
rescue JSON::ParserError
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
|
5
|
+
module Rubygpt
|
6
|
+
module Connection
|
7
|
+
# The HTTP connection adapter derived from Faraday::Connection
|
8
|
+
class Faraday < Faraday::Connection
|
9
|
+
# Initializes a new connection object
|
10
|
+
#
|
11
|
+
# @param [Configuration] configuration
|
12
|
+
# @param [Hash] faraday_options
|
13
|
+
def initialize(configuration, faraday_options = {})
|
14
|
+
options = { url: configuration.api_url, headers: configuration.to_headers }.merge(faraday_options)
|
15
|
+
super(options) do |faraday|
|
16
|
+
faraday.request :json
|
17
|
+
faraday.response :json
|
18
|
+
faraday.response :raise_error
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def post(*args)
|
23
|
+
faraday_response = super(*args)
|
24
|
+
Rubygpt::Response::StandardApiResponse.new(
|
25
|
+
adapter_response: faraday_response,
|
26
|
+
**faraday_response.slice(:status, :body, :headers)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubygpt
|
4
|
+
# Moderates available connection adapters
|
5
|
+
module Connection
|
6
|
+
class << self
|
7
|
+
# Find and initialize the connection adapter
|
8
|
+
#
|
9
|
+
# @param [Configuration] configuration
|
10
|
+
# @param [Hash] options
|
11
|
+
def new(configuration, options = {})
|
12
|
+
const_get(configuration.connection_adapter.to_s.capitalize).new(configuration, options)
|
13
|
+
rescue NameError
|
14
|
+
raise Client::Configuration::InvalidConfigurationError, "Invalid adapter provided for connection."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubygpt
|
4
|
+
module Requester
|
5
|
+
# Performs CRUD operations for OpenAI Chat Completion Objects
|
6
|
+
# https://platform.openai.com/docs/api-reference/chat
|
7
|
+
class ChatRequester < BaseRequester
|
8
|
+
# Initializes the ChatRequester
|
9
|
+
#
|
10
|
+
# @param [Client] client The client object
|
11
|
+
def initialize(client)
|
12
|
+
@api_endpoint = "chat/completions"
|
13
|
+
super(client)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Performs a POST request to the API endpoint
|
17
|
+
# https://platform.openai.com/docs/api-reference/chat/create
|
18
|
+
#
|
19
|
+
# @param [String] args The single message to send. Message will be sent with system role
|
20
|
+
# @param [Array] args The array of messages to send. Each message can be a string or a hash
|
21
|
+
# @param [Hash] args The arguments for the request body, including messages
|
22
|
+
# @option args [Array] :messages The messages to send
|
23
|
+
#
|
24
|
+
# @return [Response::ChatCompletion]
|
25
|
+
def create(args = {})
|
26
|
+
# TODO: handle args[:stream] for streaming completions
|
27
|
+
Response::ChatCompletion.new client.post(api_endpoint, create_request_body(args))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Builds the request body for the POST request
|
33
|
+
def create_request_body(args)
|
34
|
+
request_body = { model: client.configuration.model }
|
35
|
+
if args.is_a?(Hash)
|
36
|
+
# Allowing JSON mode for the request
|
37
|
+
# https://platform.openai.com/docs/guides/text-generation/json-mode
|
38
|
+
request_body[:response_format] = { type: "json_object" } if args[:json]
|
39
|
+
request_body[:messages] = messages_from_hash(args)
|
40
|
+
request_body.merge! args.except(:messages, :json)
|
41
|
+
else
|
42
|
+
request_body[:messages] = messages_from_args(args)
|
43
|
+
end
|
44
|
+
request_body.compact
|
45
|
+
end
|
46
|
+
|
47
|
+
# Handles the message data provided as arguments
|
48
|
+
def messages_from_args(args)
|
49
|
+
messages =
|
50
|
+
case args
|
51
|
+
when String then [Common::Message.new(args)]
|
52
|
+
when Array then args.map { |message| Common::Message.new(message) }
|
53
|
+
else raise ArgumentError, "Invalid message data provided"
|
54
|
+
end
|
55
|
+
map_messages(messages)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handles the message data provided as a hash
|
59
|
+
def messages_from_hash(hash)
|
60
|
+
messages =
|
61
|
+
if hash.key?(:messages)
|
62
|
+
# If it is a hash with a :messages key, then it's a request config with messages provided explicitly
|
63
|
+
# { messages: [{ content: 'foo', role: 'user' }, { content: 'bar', role: 'system' }] }
|
64
|
+
hash[:messages].map { |message| Common::Message.new(message) }
|
65
|
+
else
|
66
|
+
# If it is a hash without a :messages key, then it's a message config itself
|
67
|
+
# { content: 'foo', role: 'user' }
|
68
|
+
[Common::Message.new(hash)]
|
69
|
+
end
|
70
|
+
map_messages(messages)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Maps the messages to a hash
|
74
|
+
# Validates the messages and raises an error if no messages are provided
|
75
|
+
def map_messages(messages)
|
76
|
+
messages.map do |message|
|
77
|
+
raise ArgumentError, "Empty message contents found." if message.empty?
|
78
|
+
|
79
|
+
message.to_h
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubygpt
|
4
|
+
module Requester
|
5
|
+
# Base module for all requester modules
|
6
|
+
class BaseRequester
|
7
|
+
# The client object that will be used to make the request
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
# The API endpoint for the request
|
11
|
+
attr_reader :api_endpoint
|
12
|
+
|
13
|
+
# Initializes new Rubygpt::Requester object
|
14
|
+
#
|
15
|
+
# @param [Client] client
|
16
|
+
def initialize(client)
|
17
|
+
@client = client
|
18
|
+
end
|
19
|
+
|
20
|
+
# Performs a POST request to the API endpoint
|
21
|
+
def create
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubygpt
|
4
|
+
module Response
|
5
|
+
# Represents the ChatCompletion response from the Chat API
|
6
|
+
# https://platform.openai.com/docs/api-reference/chat/object
|
7
|
+
class ChatCompletion < BaseResponse
|
8
|
+
# Represents a Choice object in the ChatCompletion response
|
9
|
+
class Choice
|
10
|
+
# The index of the choice in the list of choices.
|
11
|
+
attr_reader :index
|
12
|
+
|
13
|
+
# A chat completion message generated by the model.
|
14
|
+
# A Message object
|
15
|
+
attr_reader :message
|
16
|
+
|
17
|
+
# Log probability information for the choice.
|
18
|
+
attr_reader :logprobs
|
19
|
+
|
20
|
+
# The reason the model stopped generating tokens.
|
21
|
+
# One of: stop, length, content_filter, tool_calls
|
22
|
+
attr_reader :finish_reason
|
23
|
+
|
24
|
+
# Initializes the Choice object
|
25
|
+
def initialize(index:, message:, logprobs:, finish_reason:)
|
26
|
+
@index = index
|
27
|
+
@message = Common::Message.new(**message.transform_keys(&:to_sym))
|
28
|
+
@logprobs = logprobs
|
29
|
+
@finish_reason = finish_reason
|
30
|
+
end
|
31
|
+
|
32
|
+
# Delegations to the message object
|
33
|
+
def content
|
34
|
+
message.content
|
35
|
+
end
|
36
|
+
|
37
|
+
def role
|
38
|
+
message.role
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns true if the choice failed to generate a complete message
|
42
|
+
def failed?
|
43
|
+
finish_reason != "stop"
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_h
|
47
|
+
{ index:, message: message.to_h, logprobs:, finish_reason: }.compact
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Readers for the standard attributes of the ChatCompletion object
|
52
|
+
attr_reader :id, :object, :created, :model, :system_fingerprint, :usage, :choices
|
53
|
+
|
54
|
+
# Initializes the ChatCompletion object
|
55
|
+
#
|
56
|
+
# @param [StandardApiResponse] api_response The response from the API, standardized by the connection object
|
57
|
+
def initialize(api_response)
|
58
|
+
super
|
59
|
+
@choices = @choices.map { |choice| Choice.new(**choice.transform_keys(&:to_sym)) }.sort_by(&:index) if @choices
|
60
|
+
end
|
61
|
+
|
62
|
+
# The list of message strings generated by the model.
|
63
|
+
#
|
64
|
+
# return [Array<String>]
|
65
|
+
def messages
|
66
|
+
@messages ||= choices.map { |choice| choice.message.content }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Messages combined into a single string, separated by newlines
|
70
|
+
def read(message_separator: "\n")
|
71
|
+
@read ||= messages.join(message_separator)
|
72
|
+
end
|
73
|
+
|
74
|
+
def failed?
|
75
|
+
choices.any?(&:failed?)
|
76
|
+
end
|
77
|
+
|
78
|
+
def cost
|
79
|
+
usage[:total_tokens]
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_h
|
83
|
+
{ id:, object:, created:, model:, system_fingerprint:, usage:, choices: choices.map(&:to_h) }.compact
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubygpt
|
4
|
+
module Response
|
5
|
+
class ConnectionReturnNotStandardizedError < StandardError; end
|
6
|
+
|
7
|
+
# Unifies responses from all connection/adapter objects.
|
8
|
+
# This is required since a response from Faraday will not be the same as a response from another adapter
|
9
|
+
# that might be introduced in the future. To solve this,
|
10
|
+
# we're overriding the post, get, put, delete methods in connection objects to return a StandardApiResponse
|
11
|
+
class StandardApiResponse
|
12
|
+
# The HTTP package contents of the response
|
13
|
+
attr_reader :status, :body, :headers
|
14
|
+
|
15
|
+
# The original, non-standardized response object received from the adapter
|
16
|
+
# This is useful for debugging and for accessing adapter-specific features
|
17
|
+
attr_reader :adapter_response
|
18
|
+
|
19
|
+
# @param [Integer] status The HTTP status code
|
20
|
+
# @param [Hash] body The response body
|
21
|
+
# @param [Hash] headers The response headers
|
22
|
+
# @param [Object] adapter_response The original, non-standardized response object received from the adapter
|
23
|
+
def initialize(status:, body:, headers:, adapter_response: nil)
|
24
|
+
@status = status
|
25
|
+
@body = body
|
26
|
+
@headers = headers
|
27
|
+
@adapter_response = adapter_response
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Base class for all API response objects
|
32
|
+
class BaseResponse
|
33
|
+
# The response from the API, standardized by the connection object
|
34
|
+
attr_reader :api_response
|
35
|
+
|
36
|
+
# @param [StandardApiResponse] api_response The response from the API, standardized by the connection object
|
37
|
+
def initialize(api_response)
|
38
|
+
raise ConnectionReturnNotStandardizedError unless api_response.is_a?(StandardApiResponse)
|
39
|
+
|
40
|
+
@api_response = api_response
|
41
|
+
build_attributes_from_body
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Builds instance variables dynamically from the response body
|
47
|
+
def build_attributes_from_body
|
48
|
+
@api_response.body&.each do |key, value|
|
49
|
+
variable_name = key.to_s.downcase
|
50
|
+
instance_variable_set("@#{variable_name}", value)
|
51
|
+
self.class.send(:attr_reader, variable_name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/rubygpt.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "rubygpt/version"
|
4
|
+
require_relative "rubygpt/connection/faraday"
|
5
|
+
require_relative "rubygpt/connection"
|
6
|
+
require_relative "rubygpt/client/configuration"
|
7
|
+
require_relative "rubygpt/client"
|
8
|
+
require_relative "rubygpt/common/message"
|
9
|
+
require_relative "rubygpt/response"
|
10
|
+
require_relative "rubygpt/response/chat_completion"
|
11
|
+
require_relative "rubygpt/requester"
|
12
|
+
require_relative "rubygpt/requester/chat_requester"
|
13
|
+
|
14
|
+
# The main module for Rubygpt
|
15
|
+
# Contains static methods for configuration
|
16
|
+
module Rubygpt
|
17
|
+
class << self
|
18
|
+
# The default singleton client for the module
|
19
|
+
# Allows a global configuration to be set across the module
|
20
|
+
#
|
21
|
+
# @return [Client]
|
22
|
+
def configure(configuration = nil, &block)
|
23
|
+
reset_requesters
|
24
|
+
@default_client = Client.new(configuration, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# A ChatRequester object, configured by default client
|
28
|
+
# Allows the Rubygpt.chat calls
|
29
|
+
#
|
30
|
+
# @return [Requester::ChatRequester]
|
31
|
+
def chat
|
32
|
+
@chat ||= Requester::ChatRequester.new(@default_client)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Remove memoized requester objects to allow reallocation
|
36
|
+
def reset_requesters
|
37
|
+
@chat = nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/sig/rubygpt.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubygpt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Furkan Enes Apaydin
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-03-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.7.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.7.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.13.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.13.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.23.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.23.0
|
69
|
+
description: ''
|
70
|
+
email:
|
71
|
+
- feapaydin@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".rspec"
|
77
|
+
- ".rubocop.yml"
|
78
|
+
- ".ruby-version"
|
79
|
+
- CHANGELOG.md
|
80
|
+
- CODE_OF_CONDUCT.md
|
81
|
+
- MIT-LICENSE
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- SECURITY.md
|
85
|
+
- lib/rubygpt.rb
|
86
|
+
- lib/rubygpt/client.rb
|
87
|
+
- lib/rubygpt/client/configuration.rb
|
88
|
+
- lib/rubygpt/common/message.rb
|
89
|
+
- lib/rubygpt/connection.rb
|
90
|
+
- lib/rubygpt/connection/faraday.rb
|
91
|
+
- lib/rubygpt/requester.rb
|
92
|
+
- lib/rubygpt/requester/chat_requester.rb
|
93
|
+
- lib/rubygpt/response.rb
|
94
|
+
- lib/rubygpt/response/chat_completion.rb
|
95
|
+
- lib/rubygpt/version.rb
|
96
|
+
- sig/rubygpt.rbs
|
97
|
+
homepage: https://github.com/feapaydin/rubygpt
|
98
|
+
licenses: []
|
99
|
+
metadata:
|
100
|
+
homepage_uri: https://github.com/feapaydin/rubygpt
|
101
|
+
source_code_uri: https://github.com/feapaydin/rubygpt
|
102
|
+
changelog_uri: https://github.com/feapaydin/rubygpt/blob/main/CHANGELOG.md
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 3.2.0
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubygems_version: 3.4.1
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Ruby wrapper for OpenAI's ChatGPT APIs.
|
122
|
+
test_files: []
|