summarize-meeting 1.4.0 → 1.5.0
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 +4 -4
- data/lib/summarize-meeting/ai.rb +122 -23
- data/lib/summarize-meeting/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1f1e203f7328efbedef8a044434bb8377aac4d8cdf5a3ffc61d903d6155b211
|
4
|
+
data.tar.gz: 4b93b413a93e5f2d6cbe1d46c842889a559f32682e19b4b1611115e2b928b5fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77a9c6ecd2f9fc14c949cc5fab380981d1016ba363332c2c1bc55d2e01433acaeaa9fa3caf5eaee9c17488255da2bfb1ec33487fc624f34dcb5f4da1a97def03
|
7
|
+
data.tar.gz: 2cde7420a3e4b5628eaae774ca6c29ca6b5be1409dd42c87ab1bd216ad749557e92b68264059fdc50fe92ec4bd8fbd921b69dc0d156d2f38493916b5e19b0c70
|
data/lib/summarize-meeting/ai.rb
CHANGED
@@ -1,56 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openai"
|
4
|
+
|
1
5
|
module SummarizeMeeting
|
2
6
|
module Ai
|
3
|
-
|
7
|
+
module OpenAIException; end
|
8
|
+
class OpenAIError < StandardError
|
9
|
+
include OpenAIException
|
10
|
+
end
|
11
|
+
class OpenAiResponseFailure < OpenAIError; end
|
12
|
+
class OpenAiNoResponseError < OpenAIError; end
|
13
|
+
class OpenAiRetryableError < OpenAIError; end
|
14
|
+
extend self
|
15
|
+
|
4
16
|
MAX_TOTAL_TOKENS = 8000
|
5
17
|
WORDS_PER_TOKEN = 0.75
|
6
18
|
|
7
|
-
|
8
|
-
@@organization_id = ENV["OPENAI_ORG"]
|
9
|
-
|
10
|
-
def self.client
|
19
|
+
def client
|
11
20
|
@client ||= new_client(access_token: access_token, organization_id: organization_id)
|
12
21
|
end
|
13
22
|
|
14
|
-
def
|
23
|
+
def new_client(access_token:, organization_id:)
|
15
24
|
OpenAI::Client.new(access_token: access_token, organization_id: organization_id)
|
16
25
|
end
|
17
26
|
|
18
|
-
def
|
19
|
-
|
27
|
+
def access_token
|
28
|
+
@access_token ||= ENV.fetch("OPENAI_KEY") do
|
29
|
+
fail KeyError, "Set OPENAI_KEY in the the ENV or set access_token directly"
|
30
|
+
end
|
20
31
|
end
|
21
32
|
|
22
|
-
def
|
23
|
-
|
33
|
+
def organization_id
|
34
|
+
@organization_id ||= ENV.fetch("OPENAI_ORG") do
|
35
|
+
fail KeyError, "Set OPENAI_ORG in the the ENV or set organization_id directly"
|
36
|
+
end
|
24
37
|
end
|
25
38
|
|
26
|
-
def
|
27
|
-
|
39
|
+
def access_token=(token)
|
40
|
+
@access_token = token
|
28
41
|
end
|
29
42
|
|
30
|
-
def
|
31
|
-
|
43
|
+
def organization_id=(id)
|
44
|
+
@organization_id = id
|
32
45
|
end
|
33
46
|
|
34
|
-
def
|
47
|
+
def calculate_token_word_count(token_count)
|
35
48
|
(token_count * WORDS_PER_TOKEN.to_f).ceil
|
36
49
|
end
|
37
50
|
|
38
|
-
def
|
51
|
+
def calculate_word_token_count(word_count)
|
39
52
|
(word_count / WORDS_PER_TOKEN.to_f).ceil
|
40
53
|
end
|
41
54
|
|
42
|
-
|
55
|
+
# @raise on response error or no expected response content
|
56
|
+
def chat(messages, client: self.client)
|
43
57
|
parameters = {
|
44
58
|
model: "gpt-4",
|
45
59
|
messages: messages,
|
46
60
|
}
|
47
|
-
response =
|
48
|
-
|
49
|
-
|
50
|
-
|
61
|
+
response = nil
|
62
|
+
begin
|
63
|
+
response = client.chat(parameters: parameters)
|
64
|
+
rescue => exception
|
65
|
+
handle_request_error(exception)
|
66
|
+
else
|
67
|
+
is_failure = !response.success?
|
68
|
+
if is_failure
|
69
|
+
log_response_failure(response)
|
70
|
+
raise OpenAiResponseFailure, "Failed response from OpenAI"
|
71
|
+
else
|
72
|
+
content = response.dig("choices", 0, "message", "content")
|
73
|
+
if !content
|
74
|
+
raise OpenAiNoResponseError, "No response from OpenAI in #{response.body}"
|
75
|
+
else
|
76
|
+
content
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def notify_error(exception, message, **options)
|
83
|
+
if exception_notifier
|
84
|
+
exception_notifier.call(exception, message, **options)
|
51
85
|
else
|
52
|
-
|
86
|
+
warn "#{exception.inspect}: #{message}. options=#{options.inspect}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# responds to call(exception, message, **options)
|
91
|
+
def exception_notifier=(exception_notifier)
|
92
|
+
@exception_notifier = exception_notifier
|
93
|
+
end
|
94
|
+
|
95
|
+
def exception_notifier
|
96
|
+
return @exception_notifier if defined?(@exception_notifier)
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def handle_request_error(exception)
|
101
|
+
is_openai_error =
|
102
|
+
case exception.inspect
|
103
|
+
when /Connection refused/i then true
|
104
|
+
when /Connection reset/i then true
|
105
|
+
when /Connection timed out/i then true
|
106
|
+
when /Could not connect/i then true
|
107
|
+
when /EOFError/i then true
|
108
|
+
when /Errno::ECONNREFUSED/i then true
|
109
|
+
when /Errno::ECONNRESET/i then true
|
110
|
+
when /Net::OpenTimeout/i then true
|
111
|
+
when /Net::ReadTimeout/i then true
|
112
|
+
when /OpenSSL::SSL::SSLError/i then false
|
113
|
+
when /Server is unavailable or does not exist/i then true
|
114
|
+
when /Unable to connect/i then true
|
115
|
+
when /closed stream/i then true
|
116
|
+
when /connect timed out/i then true
|
117
|
+
when /end of file reached/i then true
|
118
|
+
when /execution expired/i then true
|
119
|
+
else false
|
120
|
+
end
|
121
|
+
if is_openai_error
|
122
|
+
exception.extend OpenAIException
|
123
|
+
end
|
124
|
+
raise
|
125
|
+
end
|
126
|
+
|
127
|
+
def log_response_failure(response)
|
128
|
+
response_details = {
|
129
|
+
# https://github.com/jnunemaker/httparty/blob/v0.21.0/lib/httparty/response.rb#L53-L56
|
130
|
+
response: response.to_s,
|
131
|
+
}
|
132
|
+
if response.class.name.match?(/HTTParty::Response/)
|
133
|
+
response_details.merge!(
|
134
|
+
raw_request: response.request.inspect,
|
135
|
+
raw_response: response.response.inspect,
|
136
|
+
response_body: response.body,
|
137
|
+
response_code: response.code,
|
138
|
+
response_headers: response.headers,
|
139
|
+
# marshal_dump_base_64: Base64.encode64(response._dump(1)), # so it can be encoded as JSON
|
140
|
+
)
|
53
141
|
end
|
142
|
+
notify_error(
|
143
|
+
nil, # there's no exception object
|
144
|
+
"Logging OpenAI Error",
|
145
|
+
**response_details
|
146
|
+
)
|
147
|
+
rescue => exception
|
148
|
+
notify_error(
|
149
|
+
exception,
|
150
|
+
"Unhandled error in #{name}##{__callee__}",
|
151
|
+
response: response.inspect,
|
152
|
+
)
|
54
153
|
end
|
55
154
|
end
|
56
|
-
end
|
155
|
+
end
|
@@ -1,3 +1,3 @@
|
|
1
1
|
module SummarizeMeeting
|
2
|
-
VERSION = "1.
|
3
|
-
end
|
2
|
+
VERSION = "1.5.0"
|
3
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: summarize-meeting
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Devine
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: optparse
|