summarize-meeting 1.4.0 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -27
- data/bin/summarize-meeting +4 -4
- data/lib/summarize-meeting/ai.rb +122 -23
- data/lib/summarize-meeting/version.rb +2 -2
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f46de38135486b2bb4c89b9f3a5bbd205685f6dbb3cba5b9927acdbf0490dc56
|
4
|
+
data.tar.gz: 14c08f5210e5752e7c6581f047d83a52b8cc4a1d487b802c0aa928895e06f01e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a6eea93e686093632a577d1a090e2036b91ae96b230e7fe1e37da928ec5b08730d8f6ab82954c88b98f277b791f8180f69456ccbd06d2df8ca7582de2cdabfb
|
7
|
+
data.tar.gz: 06a7efc7fa8c1eb400f2ac2671589829179f2abaf8c0c4741b4577926a15938cf98f71db01593996dc98c5c097583d3c67a5d07d0374a391468eaeb6b5d4e6ba
|
data/README.md
CHANGED
@@ -18,79 +18,79 @@ Input a meeting transcript and output a summary.
|
|
18
18
|
|
19
19
|
#### Options
|
20
20
|
|
21
|
-
`-o, --output-file FILE` - Path to the output file. By default, it will be the `${input-file}-summary.txt`
|
22
|
-
`-k, --openai-key KEY` - The OpenAI API Key. Can also be set via `ENV["
|
23
|
-
`-g, --openai-org ORG` - The OpenAI org id. It can also be set via `ENV["
|
21
|
+
`-o, --output-file FILE` - Path to the output file. By default, it will be the `${input-file}-summary.txt`
|
22
|
+
`-k, --openai-key KEY` - The OpenAI API Key. Can also be set via `ENV["OPEN_AI_API_KEY"]`.
|
23
|
+
`-g, --openai-org ORG` - The OpenAI org id (optional). It can also be set via `ENV["OPEN_AI_ORGANIZATION_ID"]` .
|
24
24
|
|
25
25
|
## Example
|
26
26
|
|
27
27
|
This is an example summary from a 3,700 word transcript from a 30 minute meeting.
|
28
28
|
|
29
29
|
> Summary of the Meeting:
|
30
|
-
>
|
30
|
+
>
|
31
31
|
> The meeting started with discussions focusing on individual progress reports, where Sean shared updates on profit-related work, consolidating estimates, and improving Hey Kayla by creating a new JSON structure for Chat GPT messages API. Colenso reported finding a bug in the JavaScript Google Maps API and not making progress on push notifications. Meanwhile, Pankaj worked on bulk assign form handling, retaining time zone, and Benjamin faced some issues regarding database connection but eventually resolved the casting and integration issues.
|
32
|
-
>
|
32
|
+
>
|
33
33
|
> In the open discussion session, the team discussed the confusion over parameters and importing across different branches. Benjamin shared about an open discussion necessary for a feature related to job production plans at ACME. Shirish discussed fixing the cycle time summary query, which used to take up to 6-7 seconds and now takes less than 1 second. Anish added a business unit filter feature to the time card and will work on page titles.
|
34
|
-
>
|
34
|
+
>
|
35
35
|
> The meeting concluded with Sean hoping to have success with profit stuff before Monday's call.
|
36
|
-
>
|
36
|
+
>
|
37
37
|
> Attendees:
|
38
|
-
>
|
38
|
+
>
|
39
39
|
> - Sean
|
40
40
|
> - Colenso
|
41
41
|
> - Pankaj
|
42
42
|
> - Benjamin
|
43
43
|
> - Shirish
|
44
44
|
> - Anish
|
45
|
-
>
|
45
|
+
>
|
46
46
|
> Action Items:
|
47
|
-
>
|
47
|
+
>
|
48
48
|
> - Sean to continue working on profit-related things before the next call.
|
49
49
|
> - Colenso to continue working on resolving the bug in the Google Maps API and push notifications.
|
50
50
|
> - Pankaj to continue working on bulk assign form handling and to keep the time zone option.
|
51
51
|
> - Benjamin to finalize the release notes for the open discussion at and to continue working on resolving the database connection issues.
|
52
52
|
> - Shirish to further improve the query for cycle time summary and reduce the processing time.
|
53
|
-
> - Anish to improve page titles and add filters for the time card.
|
54
|
-
>
|
53
|
+
> - Anish to improve page titles and add filters for the time card.
|
54
|
+
>
|
55
55
|
> Detailed notes:
|
56
|
-
>
|
56
|
+
>
|
57
57
|
> Sean:
|
58
|
-
>
|
58
|
+
>
|
59
59
|
> - Reported progress on Prophet-related things
|
60
60
|
> - Worked on consolidating all effective estimates
|
61
61
|
> - Worked on improving Hey Kayla by creating a new JSON structure for Chat GPT messages API
|
62
|
-
>
|
62
|
+
>
|
63
63
|
> Colenso:
|
64
|
-
>
|
64
|
+
>
|
65
65
|
> - Found a bug in the JavaScript Google Maps API
|
66
66
|
> - Has not made progress on push notifications yet
|
67
|
-
>
|
67
|
+
>
|
68
68
|
> Pankaj:
|
69
|
-
>
|
69
|
+
>
|
70
70
|
> - Worked on bulk assign form handling
|
71
71
|
> - Allowed the option to retain the time zone
|
72
|
-
>
|
72
|
+
>
|
73
73
|
> Benjamin:
|
74
|
-
>
|
74
|
+
>
|
75
75
|
> - Faced difficulty regarding database connection issues in Heroku
|
76
76
|
> - Resolved casting issues by switching to a different cash store
|
77
77
|
> - Listed an interesting issue regarding split loads with more than one material transaction during the same trip
|
78
78
|
> - Mentioned an integration issue with ACME
|
79
79
|
> - Shared that there is an open discussion at ACME and he needs assistance with the release notes for a feature related to job production plans
|
80
|
-
>
|
80
|
+
>
|
81
81
|
> Open Discussion:
|
82
|
-
>
|
82
|
+
>
|
83
83
|
> - The team discussed the confusion over parameters and importing across different branches.
|
84
|
-
>
|
84
|
+
>
|
85
85
|
> Shirish:
|
86
|
-
>
|
86
|
+
>
|
87
87
|
> - Discussed fixing the cycle time summary query, which used to take up to 6-7 seconds and now takes less than 1 second
|
88
|
-
>
|
88
|
+
>
|
89
89
|
> Anish:
|
90
|
-
>
|
90
|
+
>
|
91
91
|
> - Added a business unit filter feature to the time card
|
92
92
|
> - Will work on improving page titles
|
93
93
|
|
94
94
|
## Credits
|
95
95
|
|
96
|
-
This gem was created by XBE.
|
96
|
+
This gem was created by XBE.
|
data/bin/summarize-meeting
CHANGED
@@ -18,16 +18,16 @@ def main
|
|
18
18
|
options[:output_file] = file
|
19
19
|
end
|
20
20
|
|
21
|
-
if ENV["
|
22
|
-
options[:openai_key] = ENV["
|
21
|
+
if ENV["OPEN_AI_API_KEY"]
|
22
|
+
options[:openai_key] = ENV["OPEN_AI_API_KEY"]
|
23
23
|
end
|
24
24
|
|
25
25
|
opts.on("-k", "--openai-key KEY", "The OpenAI API key to use") do |key|
|
26
26
|
options[:openai_key] = key
|
27
27
|
end
|
28
28
|
|
29
|
-
if ENV["
|
30
|
-
options[:openai_org] = ENV["
|
29
|
+
if ENV["OPEN_AI_ORGANIZATION_ID"]
|
30
|
+
options[:openai_org] = ENV["OPEN_AI_ORGANIZATION_ID"]
|
31
31
|
end
|
32
32
|
|
33
33
|
opts.on("-g", "--openai-org ORG", "The OpenAI organization ID to use") do |org|
|
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
|
+
return @access_token if defined?(@access_token)
|
29
|
+
@access_token = ENV.fetch("OPEN_AI_API_KEY") do
|
30
|
+
fail KeyError, "Set OPEN_AI_API_KEY in the the ENV or set access_token directly"
|
31
|
+
end
|
20
32
|
end
|
21
33
|
|
22
|
-
def
|
23
|
-
|
34
|
+
def organization_id
|
35
|
+
return @organization_id if defined?(@organization_id)
|
36
|
+
@organization_id = ENV.fetch("OPEN_AI_ORGANIZATION_ID", nil)
|
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)
|
51
66
|
else
|
52
|
-
|
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)
|
85
|
+
else
|
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.1"
|
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.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Devine
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: optparse
|
@@ -151,11 +151,11 @@ files:
|
|
151
151
|
- lib/summarize-meeting/ai.rb
|
152
152
|
- lib/summarize-meeting/meeting.rb
|
153
153
|
- lib/summarize-meeting/version.rb
|
154
|
-
homepage:
|
154
|
+
homepage:
|
155
155
|
licenses:
|
156
156
|
- MIT
|
157
157
|
metadata: {}
|
158
|
-
post_install_message:
|
158
|
+
post_install_message:
|
159
159
|
rdoc_options: []
|
160
160
|
require_paths:
|
161
161
|
- lib
|
@@ -170,8 +170,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
170
|
- !ruby/object:Gem::Version
|
171
171
|
version: '0'
|
172
172
|
requirements: []
|
173
|
-
rubygems_version: 3.
|
174
|
-
signing_key:
|
173
|
+
rubygems_version: 3.3.7
|
174
|
+
signing_key:
|
175
175
|
specification_version: 4
|
176
176
|
summary: A command line utility that summarizes a meeting
|
177
177
|
test_files: []
|