mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Refactor API keys handling. Remove ST hosting from colab
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@ public/settings.json
|
||||
/thumbnails
|
||||
whitelist.txt
|
||||
.vscode
|
||||
secrets.json
|
||||
|
297
colab/GPU.ipynb
297
colab/GPU.ipynb
@ -6,68 +6,8 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Links**<br>\n",
|
||||
"SillyTavern GitHub: https://github.com/Cohee1207/SillyTavern<br>\n",
|
||||
"Extensions API GitHub: https://github.com/Cohee1207/SillyTavern-extras/<br>\n",
|
||||
"SillyTavern community Discord (support and discussion): https://discord.gg/RZdyAEUPvj<br>\n",
|
||||
"Contact the maintainer directly: Cohee#1207"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "_1gpebrnlp5-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#@title <b><-- Convert TavernAI characters to SillyTavern format</b>\n",
|
||||
"\n",
|
||||
"!mkdir /convert\n",
|
||||
"%cd /convert\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"from google.colab import drive\n",
|
||||
"\n",
|
||||
"drive.mount(\"/convert/drive\")\n",
|
||||
"\n",
|
||||
"!git clone -b tools https://github.com/EnergoStalin/SillyTavern.git\n",
|
||||
"%cd SillyTavern\n",
|
||||
"\n",
|
||||
"!curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash\n",
|
||||
"!nvm install 19.1.0\n",
|
||||
"!nvm use 19.1.0\n",
|
||||
"\n",
|
||||
"%cd tools/charaverter\n",
|
||||
"\n",
|
||||
"!npm i\n",
|
||||
"\n",
|
||||
"path = \"/convert/drive/MyDrive/TavernAI/characters\"\n",
|
||||
"output = \"/convert/drive/MyDrive/SillyTavern/characters\"\n",
|
||||
"if not os.path.exists(path):\n",
|
||||
" path = output\n",
|
||||
"\n",
|
||||
"!mkdir -p $output\n",
|
||||
"!node main.mjs $path $output\n",
|
||||
"\n",
|
||||
"drive.flush_and_unmount()\n",
|
||||
"\n",
|
||||
"%cd /\n",
|
||||
"!rm -rf /convert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ewkXkyiFP2Hq"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#@title <-- Tap this if you play on Mobile { display-mode: \"form\" }\n",
|
||||
"%%html\n",
|
||||
"<b>Press play on the music player to keep the tab alive, then start KoboldAI below (Uses only 13MB of data)</b><br/>\n",
|
||||
"<audio src=\"https://raw.githubusercontent.com/KoboldAI/KoboldAI-Client/main/colab/silence.m4a\" controls>"
|
||||
"SillyTavern community Discord (support and discussion): https://discord.gg/RZdyAEUPvj"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -79,16 +19,6 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#@title <b><-- Select your model below and then click this to start KoboldAI</b>\n",
|
||||
"\n",
|
||||
"Model = \"Руgmаlіоn 6В\" #@param [\"Nerys V2 6B\", \"Erebus 6B\", \"Skein 6B\", \"Janeway 6B\", \"Adventure 6B\", \"Руgmаlіоn 6В\", \"Руgmаlіоn 6В Dev\", \"Lit V2 6B\", \"Lit 6B\", \"Shinen 6B\", \"Nerys 2.7B\", \"AID 2.7B\", \"Erebus 2.7B\", \"Janeway 2.7B\", \"Picard 2.7B\", \"Horni LN 2.7B\", \"Horni 2.7B\", \"Shinen 2.7B\", \"OPT 2.7B\", \"Fairseq Dense 2.7B\", \"Neo 2.7B\", \"Руgwау 6B\", \"Nerybus 6.7B\", \"Руgwау v8p4\", \"PPO-Janeway 6B\", \"PPO Shуgmаlіоn 6B\", \"LLaMA 7B\", \"Janin-GPTJ\", \"Javelin-GPTJ\", \"Javelin-R\", \"Janin-R\", \"Javalion-R\", \"Javalion-GPTJ\", \"Javelion-6B\", \"GPT-J-Руg-PPO-6B\", \"ppo_hh_pythia-6B\", \"ppo_hh_gpt-j\", \"GPT-J-Руg_PPO-6B\", \"GPT-J-Руg_PPO-6B-Dev-V8p4\", \"Dolly_GPT-J-6b\", \"Dolly_Руg-6B\"] {allow-input: true}\n",
|
||||
"Version = \"Official\" #@param [\"Official\", \"United\"] {allow-input: true}\n",
|
||||
"Provider = \"Localtunnel\" #@param [\"Localtunnel\"]\n",
|
||||
"ForceInitSteps = [] #@param {allow-input: true}\n",
|
||||
"UseGoogleDrive = True #@param {type:\"boolean\"}\n",
|
||||
"StartKoboldAI = True #@param {type:\"boolean\"}\n",
|
||||
"ModelsFromDrive = False #@param {type:\"boolean\"}\n",
|
||||
"UseExtrasExtensions = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Enables hosting of extensions backend for SillyTavern Extras\n",
|
||||
"extras_enable_captioning = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Loads the image captioning module\n",
|
||||
@ -102,150 +32,20 @@
|
||||
"#@markdown * joeddav/distilbert-base-uncased-go-emotions-student = 28 supported emotions\n",
|
||||
"extras_enable_memory = True #@param {type:\"boolean\"}\n",
|
||||
"#@markdown Loads the story summarization module\n",
|
||||
"Memory_Model = \"Qiliang/bart-large-cnn-samsum-ChatGPT_v3\" #@param [ \"Qiliang/bart-large-cnn-samsum-ChatGPT_v3\", \"Qiliang/bart-large-cnn-samsum-ElectrifAi_v10\", \"distilbart-xsum-12-3\" ]\n",
|
||||
"Memory_Model = \"slauw87/bart_summarisation\" #@param [ \"slauw87/bart_summarisation\", \"Qiliang/bart-large-cnn-samsum-ChatGPT_v3\", \"Qiliang/bart-large-cnn-samsum-ElectrifAi_v10\", \"distilbart-xsum-12-3\" ]\n",
|
||||
"#@markdown * slauw87/bart_summarisation - general purpose summarization model\n",
|
||||
"#@markdown * Qiliang/bart-large-cnn-samsum-ChatGPT_v3 - summarization model optimized for chats\n",
|
||||
"#@markdown * Qiliang/bart-large-cnn-samsum-ElectrifAi_v10 - nice results so far, but still being evaluated\n",
|
||||
"#@markdown * distilbart-xsum-12-3 - faster, but pretty basic alternative\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"%cd /content\n",
|
||||
"\n",
|
||||
"!cat .ii\n",
|
||||
"!nvidia-smi\n",
|
||||
"\n",
|
||||
"import os, subprocess, time, pathlib, json, base64, sys\n",
|
||||
"\n",
|
||||
"# ---\n",
|
||||
"# Utils\n",
|
||||
"class IncrementialInstall:\n",
|
||||
" def __init__(self, root = \"/\", tasks = [], force = []):\n",
|
||||
" self.tasks = tasks\n",
|
||||
" self.path = os.path.join(root, \".ii\")\n",
|
||||
" self.completed = list(filter(lambda x: not x in force, self.__completed()))\n",
|
||||
"\n",
|
||||
" def __completed(self):\n",
|
||||
" try:\n",
|
||||
" with open(self.path) as f:\n",
|
||||
" return json.load(f)\n",
|
||||
" except:\n",
|
||||
" return []\n",
|
||||
"\n",
|
||||
" def addTask(self, name, func):\n",
|
||||
" self.tasks.append({\"name\": name, \"func\": func})\n",
|
||||
"\n",
|
||||
" def run(self):\n",
|
||||
" todo = list(filter(lambda x: not x[\"name\"] in self.completed, self.tasks))\n",
|
||||
" try:\n",
|
||||
" for task in todo:\n",
|
||||
" task[\"func\"]()\n",
|
||||
" self.completed.append(task[\"name\"])\n",
|
||||
" finally:\n",
|
||||
" with open(self.path, \"w\") as f:\n",
|
||||
" json.dump(self.completed, f)\n",
|
||||
"\n",
|
||||
"def create_paths(paths):\n",
|
||||
" for directory in paths:\n",
|
||||
" if not os.path.exists(directory):\n",
|
||||
" os.makedirs(directory)\n",
|
||||
"\n",
|
||||
"def link(srcDir, destDir, files):\n",
|
||||
" '''\n",
|
||||
" Link source to dest copying dest to source if not present first\n",
|
||||
" '''\n",
|
||||
" for file in files:\n",
|
||||
" source = os.path.join(srcDir, file)\n",
|
||||
" dest = os.path.join(destDir, file)\n",
|
||||
" if not os.path.exists(source):\n",
|
||||
" !cp -r \"$dest\" \"$source\"\n",
|
||||
" !rm -rf \"$dest\"\n",
|
||||
" !ln -fs \"$source\" \"$dest\"\n",
|
||||
"\n",
|
||||
"from google.colab import drive\n",
|
||||
"if UseGoogleDrive:\n",
|
||||
" drive.mount(\"/content/drive/\")\n",
|
||||
"else:\n",
|
||||
" create_paths([\n",
|
||||
" \"/content/drive/MyDrive\"\n",
|
||||
" ])\n",
|
||||
"\n",
|
||||
"ii = IncrementialInstall(force=ForceInitSteps)\n",
|
||||
"\n",
|
||||
"# ---\n",
|
||||
"# SillyTavern py modules\n",
|
||||
"def cloneTavern():\n",
|
||||
" %cd /\n",
|
||||
" !git clone https://github.com/Cohee1207/SillyTavern\n",
|
||||
" %cd -\n",
|
||||
" !cp /SillyTavern/colab/*.py ./\n",
|
||||
"ii.addTask(\"Clone SillyTavern\", cloneTavern)\n",
|
||||
"ii.run()\n",
|
||||
"\n",
|
||||
"from models import GetModels, ModelData\n",
|
||||
"model = GetModels(Version).get(Model, ModelData(Model, Version))\n",
|
||||
"\n",
|
||||
"# ---\n",
|
||||
"# KoboldAI\n",
|
||||
"if StartKoboldAI:\n",
|
||||
" def downloadKobold():\n",
|
||||
" !wget https://koboldai.org/ckds && chmod +x ckds\n",
|
||||
" def initKobold():\n",
|
||||
" !./ckds --init only\n",
|
||||
"\n",
|
||||
" ii.addTask(\"Download KoboldAI\", downloadKobold)\n",
|
||||
" ii.addTask(\"Init KoboldAI\", initKobold)\n",
|
||||
" \n",
|
||||
" ii.run()\n",
|
||||
"\n",
|
||||
"kargs = [\"/content/ckds\"]\n",
|
||||
"if not ModelsFromDrive:\n",
|
||||
" kargs += [\"-x\", \"colab\", \"-l\", \"colab\"]\n",
|
||||
"if Provider == \"Localtunnel\":\n",
|
||||
" kargs += [\"--localtunnel\", \"yes\"]\n",
|
||||
"\n",
|
||||
"kargs += model.args()\n",
|
||||
"\n",
|
||||
"url = \"\"\n",
|
||||
"print(kargs)\n",
|
||||
"\n",
|
||||
"if StartKoboldAI:\n",
|
||||
" p = subprocess.Popen(kargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
|
||||
"\n",
|
||||
" prefix = \"KoboldAI has finished loading and is available at the following link\"\n",
|
||||
" urlprefix = f\"{prefix}: \"\n",
|
||||
" ui1prefix = f\"{prefix} for UI 1: \"\n",
|
||||
" while True:\n",
|
||||
" line = p.stdout.readline().decode().strip()\n",
|
||||
" print(line)\n",
|
||||
" if urlprefix in line:\n",
|
||||
" url = line.split(urlprefix)[1]\n",
|
||||
" break\n",
|
||||
" elif ui1prefix in line:\n",
|
||||
" url = line.split(ui1prefix)[1]\n",
|
||||
" break\n",
|
||||
" elif not line:\n",
|
||||
" break\n",
|
||||
" if \"INIT\" in line and \"Transformers\" in line:\n",
|
||||
" print(\"Model loading... (It will take 2 - 5 minutes)\")\n",
|
||||
"\n",
|
||||
"print(url)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# ---\n",
|
||||
"# nodejs\n",
|
||||
"%cd /\n",
|
||||
"def installNode():\n",
|
||||
" !npm install -g n\n",
|
||||
" !n 19\n",
|
||||
" !node --version\n",
|
||||
"ii.addTask(\"Install node\", installNode)\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"\n",
|
||||
"# ---\n",
|
||||
"# SillyTavern extras\n",
|
||||
"import globals\n",
|
||||
"globals.extras_url = '(disabled)'\n",
|
||||
"globals.params = []\n",
|
||||
"globals.params.append('--cpu')\n",
|
||||
"extras_url = '(disabled)'\n",
|
||||
"params = []\n",
|
||||
"params.append('--cpu')\n",
|
||||
"params.append('--share')\n",
|
||||
"ExtrasModules = []\n",
|
||||
"\n",
|
||||
"if (extras_enable_captioning):\n",
|
||||
@ -255,73 +55,28 @@
|
||||
"if (extras_enable_emotions):\n",
|
||||
" ExtrasModules.append('classify')\n",
|
||||
"\n",
|
||||
"globals.params.append(f'--classification-model={Emotions_Model}')\n",
|
||||
"globals.params.append(f'--summarization-model={Memory_Model}')\n",
|
||||
"globals.params.append(f'--captioning-model={Captions_Model}')\n",
|
||||
"globals.params.append(f'--enable-modules={\",\".join(ExtrasModules)}')\n",
|
||||
"params.append(f'--classification-model={Emotions_Model}')\n",
|
||||
"params.append(f'--summarization-model={Memory_Model}')\n",
|
||||
"params.append(f'--captioning-model={Captions_Model}')\n",
|
||||
"params.append(f'--enable-modules={\",\".join(ExtrasModules)}')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if UseExtrasExtensions:\n",
|
||||
" def cloneExtras():\n",
|
||||
" %cd /\n",
|
||||
" !git clone https://github.com/Cohee1207/SillyTavern-extras\n",
|
||||
" ii.addTask('clone extras', cloneExtras)\n",
|
||||
"%cd /\n",
|
||||
"!git clone https://github.com/Cohee1207/SillyTavern-extras\n",
|
||||
"%cd /SillyTavern-extras\n",
|
||||
"!npm install -g localtunnel\n",
|
||||
"!pip install -r requirements.txt\n",
|
||||
"!pip install tensorflow==2.11\n",
|
||||
"\n",
|
||||
" def installRequirements():\n",
|
||||
" %cd /SillyTavern-extras\n",
|
||||
" !npm install -g localtunnel\n",
|
||||
" !pip install -r requirements.txt\n",
|
||||
" !pip install tensorflow==2.11\n",
|
||||
" ii.addTask('install requirements', installRequirements)\n",
|
||||
"\n",
|
||||
" from extras_server import runServer, extractUrl\n",
|
||||
" ii.addTask('run server', runServer)\n",
|
||||
" ii.addTask('extract extras URL', extractUrl)\n",
|
||||
"\n",
|
||||
"%cd /SillyTavern\n",
|
||||
"\n",
|
||||
"if UseGoogleDrive:\n",
|
||||
" %env googledrive=2\n",
|
||||
"\n",
|
||||
" def setupTavernPaths():\n",
|
||||
" %cd /SillyTavern\n",
|
||||
" tdrive = \"/content/drive/MyDrive/SillyTavern\"\n",
|
||||
" create_paths([\n",
|
||||
" tdrive,\n",
|
||||
" os.path.join(\"public\", \"groups\"),\n",
|
||||
" os.path.join(\"public\", \"group chats\")\n",
|
||||
" ])\n",
|
||||
" link(tdrive, \"public\", [\n",
|
||||
" \"settings.json\",\n",
|
||||
" \"backgrounds\",\n",
|
||||
" \"characters\",\n",
|
||||
" \"chats\",\n",
|
||||
" \"User Avatars\",\n",
|
||||
" \"worlds\",\n",
|
||||
" \"group chats\",\n",
|
||||
" \"groups\",\n",
|
||||
" ])\n",
|
||||
" ii.addTask(\"Setup Tavern Paths\", setupTavernPaths)\n",
|
||||
"\n",
|
||||
"def installTavernDependencies():\n",
|
||||
" %cd /SillyTavern\n",
|
||||
" !npm install\n",
|
||||
" !npm install -g localtunnel\n",
|
||||
" !npm install -g forever\n",
|
||||
" !pip install flask-cloudflared==0.0.10\n",
|
||||
"ii.addTask(\"Install Tavern Dependencies\", installTavernDependencies)\n",
|
||||
"ii.run()\n",
|
||||
"\n",
|
||||
"%env colaburl=$url\n",
|
||||
"%env SILLY_TAVERN_PORT=5001\n",
|
||||
"!sed -i 's/listen = true/listen = false/g' config.conf\n",
|
||||
"!touch stdout.log stderr.log\n",
|
||||
"!forever start -o stdout.log -e stderr.log server.js\n",
|
||||
"print(\"KoboldAI LINK:\", url, '###Extensions API LINK###', globals.extras_url, \"###SillyTavern LINK###\", sep=\"\\n\")\n",
|
||||
"from flask_cloudflared import _run_cloudflared\n",
|
||||
"cloudflare = _run_cloudflared(5001)\n",
|
||||
"print(cloudflare)\n",
|
||||
"!tail -f stdout.log stderr.log"
|
||||
"cmd = f\"python server.py {' '.join(params)}\"\n",
|
||||
"print(cmd)\n",
|
||||
"extras_process = subprocess.Popen(\n",
|
||||
" cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd='/SillyTavern-extras', shell=True)\n",
|
||||
"print('processId:', extras_process.pid)\n",
|
||||
"while True:\n",
|
||||
" line = extras_process.stdout.readline().decode().strip()\n",
|
||||
" print(line)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -1,40 +0,0 @@
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import globals
|
||||
|
||||
def runServer():
|
||||
cmd = f"python server.py {' '.join(globals.params)}"
|
||||
print(cmd)
|
||||
extras_process = subprocess.Popen(
|
||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd='/SillyTavern-extras', shell=True)
|
||||
print('processId:', extras_process.pid)
|
||||
while True:
|
||||
line = extras_process.stdout.readline().decode().strip()
|
||||
if "Running on " in line:
|
||||
break
|
||||
if not line:
|
||||
print('breaking on line')
|
||||
break
|
||||
print(line)
|
||||
|
||||
|
||||
def extractUrl():
|
||||
subprocess.call(
|
||||
'nohup lt --port 5100 > ./extras.out 2> ./extras.err &', shell=True)
|
||||
print('Waiting for lt init...')
|
||||
time.sleep(5)
|
||||
while True:
|
||||
if (os.path.getsize('./extras.out') > 0):
|
||||
with open('./extras.out', 'r') as f:
|
||||
lines = f.readlines()
|
||||
for x in range(len(lines)):
|
||||
if ('your url is: ' in lines[x]):
|
||||
print('SillyTavern Extensions URL:')
|
||||
globals.extras_url = lines[x].split('your url is: ')[1]
|
||||
print(globals.extras_url)
|
||||
break
|
||||
if (os.path.getsize('./extras.err') > 0):
|
||||
with open('./extras.err', 'r') as f:
|
||||
print(f.readlines())
|
||||
break
|
@ -1,2 +0,0 @@
|
||||
extras_url = '(disabled)'
|
||||
params = []
|
@ -1,77 +0,0 @@
|
||||
class ModelData:
|
||||
def __init__(self, name, version = "", revision="", path="", download=""):
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.revision = revision
|
||||
self.path = path
|
||||
self.download = download
|
||||
|
||||
def __str__(self):
|
||||
return self.args().__str__()
|
||||
|
||||
def args(self):
|
||||
args = ["-m", self.name]
|
||||
if (self.version):
|
||||
args += ["-g", self.version]
|
||||
if (self.revision):
|
||||
args += ["-r", self.revision]
|
||||
return args
|
||||
|
||||
|
||||
class ModelFactory:
|
||||
def __init__(self, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
|
||||
def NewModelData(self, name, **kwargs):
|
||||
cpy = self.kwargs.copy()
|
||||
cpy.update(kwargs)
|
||||
return ModelData(name = name, **cpy)
|
||||
|
||||
|
||||
def GetModels(Version):
|
||||
mf = ModelFactory(version=Version)
|
||||
return {
|
||||
"Nerys V2 6B": mf.NewModelData("KoboldAI/OPT-6B-nerys-v2"),
|
||||
"Erebus 6B": mf.NewModelData("KoboldAI/OPT-6.7B-Erebus"),
|
||||
"Skein 6B": mf.NewModelData("KoboldAI/GPT-J-6B-Skein"),
|
||||
"Janeway 6B": mf.NewModelData("KoboldAI/GPT-J-6B-Janeway"),
|
||||
"Adventure 6B": mf.NewModelData("KoboldAI/GPT-J-6B-Adventure"),
|
||||
"Руgmаlіоn 6В": mf.NewModelData("PygmalionAI/pygmalion-6b"),
|
||||
"Руgmаlіоn 6В Dev": mf.NewModelData("PygmalionAI/pygmalion-6b", revision="dev"),
|
||||
"Lit V2 6B": mf.NewModelData("hakurei/litv2-6B-rev3"),
|
||||
"Lit 6B": mf.NewModelData("hakurei/lit-6B"),
|
||||
"Shinen 6B": mf.NewModelData("KoboldAI/GPT-J-6B-Shinen"),
|
||||
"Nerys 2.7B": mf.NewModelData("KoboldAI/fairseq-dense-2.7B-Nerys"),
|
||||
"Erebus 2.7B": mf.NewModelData("KoboldAI/OPT-2.7B-Erebus"),
|
||||
"Janeway 2.7B": mf.NewModelData("KoboldAI/GPT-Neo-2.7B-Janeway"),
|
||||
"Picard 2.7B": mf.NewModelData("KoboldAI/GPT-Neo-2.7B-Picard"),
|
||||
"AID 2.7B": mf.NewModelData("KoboldAI/GPT-Neo-2.7B-AID"),
|
||||
"Horni LN 2.7B": mf.NewModelData("KoboldAI/GPT-Neo-2.7B-Horni-LN"),
|
||||
"Horni 2.7B": mf.NewModelData("KoboldAI/GPT-Neo-2.7B-Horni"),
|
||||
"Shinen 2.7B": mf.NewModelData("KoboldAI/GPT-Neo-2.7B-Shinen"),
|
||||
"Fairseq Dense 2.7B": mf.NewModelData("KoboldAI/fairseq-dense-2.7B"),
|
||||
"OPT 2.7B": mf.NewModelData("facebook/opt-2.7b"),
|
||||
"Neo 2.7B": mf.NewModelData("EleutherAI/gpt-neo-2.7B"),
|
||||
"Руgwау 6B": mf.NewModelData("TehVenom/PPO_Pygway-6b"),
|
||||
"Nerybus 6.7B": mf.NewModelData("KoboldAI/OPT-6.7B-Nerybus-Mix"),
|
||||
"Руgwау v8p4": mf.NewModelData("TehVenom/PPO_Pygway-V8p4_Dev-6b"),
|
||||
"PPO-Janeway 6B": mf.NewModelData("TehVenom/PPO_Janeway-6b"),
|
||||
"PPO Shуgmаlіоn 6B": mf.NewModelData("TehVenom/PPO_Shygmalion-6b"),
|
||||
"LLaMA 7B": mf.NewModelData("decapoda-research/llama-7b-hf"),
|
||||
"Janin-GPTJ": mf.NewModelData("digitous/Janin-GPTJ"),
|
||||
"Javelin-GPTJ": mf.NewModelData("digitous/Javelin-GPTJ"),
|
||||
"Javelin-R": mf.NewModelData("digitous/Javelin-R"),
|
||||
"Janin-R": mf.NewModelData("digitous/Janin-R"),
|
||||
"Javalion-R": mf.NewModelData("digitous/Javalion-R"),
|
||||
"Javalion-GPTJ": mf.NewModelData("digitous/Javalion-GPTJ"),
|
||||
"Javelion-6B": mf.NewModelData("Cohee/Javelion-6b"),
|
||||
"GPT-J-Руg-PPO-6B": mf.NewModelData("TehVenom/GPT-J-Pyg_PPO-6B"),
|
||||
"ppo_hh_pythia-6B": mf.NewModelData("reciprocate/ppo_hh_pythia-6B"),
|
||||
"ppo_hh_gpt-j": mf.NewModelData("reciprocate/ppo_hh_gpt-j"),
|
||||
"Alpaca-7B": mf.NewModelData("chainyo/alpaca-lora-7b"),
|
||||
"LLaMA 4-bit": mf.NewModelData("decapoda-research/llama-13b-hf-int4"),
|
||||
"GPT-J-Руg_PPO-6B": mf.NewModelData("TehVenom/GPT-J-Pyg_PPO-6B"),
|
||||
"GPT-J-Руg_PPO-6B-Dev-V8p4": mf.NewModelData("TehVenom/GPT-J-Pyg_PPO-6B-Dev-V8p4"),
|
||||
"Dolly_GPT-J-6b": mf.NewModelData("TehVenom/Dolly_GPT-J-6b"),
|
||||
"Dolly_Руg-6B": mf.NewModelData("TehVenom/AvgMerge_Dolly-Pygmalion-6b")
|
||||
}
|
@ -59,6 +59,7 @@
|
||||
<script type="module" src="scripts/RossAscends-mods.js"></script>
|
||||
<script type="module" src="scripts/slash-commands.js"></script>
|
||||
<script type="module" src="scripts/tags.js"></script>
|
||||
<script type="module" src="scripts/secrets.js"></script>
|
||||
<script type="text/javascript" src="scripts/toolcool-color-picker.js"></script>
|
||||
|
||||
<title>SillyTavern</title>
|
||||
@ -1006,7 +1007,8 @@
|
||||
<h4>API key</h4>
|
||||
<h5>Get it here: <a target="_blank" href="https://horde.koboldai.net/register">Register</a>
|
||||
</h5>
|
||||
<input id="horde_api_key" name="horde_api_key" class="text_pole" maxlength="500" value="0000000000" autocomplete="off">
|
||||
<input id="horde_api_key" name="horde_api_key" class="text_pole" maxlength="500" type="text" placeholder="0000000000">
|
||||
<div class="neutral_warning">Your API key will removed from here after you click "Connect" for privacy reasons.</div>
|
||||
<h4 class="horde_model_title">
|
||||
Model
|
||||
<div id="horde_refresh" title="Refresh models" class="right_menu_button">
|
||||
@ -1036,7 +1038,8 @@
|
||||
<li>Enter it in the box below:</li>
|
||||
</ol>
|
||||
</span>
|
||||
<input id="api_key_novel" name="api_key_novel" class="text_pole" maxlength="500" size="35" value="" autocomplete="off">
|
||||
<input id="api_key_novel" name="api_key_novel" class="text_pole" maxlength="500" size="35" type="text">
|
||||
<div class="neutral_warning">Your API key will removed from here after you click "Connect" for privacy reasons.</div>
|
||||
<input id="api_button_novel" class="menu_button" type="submit" value="Connect">
|
||||
<div id="api_loading_novel" class="api-load-icon fa-solid fa-hourglass fa-spin"></div>
|
||||
<h4>Novel AI Model
|
||||
@ -1089,7 +1092,8 @@
|
||||
<li>Enter it in the box below:</li>
|
||||
</ol>
|
||||
</span>
|
||||
<input id="api_key_openai" name="api_key_openai" class="text_pole" maxlength="500" value="" autocomplete="off">
|
||||
<input id="api_key_openai" name="api_key_openai" class="text_pole" maxlength="500" value="" type="text">
|
||||
<div class="neutral_warning">Your API key will removed from here after you click "Connect" for privacy reasons.</div>
|
||||
<input id="api_button_openai" class="menu_button" type="submit" value="Connect">
|
||||
<div id="api_loading_openai" class=" api-load-icon fa-solid fa-hourglass fa-spin"></div>
|
||||
</form>
|
||||
@ -1125,8 +1129,8 @@
|
||||
</ol>
|
||||
</span>
|
||||
<div class="widthFreeExpand">
|
||||
<input id="poe_token" class="text_pole" type="text" placeholder="Example: nTLG2bNvbOi8qxc-DbaSlw%3D%3D" maxlength="100" />
|
||||
|
||||
<input id="poe_token" class="text_pole" type="text" maxlength="100" />
|
||||
<div class="neutral_warning">Your API key will removed from here after you click "Connect" for privacy reasons.</div>
|
||||
</div>
|
||||
|
||||
<input id="poe_connect" class="menu_button" type="button" value="Connect" />
|
||||
|
@ -119,6 +119,12 @@ import {
|
||||
createTagMapFromList,
|
||||
renameTagKey,
|
||||
} from "./scripts/tags.js";
|
||||
import {
|
||||
SECRET_KEYS,
|
||||
readSecretState,
|
||||
secret_state,
|
||||
writeSecret
|
||||
} from "./scripts/secrets.js";
|
||||
|
||||
//exporting functions and vars for mods
|
||||
export {
|
||||
@ -543,14 +549,15 @@ $.ajaxPrefilter((options, originalOptions, xhr) => {
|
||||
});
|
||||
|
||||
///// initialization protocol ////////
|
||||
$.get("/csrf-token").then((data) => {
|
||||
$.get("/csrf-token").then(async (data) => {
|
||||
token = data.token;
|
||||
getClientVersion();
|
||||
getCharacters();
|
||||
getSettings("def");
|
||||
await readSecretState();
|
||||
await getClientVersion();
|
||||
await getSettings("def");
|
||||
await getCharacters();
|
||||
await getBackgrounds();
|
||||
await getUserAvatars();
|
||||
sendSystemMessage(system_message_types.WELCOME);
|
||||
getBackgrounds();
|
||||
getUserAvatars();
|
||||
});
|
||||
|
||||
function checkOnlineStatus() {
|
||||
@ -3490,7 +3497,7 @@ async function displayPastChats() {
|
||||
//************************************************************
|
||||
async function getStatusNovel() {
|
||||
if (is_get_status_novel) {
|
||||
const data = { key: nai_settings.api_key_novel };
|
||||
const data = {};
|
||||
|
||||
jQuery.ajax({
|
||||
type: "POST", //
|
||||
@ -5542,17 +5549,25 @@ $(document).ready(function () {
|
||||
});
|
||||
//Select chat
|
||||
|
||||
$("#api_button_novel").click(function (e) {
|
||||
$("#api_button_novel").on('click', async function (e) {
|
||||
e.stopPropagation();
|
||||
if ($("#api_key_novel").val() != "") {
|
||||
$("#api_loading_novel").css("display", "inline-block");
|
||||
$("#api_button_novel").css("display", "none");
|
||||
nai_settings.api_key_novel = $.trim($("#api_key_novel").val());
|
||||
saveSettingsDebounced();
|
||||
is_get_status_novel = true;
|
||||
is_api_button_press_novel = true;
|
||||
const api_key_novel = $("#api_key_novel").val().trim();
|
||||
|
||||
if (api_key_novel.length) {
|
||||
await writeSecret(SECRET_KEYS.NOVEL, api_key_novel);
|
||||
}
|
||||
|
||||
if (!secret_state[SECRET_KEYS.NOVEL]) {
|
||||
console.log('No secret key saved for NovelAI');
|
||||
return;
|
||||
}
|
||||
|
||||
$("#api_loading_novel").css("display", "inline-block");
|
||||
$("#api_button_novel").css("display", "none");
|
||||
is_get_status_novel = true;
|
||||
is_api_button_press_novel = true;
|
||||
});
|
||||
|
||||
$("#anchor_order").change(function () {
|
||||
anchor_order = parseInt($("#anchor_order").find(":selected").val());
|
||||
saveSettingsDebounced();
|
||||
|
@ -7,21 +7,14 @@ import {
|
||||
online_status,
|
||||
main_api,
|
||||
api_server,
|
||||
nai_settings,
|
||||
api_server_textgenerationwebui,
|
||||
is_send_press,
|
||||
getTokenCount,
|
||||
menu_type,
|
||||
selectRightMenuWithAnimation,
|
||||
select_selected_character,
|
||||
setCharacterId,
|
||||
|
||||
|
||||
} from "../script.js";
|
||||
|
||||
import {
|
||||
select_group_chats,
|
||||
} from "./group-chats.js";
|
||||
|
||||
import {
|
||||
power_user,
|
||||
@ -30,8 +23,10 @@ import {
|
||||
|
||||
import { LoadLocal, SaveLocal, ClearLocal, CheckLocal, LoadLocalBool } from "./f-localStorage.js";
|
||||
import { selected_group, is_group_generating, getGroupAvatar, groups } from "./group-chats.js";
|
||||
import { oai_settings } from "./openai.js";
|
||||
import { poe_settings } from "./poe.js";
|
||||
import {
|
||||
SECRET_KEYS,
|
||||
secret_state,
|
||||
} from "./secrets.js";
|
||||
|
||||
var NavToggle = document.getElementById("nav-toggle");
|
||||
var RPanelPin = document.getElementById("rm_button_panel_pin");
|
||||
@ -368,13 +363,11 @@ function RA_autoconnect(PrevApi) {
|
||||
case 'kobold':
|
||||
if (api_server && isUrlOrAPIKey(api_server)) {
|
||||
$("#api_button").click();
|
||||
|
||||
}
|
||||
break;
|
||||
case 'novel':
|
||||
if (nai_settings.api_key_novel) {
|
||||
if (secret_state[SECRET_KEYS.NOVEL]) {
|
||||
$("#api_button_novel").click();
|
||||
|
||||
}
|
||||
break;
|
||||
case 'textgenerationwebui':
|
||||
@ -383,12 +376,12 @@ function RA_autoconnect(PrevApi) {
|
||||
}
|
||||
break;
|
||||
case 'openai':
|
||||
if (oai_settings.api_key_openai) {
|
||||
if (secret_state[SECRET_KEYS.OPENAI]) {
|
||||
$("#api_button_openai").click();
|
||||
}
|
||||
break;
|
||||
case 'poe':
|
||||
if (poe_settings.token) {
|
||||
if (secret_state[SECRET_KEYS.POE]) {
|
||||
$("#poe_connect").click();
|
||||
}
|
||||
break;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { saveSettingsDebounced, changeMainAPI, callPopup, setGenerationProgress, CLIENT_VERSION } from "../script.js";
|
||||
import { saveSettingsDebounced, changeMainAPI, callPopup, setGenerationProgress, CLIENT_VERSION, getRequestHeaders } from "../script.js";
|
||||
import { delay } from "./utils.js";
|
||||
|
||||
export {
|
||||
@ -14,7 +14,6 @@ export {
|
||||
let models = [];
|
||||
|
||||
let horde_settings = {
|
||||
api_key: '0000000000',
|
||||
models: [],
|
||||
use_horde: false,
|
||||
auto_adjust_response_length: true,
|
||||
@ -30,14 +29,6 @@ const getRequestArgs = () => ({
|
||||
"Client-Agent": CLIENT_VERSION,
|
||||
}
|
||||
});
|
||||
const postRequestArgs = () => ({
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"apikey": horde_settings.api_key,
|
||||
"Client-Agent": CLIENT_VERSION,
|
||||
}
|
||||
});
|
||||
|
||||
async function getWorkers() {
|
||||
const response = await fetch('https://horde.koboldai.net/api/v2/workers?type=text', getRequestArgs());
|
||||
@ -107,8 +98,12 @@ async function generateHorde(prompt, params) {
|
||||
"models": horde_settings.models,
|
||||
};
|
||||
|
||||
const response = await fetch("https://horde.koboldai.net/api/v2/generate/text/async", {
|
||||
...postRequestArgs(),
|
||||
const response = await fetch("/generate_horde", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...getRequestHeaders(),
|
||||
"Client-Agent": CLIENT_VERSION,
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
@ -176,12 +171,6 @@ async function getHordeModels() {
|
||||
if (horde_settings.models.length && models.filter(m => horde_settings.models.includes(m.name)).length === 0) {
|
||||
horde_settings.models = [];
|
||||
}
|
||||
|
||||
// if no models preselected - select a first one in dropdown
|
||||
/*if (Array.isArray(horde_settings.models) || horde_settings.models.length == 0) {
|
||||
$('#horde_model').first()
|
||||
horde_settings.models = [.find(":selected").val()];
|
||||
}*/
|
||||
}
|
||||
|
||||
function loadHordeSettings(settings) {
|
||||
@ -190,7 +179,6 @@ function loadHordeSettings(settings) {
|
||||
}
|
||||
|
||||
$('#use_horde').prop("checked", horde_settings.use_horde).trigger('input');
|
||||
$('#horde_api_key').val(horde_settings.api_key);
|
||||
$('#horde_auto_adjust_response_length').prop("checked", horde_settings.auto_adjust_response_length);
|
||||
$('#horde_auto_adjust_context_length').prop("checked", horde_settings.auto_adjust_context_length);
|
||||
}
|
||||
@ -219,11 +207,6 @@ jQuery(function () {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#horde_api_key").on("input", function () {
|
||||
horde_settings.api_key = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#horde_auto_adjust_response_length").on("input", function () {
|
||||
horde_settings.auto_adjust_response_length = !!$(this).prop("checked");
|
||||
saveSettingsDebounced();
|
||||
|
@ -14,7 +14,6 @@ const nai_settings = {
|
||||
rep_pen_novel: 1,
|
||||
rep_pen_size_novel: 100,
|
||||
model_novel: "euterpe-v2",
|
||||
api_key_novel: "",
|
||||
preset_settings_novel: "Classic-Euterpe",
|
||||
};
|
||||
|
||||
@ -44,12 +43,6 @@ function loadNovelPreset(preset) {
|
||||
}
|
||||
|
||||
function loadNovelSettings(settings) {
|
||||
//load Novel API KEY is exists
|
||||
if (settings.api_key_novel != undefined) {
|
||||
nai_settings.api_key_novel = settings.api_key_novel;
|
||||
$("#api_key_novel").val(nai_settings.api_key_novel);
|
||||
}
|
||||
|
||||
//load the rest of the Novel settings without any checks
|
||||
nai_settings.model_novel = settings.model_novel;
|
||||
$(`#model_novel_select option[value=${nai_settings.model_novel}]`).attr("selected", true);
|
||||
|
@ -23,6 +23,11 @@ import { groups, selected_group } from "./group-chats.js";
|
||||
import {
|
||||
power_user,
|
||||
} from "./power-user.js";
|
||||
import {
|
||||
SECRET_KEYS,
|
||||
secret_state,
|
||||
writeSecret,
|
||||
} from "./secrets.js";
|
||||
|
||||
import {
|
||||
delay,
|
||||
@ -76,7 +81,6 @@ const tokenCache = {};
|
||||
|
||||
const default_settings = {
|
||||
preset_settings_openai: 'Default',
|
||||
api_key_openai: '',
|
||||
temp_openai: 0.9,
|
||||
freq_pen_openai: 0.7,
|
||||
pres_pen_openai: 0.7,
|
||||
@ -101,7 +105,6 @@ const default_settings = {
|
||||
|
||||
const oai_settings = {
|
||||
preset_settings_openai: 'Default',
|
||||
api_key_openai: '',
|
||||
temp_openai: 1.0,
|
||||
freq_pen_openai: 0,
|
||||
pres_pen_openai: 0,
|
||||
@ -666,11 +669,6 @@ function countTokens(messages, full = false) {
|
||||
}
|
||||
|
||||
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;
|
||||
@ -766,7 +764,6 @@ async function getStatusOpen() {
|
||||
if (is_get_status_openai) {
|
||||
|
||||
let data = {
|
||||
key: oai_settings.api_key_openai,
|
||||
reverse_proxy: oai_settings.reverse_proxy,
|
||||
};
|
||||
|
||||
@ -873,13 +870,10 @@ async function saveOpenAIPreset(name, settings) {
|
||||
}
|
||||
|
||||
async function showApiKeyUsage() {
|
||||
const body = JSON.stringify({ key: oai_settings.api_key_openai });
|
||||
|
||||
try {
|
||||
const response = await fetch('/openai_usage', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: body,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
@ -1168,15 +1162,23 @@ function onReverseProxyInput() {
|
||||
|
||||
async function onConnectButtonClick(e) {
|
||||
e.stopPropagation();
|
||||
if ($('#api_key_openai').val() != '') {
|
||||
$("#api_loading_openai").css("display", 'inline-block');
|
||||
$("#api_button_openai").css("display", 'none');
|
||||
oai_settings.api_key_openai = $('#api_key_openai').val().trim();
|
||||
saveSettingsDebounced();
|
||||
is_get_status_openai = true;
|
||||
is_api_button_press_openai = true;
|
||||
await getStatusOpen();
|
||||
const api_key_openai = $('#api_key_openai').val().trim();
|
||||
|
||||
if (api_key_openai.length) {
|
||||
await writeSecret(SECRET_KEYS.OPENAI, api_key_openai);
|
||||
}
|
||||
|
||||
if (!secret_state[SECRET_KEYS.OPENAI]) {
|
||||
console.log('No secret key saved for OpenAI');
|
||||
return;
|
||||
}
|
||||
|
||||
$("#api_loading_openai").css("display", 'inline-block');
|
||||
$("#api_button_openai").css("display", 'none');
|
||||
saveSettingsDebounced();
|
||||
is_get_status_openai = true;
|
||||
is_api_button_press_openai = true;
|
||||
await getStatusOpen();
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
|
@ -7,6 +7,11 @@ import {
|
||||
getTokenCount,
|
||||
getRequestHeaders,
|
||||
} from "../script.js";
|
||||
import {
|
||||
SECRET_KEYS,
|
||||
secret_state,
|
||||
writeSecret,
|
||||
} from "./secrets.js";
|
||||
|
||||
export {
|
||||
is_get_status_poe,
|
||||
@ -38,7 +43,6 @@ const DEFAULT_CHARACTER_NUDGE_MESSAGE = "[Your the next response shall only be w
|
||||
const DEFAULT_IMPERSONATION_PROMPT = "[Write 1 reply only in internet RP style from the point of view of {{user}}, using the chat history so far as a guideline for the writing style of {{user}}. Don't write as {{char}} or system.]";
|
||||
|
||||
const poe_settings = {
|
||||
token: '',
|
||||
bot: 'a2',
|
||||
jailbreak_response: DEFAULT_JAILBREAK_RESPONSE,
|
||||
jailbreak_message: DEFAULT_JAILBREAK_MESSAGE,
|
||||
@ -67,7 +71,6 @@ function loadPoeSettings(settings) {
|
||||
$('#poe_auto_jailbreak').prop('checked', poe_settings.auto_jailbreak);
|
||||
$('#poe_auto_purge').prop('checked', poe_settings.auto_purge);
|
||||
$('#poe_streaming').prop('checked', poe_settings.streaming);
|
||||
$('#poe_token').val(poe_settings.token ?? '');
|
||||
$('#poe_impersonation_prompt').val(poe_settings.impersonation_prompt);
|
||||
selectBot();
|
||||
}
|
||||
@ -78,11 +81,6 @@ function selectBot() {
|
||||
}
|
||||
}
|
||||
|
||||
function onTokenInput() {
|
||||
poe_settings.token = $('#poe_token').val();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onBotChange() {
|
||||
poe_settings.bot = $('#poe_bots').find(":selected").val();
|
||||
saveSettingsDebounced();
|
||||
@ -147,7 +145,6 @@ async function generatePoe(type, finalPrompt, signal) {
|
||||
async function purgeConversation(count = -1) {
|
||||
const body = JSON.stringify({
|
||||
bot: poe_settings.bot,
|
||||
token: poe_settings.token,
|
||||
count,
|
||||
});
|
||||
|
||||
@ -167,7 +164,6 @@ async function sendMessage(prompt, withStreaming, signal) {
|
||||
|
||||
const body = JSON.stringify({
|
||||
bot: poe_settings.bot,
|
||||
token: poe_settings.token,
|
||||
streaming: withStreaming && poe_settings.streaming,
|
||||
prompt,
|
||||
});
|
||||
@ -213,7 +209,19 @@ async function sendMessage(prompt, withStreaming, signal) {
|
||||
}
|
||||
|
||||
async function onConnectClick() {
|
||||
if (!poe_settings.token || is_poe_button_press) {
|
||||
const api_key_poe = $('#poe_token').val().trim();
|
||||
|
||||
if (api_key_poe.length) {
|
||||
await writeSecret(SECRET_KEYS.POE, api_key_poe);
|
||||
}
|
||||
|
||||
if (!secret_state[SECRET_KEYS.POE]) {
|
||||
console.error('No secret key saved for Poe');
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_poe_button_press) {
|
||||
console.log('Poe API button is pressed');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -236,7 +244,7 @@ function setButtonState(value) {
|
||||
}
|
||||
|
||||
async function checkStatusPoe() {
|
||||
const body = JSON.stringify({ token: poe_settings.token });
|
||||
const body = JSON.stringify();
|
||||
const response = await fetch('/status_poe', {
|
||||
headers: getRequestHeaders(),
|
||||
body: body,
|
||||
@ -336,7 +344,6 @@ function onMessageRestoreClick() {
|
||||
}
|
||||
|
||||
$('document').ready(function () {
|
||||
$('#poe_token').on('input', onTokenInput);
|
||||
$('#poe_bots').on('change', onBotChange);
|
||||
$('#poe_connect').on('click', onConnectClick);
|
||||
$('#poe_activation_response').on('input', onResponseInput);
|
||||
|
62
public/scripts/secrets.js
Normal file
62
public/scripts/secrets.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { getRequestHeaders } from "../script.js";
|
||||
|
||||
export const SECRET_KEYS = {
|
||||
HORDE: 'api_key_horde',
|
||||
OPENAI: 'api_key_openai',
|
||||
POE: 'api_key_poe',
|
||||
NOVEL: 'api_key_novel',
|
||||
}
|
||||
|
||||
const INPUT_MAP = {
|
||||
[SECRET_KEYS.HORDE]: '#horde_api_key',
|
||||
[SECRET_KEYS.OPENAI]: '#api_key_openai',
|
||||
[SECRET_KEYS.POE]: '#poe_token',
|
||||
[SECRET_KEYS.NOVEL]: '#api_key_novel',
|
||||
}
|
||||
|
||||
function updateSecretDisplay() {
|
||||
for (const [secret_key, input_selector] of Object.entries(INPUT_MAP)) {
|
||||
const validSecret = !!secret_state[secret_key];
|
||||
const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key';
|
||||
$(input_selector).attr('placeholder', placeholder).val('');
|
||||
}
|
||||
}
|
||||
|
||||
export let secret_state = {};
|
||||
|
||||
export async function writeSecret(key, value) {
|
||||
try {
|
||||
const response = await fetch('/writesecret', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ key, value }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const text = await response.text();
|
||||
|
||||
if (text == 'ok') {
|
||||
secret_state[key] = true;
|
||||
updateSecretDisplay();
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.error('Could not write secret value: ', key);
|
||||
}
|
||||
}
|
||||
|
||||
export async function readSecretState() {
|
||||
try {
|
||||
const response = await fetch('/readsecretstate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
secret_state = await response.json();
|
||||
updateSecretDisplay();
|
||||
}
|
||||
} catch {
|
||||
console.error('Could not read secrets file');
|
||||
}
|
||||
}
|
12
readme.md
12
readme.md
@ -15,18 +15,6 @@ On its own Tavern is useless, as it's just a user interface. You have to have ac
|
||||
### Do I need a powerful PC to run Tavern?
|
||||
Since Tavern is only a user interface, it has tiny hardware requirements, it will run on anything. It's the AI system backend that needs to be powerful.
|
||||
|
||||
### I want to try self-hosted easily. Got a Google Colab?
|
||||
|
||||
Try on Colab (runs KoboldAI backend and TavernAI Extras server alongside): <a target="_blank" href="https://colab.research.google.com/github/Cohee1207/SillyTavern/blob/main/colab/GPU.ipynb">
|
||||
<img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/>
|
||||
</a>
|
||||
|
||||
https://colab.research.google.com/github/Cohee1207/SillyTavern/blob/main/colab/GPU.ipynb
|
||||
|
||||
Run on Repl.it:
|
||||
[](https://replit.com/new/github/Cohee1207/SillyTavern)
|
||||
|
||||
|
||||
## Mobile support
|
||||
|
||||
> **Note**
|
||||
|
199
server.js
199
server.js
@ -109,11 +109,9 @@ var response_dw_bg;
|
||||
var response_getstatus;
|
||||
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.
|
||||
@ -1384,7 +1382,12 @@ function getImages(path) {
|
||||
app.post("/getstatus_novelai", jsonParser, function (request, response_getstatus_novel = response) {
|
||||
|
||||
if (!request.body) return response_getstatus_novel.sendStatus(400);
|
||||
api_key_novel = request.body.key;
|
||||
const api_key_novel = readSecret(SECRET_KEYS.NOVEL);
|
||||
|
||||
if (!api_key_novel) {
|
||||
return response_generate_novel.sendStatus(401);
|
||||
}
|
||||
|
||||
var data = {};
|
||||
var args = {
|
||||
data: data,
|
||||
@ -1414,6 +1417,12 @@ app.post("/getstatus_novelai", jsonParser, function (request, response_getstatus
|
||||
app.post("/generate_novelai", jsonParser, function (request, response_generate_novel = response) {
|
||||
if (!request.body) return response_generate_novel.sendStatus(400);
|
||||
|
||||
const api_key_novel = readSecret(SECRET_KEYS.NOVEL);
|
||||
|
||||
if (!api_key_novel) {
|
||||
return response_generate_novel.sendStatus(401);
|
||||
}
|
||||
|
||||
console.log(request.body);
|
||||
var data = {
|
||||
"input": request.body.input,
|
||||
@ -2066,12 +2075,14 @@ async function getPoeClient(token, useCache = false) {
|
||||
}
|
||||
|
||||
app.post('/status_poe', jsonParser, async (request, response) => {
|
||||
if (!request.body.token) {
|
||||
return response.sendStatus(400);
|
||||
const token = readSecret(SECRET_KEYS.POE);
|
||||
|
||||
if (!token) {
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
|
||||
try {
|
||||
const client = await getPoeClient(request.body.token);
|
||||
const client = await getPoeClient(token);
|
||||
const botNames = client.get_bot_names();
|
||||
client.disconnect_ws();
|
||||
|
||||
@ -2084,11 +2095,12 @@ app.post('/status_poe', jsonParser, async (request, response) => {
|
||||
});
|
||||
|
||||
app.post('/purge_poe', jsonParser, async (request, response) => {
|
||||
if (!request.body.token) {
|
||||
return response.sendStatus(400);
|
||||
const token = readSecret(SECRET_KEYS.POE);
|
||||
|
||||
if (!token) {
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
|
||||
const token = request.body.token;
|
||||
const bot = request.body.bot ?? POE_DEFAULT_BOT;
|
||||
const count = request.body.count ?? -1;
|
||||
|
||||
@ -2106,11 +2118,16 @@ app.post('/purge_poe', jsonParser, async (request, response) => {
|
||||
});
|
||||
|
||||
app.post('/generate_poe', jsonParser, async (request, response) => {
|
||||
if (!request.body.token || !request.body.prompt) {
|
||||
if (!request.body.prompt) {
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
const token = request.body.token;
|
||||
const token = readSecret(SECRET_KEYS.POE);
|
||||
|
||||
if (!token) {
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
|
||||
const prompt = request.body.prompt;
|
||||
const bot = request.body.bot ?? POE_DEFAULT_BOT;
|
||||
const streaming = request.body.streaming ?? false;
|
||||
@ -2352,7 +2369,13 @@ app.get('/thumbnail', jsonParser, async function (request, response) {
|
||||
/* 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 api_key_openai = readSecret(SECRET_KEYS.OPENAI);
|
||||
|
||||
if (!api_key_openai) {
|
||||
return response_getstatus_openai.sendStatus(401);
|
||||
}
|
||||
|
||||
const api_url = new URL(request.body.reverse_proxy || api_openai).toString();
|
||||
const args = {
|
||||
headers: { "Authorization": "Bearer " + api_key_openai }
|
||||
@ -2455,6 +2478,12 @@ app.post("/generate_openai", jsonParser, function (request, response_generate_op
|
||||
if (!request.body) return response_generate_openai.sendStatus(400);
|
||||
const api_url = new URL(request.body.reverse_proxy || api_openai).toString();
|
||||
|
||||
const api_key_openai = readSecret(SECRET_KEYS.OPENAI);
|
||||
|
||||
if (!api_key_openai) {
|
||||
return response_generate_openai.sendStatus(401);
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
request.socket.removeAllListeners('close');
|
||||
request.socket.on('close', function () {
|
||||
@ -2658,6 +2687,7 @@ const autorunUrl = new URL(
|
||||
);
|
||||
|
||||
const setupTasks = async function () {
|
||||
migrateSecrets();
|
||||
ensurePublicDirectoriesExist();
|
||||
await ensureThumbnailCache();
|
||||
|
||||
@ -2670,10 +2700,11 @@ const setupTasks = async function () {
|
||||
|
||||
if (autorun) open(autorunUrl.toString());
|
||||
console.log('SillyTavern is listening on: ' + tavernUrl);
|
||||
if (listen &&
|
||||
!config.whitelistMode &&
|
||||
!config.basicAuthMode)
|
||||
console.log('Your SillyTavern is currently open to the public. To increase security, consider enabling whitelisting or basic authentication.')
|
||||
}
|
||||
|
||||
if (listen && !config.whitelistMode && !config.basicAuthMode) {
|
||||
console.error('Your SillyTavern is currently unsecurely open to the public. Enable whitelisting or basic authentication.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (true === cliArguments.ssl)
|
||||
@ -2742,3 +2773,139 @@ function ensurePublicDirectoriesExist() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SECRETS_FILE = './secrets.json';
|
||||
const SETTINGS_FILE = './public/settings.json';
|
||||
const SECRET_KEYS = {
|
||||
HORDE: 'api_key_horde',
|
||||
OPENAI: 'api_key_openai',
|
||||
POE: 'api_key_poe',
|
||||
NOVEL: 'api_key_novel',
|
||||
}
|
||||
|
||||
function migrateSecrets() {
|
||||
if (!fs.existsSync(SETTINGS_FILE)) {
|
||||
console.log('Settings file does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let modified = false;
|
||||
const fileContents = fs.readFileSync(SETTINGS_FILE);
|
||||
const settings = JSON.parse(fileContents);
|
||||
const oaiKey = settings?.api_key_openai;
|
||||
const hordeKey = settings?.horde_settings?.api_key;
|
||||
const poeKey = settings?.poe_settings?.token;
|
||||
const novelKey = settings?.api_key_novel;
|
||||
|
||||
if (typeof oaiKey === 'string') {
|
||||
console.log('Migrating OpenAI key...');
|
||||
writeSecret(SECRET_KEYS.OPENAI, oaiKey);
|
||||
delete settings.api_key_openai;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (typeof hordeKey === 'string') {
|
||||
console.log('Migrating Horde key...');
|
||||
writeSecret(SECRET_KEYS.HORDE, hordeKey);
|
||||
delete settings.hordeKey;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (typeof poeKey === 'string') {
|
||||
console.log('Migrating Poe key...');
|
||||
writeSecret(SECRET_KEYS.POE, poeKey);
|
||||
delete settings.poe_settings.token;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (typeof novelKey === 'string') {
|
||||
console.log('Migrating Novel key...');
|
||||
writeSecret(SECRET_KEYS.NOVEL, novelKey);
|
||||
delete settings.api_key_novel;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
console.log('Writing updated settings.json...');
|
||||
const settingsContent = JSON.stringify(settings);
|
||||
fs.writeFileSync(SETTINGS_FILE, settingsContent, "utf-8");
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Could not migrate secrets file. Proceed with caution.');
|
||||
}
|
||||
}
|
||||
|
||||
app.post('/writesecret', jsonParser, (request, response) => {
|
||||
const key = request.body.key;
|
||||
const value = request.body.value;
|
||||
|
||||
writeSecret(key,value);
|
||||
return response.send('ok');
|
||||
});
|
||||
|
||||
app.post('/readsecretstate', jsonParser, (_, response) => {
|
||||
if (!fs.existsSync(SECRETS_FILE)) {
|
||||
return response.send({});
|
||||
}
|
||||
|
||||
try {
|
||||
const fileContents = fs.readFileSync(SECRETS_FILE);
|
||||
const secrets = JSON.parse(fileContents);
|
||||
const state = {};
|
||||
|
||||
for (const key of Object.values(SECRET_KEYS)) {
|
||||
state[key] = !!secrets[key]; // convert to boolean
|
||||
}
|
||||
|
||||
return response.send(state);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.send({});
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/generate_horde', jsonParser, async (request, response) => {
|
||||
const ANONYMOUS_KEY = "0000000000";
|
||||
const api_key_horde = readSecret(SECRET_KEYS.HORDE) || ANONYMOUS_KEY;
|
||||
const url = 'https://horde.koboldai.net/api/v2/generate/text/async';
|
||||
|
||||
const args = {
|
||||
data: request.body,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Client-Agent": request.header('Client-Agent'),
|
||||
"apikey": api_key_horde,
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const data = await postAsync(url, args);
|
||||
return response.send(data);
|
||||
} catch {
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
function writeSecret(key, value) {
|
||||
if (!fs.existsSync(SECRETS_FILE)) {
|
||||
const emptyFile = JSON.stringify({});
|
||||
fs.writeFileSync(SECRETS_FILE, emptyFile, "utf-8");
|
||||
}
|
||||
|
||||
const fileContents = fs.readFileSync(SECRETS_FILE);
|
||||
const secrets = JSON.parse(fileContents);
|
||||
secrets[key] = value;
|
||||
fs.writeFileSync(SECRETS_FILE, JSON.stringify(secrets), "utf-8");
|
||||
}
|
||||
|
||||
function readSecret(key) {
|
||||
if (!fs.existsSync(SECRETS_FILE)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fileContents = fs.readFileSync(SECRETS_FILE);
|
||||
const secrets = JSON.parse(fileContents);
|
||||
return secrets[key];
|
||||
}
|
Reference in New Issue
Block a user