summarize-meeting 1.0.0 → 1.2.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 +30 -2
- data/lib/summarize-meeting/meeting.rb +9 -23
- data/lib/summarize-meeting/version.rb +1 -1
- data/lib/summarize-meeting.rb +1 -1
- 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: 50b154d8f5d117b51fb3a377ad725088262543c05d5ee2614929511ad093a553
|
4
|
+
data.tar.gz: 66f0a4144e8b5bf6d36792bdb0432014b803e4eff020060fc9d5045c1631edef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd077cdfe8a177d145a5ab4672d14477cbd7a06124f0eeb03c834ef5eb632022d76920ab1ee6cba38e81204f91faa48cb1bdcc1683a86db1f7305e232cc3380a
|
7
|
+
data.tar.gz: fc2061143753fc8865b39702cfc215ae7732f8e697f8288170f07a8ba1f627c3078f4d37c40e725506413a2718cb5b7b3f93d427cd6d94a3d1ee7f0d30246192
|
data/lib/summarize-meeting/ai.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
|
-
require "openai"
|
2
|
-
|
3
1
|
module SummarizeMeeting
|
4
2
|
module Ai
|
3
|
+
class OpenAiError < StandardError; end
|
4
|
+
MAX_TOTAL_TOKENS = 4096
|
5
|
+
WORDS_PER_TOKEN = 0.75
|
6
|
+
|
5
7
|
@@access_token = ENV["OPENAI_KEY"]
|
6
8
|
@@organization_id = ENV["OPENAI_ORG"]
|
7
9
|
|
8
10
|
def self.client
|
11
|
+
@client ||= new_client(access_token: access_token, organization_id: organization_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.new_client(access_token:, organization_id:)
|
9
15
|
OpenAI::Client.new(access_token: access_token, organization_id: organization_id)
|
10
16
|
end
|
11
17
|
|
@@ -24,5 +30,27 @@ module SummarizeMeeting
|
|
24
30
|
def self.organization_id=(id)
|
25
31
|
@@organization_id = id
|
26
32
|
end
|
33
|
+
|
34
|
+
def self.calculate_token_word_count(token_count)
|
35
|
+
(token_count * WORDS_PER_TOKEN.to_f).ceil
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.calculate_word_token_count(word_count)
|
39
|
+
(word_count / WORDS_PER_TOKEN.to_f).ceil
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.chat(messages, client: self.client)
|
43
|
+
parameters = {
|
44
|
+
model: "gpt-3.5-turbo",
|
45
|
+
messages: messages,
|
46
|
+
}
|
47
|
+
response = client.chat(parameters: parameters)
|
48
|
+
content = response.dig("choices", 0, "message", "content")
|
49
|
+
if !content
|
50
|
+
raise OpenAiError, "No response from OpenAI"
|
51
|
+
else
|
52
|
+
content
|
53
|
+
end
|
54
|
+
end
|
27
55
|
end
|
28
56
|
end
|
@@ -4,6 +4,8 @@ require "openai"
|
|
4
4
|
|
5
5
|
module SummarizeMeeting
|
6
6
|
class Meeting
|
7
|
+
RESPONSE_RESERVE_TOKENS = 500
|
8
|
+
|
7
9
|
LINE_SUMMARY_PROMPT_TEMPLATE = [
|
8
10
|
{
|
9
11
|
role: "system",
|
@@ -11,7 +13,7 @@ module SummarizeMeeting
|
|
11
13
|
},
|
12
14
|
{
|
13
15
|
role: "system",
|
14
|
-
content: "The transcript of the meeting is split into {{chunkCount}} chunks. This is the {{chunkIndex}}
|
16
|
+
content: "The transcript of the meeting is split into {{chunkCount}} chunks. This is the chunk number {{chunkIndex}} of {{chunkCount}}.",
|
15
17
|
},
|
16
18
|
{
|
17
19
|
role: "assistant",
|
@@ -60,17 +62,14 @@ module SummarizeMeeting
|
|
60
62
|
attr_reader :transcript
|
61
63
|
|
62
64
|
def summarize
|
63
|
-
|
64
65
|
# Step 1. Split the transcript into lines.
|
65
66
|
lines = transcript.lines
|
66
67
|
|
67
68
|
# Step 2. Calculate the maximum chunk size in words.
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
words_per_token = 0.7
|
73
|
-
max_chunk_word_count = max_chunk_tokens * words_per_token
|
69
|
+
template_word_count = LINE_SUMMARY_PROMPT_TEMPLATE.map { |line| line[:content].split.size }.sum
|
70
|
+
template_token_count = SummarizeMeeting::Ai.calculate_word_token_count(template_word_count)
|
71
|
+
max_chunk_token_count = SummarizeMeeting::Ai::MAX_TOTAL_TOKENS - RESPONSE_RESERVE_TOKENS - template_token_count
|
72
|
+
max_chunk_word_count = SummarizeMeeting::Ai.calculate_token_word_count(max_chunk_token_count)
|
74
73
|
|
75
74
|
# Step 3. Split the transcript into equally sized chunks.
|
76
75
|
chunks = split_lines_into_equal_size_chunks(lines, max_chunk_word_count)
|
@@ -86,27 +85,14 @@ module SummarizeMeeting
|
|
86
85
|
consolidated_template = CONSOLIDATED_SUMMARY_PROMPT_TEMPLATE
|
87
86
|
prompt = Mustache.render(consolidated_template.to_json, { notes: previous_chunks_summary.to_json })
|
88
87
|
messages = JSON.parse(prompt)
|
89
|
-
|
90
|
-
parameters: {
|
91
|
-
model: "gpt-3.5-turbo",
|
92
|
-
messages: messages,
|
93
|
-
}
|
94
|
-
)
|
95
|
-
response.dig("choices", 0, "message", "content")
|
88
|
+
SummarizeMeeting::Ai.chat(messages)
|
96
89
|
end
|
97
90
|
|
98
91
|
def summarize_chunk(chunk, chunk_index, chunk_count, previous_chunks_summary)
|
99
92
|
template = LINE_SUMMARY_PROMPT_TEMPLATE
|
100
93
|
prompt = Mustache.render(template.to_json, { chunkCount: chunk_count, chunkIndex: chunk_index + 1, chunk: chunk.join("\n").to_json })
|
101
94
|
messages = JSON.parse(prompt)
|
102
|
-
|
103
|
-
response = SummarizeMeeting::Ai.client.chat(
|
104
|
-
parameters: {
|
105
|
-
model: "gpt-3.5-turbo",
|
106
|
-
messages: messages,
|
107
|
-
}
|
108
|
-
)
|
109
|
-
response.dig("choices", 0, "message", "content")
|
95
|
+
SummarizeMeeting::Ai.chat(messages)
|
110
96
|
end
|
111
97
|
|
112
98
|
def split_lines_into_equal_size_chunks(lines, max_chunk_word_count)
|
data/lib/summarize-meeting.rb
CHANGED
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.2.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-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: optparse
|