Bill's AI Prompts Amplenote Plugin (Yappy)

To get an OpenAI API key, sign up for OpenAI, then visit OpenAI's API keys page.


name

Yappy

description

OpenAI prompts to revise and generate note content, especially inline content that you want to complete.

icon

psychology

instructions

Enter in your OpenAI key in the plugin settings. If you have access to gpt-4, you can enter that in to the "OpenAI model" setting field, otherwise, you can leave it blank.



The first commands that have been implemented are all expressions, invoked by using an opening curly brace { followed by one of the implemented commands:
1. lolz: Say something funny in the context of the note
2. complete: Write up to a paragraph that completes the thought or instructions that preceded it
3. lookup: Like complete, but when you only want the AI to insert a word or phrase, rather than a list or paragraph.
2. code: Write code that implements the adjacent instructions. Experimental.
4. sum: Sum the values in column or row adjacent to the expression. Very experimental.

There are three options added to the menu when text is selected:
1. revise: Allow passing a prompt for how to revise the selected text
2. rhyme: Suggest up to 10 words that would rhyme with the selection
3. summarize: Summarize the selected text

There are two options added to the main note menu:
1. revise: suggest an entirely new body for the note
2. summarize: summarize the full contents of the note

Examples of using the plugin at Yappy examples & documentation:https://public.amplenote.com/zSCFNYBKTtkpktmDHBHNHvqS
Get an OpenAI key at https://platform.openai.com/account/api-keys and enter it into the settings for this plugin.
This plugin lives in git with some tests at https://github.com/alloy-org/yappy

setting

API Key

setting

OpenAI model (default is gpt-3.5-turbo)


The prompts that control summarize and complete behavior at the bottom of the code block. The easiest way to play around with them is to use https://platform.openai.com/playground with the prompt and some content after it, using the same settings (temperature 0.1 and maximum length 128 - though of course those could be changed too).


{
// --------------------------------------------------------------------------------------
constants: {
defaultSystemPrompt: "You are a helpful assistant.",
pluginName: "Yappy",
strictReplacePromptEms: [ "lookup", "sum", "thesaurus" ],
tokenReplacePrompts: {
code: `Provide a snippet of source code (in either Javascript, Python or Ruby) implementing
the intent of the text preceding the token. Use the triple backtick to begin and end your code.`,
lolz: `Provide up to 100 words of entertaining, humorous content that is a little bit edgy.
Reference other text in the document provided to show how well you understand it, especially the text
near the replace token. If you can't figure out a funny joke, insert a four line limerick, song,
poem, or rap that is relevant to the nearby text.`,
sum: `Calculate the sum of the table cells in the row or column of the token. Respond only with plain text
to be placed in the table cell. If the summed cells contain a unit, include that unit in your response. The
limit of your response is 20 characters. If you can not find numbers to sum, respond with "🤔"`,
},
},
 
// --------------------------------------------------------------------------------------
insertText: {
lolz: async function(app) {
return await this._contextAwarePrompt(app, "lolz");
},
code: async function(app) {
return await this._contextAwarePrompt(app, "code");
},
complete: async function(app) {
return await this._contextAwarePrompt(app, "complete");
},
lookup: async function(app) {
return await this._contextAwarePrompt(app, "lookup");
},
sum: async function(app) {
return await this._contextAwarePrompt(app, "sum");
},
},
 
// --------------------------------------------------------------------------------------
// https://www.amplenote.com/help/developing_amplenote_plugins#noteOption
noteOption: {
"revise": async function(app, noteUUID) {
const instruction = await app.prompt("How should this note be revised?");
if (!instruction) return;
 
const note = await app.notes.find(noteUUID);
const noteContent = await note.content();
const result = await this._callOpenAI(app, "reviseContent", [ instruction, noteContent ]);
const actionIndex = await app.alert(result, {
actions: [ { icon: "post_add", label: "Insert in note" } ]
});
if (actionIndex === 0) {
note.insertContent(result);
}
},
"summarize": async function(app, noteUUID) {
const note = await app.notes.find(noteUUID);
const noteContent = await note.content();
const result = await this._callOpenAI(app, "summarize", noteContent);
const actionIndex = await app.alert(result, {
actions: [ { icon: "post_add", label: "Insert in note" } ]
});
if (actionIndex === 0) {
note.insertContent(result);
}
},
},
 
// --------------------------------------------------------------------------------------
// https://www.amplenote.com/help/developing_amplenote_plugins#replaceText
replaceText: {
"complete": async function(app, text) {
const result = await this._callOpenAI(app, "replaceTextComplete", text);
return text + " " + result;
},
"revise": async function(app, text) {
const instruction = await app.prompt("How should this text be revised?");
if (!instruction) return null;
 
const result = await this._callOpenAI(app, "reviseText", [ instruction, text ]);
 
app.alert(result);
 
return null;
},
},
 
// --------------------------------------------------------------------------------------
async _callOpenAI(app, promptType, promptContent) {
let messages = [];
const systemPrompt = this._systemPrompts[promptType] || this.constants.defaultSystemPrompt;
messages.push({ role: "system", content: systemPrompt });
const userPrompt = this._userPrompts[promptType] ? this._userPrompts[promptType](promptContent) : promptContent;
if (Array.isArray(userPrompt)) {
userPrompt.forEach(content => {
messages.push({ role: "user", content: this._truncate(content) });
});
} else {
messages.push({ role: "user", content: this._truncate(userPrompt) });
}
try {
const modelSetting = app.settings["OpenAI model (default is gpt-3.5-turbo)"];
const model = modelSetting && modelSetting.trim().length ? modelSetting.trim() : "gpt-3.5-turbo";
console.debug("Submitting messages", messages, "while using model", model);
 
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${ app.settings["API Key"] }`,
"Content-Type": "application/json"
},
body: JSON.stringify({ model, messages, })
});
const result = await response.json();
const { choices: [ { message: { content } } ] } = result;
return content;
} catch (error) {
app.alert("Failed to call OpenAI: " + error);
return null;
}
},
 
// --------------------------------------------------------------------------------------
// GPT-3.5 has a 4097 token limit, so very much approximating that limit with this number
_truncate(text, limit = 15000) {
return text.length > limit ? text.slice(0, limit) : text;
},
 
// --------------------------------------------------------------------------------------
async _contextAwarePrompt(app, promptEm) {
const noteUUID = app.context.noteUUID;
const note = await app.notes.find(noteUUID);
const noteContent = await note.content();
 
const tokenLabel = `{${ this.constants.pluginName }: ${ promptEm }}`;
const messages = this._parameterArrayFromPrompt(promptEm, tokenLabel, noteContent);
if (messages) {
return await this._callOpenAI(app, promptEm, messages);
} else {
return null;
}
},
 
// --------------------------------------------------------------------------------------
_parameterArrayFromPrompt(promptEm, tokenLabel, noteContent) {
const specificityWords = (
this.constants.strictReplacePromptEms.includes(promptEm)
? "only the exact word or words"
: "text"
);
const tokenReplacePrompt = `Respond with ${ specificityWords } that could be used to replace the token
in the following input markdown document, which begins and ends with triple tildes:`;
const prompt = this.constants.tokenReplacePrompts[promptEm];
const appendMessage = `The resulting text should be grammatically correct and make sense in context.
Do not explain how you derived your answer. Do not explain why you chose your answer.
Do not respond with the token itself.`;
if (noteContent.includes(tokenLabel)) {
const messages = [
tokenReplacePrompt,
prompt && prompt.length ? prompt : null,
`~~~\n${ noteContent.replace(tokenLabel, "") }\n~~~`,
appendMessage
].filter(n => n);
console.log("Composed messages for sending", messages);
return messages;
} else {
app.alert("Couldn't find expected token in document")
return null;
}
},
 
// --------------------------------------------------------------------------------------
_systemPrompts: {
replaceTextComplete: "You are a helpful assistant helping continue writing markdown-formatted content.",
reviseContent: "You are a helpful assistant that revises markdown-formatted content, as instructed.",
reviseText: "You are a helpful assistant that revises text, as instructed.",
summarize: "You are a helpful assistant that summarizes notes that are markdown-formatted.",
},
 
// --------------------------------------------------------------------------------------
_userPrompts: {
replaceTextComplete: content => `Continue the following markdown-formatted content:\n\n${ content }`,
reviseContent: ([ instruction, content ]) => [ instruction, content ],
reviseText: ([ instruction, text ]) => [ instruction, text ],
summarize: content => `Summarize the following markdown-formatted note:\n\n${ content }`,
},
}


linkChangelog

April 24 v0.3 removes thesaurus and image generator to move them to dedicated Image Generation and Thesaurus plugins

April 11 v0.2 Added basic test framework for more rapid iteration and thesaurus method to context menu options

April 8 v0.1.2 Added lookup to disambiguate when seeking short answers and vs expository ones

April 8 v0.1.1 Updated complete so that it is more prone to respond with a specific word or phrase when appropriate, instead of waxing on.

April 7 v0.1 Implement initial commands for lolz complete code and sum. Try to minimize boilerplate needed for each prompt, while getting OpenAI to respond in such a way that it incorporates the surrounding note context.