ruby-openai 5.0.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f487bd64d6f7a7c4f0ccc3198d9bec592b199792763b6885b12b21f267ff80a
4
- data.tar.gz: 566af61bb906edbb2315ee343aae62f82e6837aa318dfb505d67d73497fcb27a
3
+ metadata.gz: 996d39cd32c3c05c73efea0177c12d0751b5dda208b2855aaac440af7b2702d8
4
+ data.tar.gz: 65471a670e34f537fe4878322c87978f1c2beaf93336a7f2104baaa86b018c60
5
5
  SHA512:
6
- metadata.gz: 7e500ce6b1cff92bdb78b4cf455aac251cb229e43e52c4c863a98f44a351ac1ef4abd9692dde13f822f458b7373ee063e34dc0670b72dc67e02827533e316b13
7
- data.tar.gz: 5d90d4ae80e14da163655a29d598c09834cfd0cad728bebe18f09b015aff7fa0881e736408360b228aa4921497de3f41b6122ee0a5e7a6011001b32403f70b41
6
+ metadata.gz: deab41c7c7f4ee21b4ed1a17f289b147b2e4960b33fd12ce863d5bdb8c835a955215d01438890c1ab8d9a1c7026faba0e5b8359c1fe3d9139082f8de58dce616
7
+ data.tar.gz: 3309d1c3a68736816c4f3bd1d465021ee3f162b5f5c3dbb7915ed5ce6f3a8d7014f9f1c4b07cf630f3f90201bdbe0ec308f1dc00fb6b075f45546fe519afb553
@@ -0,0 +1,16 @@
1
+ FROM ruby:3.2.2-slim-bullseye
2
+
3
+ ENV TZ="Europe/London"
4
+
5
+ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
6
+ && apt-get -y install --no-install-recommends \
7
+ apt-utils \
8
+ build-essential \
9
+ curl \
10
+ git \
11
+ vim \
12
+ zsh
13
+
14
+ RUN gem install bundler
15
+
16
+ WORKDIR /workspace
@@ -0,0 +1,36 @@
1
+ // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2
+ // https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/ruby-rails-postgres
3
+ // Update the VARIANT arg in docker-compose.yml to pick a Ruby version
4
+ {
5
+ "name": "ruby-openai",
6
+ "dockerComposeFile": "docker-compose.yml",
7
+ "service": "app",
8
+ "workspaceFolder": "/workspace",
9
+ "containerEnv": {
10
+ "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}",
11
+ "GITHUB_USER": "${localEnv:GITHUB_USER}"
12
+ },
13
+ // Configure tool-specific properties.
14
+ "customizations": {
15
+ // Configure properties specific to VS Code.
16
+ "vscode": {
17
+ // Add the IDs of extensions you want installed when the container is created.
18
+ "extensions": [
19
+ "rebornix.Ruby",
20
+ "sleistner.vscode-fileutils",
21
+ "ms-azuretools.vscode-docker",
22
+ "samverschueren.final-newline",
23
+ "GitHub.copilot",
24
+ "usernamehw.remove-empty-lines",
25
+ "wingrunr21.vscode-ruby",
26
+ ]
27
+ }
28
+ },
29
+ // Use 'postCreateCommand' to run commands after the container is created.
30
+ "postCreateCommand": "bundle install",
31
+ // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
32
+ "features": {
33
+ "git": "os-provided",
34
+ "github-cli": "latest"
35
+ }
36
+ }
@@ -0,0 +1,19 @@
1
+ version: "3"
2
+
3
+ services:
4
+ app:
5
+ build:
6
+ context: ..
7
+ dockerfile: .devcontainer/Dockerfile
8
+
9
+ volumes:
10
+ - ..:/workspace:cached
11
+ - bundle_cache:/bundle
12
+
13
+ command: sleep infinity
14
+
15
+ environment:
16
+ TZ: Europe/London
17
+
18
+ volumes:
19
+ bundle_cache:
@@ -0,0 +1,13 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: alexrudall
4
+ patreon: # Replace with a single Patreon username
5
+ open_collective: # Replace with a single Open Collective username
6
+ ko_fi: # Replace with a single Ko-fi username
7
+ tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8
+ community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9
+ liberapay: # Replace with a single Liberapay username
10
+ issuehunt: # Replace with a single IssueHunt username
11
+ otechie: # Replace with a single Otechie username
12
+ lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13
+ custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
data/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.2.0] - 2023-10-30
9
+
10
+ ### Fix
11
+
12
+ - Added more spec-compliant SSE parsing: see here https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
13
+ - Fixes issue where OpenAI or an intermediary returns only partial JSON per chunk of streamed data
14
+ - Huge thanks to [@atesgoral](https://github.com/atesgoral) for this important fix!
15
+
16
+ ## [5.1.0] - 2023-08-20
17
+
18
+ ### Added
19
+
20
+ - Added rough_token_count to estimate tokens in a string according to OpenAI's "rules of thumb". Thank you to [@jamiemccarthy](https://github.com/jamiemccarthy) for the idea and implementation!
21
+
8
22
  ## [5.0.0] - 2023-08-14
9
23
 
10
24
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-openai (5.0.0)
4
+ ruby-openai (5.2.0)
5
+ event_stream_parser (>= 0.3.0, < 1.0.0)
5
6
  faraday (>= 1)
6
7
  faraday-multipart (>= 1)
7
8
 
@@ -16,6 +17,7 @@ GEM
16
17
  rexml
17
18
  diff-lcs (1.5.0)
18
19
  dotenv (2.8.1)
20
+ event_stream_parser (0.3.0)
19
21
  faraday (2.7.10)
20
22
  faraday-net_http (>= 2.0, < 3.1)
21
23
  ruby2_keywords (>= 0.0.4)
data/README.md CHANGED
@@ -8,11 +8,9 @@ Use the [OpenAI API](https://openai.com/blog/openai-api/) with Ruby! 🤖❤️
8
8
 
9
9
  Stream text with GPT-4, transcribe and translate audio with Whisper, or create images with DALL·E...
10
10
 
11
- [Ruby AI Builders Discord](https://discord.gg/k4Uc224xVD)
11
+ 🚢 Based in the UK and want to hire me? Now you can! [railsai.com](https://railsai.com?utm_source=ruby-openai&utm_medium=readme&utm_id=26072023)
12
12
 
13
- [Quick guide to streaming ChatGPT with Rails 7 and Hotwire](https://gist.github.com/alexrudall/cb5ee1e109353ef358adb4e66631799d)
14
-
15
- Follow me on [Twitter](https://twitter.com/alexrudall) for more Ruby / AI content
13
+ [🎮 Ruby AI Builders Discord](https://discord.gg/k4Uc224xVD) | [🐦 Twitter](https://twitter.com/alexrudall) | [🧠 Anthropic Gem](https://github.com/alexrudall/anthropic) | [🚂 Midjourney Gem](https://github.com/alexrudall/midjourney)
16
14
 
17
15
  ### Bundler
18
16
 
@@ -24,13 +22,17 @@ gem "ruby-openai"
24
22
 
25
23
  And then execute:
26
24
 
25
+ ```bash
27
26
  $ bundle install
27
+ ```
28
28
 
29
29
  ### Gem install
30
30
 
31
31
  Or install with:
32
32
 
33
+ ```bash
33
34
  $ gem install ruby-openai
35
+ ```
34
36
 
35
37
  and require with:
36
38
 
@@ -86,7 +88,8 @@ client = OpenAI::Client.new(
86
88
  extra_headers: {
87
89
  "X-Proxy-TTL" => "43200", # For https://github.com/6/openai-caching-proxy-worker#specifying-a-cache-ttl
88
90
  "X-Proxy-Refresh": "true", # For https://github.com/6/openai-caching-proxy-worker#refreshing-the-cache
89
- "Helicone-Auth": "Bearer HELICONE_API_KEY" # For https://docs.helicone.ai/getting-started/integration-method/openai-proxy
91
+ "Helicone-Auth": "Bearer HELICONE_API_KEY", # For https://docs.helicone.ai/getting-started/integration-method/openai-proxy
92
+ "helicone-stream-force-format" => "true", # Use this with Helicone otherwise streaming drops chunks # https://github.com/alexrudall/ruby-openai/issues/251
90
93
  }
91
94
  )
92
95
  ```
@@ -122,6 +125,18 @@ To use the [Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/cognit
122
125
 
123
126
  where `AZURE_OPENAI_URI` is e.g. `https://custom-domain.openai.azure.com/openai/deployments/gpt-35-turbo`
124
127
 
128
+ ### Counting Tokens
129
+
130
+ OpenAI parses prompt text into [tokens](https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them), which are words or portions of words. (These tokens are unrelated to your API access_token.) Counting tokens can help you estimate your [costs](https://openai.com/pricing). It can also help you ensure your prompt text size is within the max-token limits of your model's context window, and choose an appropriate [`max_tokens`](https://platform.openai.com/docs/api-reference/chat/create#chat/create-max_tokens) completion parameter so your response will fit as well.
131
+
132
+ To estimate the token-count of your text:
133
+
134
+ ```ruby
135
+ OpenAI.rough_token_count("Your text")
136
+ ```
137
+
138
+ If you need a more accurate count, try [tiktoken_ruby](https://github.com/IAPark/tiktoken_ruby).
139
+
125
140
  ### Models
126
141
 
127
142
  There are different models that can be used to generate text. For a full list and to retrieve information about a single model:
@@ -180,7 +195,7 @@ client.chat(
180
195
  # => "Anna is a young woman in her mid-twenties, with wavy chestnut hair that falls to her shoulders..."
181
196
  ```
182
197
 
183
- Note: the API docs state that token usage is included in the streamed chat chunk objects, but this doesn't currently appear to be the case. If you need to work out how many tokens are being used while streaming, try [tiktoken_ruby](https://github.com/IAPark/tiktoken_ruby).
198
+ Note: the API docs state that token usage is included in the streamed chat chunk objects, but this doesn't currently appear to be the case. To count tokens while streaming, try `OpenAI.rough_token_count` or [tiktoken_ruby](https://github.com/IAPark/tiktoken_ruby).
184
199
 
185
200
  ### Functions
186
201
 
data/lib/openai/http.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "event_stream_parser"
2
+
1
3
  module OpenAI
2
4
  module HTTP
3
5
  def get(path:)
@@ -53,15 +55,25 @@ module OpenAI
53
55
  # @param user_proc [Proc] The inner proc to call for each JSON object in the chunk.
54
56
  # @return [Proc] An outer proc that iterates over a raw stream, converting it to JSON.
55
57
  def to_json_stream(user_proc:)
56
- proc do |chunk, _|
57
- chunk.scan(/(?:data|error): (\{.*\})/i).flatten.each do |data|
58
- user_proc.call(JSON.parse(data))
59
- rescue JSON::ParserError
60
- # Ignore invalid JSON.
58
+ parser = EventStreamParser::Parser.new
59
+
60
+ proc do |chunk, _bytes, env|
61
+ if env && env.status != 200
62
+ emit_json(json: chunk, user_proc: user_proc)
63
+ else
64
+ parser.feed(chunk) do |_type, data|
65
+ emit_json(json: data, user_proc: user_proc) unless data == "[DONE]"
66
+ end
61
67
  end
62
68
  end
63
69
  end
64
70
 
71
+ def emit_json(json:, user_proc:)
72
+ user_proc.call(JSON.parse(json))
73
+ rescue JSON::ParserError
74
+ # Ignore invalid JSON.
75
+ end
76
+
65
77
  def conn(multipart: false)
66
78
  Faraday.new do |f|
67
79
  f.options[:timeout] = @request_timeout
@@ -1,3 +1,3 @@
1
1
  module OpenAI
2
- VERSION = "5.0.0".freeze
2
+ VERSION = "5.2.0".freeze
3
3
  end
data/lib/openai.rb CHANGED
@@ -52,4 +52,16 @@ module OpenAI
52
52
  def self.configure
53
53
  yield(configuration)
54
54
  end
55
+
56
+ # Estimate the number of tokens in a string, using the rules of thumb from OpenAI:
57
+ # https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
58
+ def self.rough_token_count(content = "")
59
+ raise ArgumentError, "rough_token_count requires a string" unless content.is_a? String
60
+ return 0 if content.empty?
61
+
62
+ count_by_chars = content.size / 4.0
63
+ count_by_words = content.split.size * 4.0 / 3
64
+ estimate = ((count_by_chars + count_by_words) / 2.0).round
65
+ [1, estimate].max
66
+ end
55
67
  end
data/ruby-openai.gemspec CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
27
 
28
+ spec.add_dependency "event_stream_parser", ">= 0.3.0", "< 1.0.0"
28
29
  spec.add_dependency "faraday", ">= 1"
29
30
  spec.add_dependency "faraday-multipart", ">= 1"
30
31
  end
metadata CHANGED
@@ -1,15 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-openai
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-14 00:00:00.000000000 Z
11
+ date: 2023-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: event_stream_parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.3.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: faraday
15
35
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +58,7 @@ dependencies:
38
58
  - - ">="
39
59
  - !ruby/object:Gem::Version
40
60
  version: '1'
41
- description:
61
+ description:
42
62
  email:
43
63
  - alexrudall@users.noreply.github.com
44
64
  executables: []
@@ -46,6 +66,10 @@ extensions: []
46
66
  extra_rdoc_files: []
47
67
  files:
48
68
  - ".circleci/config.yml"
69
+ - ".devcontainer/Dockerfile"
70
+ - ".devcontainer/devcontainer.json"
71
+ - ".devcontainer/docker-compose.yml"
72
+ - ".github/FUNDING.yml"
49
73
  - ".github/ISSUE_TEMPLATE/bug_report.md"
50
74
  - ".github/ISSUE_TEMPLATE/feature_request.md"
51
75
  - ".github/dependabot.yml"
@@ -83,7 +107,7 @@ metadata:
83
107
  source_code_uri: https://github.com/alexrudall/ruby-openai
84
108
  changelog_uri: https://github.com/alexrudall/ruby-openai/blob/main/CHANGELOG.md
85
109
  rubygems_mfa_required: 'true'
86
- post_install_message:
110
+ post_install_message:
87
111
  rdoc_options: []
88
112
  require_paths:
89
113
  - lib
@@ -98,8 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
122
  - !ruby/object:Gem::Version
99
123
  version: '0'
100
124
  requirements: []
101
- rubygems_version: 3.4.12
102
- signing_key:
125
+ rubygems_version: 3.4.10
126
+ signing_key:
103
127
  specification_version: 4
104
128
  summary: "OpenAI API + Ruby! \U0001F916❤️"
105
129
  test_files: []