def getGenerator(): colPrint( "\nInitializing AI Engine! (This might take a few minutes)\n", colors["loading-message"], ) models = [x for x in Path('models').iterdir() if x.is_dir()] if not models: raise FileNotFoundError( 'There are no models in the models directory! You must download a pytorch compatible model!' ) elif len(models) > 1: colPrint( "You have multiple models in your models folder. Please select one to load:", colors['message']) for n, model_path in enumerate(models): colPrint("{}: {}".format(n, model_path.name), colors['menu']) model = models[getNumberInput(len(models) - 1)] else: model = models[0] logger.info("Using model: " + str(model)) return GPT2Generator( model_path=model, generate_num=settings.getint("generate-num"), temperature=settings.getfloat("temp"), top_k=settings.getint("top-keks"), top_p=settings.getfloat("top-p"), repetition_penalty=settings.getfloat("rep-pen"), )
def instructions(): if settings.getboolean("console-bell"): bell = "on" else: bell = "off" if settings.getboolean("action-d20"): d20 = "on" else: d20 = "off" print( '\033[' + colors["instructions"] + 'm' + 'AID2: Clover Edition Instructions: \n Enter actions starting with a verb ex. "go to the tavern" or "attack the orc."\n To speak enter say "(thing you want to say)" or just "(thing you want to say)"' ) print('The following commands can be entered for any action:') print( ' "/revert" Reverts the last action allowing you to pick a different action.' ) print(' "/quit" Quits the game and saves') print( ' "/menu" Starts a new game and saves your current one') print(' "/retry" Retries the last action') print(' "/restart" Restarts the current story') print( ' "/print" Prints a transcript of your adventure (without extra newline formatting)' ) print(' "/help" Prints these instructions again') print( ' "/set SETTING VALUE" Sets the specified setting to the specified value.:' ) print( ' temp Higher values make the AI more random. Default: 0.4 | Current:', settings.getfloat("temp")) print( ' rep-pen Controls how repetitive the AI is allowed to be. Default: 1.2 | Current:', settings.getfloat("rep-pen")) print( ' text-wrap-width Maximum width of lines printed by computer. Default: 80 | Current:', settings.getint("text-wrap-width")) print( ' console-bell Beep after AI generates text? Default: on | Current:', bell) print( ' top-keks Number of words the AI can randomly choose. Default: 20 | Current:', settings.getint("top-keks")) print(' generate-num Default: 60 | Current:', settings.getint("generate-num")) print(' top-p Default: 0.9 | Current:', settings.getfloat("top-p")) print(' log-level Default: 3 | Current:', settings.getint("log-level")) print( ' action-sugg How many actions to generate, 0 is off. Default: 4 | Current:', settings.getint("action-sugg")) print( ' action-d20 Make actions difficult. Default: on | Current:', d20) print( ' action-temp How random the suggested actions are. Default: 1 | Current:', settings.getfloat("action-temp"), '\033[39m')
def act(self, action): assert (self.context.strip() + action.strip()) assert (settings.getint('top-keks') is not None) self.actions.append(format_result(action)) result = self.generator.generate( self.get_story() + action, self.context + ' '.join(self.memory), temperature=settings.getfloat('temp'), top_p=settings.getfloat('top-p'), top_k=settings.getint('top-keks'), repetition_penalty=settings.getfloat('rep-pen')) self.results.append(format_result(result)) return self.results[-1]
def colPrint(text, col="0", wrap=True, end=None): if wrap: width = settings.getint("text-wrap-width") width = 999999999 if width < 2 else width width = min(width, termWidth) text = textwrap.fill(text, width, replace_whitespace=False) print("\x1B[{}m{}\x1B[{}m".format(col, text, colors["default"]), end=end) return text.count('\n') + 1
def act(self, action): assert (self.prompt + action) results = [] for i in range(self.numResults): assert (settings.getint('top-keks') is not None) results.append( self.generator.generate( self.getStory() + action, self.prompt, temperature=settings.getfloat('temp'), top_p=settings.getfloat('top-p'), top_k=settings.getint('top-keks'), repetition_penalty=settings.getfloat('rep-pen'))) #self.longTermMemory.join('\n\n'), self.prompt)) self.story.append([action, results]) return results
def get_suggestion(self): return re.sub('\n.*', '', self.generator.generate_raw( self.get_story() + "\n\n> You", self.context, temperature=settings.getfloat('action-temp'), top_p=settings.getfloat('top-p'), top_k=settings.getint('top-keks'), repetition_penalty=1))
def getSuggestion(self): #temporary fix (TODO) return re.sub( '\n.*', '', self.generator.generate_raw( self.getStory() + "\n\n> You", self.prompt, temperature=settings.getfloat('action-temp'), top_p=settings.getfloat('top-p'), top_k=settings.getint('top-keks'), repetition_penalty=1))
def to_dict(self): res = {} res["temp"] = settings.getfloat('temp') res["top-p"] = settings.getfloat("top-p") res["top-keks"] = settings.getint("top-keks") res["rep-pen"] = settings.getfloat("rep-pen") res["context"] = self.context res["memory"] = self.memory res["actions"] = self.actions res["results"] = self.results return res
def get_action(self): # While we want the story to be on track, but not to on track that it loops # the actions can be quite random, and this helps inject some user curated randomness # and prevent loops. So lets make the actions quite random, and prevent duplicates while we are at it # what to feed to model? mem_ind = random.randint(1, 6) # How many steps to include sample = random.randint(0, 1) # Random steps from history? include_prompt = random.randint(0, 1) # Include the initial promts predicates = ['You try to ', 'You say "', 'You start to ', '"'] # The model has to continue from here predicate = random.sample(predicates, 1)[0] action_prompt = self.story_manager.story_context( mem_ind, sample, include_prompt) action_prompt[-1] = action_prompt[-1].strip() + "\n> " + predicate result_raw = self.story_manager.generator.generate_raw( action_prompt, generate_num=settings.getint("action-generate-num"), temperature=settings.getfloat("action-temp"), stop_tokens=self.story_manager.generator.tokenizer.encode( ["<|endoftext|>", "\n", ">"]) # stop_tokens=self.generator.tokenizer.encode(['>', '<|endoftext|>']) ) logger.info( "get_action (mem_ind=%s, sample=%s, include_prompt=%s, predicate=`%r`) -> %r", mem_ind, sample, include_prompt, predicate, result_raw) result = predicate + result_raw.lstrip() result = clean_suggested_action( result, min_length=settings.getint("action-min-length")) # Sometimes the suggestion start with "You" we will add that on later anyway so remove it here result = re.sub("^ ?[Yy]ou try to ?", "You ", result) result = re.sub("^ ?[Yy]ou start to ?", "You ", result) result = re.sub("^ ?[Yy]ou say \"", "\"", result) result = re.sub("^ ?[Yy]ou ?", "", result) return result
def play(generator): print("\n") with open(Path("interface", "mainTitle.txt"), "r", encoding="utf-8") as file: colPrint(file.read(), colors["title"], wrap=False) with open(Path("interface", "subTitle.txt"), "r", encoding="utf-8") as file: cols = termWidth for line in file: line = re.sub(r'\n', '', line) line = line[:cols] #fills in the graphic using reverse video mode substituted into the areas between |'s colPrint( re.sub(r'\|[ _]*(\||$)', lambda x: '\x1B[7m' + x.group(0) + '\x1B[27m', line), colors['subtitle'], False) print() colPrint( "Go to https://github.com/cloveranon/Clover-Edition/ or email [email protected] for bug reports, help, and feature requests.", colors['subsubtitle']) while True: # May be needed to avoid out of mem gc.collect() torch.cuda.empty_cache() print("\n\n") colPrint( "0: Pick Prompt From File (Default if you type nothing)\n1: Write Custom Prompt", colors['menu']) if getNumberInput(1) == 1: with open(Path("interface", "prompt-instructions.txt"), "r", encoding="utf-8") as file: colPrint(file.read(), colors["instructions"], False) prompt = colInput("Prompt>", colors["main-prompt"], colors["user-text"]) context = colInput("Context>", colors["main-prompt"], colors["user-text"]) filename = colInput( "Name to save prompt as? (Leave blank for no save): ", colors["query"], colors["user-text"], ) filename = re.sub( "-$", "", re.sub("^-", "", re.sub("[^a-zA-Z0-9_-]+", "-", filename))) if filename != "": with open(Path("prompts", filename + ".txt"), "w", encoding="utf-8") as f: f.write(context + "\n" + prompt) else: prompt, context = selectFile() assert (prompt + context) instructions() print() colPrint("Generating story...", colors["loading-message"]) story = newStory(generator, prompt, context) while True: # Generate suggested actions act_alts = settings.getint("action-sugg") if act_alts > 0: # TODO change this to two messages for different colors suggested_actions = [] colPrint("\nSuggested actions:", colors["selection-value"]) action_suggestion_lines = 2 for i in range(act_alts): suggested_action = story.getSuggestion() if len(suggested_action.strip()) > 0: j = len(suggested_actions) suggested_actions.append(suggested_action) suggestion = "{}> {}".format(j, suggested_action) action_suggestion_lines += colPrint( suggestion, colors["selection-value"]) print() bell() action = colInput("> You ", colors["main-prompt"], colors["user-text"]) # Clear suggestions and user input if act_alts > 0: action_suggestion_lines += 2 if not IN_COLAB: clear_lines(action_suggestion_lines) # Show user input again # colPrint("\n> " + action.rstrip(), colors["user-text"], end="") setRegex = re.search("^/set ([^ ]+) ([^ ]+)$", action) if setRegex: if setRegex.group(1) in settings: currentSettingValue = settings[setRegex.group(1)] colPrint( "Current Value of {}: {} Changing to: {}".format( setRegex.group(1), currentSettingValue, setRegex.group(2))) settings[setRegex.group(1)] = setRegex.group(2) colPrint("Save config file?", colors["query"]) colPrint("Saving an invalid option will corrupt file!", colors["error"]) if (colInput( "y/n? >", colors["selection-prompt"], colors["selection-value"], ) == "y"): with open("config.ini", "w", encoding="utf-8") as file: config.write(file) else: colPrint("Invalid Setting", colors["error"]) instructions() elif action == "/menu": break elif action == "/restart": print() colPrint("Restarting story...", colors["loading-message"]) story = newStory(generator, story.prompt, context) continue elif action == "/quit": exit() elif action == "/help": instructions() elif action == "/print": print("\nPRINTING\n") #TODO colorize printed story colPrint(str(story), colors["print-story"]) elif action == '/retry': if len(story.story) == 1: print() colPrint("Restarting story...", colors["loading-message"]) story = newStory(generator, story.prompt, context) continue else: newaction = story.story[-1][0] colPrint(newaction, colors['user-text'], end='') story.story = story.story[:-1] result = "\n" + story.act(newaction)[0] if len(story.story) >= 2: similarity = get_similarity(result, story.story[-2][1][0]) if similarity > 0.9: story.story = story.story[:-1] colPrint( "Woops that action caused the model to start looping. Try a different action to prevent that.", colors["error"], ) continue colPrint(result, colors["ai-text"]) continue elif action == '/revert': if len(story.story) == 1: colPrint("You can't go back any farther. ", colors["error"]) continue story.story = story.story[:-1] colPrint("Last action reverted. ", colors["message"]) if len(story.story) < 2: colPrint(story.prompt, colors["ai-text"]) colPrint(story.story[-1][1][0], colors["ai-text"]) continue elif action == "/alter": story.story[-1][1][0] = alterText(story.story[-1][1][0]) if len(story.story) < 2: colPrint(story.prompt, colors["ai-text"]) else: colPrint("\n" + story.story[-1][0] + "\n", colors["transformed-user-text"]) colPrint("\n" + story.story[-1][1][0] + "\n\n", colors["ai-text"]) elif action == "/prompt": story.prompt = alterText(story.prompt) if len(story.story) < 2: colPrint(story.prompt, colors["ai-text"]) else: colPrint("\n" + story.story[-1][0] + "\n", colors["transformed-user-text"]) colPrint("\n" + story.story[-1][1][0] + "\n\n", colors["ai-text"]) else: if act_alts > 0: # Options to select a suggestion action if action in [ str(i) for i in range(len(suggested_actions)) ]: action = suggested_actions[int(action)] original_action = action action = action.strip() #TODO debug stuff to delete if action != original_action: logger.debug("STRIPPED WHITE SPACE OFF ACTION %r vs %r", original_action, action) # Crop actions to a max length #action = action[:4096] if action != "": # Roll a 20 sided dice to make things interesting d = random.randint(1, 20) logger.debug("roll d20=%s", d) # If it says 'You say "' then it's still dialouge. Normalise it by removing `You say `, we will add again soon action = re.sub("^ ?[Yy]ou say [\"']", '"', action) if any(action.lstrip().startswith(t) for t in ['"', "'"]): if settings.getboolean("action-d20"): action = d20ify_speech(action, d) else: action = "You say " + action logger.info( "%r. %r, %r", action, any(action.lstrip().startswith(t) for t in ['"', "'"]), settings.getboolean("action-d20")) else: action = first_to_second_person(action) if not action.lower().startswith( "you ") and not action.lower().startswith( "i "): action = action[0].lower() + action[1:] # roll a d20 if settings.getboolean("action-d20"): action = d20ify_action(action, d) else: action = "You " + action if action[-1] not in [".", "?", "!"]: action = action + "." action = "\n> " + action + "\n" colPrint( "\n>" + action.lstrip().lstrip("> \n"), colors["transformed-user-text"], ) #TODO check if leading white space makes sense result = "\n" + story.act(action)[0] #TODO: Replace all this nonsense if len(story.story) >= 2: similarity = get_similarity(result, story.story[-2][1][0]) if similarity > 0.9: story.story = story.story[:-1] colPrint( "Woops that action caused the model to start looping. Try a different action to prevent that.", colors["error"], ) continue if player_won(result): colPrint(result + "\n CONGRATS YOU WIN", colors["message"]) break elif player_died(result): colPrint(result, colors["ai-text"]) colPrint("YOU DIED. GAME OVER", colors["error"]) colPrint( "\nOptions:\n0)Start a new game\n1)\"I'm not dead yet!\" (If you didn't actually die)", colors["menu"], ) choice = getNumberInput(1) if choice == 0: break else: colPrint("Sorry about that...where were we?", colors["query"]) colPrint(result, colors["ai-text"])
def output(text1, col1=None, text2=None, col2=None, wrap=True, beg=None, end='\n', sep=' ', rem_beg_spaces=True): print('', end=beg) ptoolkit = use_ptoolkit() and ptcolors['displaymethod'] == "prompt-toolkit" if wrap: width = settings.getint("text-wrap-width") width = 999999999 if width < 2 else width width = min(width, termWidth) wtext = text1 + '\u200D' + sep + '\u200D' + text2 if text2 is not None else text1 wtext = fill_text(wtext, width) wtext = re.sub(r"\n[ \t]+", "\n", wtext) if rem_beg_spaces else wtext wtext = wtext.split('\u200D') text1 = wtext[0] if text2 is not None: sep = wtext[1] text2 = ' '.join(wtext[2:]) if ptoolkit: col1 = ptcolors[col1] if col1 and ptcolors[col1] else "" col2 = ptcolors[col2] if col2 and ptcolors[col2] else "" print_formatted_text(to_formatted_text(text1, col1), end='') if text2: print_formatted_text(to_formatted_text(sep), end='') print_formatted_text(to_formatted_text(text2, col2), end='') print('', end=end) else: col1 = colors[col1] if col1 and colors[col1] and colors[col1][ 0].isdigit() else None col2 = colors[col2] if col2 and colors[col2] and colors[col2][ 0].isdigit() else None clb1 = "\x1B[{}m".format(col1) if col1 else "" clb2 = "\x1B[{}m".format(col2) if col2 else "" cle1 = "\x1B[0m" if col1 else "" cle2 = "\x1B[0m" if col2 else "" text1 = clb1 + text1 + cle1 if text2 is not None: text2 = clb2 + text2 + cle2 print(text1, end='') print(sep, end='') print(text2, end=end) else: print(text1, end=end) linecount = 1 if beg: linecount += beg.count('\n') if text1: linecount += text1.count('\n') if end: linecount += end.count('\n') if text2: linecount += text2.count('\n') if sep: linecount += sep.count('\n') return linecount
#a little script to test if 16 bit degrades accuracy of the model. Not a perfect experiment but I think it's good enough #must be moved to the clover-edition directory or it will not run #I can not figure out why. According to pythons documentation, only the current directory matters, not the placement of the file from getconfig import settings settings['log-level'] = str(min(settings.getint('log-level'), 10)) from gpt2generator import GPT2Generator import torch import numpy as np import gc from pathlib import Path import random import string import time #seed=0 seed = int(time.time()) % 10000 top_p = 0.5 top_k = 0 temp = 0.2 def randomString(stringLength=12): letters = string.ascii_lowercase + ' ' + string.digits return ''.join(random.choice(letters) for i in range(stringLength)) prompt = 'abcd1234' #prompt=randomString(12) print('\x1B[0m') #I really am addicted to color output with open(Path('interface/', 'clover', encoding='utf-8')) as file:
def output(text1, col1=None, text2=None, col2=None, wrap=True, beg=None, end='\n', sep=' '): print('', end=beg) ptoolkit = use_ptoolkit() and ptcolors['displaymethod'] == "prompt-toolkit" if not ptoolkit: col1 = colors[col1] if col1 and colors[col1] and colors[col1][ 0].isdigit() else None col2 = colors[col2] if col2 and colors[col2] and colors[col2][ 0].isdigit() else None clb1 = "\x1B[{}m".format(col1) if col1 else "" clb2 = "\x1B[{}m".format(col2) if col2 else "" cle1 = "\x1B[0m" if col1 else "" cle2 = "\x1B[0m" if col2 else "" text1 = clb1 + text1 + cle1 if text2 is not None: text2 = clb2 + text2 + cle2 if wrap: width = settings.getint("text-wrap-width") width = 999999999 if width < 2 else width width = min(width, termWidth) text1 = textwrap.fill(text1, width, replace_whitespace=False, drop_whitespace=False) if text2: if len(text1) + 1 + len(text2) >= width: if not re.match("^\n+$", sep): sep += '\n' text2 = textwrap.fill(text2, width, replace_whitespace=False, drop_whitespace=False) if ptoolkit: col1 = ptcolors[col1] if col1 and ptcolors[col1] else "" col2 = ptcolors[col2] if col2 and ptcolors[col2] else "" print_formatted_text(to_formatted_text(text1, col1), end='') if text2: print_formatted_text(to_formatted_text(sep), end='') print_formatted_text(to_formatted_text(text2, col2), end='') print('', end=end) else: if not text2: print(text1, end=end) else: print(text1, end='') print(sep, end='') print(text2, end=end) linecount = 1 if text1: linecount += text1.count('\n') if end: linecount += end.count('\n') if sep: linecount += sep.count('\n') if text2: linecount += text2.count('\n') return linecount