Package: chatterstack
Chatterstack is a dead simple and intuitive way to handle the "conversation" variables used by the ChatGPT API, while also giving you advanced functionality.🛠️ SetupInstall package from pip:pipinstallchatterstackimport & initialization:There are two options for Chatterstack. The Base Library, and the Advanced Library, which extends the Base.importchatterstack# The base library if you are only really concerned about mananging# the conversation variable, it's order, and maybe tracking tokens.convo=chatterstack.Chatterstack()# The advanced library allows you issuing commands from the chat input,# gives the bot the ability to reach out to you with reminders, etcconvo=chatterstack.ChatterstackAdvanced()Overview: Chatterstack Base Class🤔 The  ProblemThe ChatGPT APIs use a "conversation" variables to keep track of your interactions with the bot. This is a list of dictionaries, with each dictionary representing a message.This leaves your program being littered with things like this:# append the bot's response to your conversationconversation.append({'role':response.choices[0].message.role,'content':response.choices[0].message.content})# to print the last messageprint(conversation[-1]["content"])If you want to do anything more advanced like "keep conversation variable to 8 messages max", or "make the System message always be the most recent message in the conversation"... well, I'll spare you the code snippet for those.But,Since this conversation variable is just made short, structured and highly predictable dictionaries......this means we can actually abstract away a lot of this mess in a way that is much more intuitive to use - while also keeping basically all of the inherent flexibility! That is a rare thing to be able to do.look:This is a fully functional chatbot program using chatterstack:importchatterstackimportosos.environ['OPENAI_API_KEY']='YOUR_API_KEY_HERE'convo=chatterstack.Chatterstack()whileTrue:convo.user_input()convo.send_to_bot()convo.print_last_message()That's the whole thing!Here's what that conversation looks like:USER: hey, can you help me with something?

ASSISTANT: Sure! What would you like help with?

USER: I need to know if France has a President or a Prime Minister

ASSISTANT: France has both a President and a Prime Minister, [... bot goes on]But still flexibleThis library is built to beintuitive and flexible, so you can easily change the behavior or look, in many ways, whatever suits your needs. Here's some basic examples:whileTrue:convo.user_input(prefix="ME: ")# change any of the API argumentsconvo.send_to_bot(model="gpt-4",temperature=1,max_tokens=40)# change the line spacingconvo.print_last_message(prefix="GPT: ",lines_before=0,lines_after=2)# and let's have it print the total tokens after each turnprint(convo.tokens_total_all)Now your conversation is with GPT-4, and conversation looks like this, with token counts:ME: hey, can you help me with something?
GPT: Of course! I'm here to help. Please let me know what you need assistance with, and I'll do my best to help you.

28

ME: I need to know if France has a President or a Prime Minister
GPT: France has both a President and a Prime Minister. The President of France is [...bot goes on]

