2021-05-03 00:46:45 +02:00
#==================================================================#
# KoboldAI Client
2021-05-16 20:53:19 +02:00
# Version: 1.14.0
2021-05-03 00:46:45 +02:00
# By: KoboldAIDev
#==================================================================#
2021-05-11 01:17:10 +02:00
# External packages
2021-05-03 00:46:45 +02:00
from os import path , getcwd
2021-05-05 17:18:24 +02:00
import tkinter as tk
2021-05-07 20:32:10 +02:00
from tkinter import messagebox
2021-05-03 00:46:45 +02:00
import json
2021-05-16 11:37:38 +02:00
import requests
2021-05-03 00:46:45 +02:00
2021-05-11 01:17:10 +02:00
# KoboldAI
2021-05-07 20:32:10 +02:00
import fileops
import gensettings
from utils import debounce
2021-05-11 01:17:10 +02:00
import utils
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
#==================================================================#
# Variables & Storage
#==================================================================#
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
# Terminal tags for colored text
class colors :
2021-05-07 20:32:10 +02:00
PURPLE = ' \033 [95m '
BLUE = ' \033 [94m '
CYAN = ' \033 [96m '
GREEN = ' \033 [92m '
YELLOW = ' \033 [93m '
RED = ' \033 [91m '
END = ' \033 [0m '
2021-05-03 00:46:45 +02:00
UNDERLINE = ' \033 [4m '
2021-05-07 20:32:10 +02:00
# AI models
2021-05-03 00:46:45 +02:00
modellist = [
2021-05-03 21:19:03 +02:00
[ " GPT Neo 1.3B " , " EleutherAI/gpt-neo-1.3B " , " 8GB " ] ,
[ " GPT Neo 2.7B " , " EleutherAI/gpt-neo-2.7B " , " 16GB " ] ,
[ " GPT-2 " , " gpt2 " , " 1.2GB " ] ,
[ " GPT-2 Med " , " gpt2-medium " , " 2GB " ] ,
[ " GPT-2 Large " , " gpt2-large " , " 16GB " ] ,
2021-05-04 07:47:23 +02:00
[ " GPT-2 XL " , " gpt2-xl " , " 16GB " ] ,
2021-05-07 20:32:10 +02:00
[ " InferKit API (requires API key) " , " InferKit " , " " ] ,
2021-05-04 07:47:23 +02:00
[ " Custom Neo (eg Neo-horni) " , " NeoCustom " , " " ] ,
2021-05-14 00:58:52 +02:00
[ " Custom GPT-2 (eg CloverEdition) " , " GPT2Custom " , " " ] ,
[ " Google Colab " , " Colab " , " " ]
2021-05-03 00:46:45 +02:00
]
# Variables
class vars :
2021-05-07 20:32:10 +02:00
lastact = " " # The last action submitted to the generator
2021-05-05 09:04:06 +02:00
model = " "
2021-05-03 00:46:45 +02:00
noai = False # Runs the script without starting up the transformers pipeline
aibusy = False # Stops submissions while the AI is working
2021-05-08 01:04:51 +02:00
max_length = 512 # Maximum number of tokens to submit per action
2021-05-07 20:32:10 +02:00
ikmax = 3000 # Maximum number of characters to submit to InferKit
genamt = 60 # Amount of text for each action to generate
ikgen = 200 # Number of characters for InferKit to generate
rep_pen = 1.0 # Default generator repetition_penalty
temp = 1.0 # Default generator temperature
top_p = 1.0 # Default generator top_p
2021-05-03 00:46:45 +02:00
gamestarted = False
prompt = " "
memory = " "
2021-05-05 09:04:06 +02:00
authornote = " "
2021-05-07 20:32:10 +02:00
andepth = 3 # How far back in history to append author's note
2021-05-03 00:46:45 +02:00
actions = [ ]
2021-05-13 07:26:42 +02:00
worldinfo = [ ]
deletewi = - 1 # Temporary storage for index to delete
2021-05-03 00:46:45 +02:00
mode = " play " # Whether the interface is in play, memory, or edit mode
2021-05-05 09:04:06 +02:00
editln = 0 # Which line was last selected in Edit Mode
2021-05-03 00:46:45 +02:00
url = " https://api.inferkit.com/v1/models/standard/generate " # InferKit API URL
2021-05-14 00:58:52 +02:00
colaburl = " " # Ngrok url for Google Colab mode
2021-05-04 07:47:23 +02:00
apikey = " " # API key to use for InferKit API calls
2021-05-05 17:18:24 +02:00
savedir = getcwd ( ) + " \ stories "
2021-05-04 07:47:23 +02:00
hascuda = False # Whether torch has detected CUDA on the system
usegpu = False # Whether to launch pipeline with GPU support
custmodpth = " " # Filesystem location of custom model to run
2021-05-11 01:17:10 +02:00
formatoptns = { } # Container for state of formatting options
2021-05-11 06:27:34 +02:00
importnum = - 1 # Selection on import popup list
importjs = { } # Temporary storage for import data
2021-05-04 07:47:23 +02:00
#==================================================================#
# Function to get model selection at startup
#==================================================================#
def getModelSelection ( ) :
2021-05-16 01:29:41 +02:00
print ( " # Model V/RAM \n ========================================= " )
2021-05-04 07:47:23 +02:00
i = 1
for m in modellist :
2021-05-16 01:29:41 +02:00
print ( " {0} - {1} \t \t {2} " . format ( " {:<2} " . format ( i ) , m [ 0 ] . ljust ( 15 ) , m [ 2 ] ) )
2021-05-04 07:47:23 +02:00
i + = 1
print ( " " ) ;
modelsel = 0
vars . model = ' '
while ( vars . model == ' ' ) :
modelsel = input ( " Model #> " )
if ( modelsel . isnumeric ( ) and int ( modelsel ) > 0 and int ( modelsel ) < = len ( modellist ) ) :
vars . model = modellist [ int ( modelsel ) - 1 ] [ 1 ]
else :
2021-05-07 20:32:10 +02:00
print ( " {0} Please enter a valid selection. {1} " . format ( colors . RED , colors . END ) )
2021-05-04 07:47:23 +02:00
# If custom model was selected, get the filesystem location and store it
if ( vars . model == " NeoCustom " or vars . model == " GPT2Custom " ) :
2021-05-07 20:32:10 +02:00
print ( " {0} Please choose the folder where pytorch_model.bin is located: {1} \n " . format ( colors . CYAN , colors . END ) )
2021-05-05 17:18:24 +02:00
2021-05-07 20:32:10 +02:00
modpath = fileops . getdirpath ( getcwd ( ) , " Select Model Folder " )
2021-05-05 17:18:24 +02:00
2021-05-07 20:32:10 +02:00
if ( modpath ) :
2021-05-04 07:47:23 +02:00
# Save directory to vars
2021-05-07 20:32:10 +02:00
vars . custmodpth = modpath
2021-05-04 07:47:23 +02:00
else :
# Print error and retry model selection
2021-05-07 20:32:10 +02:00
print ( " {0} Model select cancelled! {1} " . format ( colors . RED , colors . END ) )
print ( " {0} Select an AI model to continue: {1} \n " . format ( colors . CYAN , colors . END ) )
2021-05-04 07:47:23 +02:00
getModelSelection ( )
2021-05-03 00:46:45 +02:00
#==================================================================#
# Startup
#==================================================================#
2021-05-03 21:19:03 +02:00
2021-05-03 00:46:45 +02:00
# Select a model to run
2021-05-07 20:32:10 +02:00
print ( " {0} Welcome to the KoboldAI Client! \n Select an AI model to continue: {1} \n " . format ( colors . CYAN , colors . END ) )
2021-05-04 07:47:23 +02:00
getModelSelection ( )
2021-05-03 00:46:45 +02:00
2021-05-03 21:19:03 +02:00
# If transformers model was selected & GPU available, ask to use CPU or GPU
2021-05-16 01:29:41 +02:00
if ( not vars . model in [ " InferKit " , " Colab " ] ) :
# Test for GPU support
import torch
print ( " {0} Looking for GPU support... {1} " . format ( colors . PURPLE , colors . END ) , end = " " )
vars . hascuda = torch . cuda . is_available ( )
if ( vars . hascuda ) :
print ( " {0} FOUND! {1} " . format ( colors . GREEN , colors . END ) )
else :
print ( " {0} NOT FOUND! {1} " . format ( colors . YELLOW , colors . END ) )
2021-05-07 20:32:10 +02:00
print ( " {0} Use GPU or CPU for generation?: (Default GPU) {1} \n " . format ( colors . CYAN , colors . END ) )
2021-05-16 01:29:41 +02:00
if ( vars . hascuda ) :
print ( " 1 - GPU \n 2 - CPU \n " )
genselected = False
while ( genselected == False ) :
genselect = input ( " Mode> " )
if ( genselect == " " ) :
vars . usegpu = True
genselected = True
elif ( genselect . isnumeric ( ) and int ( genselect ) == 1 ) :
vars . usegpu = True
genselected = True
elif ( genselect . isnumeric ( ) and int ( genselect ) == 2 ) :
vars . usegpu = False
genselected = True
else :
print ( " {0} Please enter a valid selection. {1} " . format ( colors . RED , colors . END ) )
2021-05-03 21:19:03 +02:00
2021-05-03 00:46:45 +02:00
# Ask for API key if InferKit was selected
if ( vars . model == " InferKit " ) :
if ( not path . exists ( " client.settings " ) ) :
# If the client settings file doesn't exist, create it
2021-05-07 20:32:10 +02:00
print ( " {0} Please enter your InferKit API key: {1} \n " . format ( colors . CYAN , colors . END ) )
2021-05-03 00:46:45 +02:00
vars . apikey = input ( " Key> " )
# Write API key to file
file = open ( " client.settings " , " w " )
2021-05-05 17:18:24 +02:00
try :
2021-05-16 01:29:41 +02:00
js = { " apikey " : vars . apikey }
file . write ( json . dumps ( js , indent = 3 ) )
2021-05-05 17:18:24 +02:00
finally :
file . close ( )
2021-05-03 00:46:45 +02:00
else :
2021-05-07 20:32:10 +02:00
# Otherwise open it up
2021-05-03 00:46:45 +02:00
file = open ( " client.settings " , " r " )
2021-05-07 20:32:10 +02:00
# Check if API key exists
js = json . load ( file )
if ( js [ " apikey " ] != " " ) :
# API key exists, grab it and close the file
vars . apikey = js [ " apikey " ]
file . close ( )
else :
# Get API key, add it to settings object, and write it to disk
print ( " {0} Please enter your InferKit API key: {1} \n " . format ( colors . CYAN , colors . END ) )
vars . apikey = input ( " Key> " )
js [ " apikey " ] = vars . apikey
# Write API key to file
file = open ( " client.settings " , " w " )
try :
2021-05-16 01:29:41 +02:00
file . write ( json . dumps ( js , indent = 3 ) )
2021-05-07 20:32:10 +02:00
finally :
file . close ( )
2021-05-03 00:46:45 +02:00
2021-05-14 00:58:52 +02:00
# Ask for ngrok url if Google Colab was selected
if ( vars . model == " Colab " ) :
print ( " {0} Please enter the ngrok.io URL displayed in Google Colab: {1} \n " . format ( colors . CYAN , colors . END ) )
vars . colaburl = input ( " URL> " ) + " /request "
2021-05-03 00:46:45 +02:00
# Set logging level to reduce chatter from Flask
import logging
log = logging . getLogger ( ' werkzeug ' )
log . setLevel ( logging . ERROR )
# Start flask & SocketIO
2021-05-07 20:32:10 +02:00
print ( " {0} Initializing Flask... {1} " . format ( colors . PURPLE , colors . END ) , end = " " )
2021-05-03 00:46:45 +02:00
from flask import Flask , render_template
from flask_socketio import SocketIO , emit
app = Flask ( __name__ )
app . config [ ' SECRET KEY ' ] = ' secret! '
socketio = SocketIO ( app )
2021-05-07 20:32:10 +02:00
print ( " {0} OK! {1} " . format ( colors . GREEN , colors . END ) )
2021-05-03 00:46:45 +02:00
# Start transformers and create pipeline
2021-05-14 00:58:52 +02:00
if ( not vars . model in [ " InferKit " , " Colab " ] ) :
2021-05-03 00:46:45 +02:00
if ( not vars . noai ) :
2021-05-07 20:32:10 +02:00
print ( " {0} Initializing transformers, please wait... {1} " . format ( colors . PURPLE , colors . END ) )
2021-05-04 07:47:23 +02:00
from transformers import pipeline , GPT2Tokenizer , GPT2LMHeadModel , GPTNeoForCausalLM
2021-05-03 06:24:16 +02:00
2021-05-04 07:47:23 +02:00
# If custom GPT Neo model was chosen
if ( vars . model == " NeoCustom " ) :
model = GPTNeoForCausalLM . from_pretrained ( vars . custmodpth )
tokenizer = GPT2Tokenizer . from_pretrained ( vars . custmodpth )
# Is CUDA available? If so, use GPU, otherwise fall back to CPU
if ( vars . hascuda and vars . usegpu ) :
generator = pipeline ( ' text-generation ' , model = model , tokenizer = tokenizer , device = 0 )
else :
generator = pipeline ( ' text-generation ' , model = model , tokenizer = tokenizer )
# If custom GPT2 model was chosen
elif ( vars . model == " GPT2Custom " ) :
model = GPT2LMHeadModel . from_pretrained ( vars . custmodpth )
tokenizer = GPT2Tokenizer . from_pretrained ( vars . custmodpth )
# Is CUDA available? If so, use GPU, otherwise fall back to CPU
if ( vars . hascuda and vars . usegpu ) :
generator = pipeline ( ' text-generation ' , model = model , tokenizer = tokenizer , device = 0 )
else :
generator = pipeline ( ' text-generation ' , model = model , tokenizer = tokenizer )
# If base HuggingFace model was chosen
2021-05-03 06:24:16 +02:00
else :
2021-05-04 07:47:23 +02:00
# Is CUDA available? If so, use GPU, otherwise fall back to CPU
tokenizer = GPT2Tokenizer . from_pretrained ( vars . model )
if ( vars . hascuda and vars . usegpu ) :
generator = pipeline ( ' text-generation ' , model = vars . model , device = 0 )
else :
generator = pipeline ( ' text-generation ' , model = vars . model )
2021-05-07 20:32:10 +02:00
print ( " {0} OK! {1} pipeline created! {2} " . format ( colors . GREEN , vars . model , colors . END ) )
2021-05-03 00:46:45 +02:00
else :
2021-05-14 00:58:52 +02:00
# If we're running Colab, we still need a tokenizer.
if ( vars . model == " Colab " ) :
from transformers import GPT2Tokenizer
tokenizer = GPT2Tokenizer . from_pretrained ( " EleutherAI/gpt-neo-2.7B " )
2021-05-03 00:46:45 +02:00
# Set up Flask routes
@app.route ( ' / ' )
@app.route ( ' /index ' )
def index ( ) :
return render_template ( ' index.html ' )
#============================ METHODS =============================#
#==================================================================#
# Event triggered when browser SocketIO is loaded and connects to server
#==================================================================#
@socketio.on ( ' connect ' )
def do_connect ( ) :
2021-05-07 20:32:10 +02:00
print ( " {0} Client connected! {1} " . format ( colors . GREEN , colors . END ) )
2021-05-03 00:46:45 +02:00
emit ( ' from_server ' , { ' cmd ' : ' connected ' } )
if ( not vars . gamestarted ) :
setStartState ( )
2021-05-07 20:32:10 +02:00
sendsettings ( )
2021-05-04 15:56:48 +02:00
refresh_settings ( )
2021-05-13 07:26:42 +02:00
sendwi ( )
vars . mode = " play "
2021-05-03 00:46:45 +02:00
else :
# Game in session, send current game data and ready state to browser
refresh_story ( )
2021-05-07 20:32:10 +02:00
sendsettings ( )
2021-05-04 15:56:48 +02:00
refresh_settings ( )
2021-05-13 07:26:42 +02:00
sendwi ( )
2021-05-03 00:46:45 +02:00
if ( vars . mode == " play " ) :
if ( not vars . aibusy ) :
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' ready ' } )
else :
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' wait ' } )
elif ( vars . mode == " edit " ) :
emit ( ' from_server ' , { ' cmd ' : ' editmode ' , ' data ' : ' true ' } )
elif ( vars . mode == " memory " ) :
emit ( ' from_server ' , { ' cmd ' : ' memmode ' , ' data ' : ' true ' } )
2021-05-13 07:26:42 +02:00
elif ( vars . mode == " wi " ) :
emit ( ' from_server ' , { ' cmd ' : ' wimode ' , ' data ' : ' true ' } )
2021-05-03 00:46:45 +02:00
#==================================================================#
# Event triggered when browser SocketIO sends data to the server
#==================================================================#
@socketio.on ( ' message ' )
def get_message ( msg ) :
2021-05-07 20:32:10 +02:00
print ( " {0} Data recieved: {1} {2} " . format ( colors . GREEN , msg , colors . END ) )
2021-05-03 00:46:45 +02:00
# Submit action
if ( msg [ ' cmd ' ] == ' submit ' ) :
if ( vars . mode == " play " ) :
actionsubmit ( msg [ ' data ' ] )
elif ( vars . mode == " edit " ) :
editsubmit ( msg [ ' data ' ] )
elif ( vars . mode == " memory " ) :
memsubmit ( msg [ ' data ' ] )
# Retry Action
elif ( msg [ ' cmd ' ] == ' retry ' ) :
if ( vars . aibusy ) :
return
set_aibusy ( 1 )
# Remove last action if possible and resubmit
if ( len ( vars . actions ) > 0 ) :
vars . actions . pop ( )
refresh_story ( )
calcsubmit ( ' ' )
# Back/Undo Action
elif ( msg [ ' cmd ' ] == ' back ' ) :
if ( vars . aibusy ) :
return
# Remove last index of actions and refresh game screen
if ( len ( vars . actions ) > 0 ) :
vars . actions . pop ( )
refresh_story ( )
# EditMode Action
elif ( msg [ ' cmd ' ] == ' edit ' ) :
if ( vars . mode == " play " ) :
vars . mode = " edit "
emit ( ' from_server ' , { ' cmd ' : ' editmode ' , ' data ' : ' true ' } )
elif ( vars . mode == " edit " ) :
vars . mode = " play "
emit ( ' from_server ' , { ' cmd ' : ' editmode ' , ' data ' : ' false ' } )
# EditLine Action
elif ( msg [ ' cmd ' ] == ' editline ' ) :
editrequest ( int ( msg [ ' data ' ] ) )
# DeleteLine Action
elif ( msg [ ' cmd ' ] == ' delete ' ) :
deleterequest ( )
elif ( msg [ ' cmd ' ] == ' memory ' ) :
togglememorymode ( )
elif ( msg [ ' cmd ' ] == ' save ' ) :
saveRequest ( )
elif ( msg [ ' cmd ' ] == ' load ' ) :
loadRequest ( )
2021-05-11 06:27:34 +02:00
elif ( msg [ ' cmd ' ] == ' import ' ) :
importRequest ( )
2021-05-03 00:46:45 +02:00
elif ( msg [ ' cmd ' ] == ' newgame ' ) :
newGameRequest ( )
2021-05-04 15:56:48 +02:00
elif ( msg [ ' cmd ' ] == ' settemp ' ) :
2021-05-07 20:32:10 +02:00
vars . temp = float ( msg [ ' data ' ] )
2021-05-04 15:56:48 +02:00
emit ( ' from_server ' , { ' cmd ' : ' setlabeltemp ' , ' data ' : msg [ ' data ' ] } )
2021-05-07 20:32:10 +02:00
settingschanged ( )
2021-05-04 15:56:48 +02:00
elif ( msg [ ' cmd ' ] == ' settopp ' ) :
vars . top_p = float ( msg [ ' data ' ] )
emit ( ' from_server ' , { ' cmd ' : ' setlabeltopp ' , ' data ' : msg [ ' data ' ] } )
2021-05-07 20:32:10 +02:00
settingschanged ( )
2021-05-04 15:56:48 +02:00
elif ( msg [ ' cmd ' ] == ' setreppen ' ) :
vars . rep_pen = float ( msg [ ' data ' ] )
emit ( ' from_server ' , { ' cmd ' : ' setlabelreppen ' , ' data ' : msg [ ' data ' ] } )
2021-05-07 20:32:10 +02:00
settingschanged ( )
2021-05-04 15:56:48 +02:00
elif ( msg [ ' cmd ' ] == ' setoutput ' ) :
vars . genamt = int ( msg [ ' data ' ] )
emit ( ' from_server ' , { ' cmd ' : ' setlabeloutput ' , ' data ' : msg [ ' data ' ] } )
2021-05-07 20:32:10 +02:00
settingschanged ( )
elif ( msg [ ' cmd ' ] == ' settknmax ' ) :
vars . max_length = int ( msg [ ' data ' ] )
emit ( ' from_server ' , { ' cmd ' : ' setlabeltknmax ' , ' data ' : msg [ ' data ' ] } )
settingschanged ( )
elif ( msg [ ' cmd ' ] == ' setikgen ' ) :
vars . ikgen = int ( msg [ ' data ' ] )
emit ( ' from_server ' , { ' cmd ' : ' setlabelikgen ' , ' data ' : msg [ ' data ' ] } )
settingschanged ( )
2021-05-05 09:04:06 +02:00
# Author's Note field update
elif ( msg [ ' cmd ' ] == ' anote ' ) :
anotesubmit ( msg [ ' data ' ] )
# Author's Note depth update
elif ( msg [ ' cmd ' ] == ' anotedepth ' ) :
vars . andepth = int ( msg [ ' data ' ] )
emit ( ' from_server ' , { ' cmd ' : ' setlabelanotedepth ' , ' data ' : msg [ ' data ' ] } )
2021-05-07 20:32:10 +02:00
settingschanged ( )
2021-05-11 01:17:10 +02:00
# Format - Trim incomplete sentences
elif ( msg [ ' cmd ' ] == ' frmttriminc ' ) :
if ( ' frmttriminc ' in vars . formatoptns ) :
vars . formatoptns [ " frmttriminc " ] = msg [ ' data ' ]
settingschanged ( )
elif ( msg [ ' cmd ' ] == ' frmtrmblln ' ) :
if ( ' frmtrmblln ' in vars . formatoptns ) :
vars . formatoptns [ " frmtrmblln " ] = msg [ ' data ' ]
settingschanged ( )
elif ( msg [ ' cmd ' ] == ' frmtrmspch ' ) :
if ( ' frmtrmspch ' in vars . formatoptns ) :
vars . formatoptns [ " frmtrmspch " ] = msg [ ' data ' ]
settingschanged ( )
elif ( msg [ ' cmd ' ] == ' frmtadsnsp ' ) :
if ( ' frmtadsnsp ' in vars . formatoptns ) :
vars . formatoptns [ " frmtadsnsp " ] = msg [ ' data ' ]
settingschanged ( )
2021-05-11 06:27:34 +02:00
elif ( msg [ ' cmd ' ] == ' importselect ' ) :
2021-05-14 22:27:47 +02:00
vars . importnum = int ( msg [ " data " ] . replace ( " import " , " " ) )
2021-05-11 06:27:34 +02:00
elif ( msg [ ' cmd ' ] == ' importcancel ' ) :
emit ( ' from_server ' , { ' cmd ' : ' popupshow ' , ' data ' : False } )
vars . importjs = { }
elif ( msg [ ' cmd ' ] == ' importaccept ' ) :
emit ( ' from_server ' , { ' cmd ' : ' popupshow ' , ' data ' : False } )
importgame ( )
2021-05-13 07:26:42 +02:00
elif ( msg [ ' cmd ' ] == ' wi ' ) :
togglewimode ( )
elif ( msg [ ' cmd ' ] == ' wiinit ' ) :
if ( int ( msg [ ' data ' ] ) < len ( vars . worldinfo ) ) :
vars . worldinfo [ msg [ ' data ' ] ] [ " init " ] = True
addwiitem ( )
elif ( msg [ ' cmd ' ] == ' widelete ' ) :
deletewi ( msg [ ' data ' ] )
elif ( msg [ ' cmd ' ] == ' sendwilist ' ) :
commitwi ( msg [ ' data ' ] )
2021-05-16 11:29:39 +02:00
elif ( msg [ ' cmd ' ] == ' aidgimport ' ) :
importAidgRequest ( msg [ ' data ' ] )
2021-05-13 07:26:42 +02:00
2021-05-03 00:46:45 +02:00
#==================================================================#
#
#==================================================================#
def setStartState ( ) :
emit ( ' from_server ' , { ' cmd ' : ' updatescreen ' , ' data ' : ' <span>Welcome to <span class= " color_cyan " >KoboldAI Client</span>! You are running <span class= " color_green " > ' + vars . model + ' </span>.<br/>Please load a game or enter a prompt below to begin!</span> ' } )
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' start ' } )
2021-05-07 20:32:10 +02:00
#==================================================================#
#
#==================================================================#
def sendsettings ( ) :
# Send settings for selected AI type
if ( vars . model != " InferKit " ) :
for set in gensettings . gensettingstf :
emit ( ' from_server ' , { ' cmd ' : ' addsetting ' , ' data ' : set } )
else :
for set in gensettings . gensettingsik :
emit ( ' from_server ' , { ' cmd ' : ' addsetting ' , ' data ' : set } )
2021-05-11 01:17:10 +02:00
# Send formatting options
for frm in gensettings . formatcontrols :
emit ( ' from_server ' , { ' cmd ' : ' addformat ' , ' data ' : frm } )
# Add format key to vars if it wasn't loaded with client.settings
if ( not frm [ " id " ] in vars . formatoptns ) :
vars . formatoptns [ frm [ " id " ] ] = False ;
2021-05-07 20:32:10 +02:00
#==================================================================#
#
#==================================================================#
def savesettings ( ) :
# Build json to write
js = { }
2021-05-11 01:17:10 +02:00
js [ " apikey " ] = vars . apikey
js [ " andepth " ] = vars . andepth
js [ " temp " ] = vars . temp
js [ " top_p " ] = vars . top_p
js [ " rep_pen " ] = vars . rep_pen
js [ " genamt " ] = vars . genamt
js [ " max_length " ] = vars . max_length
js [ " ikgen " ] = vars . ikgen
js [ " formatoptns " ] = vars . formatoptns
2021-05-07 20:32:10 +02:00
# Write it
file = open ( " client.settings " , " w " )
try :
2021-05-16 01:29:41 +02:00
file . write ( json . dumps ( js , indent = 3 ) )
2021-05-07 20:32:10 +02:00
finally :
file . close ( )
#==================================================================#
#
#==================================================================#
def loadsettings ( ) :
if ( path . exists ( " client.settings " ) ) :
# Read file contents into JSON object
file = open ( " client.settings " , " r " )
js = json . load ( file )
# Copy file contents to vars
2021-05-10 15:33:41 +02:00
if ( " apikey " in js ) :
vars . apikey = js [ " apikey " ]
2021-05-10 15:26:31 +02:00
if ( " andepth " in js ) :
vars . andepth = js [ " andepth " ]
if ( " temp " in js ) :
vars . temp = js [ " temp " ]
if ( " top_p " in js ) :
vars . top_p = js [ " top_p " ]
if ( " rep_pen " in js ) :
vars . rep_pen = js [ " rep_pen " ]
if ( " genamt " in js ) :
vars . genamt = js [ " genamt " ]
if ( " max_length " in js ) :
vars . max_length = js [ " max_length " ]
if ( " ikgen " in js ) :
vars . ikgen = js [ " ikgen " ]
2021-05-11 01:17:10 +02:00
if ( " formatoptns " in js ) :
vars . formatoptns = js [ " formatoptns " ]
2021-05-07 20:32:10 +02:00
file . close ( )
#==================================================================#
#
#==================================================================#
@debounce ( 2 )
def settingschanged ( ) :
print ( " {0} Saving settings! {1} " . format ( colors . GREEN , colors . END ) )
savesettings ( )
2021-05-03 00:46:45 +02:00
#==================================================================#
#
#==================================================================#
def actionsubmit ( data ) :
if ( vars . aibusy ) :
return
set_aibusy ( 1 )
if ( not vars . gamestarted ) :
2021-05-11 01:17:10 +02:00
# Start the game
vars . gamestarted = True
# Save this first action as the prompt
vars . prompt = data
# Clear the startup text from game screen
emit ( ' from_server ' , { ' cmd ' : ' updatescreen ' , ' data ' : ' Please wait, generating story... ' } )
2021-05-03 00:46:45 +02:00
calcsubmit ( data ) # Run the first action through the generator
else :
# Dont append submission if it's a blank/continue action
if ( data != " " ) :
2021-05-11 01:17:10 +02:00
# Apply input formatting & scripts before sending to tokenizer
data = applyinputformatting ( data )
# Store the result in the Action log
2021-05-03 00:46:45 +02:00
vars . actions . append ( data )
2021-05-11 01:17:10 +02:00
# Off to the tokenizer!
2021-05-03 00:46:45 +02:00
calcsubmit ( data )
#==================================================================#
# Take submitted text and build the text to be given to generator
#==================================================================#
def calcsubmit ( txt ) :
2021-05-05 09:04:06 +02:00
vars . lastact = txt # Store most recent action in memory (is this still needed?)
anotetxt = " " # Placeholder for Author's Note text
lnanote = 0 # Placeholder for Author's Note length
forceanote = False # In case we don't have enough actions to hit A.N. depth
anoteadded = False # In case our budget runs out before we hit A.N. depth
actionlen = len ( vars . actions )
2021-05-13 07:26:42 +02:00
# Scan for WorldInfo matches
winfo = checkworldinfo ( txt )
# Add a newline to the end of memory
if ( vars . memory != " " and vars . memory [ - 1 ] != " \n " ) :
mem = vars . memory + " \n "
else :
mem = vars . memory
2021-05-05 09:04:06 +02:00
# Build Author's Note if set
if ( vars . authornote != " " ) :
anotetxt = " \n [Author ' s note: " + vars . authornote + " ] \n "
2021-05-03 00:46:45 +02:00
# For all transformers models
if ( vars . model != " InferKit " ) :
2021-05-05 09:04:06 +02:00
anotetkns = [ ] # Placeholder for Author's Note tokens
2021-05-03 00:46:45 +02:00
# Calculate token budget
prompttkns = tokenizer . encode ( vars . prompt )
lnprompt = len ( prompttkns )
2021-05-13 07:26:42 +02:00
memtokens = tokenizer . encode ( mem )
2021-05-03 00:46:45 +02:00
lnmem = len ( memtokens )
2021-05-13 07:26:42 +02:00
witokens = tokenizer . encode ( winfo )
lnwi = len ( witokens )
2021-05-05 09:04:06 +02:00
if ( anotetxt != " " ) :
anotetkns = tokenizer . encode ( anotetxt )
lnanote = len ( anotetkns )
2021-05-03 00:46:45 +02:00
2021-05-13 07:26:42 +02:00
budget = vars . max_length - lnprompt - lnmem - lnanote - lnwi - vars . genamt
2021-05-05 09:04:06 +02:00
if ( actionlen == 0 ) :
2021-05-03 00:46:45 +02:00
# First/Prompt action
2021-05-13 07:26:42 +02:00
subtxt = vars . memory + winfo + anotetxt + vars . prompt
lnsub = lnmem + lnwi + lnprompt + lnanote
2021-05-05 09:04:06 +02:00
2021-05-14 05:30:54 +02:00
if ( vars . model != " Colab " ) :
generate ( subtxt , lnsub + 1 , lnsub + vars . genamt )
else :
sendtocolab ( subtxt , lnsub + 1 , lnsub + vars . genamt )
2021-05-03 00:46:45 +02:00
else :
2021-05-05 09:04:06 +02:00
tokens = [ ]
# Check if we have the action depth to hit our A.N. depth
if ( anotetxt != " " and actionlen < vars . andepth ) :
forceanote = True
2021-05-03 00:46:45 +02:00
# Get most recent action tokens up to our budget
2021-05-05 09:04:06 +02:00
for n in range ( actionlen ) :
2021-05-03 00:46:45 +02:00
if ( budget < = 0 ) :
break
acttkns = tokenizer . encode ( vars . actions [ ( - 1 - n ) ] )
tknlen = len ( acttkns )
if ( tknlen < budget ) :
tokens = acttkns + tokens
budget - = tknlen
else :
count = budget * - 1
tokens = acttkns [ count : ] + tokens
2021-05-03 19:57:27 +02:00
break
2021-05-05 09:04:06 +02:00
# Inject Author's Note if we've reached the desired depth
if ( n == vars . andepth - 1 ) :
if ( anotetxt != " " ) :
tokens = anotetkns + tokens # A.N. len already taken from bdgt
anoteadded = True
# Did we get to add the A.N.? If not, do it here
if ( anotetxt != " " ) :
if ( ( not anoteadded ) or forceanote ) :
tokens = memtokens + anotetkns + prompttkns + tokens
else :
tokens = memtokens + prompttkns + tokens
else :
2021-05-13 07:26:42 +02:00
# Prepend Memory, WI, and Prompt before action tokens
tokens = memtokens + witokens + prompttkns + tokens
2021-05-03 00:46:45 +02:00
# Send completed bundle to generator
ln = len ( tokens )
2021-05-14 00:58:52 +02:00
if ( vars . model != " Colab " ) :
generate (
tokenizer . decode ( tokens ) ,
ln + 1 ,
ln + vars . genamt
)
else :
sendtocolab (
tokenizer . decode ( tokens ) ,
ln + 1 ,
ln + vars . genamt
)
2021-05-03 00:46:45 +02:00
# For InferKit web API
else :
2021-05-05 09:04:06 +02:00
# Check if we have the action depth to hit our A.N. depth
if ( anotetxt != " " and actionlen < vars . andepth ) :
forceanote = True
2021-05-13 07:26:42 +02:00
budget = vars . ikmax - len ( vars . prompt ) - len ( anotetxt ) - len ( mem ) - len ( winfo ) - 1
2021-05-03 00:46:45 +02:00
subtxt = " "
2021-05-05 09:04:06 +02:00
for n in range ( actionlen ) :
2021-05-03 00:46:45 +02:00
if ( budget < = 0 ) :
break
actlen = len ( vars . actions [ ( - 1 - n ) ] )
if ( actlen < budget ) :
subtxt = vars . actions [ ( - 1 - n ) ] + subtxt
budget - = actlen
else :
count = budget * - 1
subtxt = vars . actions [ ( - 1 - n ) ] [ count : ] + subtxt
2021-05-03 19:57:27 +02:00
break
2021-05-05 09:04:06 +02:00
# Inject Author's Note if we've reached the desired depth
if ( n == vars . andepth - 1 ) :
if ( anotetxt != " " ) :
subtxt = anotetxt + subtxt # A.N. len already taken from bdgt
anoteadded = True
2021-05-03 00:46:45 +02:00
2021-05-05 09:04:06 +02:00
# Did we get to add the A.N.? If not, do it here
if ( anotetxt != " " ) :
if ( ( not anoteadded ) or forceanote ) :
2021-05-13 07:26:42 +02:00
subtxt = mem + winfo + anotetxt + vars . prompt + subtxt
2021-05-05 09:04:06 +02:00
else :
2021-05-13 07:26:42 +02:00
subtxt = mem + winfo + vars . prompt + subtxt
2021-05-03 00:46:45 +02:00
else :
2021-05-13 07:26:42 +02:00
subtxt = mem + winfo + vars . prompt + subtxt
2021-05-03 00:46:45 +02:00
# Send it!
ikrequest ( subtxt )
#==================================================================#
# Send text to generator and deal with output
#==================================================================#
def generate ( txt , min , max ) :
2021-05-07 20:32:10 +02:00
print ( " {0} Min: {1} , Max: {2} , Txt: {3} {4} " . format ( colors . YELLOW , min , max , txt , colors . END ) )
2021-05-05 09:04:06 +02:00
# Clear CUDA cache if using GPU
if ( vars . hascuda and vars . usegpu ) :
torch . cuda . empty_cache ( )
2021-05-16 20:53:19 +02:00
# Suppress Author's Note by flagging square brackets
bad_words = [ ]
bad_words . append ( tokenizer ( " [ " , add_prefix_space = True ) . input_ids )
bad_words . append ( tokenizer ( " [ " , add_prefix_space = False ) . input_ids )
2021-05-05 09:04:06 +02:00
# Submit input text to generator
2021-05-03 00:46:45 +02:00
genout = generator (
txt ,
do_sample = True ,
min_length = min ,
max_length = max ,
repetition_penalty = vars . rep_pen ,
2021-05-11 20:16:34 +02:00
top_p = vars . top_p ,
2021-05-16 20:53:19 +02:00
temperature = vars . temp ,
bad_words_ids = bad_words
2021-05-03 00:46:45 +02:00
) [ 0 ] [ " generated_text " ]
2021-05-07 20:32:10 +02:00
print ( " {0} {1} {2} " . format ( colors . CYAN , genout , colors . END ) )
2021-05-11 01:17:10 +02:00
# Format output before continuing
genout = applyoutputformatting ( getnewcontent ( genout ) )
# Add formatted text to Actions array and refresh the game screen
vars . actions . append ( genout )
2021-05-03 00:46:45 +02:00
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' texteffect ' , ' data ' : len ( vars . actions ) } )
2021-05-05 09:04:06 +02:00
# Clear CUDA cache again if using GPU
if ( vars . hascuda and vars . usegpu ) :
torch . cuda . empty_cache ( )
2021-05-03 00:46:45 +02:00
set_aibusy ( 0 )
2021-05-14 00:58:52 +02:00
#==================================================================#
# Send transformers-style request to ngrok/colab host
#==================================================================#
def sendtocolab ( txt , min , max ) :
# Log request to console
2021-05-16 01:29:41 +02:00
print ( " {0} Tokens: {1} , Txt: {2} {3} " . format ( colors . YELLOW , min - 1 , txt , colors . END ) )
2021-05-14 00:58:52 +02:00
# Build request JSON data
reqdata = {
' text ' : txt ,
' min ' : min ,
' max ' : max ,
' rep_pen ' : vars . rep_pen ,
' temperature ' : vars . temp ,
' top_p ' : vars . top_p
}
# Create request
req = requests . post (
vars . colaburl ,
json = reqdata
)
# Deal with the response
if ( req . status_code == 200 ) :
genout = req . json ( ) [ " data " ] [ " text " ]
print ( " {0} {1} {2} " . format ( colors . CYAN , genout , colors . END ) )
# Format output before continuing
genout = applyoutputformatting ( getnewcontent ( genout ) )
# Add formatted text to Actions array and refresh the game screen
vars . actions . append ( genout )
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' texteffect ' , ' data ' : len ( vars . actions ) } )
set_aibusy ( 0 )
else :
# Send error message to web client
er = req . json ( )
if ( " error " in er ) :
code = er [ " error " ] [ " extensions " ] [ " code " ]
elif ( " errors " in er ) :
code = er [ " errors " ] [ 0 ] [ " extensions " ] [ " code " ]
2021-05-16 01:29:41 +02:00
errmsg = " Colab API Error: {0} - {1} " . format ( req . status_code , code )
2021-05-14 00:58:52 +02:00
emit ( ' from_server ' , { ' cmd ' : ' errmsg ' , ' data ' : errmsg } )
set_aibusy ( 0 )
2021-05-03 00:46:45 +02:00
#==================================================================#
# Replaces returns and newlines with HTML breaks
#==================================================================#
def formatforhtml ( txt ) :
return txt . replace ( " \\ r " , " <br/> " ) . replace ( " \\ n " , " <br/> " ) . replace ( ' \n ' , ' <br/> ' ) . replace ( ' \r ' , ' <br/> ' )
#==================================================================#
2021-05-11 01:17:10 +02:00
# Strips submitted text from the text returned by the AI
2021-05-03 00:46:45 +02:00
#==================================================================#
def getnewcontent ( txt ) :
ln = len ( vars . actions )
if ( ln == 0 ) :
delim = vars . prompt
else :
delim = vars . actions [ - 1 ]
2021-05-13 15:35:11 +02:00
# Fix issue with tokenizer replacing space+period with period
delim = delim . replace ( " . " , " . " )
split = txt . split ( delim )
return ( split [ - 1 ] )
2021-05-03 00:46:45 +02:00
2021-05-11 01:17:10 +02:00
#==================================================================#
# Applies chosen formatting options to text submitted to AI
#==================================================================#
def applyinputformatting ( txt ) :
# Add sentence spacing
if ( vars . formatoptns [ " frmtadsnsp " ] ) :
2021-05-14 08:39:36 +02:00
txt = utils . addsentencespacing ( txt , vars )
2021-05-11 01:17:10 +02:00
return txt
#==================================================================#
# Applies chosen formatting options to text returned from AI
#==================================================================#
def applyoutputformatting ( txt ) :
# Use standard quotes and apostrophes
txt = utils . fixquotes ( txt )
# Trim incomplete sentences
if ( vars . formatoptns [ " frmttriminc " ] ) :
txt = utils . trimincompletesentence ( txt )
# Replace blank lines
if ( vars . formatoptns [ " frmtrmblln " ] ) :
txt = utils . replaceblanklines ( txt )
# Remove special characters
if ( vars . formatoptns [ " frmtrmspch " ] ) :
txt = utils . removespecialchars ( txt )
return txt
2021-05-03 00:46:45 +02:00
#==================================================================#
# Sends the current story content to the Game Screen
#==================================================================#
def refresh_story ( ) :
txt = ' <chunk n= " 0 " id= " n0 " > ' + vars . prompt + ' </chunk> '
i = 1
for item in vars . actions :
txt = txt + ' <chunk n= " ' + str ( i ) + ' " id= " n ' + str ( i ) + ' " > ' + item + ' </chunk> '
i + = 1
emit ( ' from_server ' , { ' cmd ' : ' updatescreen ' , ' data ' : formatforhtml ( txt ) } )
2021-05-04 15:56:48 +02:00
#==================================================================#
# Sends the current generator settings to the Game Menu
#==================================================================#
def refresh_settings ( ) :
2021-05-11 01:17:10 +02:00
# Suppress toggle change events while loading state
emit ( ' from_server ' , { ' cmd ' : ' allowtoggle ' , ' data ' : False } )
2021-05-07 20:32:10 +02:00
if ( vars . model != " InferKit " ) :
emit ( ' from_server ' , { ' cmd ' : ' updatetemp ' , ' data ' : vars . temp } )
emit ( ' from_server ' , { ' cmd ' : ' updatetopp ' , ' data ' : vars . top_p } )
emit ( ' from_server ' , { ' cmd ' : ' updatereppen ' , ' data ' : vars . rep_pen } )
emit ( ' from_server ' , { ' cmd ' : ' updateoutlen ' , ' data ' : vars . genamt } )
emit ( ' from_server ' , { ' cmd ' : ' updatetknmax ' , ' data ' : vars . max_length } )
else :
emit ( ' from_server ' , { ' cmd ' : ' updatetemp ' , ' data ' : vars . temp } )
emit ( ' from_server ' , { ' cmd ' : ' updatetopp ' , ' data ' : vars . top_p } )
emit ( ' from_server ' , { ' cmd ' : ' updateikgen ' , ' data ' : vars . ikgen } )
emit ( ' from_server ' , { ' cmd ' : ' updateanotedepth ' , ' data ' : vars . andepth } )
2021-05-11 01:17:10 +02:00
emit ( ' from_server ' , { ' cmd ' : ' updatefrmttriminc ' , ' data ' : vars . formatoptns [ " frmttriminc " ] } )
emit ( ' from_server ' , { ' cmd ' : ' updatefrmtrmblln ' , ' data ' : vars . formatoptns [ " frmtrmblln " ] } )
emit ( ' from_server ' , { ' cmd ' : ' updatefrmtrmspch ' , ' data ' : vars . formatoptns [ " frmtrmspch " ] } )
emit ( ' from_server ' , { ' cmd ' : ' updatefrmtadsnsp ' , ' data ' : vars . formatoptns [ " frmtadsnsp " ] } )
# Allow toggle events again
emit ( ' from_server ' , { ' cmd ' : ' allowtoggle ' , ' data ' : True } )
2021-05-04 15:56:48 +02:00
2021-05-03 00:46:45 +02:00
#==================================================================#
# Sets the logical and display states for the AI Busy condition
#==================================================================#
def set_aibusy ( state ) :
if ( state ) :
vars . aibusy = True
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' wait ' } )
else :
vars . aibusy = False
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' ready ' } )
#==================================================================#
#
#==================================================================#
def editrequest ( n ) :
if ( n == 0 ) :
txt = vars . prompt
else :
txt = vars . actions [ n - 1 ]
vars . editln = n
emit ( ' from_server ' , { ' cmd ' : ' setinputtext ' , ' data ' : txt } )
emit ( ' from_server ' , { ' cmd ' : ' enablesubmit ' , ' data ' : ' ' } )
#==================================================================#
#
#==================================================================#
def editsubmit ( data ) :
if ( vars . editln == 0 ) :
vars . prompt = data
else :
vars . actions [ vars . editln - 1 ] = data
vars . mode = " play "
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' texteffect ' , ' data ' : vars . editln } )
emit ( ' from_server ' , { ' cmd ' : ' editmode ' , ' data ' : ' false ' } )
#==================================================================#
#
#==================================================================#
def deleterequest ( ) :
# Don't delete prompt
if ( vars . editln == 0 ) :
# Send error message
pass
else :
del vars . actions [ vars . editln - 1 ]
vars . mode = " play "
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' editmode ' , ' data ' : ' false ' } )
#==================================================================#
# Toggles the game mode for memory editing and sends UI commands
#==================================================================#
def togglememorymode ( ) :
if ( vars . mode == " play " ) :
vars . mode = " memory "
emit ( ' from_server ' , { ' cmd ' : ' memmode ' , ' data ' : ' true ' } )
emit ( ' from_server ' , { ' cmd ' : ' setinputtext ' , ' data ' : vars . memory } )
2021-05-05 09:04:06 +02:00
emit ( ' from_server ' , { ' cmd ' : ' setanote ' , ' data ' : vars . authornote } )
2021-05-03 00:46:45 +02:00
elif ( vars . mode == " memory " ) :
vars . mode = " play "
emit ( ' from_server ' , { ' cmd ' : ' memmode ' , ' data ' : ' false ' } )
2021-05-13 07:26:42 +02:00
#==================================================================#
# Toggles the game mode for WI editing and sends UI commands
#==================================================================#
def togglewimode ( ) :
if ( vars . mode == " play " ) :
vars . mode = " wi "
emit ( ' from_server ' , { ' cmd ' : ' wimode ' , ' data ' : ' true ' } )
elif ( vars . mode == " wi " ) :
# Commit WI fields first
requestwi ( )
# Then set UI state back to Play
vars . mode = " play "
emit ( ' from_server ' , { ' cmd ' : ' wimode ' , ' data ' : ' false ' } )
#==================================================================#
#
#==================================================================#
def addwiitem ( ) :
ob = { " key " : " " , " content " : " " , " num " : len ( vars . worldinfo ) , " init " : False }
vars . worldinfo . append ( ob ) ;
emit ( ' from_server ' , { ' cmd ' : ' addwiitem ' , ' data ' : ob } )
#==================================================================#
#
#==================================================================#
def sendwi ( ) :
# Cache len of WI
ln = len ( vars . worldinfo )
# Clear contents of WI container
emit ( ' from_server ' , { ' cmd ' : ' clearwi ' , ' data ' : ' ' } )
# If there are no WI entries, send an empty WI object
if ( ln == 0 ) :
addwiitem ( )
else :
# Send contents of WI array
for wi in vars . worldinfo :
ob = wi
emit ( ' from_server ' , { ' cmd ' : ' addwiitem ' , ' data ' : ob } )
# Make sure last WI item is uninitialized
if ( vars . worldinfo [ - 1 ] [ " init " ] ) :
addwiitem ( )
#==================================================================#
# Request current contents of all WI HTML elements
#==================================================================#
def requestwi ( ) :
list = [ ]
for wi in vars . worldinfo :
list . append ( wi [ " num " ] )
emit ( ' from_server ' , { ' cmd ' : ' requestwiitem ' , ' data ' : list } )
#==================================================================#
# Renumber WI items consecutively
#==================================================================#
def organizewi ( ) :
if ( len ( vars . worldinfo ) > 0 ) :
count = 0
for wi in vars . worldinfo :
wi [ " num " ] = count
count + = 1
#==================================================================#
# Extract object from server and send it to WI objects
#==================================================================#
def commitwi ( ar ) :
for ob in ar :
vars . worldinfo [ ob [ " num " ] ] [ " key " ] = ob [ " key " ]
vars . worldinfo [ ob [ " num " ] ] [ " content " ] = ob [ " content " ]
# Was this a deletion request? If so, remove the requested index
if ( vars . deletewi > = 0 ) :
del vars . worldinfo [ vars . deletewi ]
organizewi ( )
# Send the new WI array structure
sendwi ( )
# And reset deletewi index
vars . deletewi = - 1
#==================================================================#
#
#==================================================================#
def deletewi ( num ) :
if ( num < len ( vars . worldinfo ) ) :
# Store index of deletion request
vars . deletewi = num
# Get contents of WI HTML inputs
requestwi ( )
#==================================================================#
# Look for WI keys in text to generator
#==================================================================#
def checkworldinfo ( txt ) :
# Dont go any further if WI is empty
if ( len ( vars . worldinfo ) == 0 ) :
return
# Join submitted text to last action
if ( len ( vars . actions ) > 0 ) :
txt = vars . actions [ - 1 ] + txt
# Scan text for matches on WI keys
wimem = " "
for wi in vars . worldinfo :
if ( wi [ " key " ] != " " ) :
# Split comma-separated keys
keys = wi [ " key " ] . split ( " , " )
for k in keys :
# Remove leading/trailing spaces
ky = k . strip ( )
if ky in txt :
wimem = wimem + wi [ " content " ] + " \n "
break
return wimem
2021-05-03 00:46:45 +02:00
#==================================================================#
# Commit changes to Memory storage
#==================================================================#
def memsubmit ( data ) :
# Maybe check for length at some point
# For now just send it to storage
vars . memory = data
vars . mode = " play "
emit ( ' from_server ' , { ' cmd ' : ' memmode ' , ' data ' : ' false ' } )
2021-05-05 09:04:06 +02:00
# Ask for contents of Author's Note field
emit ( ' from_server ' , { ' cmd ' : ' getanote ' , ' data ' : ' ' } )
#==================================================================#
# Commit changes to Author's Note
#==================================================================#
def anotesubmit ( data ) :
# Maybe check for length at some point
# For now just send it to storage
vars . authornote = data
2021-05-03 00:46:45 +02:00
#==================================================================#
# Assembles game data into a request to InferKit API
#==================================================================#
def ikrequest ( txt ) :
# Log request to console
2021-05-07 20:32:10 +02:00
print ( " {0} Len: {1} , Txt: {2} {3} " . format ( colors . YELLOW , len ( txt ) , txt , colors . END ) )
2021-05-03 00:46:45 +02:00
# Build request JSON data
reqdata = {
' forceNoEnd ' : True ,
2021-05-07 20:32:10 +02:00
' length ' : vars . ikgen ,
2021-05-03 00:46:45 +02:00
' prompt ' : {
' isContinuation ' : False ,
' text ' : txt
} ,
' startFromBeginning ' : False ,
' streamResponse ' : False ,
' temperature ' : vars . temp ,
2021-05-04 17:48:24 +02:00
' topP ' : vars . top_p
2021-05-03 00:46:45 +02:00
}
# Create request
req = requests . post (
vars . url ,
json = reqdata ,
headers = {
' Authorization ' : ' Bearer ' + vars . apikey
}
)
# Deal with the response
if ( req . status_code == 200 ) :
genout = req . json ( ) [ " data " ] [ " text " ]
2021-05-07 20:32:10 +02:00
print ( " {0} {1} {2} " . format ( colors . CYAN , genout , colors . END ) )
2021-05-03 00:46:45 +02:00
vars . actions . append ( genout )
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' texteffect ' , ' data ' : len ( vars . actions ) } )
set_aibusy ( 0 )
else :
# Send error message to web client
er = req . json ( )
if ( " error " in er ) :
code = er [ " error " ] [ " extensions " ] [ " code " ]
elif ( " errors " in er ) :
code = er [ " errors " ] [ 0 ] [ " extensions " ] [ " code " ]
errmsg = " InferKit API Error: {0} - {1} " . format ( req . status_code , code )
emit ( ' from_server ' , { ' cmd ' : ' errmsg ' , ' data ' : errmsg } )
set_aibusy ( 0 )
#==================================================================#
# Forces UI to Play mode
#==================================================================#
def exitModes ( ) :
if ( vars . mode == " edit " ) :
emit ( ' from_server ' , { ' cmd ' : ' editmode ' , ' data ' : ' false ' } )
elif ( vars . mode == " memory " ) :
emit ( ' from_server ' , { ' cmd ' : ' memmode ' , ' data ' : ' false ' } )
2021-05-13 07:26:42 +02:00
elif ( vars . mode == " wi " ) :
emit ( ' from_server ' , { ' cmd ' : ' wimode ' , ' data ' : ' false ' } )
2021-05-03 00:46:45 +02:00
vars . mode = " play "
#==================================================================#
# Save the story to a file
#==================================================================#
def saveRequest ( ) :
2021-05-07 20:32:10 +02:00
savpath = fileops . getsavepath ( vars . savedir , " Save Story As " , [ ( " Json " , " *.json " ) ] )
2021-05-03 00:46:45 +02:00
2021-05-07 20:32:10 +02:00
if ( savpath ) :
2021-05-03 00:46:45 +02:00
# Leave Edit/Memory mode before continuing
exitModes ( )
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
# Save path for future saves
2021-05-07 20:32:10 +02:00
vars . savedir = savpath
2021-05-03 00:46:45 +02:00
# Build json to write
js = { }
js [ " gamestarted " ] = vars . gamestarted
js [ " prompt " ] = vars . prompt
js [ " memory " ] = vars . memory
2021-05-05 09:04:06 +02:00
js [ " authorsnote " ] = vars . authornote
2021-05-03 00:46:45 +02:00
js [ " actions " ] = vars . actions
2021-05-13 07:26:42 +02:00
js [ " worldinfo " ] = [ ]
# Extract only the important bits of WI
for wi in vars . worldinfo :
if ( wi [ " key " ] != " " ) :
js [ " worldinfo " ] . append ( {
" key " : wi [ " key " ] ,
" content " : wi [ " content " ]
} )
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
# Write it
2021-05-07 20:32:10 +02:00
file = open ( savpath , " w " )
2021-05-05 17:18:24 +02:00
try :
2021-05-16 01:29:41 +02:00
file . write ( json . dumps ( js , indent = 3 ) )
2021-05-05 17:18:24 +02:00
finally :
file . close ( )
2021-05-03 00:46:45 +02:00
#==================================================================#
# Load a stored story from a file
#==================================================================#
2021-05-07 20:32:10 +02:00
def loadRequest ( ) :
loadpath = fileops . getloadpath ( vars . savedir , " Select Story File " , [ ( " Json " , " *.json " ) ] )
2021-05-03 00:46:45 +02:00
2021-05-07 20:32:10 +02:00
if ( loadpath ) :
2021-05-03 00:46:45 +02:00
# Leave Edit/Memory mode before continuing
exitModes ( )
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
# Read file contents into JSON object
2021-05-07 20:32:10 +02:00
file = open ( loadpath , " r " )
js = json . load ( file )
2021-05-03 00:46:45 +02:00
# Copy file contents to vars
vars . gamestarted = js [ " gamestarted " ]
vars . prompt = js [ " prompt " ]
vars . memory = js [ " memory " ]
vars . actions = js [ " actions " ]
2021-05-13 07:26:42 +02:00
vars . worldinfo = [ ]
2021-05-05 09:04:06 +02:00
# Try not to break older save files
if ( " authorsnote " in js ) :
vars . authornote = js [ " authorsnote " ]
2021-05-07 20:32:10 +02:00
else :
vars . authornote = " "
2021-05-05 09:04:06 +02:00
2021-05-13 07:26:42 +02:00
if ( " worldinfo " in js ) :
num = 0
for wi in js [ " worldinfo " ] :
vars . worldinfo . append ( {
" key " : wi [ " key " ] ,
" content " : wi [ " content " ] ,
" num " : num ,
" init " : True
} )
num + = 1
2021-05-03 00:46:45 +02:00
file . close ( )
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
# Refresh game screen
2021-05-13 07:26:42 +02:00
sendwi ( )
2021-05-03 00:46:45 +02:00
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' ready ' } )
2021-05-11 06:27:34 +02:00
#==================================================================#
# Import an AIDungon game exported with Mimi's tool
#==================================================================#
def importRequest ( ) :
importpath = fileops . getloadpath ( vars . savedir , " Select AID CAT File " , [ ( " Json " , " *.json " ) ] )
if ( importpath ) :
# Leave Edit/Memory mode before continuing
exitModes ( )
# Read file contents into JSON object
2021-05-14 22:27:47 +02:00
file = open ( importpath , " rb " )
2021-05-11 06:27:34 +02:00
vars . importjs = json . load ( file )
# Clear Popup Contents
emit ( ' from_server ' , { ' cmd ' : ' clearpopup ' , ' data ' : ' ' } )
# Initialize vars
num = 0
vars . importnum = - 1
# Get list of stories
for story in vars . importjs :
ob = { }
ob [ " num " ] = num
2021-05-14 22:27:47 +02:00
if ( story [ " title " ] != " " and story [ " title " ] != None ) :
ob [ " title " ] = story [ " title " ]
else :
ob [ " title " ] = " (No Title) "
if ( story [ " description " ] != " " and story [ " description " ] != None ) :
2021-05-11 06:27:34 +02:00
ob [ " descr " ] = story [ " description " ]
else :
ob [ " descr " ] = " (No Description) "
ob [ " acts " ] = len ( story [ " actions " ] )
emit ( ' from_server ' , { ' cmd ' : ' addimportline ' , ' data ' : ob } )
num + = 1
# Show Popup
emit ( ' from_server ' , { ' cmd ' : ' popupshow ' , ' data ' : True } )
#==================================================================#
# Import an AIDungon game selected in popup
#==================================================================#
def importgame ( ) :
if ( vars . importnum > = 0 ) :
# Cache reference to selected game
ref = vars . importjs [ vars . importnum ]
# Copy game contents to vars
vars . gamestarted = True
if ( len ( ref [ " actions " ] ) > 0 ) :
vars . prompt = ref [ " actions " ] [ 0 ] [ " text " ]
else :
vars . prompt = " "
vars . memory = ref [ " memory " ]
vars . authornote = ref [ " authorsNote " ]
vars . actions = [ ]
2021-05-13 07:26:42 +02:00
vars . worldinfo = [ ]
2021-05-11 06:27:34 +02:00
# Get all actions except for prompt
if ( len ( ref [ " actions " ] ) > 1 ) :
for act in ref [ " actions " ] [ 1 : ] :
vars . actions . append ( act [ " text " ] )
2021-05-13 07:26:42 +02:00
# Get just the important parts of world info
2021-05-14 22:27:47 +02:00
if ( ref [ " worldInfo " ] != None ) :
if ( len ( ref [ " worldInfo " ] ) > 1 ) :
num = 0
for wi in ref [ " worldInfo " ] :
vars . worldinfo . append ( {
" key " : wi [ " keys " ] ,
" content " : wi [ " entry " ] ,
" num " : num ,
" init " : True
} )
num + = 1
2021-05-13 07:26:42 +02:00
2021-05-11 06:27:34 +02:00
# Clear import data
vars . importjs = { }
# Refresh game screen
2021-05-13 07:26:42 +02:00
sendwi ( )
2021-05-11 06:27:34 +02:00
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' ready ' } )
2021-05-16 11:29:39 +02:00
#==================================================================#
# Import an aidg.club prompt and start a new game with it.
#==================================================================#
2021-05-16 20:53:19 +02:00
def importAidgRequest ( id ) :
2021-05-16 11:29:39 +02:00
exitModes ( )
2021-05-16 20:53:19 +02:00
urlformat = " https://prompts.aidg.club/api/ "
2021-05-16 11:29:39 +02:00
req = requests . get ( urlformat + id )
if ( req . status_code == 200 ) :
2021-05-16 20:53:19 +02:00
js = req . json ( )
2021-05-16 11:29:39 +02:00
2021-05-16 20:53:19 +02:00
# Import game state
2021-05-16 11:29:39 +02:00
vars . gamestarted = True
2021-05-16 20:53:19 +02:00
vars . prompt = js [ " promptContent " ]
vars . memory = js [ " memory " ]
vars . authornote = js [ " authorsNote " ]
2021-05-16 11:29:39 +02:00
vars . actions = [ ]
vars . worldinfo = [ ]
2021-05-16 20:53:19 +02:00
num = 0
for wi in js [ " worldInfos " ] :
vars . worldinfo . append ( {
" key " : wi [ " keys " ] ,
" content " : wi [ " entry " ] ,
" num " : num ,
" init " : True
} )
num + = 1
2021-05-16 11:29:39 +02:00
# Refresh game screen
sendwi ( )
refresh_story ( )
emit ( ' from_server ' , { ' cmd ' : ' setgamestate ' , ' data ' : ' ready ' } )
2021-05-03 00:46:45 +02:00
#==================================================================#
# Starts a new story
#==================================================================#
def newGameRequest ( ) :
# Ask for confirmation
2021-05-05 17:18:24 +02:00
root = tk . Tk ( )
root . attributes ( " -topmost " , True )
2021-05-07 20:32:10 +02:00
confirm = tk . messagebox . askquestion ( " Confirm New Game " , " Really start new Story? " )
2021-05-05 17:18:24 +02:00
root . destroy ( )
if ( confirm == " yes " ) :
2021-05-03 00:46:45 +02:00
# Leave Edit/Memory mode before continuing
exitModes ( )
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
# Clear vars values
vars . gamestarted = False
vars . prompt = " "
vars . memory = " "
vars . actions = [ ]
2021-05-07 20:32:10 +02:00
vars . savedir = getcwd ( ) + " \ stories "
vars . authornote = " "
2021-05-16 01:29:41 +02:00
vars . worldinfo = [ ]
2021-05-07 20:32:10 +02:00
2021-05-03 00:46:45 +02:00
# Refresh game screen
2021-05-16 01:29:41 +02:00
sendwi ( )
2021-05-03 00:46:45 +02:00
setStartState ( )
#==================================================================#
2021-05-07 20:32:10 +02:00
# Final startup commands to launch Flask app
2021-05-03 00:46:45 +02:00
#==================================================================#
if __name__ == " __main__ " :
2021-05-07 20:32:10 +02:00
# Load settings from client.settings
loadsettings ( )
# Start Flask/SocketIO (Blocking, so this must be last method!)
print ( " {0} Server started! \r You may now connect with a browser at http://127.0.0.1:5000/ {1} " . format ( colors . GREEN , colors . END ) )
2021-05-13 07:26:42 +02:00
#socketio.run(app, host='0.0.0.0', port=5000)
socketio . run ( app )