From df8e44e527f2b7a47b48adabe1cb67995093d50f Mon Sep 17 00:00:00 2001 From: Cohee Date: Mon, 20 Mar 2023 19:22:57 +0200 Subject: [PATCH 01/11] Update readme.md --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index d0e30104d..bb09f425f 100644 --- a/readme.md +++ b/readme.md @@ -89,6 +89,8 @@ Contact us on Discord: Cohee#1207 or RossAscends#1779 ## Screenshots image +image + ## License * TAI Base: Unknown From 8bae9d8472926da560c20da689f1f2dcc3e4e301 Mon Sep 17 00:00:00 2001 From: Cohee Date: Mon, 20 Mar 2023 19:28:20 +0200 Subject: [PATCH 02/11] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index bb09f425f..7f67e761e 100644 --- a/readme.md +++ b/readme.md @@ -88,8 +88,8 @@ if you want other people on the internet to connect, and check [here](https://wh Contact us on Discord: Cohee#1207 or RossAscends#1779 ## Screenshots -image image +image ## License From 847a179b7a571fd214bdb25079ad766b806063bb Mon Sep 17 00:00:00 2001 From: Cohee Date: Tue, 21 Mar 2023 02:32:08 +0200 Subject: [PATCH 03/11] Update readme.md --- readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/readme.md b/readme.md index 7f67e761e..bc8963987 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,10 @@ ## Silly TavernAI mod. Based on fork of TavernAI 1.2.8 ### Brought to you by @SillyLossy and @RossAscends +https://colab.research.google.com/github/SillyLossy/TavernAI-extras/blob/main/colab/GPU.ipynb + +Try on Colab (runs KoboldAI backend and TavernAI Extras server alongside): + Open In Colab + ## This branch includes: * Base TavernAI 1.2.8 From c414a20221bc1244c03d83b95f2024c714659ce9 Mon Sep 17 00:00:00 2001 From: Cohee Date: Tue, 21 Mar 2023 02:32:31 +0200 Subject: [PATCH 04/11] Update readme.md --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index bc8963987..53a08184c 100644 --- a/readme.md +++ b/readme.md @@ -1,11 +1,12 @@ ## Silly TavernAI mod. Based on fork of TavernAI 1.2.8 ### Brought to you by @SillyLossy and @RossAscends -https://colab.research.google.com/github/SillyLossy/TavernAI-extras/blob/main/colab/GPU.ipynb Try on Colab (runs KoboldAI backend and TavernAI Extras server alongside): Open In Colab +https://colab.research.google.com/github/SillyLossy/TavernAI-extras/blob/main/colab/GPU.ipynb + ## This branch includes: * Base TavernAI 1.2.8 * Swipes From 4c9ce79811a032ff727da7c01a2c218b63ec1707 Mon Sep 17 00:00:00 2001 From: Cohee Date: Tue, 21 Mar 2023 02:33:41 +0200 Subject: [PATCH 05/11] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 53a08184c..abfa4009c 100644 --- a/readme.md +++ b/readme.md @@ -22,7 +22,7 @@ https://colab.research.google.com/github/SillyLossy/TavernAI-extras/blob/main/co * Sending images to chat, and the AI interpreting the content. ## UI Extensions 🚀 -| Name | Description | Required [Modules](#modules) | Screenshot | +| Name | Description | Required [Modules](https://github.com/SillyLossy/TavernAI-extras#modules) | Screenshot | | ---------------- | ---------------------------------| ---------------------------- | ---------- | | Image Captioning | Send a cute picture to your bot!

Picture select option will appear beside "Message send" button. | `caption` | | | Character Expressions | See your character reacting to your messages!

**You need to provide your own character images!**

1. Create a folder in TavernAI called `public/characters/`, where `` is a name of your character.
2. For base emotion classification model, put six PNG files there with the following names: `joy.png`, `anger.png`, `fear.png`, `love.png`, `sadness.png`, `surprise.png`. Other models may provide another options.
3. Images only display in desktop mode. | `classify` | image | From b124c5c87d2dd17182baf90689aeb1aaa5101840 Mon Sep 17 00:00:00 2001 From: Cohee Date: Tue, 21 Mar 2023 02:34:09 +0200 Subject: [PATCH 06/11] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index abfa4009c..28ce6a2a2 100644 --- a/readme.md +++ b/readme.md @@ -22,7 +22,7 @@ https://colab.research.google.com/github/SillyLossy/TavernAI-extras/blob/main/co * Sending images to chat, and the AI interpreting the content. ## UI Extensions 🚀 -| Name | Description | Required [Modules](https://github.com/SillyLossy/TavernAI-extras#modules) | Screenshot | +| Name | Description | Required [Extra Modules](https://github.com/SillyLossy/TavernAI-extras#modules) | Screenshot | | ---------------- | ---------------------------------| ---------------------------- | ---------- | | Image Captioning | Send a cute picture to your bot!

Picture select option will appear beside "Message send" button. | `caption` | | | Character Expressions | See your character reacting to your messages!

**You need to provide your own character images!**

1. Create a folder in TavernAI called `public/characters/`, where `` is a name of your character.
2. For base emotion classification model, put six PNG files there with the following names: `joy.png`, `anger.png`, `fear.png`, `love.png`, `sadness.png`, `surprise.png`. Other models may provide another options.
3. Images only display in desktop mode. | `classify` | image | From cc42dcf555f7ddc2dcdbd60a9dac8a0cc40d3573 Mon Sep 17 00:00:00 2001 From: Cohee Date: Tue, 21 Mar 2023 02:34:59 +0200 Subject: [PATCH 07/11] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 28ce6a2a2..e46afe6cb 100644 --- a/readme.md +++ b/readme.md @@ -22,7 +22,7 @@ https://colab.research.google.com/github/SillyLossy/TavernAI-extras/blob/main/co * Sending images to chat, and the AI interpreting the content. ## UI Extensions 🚀 -| Name | Description | Required [Extra Modules](https://github.com/SillyLossy/TavernAI-extras#modules) | Screenshot | +| Name | Description | Required Extra Modules | Screenshot | | ---------------- | ---------------------------------| ---------------------------- | ---------- | | Image Captioning | Send a cute picture to your bot!

Picture select option will appear beside "Message send" button. | `caption` | | | Character Expressions | See your character reacting to your messages!

**You need to provide your own character images!**

