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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07e3eaa4b04f7b2d9ccb00632ab021a85b87dd8a6798a72319d84ec3c905fa4f
4
- data.tar.gz: 200e57a22c74d4c7c418f2b34ec4fbec6f53119ca1e381266ce16d53ac680e36
3
+ metadata.gz: f46de38135486b2bb4c89b9f3a5bbd205685f6dbb3cba5b9927acdbf0490dc56
4
+ data.tar.gz: 14c08f5210e5752e7c6581f047d83a52b8cc4a1d487b802c0aa928895e06f01e
5
5
  SHA512:
6
- metadata.gz: a201d18bd409b281bf8e2e7610d4f98a37cc29dfff8f1a6b1c631d61fe88c00b43977b076bfa887e3f074bc64a6fa03951e2459cdc5baad738e47f5d0255d414
7
- data.tar.gz: d3ea73f7bfbc3f756324d2e72c49ff9cd67694ef7da7647b9e8b0dc08805f745d9d265a906e49ff49c55b08e406b5ddb8b8887d45be0399962a1efd7b1707964
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["OPENAI_KEY"]`.
23
- `-g, --openai-org ORG` - The OpenAI org id. It can also be set via `ENV["OPENAI_ORG"]` .
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.
@@ -18,16 +18,16 @@ def main
18
18
  options[:output_file] = file
19
19
  end
20
20
 
21
- if ENV["OPENAI_API_KEY"]
22
- options[:openai_key] = ENV["OPENAI_API_KEY"]
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["OPENAI_ORG"]
30
- options[:openai_org] = ENV["OPENAI_ORG"]
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|
@@ -1,56 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openai"
4
+
1
5
  module SummarizeMeeting
2
6
  module Ai
3
- class OpenAiError < StandardError; end
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
- @@access_token = ENV["OPENAI_KEY"]
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 self.new_client(access_token:, organization_id:)
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 self.access_token
19
- @@access_token
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 self.organization_id
23
- @@organization_id
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 self.access_token=(token)
27
- @@access_token = token
39
+ def access_token=(token)
40
+ @access_token = token
28
41
  end
29
42
 
30
- def self.organization_id=(id)
31
- @@organization_id = id
43
+ def organization_id=(id)
44
+ @organization_id = id
32
45
  end
33
46
 
34
- def self.calculate_token_word_count(token_count)
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 self.calculate_word_token_count(word_count)
51
+ def calculate_word_token_count(word_count)
39
52
  (word_count / WORDS_PER_TOKEN.to_f).ceil
40
53
  end
41
54
 
42
- def self.chat(messages, client: self.client)
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 = client.chat(parameters: parameters)
48
- content = response.dig("choices", 0, "message", "content")
49
- if !content
50
- raise OpenAiError, "No response from OpenAI"
61
+ response = nil
62
+ begin
63
+ response = client.chat(parameters: parameters)
64
+ rescue => exception
65
+ handle_request_error(exception)
51
66
  else
52
- content
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.4.0"
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.0
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-17 00:00:00.000000000 Z
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.1.4
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: []