87There is more info about the current defaults and various methods to change them below in the section about sending messages to the API📨 Getting InputLet's look at the fundamentals first.Even if you plan to use the Advanced Library, I highly recommend you read the whole README in order, starting here.Theuser_input()method is the same as the pythoninput()method, except it also automatically appends the user input in a correctly-formatted dict to your conversationconvo.user_input()As seen above, this method defaults to prompting the user with "USER: ", but you can change it to whatever you'd likeconvo.user_input("Ask the bot: ")Ask the bot:📨 Adding MessagesMaybe you aren't collecting your input from the terminal.Or maybe you want alter the input before appending it.There are several ways to take string variables and add them to the conversation as a correctly formatted dict:# A string you did some formatting onformatted_message="I want this all to be in LOWER case".lower()# Use the .add() method. Pass it the role, then the contentconvo.add("user",formatted_message)# or use the role-specific methods & just pass the contentconvo.add_user(formatted_message)convo.add_assistant("I'm a manually added assistant response")convo.add_system("SYSTEM INSTRUCTIONS - you are a helpful assistant who responds only in JSON")There is also .insert() if you want to add a message at a specific index, instead of the end of the conversation:# Here's the formatconvo.insert(index,role,content)# And an exampleconvo.insert(4,"system","IMPORTANT: Remember to not apologize to the user so much")💌 Sending messages to the APIThe chatterstack "send_to_bot" method is a standard OpenAI API call, but it's simpler to use and does a bunch of handy stuff for you in the background. Call it like this:convo.send_to_bot()That's it!It will take care of passing all the default values for you, as well as appending the response to your conversation. It also keeps token counts for you (and in the advanced class, much more)Changing the defaults for the send_to_bot() method:By default, chatterstack uses these values:model="gpt-3.5-turbo",temperature=0.8,top_p=1,frequency_penalty=0,presence_penalty=0,max_tokens=200There are several ways to change these, depending on what is convenient to you.The most obvious way is just to pass them as arguments when you make the call. For instance, if you wanted GPT-4 and 800 max tokens:convo.send_to_bot(model="gpt-4",max_tokens=800)This approach is great when you want to make just one call with some different values.But if you know you want different values all the time, you can define them in caps at the top of your file, and initialize chatterstack using theglobals()dict, like this:MODEL="gpt-4"TEMPERATURE=0.6FREQUENCY_PENALTY=1.25MAX_TOKENS=500# initialize with 'globals()'convo=chatterstack.Chatterstack(user_defaults=globals())# and now you can just call it like this againconvo.send_to_bot()Finally, if you want to just use the boilerplate OpenAI call, you can still do that! Just pass it the .list attribute of your Chatterstack:response=openai.ChatCompletion.create(model="gpt-3.5-turbo",messages=convo.list,# <--- right heretemperature=0.9,top_p=1,frequency_penalty=0,presence_penalty=0,max_tokens=200,)📂 Accessing and Printing MessagesSuper Simple:# Print the "content" of the last messageconvo.print_last_message()Or, if you wanted to do formatting on the string first...# This represents/is the content of the last messageconvo.last_message# So you can do stuff like this:message_in_caps=convo.last_message.upper()# print the message in all upper caseprint(message_in_caps)🪙 What About Tokens?oh yeah. We're keeping track of tokens.# See the tokens used on the last API callself.last_call_prompt_tokensself.last_call_full_context_prompt_tokensself.last_call_completion_tokensself.last_call_tokens_all# At any time, check the totals for the whole conversation so farself.prompt_tokens_totalself.assistant_tokens_totalself.tokens_total_all⤵️ List ManipulationVarious methods are available to manipulate the order of the conversation, here are a few:# Insert message at a specified indexconvo.insert(index,role,content)# Remove N messages from the end of the listconvo.remove_from_end(count)# Remove N messages from the start of the listconvo.remove_from_start(count)But much more importantly - methods for fine-grained control over the system message.System messages are usually used for instructions, and often it can be helpful to have the instructions appear more "recently" in the conversation. Which means tracking and moving this message, with out disrupting the others.# move your system message to be the most recent message in convoconvo.move_system_to_end()# or second to last, etcconvo.move_system_to_end(minus=1)And my personal favorite - basically the whole reason I wrote this entire library -convo.set_system_lock_index(-1)Passing it a positive value to this function will lock the system message to the index. Anytime messages are added, removed, or re-ordered, it will make sure the system message stays at that position (or as close to it as possible).Passing it a negative value will lock your system message to the index counting from the end of the conversation (the example above would make it always be the second to last message in the conversation).NOTE: Currently, these methods assume that you only have one system message📊 Track and Debug Your ConversationPrint a formatted version of your conversation (great for debugging)convo.print_formatted_conversation()By default, prints like this:System: You are a helpful assistant.
User: hi!
Assistant: Hi! How can I help you?see the overall stats of your conversation.convo.summary()SUMMARY:
{'total_messages': 2, 'prompt_tokens': 200, 'assistant_tokens': 78, 'total_tokens': 278}Advanced LibraryChatterstack has much more functionality built-in, and is easily extensible.RemindersYou can now tell the bot "remind me to take the take the garbage out at 8pm", or "remind me to take the garbage out in an hour"USER: hey, can you remind me to take the garbage out in an hour

ASSISTANT: Sure! I'll send you a reminder in an hour.

{...time passes...}

ASSISTANT: Hey! Just a quick reminder to take the garbage out!The bot can keep track of as many reminders at you want to issue.Issuing CommandsIssue commands from the user input:# saves the conversation to a txt file

USER: [save]

# quit the program

USER: [quit]By default you issue commands by using [ and ] as delimiters. But you can change these to whatever you want:convo.open_command = "{{"
convo.close_command = "}}"you can also self-call any method (or set any attribute) of the chatterstack class itself, right from the chat interface:# if you want to see what is currently in the conversation history
USER: [print_formatted_conversation]

# or how many tokens you have used so far
USER: [print_total_tokens]

# you can even pass arguments to commands
USER: [set_system_lock_index(-2)]So, while the initial chat program example at the start of this repo may have seemed simplistic at first, you can see that it's really all you need, as almost any functionality you want can actually be called from inside the chat itself.Extra Advanced: Adding your own commandsIf you want to write your own commands, chatterstack provides a simple interface class to do so, calledICommand.classICommand:defexecute(self):passBasically, you write your command as a class, which inherits from theICommandclass, and has an "execute" method (which is what you want to actually happen when your command gets called.)Here is an example:classExampleCommand(ICommand):defexecute(self):print("An example command that prints this statement right here.")If your command needs arguments, you can also add an__init__method, and pass it*argsexactly like this:classExampleCommand(ICommand):def__init__(self,*args):self.args=argsdefexecute(self):print("Example command that print this statement with this extra stuff:",self.args)The last thing you need to do is assign your command a trigger word or phrase by adding it to theinitmethod in the ChatterstackAdvanced class.classChatterstackAdvanced(Chatterstack):def__init__(self,...)# ...Lots of other code here...# This command is already in the class:self.command_handler.register_command('save',SaveConversationCommand(self))# Add your new commandself.command_handler.register_command("example",ExampleCommand)JavascriptThere is currently a Javascript version of Chatterstack, butt I have not made it available yet because I don't know Javascript as well, and am not so confident in it's dependability. If you do know Javascript and would like to help, please let me know!