Skip to content

Commit

Permalink
v1.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
PepijnMC committed Jan 14, 2023
1 parent e5d1a21 commit 50929e3
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 21 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -93,12 +93,12 @@ The module will never send a request to GPT-3 without being told to by pressing
> WARNING! Using any of these functions will send a request to GPT-3 for which you will be charged like any other request made by this module. As such please be careful implenting them in macros and other modules. Test your code well before implementing these functions and I strongly advice users to avoid looping and recursive functions.
Functions to construct and send your own prompts are provided under `game.modules.get('ai-description-generator').api`:
- `constructPrompt(language, system, world, entityType, subject, key)`: Construct and sends a prompt based on the provided context similar to how the base module does it.
- `constructPrompt(language, system, world, subject, subjectType, key)`: Construct and sends a prompt based on the provided context similar to how the base module does it.
- `language`: The language GPT-3 will be encouraged to respond in. Use `game.settings.get('ai-description-generator', 'language')` to use the language provided in the module's/core's settings.
- `system`: The RPG system to be used for context. Use `game.settings.get('ai-description-generator', 'system')` to use the system that was provided in the module's settings.
- `world`: The world/setting to be used for context. Use `game.settings.get('ai-description-generator', 'world')` to use the world that was provided in the module's settings.
- `entityType`: Either `creature`, `item`, or `spell`.
- `subject`: The name of the subject.
- `subjectType`: Additional information about the nature of the `subject`, like `creature` or `spell`.
- `key`: Your API key. Use `game.settings.get('ai-description-generator', 'key')` to use the key that was provided in the module's settings.
- `sendPrompt(prompt, key)`: Sends a completely custom prompt.
- `prompt`: The prompt you want to send.
Expand Down
2 changes: 1 addition & 1 deletion module.json
Expand Up @@ -2,7 +2,7 @@
"id": "ai-description-generator",
"title": "AI Description Generator",
"description": "Utilizes the GPT-3 AI to generate descriptions from within Foundry.",
"version": "1.2.0",
"version": "1.3.0",
"authors": [
{
"name": "Pepijn Sietsema",
Expand Down
Binary file modified module.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion scripts/chat_commands.js
Expand Up @@ -27,8 +27,8 @@ export function addChatCommands(log, data, chatData) {
game.settings.get('ai-description-generator', 'language'),
game.settings.get('ai-description-generator', 'system'),
game.settings.get('ai-description-generator', 'world'),
subjectType,
subject,
subjectType,
game.settings.get('ai-description-generator', 'key')
);
return false;
Expand Down
49 changes: 34 additions & 15 deletions scripts/generator.js
@@ -1,5 +1,6 @@
export function constructPrompt(language, system, world, subjectType, subject, key) {
var prompt = ''
//Construct a prompt based on the given parameters.
export function constructPrompt(language, system, world, subject, subjectType, key) {
//A mapping for Foundry's languages since only the key is stored but the value is needed.
const foundryLanguages = {
"en": "English",
"fr": "French",
Expand Down Expand Up @@ -30,31 +31,47 @@ export function constructPrompt(language, system, world, subjectType, subject, k
'aus'
];

if (language == '') language = foundryLanguages[game.settings.get('core', 'language')];
if (!englishLanguages.includes(language.toLowerCase())) prompt += `Reply in ${language}. `
prompt += `This is a tabletop roleplaying game using the ${system}`;
if (!system.toLowerCase().includes('system')) prompt += ' system';
if (world) prompt += ` and the ${world} setting`;
prompt += `. Give a cool short sensory description the game master can use for a ${subject}`;
switch (subjectType.toLowerCase()) {
case 'creature':
case 'item':
case 'spell':
prompt += ` ${subjectType}`;
const defaultPrompt = 'Reply in {language}. This is a tabletop roleplaying game using the {system} system and the {world} setting. Give a cool short sensory description the game master can use for a {subject} {subjectType}.';
var prompt = game.settings.get('ai-description-generator', 'prompt');
if (prompt === defaultPrompt) {
//If the module's language setting is left blank use the core language setting instead.
if (language == '') language = foundryLanguages[game.settings.get('core', 'language')];
//Remove the language request from the prompt for English languages.
if (englishLanguages.includes(language.toLowerCase())) prompt = prompt.replace('Reply in {language}. ', '');
//If the system name already includes the word 'system' remove it from the prompt.
if (system.toLowerCase().includes('system')) prompt = prompt.replace(' system ', ' ');
//If no world is given remove it from the prompt.
if (world == '') prompt = prompt.replace(' and the {world} setting', '');
//If no subject type is given remove it from the prompt.
if (subjectType == '') prompt = prompt.replace(' {subjectType}', '');
}
prompt += '.';
var prompt_mapping = {
'{language}': language,
'{system}': system,
'{world}': world,
'{subject}': subject,
'{subjectType}': subjectType
};
for (const [key, value] of Object.entries(prompt_mapping)) {
prompt = prompt.replace(key, value);
}
//Send the prompt.
return sendPrompt(prompt, key)
}

//Send a prompt the GPT-3.
export function sendPrompt(prompt, key) {
console.log(`AI Description Generator | Sending the following prompt to GPT-3: ${prompt}`);
var response = '';

//Setup a http request to OpenAI's API using the provided key.
var oHttp = new XMLHttpRequest();
oHttp.open("POST", "https://api.openai.com/v1/completions");
oHttp.setRequestHeader("Accept", "application/json");
oHttp.setRequestHeader("Content-Type", "application/json");
oHttp.setRequestHeader("Authorization", 'Bearer ' + key);

//Add a listener to the request to catch its response.
oHttp.onreadystatechange = function () {
if (oHttp.readyState === 4) {
var oJson = {}
Expand All @@ -81,8 +98,9 @@ export function sendPrompt(prompt, key) {
}
};

//The data to send including the prompt.
var data = {
model: 'text-davinci-003',
model: game.settings.get('ai-description-generator', 'model'),
prompt: prompt,
max_tokens: game.settings.get('ai-description-generator', 'max_tokens'),
user: '1',
Expand All @@ -92,5 +110,6 @@ export function sendPrompt(prompt, key) {
stop: ["#", ";"] //Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.
};

//Send the data.
oHttp.send(JSON.stringify(data));
}
7 changes: 5 additions & 2 deletions scripts/main.js
Expand Up @@ -3,11 +3,13 @@ import { registerAPI } from './api.js';
import { constructPrompt } from './generator.js';
import { addChatCommands } from './chat_commands.js';

//Register the settings and api function when Foundry is ready.
Hooks.once('init', () => {
registerSettings();
registerAPI();
})

//Add a new button to the header of the actor sheet.
Hooks.on('getActorSheetHeaderButtons', (sheet, headerButtons) => {
if (!game.user.isGM) return;
headerButtons.unshift({
Expand All @@ -19,14 +21,15 @@ Hooks.on('getActorSheetHeaderButtons', (sheet, headerButtons) => {
game.settings.get('ai-description-generator', 'language'),
game.settings.get('ai-description-generator', 'system'),
game.settings.get('ai-description-generator', 'world'),
'creature',
sheet.object.name,
'creature',
game.settings.get('ai-description-generator', 'key')
);
}
})
})

//Add a new button the the header of the itme sheet. Spells are also considered items.
Hooks.on('getItemSheetHeaderButtons', (sheet, headerButtons) => {
headerButtons.unshift({
label: 'GPT-3',
Expand All @@ -37,8 +40,8 @@ Hooks.on('getItemSheetHeaderButtons', (sheet, headerButtons) => {
game.settings.get('ai-description-generator', 'language'),
game.settings.get('ai-description-generator', 'system'),
game.settings.get('ai-description-generator', 'world'),
sheet.object.type == 'spell' ? 'spell': 'item',
sheet.object.name,
sheet.object.type == 'spell' ? 'spell': 'item',
game.settings.get('ai-description-generator', 'key')
);
}
Expand Down
19 changes: 19 additions & 0 deletions scripts/settings.js
Expand Up @@ -53,6 +53,15 @@ export function registerSettings() {
default: 'GPT-3'
});

game.settings.register('ai-description-generator', 'prompt', {
name: 'AI Prompt',
hint: 'The prompt that is used to contruct a request for GPT-3. Only alter this if you are dissatified with the results and know what you are doing!',
scope: 'world',
config: true,
type: String,
default: 'Reply in {language}. This is a tabletop roleplaying game using the {system} system and the {world} setting. Give a cool short sensory description the game master can use for a {subject} {subjectType}.'
});

game.settings.register('ai-description-generator', 'max_tokens', {
name: 'AI Max Tokens',
hint: 'The maximum amount of tokens the AI can use per request.',
Expand Down Expand Up @@ -92,6 +101,16 @@ export function registerSettings() {
default: 0.0
});

game.settings.register('ai-description-generator', 'model', {
name: 'AI Model',
hint: 'GPT-3 offers 4 main models, davanci-003 being the latest. This setting should not be changed if you do not know what this means.',
scope: 'world',
config: true,
type: String,
default: 'text-davinci-003',
choices: { 'text-davinci-003': 'text-davinci-003', 'text-curie-001': 'text-curie-001', 'text-babbage-001': 'text-babbage-001', 'text-ada-001': 'text-ada-001' }
});

game.settings.register('ai-description-generator', 'api', {
name: 'Enable API Functions',
hint: 'Exposes functions to construct and send prompts in macros or other modules.',
Expand Down

0 comments on commit 50929e3

Please sign in to comment.