Skip to content

Commit 53c25c6

Browse files
committed
Allow models to be selected and the context window to be defined
1 parent a458762 commit 53c25c6

File tree

4 files changed

+96
-27
lines changed

4 files changed

+96
-27
lines changed

background.js

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import * as messageTools from '/modules/messageTools.mjs';
22

33
const SUMMARY_PREFIX = "[SUMMARY]"
4-
/**
5-
* Ollama has a default context window of 2048 tokens. Tokens are approximately 4 characters each.
6-
* Any content over this limit is ignores.
7-
* Typically, the content we are interested in is the start of the email, with the trailing content
8-
* being quoted replies or signatures.
9-
* So, we must trim the email to the first 2000 characters.
10-
*/
11-
const MAX_CONTENT_LENGTH = 2000 * 4
4+
const DEFAULT_CONTEXT_WINDOW = 2048
125

136
class ResponseError extends Error {
147
constructor(message, res) {
@@ -23,6 +16,8 @@ class ResponseError extends Error {
2316
messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
2417
console.log("New mail received")
2518

19+
const contextLength = await getContextLength()
20+
2621
/*
2722
We use the messageTools.iterateMessagePages function to iterate over all the messages in the folder.
2823
*/
@@ -45,7 +40,8 @@ messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
4540
/*
4641
Get the text content of the email, stripping out HTML and CSS
4742
*/
48-
const content = (await getBody(message)).substring(0, MAX_CONTENT_LENGTH)
43+
const content = await getBody(message)
44+
.then(body => body.substring(0, contextLength))
4945

5046
/*
5147
Call Ollama to generate a summary of the email. The service may be (re)starting,
@@ -75,14 +71,42 @@ messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
7571
}
7672
})
7773

78-
async function sendNewEmail(message, summary) {
79-
const getItem = await browser.storage.local.get()
74+
async function getEmailAddress() {
75+
return await browser.storage.local.get()
76+
.then(getItem => getItem.email?.trim() || "")
77+
}
78+
79+
async function getEmailAlias() {
80+
return await browser.storage.local.get()
81+
.then(getItem => getItem.alias?.trim() || "")
82+
}
83+
84+
async function getModel() {
85+
return await browser.storage.local.get()
86+
.then(getItem => getItem.model?.trim() || "llama3.2")
87+
}
8088

81-
if (getItem.email.trim().length === 0) {
89+
async function getContextLength() {
90+
return await browser.storage.local.get()
91+
.then(getItem => getItem.contextwindow?.trim() || "2048")
92+
.then(contextWindow => parseInt(contextWindow))
93+
.then(contextWindow => isNaN(contextWindow) || contextWindow < 0
94+
? DEFAULT_CONTEXT_WINDOW
95+
: contextWindow)
96+
/*
97+
Context window measures the number of tokens. There are approx 4 chars per token.
98+
To give us a buffer and allow for the prompt template we subtract 256 tokens and
99+
multiply by 4 to get the number of characters that can be passed into the model.
100+
*/
101+
.then(contextWindow => (contextWindow - 256) * 4)
102+
}
103+
104+
async function sendNewEmail(message, summary) {
105+
if (getEmailAddress().length === 0) {
82106
return
83107
}
84108

85-
const emailWithAlias = getToAddress(getItem.email, getItem.alias)
109+
const emailWithAlias = getToAddress(getEmailAddress(), getEmailAlias())
86110

87111
const composeTab = await browser.compose.beginNew({
88112
to: emailWithAlias,
@@ -123,6 +147,45 @@ async function getBody(message) {
123147
return plainTextParts.join("\n")
124148
}
125149

150+
function getPrompt(content) {
151+
const model = getModel()
152+
153+
if (model.startsWith("phi")) {
154+
return getPhiPrompt(content)
155+
}
156+
157+
return getLlamaPrompt(content)
158+
}
159+
160+
function getPhiPrompt(content) {
161+
return "<|im_start|>system<|im_sep|>" +
162+
"You are an expert in reading and summarizing emails." +
163+
"<|im_end|>" +
164+
"<|im_start|>system<|im_sep|>" +
165+
"The email content is: " + content +
166+
"<|im_end|>" +
167+
"<|im_start|>user<|im_sep|>" +
168+
"Provide a two paragraph summary of the email. " +
169+
"The summary must highlight the important points, dates, people, questions, and action items." +
170+
"<|im_end|>" +
171+
"<|im_start|>assistant<|im_sep|>"
172+
}
173+
174+
function getLlamaPrompt(content) {
175+
return "<|begin_of_text|>" +
176+
"<|start_header_id|>system<|end_header_id|>" +
177+
"You are an expert in reading and summarizing emails." +
178+
"<|eot_id|>" +
179+
"<|start_header_id|>system<|end_header_id|>" +
180+
"The email content is: " + content +
181+
"<|eot_id|>" +
182+
"<|start_header_id|>user<|end_header_id|>" +
183+
"Provide a two paragraph summary of the email. " +
184+
"The summary must highlight the important points, dates, people, questions, and action items." +
185+
"<|eot_id|>" +
186+
"<|start_header_id|>assistant<|end_header_id|>"
187+
}
188+
126189
/**
127190
* Call Ollama to generate a summary of the email
128191
* @param content The plain text context of the email
@@ -135,19 +198,8 @@ function getSummary(content) {
135198
method: "POST",
136199
body: JSON.stringify(
137200
{
138-
"model": "llama3.2",
139-
"prompt": "<|begin_of_text|>" +
140-
"<|start_header_id|>system<|end_header_id|>" +
141-
"You are an expert in reading and summarizing emails." +
142-
"<|eot_id|>" +
143-
"<|start_header_id|>system<|end_header_id|>" +
144-
"The email content is: " + content +
145-
"<|eot_id|>" +
146-
"<|start_header_id|>user<|end_header_id|>" +
147-
"Provide a two paragraph summary of the email. " +
148-
"The summary must highlight the important points, dates, people, questions, and action items." +
149-
"<|eot_id|>" +
150-
"<|start_header_id|>assistant<|end_header_id|>",
201+
"model": getModel(),
202+
"prompt": getPrompt(content),
151203
"stream": false
152204
}
153205
),

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 2,
33
"name": "Ollama Summarizer",
44
"description": "Generate email summaries with Ollama",
5-
"version": "1.4",
5+
"version": "1.5",
66
"author": "Matthew Casperson",
77
"browser_specific_settings": {
88
"gecko": {

options.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@
1717
<p>This is an optional alias added to the email address to make it easier to filter summary emails.</p>
1818
<p>For example, if the email address is test@example.org, and the alias is summary, summary emails are sent to test+summary@example.org.</p>
1919
<br/>
20+
<label>Model</label>
21+
<select id="model">
22+
<option value="llama3.2">llama3.2</option>
23+
<option value="phi4">phi4</option>
24+
</select>
25+
<p>This is the model used to summarize the email. These models must be
26+
pulled by Ollama with the command "ollama pull &lt;modelname&gt;".</p>
27+
<br/>
28+
<label>Context Window</label>
29+
<input type="text" id="contextwindow">
30+
<p>This is the size of the context window. Larger sizes allow larger
31+
emails to be summarized, but consume more memory and take longer.</p>
32+
<br/>
2033
<button type="submit">Save</button>
2134
</form>
2235
<script src="options.js"></script>

options.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ function saveOptions(e) {
22
browser.storage.local.set({
33
email: document.querySelector("#email").value,
44
alias: document.querySelector("#alias").value,
5+
model: document.querySelector("#model").value,
6+
contextwindow: document.querySelector("#contextwindow").value,
57
});
68
e.preventDefault();
79
}
@@ -11,6 +13,8 @@ function restoreOptions() {
1113
gettingItem.then((res) => {
1214
document.querySelector("#email").value = res.email || '';
1315
document.querySelector("#alias").value = res.alias || '';
16+
document.querySelector("#model").value = res.model || 'llama3.2';
17+
document.querySelector("#contextwindow").value = res.contextwindow || '2048';
1418
});
1519
}
1620

0 commit comments

Comments
 (0)