1. Create a folder in TavernAI called `public/characters/`, where `` is a name of your character.
2. For base emotion classification model, put six PNG files there with the following names: `joy.png`, `anger.png`, `fear.png`, `love.png`, `sadness.png`, `surprise.png`. Other models may provide another options.
3. Images only display in desktop mode. | `classify` | image | From e6413d0905dbc9b61e89b00e498292f8ab92587c Mon Sep 17 00:00:00 2001 From: SillyLossy Date: Tue, 21 Mar 2023 23:31:26 +0200 Subject: [PATCH 08/11] OpenAI support (by @CncAnon1) --- package-lock.json | 107 +++++ package.json | 2 + public/OpenAI Settings/Default.settings | 5 + public/index.html | 246 ++++++++-- public/script.js | 386 +++++++++------- public/scripts/RossAscends-mods.js | 7 + public/scripts/openai.js | 575 ++++++++++++++++++++++++ public/style.css | 20 +- server.js | 157 +++++++ 9 files changed, 1280 insertions(+), 225 deletions(-) create mode 100644 public/OpenAI Settings/Default.settings create mode 100644 public/scripts/openai.js diff --git a/package-lock.json b/package-lock.json index 880805c1f..d91b4d8b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "TavernAI", "version": "1.1.0", "dependencies": { + "@dqbd/tiktoken": "^1.0.2", + "axios": "^1.3.4", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "csrf-csrf": "^2.2.3", @@ -28,6 +30,11 @@ "TavernAI": "server.js" } }, + "node_modules/@dqbd/tiktoken": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dqbd/tiktoken/-/tiktoken-1.0.2.tgz", + "integrity": "sha512-AjGTBRWsMoVmVeN55NLyupyM8TNamOUBl6tj5t/leLDVup3CFGO9tVagNL1jf3GyZLkWZSTmYVbPQ/M2LEcNzw==" + }, "node_modules/@jimp/bmp": { "version": "0.22.7", "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.7.tgz", @@ -468,6 +475,21 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -609,6 +631,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -737,6 +770,14 @@ "node": ">=8" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -909,6 +950,19 @@ } } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1513,6 +1567,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -1951,6 +2010,11 @@ } }, "dependencies": { + "@dqbd/tiktoken": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dqbd/tiktoken/-/tiktoken-1.0.2.tgz", + "integrity": "sha512-AjGTBRWsMoVmVeN55NLyupyM8TNamOUBl6tj5t/leLDVup3CFGO9tVagNL1jf3GyZLkWZSTmYVbPQ/M2LEcNzw==" + }, "@jimp/bmp": { "version": "0.22.7", "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.7.tgz", @@ -2281,6 +2345,21 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2380,6 +2459,14 @@ "get-intrinsic": "^1.0.2" } }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2475,6 +2562,11 @@ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2612,6 +2704,16 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3068,6 +3170,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", diff --git a/package.json b/package.json index eebd491a1..102f4e6d3 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,7 @@ { "dependencies": { + "@dqbd/tiktoken": "^1.0.2", + "axios": "^1.3.4", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "csrf-csrf": "^2.2.3", diff --git a/public/OpenAI Settings/Default.settings b/public/OpenAI Settings/Default.settings new file mode 100644 index 000000000..8db809591 --- /dev/null +++ b/public/OpenAI Settings/Default.settings @@ -0,0 +1,5 @@ +{ + "temperature": 0.9, + "frequency_penalty": 0.7, + "presence_penalty": 0.7 +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index a89e6743d..cd3e61de9 100644 --- a/public/index.html +++ b/public/index.html @@ -92,6 +92,7 @@ +
@@ -176,11 +177,30 @@ -
-
-
Not connected
+
+
+
Not connected
+ +
@@ -227,7 +247,7 @@ - +
@@ -242,14 +262,16 @@ Amount generation
select - +
Context Size
select - +
@@ -310,7 +332,7 @@
Repetition Penalty Range - +
select
@@ -327,7 +349,8 @@ select
- +
@@ -338,7 +361,8 @@ select
- +
@@ -349,7 +373,67 @@ select
- + +
+ + +
+
+
+ OpenAI Context Size +
+
+ select +
+
+ +
+
+ +
+
+ OpenAI max response length (in tokens) +
+
+ +
+
+ +
+
+ Temperature +
+
+ select +
+
+ +
+
+ +
+
+ Frequency Penalty +
+
+ select +
+
+ +
+
+ +
+
+ Presence Penalty +
+
+ select +
+
+
@@ -453,9 +537,9 @@
- + ***COMING SOON*** - +
@@ -466,49 +550,123 @@ select
- +
-
-
- Top P -
-
- select -
-
- -
+
+
+
+ Top P
-
-
- Typical P -
-
- select -
-
- -
+
+ select
-
-
- Penalty Alpha -
-
- select -
-
- -
+
+
+
+
+ Typical P +
+
+ select +
+
+ +
+
+
+
+ Penalty Alpha +
+
+ select +
+
+ +
+
+
+ +
+
+ +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ +
+ +
+
+ Main prompt +
+
+ The main prompt used to set the model behavior +
+
+ +
+
+ +
+
+ NSFW prompt +
+
+ Prompt that is used when the NSFW toggle is on +
+
+ +
+
+ +
+ +
- +
diff --git a/public/script.js b/public/script.js index c000956d1..13882eb31 100644 --- a/public/script.js +++ b/public/script.js @@ -46,6 +46,18 @@ import { disable_scenario_formatting, } from "./scripts/power-user.js"; +import { + setOpenAIMessageExamples, + setOpenAIMessages, + prepareOpenAIMessages, + sendOpenAIRequest, + loadOpenAISettings, + setOpenAIOnlineStatus, + generateOpenAIPromptCache, + oai_settings, + is_get_status_openai +} from "./scripts/openai.js"; + import { getNovelTier, loadNovelPreset, @@ -74,10 +86,13 @@ export { select_rm_info, setCharacterId, setCharacterName, + setOnlineStatus, + checkOnlineStatus, setEditedMessageId, setSendButtonState, selectRightMenuWithAnimation, setRightTabSelectedClass, + messageFormating, chat, this_chid, settings, @@ -89,10 +104,12 @@ export { token, is_send_press, api_server_textgenerationwebui, + count_view_mes, default_avatar, system_message_types, talkativeness_default, default_ch_mes, + saveChat, } // API OBJECT FOR EXTERNAL WIRING @@ -343,15 +360,18 @@ function checkOnlineStatus() { $("#online_status_text2").html("No connection..."); $("#online_status_indicator3").css("background-color", "red"); //Novel $("#online_status_text3").html("No connection..."); + $(".online_status_indicator4").css("background-color", "red"); //OAI / ooba + $(".online_status_text4").html("No connection..."); is_get_status = false; is_get_status_novel = false; + setOpenAIOnlineStatus(false); } else { $("#online_status_indicator2").css("background-color", "green"); //kobold $("#online_status_text2").html(online_status); $("#online_status_indicator3").css("background-color", "green"); //novel $("#online_status_text3").html(online_status); - $("#online_status_indicator4").css("background-color", "green"); //extensions api - $("#online_status_text4").html(online_status); + $(".online_status_indicator4").css("background-color", "green"); //OAI / ooba + $(".online_status_text4").html(online_status); } } @@ -426,7 +446,7 @@ async function getStatus() { }, }); } else { - if (is_get_status_novel != true) { + if (is_get_status_novel != true && is_get_status_openai != true) { online_status = "no_connection"; } } @@ -951,6 +971,7 @@ async function Generate(type, automatic_trigger, force_name2) {//encode("dsfs"). else if (type !== "swipe") { chat.length = chat.length - 1; count_view_mes -= 1; + openai_msgs.pop(); $('#chat').children().last().hide(500, function () { $(this).remove(); }); @@ -1038,6 +1059,11 @@ async function Generate(type, automatic_trigger, force_name2) {//encode("dsfs"). let mesExamplesArray = mesExamples.split(//gi).slice(1).map(block => `\n${block.trim()}\n`); + if (main_api === 'openai') { + setOpenAIMessages(chat); + setOpenAIMessageExamples(mesExamplesArray); + } + if (is_pygmalion) { storyString += appendToStoryString(charDescription, disable_description_formatting ? '' : name2 + "'s Persona: "); storyString += appendToStoryString(charPersonality, disable_personality_formatting ? '' : 'Personality: '); @@ -1190,6 +1216,10 @@ async function Generate(type, automatic_trigger, force_name2) {//encode("dsfs"). generatedPromtCache += cycleGenerationPromt; if (generatedPromtCache.length == 0) { + if (main_api === 'openai') { + generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, anchorBottom); + } + console.log('generating prompt'); chatString = ""; arrMes = arrMes.reverse(); @@ -1417,6 +1447,8 @@ async function Generate(type, automatic_trigger, force_name2) {//encode("dsfs"). "order": this_settings.order }; } + + var generate_url = ''; if (main_api == 'kobold') { generate_url = '/generate'; @@ -1426,180 +1458,182 @@ async function Generate(type, automatic_trigger, force_name2) {//encode("dsfs"). generate_url = '/generate_novelai'; } console.log('rungenerate calling API'); - jQuery.ajax({ - type: 'POST', // - url: generate_url, // - data: JSON.stringify(generate_data), - beforeSend: function () { - //$('#create_button').attr('value','Creating...'); - }, - cache: false, - dataType: "json", - contentType: "application/json", - success: function (data) { - //console.log('generation success'); - tokens_already_generated += this_amount_gen; // add new gen amt to any prev gen counter.. - //console.log('Tokens requested in total: '+tokens_already_generated); - //$("#send_textarea").focus(); - //$("#send_textarea").removeAttr('disabled'); - is_send_press = false; - if (!data.error) { - //const getData = await response.json(); - var getMessage = ""; - if (main_api == 'kobold') { - getMessage = data.results[0].text; - } else if (main_api == 'textgenerationwebui') { - getMessage = data.data[0]; - if (getMessage == null || data.error) { - callPopup('

Got empty response from Text generation web UI. Try restarting the API with recommended options.

', 'text'); - return; - } - getMessage = getMessage.substring(finalPromt.length); - } else if (main_api == 'novel') { - getMessage = data.output; + if (main_api == 'openai') { + let prompt = prepareOpenAIMessages(name2, storyString); + sendOpenAIRequest(prompt).then(onSuccess).catch(onError); + } + else { + jQuery.ajax({ + type: 'POST', // + url: generate_url, // + data: JSON.stringify(generate_data), + beforeSend: function () { + //$('#create_button').attr('value','Creating...'); + }, + cache: false, + dataType: "json", + contentType: "application/json", + success: onSuccess, + error: onError + }); //end of "if not data error" + } + + function onSuccess(data) { + tokens_already_generated += this_amount_gen; // add new gen amt to any prev gen counter.. + + is_send_press = false; + if (!data.error) { + //const getData = await response.json(); + var getMessage = ""; + if (main_api == 'kobold') { + getMessage = data.results[0].text; + } else if (main_api == 'textgenerationwebui') { + getMessage = data.data[0]; + if (getMessage == null || data.error) { + callPopup('

Got empty response from Text generation web UI. Try restarting the API with recommended options.

', 'text'); + return; } - - if (collapse_newlines) { - getMessage = collapseNewlines(getMessage); - } - - //Pygmalion run again // to make it continue generating so long as it's under max_amount and hasn't signaled - // an end to the character's response via typing "You:" or adding "" - if (is_pygmalion) { - if_typing_text = false; - message_already_generated += getMessage; - promptBias = ''; - //console.log('AI Response so far: '+message_already_generated); - if (message_already_generated.indexOf('You:') === -1 && //if there is no 'You:' in the response msg - message_already_generated.indexOf('<|endoftext|>') === -1 && //if there is no stamp in the response msg - tokens_already_generated < parseInt(amount_gen) && //if the gen'd msg is less than the max response length.. - getMessage.length > 0) { //if we actually have gen'd text at all... - runGenerate(getMessage); - console.log('returning to make pyg generate again'); //generate again with the 'GetMessage' argument.. - return; - } - - getMessage = message_already_generated; - - } - //Formating - getMessage = $.trim(getMessage); - if (is_pygmalion) { - getMessage = getMessage.replace(new RegExp('', "g"), name1); - getMessage = getMessage.replace(new RegExp('', "g"), name2); - getMessage = getMessage.replace(new RegExp('You:', "g"), name1 + ':'); - } - if (getMessage.indexOf(name1 + ":") != -1) { - getMessage = getMessage.substr(0, getMessage.indexOf(name1 + ":")); - - } - if (getMessage.indexOf('<|endoftext|>') != -1) { - getMessage = getMessage.substr(0, getMessage.indexOf('<|endoftext|>')); - - } - // clean-up group message from excessive generations - if (selected_group) { - getMessage = cleanGroupMessage(getMessage); - } - let this_mes_is_name = true; - if (getMessage.indexOf(name2 + ":") === 0) { - getMessage = getMessage.replace(name2 + ':', ''); - getMessage = getMessage.trimStart(); - } else { - this_mes_is_name = false; - } - if (force_name2) this_mes_is_name = true; - //getMessage = getMessage.replace(/^\s+/g, ''); - if (getMessage.length > 0) { - if (chat[chat.length - 1]['swipe_id'] === undefined || - chat[chat.length - 1]['is_user']) { type = 'normal'; } - if (type === 'swipe') { - - chat[chat.length - 1]['swipes'][chat[chat.length - 1]['swipes'].length] = getMessage; - if (chat[chat.length - 1]['swipe_id'] === chat[chat.length - 1]['swipes'].length - 1) { - //console.log(getMessage); - chat[chat.length - 1]['mes'] = getMessage; - // console.log('runGenerate calls addOneMessage for swipe'); - addOneMessage(chat[chat.length - 1], 'swipe'); - } else { - chat[chat.length - 1]['mes'] = getMessage; - } - is_send_press = false; - } else { - console.log('entering chat update routine for non-swipe post'); - is_send_press = false; - chat[chat.length] = {}; - chat[chat.length - 1]['name'] = name2; - chat[chat.length - 1]['is_user'] = false; - chat[chat.length - 1]['is_name'] = this_mes_is_name; - chat[chat.length - 1]['send_date'] = humanizedDateTime(); - getMessage = $.trim(getMessage); - chat[chat.length - 1]['mes'] = getMessage; - - if (selected_group) { - console.log('entering chat update for groups'); - let avatarImg = 'img/fluffy.png'; - if (characters[this_chid].avatar != 'none') { - avatarImg = `characters/${characters[this_chid].avatar}?${Date.now()}`; - } - chat[chat.length - 1]['is_name'] = true; - chat[chat.length - 1]['force_avatar'] = avatarImg; - } - //console.log('runGenerate calls addOneMessage'); - addOneMessage(chat[chat.length - 1]); - - $("#send_but").css("display", "inline"); - $("#loading_mes").css("display", "none"); - } - - - } else { - // regenerate with character speech reenforced - // to make sure we leave on swipe type while also adding the name2 appendage - const newType = type == "swipe" ? "swipe" : "force_name2"; - Generate(newType, automatic_trigger = false, force_name2 = true); - } - } else { - - $("#send_but").css("display", "inline"); - $("#loading_mes").css("display", "none"); - //console.log('runGenerate calling showSwipeBtns'); - showSwipeButtons(); + getMessage = getMessage.substring(finalPromt.length); + } else if (main_api == 'novel') { + getMessage = data.output; + } + if (main_api == 'openai') { + getMessage = data; } - console.log('/savechat called by /Generate'); + if (collapse_newlines) { + getMessage = collapseNewlines(getMessage); + } + + //Pygmalion run again // to make it continue generating so long as it's under max_amount and hasn't signaled + // an end to the character's response via typing "You:" or adding "" + if (is_pygmalion) { + if_typing_text = false; + message_already_generated += getMessage; + promptBias = ''; + //console.log('AI Response so far: '+message_already_generated); + if (message_already_generated.indexOf('You:') === -1 && //if there is no 'You:' in the response msg + message_already_generated.indexOf('<|endoftext|>') === -1 && //if there is no stamp in the response msg + tokens_already_generated < parseInt(amount_gen) && //if the gen'd msg is less than the max response length.. + getMessage.length > 0) { //if we actually have gen'd text at all... + runGenerate(getMessage); + console.log('returning to make pyg generate again'); //generate again with the 'GetMessage' argument.. + return; + } + + getMessage = message_already_generated; + + } + //Formating + getMessage = $.trim(getMessage); + if (is_pygmalion) { + getMessage = getMessage.replace(//g, name1); + getMessage = getMessage.replace(//g, name2); + getMessage = getMessage.replace(/You:/g, name1 + ':'); + } + if (getMessage.indexOf(name1 + ":") != -1) { + getMessage = getMessage.substr(0, getMessage.indexOf(name1 + ":")); + + } + if (getMessage.indexOf('<|endoftext|>') != -1) { + getMessage = getMessage.substr(0, getMessage.indexOf('<|endoftext|>')); + + } + // clean-up group message from excessive generations if (selected_group) { - saveGroupChat(selected_group); - } else { - saveChat(); + getMessage = cleanGroupMessage(getMessage); } - //let final_message_length = encode(JSON.stringify(getMessage)).length; - //console.log('AI Response: +'+getMessage+ '('+final_message_length+' tokens)'); + let this_mes_is_name = true; + if (getMessage.indexOf(name2 + ":") === 0) { + getMessage = getMessage.replace(name2 + ':', ''); + getMessage = getMessage.trimStart(); + } else { + this_mes_is_name = false; + } + if (force_name2) this_mes_is_name = true; + //getMessage = getMessage.replace(/^\s+/g, ''); + if (getMessage.length > 0) { + if (chat[chat.length - 1]['swipe_id'] === undefined || + chat[chat.length - 1]['is_user']) { type = 'normal'; } + if (type === 'swipe') { + chat[chat.length - 1]['swipes'][chat[chat.length - 1]['swipes'].length] = getMessage; + if (chat[chat.length - 1]['swipe_id'] === chat[chat.length - 1]['swipes'].length - 1) { + //console.log(getMessage); + chat[chat.length - 1]['mes'] = getMessage; + // console.log('runGenerate calls addOneMessage for swipe'); + addOneMessage(chat[chat.length - 1], 'swipe'); + } else { + chat[chat.length - 1]['mes'] = getMessage; + } + is_send_press = false; + } else { + console.log('entering chat update routine for non-swipe post'); + is_send_press = false; + chat[chat.length] = {}; + chat[chat.length - 1]['name'] = name2; + chat[chat.length - 1]['is_user'] = false; + chat[chat.length - 1]['is_name'] = this_mes_is_name; + chat[chat.length - 1]['send_date'] = humanizedDateTime(); + getMessage = $.trim(getMessage); + chat[chat.length - 1]['mes'] = getMessage; + + if (selected_group) { + console.log('entering chat update for groups'); + let avatarImg = 'img/fluffy.png'; + if (characters[this_chid].avatar != 'none') { + avatarImg = `characters/${characters[this_chid].avatar}?${Date.now()}`; + } + chat[chat.length - 1]['is_name'] = true; + chat[chat.length - 1]['force_avatar'] = avatarImg; + } + //console.log('runGenerate calls addOneMessage'); + addOneMessage(chat[chat.length - 1]); + + $("#send_but").css("display", "inline"); + $("#loading_mes").css("display", "none"); + } + } else { + // regenerate with character speech reenforced + // to make sure we leave on swipe type while also adding the name2 appendage + const newType = type == "swipe" ? "swipe" : "force_name2"; + Generate(newType, automatic_trigger = false, force_name2 = true); + } + } else { $("#send_but").css("display", "inline"); - //console.log('runGenerate calling showSwipeBtns pt. 2'); + $("#loading_mes").css("display", "none"); + //console.log('runGenerate calling showSwipeBtns'); showSwipeButtons(); - $("#loading_mes").css("display", "none"); - $('.mes_edit:last').show(); - - }, - error: function (jqXHR, exception) { - - - $("#send_textarea").removeAttr('disabled'); - is_send_press = false; - $("#send_but").css("display", "inline"); - $("#loading_mes").css("display", "none"); - console.log(exception); - console.log(jqXHR); - } + console.log('/savechat called by /Generate'); + + if (selected_group) { + saveGroupChat(selected_group); + } else { + saveChat(); + } + //let final_message_length = encode(JSON.stringify(getMessage)).length; + //console.log('AI Response: +'+getMessage+ '('+final_message_length+' tokens)'); + + $("#send_but").css("display", "inline"); + //console.log('runGenerate calling showSwipeBtns pt. 2'); + showSwipeButtons(); + $("#loading_mes").css("display", "none"); + $('.mes_edit:last').show(); + }; + + function onError(jqXHR, exception) { + $("#send_textarea").removeAttr('disabled'); + is_send_press = false; + $("#send_but").css("display", "inline"); + $("#loading_mes").css("display", "none"); + console.log(exception); + console.log(jqXHR); + }; - }); //end of "if not data error" } //rungenerate ends - } else { //generate's primary loop ends, after this is error handling for no-connection or safety-id if (this_chid == undefined || this_chid == 'invalid-safety-id') { @@ -1628,6 +1662,10 @@ function setCharacterName(value) { name2 = value; } +function setOnlineStatus(value) { + online_status = value; +} + function setEditedMessageId(value) { this_edit_mes_id = value; } @@ -1797,7 +1835,15 @@ function changeMainAPI() { maxContextElem: $("#max_context_block"), amountGenElem: $("#amount_gen_block"), softPromptElem: $("#softprompt_block") - } + }, + "openai": { + apiSettings: $("#openai_settings"), + apiConnector: $("#openai_api"), + apiRanges: $("#range_block_openai"), + maxContextElem: $("#max_context_block"), + amountGenElem: $("#amount_gen_block"), + softPromptElem: $("#softprompt_block"), + }, }; console.log('--- apiElements--- '); //console.log(apiElements); @@ -1810,6 +1856,9 @@ function changeMainAPI() { apiObj.apiConnector.css("display", isCurrentApi ? "block" : "none"); apiObj.apiRanges.css("display", isCurrentApi ? "block" : "none"); + // Hide common settings for OpenAI + $("#common-gen-settings-block").css("display", isCurrentApi && apiName !== "openai" ? "block" : "none"); + if (isCurrentApi && apiName === "kobold") { console.log("enabling SP for kobold"); $("#softprompt_block").css("display", "block"); @@ -1989,6 +2038,9 @@ async function getSettings(type) { //Novel loadNovelSettings(settings); + // OpenAI + loadOpenAISettings(data, settings); + //Enable GUI deference settings if GUI is selected for Kobold if (main_api === "kobold") { if (preset_settings == "gui") { @@ -2086,6 +2138,7 @@ async function saveSettings(type) { swipes: swipes, ...nai_settings, ...kai_settings, + ...oai_settings, }), beforeSend: function () { //console.log('saveSettings() -- active_character -- '+active_character); @@ -2253,7 +2306,7 @@ async function getStatusNovel() { }, }); } else { - if (is_get_status != true) { + if (is_get_status != true && is_get_status_openai != true) { online_status = "no_connection"; } } @@ -3591,6 +3644,7 @@ $(document).ready(function () { is_pygmalion = false; is_get_status = false; is_get_status_novel = false; + setOpenAIOnlineStatus(false); online_status = "no_connection"; clearSoftPromptsList(); checkOnlineStatus(); diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 550949bed..ddf6410a3 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -20,6 +20,7 @@ import { import { LoadLocal, SaveLocal, ClearLocal, CheckLocal, LoadLocalBool } from "./f-localStorage.js"; import { selected_group, is_group_generating } from "./group-chats.js"; +import { oai_settings } from "./openai.js"; var NavToggle = document.getElementById("nav-toggle"); var PanelPin = document.getElementById("rm_button_panel_pin"); @@ -233,6 +234,12 @@ function RA_autoconnect() { RA_AC_retries = 1; } break; + case 'openai': + if (oai_settings.api_key_openai) { + $("#api_button_openai").click(); + retry_delay = 100; + RA_AC_retries = 1; + } } if (!connection_made) { diff --git a/public/scripts/openai.js b/public/scripts/openai.js new file mode 100644 index 000000000..61fb1b8e7 --- /dev/null +++ b/public/scripts/openai.js @@ -0,0 +1,575 @@ +/* +* CODE FOR OPENAI SUPPORT +* By CncAnon (@CncAnon1) +*/ + +import { + saveSettingsDebounced, + addOneMessage, + messageFormating, + substituteParams, + count_view_mes, + saveChat, + checkOnlineStatus, + setOnlineStatus, + token, +} from "../script.js"; + +import { + pin_examples, +} from "./power-user.js"; + +export { + is_get_status_openai, + openai_msgs, + oai_settings, + loadOpenAISettings, + setOpenAIMessages, + setOpenAIMessageExamples, + generateOpenAIPromptCache, + prepareOpenAIMessages, + sendOpenAIRequest, + setOpenAIOnlineStatus, +} + +let openai_msgs = []; +let openai_msgs_example = []; + +let is_get_status_openai = false; +let is_api_button_press_openai = false; + +const default_main_prompt = "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}. Write 1 reply only in internet RP style, italicize actions, and avoid quotation marks. Use markdown. Be proactive, creative, and drive the plot and conversation forward. Write at least 1 paragraph, up to 4. Always stay in character and avoid repetition."; +const default_nsfw_prompt = "NSFW/Smut is allowed. Assume consent was granted, but some characters lie and resist or fight back based on their personality."; + +const oai_settings = { + preset_settings_openai: 'Default', + api_key_openai: '', + temp_openai: 1.0, + freq_pen_openai: 0, + pres_pen_openai: 0, + stream_openai: true, + openai_max_context: 4095, + openai_max_tokens: 300, + nsfw_toggle: true, + enhance_definitions: false, + wrap_in_quotes: false, + nsfw_first: false, + main_prompt: default_main_prompt, + nsfw_prompt: default_nsfw_prompt, +}; + +let openai_setting_names; +let openai_settings; + +function setOpenAIOnlineStatus(value) { + is_get_status_openai = value; +} + +function setOpenAIMessages(chat) { + let j = 0; + // clean openai msgs + openai_msgs = []; + for (let i = chat.length - 1; i >= 0; i--) { + // first greeting message + if (j == 0) { + chat[j]['mes'] = substituteParams(chat[j]['mes']); + } + let role = chat[j]['is_user'] ? 'user' : 'assistant'; + let content = chat[j]['mes']; + // Apply the "wrap in quotes" option + if (role == 'user' && oai_settings.wrap_in_quotes) content = `"${content}"`; + openai_msgs[i] = { "role": role, "content": content }; + j++; + } +} + +function setOpenAIMessageExamples(mesExamplesArray) { + // get a nice array of all blocks of all example messages = array of arrays (important!) + openai_msgs_example = []; + for (let item of mesExamplesArray) { + // remove {Example Dialogue:} and replace \r\n with just \n + let replaced = item.replace(//i, "{Example Dialogue:}").replace(/\r/gm, ''); + let parsed = parseExampleIntoIndividual(replaced); + // add to the example message blocks array + openai_msgs_example.push(parsed); + } +} + +function generateOpenAIPromptCache(charPersonality, topAnchorDepth, anchorTop, anchorBottom) { + openai_msgs = openai_msgs.reverse(); + let is_add_personality = false; + openai_msgs.forEach(function (msg, i, arr) {//For added anchors and others + let item = msg["content"]; + if (i === openai_msgs.length - topAnchorDepth && count_view_mes >= topAnchorDepth && !is_add_personality) { + is_add_personality = true; + if ((anchorTop != "" || charPersonality != "")) { + if (anchorTop != "") charPersonality += ' '; + item = `[${name2} is ${charPersonality}${anchorTop}]\n${item}`; + } + } + if (i >= openai_msgs.length - 1 && count_view_mes > 8 && $.trim(item).substr(0, (name1 + ":").length) == name1 + ":") {//For add anchor in end + item = anchorBottom + "\n" + item; + } + msg["content"] = item; + openai_msgs[i] = msg; + }); +} + +function parseExampleIntoIndividual(messageExampleString) { + let result = []; // array of msgs + let tmp = messageExampleString.split("\n"); + let cur_msg_lines = []; + let in_user = false; + let in_bot = false; + // DRY my cock and balls + function add_msg(name, role) { + // join different newlines (we split them by \n and join by \n) + // remove char name + // strip to remove extra spaces + let parsed_msg = cur_msg_lines.join("\n").replace(name + ":", "").trim(); + result.push({ "role": role, "content": parsed_msg }); + cur_msg_lines = []; + } + // skip first line as it'll always be "This is how {bot name} should talk" + for (let i = 1; i < tmp.length; i++) { + let cur_str = tmp[i]; + // if it's the user message, switch into user mode and out of bot mode + // yes, repeated code, but I don't care + if (cur_str.indexOf(name1 + ":") === 0) { + in_user = true; + // we were in the bot mode previously, add the message + if (in_bot) { + add_msg(name2, "assistant"); + } + in_bot = false; + } else if (cur_str.indexOf(name2 + ":") === 0) { + in_bot = true; + // we were in the user mode previously, add the message + if (in_user) { + add_msg(name1, "user"); + } + in_user = false; + } + // push the current line into the current message array only after checking for presence of user/bot + cur_msg_lines.push(cur_str); + } + // Special case for last message in a block because we don't have a new message to trigger the switch + if (in_user) { + add_msg(name1, "user"); + } else if (in_bot) { + add_msg(name2, "assistant"); + } + return result; +} + +function prepareOpenAIMessages(name2, storyString) { + let this_max_context = oai_settings.openai_max_context; + let nsfw_toggle_prompt = ""; + let enhance_definitions_prompt = ""; + + if (oai_settings.nsfw_toggle) { + nsfw_toggle_prompt = oai_settings.nsfw_prompt; + } else { + nsfw_toggle_prompt = "Avoid writing a NSFW/Smut reply. Creatively write around it NSFW/Smut scenarios in character."; + } + // Experimental but kinda works + if (oai_settings.enhance_definitions) { + enhance_definitions_prompt = "If you have more knowledge of " + name2 + ", add to the character's lore and personality to enhance them but keep the Character Sheet's definitions absolute."; + } + + let whole_prompt = []; + // If it's toggled, NSFW prompt goes first. + if (oai_settings.nsfw_first) { + whole_prompt = [nsfw_toggle_prompt, oai_settings.main_prompt, enhance_definitions_prompt, "\n\n", storyString] + } + else { + whole_prompt = [oai_settings.main_prompt, nsfw_toggle_prompt, enhance_definitions_prompt, "\n\n", storyString] + } + + // Join by a space and replace placeholders with real user/char names + storyString = substituteParams(whole_prompt.join(" ")) + + let prompt_msg = { "role": "system", "content": storyString } + let examples_tosend = []; + let openai_msgs_tosend = []; + + // todo: static value, maybe include in the initial context calculation + let new_chat_msg = { "role": "system", "content": "[Start a new chat]" }; + let start_chat_count = countTokens([new_chat_msg]); + let total_count = countTokens([prompt_msg], true) + start_chat_count; + + // The user wants to always have all example messages in the context + if (pin_examples) { + // first we send *all* example messages + // we don't check their token size since if it's bigger than the context, the user is fucked anyway + // and should've have selected that option (maybe have some warning idk, too hard to add) + for (const element of openai_msgs_example) { + // get the current example block with multiple user/bot messages + let example_block = element; + // add the first message from the user to tell the model that it's a new dialogue + // TODO: instead of role user content use role system name example_user + // message from the user so the model doesn't confuse the context (maybe, I just think that this should be done) + if (example_block.length != 0) { + examples_tosend.push(new_chat_msg); + } + for (const example of example_block) { + // add all the messages from the example + examples_tosend.push(example); + } + } + total_count += countTokens(examples_tosend); + // go from newest message to oldest, because we want to delete the older ones from the context + for (let j = openai_msgs.length - 1; j >= 0; j--) { + let item = openai_msgs[j]; + let item_count = countTokens(item); + // If we have enough space for this message, also account for the max assistant reply size + if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) { + openai_msgs_tosend.push(item); + total_count += item_count; + } + else { + // early break since if we still have more messages, they just won't fit anyway + break; + } + } + } else { + for (let j = openai_msgs.length - 1; j >= 0; j--) { + let item = openai_msgs[j]; + let item_count = countTokens(item); + // If we have enough space for this message, also account for the max assistant reply size + if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) { + openai_msgs_tosend.push(item); + total_count += item_count; + } + else { + // early break since if we still have more messages, they just won't fit anyway + break; + } + } + + console.log(total_count); + + for (const example of openai_msgs_example) { + // get the current example block with multiple user/bot messages + let example_block = example; + + for (let k = 0; k < example_block.length; k++) { + if (example_block.length == 0) { continue; } + let example_count = countTokens(example_block[k]); + // add all the messages from the example + if ((total_count + example_count + start_chat_count) < (this_max_context - oai_settings.openai_max_tokens)) { + if (k == 0) { + examples_tosend.push(new_chat_msg); + total_count += start_chat_count; + } + examples_tosend.push(example_block[k]); + total_count += example_count; + } + else { break; } + } + } + } + // reverse the messages array because we had the newest at the top to remove the oldest, + // now we want proper order + openai_msgs_tosend.reverse(); + openai_msgs_tosend = [prompt_msg, ...examples_tosend, new_chat_msg, ...openai_msgs_tosend] + + console.log("We're sending this:") + console.log(openai_msgs_tosend); + console.log(`Calculated the total context to be ${total_count} tokens`); + return openai_msgs_tosend; +} + +async function sendOpenAIRequest(openai_msgs_tosend) { + const generate_data = { + "messages": openai_msgs_tosend, + // todo: add setting for le custom model + "model": "gpt-3.5-turbo-0301", + "temperature": parseFloat(oai_settings.temp_openai), + "frequency_penalty": parseFloat(oai_settings.freq_pen_openai), + "presence_penalty": parseFloat(oai_settings.pres_pen_openai), + "max_tokens": oai_settings.openai_max_tokens, + "stream": oai_settings.stream_openai, + }; + + const generate_url = '/generate_openai'; + const streaming = oai_settings.stream_openai; + const last_view_mes = count_view_mes; + + const response = await fetch(generate_url, { + method: 'POST', + body: JSON.stringify(generate_data), + headers: { + 'Content-Type': 'application/json', + "X-CSRF-Token": token, + } + }); + + const data = await response.json(); + + if (data.error) { + throw new Error(data); + } + + return data.choices[0]["message"]["content"]; +} + +// Unused +function onStream(e, resolve, reject, last_view_mes) { + let end = false; + if (!oai_settings.stream_openai) + return; + let response = e.currentTarget.response; + if (response == "{\"error\":true}") { + reject('', 'error'); + } + + let eventList = response.split("\n"); + let getMessage = ""; + for (let event of eventList) { + if (!event.startsWith("data")) + continue; + if (event == "data: [DONE]") { + chat[chat.length - 1]['mes'] = getMessage; + $("#send_but").css("display", "block"); + $("#loading_mes").css("display", "none"); + saveChat(); + end = true; + break; + } + let data = JSON.parse(event.substring(6)); + // the first and last messages are undefined, protect against that + getMessage += data.choices[0]["delta"]["content"] || ""; + } + + if ($("#chat").children().filter(`[mesid="${last_view_mes}"]`).length == 0) { + chat[chat.length] = {}; + chat[chat.length - 1]['name'] = name2; + chat[chat.length - 1]['is_user'] = false; + chat[chat.length - 1]['is_name'] = false; + chat[chat.length - 1]['send_date'] = Date.now(); + chat[chat.length - 1]['mes'] = ""; + addOneMessage(chat[chat.length - 1]); + } + + let messageText = messageFormating($.trim(getMessage), name1); + $("#chat").children().filter(`[mesid="${last_view_mes}"]`).children('.mes_block').children('.mes_text').html(messageText); + + let $textchat = $('#chat'); + $textchat.scrollTop($textchat[0].scrollHeight); + + if (end) { + resolve(); + } +} + +function countTokens(messages, full = false) { + if (!Array.isArray(messages)) { + messages = [messages]; + } + let token_count = -1; + jQuery.ajax({ + async: false, + type: 'POST', // + url: '/tokenize_openai', // + data: JSON.stringify(messages), + dataType: "json", + contentType: "application/json", + success: function (data) { + token_count = data.token_count; + } + }); + if (!full) token_count -= 2; + return token_count; +} + +function loadOpenAISettings(data, settings) { + if (settings.api_key_openai != undefined) { + oai_settings.api_key_openai = settings.api_key_openai; + $("#api_key_openai").val(oai_settings.api_key_openai); + } + + openai_setting_names = data.openai_setting_names; + openai_settings = data.openai_settings; + openai_settings = data.openai_settings; + openai_settings.forEach(function (item, i, arr) { + openai_settings[i] = JSON.parse(item); + }); + + $("#settings_perset_openai").empty(); + let arr_holder = {}; + openai_setting_names.forEach(function (item, i, arr) { + arr_holder[item] = i; + $('#settings_perset_openai').append(``); + + }); + openai_setting_names = arr_holder; + + oai_settings.preset_settings_openai = settings.preset_settings_openai; + $(`#settings_perset_openai option[value=${openai_setting_names[oai_settings.preset_settings_openai]}]`).attr('selected', true); + + oai_settings.temp_openai = settings.temp_openai ?? 0.9; + oai_settings.freq_pen_openai = settings.freq_pen_openai ?? 0.7; + oai_settings.pres_pen_openai = settings.pres_pen_openai ?? 0.7; + oai_settings.stream_openai = settings.stream_openai ?? true; + oai_settings.openai_max_context = settings.openai_max_context ?? 4095; + oai_settings.openai_max_tokens = settings.openai_max_tokens ?? 300; + + if (settings.nsfw_toggle !== undefined) oai_settings.nsfw_toggle = !!settings.nsfw_toggle; + if (settings.keep_example_dialogue !== undefined) oai_settings.keep_example_dialogue = !!settings.keep_example_dialogue; + if (settings.enhance_definitions !== undefined) oai_settings.enhance_definitions = !!settings.enhance_definitions; + if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes; + if (settings.nsfw_first !== undefined) oai_settings.nsfw_first = !!settings.nsfw_first; + + $('#stream_toggle').prop('checked', oai_settings.stream_openai); + + $('#openai_max_context').val(oai_settings.openai_max_context); + $('#openai_max_context_counter').html(`${oai_settings.openai_max_context} Tokens`); + + $('#openai_max_tokens').val(oai_settings.openai_max_tokens); + + $('#nsfw_toggle').prop('checked', oai_settings.nsfw_toggle); + $('#keep_example_dialogue').prop('checked', oai_settings.keep_example_dialogue); + $('#enhance_definitions').prop('checked', oai_settings.enhance_definitions); + $('#wrap_in_quotes').prop('checked', oai_settings.wrap_in_quotes); + $('#nsfw_first').prop('checked', oai_settings.nsfw_first); + + if (settings.main_prompt !== undefined) oai_settings.main_prompt = settings.main_prompt; + if (settings.nsfw_prompt !== undefined) oai_settings.nsfw_prompt = settings.nsfw_prompt; + $('#main_prompt_textarea').val(oai_settings.main_prompt); + $('#nsfw_prompt_textarea').val(oai_settings.nsfw_prompt); + + $('#temp_openai').val(oai_settings.temp_openai); + $('#temp_counter_openai').text(Number(oai_settings.temp_openai).toFixed(2)); + + $('#freq_pen_openai').val(oai_settings.freq_pen_openai); + $('#freq_pen_counter_openai').text(Number(oai_settings.freq_pen_openai).toFixed(2)); + + $('#pres_pen_openai').val(oai_settings.pres_pen_openai); + $('#pres_pen_counter_openai').text(Number(oai_settings.pres_pen_openai).toFixed(2)); +} + +async function getStatusOpen() { + if (is_get_status_openai) { + let data = { key: oai_settings.api_key_openai }; + + jQuery.ajax({ + type: 'POST', // + url: '/getstatus_openai', // + data: JSON.stringify(data), + beforeSend: function () { }, + cache: false, + dataType: "json", + contentType: "application/json", + success: function (data) { + if (!('error' in data)) + setOnlineStatus('Valid'); + resultCheckStatusOpen(); + }, + error: function (jqXHR, exception) { + setOnlineStatus('no_connection'); + console.log(exception); + console.log(jqXHR); + resultCheckStatusOpen(); + } + }); + } else { + setOnlineStatus('no_connection'); + } +} + +function resultCheckStatusOpen() { + is_api_button_press_openai = false; + checkOnlineStatus(); + $("#api_loading_openai").css("display", 'none'); + $("#api_button_openai").css("display", 'inline-block'); +} + +$(document).ready(function () { + $(document).on('input', '#temp_openai', function () { + oai_settings.temp_openai = $(this).val(); + $('#temp_counter_openai').text(Number($(this).val()).toFixed(2)); + saveSettingsDebounced(); + }); + + $(document).on('input', '#freq_pen_openai', function () { + oai_settings.freq_pen_openai = $(this).val(); + $('#freq_pen_counter_openai').text(Number($(this).val()).toFixed(2)); + saveSettingsDebounced(); + }); + + $(document).on('input', '#pres_pen_openai', function () { + oai_settings.pres_pen_openai = $(this).val(); + $('#pres_pen_counter_openai').text(Number($(this).val())); + saveSettingsDebounced(); + + }); + + $(document).on('input', '#openai_max_context', function () { + oai_settings.openai_max_context = parseInt($(this).val()); + $('#openai_max_context_counter').text(`${$(this).val()} Tokens`); + saveSettingsDebounced(); + }); + + $(document).on('input', '#openai_max_tokens', function () { + oai_settings.openai_max_tokens = parseInt($(this).val()); + saveSettingsDebounced(); + }); + + $('#stream_toggle').change(function () { + oai_settings.stream_openai = !!$('#stream_toggle').prop('checked'); + saveSettingsDebounced(); + }); + + $('#nsfw_toggle').change(function () { + oai_settings.nsfw_toggle = !!$('#nsfw_toggle').prop('checked'); + saveSettingsDebounced(); + }); + + $('#enhance_definitions').change(function () { + oai_settings.enhance_definitions = !!$('#enhance_definitions').prop('checked'); + saveSettingsDebounced(); + }); + + $('#wrap_in_quotes').change(function () { + oai_settings.wrap_in_quotes = !!$('#wrap_in_quotes').prop('checked'); + saveSettingsDebounced(); + }); + + $('#nsfw_first').change(function () { + oai_settings.nsfw_first = !!$('#nsfw_first').prop('checked'); + saveSettingsDebounced(); + }); + + $("#settings_perset_openai").change(function () { + oai_settings.preset_settings_openai = $('#settings_perset_openai').find(":selected").text(); + + const preset = openai_settings[openai_setting_names[preset_settings_openai]]; + oai_settings.temp_openai = preset.temperature; + oai_settings.freq_pen_openai = preset.frequency_penalty; + oai_settings.pres_pen_openai = preset.presence_penalty; + + // probably not needed + $('#temp_counter_openai').text(oai_settings.temp_openai); + $('#freq_pen_counter_openai').text(oai_settings.freq_pen_openai); + $('#pres_pen_counter_openai').text(oai_settings.pres_pen_openai); + + $('#temp_openai').val(oai_settings.temp_openai).trigger('input'); + $('#freq_pen_openai').val(oai_settings.freq_pen_openai).trigger('input'); + $('#pres_pen_openai').val(oai_settings.pres_pen_openai).trigger('input'); + + saveSettingsDebounced(); + }); + + $("#api_button_openai").click(function () { + if ($('#api_key_openai').val() != '') { + $("#api_loading_openai").css("display", 'inline-block'); + $("#api_button_openai").css("display", 'none'); + oai_settings.api_key_openai = $.trim($('#api_key_openai').val()); + saveSettingsDebounced(); + is_get_status_openai = true; + is_api_button_press_openai = true; + getStatusOpen(); + } + }); +}); \ No newline at end of file diff --git a/public/style.css b/public/style.css index c9b73a2ad..3571fa065 100644 --- a/public/style.css +++ b/public/style.css @@ -788,23 +788,14 @@ img[src*="user-slash-solid.svg"] { #api_button:hover, #api_button_novel:hover, -#api_button_textgenerationwebui { +#api_button_textgenerationwebui:hover { background-color: green; } -#api_loading, -#api_loading_textgenerationwebui { +img[src="img/load.svg"] { width: 25px; height: 25px; display: none; - -} - -#api_loading_novel { - width: 25px; - height: 25px; - display: none; - } #rm_characters_block { @@ -1237,15 +1228,14 @@ input[type=search]:focus::-webkit-search-cancel-button { } /* ------ online status indicators and texts. 2 = kobold AI, 3 = Novel AI ----------*/ #online_status2, -#online_status4 { +.online_status4 { opacity: 0.5; margin-top: 2px; - margin-bottom: 15px; } #online_status_indicator2, -#online_status_indicator4 { +.online_status_indicator4 { border-radius: 7px; width: 14px; height: 14px; @@ -1254,7 +1244,7 @@ input[type=search]:focus::-webkit-search-cancel-button { } #online_status_text2, -#online_status_text4 { +.online_status_text4 { margin-left: 4px; display: inline-block; } diff --git a/server.js b/server.js index b439d0762..5553b5c9a 100644 --- a/server.js +++ b/server.js @@ -29,11 +29,15 @@ const autorun = config.autorun; const enableExtensions = config.enableExtensions; const listen = config.listen; +const axios = require('axios'); +const tiktoken = require('@dqbd/tiktoken'); + var Client = require('node-rest-client').Client; var client = new Client(); var api_server = "http://0.0.0.0:5000"; var api_novelai = "https://api.novelai.net"; +let api_openai = "https://api.openai.com/v1"; var main_api = "kobold"; var response_get_story; @@ -51,6 +55,10 @@ var response_getstatus_novel; var response_getlastversion; var api_key_novel; +let response_generate_openai; +let response_getstatus_openai; +let api_key_openai; + //RossAscends: Added function to format dates used in files and chat timestamps to a humanized format. //Mostly I wanted this to be for file names, but couldn't figure out exactly where the filename save code was as everything seemed to be connected. //During testing, this performs the same as previous date.now() structure. @@ -93,6 +101,7 @@ const directories = { backgrounds: 'public/backgrounds', novelAI_Settings: 'public/NovelAI Settings', koboldAI_Settings: 'public/KoboldAI Settings', + openAI_Settings: 'public/OpenAI Settings', }; // CSRF Protection // @@ -851,6 +860,8 @@ app.post('/getsettings', jsonParser, (request, response) => { //Wintermute's cod const koboldai_setting_names = []; const novelai_settings = []; const novelai_setting_names = []; + const openai_settings = []; + const openai_setting_names = []; const settings = fs.readFileSync('public/settings.json', 'utf8', (err, data) => { if (err) return response.sendStatus(500); @@ -909,6 +920,30 @@ app.post('/getsettings', jsonParser, (request, response) => { //Wintermute's cod novelai_setting_names.push(item.replace(/\.[^/.]+$/, '')); }); + //OpenAI + const files3 = fs + .readdirSync('public/OpenAI Settings') + .sort( + (a, b) => + new Date(fs.statSync(`public/OpenAI Settings/${b}`).mtime) - + new Date(fs.statSync(`public/OpenAI Settings/${a}`).mtime) + ); + + files3.forEach(item => { + const file3 = fs.readFileSync( + `public/OpenAI Settings/${item}`, + 'utf8', + (err, data) => { + if (err) return response.sendStatus(500); + + return data; + } + ); + + openai_settings.push(file3); + openai_setting_names.push(item.replace(/\.[^/.]+$/, '')); + }); + response.send({ settings, koboldai_settings, @@ -916,6 +951,8 @@ app.post('/getsettings', jsonParser, (request, response) => { //Wintermute's cod world_names, novelai_settings, novelai_setting_names, + openai_settings, + openai_setting_names, enable_extensions: enableExtensions, }); }); @@ -1550,6 +1587,126 @@ app.post('/deletegroup', jsonParser, async (request, response) => { return response.send({ ok: true }); }); +/* OpenAI */ +app.post("/getstatus_openai", jsonParser, function(request, response_getstatus_openai = response){ + if(!request.body) return response_getstatus_openai.sendStatus(400); + api_key_openai = request.body.key; + const args = { + headers: { "Authorization": "Bearer "+api_key_openai} + }; + client.get(api_openai+"/models",args, function (data, response) { + if(response.statusCode == 200){ + console.log(data); + response_getstatus_openai.send(data);//data); + } + if(response.statusCode == 401){ + console.log('Access Token is incorrect.'); + response_getstatus_openai.send({error: true}); + } + if(response.statusCode == 500 || response.statusCode == 501 || response.statusCode == 501 || response.statusCode == 503 || response.statusCode == 507){ + console.log(data); + response_getstatus_openai.send({error: true}); + } + }).on('error', function (err) { + response_getstatus_openai.send({error: true}); + }); +}); + +app.post("/generate_openai", jsonParser, function(request, response_generate_openai){ + if(!request.body) return response_generate_openai.sendStatus(400); + + console.log(request.body); + const config = { + method: 'post', + url: api_openai + '/chat/completions', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + api_key_openai + }, + data: { + "messages": request.body.messages, + "model": request.body.model, + "temperature": request.body.temperature, + "max_tokens": request.body.max_tokens, + "stream": request.body.stream, + "presence_penalty": request.body.presence_penalty, + "frequency_penalty": request.body.frequency_penalty, + "stop": request.body.stop, + "logit_bias": request.body.logit_bias + } + }; + + if (request.body.stream) + config.responseType = 'stream'; + + axios(config) + .then(function (response) { + if (response.status <= 299) { + if (request.body.stream) { + console.log("Streaming request in progress") + response.data.pipe(response_generate_openai); + response.data.on('end', function () { + console.log("Streaming request finished"); + response_generate_openai.end(); + }); + } else { + console.log(response.data); + response_generate_openai.send(response.data); + } + } else if (response.status == 400) { + console.log('Validation error'); + response_generate_openai.send({ error: true }); + } else if (response.status == 401) { + console.log('Access Token is incorrect'); + response_generate_openai.send({ error: true }); + } else if (response.status == 402) { + console.log('An active subscription is required to access this endpoint'); + response_generate_openai.send({ error: true }); + } else if (response.status == 500 || response.status == 409) { + if (request.body.stream) { + response.data.on('data', chunk => { + console.log(chunk.toString()); + }); + } else { + console.log(response.data); + } + response_generate_openai.send({ error: true }); + } + }) + .catch(function (error) { + if(error.response){ + if (request.body.stream) { + error.response.data.on('data', chunk => { + console.log(chunk.toString()); + }); + } else { + console.log(error.response.data); + } + } + response_generate_openai.send({ error: true }); + }); +}); + +const turbo_encoder = tiktoken.get_encoding("cl100k_base"); + +app.post("/tokenize_openai", jsonParser, function(request, response_tokenize_openai = response){ + if(!request.body) return response_tokenize_openai.sendStatus(400); + + let num_tokens = 0; + for (var msg of request.body) { + num_tokens += 4; + for (const [key, value] of Object.entries(msg)) { + num_tokens += turbo_encoder.encode(value).length; + if (key == "name") { + num_tokens += -1; + } + } + } + num_tokens += 2; + + response_tokenize_openai.send({"token_count": num_tokens}); +}); + // ** REST CLIENT ASYNC WRAPPERS ** function deleteAsync(url, args) { return new Promise((resolve, reject) => { From edab2e84b9d2b30780d152708ece9e7d537c07b5 Mon Sep 17 00:00:00 2001 From: SillyLossy Date: Wed, 22 Mar 2023 00:51:06 +0200 Subject: [PATCH 09/11] More credits --- public/scripts/openai.js | 1 + readme.md | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 61fb1b8e7..9bb90de03 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -1,6 +1,7 @@ /* * CODE FOR OPENAI SUPPORT * By CncAnon (@CncAnon1) +* https://github.com/CncAnon1/TavernAITurbo */ import { diff --git a/readme.md b/readme.md index d0e30104d..617bb5311 100644 --- a/readme.md +++ b/readme.md @@ -90,7 +90,8 @@ Contact us on Discord: Cohee#1207 or RossAscends#1779 ## Screenshots image -## License -* TAI Base: Unknown +## License and credits +* TAI Base by Humi: Unknown license * SillyLossy's TAI mod: Public domain * RossAscends' additions: Public domain +* Portions of CncAnon's TavernAITurbo mod: Unknown license \ No newline at end of file From 7cce26bd221b44f39e9b55203b5f05ea337acb26 Mon Sep 17 00:00:00 2001 From: SillyLossy Date: Wed, 22 Mar 2023 01:16:16 +0200 Subject: [PATCH 10/11] Add world info support for OAI. Fix regens --- public/script.js | 19 ++++--------------- public/scripts/openai.js | 15 ++++++++++++--- public/scripts/world-info.js | 13 +++++++++++++ 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/public/script.js b/public/script.js index a69307590..f2d13c81b 100644 --- a/public/script.js +++ b/public/script.js @@ -16,7 +16,7 @@ import { world_info_data, world_info_depth, world_info, - checkWorldInfo, + getWorldInfoPrompt, selectImportedWorldInfo, setWorldInfoSettings, deleteWorldInfo, @@ -55,7 +55,8 @@ import { setOpenAIOnlineStatus, generateOpenAIPromptCache, oai_settings, - is_get_status_openai + is_get_status_openai, + openai_msgs, } from "./scripts/openai.js"; import { @@ -900,18 +901,6 @@ function getExtensionPrompt() { return extension_prompt; } -function getWorldInfoPrompt(chat2) { - let worldInfoString = "", worldInfoBefore = "", worldInfoAfter = ""; - - if (world_info && world_info_data) { - const activatedWorldInfo = checkWorldInfo(chat2); - worldInfoBefore = activatedWorldInfo.worldInfoBefore; - worldInfoAfter = activatedWorldInfo.worldInfoAfter; - worldInfoString = worldInfoBefore + worldInfoAfter; - } - return { worldInfoString, worldInfoBefore, worldInfoAfter }; -} - function baseChatReplace(value, name1, name2) { if (value !== undefined && value.length > 0) { if (is_pygmalion) { @@ -1461,7 +1450,7 @@ async function Generate(type, automatic_trigger, force_name2) {//encode("dsfs"). if (main_api == 'openai') { - let prompt = prepareOpenAIMessages(name2, storyString); + let prompt = prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldInfoAfter, extension_prompt); sendOpenAIRequest(prompt).then(onSuccess).catch(onError); } else { diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 9bb90de03..743140f34 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -163,7 +163,16 @@ function parseExampleIntoIndividual(messageExampleString) { return result; } -function prepareOpenAIMessages(name2, storyString) { +function formatWorldInfo(value) { + if (!value) { + return ''; + } + + // placeholder if we would want to apply some formatting + return `[Details of the fictional world the RP set in:\n${value}\n]`; +} + +function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldInfoAfter, extensionPrompt) { let this_max_context = oai_settings.openai_max_context; let nsfw_toggle_prompt = ""; let enhance_definitions_prompt = ""; @@ -181,10 +190,10 @@ function prepareOpenAIMessages(name2, storyString) { let whole_prompt = []; // If it's toggled, NSFW prompt goes first. if (oai_settings.nsfw_first) { - whole_prompt = [nsfw_toggle_prompt, oai_settings.main_prompt, enhance_definitions_prompt, "\n\n", storyString] + whole_prompt = [nsfw_toggle_prompt, oai_settings.main_prompt, enhance_definitions_prompt, "\n\n", formatWorldInfo(worldInfoBefore), storyString, formatWorldInfo(worldInfoAfter), extensionPrompt] } else { - whole_prompt = [oai_settings.main_prompt, nsfw_toggle_prompt, enhance_definitions_prompt, "\n\n", storyString] + whole_prompt = [oai_settings.main_prompt, nsfw_toggle_prompt, enhance_definitions_prompt, "\n\n", formatWorldInfo(worldInfoBefore), storyString, formatWorldInfo(worldInfoAfter), extensionPrompt] } // Join by a space and replace placeholders with real user/char names diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index bafddbfb5..02dcb37ba 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -13,6 +13,7 @@ export { deleteWorldInfo, selectImportedWorldInfo, setWorldInfoSettings, + getWorldInfoPrompt, } let world_info = null; @@ -30,6 +31,18 @@ const world_info_position = { after: 1, }; +function getWorldInfoPrompt(chat2) { + let worldInfoString = "", worldInfoBefore = "", worldInfoAfter = ""; + + if (world_info && world_info_data) { + const activatedWorldInfo = checkWorldInfo(chat2); + worldInfoBefore = activatedWorldInfo.worldInfoBefore; + worldInfoAfter = activatedWorldInfo.worldInfoAfter; + worldInfoString = worldInfoBefore + worldInfoAfter; + } + return { worldInfoString, worldInfoBefore, worldInfoAfter }; +} + function setWorldInfoSettings(settings, data) { if (settings.world_info_depth !== undefined) world_info_depth = Number(settings.world_info_depth); From fd14855da979e475e14625d7f0216af992592165 Mon Sep 17 00:00:00 2001 From: SillyLossy Date: Wed, 22 Mar 2023 02:18:57 +0200 Subject: [PATCH 11/11] New icon for connection. Note page for OAI key. Fix swipes with OAI. OAI model selection. --- public/img/plug-circle-exclamation-solid.svg | 1 + public/img/plug-solid.svg | 1 + public/index.html | 22 ++++++++++-- public/notes/oai_api_key.html | 37 ++++++++++++++++++++ public/script.js | 9 ++++- public/scripts/openai.js | 23 ++++++++++-- public/style.css | 5 ++- 7 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 public/img/plug-circle-exclamation-solid.svg create mode 100644 public/img/plug-solid.svg create mode 100644 public/notes/oai_api_key.html diff --git a/public/img/plug-circle-exclamation-solid.svg b/public/img/plug-circle-exclamation-solid.svg new file mode 100644 index 000000000..cb20178ab --- /dev/null +++ b/public/img/plug-circle-exclamation-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/img/plug-solid.svg b/public/img/plug-solid.svg new file mode 100644 index 000000000..d80710780 --- /dev/null +++ b/public/img/plug-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/index.html b/public/index.html index cd3e61de9..2a583bb90 100644 --- a/public/index.html +++ b/public/index.html @@ -122,7 +122,11 @@

API key

-
Where to get (?)
+
Where to get + + ? + +
@@ -186,6 +190,11 @@

Preset settings

-
Selecting settings
- +

Model

+
diff --git a/public/notes/oai_api_key.html b/public/notes/oai_api_key.html new file mode 100644 index 000000000..971918c3d --- /dev/null +++ b/public/notes/oai_api_key.html @@ -0,0 +1,37 @@ + + + + Advanced Settings + + + + + + + + + +
+
+

OpenAI API key

+
+

How to get:

+
    +
  1. + Go to OpenAI and sign in. +
  2. +
  3. + Use "View API keys" option to create a new API key. +
  4. +
+

Important!

+

+ Lost API keys can't be restored! Make sure to keep it safe! +

+
+
+ + + \ No newline at end of file diff --git a/public/script.js b/public/script.js index f2d13c81b..fdfc4c058 100644 --- a/public/script.js +++ b/public/script.js @@ -103,6 +103,7 @@ export { api_server, nai_settings, token, + name1, is_send_press, api_server_textgenerationwebui, count_view_mes, @@ -1049,7 +1050,13 @@ async function Generate(type, automatic_trigger, force_name2) {//encode("dsfs"). let mesExamplesArray = mesExamples.split(//gi).slice(1).map(block => `\n${block.trim()}\n`); if (main_api === 'openai') { - setOpenAIMessages(chat); + const oai_chat = [...chat]; + + if (type == 'swipe') { + oai_chat.pop(); + } + + setOpenAIMessages(oai_chat); setOpenAIMessageExamples(mesExamplesArray); } diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 743140f34..fa4766130 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -14,6 +14,7 @@ import { checkOnlineStatus, setOnlineStatus, token, + name1, } from "../script.js"; import { @@ -57,6 +58,7 @@ const oai_settings = { nsfw_first: false, main_prompt: default_main_prompt, nsfw_prompt: default_nsfw_prompt, + openai_model: 'gpt-3.5-turbo-0301', }; let openai_setting_names; @@ -77,6 +79,16 @@ function setOpenAIMessages(chat) { } let role = chat[j]['is_user'] ? 'user' : 'assistant'; let content = chat[j]['mes']; + + // system messages produce no content + if (chat[j]['is_system']) { + role = 'system'; + content = ''; + } + + // replace bias markup + content = (content ?? '').replace(/{([^}]+)}/g, ''); + // Apply the "wrap in quotes" option if (role == 'user' && oai_settings.wrap_in_quotes) content = `"${content}"`; openai_msgs[i] = { "role": role, "content": content }; @@ -293,8 +305,7 @@ function prepareOpenAIMessages(name2, storyString, worldInfoBefore, worldInfoAft async function sendOpenAIRequest(openai_msgs_tosend) { const generate_data = { "messages": openai_msgs_tosend, - // todo: add setting for le custom model - "model": "gpt-3.5-turbo-0301", + "model": oai_settings.openai_model, "temperature": parseFloat(oai_settings.temp_openai), "frequency_penalty": parseFloat(oai_settings.freq_pen_openai), "presence_penalty": parseFloat(oai_settings.pres_pen_openai), @@ -430,6 +441,7 @@ function loadOpenAISettings(data, settings) { if (settings.enhance_definitions !== undefined) oai_settings.enhance_definitions = !!settings.enhance_definitions; if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes; if (settings.nsfw_first !== undefined) oai_settings.nsfw_first = !!settings.nsfw_first; + if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model; $('#stream_toggle').prop('checked', oai_settings.stream_openai); @@ -437,6 +449,7 @@ function loadOpenAISettings(data, settings) { $('#openai_max_context_counter').html(`${oai_settings.openai_max_context} Tokens`); $('#openai_max_tokens').val(oai_settings.openai_max_tokens); + $(`#model_openai_select option[value="${oai_settings.openai_model}"`).attr('selected', true); $('#nsfw_toggle').prop('checked', oai_settings.nsfw_toggle); $('#keep_example_dialogue').prop('checked', oai_settings.keep_example_dialogue); @@ -526,6 +539,12 @@ $(document).ready(function () { saveSettingsDebounced(); }); + $("#model_openai_select").change(function() { + const value = $(this).val(); + oai_settings.openai_model = value; + saveSettingsDebounced(); + }); + $('#stream_toggle').change(function () { oai_settings.stream_openai = !!$('#stream_toggle').prop('checked'); saveSettingsDebounced(); diff --git a/public/style.css b/public/style.css index 3571fa065..73c77dfe4 100644 --- a/public/style.css +++ b/public/style.css @@ -2716,7 +2716,10 @@ label[for="extensions_autoconnect"] { } .icon-connect{ - background-image: url('img/wifi-solid.svg'); + background-image: url('img/plug-solid.svg'); +} +.icon-connect.redOverlayGlow { + background-image: url('img/plug-circle-exclamation-solid.svg'); } .icon-sliders{ background-image: url('img/sliders-solid.svg');