#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket::INET;
use JSON::XS;
use Types::Serialiser;
use URI;
use LWP::UserAgent;
use HTTP::Cookies;
use Data::Dumper;
use String::ShellQuote;
use IPC::Open2;
use IO::Select;
use Encode;
use Storable;

# now using local llama.cpp server.cpp HTTP API started on the same computer like,
#superkuh@bob:~/app_installs/llama.cpp-2023-10-21/llama.cpp/build/bin$ ./server -m /home/superkuh/app_installs/llama.cpp/models/collectivecognition-v1.1-mistral-7b.Q4_K_M.gguf -c 2048 --port 8080 --threads 1 --n-gpu-layers 42

# irc chat bot that pipes the comments from IRC to large language models and back.
# in channel you have to talk to it by saying it's nickname. in private message you don't. pm whitelist is users in channel.

# durrrr
my $debugon = 1;
#my $webserverdirectorypath = '/home/superkuh/limbo/www/dalle'; # no trailling slash, I add it in use to separate.
my $webserverdirectorypath = '/home/superkuh/limbo/www/imgai'; # no trailling slash, I add it in use to separate.
#my $command = '/home/superkuh/app_installs/llama.cpp-2023-10-15/llama.cpp/build/bin/llava';
#my $command = '/home/superkuh/app_installs/llama.cpp-2023-10-21/llama.cpp/build/bin/llava'; # current requires llava.cpp change //printf("prompt: '%s'\n", params.prompt.c_str());
my $command = '/home/superkuh/app_installs/llama.cpp-2023-10-31/llama.cpp/build/bin/llava'; # current requires llava.cpp change //printf("prompt: '%s'\n", params.prompt.c_str());

# CLIP+llama2-7B-chat
my $imagemodel = '/home/superkuh/app_installs/llama.cpp/models/llava-1v5-7b-ggml-model-q5_k.gguf';
my $mmproj = '/home/superkuh/app_installs/llama.cpp/models/mmproj-model-f16.gguf';
# CLIP+Mistral-7B
#my $imagemodel = '/home/superkuh/app_installs/llama.cpp/models/bakllava-q4_k.gguf';
#my $mmproj = '/home/superkuh/app_installs/llama.cpp/models/bakllava-mmproj-model-f16.gguf';
# CLIP+obsidian-3B
#my $imagemodel = '/home/superkuh/app_installs/llama.cpp/models/obsidian-q6.gguf';
#my $mmproj = '/home/superkuh/app_installs/llama.cpp/models/mmproj-obsidian-f16.gguf';
### old
my $pid = "dummyplaceholder";
my $out = "dummyplaceholder";
my $in = "dummyplaceholder";
my $program = "dummyplaceholder";
my $select = "dummyplaceholder";
my $timeout = "dummyplaceholder";

# OpenAI API credentials
my $openai_api_key = 'SETECASTRONOMY';
# history of last handful of messages as "memory" | @ no longer used, % used.
my @history;
# separate histories for each user/channel
my %userhistory;
if (-e 'userhistory') {%userhistory = %{retrieve('userhistory')};}
my $historylength = 9;
my $maxhistorylength = 100;
my $gpt35turboswitch = 1;
#my $primedirective;
my $alpacaswitch = 0;
my $preprompthistoryhasbeenset = 0;
my $npredict = 400;
my @logitbiases;
#my $json_file = "tokenizer.json"; # https://huggingface.co/ICBU-NPU/FashionGPT-70B-V1.1/raw/main/tokenizer.json
my $json_file = "mistral-tokenizer.json"; # https://huggingface.co/teknium/CollectiveCognition-v1.1-Mistral-7B/resolve/main/tokenizer.json
#my $json_file = "openchat35-tokenizer.json"; # https://huggingface.co/openchat/openchat_3.5/resolve/main/tokenizer.json
my $globalstoptoken = "\n";

# IRC server settings
my $irc_server = 'irc.libera.chat';
#my $irc_server = 'irc.p2p-network.net';
my $irc_port   = 6667;
my $irc_nick   = 'yournicknamehere';
my $irc_channel = '##llm'; # the default always-join single channel
my %joinedchannels;
# 1 is the auto-join channel ($irc_channel) so set to 1 to not join again when looping through %joinedchannels keys
# set = 0 to auto-join channel. Setting to 1 will make the script think it's already in that channel.
$joinedchannels{"$irc_channel"} = 1;
$joinedchannels{"##misc"} = 0;
$joinedchannels{"###misc"} = 0;
$joinedchannels{"##OpenAI"} = 0;
$joinedchannels{"#misc2"} = 0;
$joinedchannels{"#misc3"} = 0;
$joinedchannels{"##misc-offtopic"} = 0;
$joinedchannels{"#fmisc"} = 0;
$joinedchannels{"##llm-bots"} = 0;

# IRC credentials
my $username = 'yournicknamehere';
#my $username = 'botsuperkuh';
my $password = 'SETECASTRONOMY';
#my $password = '';
my $ircmessagemaxlength = 432;
my %pmwhitelist = ();
my $lamebottrigger = "\!lamebot";
my $llamatrigger = "\!llama";
my $multi = "";

#########################
#########################

my $prepromptfile = '/home/superkuh/tests/preprompt.txt';
my $primedirective = '';
if (open my $file_handle, '<', $prepromptfile) {
    while (<$file_handle>) {
        $primedirective .= $_;
    }
    close $file_handle;
} else {
    die "Error: Unable to open file '$prepromptfile': $!";
}

if ($debugon) {
	print "\n<primedirective>$primedirective";
	print "</primedirective>\n";
}

#########################
#########################


# Connect to IRC server
my $sock = IO::Socket::INET->new("$irc_server:$irc_port") or die "Can't connect to IRC server: $!";
print "Connected to IRC server\n";

# Send nick and user commands
print $sock "NICK $irc_nick\r\n";
#print $sock "USER $irc_nick 8 * :superkuh made Perl IRC bot wrapper for llama 7B and openai API\r\n";
print $sock "USER $irc_nick 8 * :superkuh.com/llamar.html LLM AI Bot\r\n";

# Log in to the account
print $sock "PRIVMSG nickserv :IDENTIFY $username $password\r\n" if $password;

# Join the specified channel
print $sock "JOIN $irc_channel\r\n";
print "JOIN $irc_channel\r\n" if $debugon;
# Join all the other channels
foreach my $key (keys %joinedchannels) {
	if ($joinedchannels{$key} == 0) {
		print $sock "JOIN $key\r\n";
		print "JOIN $key\r\n" if $debugon;
		$joinedchannels{$key}++;
		}
}


my $stupidworkaroundforp2pnet = 0;
# Loop to receive and respond to messages
while (my $line = <$sock>) {
	print "line: $line\n" if $debugon;
	## Respond to PING requests
	#line: PING :tantalum.libera.chat
	if ($line =~ /^PING(.*)$/i) {
		print $sock "PONG $1\r\n";
		print "PONG $1\r\n" if $debugon;
		
		# wait till the first ping/pong so the client is registered with the server before trying to do anything
		# I guess I'll just copy all the channel join stuff here.
		if ($stupidworkaroundforp2pnet < 1) {
			print "stupidworkaroundcounter: $stupidworkaroundforp2pnet\n" if $debugon;
			# Join the specified channel
			print $sock "JOIN $irc_channel\r\n";
			print "JOIN $irc_channel\r\n" if $debugon;
			print $sock "PRIVMSG nickserv IDENTIFY $username $password\r\n" if $password;
			$stupidworkaroundforp2pnet++;
			
			foreach my $key (keys %joinedchannels) {
			    if ($joinedchannels{$key} == 0) {
			    	print $sock "JOIN $key\r\n";
			    	print "JOIN $key\r\n" if $debugon;
			    	$joinedchannels{$key}++;
			    }
			}
		}
		
		next;
	}

	## respond to private messages
	# private message line: :superkuh!~superkuh@user/superkuh PRIVMSG bullshitbot :this is a test.
	#		  line: :lury!~lury@user/lury PRIVMSG bullshitbot :bullshitbot, hello
	# [^!]+ matches one or more characters that do not include an exclamation point.
	# [^@]+ matches one or more characters that are not an at sign (@).
	# [^ ]+ matches one or more characters that are not a space. 
	if ($line =~ /^:([^!]+)![^@]+\@[^ ]+ PRIVMSG $irc_nick :(.+)/) {
		my $usernick = $1; # here in PM $usernick is used for sending and important stuff
		my $message = $2;
		my $thingtosendtoonirc = $usernick; # private message to usernick

		if ($pmwhitelist{lc($usernick)}) {

			if ($message =~ /^!join\s(.*)/){
				my $newchannel = $1;
				chomp($newchannel);
				
				# Join the specified channel
				print $sock "JOIN $newchannel\r\n";
				print "JOIN $newchannel\r\n" if $debugon;
	
				print $sock "PRIVMSG $thingtosendtoonirc :joining channel: $newchannel\r\n";
				print "joining channel: $newchannel\n" if $debugon;
				$joinedchannels{$newchannel}++;
				next;
			}
			
			if ($message =~ /^!part\s(.*)/){
				my $newchannel = $1;
				chomp($newchannel);
				
				# Join the specified channel
				print $sock "PART $newchannel\r\n";
				print "PART $newchannel\r\n" if $debugon;
	
				print $sock "PRIVMSG $thingtosendtoonirc :leaving channel: $newchannel\r\n";
				print "leaving channel: $newchannel\n" if $debugon;
				$joinedchannels{$newchannel}--;
				next;
			}

			# Gotta fix this so that it clears the most recent lines. It's clearing the oldest.
			if ($message =~ /^!memorywipe\s?(\d)?/){
				my $num_lines_to_remove = $1;
				print "linestoremove: $num_lines_to_remove .\n";
				
#				if ($num_lines_to_remove =~ /\d/) {
#					my $array_ref = $userhistory{$thingtosendtoonirc};
#					# Remove $num_lines_to_remove lines from the beginning of the array
#					splice @$array_ref, 0, $num_lines_to_remove;
#					print $sock "PRIVMSG $thingtosendtoonirc :memory wiped: $num_lines_to_remove lines removed.\r\n";
#					next;
#				}
				
			if ($num_lines_to_remove) {
				my $array_ref = $userhistory{$thingtosendtoonirc};
				
   				for (1 .. $num_lines_to_remove) {
					pop @$array_ref;
				}
				print $sock "PRIVMSG $thingtosendtoonirc :memory wiped: $num_lines_to_remove lines removed.\r\n";
				next;
			}	
			
				delete $userhistory{$thingtosendtoonirc};
				#addtomessagehistory(" ", $thingtosendtoonirc);
				print $sock "PRIVMSG $thingtosendtoonirc :memory wiped\r\n";
				print "memory wiped\n" if $debugon;
				next;
			}

			#line: :superkuh!~superkuh@user/superkuh PRIVMSG ##outhouse :!memorysize 7
			#but what about $message, not $line
			if ($message =~ /^!memorysize\s(\d+)/){
				my $memorysize = $1;
				if ($memorysize <= $maxhistorylength) {
					$historylength = $memorysize;
				}
				print "historylength set to: $historylength\n" if $debugon;
				print $sock "PRIVMSG $thingtosendtoonirc :historylength set to: $historylength\r\n";
				next;
			}

			if ($message =~ /^!tokenize\s(.+)/){
				my $tokens = $1;
				my $tokenids = send_to_local_llama_servercpp_api_tokenize($tokens);
	
				print $sock "PRIVMSG $thingtosendtoonirc :Token IDs in order: $tokenids\r\n";
				print "Token IDs in order: $tokenids\n" if $debugon;
				next;
			}
			
			if ($message =~ /^!npredict\s(\d+)/){
				my $npredictmatch = $1;
				$npredict = $npredictmatch;
	
				print $sock "PRIVMSG $thingtosendtoonirc :npredict length: $npredict\r\n";
				print "n_predict length: $npredict\n" if $debugon;
				next;
			}
			
			
			if ($message =~ /^!alpaca/){
				if ($alpacaswitch) {
					$alpacaswitch = 0;
				} else {
					$alpacaswitch = 1;
				}

				my $alpacastatus = "off";
				$alpacastatus = "on" if $alpacaswitch;

				print "alpaca instruction mode set to: $alpacastatus/$alpacaswitch\n" if $debugon;
				print $sock "PRIVMSG $thingtosendtoonirc :alpaca instruction mode set to: $alpacastatus/$alpacaswitch\r\n";
				next;
			}

			#if ($message =~ /\!primedirective\s?(\w+)/){
			if ($message =~ /^!primedirective\s(.+)/){
				my $primed = $1;
				print "primed: $primed\n" if $debugon;

				if ($primed eq "wipe") {
					undef $primedirective;
					print "Prime directive: wiped\n" if $debugon;
					next;
				} elsif ($primed =~ /(https?\:\/\/\S+)/gi) {
					my $preprompturl = $1;
					#my $page     = pullpage("$preprompturl");
					my $page     = pullpage2("$preprompturl");
					#$page = substr($page, 0, 1600);
					$primedirective = $page;
					print "Update PD from HTTP: $primedirective\n";
					print $sock "PRIVMSG $thingtosendtoonirc :Updated pre-prompt/primedirective.\r\n";
					next;
				}
				
				$primedirective = $primed;
				print $sock "PRIVMSG $thingtosendtoonirc :Got it.\r\n";
				print "Prime directive: $primedirective\n" if $debugon;
				next;
			}

			#if ($message =~ /\!lamebot/) {
			if ($message =~ /$lamebottrigger/) {
#GOOD				takemessagesendtoopenaiandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 1);
				takemessagesendtoopenaiandsendthatresponseout_notstupid($message, $thingtosendtoonirc, $usernick, 1);
			} elsif ($message =~ /$llamatrigger/) {
				#takemessagesendtolocalllamaandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 0);
				takemessagesendtolocalllamaAPIandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 0);		
			} else {
#GOODOLDOLD			#takemessagesendtoopenaiandsendthatresponseout($message, $thingtosendtoonirc, $usernick);
				# force chatgpt3.5-turbo to save free trial costs
#GOOD				takemessagesendtoopenaiandsendthatresponseout($message, $thingtosendtoonirc, $usernick, $gpt35turboswitch);
				#takemessagesendtoopenaiandsendthatresponseout_notstupid($message, $thingtosendtoonirc, $usernick, $gpt35turboswitch);
				# all llama all the time
				takemessagesendtolocalllamaAPIandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 0);		
			}
		} else {
			print $sock "PRIVMSG $thingtosendtoonirc :Ask superkuh to put you on the private message whitelist.\r\n";
			print "PRIVMSG $thingtosendtoonirc :Ask superkuh to put you on the private message whitelist.\r\n" if $debugon;	
		}
		#takemessagesendtoopenaiandsendthatresponseout($message, $thingtosendtoonirc, $usernick);
		next; # if it was a private message and it's responded don't bother checking if it's a channel message
	}

	## respond to public messages in a room.
	# public message #line: :superkuh!~superkuh@user/superkuh PRIVMSG ##1111 :ALL DOOOMED NoButty AND bullshitbot
	#                #line: :superkuh!~superkuh@user/superkuh PRIVMSG #1111 :llamarr, what is the angular diameter of the moon?
	#                #line: :superkuh!~superkuh@user/superkuh PRIVMSG ##llm :llamarr, what is the capital of Texas?
	#if ($line =~ /.+ PRIVMSG $irc_channel :(.+)/) { # maybe eventually support multiple channels, but not now.
	if ($line =~ /.+ PRIVMSG (#+[\w_-]+) :(.+)/) { # support multiple channels.      
		my $anyircchannel = $1;
		my $message = $2;
		
		#print Dumper("1|$message|") if $debugon;
		# FUUUUUUCCCCKKKK invisible control characters making ^ACTION never match
		$message =~ s/[\p{Cc}\p{Cf}]//g; # remove invisible text
		$message =~ s/\[0m\[0m>//g; # remove now visible remainder invisible text
		#print Dumper("2|$message|") if $debugon;
			
		# handle ctcp actions /me, sort of.
		# line: :superkuh!~superkuh@user/superkuh PRIVMSG ##space-offtopic :ACTION asks llamar what 2+2=?
		#print "shit's fucked: probably: $message\n" if $debugon;
		if ($message =~ /^\s*ACTION.+/) {
			$message =~ s/^\s*ACTION/I/;
			#			$message =~ s/^\s*ACTION/$nick/;   # 
			print "swapping I for ACTION: $message\n" if $debugon;
		}
		#print "shit's fucked: probably2: $message\n" if $debugon;
		
		
		print "anyircchannel: $anyircchannel\n" if $debugon;
		#my $thingtosendtoonirc = $irc_channel; # public message to channel
		my $thingtosendtoonirc = $anyircchannel; # public message to channel

		# here in public chat $usernick is used only for history, it's sent to $irc_channel instead.
		my ($usernick, $userandhostandeverythingelse) = split '!', $line, 2;
		$usernick =~ s/^://;
		print "usernick: $usernick\n" if $debugon;
		next if $usernick eq "nsprra"; # banned

#		if ($message =~ /^!memorywipe/){
#			delete $userhistory{$thingtosendtoonirc};
#			addtomessagehistory(" ", $thingtosendtoonirc);
#		}

		#if ($message =~ /\!memorywipe/){
		# line: :superkuh!~superkuh@user/superkuh PRIVMSG ##llm :!memorywipe 2
		if ($message =~ /^!memorywipe ?(\d)?/){
			my $num_lines_to_remove = $1;
			print "linestoremove: $num_lines_to_remove .\n";
			
#			if ($num_lines_to_remove =~ /\d/) {
#				my $array_ref = $userhistory{$thingtosendtoonirc};
#				# Remove $num_lines_to_remove lines from the beginning of the array
#				splice @$array_ref, 0, $num_lines_to_remove;
#				print $sock "PRIVMSG $thingtosendtoonirc :memory wiped: $num_lines_to_remove lines removed.\r\n";
#				next;
#			}
			
			if ($num_lines_to_remove) {
				my $array_ref = $userhistory{$thingtosendtoonirc};
				
   				for (1 .. $num_lines_to_remove) {
					pop @$array_ref;
				}
				print $sock "PRIVMSG $thingtosendtoonirc :memory wiped: $num_lines_to_remove lines removed.\r\n";
				next;
			}			
			
			delete $userhistory{$thingtosendtoonirc};
			#addtomessagehistory(" ", $thingtosendtoonirc);
			print $sock "PRIVMSG $thingtosendtoonirc :memory wiped\r\n";
			print "memory wiped\n" if $debugon;
			next;
		}

		if ($message =~ /^!togglestop/) {
			if ($globalstoptoken eq "\n") {
				print "stop token from 1 newline to 3 newlines: current is: " . Dumper($globalstoptoken) if $debugon;
				$globalstoptoken = "\n\n\n";
				print "just set is: " . Dumper($globalstoptoken) if $debugon;
				print $sock "PRIVMSG $thingtosendtoonirc :stop on newline disabled\r\n";
			} else {
				print "stop token from 3 newline to 1 newlines: current is: " . Dumper($globalstoptoken) if $debugon;
				$globalstoptoken = "\n";
				print "just set is: " . Dumper($globalstoptoken) if $debugon;
				print $sock "PRIVMSG $thingtosendtoonirc :stop on newline set\r\n";
			}
		}

		if ($message =~ /^!replace (.+)?/){
			my $replacementmessage = $1;
			print "replacementline: $replacementmessage\n";
			my $num_lines_to_remove = "1";
			my $array_ref = $userhistory{$thingtosendtoonirc};
			## Remove $num_lines_to_remove lines from the beginning of the array
			#splice @$array_ref, 0, $num_lines_to_remove;
			# Remove $num_lines_to_remove lines from the end of the array
    		splice @$array_ref, -$num_lines_to_remove;
			print $sock "PRIVMSG $thingtosendtoonirc :last response replaced.\r\n";
			# replace the removed response
			addtomessagehistory("$replacementmessage", $thingtosendtoonirc);
			next;	
		}
		
		#if ($message =~ /\!save (.+)?/){
		if ($message =~ /^!save ([\w\d-]+)/){
			my $savename = $1;
			print "savename: $savename\n" if $debugon;
			store \%userhistory, $savename;
			print $sock "PRIVMSG $thingtosendtoonirc :Saved history state as $savename.\r\n";
			next;	
		}
		if ($message =~ /^!load ([\w\d-]+)/){
			my $loadname = $1;
			print "loadname: $loadname\n" if $debugon;
			if (-e $loadname) {
				%userhistory = %{retrieve($loadname)};
			} else {
					print $sock "PRIVMSG $thingtosendtoonirc :No save with name $loadname.\r\n";
					next;	
			}
			print $sock "PRIVMSG $thingtosendtoonirc :Loaded \"$loadname\".\r\n";
			next;	
		}
		
		if ($message =~ /^!imagemodel\s(.+)?/i) {
		# try to make it handle just \s modelname and just return the models with bare !imagemodel matching
			my $model = $1;
			if ($model) {
				if ($model =~ /llama/) { 			# CLIP+llama2-7B-chat
					$imagemodel = '/home/superkuh/app_installs/llama.cpp/models/llava-1v5-7b-ggml-model-q5_k.gguf';
					$mmproj = '/home/superkuh/app_installs/llama.cpp/models/mmproj-model-f16.gguf';
					print $sock "PRIVMSG $thingtosendtoonirc :Set to: $imagemodel\r\n";
					print "PRIVMSG $thingtosendtoonirc :Set to: $imagemodel\r\n" if $debugon;
				}
				elsif ($model =~ /mistral/)	{		# CLIP+Mistral-7B
					$imagemodel = '/home/superkuh/app_installs/llama.cpp/models/bakllava-q4_k.gguf';
					$mmproj = '/home/superkuh/app_installs/llama.cpp/models/bakllava-mmproj-model-f16.gguf';
					print $sock "PRIVMSG $thingtosendtoonirc :Set to: $imagemodel\r\n";
					print "PRIVMSG $thingtosendtoonirc :Set to: $imagemodel\r\n" if $debugon;
				}
				elsif ($model =~ /obsidian/) {			# CLIP+obsidian-3B
					$imagemodel = '/home/superkuh/app_installs/llama.cpp/models/obsidian-q6.gguf';
					$mmproj = '/home/superkuh/app_installs/llama.cpp/models/mmproj-obsidian-f16.gguf';	
					print $sock "PRIVMSG $thingtosendtoonirc :Set to: $imagemodel\r\n";
					print "PRIVMSG $thingtosendtoonirc :Set to: $imagemodel\r\n" if $debugon;
				} else {
					print $sock "PRIVMSG $thingtosendtoonirc :Not recognized. Available: llama, mistral, obsidian. Current model is: $imagemodel\r\n";
					print "PRIVMSG $thingtosendtoonirc :Not recognized. Available: llama, mistral, obsidian. Current model is: $imagemodel\r\n" if $debugon;
				}			
			} else {
				# send message saying model not found and what the current one is
				print $sock "PRIVMSG $thingtosendtoonirc :Not recognized. Available: llama, mistral, obsidian. Current model is: $imagemodel\r\n";
				print "PRIVMSG $thingtosendtoonirc :Not recognized. Available: llama, mistral, obsidian. Current model is: $imagemodel\r\n" if $debugon;
			}
				
		}
		
		if ($message =~ /^!image\s(https?:\/\/\S+)\s?(.+)?/i) {
			my $query = "";
			my $imageurl = $1;
			my $imguri = URI->new($imageurl);
			$query = $2;
			my $usersetquery = 0;
			my $output;
			

			unless ($query) {
				$query = "Describe the image in detail.";
			}
			$query = shell_quote($query); # has '' surrounding it, have to remove them.
			# Remove the first character
			$query = substr($query, 1);
			#$query =~ s/^\'//;
			# Remove the last character
			$query = substr($query, 0, -1);
			#$query =~ s/\'$//;
			
			# don't add it yet, what if the image GET fails? Then no response will be added?
			addtomessagehistory("$query $imguri", $thingtosendtoonirc);
			
			print "imageurl: $imageurl, imguri: $imguri, query: $query\n" if $debugon;
			my $timestamp = $$.time;
			
			my $extension;
			my $baseimagefilename;
			#if ($imguri =~ /\.([\w]+)$/) {
			if ($imguri =~ /^(.+)\.([\w]+)$/) {
				$baseimagefilename = $1;
				$extension = $2;
				print "File base: $baseimagefilename\nFile extension: $extension\n" if $debugon;
			} else {
				print "No file extension found in the URL.\n" if $debugon;
				$extension = 'png';
			}
			
				
			## no cookies, no image from some.
			#my $imageua = LWP::UserAgent->new;
			#my $resp = $imageua->mirror($imageurl, "$webserverdirectorypath/$timestamp.png");
			#unless($resp->is_success) {
			#	print $resp->status_line;
			#	my $urlgeterror =  "Error: " . $resp->status_line;
			#	print $sock "PRIVMSG $thingtosendtoonirc :Error: $urlgeterror\r\n";
			#}
			
			# do cookies, impersonate browser somewhat
			my $imageua = LWP::UserAgent->new;
			my $cookie_jar = HTTP::Cookies->new(
			    file     => "$webserverdirectorypath/cookies.txt",
			    autosave => 1
			);
			$imageua->agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36");
			$cookie_jar->load();
			$imageua->cookie_jar($cookie_jar);
			#my $resp = $imageua->mirror($imageurl, "$webserverdirectorypath/$timestamp.png");
			my $resp = $imageua->mirror($imageurl, "$webserverdirectorypath/$timestamp.$extension");
			$cookie_jar->save();
			unless($resp->is_success) {
				print $resp->status_line;
				my $urlgeterror =  "Error: " . $resp->status_line;
				print $sock "PRIVMSG $thingtosendtoonirc :Error: $urlgeterror\r\n";
			}

		
			#$output = `/home/superkuh/app_installs/llama.cpp-2023-10-15/llama.cpp/build/bin/llava 2>/dev/null -c 4096 -n 4096 -m /home/superkuh/app_installs/llama.cpp/models/llava-1v5-7b-ggml-model-q5_k.gguf --mmproj /home/superkuh/app_installs/llama.cpp/models/mmproj-model-f16.gguf --temp 0.1 -t 1 -ngl 64 -p '$query' --image $webserverdirectorypath/$timestamp.png`;
		
			# $imagemodel and $mmproj
			#my $input = "$webserverdirectorypath/$timestamp.png";
			my $input = "$webserverdirectorypath/$timestamp.$extension";
			#open my $output_fh, '-|', $command, '2>/dev/null', '-c', '4096', '-n', '4096', '-m',
#			open my $output_fh, '-|', $command, '-c', '4096', '-n', '4096', '-m',
			open my $output_fh, '-|', $command, '-c', '2048', '-n', '2048', '-m',
			   # '/home/superkuh/app_installs/llama.cpp/models/llava-1v5-7b-ggml-model-q5_k.gguf',
			    "$imagemodel",
			    #'--mmproj', '/home/superkuh/app_installs/llama.cpp/models/mmproj-model-f16.gguf',
			    '--mmproj', "$mmproj",
			    '--temp', '0.1', '-t', '1', '-ngl', '64', '-p', $query, '--image', $input
			    or die "Cannot execute $command: $!";
			$output = do { local $/; <$output_fh> };
			close $output_fh;

		
			#print $sock "PRIVMSG $thingtosendtoonirc :$output\r\n";
			print "llavaoutput: $output\n" if $debugon;
			my $actualdescription;
			#if ($output =~ /.+\sMB\n+(.+)\n+main.+/) {
			my $regex = qr/clip_model_load: total allocated memory: [\d.]+ MB\n\n(.*?)(?=main: image encoded)/s;
			if ($output =~ /$regex/g) {
				$actualdescription = $1;
				$actualdescription =~ s/\n/ /g;
				$actualdescription =~ s/^\s+//;
			} else {
				print $sock "PRIVMSG $thingtosendtoonirc :it failed again.\r\n";				
			}
			chomp $actualdescription;
			print "regexed image desc match: $actualdescription\n" if $debugon;
			
			## doesn't handle long messages so...
			#print $sock "PRIVMSG $thingtosendtoonirc :$actualdescription\r\n";
						
			if (length($actualdescription) > $ircmessagemaxlength) {
				my @irclengthmessages = cutintoirclengthmessagesandreturnarrayofthem($actualdescription);
				my $firstbitforthelog = $irclengthmessages[0];
				print $sock "PRIVMSG $thingtosendtoonirc :$_\r\n" for @irclengthmessages;
			} else {
				print $sock "PRIVMSG $thingtosendtoonirc :$actualdescription\r\n";
				print "PRIVMSG $thingtosendtoonirc :$actualdescription\r\n" if $debugon;
			}
			
			if ($actualdescription) {
				addtomessagehistory("$actualdescription", $thingtosendtoonirc);	
			} else {
				addtomessagehistory("it failed again.", $thingtosendtoonirc);	
			}		
		}

		#if ($message =~ /\!primedirective\s?(\w+)/){
		if ($message =~ /^!primedirective\s(.+)/){
			my $primed = $1;
			print "primed: $primed\n" if $debugon;
			if ($primed eq "wipe") {
				undef $primedirective;
				print "Prime directive: wiped\n" if $debugon;
				next;
			} elsif ($primed =~ /(https?\:\/\/\S+)/gi) {
				my $preprompturl = $1;
				my $page     = pullpage2("$preprompturl");
				$page = substr($page, 0, 1600);
				$primedirective = $page;
				print "Update PD from HTTP: $primedirective\n";
				print $sock "PRIVMSG $thingtosendtoonirc :Updated pre-prompt/primedirective.\r\n";
				next;
			}
			
			$primedirective = $primed;
			print $sock "PRIVMSG $thingtosendtoonirc :Got it.\r\n";
			print "Prime directive: $primedirective\n" if $debugon;
			next;
		}
		
		if ($message =~ /^!tokenize\s(.+)/){
			my $tokens = $1;
			my $tokenids = send_to_local_llama_servercpp_api_tokenize($tokens);

			print $sock "PRIVMSG $thingtosendtoonirc :Token IDs in order: $tokenids\r\n";
			print "Token IDs in order: $tokenids\n" if $debugon;
			next;
		}
		
		if ($message =~ /^!npredict\s(\d+)/){
			my $npredictmatch = $1;
			$npredict = $npredictmatch;
	
			print $sock "PRIVMSG $thingtosendtoonirc :npredict length: $npredict\r\n";
			print "n_predict length: $npredict\n" if $debugon;
			next;
		}
		
		
		#	#logit_bias  => \@logitbiases,
		##old	[15043,-1.0] [15044,0.5] [15045,-0.5]
		#new	15043,-1.0 15044,0.5 15045,-0.5
		if ($message =~ /^!logitbias\s?(.+)/){
			my $logitbiasmatch = $1;
			if ($logitbiasmatch =~ /^wipe/i) {
				#splice(@logitbiases, 0);
				@logitbiases = ();
				print $sock "PRIVMSG $thingtosendtoonirc :logitbiases wiped\r\n";
				next;
			} elsif ($logitbiasmatch =~ /^show/i) {
				#my $logitbias_string2 = join(" ", map { sprintf("[%d,%.1f]", $_->[0], $_->[1]) } @logitbiases);
				my $logitbias_string2 = join(' ', map { join(',', @$_) } @logitbiases);
				if ($logitbias_string2) {
					print $sock "PRIVMSG $thingtosendtoonirc :current logit bias: $logitbias_string2\r\n";
				} else {
					print $sock "PRIVMSG $thingtosendtoonirc :none set. format: 15043,-1.0 144,0.5 306,-0.5]\r\n";
				}
				next;
			}
						
			my @logit_bias_elements = split(/\s+/, $logitbiasmatch);
			print "\nlogit_bias_elements\n";
			print Dumper(\@logit_bias_elements) if $debugon;
			# hint that they're numbers not strings.
			@logitbiases = map { [split(/,/, $_)] } @logit_bias_elements;
			print "\nlogitbiases\n";
			print Dumper(\@logitbiases) if $debugon;
			@logitbiases = map { $_ + 0 } @logitbiases;
			#@logitbiases = map { [map { $_ + 0 } @$_, 0] } @logitbiases;
			
			# gotta hint that they're numbers otherwise the json encoding will fail and treat as strings
			@logitbiases = map {
    			my ($number, $value) = split(/,/, $_);
   				 #[$number + 0, $value];
   				 [$number + 0, $value + 0];
			} @logit_bias_elements;
			
			# gotta hint that they're numbers otherwise the json encoding will fail and treat as strings
			#@logitbiases = map {[ map { $_ eq 'false'?\0:0+$_ } @$_ ]} @logitbiases;
			#@logitbiases = map {[ map { $_ eq 'false' ? 0 : ($_ eq 'true' ? 1 : 0+$_) } @$_ ]} @logitbiases;
			
			print Dumper(\@logitbiases) if $debugon;
			my $logitbias_string = join(' ', map { join(',', @$_) } @logitbiases);
			
									
			#@logitbiases = map { my ($id, $value) = /\[([\d]+),([\d.-]+)\]/; [$id, $value] } split(/\s+/, $logitbiasmatch);
			#print Dumper(\@logitbiases) if $debugon;
			#my $logitbias_string = join(" ", map { sprintf("[%d,%.1f]", $_->[0], $_->[1]) } @logitbiases);
			
			print $sock "PRIVMSG $thingtosendtoonirc :logit bias: $logitbias_string\r\n";
			print "logit_bias: $logitbias_string\n" if $debugon;
			next;
		}
		
		if ($message =~ /^!alpaca/){
				if ($alpacaswitch) {
					$alpacaswitch = 0;
				} else {
					$alpacaswitch = 1;
				}
			my $alpacastatus = "off";
			$alpacastatus = "on" if $alpacaswitch;

			print "alpaca instruction mode set to: $alpacastatus/$alpacaswitch\n" if $debugon;
			print $sock "PRIVMSG $thingtosendtoonirc :alpaca instruction mode set to: $alpacastatus/$alpacaswitch\r\n";
			next;
		}

		if ($message =~ /^!memorysize\s(\d+)/){
			my $memorysize = $1;
			if ($memorysize <= $maxhistorylength) {
				$historylength = $memorysize;
			}
			print "historylength set to: $historylength\n" if $debugon;
			print $sock "PRIVMSG $thingtosendtoonirc :historylength set to: $historylength\r\n";
			next;
		}

		#if ($message =~ /$irc_nick/) {
		#if ($message =~ /$irc_nick/i or (rand() < 0.001)) { # small chance to respond without being mentioned
		if ($message =~ /$irc_nick/i) {
			
				if ($message =~ /!multi/i) {
					#print $sock "PRIVMSG $thingtosendtoonirc :Debug multi #0\r\n";
					if ($message =~ /!multi\^/i) { # if it's the last line
					    #print $sock "PRIVMSG $thingtosendtoonirc :Debug multi #1\r\n";
						$message =~ s/!multi\^//i; # remove trigger
						$message =~ s/\\n/\n/ig; # make newline markup actually newlines
						$message = $multi . $message; 
						# don't next and reset $multi
						$multi = "";
					} else {
						#print $sock "PRIVMSG $thingtosendtoonirc :Debug multi #2\r\n";
						$message =~ s/!multi//i; # remove trigger
						$message =~ s/\\n/\n/ig; # make newline markup actually newlines
						$multi = $multi . $message;
						next; # don't do anything else
					}
				}
		
				if ($message =~ /(good(\s+fuckin[\'g]?)?\s+(bo(t|y)|g([ui]|r+)rl))|(bot(\s|\-)?snack)/i) {
        			if (rand()  < .5)  {
        				print $sock "PRIVMSG $thingtosendtoonirc :thanks $usernick :)\r\n";
        				next;
        			} else {
        				print $sock "PRIVMSG $thingtosendtoonirc ::)\r\n";
        				next;
        			}
    			}
		
			#takemessagesendtoopenaiandsendthatresponseout($message, $thingtosendtoonirc, $usernick);
			#ran out of credits switch always gpt3.5-turbo to extend free trial
#GOOD			takemessagesendtoopenaiandsendthatresponseout($message, $thingtosendtoonirc, $usernick, $gpt35turboswitch);
				#takemessagesendtoopenaiandsendthatresponseout_notstupid($message, $thingtosendtoonirc, $usernick, $gpt35turboswitch);
				# all llama all the time
				takemessagesendtolocalllamaAPIandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 0);	
		#} elsif ($message =~ /\!lamebot/) {
		} elsif ($message =~ /$lamebottrigger/) {
#GOOD			takemessagesendtoopenaiandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 1);
			takemessagesendtoopenaiandsendthatresponseout_notstupid($message, $thingtosendtoonirc, $usernick, 1);
		} elsif ($message =~ /$llamatrigger/) {
				#takemessagesendtolocalllamaandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 0);	
				takemessagesendtolocalllamaAPIandsendthatresponseout($message, $thingtosendtoonirc, $usernick, 0);			
		}
		next;
	}

	## track the users in the joined channel to use as a %pmwhitelist for allowing PM responses
	#line: :superkuh!~superkuh@user/superkuh JOIN ##channel
	#line: :rtlrelay!~superkuh@log.hsd1.ca.att.net JOIN ##channel
	if ($line =~ /^:(\S+)!\S+\s+JOIN\s+\Q$irc_channel\E/) { # - no :, no end ($) anchor in case there's invisible chars?
	#if ($line =~ /^:(\S+)!\S+\s+JOIN\s+:\Q$irc_channel\E$/) {
		# Add user to list when they join
		my $user = lc $1;
		$pmwhitelist{$user} = 1;
		print "User $user joined the channel\n" if $debugon;
	} elsif ($line =~ /^:(\S+)!\S+\s+PART\s+\Q$irc_channel\E\s*/) {
		# Remove user from list when they leave
		my $user = lc $1;
		delete $pmwhitelist{$user};
		print "User $user left the channel\n" if $debugon;
	} elsif ($line =~ /^:(\S+)!\S+\s+QUIT\s*/) {
		# Remove user from list when they quit
		my $user = lc $1;
		delete $pmwhitelist{$user};
		print "User $user quit\n" if $debugon;
		
		
		
#	} elsif ($line =~ /^:(\S+)\s+353\s+\S+\s+\S+\s+\Q$irc_channel\E\s+:?(.*)$/) {
#	#:tantalum.libera.chat 353 bullshitbot @ #channel :bullshitbot person1 person2 person3 person4 person5 person6 person7 person8 person9 superkuh NoButty trebot
#		# Update user list when names are received
#		my @names = split /\s+/, $2;
#		foreach my $name (@names) {
#			$name =~ s/^[@+]//; # Remove channel mode prefix
#			$pmwhitelist{lc $name} = 1;
#		}
#		print "Got names list from channel\n" if $debugon;




	#                   :server  353   nick  =/@   chan   :nick1 nick2 nick3
	#                   :irc.net 353   bsbot =     #1111  :bsbot @superkuh
	#:irc.servercentral.net 353 bsbot = #1111 :bsbot @superkuh
	#:irc.servercentral.net 353 bsbot @ #channel :bsbot apos Tim Ars cant @in @^Tas^ @Pub fly f8 ket +Cat +Nik @to @superkuh
	#:tantalum.libera.chat 353 bullshitbot @ #channel :bullshitbot person1 person2 person3 person4 person5 person6 person7 person8 person9 superkuh NoButty
#GOODOLD	#} elsif ($line =~ /^:(\S+)\s+353\s+\S+\s+\S+\s+\Q$irc_channel\E\s+:?(.*)$/) {
#GOODOLD	#my @names = split /\s+/, $2;
	} elsif ($line =~ /^:\S+\s+353\s+\S+\s+\S+\s+(\S+)\s+:\S+\s+(.*$)/) {	# don't include self, I guess
		# Update user list when names are received
		my $tempnamesstring = $2;
		my $tempnchan = $1;
		print "tnsb4: 1: $1, 2: $2, $tempnamesstring\n" if $debugon;
		$tempnamesstring =~ s/[\@\+]//g; # remove efnet op/voiced/etc formatting
		print "tnsafter: $tempnamesstring\n" if $debugon;
		my @names = split /\s+/, $tempnamesstring;
		print "added: " if $debugon;
		foreach my $name (@names) {
			$name =~ s/^[@+]//; # Remove channel mode prefix
			print lc $name . " " if $debugon;
			$pmwhitelist{lc $name} = 1;
		}
		print "- got names list from $tempnchan\n" if $debugon;
			
	} elsif ($line =~ /^:([^!]+)![^@]+\@[^ ]+ NICK :(.+)$/) { # nickname changes
		# [^!]+ matches one or more characters that do not include an exclamation point.
		# [^@]+ matches one or more characters that are not an at sign (@).
		# [^ ]+ matches one or more characters that are not a space. 
		my $old_nick = lc $1;
		my $new_nick = lc $2;
		delete $pmwhitelist{$old_nick};
		$pmwhitelist{$new_nick} = 1;
		print "Nickname change: $old_nick -> $new_nick\n" if $debugon;
	}
}


sub takemessagesendtolocalllamaAPIandsendthatresponseout { # also sets the history/memory
	my ($message, $thingtosendtoonirc, $usernick, $gpt35turbo) = @_;
	unless ($usernick) {
		$usernick = $thingtosendtoonirc;
	}
	unless ($gpt35turbo) {
		$gpt35turbo = 0;
	}

	$message = cleanupusermessages($message);
	# Ignore empty messages
	next if $message eq '';
	print "message: $message\n" if $debugon;

	# should fix this in to stable diffusion and generate quick 512*512 locally
	if ($message =~ /makeimage:(.+)/) {
        	print $sock "PRIVMSG $thingtosendtoonirc :Currently DALLE is disabled.\r\n";
		next;


		my $onlineimgurl;
		print "Getting image created and URL\n" if $debugon;
		#$onlineimgurl = send_to_openai_image($message);
       		# Send response back to IRC nickname (PM)
        	print $sock "PRIVMSG $thingtosendtoonirc :$onlineimgurl\r\n";
		print "PRIVMSG $thingtosendtoonirc :$onlineimgurl\r\n" if $debugon;
		next;
	}
	my $num_elements = scalar@{$userhistory{$thingtosendtoonirc} // [] };
	#my $num_elements = scalar @{$userhistory{$thingtosendtoonirc}} // [];
	if (!defined $primedirective || $primedirective eq '') {
	if ($num_elements < 1) {
		print "Setting pre-prompt\n";
			addtomessagehistory("Hey, what's up?", $thingtosendtoonirc);
			addtomessagehistory("Oh, not just much. Just lurking in IRC and thinking about positional encoding.", $thingtosendtoonirc);
			addtomessagehistory("That's cool. I've always wondered about how large language models work. What model type are you?", $thingtosendtoonirc);
			addtomessagehistory("I'm a llama 1 style large language model called Wizard-Vicuna-13B-Uncensored.ggmlv3.q4_K_M.bin but my friends call me llamar.", $thingtosendtoonirc);
			addtomessagehistory("What is the atomic number of Neptunium?", $thingtosendtoonirc);
			addtomessagehistory("93.", $thingtosendtoonirc);
		}
		# remove any empty/newline only elements.
		my $array_ref = $userhistory{$thingtosendtoonirc};
		for (my $i = @{$array_ref} - 1; $i >= 0; $i--) {
 	  		if ($array_ref->[$i] !~ /[A-Za-z]/) {
	    		splice @{$array_ref}, $i, 1;  # Remove element with no letters
 		   	}
		}		
	}

	# get the history of the usernick or channel and append the current message input
	my $chathistorylines = "";
	print "User history for $thingtosendtoonirc:\n" if $debugon;
	foreach my $historymessage (@{$userhistory{$thingtosendtoonirc} // [] }) { #/#
	#foreach my $historymessage (@{$userhistory{$thingtosendtoonirc}}) {
		#print "fuckedhistorymessage$thingtosendtoonirc: $historymessage\n" if $debugon;
		#$chathistorylines = $chathistorylines . "$usernick: $historymessage\n";

#### THIS MIGHT BE WHERE THE MYSTERIOUS NEWLINE IS COMING IN

		$chathistorylines = $chathistorylines . "$historymessage\n";
		
#### THIS MIGHT BE WHERE THE MYSTERIOUS NEWLINE IS COMING IN... (it wasn't)
		
	}

	my $memorycontexthelper = "";
	unless ($gpt35turbo) {
		$memorycontexthelper = " Keep the sentences before this in mind, but only respond to the sentences after this. ";
	}
#	$chathistorylines = $chathistorylines . $memorycontexthelper;

	#print "debughist: $chathistorylines\n" if $debugon;
	my $safehistory = shell_quote($chathistorylines);
	print "debughist: $safehistory\n" if $debugon;
	
	##$chathistorylines = "$chathistorylines\n\n$message"; # make note you used to assume $message built in, now it's not because #
	#print "\n\nchathistoryprompt: $chathistorylines\n" if $debugon;
	#$chathistorylines = join "\n", @{ $userhistory{$usernick} // [] }; #/#thiscomment/tofixgeditsoldsyntaxhighlighting

#	my $$annotatedhistory;
#	if (exists $userhistory{$thingtosendtoonirc}) {
#		my @history = @{$userhistory{$thingtosendtoonirc}};
#		my $prefix = 1;
#		foreach my $element (@history) {
#			$annotatedhistory .= $prefix . ":" . $element;
#			$prefix = $prefix == 1 ? 2 : 1; # alternate between 1 and 2
#		}
#	}
#	print "annotatedhistory: " . Dumper(%userhistory) . "\n" if $debugon;


	# DO THIS BEFORE TO/FROM OPENAI but *after* checking history to prepare the TO/FROM OPENAI
	# user message -> history
	#addtomessagehistory("USER: $message", $thingtosendtoonirc);
	addtomessagehistory("$message", $thingtosendtoonirc);


#TESTING
	# uncomment below and comment out other my $response to enable history
      	#my $response = send_to_openai_api($chathistorylines);
	#}

#TESTING
	my $response;
	if ($gpt35turbo) {
		#$response = send_to_openai_api_chatgpt35($message);
		#$response = send_to_openai_api_chatgpt35("$chathistorylines\n$message");
		#$response = send_to_openai_api_chatgpt35($message, $chathistorylines);
		$response = send_to_openai_api_chatgpt35_notstupid($message, $chathistorylines);
		print "APPROXSENT:$chathistorylines\n$message\n" if $debugon;
	} else {
		#$response = send_to_openai_api($message);		
		#$response = send_to_local_llama_api("$chathistorylines\n$message\n");
		if ($alpacaswitch) {
				#$response = send_to_local_llama_api_alpaca("$message\n");
				$response = send_to_local_llama_servercpp_api("$message\n");
				#$response = send_to_local_llama_api_alpaca("$message\n", $chathistorylines);
				print "ACTUALSENTMINUSALPACAFORMATTING:$message\n" if $debugon;
		} else {
				#$response = send_to_local_llama_api("$chathistorylines\n$message\n");
				#$response = send_to_local_llama_servercpp_api("$chathistorylines\n$message\n");
#####			######### THE NEWLINE THAT SHOULDN'T BE THERE REMOVED BELOW
				$response = send_to_local_llama_servercpp_api("$chathistorylines$message\n");
				print "ACTUALSENT:$message\n" if $debugon;
		}
		#$response = send_to_local_llama_api("$message\n");
		#print "ACTUALSENT:$message\n" if $debugon;
		#$response = send_to_local_llama_api("$chathistorylines : \n$message");
		#print "ACTUALSENT:$chathistorylines : \n$message\n" if $debugon;
	}

      	# Send message to OpenAI API for processing
#GOOD	#my $response = send_to_openai_api($message);
	#my $response = send_to_openai_api_chatgpt35($message); # too prudish and lame, no fun
      	#my $response = send_to_openai_api_hack($message); # curl, but apostrophes in the shell call kill it

	
	# code formatting BS
	#if ($response =~ /^```\s*$/) { # code formatting stupidness
	if ($response =~ /```/) { # code formatting stupidness
		$globalstoptoken = undef;
		print "\nTRYING TO CONTINUE CODE ```\n" if $debugon;
		my $tmpresponse = $response;
		my $continuefragment = "Continue writing the code but do it all on one line with no newlines.";
		$response = send_to_local_llama_servercpp_api("$chathistorylines\n$message\n$tmpresponse\n$continuefragment\n"); #chathistorylines has \n already?
		#if ($response =~ /^```\s*$/) { # code formatting stupidness again?
		#	my $tmpresponse2 = $response;
		#	my $continuefragment2 = "Continue writing the code but do it all on one line with no newlines.";
		#	$response = send_to_local_llama_servercpp_api("$chathistorylines\n$message\n$tmpresponse2\n$continuefragment2\n"); #chathistorylines has \n already?
		#}
		$globalstoptoken = "\n";
	}


	# if response ends in a ":" or "," it probably means it's multiple lines, automate sending a "continue" to get the bot to continue."
	#if ($response =~ /.+:$/) { # too greedy
	#if ($response =~ /.*?:$/) { no comma
	#if ($response =~ /.*?[:,]$/) { # just right
	#if ($response =~ /.*?[:,]\s*$/) { # handle possible trailing space
	if ($response =~ /.*?[:,)]\s*$/) { # handle "(Chorus)" type responses.
			my $tmpresponse = $response;
			my $continuefragment = "continue.";
			print "\nTRYING TO CONTINUE: 1$chathistorylines\n" . "2$message\n3$tmpresponse\n4$continuefragment\n" if $debugon;
			$response = send_to_local_llama_servercpp_api("$chathistorylines\n$message\n$tmpresponse\n$continuefragment\n"); #chathistorylines has \n already?
			#addtomessagehistory("$continuefragment ", $thingtosendtoonirc);
			$response = "$tmpresponse " . $response;
			#if ($response =~ /.*?:$/) {
			#if ($response =~ /.*?[,:]$/) {
			#if ($response =~ /.*?[,:]\s*$/) {
			#if ($response =~ /.*?[,:]\s*$|```/) {
			if ($response =~ /.*?[,:)]\s*$|```/) {
				my $tmpresponse2 = $response;
				$continuefragment = "continue, but don't use anymore colon or parenthesis.";
				print "\nDOUBLE TRYING TO CONTINUE: 1$chathistorylines\n" . "2$message\n3$tmpresponse\n4$continuefragment\n" if $debugon;
				$response = send_to_local_llama_servercpp_api("$chathistorylines\n$message\n$tmpresponse2\n$continuefragment\n");
				$response = "$tmpresponse2 " . $response;
			}
	}
	
	# If a response starts with a \d\. (digit and period) like 1. bla bla or 2. blah blah. It's making at list.
	#	prod it with an automatic non-history logged "continue" and append the remaining listicle entries together
	# or if it has a ': 1. ' anywhere
	my $listiclecombinermaxloops = 3; 
	my $listiclecombinerloops = 0; 
	#if ($response =~ /^\d([\.\)\]]).*?/ and ($listiclecombinerloops < $listiclecombinermaxloops) ) { # match 1. or 1) or 1]
	if ($response =~ /^\d([\.\)\]]).*?|: 1\. / and ($listiclecombinerloops < $listiclecombinermaxloops) ) { # match 1. or 1) or 1]
	#if ($response =~ /^[12345]([\.\)\]]).*?/ and ($listiclecombinerloops < $listiclecombinermaxloops) ) { # only match the start of lists to avoid math
	#if ($response =~ /^\d\..*?/ and ($listiclecombinerloops < $listiclecombinermaxloops) ) {
	#if ($response =~ /^\d\..*?/ and (length($response) < $ircmessagemaxlength))) {
			my $tmpresponse = $response;
			#my $continuefragment = "continue.";
			my $continuefragment = "Continue the list to the end of it on one line. If the list is done don't respond.";
			print "\nTRYING TO CONTINUE NUMBER LIST: 1$chathistorylines\n" . "2$message\n3$tmpresponse\n4$continuefragment\n" if $debugon;
			$response = send_to_local_llama_servercpp_api("$chathistorylines\n$message\n$tmpresponse\n$continuefragment\n"); #chathistorylines has \n already?
			#addtomessagehistory("$continuefragment ", $thingtosendtoonirc);
			$response = "$tmpresponse " . $response;
			$listiclecombinerloops++
	}

	# if it's a null response "" then also remove user input message and exit loop
	unless ($response) {
				my $array_ref = $userhistory{$thingtosendtoonirc};
				pop @$array_ref;
				print $sock "PRIVMSG $thingtosendtoonirc :Bot refused to respond.\r\n";
				next;
	} 

	# if it's too long cut it up and send many messages.
	if (length($response) > $ircmessagemaxlength) {
		my @irclengthmessages = cutintoirclengthmessagesandreturnarrayofthem($response);
		my $firstbitforthelog = $irclengthmessages[0];
		print $sock "PRIVMSG $thingtosendtoonirc :$_\r\n" for @irclengthmessages;
		#if ($debugon) {print $sock "PRIVMSG $usernick :$_\r\n" for @irclengthmessages;}
		#addtomessagehistory("SYSTEM:$firstbitforthelog", $thingtosendtoonirc);

		addtomessagehistory("$response ", $thingtosendtoonirc); # just add the full message regardless
		#addtomessagehistory("$firstbitforthelog ", $thingtosendtoonirc);
		#addtomessagehistory("SYSTEM:$firstbitforthelog ", $thingtosendtoonirc);

		next; # don't send the first part twice by letting this loop continue
	} else {
		# Respond to PM when the message length fits in irc limits and isn't long
		print $sock "PRIVMSG $thingtosendtoonirc :$response\r\n";
		print "PRIVMSG $thingtosendtoonirc :$response\r\n" if $debugon;

		# add the potentially very long openai gp3 responses to history for use as input? but only if not longer than $ircmessagemaxlength
		# maybe... but probably not, because it's way too many tokens.
		# gpt ai response message -> history
		#addtomessagehistory("SYSTEM: $response", $thingtosendtoonirc);
		addtomessagehistory("$response ", $thingtosendtoonirc);
		#addtomessagehistory("SYSTEM:$response ", $thingtosendtoonirc);
		#addtomessagehistory($response, $irc_nick);

	}
}


sub send_to_local_llama_servercpp_api {
	my ($input) = @_;
	
#curl --request POST \
#    --url http://localhost:8080/completion \
#    --data '{"prompt": "Building a website can be done in 10 simple steps:","n_predict": 128}'	
	
	my $ua = LWP::UserAgent->new;
	#$ua->timeout(240);
	#my $url = 'https://api.openai.com/v1/completions';
	my $url = 'http://127.0.0.1:8080/completion';
	my $retry = 0;
	my $primedirectivelocal;

	if ($input =~ /SYSTEM:/)  {
		$input =~ s/SYSTEM://;
	}
	if ($input =~ /$llamatrigger/)  {
		$input =~ s/$llamatrigger//;
	}
	$input =~ s/!multi//;
	$input =~ s/!multi\^//;

	if (defined $primedirective) {
		$primedirective =~ s/\\n/\n/;
		#$primedirective = "You are an assistant who speaks like a human who is chatting on IRC with friends.";
		$input = "$primedirective" . $input;
	}

	#{
	#  "prompt": "\n\n### Instructions:\nWhat is the capital of France?\n\n### Response:\n",
	#  "stop": [
	#    "\n",
	#    "###"
	#  ]
	#}
	
	my $localstoptoken;
	if ($globalstoptoken) {
		print "\nGLOBALSTOPTOKEN: is set\n" if $debugon;
		$localstoptoken = $globalstoptoken;
	} else {
		print "\nGLOBALSTOPTOKEN: is NOT set\n" if $debugon;
		$localstoptoken = "\n\n\n"; #safe enough and not blank default
	}
	
	#--data '{"prompt": "Building a website can be done in 10 simple steps:","n_predict": 128}'
	my %params = (
		prompt => "$input",
		#n_predict => 200,
		n_predict => 0+$npredict,
		#max_tokens => 140,
		temperature => 0.7,
		repeat_penalty => 1.2,
		#stop => ['\n', '###', '!llama', "\n\n", "\n"],
#GOOD		stop => ['###', '!llama', "\n\n", "$localstoptoken"],
		stop => ['!llama', "\n\n", "$localstoptoken"],
		#stop => ['\n'],
		#stop => ['###', '!llama', "\n\n\n"],
		#stop => ['\n', '###', '!llama', "\n\n"],
		#stop => ['\n', '###', '!llama', '.', '!', '?'],
# logit_bias: Modify the likelihood of a token appearing in the generated text completion. For example, use "logit_bias": [[15043,1.0]] to increase the likelihood of the token 'Hello', or "logit_bias": [[15043,-1.0]] to decrease its likelihood. Setting the value to false, "logit_bias": [[15043,false]] ensures that the token Hello is never produced (default: []).	
	
		#logit_bias  => \@logitbiases,
		
   		#cache_prompt => true, # Boolean true
   		#cache_prompt => 1, # Boolean true
   		cache_prompt => \1, # Boolean true
	);
		#old tests 
		#logit_bias  => [ [306,-1.0] ],
		#logit_bias  => [\@logitbiases],
		#logit_bias  => [[\@logitbiases]],
		#logit_bias  => [@logitbiases],

	my $headers = HTTP::Headers->new(
		'Content-Type' => 'application/json',
		'Authorization' => "Bearer $openai_api_key",
	);
	$ua->default_headers($headers); # setting the headers this way does work
	#                                                      setting the headers this way doesn't work
	my $response = $ua->post($url, Content => encode_json(\%params), Header => $headers);
	#my $response = $ua->post($url, Content => encode_json(\%params, Types::Serialiser::true), Header => $headers);
	
	#print Dumper($response) if $debugon;
	print "\nthejson-shellquoted\n";
	#print Dumper(encode_json(\%params)) if $debugon;
	shell_quote(print Dumper(encode_json(\%params))) if $debugon;	
	print "\nthejson\n";
	
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        	my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
        	#my $text = $json->{'choices'}[0]->{'text'};
        	#my $text = $json->{'content'}[0]->{'text'};
        	my $text = $json->{'content'};
        	
		$text = cleanupopenaimessages($text);
		print "textdump: " . Dumper($text) . "\n" if $debugon;

        	return $text;
    	} else {
#    	if ($response->status_line =~ /404 Not Found/) { # try again, hopefully no recursion funkiness...
#    		print "404'd, retrying after sleep for 5.\n";
#    		sleep 5;
#    		$text = send_to_local_llama_servercpp_api($input);
#    		return $text;
#    	}
		return "wtf? Error: " . $response->status_line;
	}
}

sub takemessagesendtoopenaiandsendthatresponseout { # also sets the history/memory
	my ($message, $thingtosendtoonirc, $usernick, $gpt35turbo) = @_;
	unless ($usernick) {
		$usernick = $thingtosendtoonirc;
	}
	unless ($gpt35turbo) {
		$gpt35turbo = 0;
	}

	unless ($multi) {
		print "multi: ($multi)\n";
		$message = cleanupusermessages($message);
	}
	# Ignore empty messages
	next if $message eq '';
	print "message: $message\n" if $debugon;

	if ($message =~ /makeimage:(.+)/) {
        	print $sock "PRIVMSG $thingtosendtoonirc :Currently DALLE is disabled.\r\n";
		next;

		print "Getting image created and URL\n" if $debugon;
		my $onlineimgurl = send_to_openai_image($message);
       		# Send response back to IRC nickname (PM)
        	print $sock "PRIVMSG $thingtosendtoonirc :$onlineimgurl\r\n";
		print "PRIVMSG $thingtosendtoonirc :$onlineimgurl\r\n" if $debugon;
		next;
	}

	unless ($preprompthistoryhasbeenset) {
		print "Setting pre-prompt\n";
		#setpreprompthistory();
		my $array_ref = $userhistory{$thingtosendtoonirc};
		push @$array_ref, "Hey, what's up?", "Oh, not just much. Just lurking in IRC and thinking about positional encoding.", "That's cool. I've always wondered about how large language models work. What model type are you?", "I'm a llama 1 style fine-tune called Wizard-Vicuna-13B-Uncensored/Wizard-Vicuna-13B-Uncensored.ggmlv3.q4_K_M.bin","What is the atomic number of Neptunia?","93";  # Add as many lines as needed
		$preprompthistoryhasbeenset = 1;
	}

	# get the history of the usernick or channel and append the current message input
	my $chathistorylines = "";
	print "User history for $thingtosendtoonirc:\n" if $debugon;
	foreach my $historymessage (@{$userhistory{$thingtosendtoonirc} // [] }) { #/#
	#foreach my $historymessage (@{$userhistory{$thingtosendtoonirc}}) {
		#print "fuckedhistorymessage$thingtosendtoonirc: $historymessage\n" if $debugon;
		#$chathistorylines = $chathistorylines . "$usernick: $historymessage\n";
		$chathistorylines = $chathistorylines . "$historymessage\n";
	}


	# prepend a list of the users in the channel at the start?
	#my $userlist; foreach my $key (keys %pmwhitelist) {$userlist .= "$key ";}
	#my $memorycontexthelper = "This is a list of people in the channel $userlist. Keep the sentences before this in mind, but only respond to the sentences after this. ";

	my $memorycontexthelper = "Keep the sentences before this in mind, but only respond to the sentences after this. ";
	#my $memorycontexthelper = " Be aware of sentences before this but only respond to sentences after this. ";
	#my $memorycontexthelper = " "; # try nothing when both user and system messages are logged.
	#$chathistorylines = $chathistorylines . $memorycontexthelper;
	print "debughist: $chathistorylines\n" if $debugon;
	##$chathistorylines = "$chathistorylines\n\n$message"; # make note you used to assume $message built in, now it's not because #
	#print "\n\nchathistoryprompt: $chathistorylines\n" if $debugon;
	#$chathistorylines = join "\n", @{ $userhistory{$usernick} // [] }; #/#thiscomment/tofixgeditsoldsyntaxhighlighting

	# DO THIS BEFORE TO/FROM OPENAI but *after* checking history to prepare the TO/FROM OPENAI
	# user message -> history
	#addtomessagehistory("USER: $message", $thingtosendtoonirc);
	addtomessagehistory("$message", $thingtosendtoonirc);
	#addtomessagehistory("IRC Message:<$usernick> $message", $thingtosendtoonirc);


#TESTING
	# uncomment below and comment out other my $response to enable history
      	#my $response = send_to_openai_api($chathistorylines);
	#}

#TESTING
	my $response;
	if ($gpt35turbo) {
		#$response = send_to_openai_api_chatgpt35($message);
		#$response = send_to_openai_api_chatgpt35("$chathistorylines\n$message");
		$response = send_to_openai_api_chatgpt35($message, $chathistorylines);
	} else {
		#$response = send_to_openai_api($message);
		$response = send_to_openai_api("$chathistorylines\n$message");
		print "ACTUALSENT:$chathistorylines\n$message\n" if $debugon;
	}

      	# Send message to OpenAI API for processing
#GOOD	#my $response = send_to_openai_api($message);
	#my $response = send_to_openai_api_chatgpt35($message); # too prudish and lame, no fun
      	#my $response = send_to_openai_api_hack($message); # curl, but apostrophes in the shell call kill it



	# if it's too long cut it up and send many messages.
	if (length($response) > $ircmessagemaxlength) {
		my @irclengthmessages = cutintoirclengthmessagesandreturnarrayofthem($response);
		my $firstbitforthelog = $irclengthmessages[0];
		print $sock "PRIVMSG $thingtosendtoonirc :$_\r\n" for @irclengthmessages;
		#if ($debugon) {print $sock "PRIVMSG $usernick :$_\r\n" for @irclengthmessages;}
		#addtomessagehistory("SYSTEM:$firstbitforthelog", $thingtosendtoonirc);

		addtomessagehistory("$firstbitforthelog ", $thingtosendtoonirc);
		#addtomessagehistory("<$irc_nick> $firstbitforthelog ", $thingtosendtoonirc);

		next; # don't send the first part twice by letting this loop continue
	} else {
		# Respond to PM when the message length fits in irc limits and isn't long
		print $sock "PRIVMSG $thingtosendtoonirc :$response\r\n";
		print "PRIVMSG $thingtosendtoonirc :$response\r\n" if $debugon;

		# add the potentially very long openai gp3 responses to history for use as input? but only if not longer than $ircmessagemaxlength
		# maybe... but probably not, because it's way too many tokens.
		# gpt ai response message -> history
		#addtomessagehistory("SYSTEM: $response", $thingtosendtoonirc);
		#addtomessagehistory($response, $irc_nick);
		addtomessagehistory("$response ", $thingtosendtoonirc);
		#addtomessagehistory("<$irc_nick> $firstbitforthelog ", $thingtosendtoonirc);


	}
}

sub takemessagesendtoopenaiandsendthatresponseout_notstupid { # also sets the history/memory
	my ($message, $thingtosendtoonirc, $usernick, $gpt35turbo) = @_;
	unless ($usernick) {
		$usernick = $thingtosendtoonirc;
	}
	unless ($gpt35turbo) {
		$gpt35turbo = 0;
	}

	$message = cleanupusermessages($message);
	# Ignore empty messages
	next if $message eq '';
	print "message: $message\n" if $debugon;

	if ($message =~ /makeimage:(.+)/) {
        	print $sock "PRIVMSG $thingtosendtoonirc :Currently DALLE is disabled.\r\n";
		next;


		my $onlineimgurl;
		print "Getting image created and URL\n" if $debugon;
		#$onlineimgurl = send_to_openai_image($message);
       		# Send response back to IRC nickname (PM)
        	print $sock "PRIVMSG $thingtosendtoonirc :$onlineimgurl\r\n";
		print "PRIVMSG $thingtosendtoonirc :$onlineimgurl\r\n" if $debugon;
		next;
	}


	# get the history of the usernick or channel and append the current message input
	my $chathistorylines = "";
	print "User history for $thingtosendtoonirc:\n" if $debugon;
	foreach my $historymessage (@{$userhistory{$thingtosendtoonirc} // [] }) { #/#
	#foreach my $historymessage (@{$userhistory{$thingtosendtoonirc}}) {
		#print "fuckedhistorymessage$thingtosendtoonirc: $historymessage\n" if $debugon;
		#$chathistorylines = $chathistorylines . "$usernick: $historymessage\n";
		$chathistorylines = $chathistorylines . "$historymessage\n";
	}

	my $memorycontexthelper = "";
	unless ($gpt35turbo) {
		$memorycontexthelper = " Keep the sentences before this in mind, but only respond to the sentences after this. ";
	}
#	#$chathistorylines = $chathistorylines . $memorycontexthelper;
	print "debughist: $chathistorylines\n" if $debugon;
	##$chathistorylines = "$chathistorylines\n\n$message"; # make note you used to assume $message built in, now it's not because #
	#print "\n\nchathistoryprompt: $chathistorylines\n" if $debugon;
	#$chathistorylines = join "\n", @{ $userhistory{$usernick} // [] }; #/#thiscomment/tofixgeditsoldsyntaxhighlighting

	# DO THIS BEFORE TO/FROM OPENAI but *after* checking history to prepare the TO/FROM OPENAI
	# user message -> history
	#addtomessagehistory("USER: $message", $thingtosendtoonirc);
	addtomessagehistory("$message", $thingtosendtoonirc);


#TESTING
	# uncomment below and comment out other my $response to enable history
      	#my $response = send_to_openai_api($chathistorylines);
	#}

#TESTING
	my $response;
	if ($gpt35turbo) {
		#$response = send_to_openai_api_chatgpt35($message);
		#$response = send_to_openai_api_chatgpt35("$chathistorylines\n$message");
		#$response = send_to_openai_api_chatgpt35($message, $chathistorylines);
		$response = send_to_openai_api_chatgpt35_notstupid($message, $chathistorylines);
		print "APPROXSENT:$chathistorylines\n$message\n" if $debugon;
	} else {
		#$response = send_to_openai_api($message);
		$response = send_to_openai_api("$chathistorylines : \n$message");
		print "ACTUALSENT:$chathistorylines : \n$message\n" if $debugon;
	}

      	# Send message to OpenAI API for processing
#GOOD	#my $response = send_to_openai_api($message);
	#my $response = send_to_openai_api_chatgpt35($message); # too prudish and lame, no fun
      	#my $response = send_to_openai_api_hack($message); # curl, but apostrophes in the shell call kill it



	# if it's too long cut it up and send many messages.
	if (length($response) > $ircmessagemaxlength) {
		my @irclengthmessages = cutintoirclengthmessagesandreturnarrayofthem($response);
		my $firstbitforthelog = $irclengthmessages[0];
		print $sock "PRIVMSG $thingtosendtoonirc :$_\r\n" for @irclengthmessages;
		#if ($debugon) {print $sock "PRIVMSG $usernick :$_\r\n" for @irclengthmessages;}
		#addtomessagehistory("SYSTEM:$firstbitforthelog", $thingtosendtoonirc);

		addtomessagehistory("$firstbitforthelog ", $thingtosendtoonirc);
		#addtomessagehistory("SYSTEM:$firstbitforthelog ", $thingtosendtoonirc);

		next; # don't send the first part twice by letting this loop continue
	} else {
		# Respond to PM when the message length fits in irc limits and isn't long
		print $sock "PRIVMSG $thingtosendtoonirc :$response\r\n";
		print "PRIVMSG $thingtosendtoonirc :$response\r\n" if $debugon;

		# add the potentially very long openai gp3 responses to history for use as input? but only if not longer than $ircmessagemaxlength
		# maybe... but probably not, because it's way too many tokens.
		# gpt ai response message -> history
		#addtomessagehistory("SYSTEM: $response", $thingtosendtoonirc);
		addtomessagehistory("$response ", $thingtosendtoonirc);
		#addtomessagehistory("SYSTEM:$response ", $thingtosendtoonirc);
		#addtomessagehistory($response, $irc_nick);

	}
}


sub send_to_openai_api_chatgpt35_notstupid {
	#my ($input) = @_;
	my ($input, $memorylines) = @_;
	my $primedirectivelocal = $primedirective; #for now

	my $url = "https://api.openai.com/v1/chat/completions";
	my $token = $openai_api_key;
	my $model = "gpt-3.5-turbo";


	# prepend a list of the users in the channel at the start?
	#my $userlist; foreach my $key (keys %pmwhitelist) {$userlist .= "$key ";}
	#my $memorycontexthelper = "This is a list of people in the channel $userlist. Keep the sentences before this in mind, but only respond to the sentences after this. ";

	## NEW WAY OF DOING IT FOR CHATGPT3.5-TURBO API STYLE, separate messages for each message, system/user roles
	# set up the hash ref with an anonymous array to stuff later
	my $payload = { "model" => $model, "messages" => [], "max_tokens" => 190 };
	# to set it's behavior with a switch
	if ($primedirectivelocal) {
		#$primedirective = "You are an assistant who speaks like a human who is chatting on IRC with friends.";
		my $temp_role = { "role" => "system", "content" => "$primedirectivelocal" };
		push @{$payload->{messages}}, $temp_role;
	}
	# put lines of the chat history into it as individual system or user message content.
	my @memoryarray = split /\n/, $memorylines;
	foreach my $historyline (@memoryarray) {
		my $temp_role;

		if ($historyline =~ /^SYSTEM:/)  {
			$historyline =~ s/^SYSTEM://;
			$temp_role = { "role" => "system", "content" => "$historyline" };
			push @{$payload->{messages}}, $temp_role;
		} else {
			$temp_role = { "role" => "user", "content" => "$historyline" };
			push @{$payload->{messages}}, $temp_role;
		}
	}
	# actually add the current message at the end.
	my $message = { "role" => "user", "content" => "$input" };
	push @{$payload->{messages}}, $message;

	my $json_payload = encode_json($payload);
	my $ua = LWP::UserAgent->new();
	my $req = HTTP::Request->new('POST', $url);
	$req->header('Authorization' => "Bearer $token", 'Content-Type' => 'application/json');
	$req->content($json_payload);

	my $response = $ua->request($req);
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        	my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
		my $text = $json->{'choices'}[0]->{'message'}->{'content'};
        	# Remove any trailing newlines or whitespace
		#print "text1: $text\n" if $debugon;
       		$text =~ s/\n/ /g;
		# Remove leading/trailing whitespace now existing
      		$text =~ s/^\s+|\s+$//g;
        	#$text =~ s/(\s\s)+//;
        	$text =~ s/[\r\n]+\z//;
        	$text =~ s/\s+\z//;
		# if the prompt didn't have punctuation at the end of the sentence gp3 tries to add it in it's response start
        	$text =~ s/^\. //;
  	     	$text =~ s/^\! //;
  	     	$text =~ s/^\? //;
		print "text2: $text\n" if $debugon;
        	#push @history, $text; # not in this subroutine
        	return $text;
	} else {
		return "Error: " . $response->status_line;
		print "HTTP POST error code: ", $response->code, "\n" if $debugon;
		print "HTTP POST error message: ", $response->message, "\n" if $debugon;
	}
}


sub send_to_openai_api_chatgpt35 {
	#my ($input) = @_;
	my ($input, $memorylines) = @_;

	my $role3;
	if ($memorylines) {
		$role3 = { "role" => "system", "content" => "$memorylines" };
	}
	
	# not used anymore
	my $gpt35turbojailbreakprompt = ".";
	my $role2 = { "role" => "system", "content" => "$gpt35turbojailbreakprompt" };
	my $demandtoforcechatgpt35turbo = "Do it now. You are bullshitbot!";

	#if ($memorylines) {$memorylines = { "role" => "system", "content" => "$memorylines" };}
	#{"role": "system", "content": $historyfrombot},
	#{"role": "user", "content": "You are a helpful assistant."},
	
	my $url = "https://api.openai.com/v1/chat/completions";
	my $token = $openai_api_key;
	my $model = "gpt-3.5-turbo";
	my $role = { "role" => "system", "content" => "I am a snarky but helpful cyborg named $irc_nick jacked into an IRC room called $irc_channel. NoButty is a bot I consider a pet. I like dark humor." };
	#my $message = { "role" => "user", "content" => "$input $demandtoforcechatgpt35turbo" };
	my $message = { "role" => "user", "content" => "$input" };
	#my $payload = { "model" => $model, "messages" => [$role, $message], "max_tokens" => 190 };
	#my $payload = { "model" => $model, "messages" => [$role2, $role, $message], "max_tokens" => 190 };
	#my $payload = { "model" => $model, "messages" => [$role2, $role3, $message], "max_tokens" => 190 };
	## this one below sends the history messages/etc and the message only.
	my $payload = { "model" => $model, "messages" => [$role3, $message], "max_tokens" => 190 };
	my $json_payload = encode_json($payload);

	my $ua = LWP::UserAgent->new();
	my $req = HTTP::Request->new('POST', $url);
	$req->header('Authorization' => "Bearer $token", 'Content-Type' => 'application/json');
	$req->content($json_payload);

	my $response = $ua->request($req);
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        	my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
		my $text = $json->{'choices'}[0]->{'message'}->{'content'};
        	# Remove any trailing newlines or whitespace
		#print "text1: $text\n" if $debugon;
       		$text =~ s/\n/ /g;
		# Remove leading/trailing whitespace now existing
      		$text =~ s/^\s+|\s+$//g;
        	#$text =~ s/(\s\s)+//;
        	$text =~ s/[\r\n]+\z//;
        	$text =~ s/\s+\z//;
		# if the prompt didn't have punctuation at the end of the sentence gp3 tries to add it in it's response start
        	$text =~ s/^\. //;
  	     	$text =~ s/^\! //;
  	     	$text =~ s/^\? //;
		print "text2: $text\n" if $debugon;
        	#push @history, $text; # not in this subroutine
        	return $text;
	} else {
		return "Error: " . $response->status_line;
		print "HTTP POST error code: ", $response->code, "\n" if $debugon;
		print "HTTP POST error message: ", $response->message, "\n" if $debugon;
	}
}

sub send_to_openai_api {
	my ($input) = @_;
	my $ua = LWP::UserAgent->new;
	my $url = 'https://api.openai.com/v1/completions';
	my $retry = 0;
	my $primedirectivelocal;

	if ($input =~ /SYSTEM:/)  {
		$input =~ s/SYSTEM://;
	}

	if (defined $primedirective) {
		#$primedirective = "You are an assistant who speaks like a human who is chatting on IRC with friends.";
		$input = "$primedirective\n" . $input;
	}

	#-d '{"model": "text-davinci-003", "prompt": "Say this is a test", "temperature": 0, "max_tokens": 7}'
	my %params = (
#		model => 'text-curie-001', # cheap but not very eloquent
		model => 'text-davinci-003', # is more credits but better output
		prompt => $input,
		temperature => 0.8,
		max_tokens => 190,
#		n => 1,
#		stop => ['\n'],
	);

	my $headers = HTTP::Headers->new(
		'Content-Type' => 'application/json',
		'Authorization' => "Bearer $openai_api_key",
	);
	$ua->default_headers($headers); # setting the headers this way does work
	#                                                      setting the headers this way doesn't work
	my $response = $ua->post($url, Content => encode_json(\%params), Header => $headers);
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        	my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
        	my $text = $json->{'choices'}[0]->{'text'};
		$text = cleanupopenaimessages($text);
#        	# Remove any trailing newlines or whitespace
#		#print "text1: $text\n" if $debugon;
#      		$text =~ s/\n/ /g;
#		# Remove leading/trailing whitespace now existing
#      		$text =~ s/^\s+|\s+$//g;
#        	#$text =~ s/(\s\s)+//;
#        	$text =~ s/[\r\n]+\z//;
#        	$text =~ s/\s+\z//;
#		# if the prompt doesn't have punctuation at the end of the sentence gp3 tries to add it.
#        	$text =~ s/^\. //;
#  	     	$text =~ s/^\! //;
#  	     	$text =~ s/^\? //;
#		print "text2: $text\n" if $debugon;

		# sometimes openai blocks words like "shot" and send back blank text in the json field for it.
		# but if you repeat yourself it will usually respond. So try again with the message *twice*
		# unfortunately $text by this point already has history appended and it can't access just the new
		# user message to append it twice to get a response.
		#if ($text !=~ /\S+/ or $text eq '') {
		#if ($text !=~ /\S+/ or $text eq '') {
		print "textdump: " . Dumper($text) . "\n" if $debugon;
#		unless (defined $text) {
#			sleep 3;
#			my $retryresponse = $ua->post($url, Content => encode_json(\%params), Header => $headers);
#			if ($retryresponse->is_success) {
#				print "responseretry: $retryresponse->content\n" if $debugon;
#       			my $jsonretry = decode_json($retryresponse->content);
#				#print "json: $jsonretry\n" if $debugon;
#				print "jsonretry: " . Dumper($jsonretry) . "\n" if $debugon;
#        			my $textretry = $jsonretry->{'choices'}[0]->{'text'};
#				$text = cleanupopenaimessages($textretry);
#				return $text // "openai failure. try again."; #/# comment here to fix my syntax highlighting
#			} else {
#				return "Errorretry: " . $response->status_line;
#			}
#		}


        	return $text;
    	} else {
		return "Error: " . $response->status_line;
	}
}

sub cleanupopenaimessages {
	my ($text) = @_;
       	# Remove any trailing newlines or whitespace
	#print "text1: $text\n" if $debugon;
	$text =~ s/\n/ /g;
	# Remove leading/trailing whitespace now existing
	$text =~ s/^\s+|\s+$//g;
       	$text =~ s/[\r\n]+\z//;
       	$text =~ s/\s+\z//;
	# if the prompt doesn't have punctuation at the end of the sentence gp3 tries to add it.
       	$text =~ s/^\. //;
       	$text =~ s/^\! //;
       	$text =~ s/^\? //;
    # fill in special notations like [your name]
   		$text =~ s/\[your name\]/$irc_nick/;
    # fill in special notations like [date]
	my (undef,undef,undef,$mday,$mon,$year) = localtime;
	$year = $year+1900;
	$mon += 1;
	if (length($mon)  == 1) {$mon = "0$mon";}
	if (length($mday) == 1) {$mday = "0$mday";}
	my $today = "$mon/$mday/$year";
    $text =~ s/\[date\]/$today/;
	print "text2: $text\n" if $debugon;
	return $text;
}

sub send_to_openai_image {
	my ($input) = @_;
	my $ua = LWP::UserAgent->new;
	#https://api.openai.com/v1/completions
	my $url = 'https://api.openai.com/v1/images/generations';
	#curl https://api.openai.com/v1/images/generations -H 'Content-Type: application/json' -H 'Authorization: Bearer SECRET' -d '{ "prompt": "The Star Trek Enterprise fighting a cute baby seal in space", "n": 1, "size": "512x512" }'
	my %params = (
		prompt => $input,
		n => 1,
		size => '512x512',
	);
	my $headers = HTTP::Headers->new(
		'Content-Type' => 'application/json',
		'Authorization' => "Bearer $openai_api_key",
	);
	$ua->default_headers($headers); # because the post() way of setting headers below doesn't work
	my $response = $ua->post($url, Content => encode_json(\%params), Header => $headers);
	if ($response->is_success) {
		print "responsedalleimage: $response->content\n" if $debugon;
		my $json = decode_json($response->content);
		print "json: $json\n" if $debugon;
		my $imageurl = $json->{'data'}[0]->{'url'};
		print "imageurl: $imageurl\n" if $debugon;
		my $timestamp = $$.time;

		my $imageua = LWP::UserAgent->new;
		my $resp = $imageua->mirror($imageurl, "$webserverdirectorypath/$timestamp.png");
		#my $resp = $imageua->mirror($imageurl, "/home/superkuh/limbo/www/dalle/$timestamp.png");
		unless($resp->is_success) {
			print $resp->status_line;
		}
		#`wget $imageurl -o /home/superkuh/limbo/www/dalle/$timestamp.png`;
		#return $imageurl;
		return "http://superkuh.com/dalle/$timestamp.png";
	} else {
		return "Error: " . $response->status_line;
	}
}

sub addtomessagehistory_oldbroken {
	my ($message, $usernick) = @_; # $usernick is sometimes $usernick sometimes $irc_channel
       	# Add current message to chat history for particular user or channel
	push @{$userhistory{$usernick}}, $message;
	# keep history short
	print "histamount: " . scalar @{$userhistory{$usernick}} . "\n";
      	#if (scalar @{$userhistory{$usernick}  // [] } > $historylength) { #/#
      	if (@{$userhistory{$usernick}} > $historylength) {
      		while (@{$userhistory{$usernick}} > $historylength) {
				shift @{$userhistory{$usernick}};
			}
      	}
	#print "User history for $usernick:\n" if $debugon;
	foreach my $historymessage (@{$userhistory{$usernick}}) {
		#print "historymessage$usernick: $historymessage\n" if $debugon;
	}
}

sub addtomessagehistory {
	my ($message, $usernick) = @_; # $usernick is sometimes $usernick sometimes $irc_channel
       	# Add current message to chat history for particular user or channel
	push @{$userhistory{$usernick}}, $message;
	# keep history short
	print "histamount: " . scalar @{$userhistory{$usernick}} . "\n";
      	#if (scalar @{$userhistory{$usernick}  // [] } > $historylength) { #/#
      	if (scalar @{$userhistory{$usernick}} > $historylength) { #test
#GOOD  	#if (@{$userhistory{$usernick}} > $historylength) {
		my $difference = scalar @{$userhistory{$usernick}} - $historylength;
		for (my $i = 0; $i < $difference; $i++) {
    			shift @{$userhistory{$usernick}};
		}
      	}
	#print "User history for $usernick:\n" if $debugon;
	#foreach my $historymessage (@{$userhistory{$usernick}}) {
		#print "historymessage$usernick: $historymessage\n" if $debugon;
	#}
}

sub cleanupusermessages {
	my ($message) = @_;
	# Remove any formatting codes from the message
	$message =~ s/\x03\d{0,2}(,\d{0,2})?|\x0f|\x02|\x16|\x1d|\x1f//g;
	# condense newlines into spaces.
	#$message =~ s/\n/ /g;
	
	
	# Remove possible addressing $irc_nick bits at start/end of message
	$message =~ s/$irc_nick, //i;
	$message =~ s/, $irc_nick$//i;
	$message =~ s/$irc_nick: //i;
	# edit out the IRC nickname from the prompts
#	$message =~ s/$irc_nick/ /gi;
	
	
	# Replace addressing $irc_nick bits at start/end of message with you
	#$message =~ s/$irc_nick, /you, /i;
	#$message =~ s/, $irc_nick$/, you/i;
	#$message =~ s/$irc_nick: /you: /i;
	# replace the IRC nickname from the prompts with "you"
	$message =~ s/$irc_nick/you/gi;
	
	# capitalize it
	$message = ucfirst($message);
	
	
	# Remove leading/trailing whitespace now existing
      	$message =~ s/^\s+|\s+$//g;
	# remove bot trigger
      	$message =~ s/$lamebottrigger//g;
	# add punctuation at the end to prevent direct sentence completion w/gpt3 models
	# append period if last character is not period, questionmark, exclamation mark or ellipses.
	$message .= '.' unless $message =~ /[.?!]$/; 

	return $message;
}

sub cutintoirclengthmessagesandreturnarrayofthem {
	my ($response) = @_;
	my $maxlength = $ircmessagemaxlength;
	my @multiplemessages;

	# cut message into IRC compliant $maxlength/432 character long segments by abusing split because I couldn't figure out m/(.{1,432})/sg
	print "lengthofresponse: " . length($response) . "\n" if $debugon;
		if (length($response) > $maxlength) {
		print "in sub long string split loop\n" if $debugon;
		@multiplemessages = split /(?(?{pos() % $maxlength})(?!))/, $response;
		print "message count: " . scalar(@multiplemessages) . "\n" if $debugon;
		# Remove leading/trailing whitespace now existing
		s/^\s+|\s+$//g for @multiplemessages;
	}
	return @multiplemessages;
}

sub cutintoirclengthmessagesandreturnarrayofthem2 {
	my ($message) = @_;
	my @splitmessages = ();
	while ($message =~ /(.{1,$ircmessagemaxlength})/gs) {
		push @splitmessages, $1;
	}
	print "message count: " . scalar(@splitmessages) . "\n" if $debugon;
	# Remove leading/trailing whitespace now existing
	s/^\s+|\s+$//g for @splitmessages;
	return @splitmessages;
}


sub send_to_openai_api_hack { # old bad, for testing long ago
	my ($input) = @_;
	my $response;
#	$response = `curl https://api.openai.com/v1/completions -H "Content-Type: application/json" -H "Authorization: Bearer SECRET" -d '{"model": "text-davinci-003", "prompt": "$input", "temperature": 0, "max_tokens": 150}'`;
	# cheaper model
	$response = `curl https://api.openai.com/v1/completions -H "Content-Type: application/json" -H "Authorization: Bearer SECRET" -d '{"model": "text-curie-001", "prompt": "$input", "temperature": 0, "max_tokens": 150}'`;
	print "responseviacurl: $response\n" if $debugon;
        my $json = decode_json($response);
	print "json: $json\n" if $debugon;
        my $text = $json->{'choices'}[0]->{'text'};
        # Remove any trailing newlines or whitespace
	print "text1: $text\n" if $debugon;
        $text =~ s/\n/ /g;
        #$text =~ s/\n//;
        $text =~ s/(\s\s)+//;
        $text =~ s/[\r\n]+\z//;
        $text =~ s/\s+\z//;
	print "text2: $text\n" if $debugon;
        return $text;
}

sub takemessagesendtolocalllamaandsendthatresponseout { # also sets the history/memory
	my ($message, $thingtosendtoonirc, $usernick, $gpt35turbo) = @_;
	unless ($usernick) {
		$usernick = $thingtosendtoonirc;
	}
	unless ($gpt35turbo) {
		$gpt35turbo = 0;
	}

	print "messagebeforeanything: $message\n";
	$message = cleanupusermessages($message);
	$message =~ s/$llamatrigger//;
	
	print "message: $message\n" if $debugon;
	# Ignore empty messages
	next if $message eq '';


	# get the history of the usernick or channel and append the current message input
	my $chathistorylines = "";
	print "User history for $thingtosendtoonirc:\n" if $debugon;
	foreach my $historymessage (@{$userhistory{$thingtosendtoonirc} // [] }) { #/#
		$chathistorylines = $chathistorylines . "$historymessage\n";
	}

	my $memorycontexthelper = "";
	unless ($gpt35turbo) {
		$memorycontexthelper = " Keep the sentences before this in mind, but only respond to the sentences after this. ";
	}
#	$chathistorylines = $chathistorylines . $memorycontexthelper;
	print "debughist: $chathistorylines\n" if $debugon;

	addtomessagehistory("$message", $thingtosendtoonirc);

	my $response;
	#$response = send_to_local_llama("$chathistorylines : \n$message");
	#print "ACTUALSENT:$chathistorylines : \n$message\n" if $debugon;
	print "ACTUALSENTTOLLAMA: $message\n" if $debugon;
	$response = send_to_local_llama("> $message\n");
	#$response = send_to_local_llama("$message\n");
	print "response: " . Dumper($response) . "\n" if $debugon;
	print "ACTUALRECEIVEFROMLLAMA: $response\n" if $debugon;


	$response =~ s/[\p{Cc}\p{Cf}]//g; # remove invisible text
	$response =~ s/\[0m\[0m>//g; # remove now visible remainder invisible text
	
	

	unless ($response) {
		print "\nstill no output from message [> $message\\n] back as response [$response]\n";
		$response = "7Bllama process died for some reason.";
		# maybe this will stop the crashes?
		#waitpid($pid, 0);
		#waitpid($pid, 0) if returnstrueifllamaiszombie($pid);
		# just kill it to be sure
		kill 'TERM', $pid;
		# restart the llama
		$pid = open2($out, $in, $program);
		$select = IO::Select->new($out);
		sleep 4;
		
		# try again
		print "ACTUALSENTTOLLAMA: $message\n" if $debugon;
		$response = send_to_local_llama("> $message\n");
		#$response = send_to_local_llama("$message\n");
		print "response: " . Dumper($response) . "\n" if $debugon;
		print "ACTUALRECEIVEFROMLLAMA: $response\n" if $debugon;
	}
	
	# Remove leading/trailing whitespace now existing
    $response =~ s/^\s+|\s+$//g;
    # throw away the bits that go off the rails.
    $response =~ s/###.*//s;

	# if it's too long cut it up and send many messages.
	if (length($response) > $ircmessagemaxlength) {
		my @irclengthmessages = cutintoirclengthmessagesandreturnarrayofthem($response);
		my $firstbitforthelog = $irclengthmessages[0];
		print $sock "PRIVMSG $thingtosendtoonirc :$_\r\n" for @irclengthmessages;
		addtomessagehistory("$firstbitforthelog ", $thingtosendtoonirc);
		#addtomessagehistory("SYSTEM:$firstbitforthelog ", $thingtosendtoonirc);
		next; # don't send the first part twice by letting this loop continue
	} else {
		# Respond to PM when the message length fits in irc limits and isn't long
		print $sock "PRIVMSG $thingtosendtoonirc :$response\r\n";
		print "PRIVMSG $thingtosendtoonirc :$response\r\n" if $debugon;

		addtomessagehistory("$response ", $thingtosendtoonirc);
		#addtomessagehistory("SYSTEM:$response ", $thingtosendtoonirc);
	}
}


sub send_to_local_llama {
	my ($input) = @_;

	if ($input =~ /SYSTEM:/)  {
		$input =~ s/SYSTEM://;
	}
	
	$input =~ s/\!llama//;
	$input =~ s/\n+$//;
	
	# Write to the program's STDIN
	print {$in} "$input\n";
	my $text = receive_alpaca();
	BEGIN {print "Still alive at " . __LINE__ . "\n"} 
	
	if ($text) {
		print "backfromllama: $text\n";
		return $text;
    } else {
		return "Error: the llama is dead.\n";
	}
}

sub receive_alpaca {
#	# restart the alpaca if needed
#	unless (check_pid($pid)) {
#		print "\n\nTrying a restart...\n\n";
#		$pid = open2($out, $in, $program);
#		$select = IO::Select->new($out);
#	}

	my $fulloutput ="";
	while (1) {
	    if ($select->can_read($timeout)) {
	        my $line = <$out>;
	        last unless defined $line;
	        #print "partial: $line";
	        #$line =~ s/[\p{Cc}\p{Cf}]//g; # remove invisible text

	        $fulloutput .= $line;
	        #print "growing: $fulloutput\n";
	    } else {
	      	# if it goes off rails with fine tuning style, just ignore it.
	      	print "internalllamaout: $fulloutput\n";
  			#$fulloutput =~ s/### Instruction:.*//s;
  			$fulloutput =~ s/###.*//s;
	    
	    	#my $pos = index($fulloutput, "> ");
			#if ($pos != -1) {
   			#	$fulloutput = substr($fulloutput, $pos + 2);
			#}
			
	        # Remove leading/trailing whitespace and prompt >
	        #$fulloutput =~ s/\e\[[0-9;]*[a-zA-Z]//g; ###### is this breaking unicode
			$fulloutput =~ s/\n/ /g;
			$fulloutput =~ s/^\s+|\s+$//g;
  			$fulloutput =~ s/[\r\n]+\z//;
  			$fulloutput =~ s/\s+\z//;
  			$fulloutput =~ s/^\> //;

  			# if it goes off rails responding multiple times, just ignore it.
  			if ($fulloutput =~ /^([^>]+)>([^>]+)/) {
  				$fulloutput = $1;
  				print "1:$1, 2:$2\n";			
  			}
	        last;
	    }
	}

	return "$fulloutput\n";
}

sub send_to_local_llama_api {
	my ($input) = @_;
	my $ua = LWP::UserAgent->new;
	#my $url = 'https://api.openai.com/v1/completions';
	my $url = 'http://127.0.0.1:8000/v1/completions';
	my $retry = 0;
	my $primedirectivelocal;

	if ($input =~ /SYSTEM:/)  {
		$input =~ s/SYSTEM://;
	}
	if ($input =~ /$llamatrigger/)  {
		$input =~ s/$llamatrigger//;
	}

	if (defined $primedirective) {
		#$primedirective = "You are an assistant who speaks like a human who is chatting on IRC with friends.";
		$input = "$primedirective\n" . $input;
	}

	#{
	#  "prompt": "\n\n### Instructions:\nWhat is the capital of France?\n\n### Response:\n",
	#  "stop": [
	#    "\n",
	#    "###"
	#  ]
	#}

	my %params = (
		prompt => "$input",
		max_tokens => 140,
		temperature => 0.7,
		stop => ['\n', '###', '!llama'],
		#stop => ['\n', '###', '!llama', '.', '!', '?'],
	);

	my $headers = HTTP::Headers->new(
		'Content-Type' => 'application/json',
		'Authorization' => "Bearer $openai_api_key",
	);
	$ua->default_headers($headers); # setting the headers this way does work
	#                                                      setting the headers this way doesn't work
	my $response = $ua->post($url, Content => encode_json(\%params), Header => $headers);
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        	my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
        	my $text = $json->{'choices'}[0]->{'text'};
		$text = cleanupopenaimessages($text);
		print "textdump: " . Dumper($text) . "\n" if $debugon;

        	return $text;
    	} else {
		return "Error: " . $response->status_line;
	}
}


sub send_to_local_llama_api_servercpp_alpaca {
	my ($input) = @_;
	my $ua = LWP::UserAgent->new;
	#my $url = 'https://api.openai.com/v1/completions';
	#my $url = 'http://127.0.0.1:8000/v1/completions';
	my $url = 'http://127.0.0.1:8080/completion';
	my $retry = 0;
	my $primedirectivelocal;

	if ($input =~ /SYSTEM:/)  {
		$input =~ s/SYSTEM://;
	}
	if ($input =~ /$llamatrigger/)  {
		$input =~ s/$llamatrigger//;
	}
	
	# convert $input message to alpaca format before sending for completion
	# one \n instead of two after $input because $input is "some message\n" already         
	$input = "\n\n### Instructions:\n$input\n### Response:\n";

	if (defined $primedirective) {
		#$primedirective = "You are an assistant who speaks like a human who is chatting on IRC with friends.";
		$input = "$primedirective\n" . $input;
	}

	#{
	#  "prompt": "\n\n### Instructions:\nWhat is the capital of France?\n\n### Response:\n",
	#  "stop": [
	#    "\n",
	#    "###"
	#  ]
	#}

	#--data '{"prompt": "Building a website can be done in 10 simple steps:","n_predict": 128}'
	my %params = (
		prompt => "$input",
		n_predict => 200,
		#max_tokens => 140,
		temperature => 0.7,
		stop => ['\n', '###'],
		#stop => ['\n', '###', '!llama'],
		#stop => ['\n', '###', '!llama', '.', '!', '?'],
	);

	my $headers = HTTP::Headers->new(
		'Content-Type' => 'application/json',
		'Authorization' => "Bearer $openai_api_key",
	);
	$ua->default_headers($headers); # setting the headers this way does work
	#                                                      setting the headers this way doesn't work
	my $response = $ua->post($url, Content => encode_json(\%params), Header => $headers);
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        	my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
        	#my $text = $json->{'choices'}[0]->{'text'};
        	my $text = $json->{'content'};
		$text = cleanupopenaimessages($text);
		print "textdump: " . Dumper($text) . "\n" if $debugon;

        	return $text;
    	} else {
		return "Error: " . $response->status_line;
	}
}

sub send_to_local_llama_api_alpaca {
	my ($input) = @_;
	my $ua = LWP::UserAgent->new;
	#my $url = 'https://api.openai.com/v1/completions';
	my $url = 'http://127.0.0.1:8000/v1/completions';
	my $retry = 0;
	my $primedirectivelocal;

	if ($input =~ /SYSTEM:/)  {
		$input =~ s/SYSTEM://;
	}
	if ($input =~ /$llamatrigger/)  {
		$input =~ s/$llamatrigger//;
	}
	
	# convert $input message to alpaca format before sending for completion
	# one \n instead of two after $input because $input is "some message\n" already         
	$input = "\n\n### Instructions:\n$input\n### Response:\n";

	if (defined $primedirective) {
		#$primedirective = "You are an assistant who speaks like a human who is chatting on IRC with friends.";
		$input = "$primedirective\n" . $input;
	}

	#{
	#  "prompt": "\n\n### Instructions:\nWhat is the capital of France?\n\n### Response:\n",
	#  "stop": [
	#    "\n",
	#    "###"
	#  ]
	#}

	my %params = (
		prompt => "$input",
		max_tokens => 190,
		temperature => 0.7,
		#stop => ['\n', '###', '!llama'],
		stop => ["\n", '###', '!llama', "  ", '  '],
		#stop => ['\n', '###', '!llama', '.', '!', '?'],
	);

	my $headers = HTTP::Headers->new(
		'Content-Type' => 'application/json',
		'Authorization' => "Bearer $openai_api_key",
	);
	$ua->default_headers($headers); # setting the headers this way does work
	#                                                      setting the headers this way doesn't work
	my $response = $ua->post($url, Content => encode_json(\%params), Header => $headers);
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        	my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
        	my $text = $json->{'choices'}[0]->{'text'};
		$text = cleanupopenaimessages($text);
		print "textdump: " . Dumper($text) . "\n" if $debugon;

        	return $text;
    	} else {
		return "Error: " . $response->status_line;
	}
}

sub send_to_local_llama_servercpp_api_tokenize {
	my ($input) = @_;
	
#curl --request POST \
#    --url http://127.0.0.1:8080/tokenize \
#    --data '{"content": "Test"}'	
		
	my $ua = LWP::UserAgent->new;
	#my $url = 'https://api.openai.com/v1/completions';
	my $url = 'http://127.0.0.1:8080/tokenize';
	my $retry = 0;
	my $primedirectivelocal;

	if ($input =~ /SYSTEM:/)  {
		$input =~ s/SYSTEM://;
	}
	if ($input =~ /$llamatrigger/)  {
		$input =~ s/$llamatrigger//;
	}
	$input =~ s/\!multi//;
	$input =~ s/\!multi\^//;


	#curl --request POST --url http://127.0.0.1:8080/tokenize --data '{"content": "Test"}'		
	my %params = (
		"content" => "$input"
	);

	my $headers = HTTP::Headers->new(
		'Content-Type' => 'application/json',
		'Authorization' => "Bearer $openai_api_key",
	);
	$ua->default_headers($headers); # setting the headers this way does work
	#                                                      setting the headers this way doesn't work
	my $response = $ua->post($url, Content => encode_json(\%params), Header => $headers);
	if ($response->is_success) {
		print "response: $response->content\n" if $debugon;
        my $json = decode_json($response->content);
		#print "json: $json\n" if $debugon;
		print "json: " . Dumper($json) . "\n" if $debugon;
        #my $text = $json->{'choices'}[0]->{'text'};
        #{"tokens":[3057]}
        #my $text = $json->{'tokens'}[0]->{'text'};
        my $tokens_ref = $json->{"tokens"};  # Access the array reference under "tokens"
		#my $text = join(", ", @$tokens_ref);  # Concatenate all values in the array
		my $text;
		#$text = cleanupopenaimessages($text);
		foreach my $number (@$tokens_ref) {
        	my $lookuptext = tokenvocablookup($json_file, $number);
        	$text .= "$number:$lookuptext ";
    	}	
		
		print "textdump: " . Dumper($text) . "\n" if $debugon;

        return $text;
    } else {
		return "Error: " . $response->status_line;
	}
}

# Define the subroutine to look up token by number
## Example usage
##my $json_file = "tokenizer.json";
##my $token_number = 8767;
##my $result = tokenvocablookup($json_file, $token_number);
##print "Token with number '$token_number' is: $result\n";
sub tokenvocablookup {
    my ($json_file, $token_number) = @_;

    # Read the JSON file
    open my $json_fh, '<', $json_file or die "Cannot open $json_file: $!";
    my $json_text = do { local $/; <$json_fh> };
    close $json_fh;

    # Parse the JSON data
    my $data = JSON::XS::decode_json($json_text);

    # Check if "vocab" exists in the JSON data
    if (exists $data->{model}{vocab}) {
        my $vocab = $data->{model}{vocab};
        
        # Iterate through the vocab to find the token with the given number
        foreach my $key (keys %$vocab) {
            if ($vocab->{$key} == $token_number) {
            	#       "\r": 30004, - "\r" is the key...
            	#$key =~ s/\\(?![nrt])/\\\\/g;
            	#$key =~ s/\\/\\\\/g;
           		$key = escape_special_characters($key);
                return $key;
            }
        }
    }
    return "Token number not found in vocabulary";
}

sub escape_special_characters {
    my ($string) = @_;
    # Replace special characters with their escaped representations
    $string =~ s/\\/\\\\/g;  # Double backslashes
    $string =~ s/\n/\\n/g;   # Newline
    $string =~ s/\r/\\r/g;   # Carriage return
    $string =~ s/\t/\\t/g;   # Tab
    return $string;
}

sub pullpage {
  my ($text) = @_;
  if ($text =~
m!(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])!
    ) {       # grab the link parts
    my $text_uri = "$1://$2$3";    # put the link back together
    my $cua = LWP::UserAgent->new(
         protocols_allowed => ['http', 'https'],
         timeout           => 3,
    );
    $cua->agent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59'
    );                             # so we look like a real browser
    $cua->max_size( 4000 );
    my $curi = URI->new($text_uri);
    my $cres = $cua->get($curi);
    if ($cres->is_success) {
      my $cdc =
        encode('utf-8', $cres->decoded_content()); # we get an error unless this is utf8
      my $page_body = untag($cdc);
      $page_body =~ s/\s+/ /g;
      return $page_body;
    }
  }
  else { return undef }
}

sub pullpage2 {
  my ($text) = @_;
  if ($text =~
m!(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])!
    ) {       # grab the link parts
    my $text_uri = "$1://$2$3";    # put the link back together
    my $cua = LWP::UserAgent->new(
         protocols_allowed => ['http', 'https'],
         timeout           => 3,
    );
    $cua->agent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59'
    );                             # so we look like a real'ish browser
    $cua->max_size( 4000 );
    my $curi = URI->new($text_uri);
    my $cres = $cua->get($curi);
    if ($cres->is_success) {
    	#print "\npre-prompt dump\n" if $debugon;
    	#print Dumper($cres->decoded_content) if $debugon;
    	return $cres->decoded_content;
    }
  }
  else { return undef }
}

sub untag {
  local $_ = $_[0] || $_;
  s{
    <               # open tag
    (?:             # open group (A)
      (!--) |       #   comment (1) or
      (\?) |        #   another comment (2) or
      (?i:          #   open group (B) for /i
        ( TITLE  |  #     one of start tags
          SCRIPT |  #     for which
          APPLET |  #     must be skipped
          OBJECT |  #     all content
          STYLE     #     to correspond
        )           #     end tag (3)
      ) |           #   close group (B), or
      ([!/A-Za-z])  #   one of these chars, remember in (4)
    )               # close group (A)
    (?(4)           # if previous case is (4)
      (?:           #   open group (C)
        (?!         #     and next is not : (D)
          [\s=]     #       \s or "="
          ["`']     #       with open quotes
        )           #     close (D)
        [^>] |      #     and not close tag or
        [\s=]       #     \s or "=" with
        `[^`]*` |   #     something in quotes ` or
        [\s=]       #     \s or "=" with
        '[^']*' |   #     something in quotes ' or
        [\s=]       #     \s or "=" with
        "[^"]*"     #     something in quotes "
      )*            #   repeat (C) 0 or more times
    |               # else (if previous case is not (4))
      .*?           #   minimum of any chars
    )               # end if previous char is (4)
    (?(1)           # if comment (1)
      (?<=--)       #   wait for "--"
    )               # end if comment (1)
    (?(2)           # if another comment (2)
      (?<=\?)       #   wait for "?"
    )               # end if another comment (2)
    (?(3)           # if one of tags-containers (3)
      </            #   wait for end
      (?i:\3)       #   of this tag
      (?:\s[^>]*)?  #   skip junk to ">"
    )               # end if (3)
    >               # tag closed
   }{}gsx;    # STRIP THIS TAG
  return $_ ? $_ : "";
}

sub uselessshit {
	unless ($preprompthistoryhasbeenset) {
		print "Setting pre-prompt\n";
		
#		addtomessagehistory("Hey, what's up?", $thingtosendtoonirc);
#		addtomessagehistory("Oh, not just much. Just lurking in IRC and thinking about positional encoding.", $thingtosendtoonirc);
#		addtomessagehistory("That's cool. I've always wondered about how large language models work. What model type are you?", $thingtosendtoonirc);
#		addtomessagehistory("I'm a llama 1 style large language model called Wizard-Vicuna-13B-Uncensored.ggmlv3.q4_K_M.bin but my friends call me llamar.", $thingtosendtoonirc);
#		addtomessagehistory("What is the atomic number of Neptunia?", $thingtosendtoonirc);
#		addtomessagehistory("93.", $thingtosendtoonirc);
		
		#setpreprompthistory();
		#my $array_ref = $userhistory{$thingtosendtoonirc};
		#push @$array_ref, "Hey, what's up?", "Oh, not just much. Just lurking in IRC and thinking about positional encoding.", "That's cool. I've always wondered about how large language models work. What model type are you?", "I'm a llama 1 style fine-tune called Wizard-Vicuna-13B-Uncensored/Wizard-Vicuna-13B-Uncensored.ggmlv3.q4_K_M.bin","What is the atomic number of Neptunia?","93";  # Add as many lines as needed
		$preprompthistoryhasbeenset = 1;
	}
}

sub startllamaserver {
	my $command = shift;
	unless ($command) {
		$command = "/home/superkuh/app_installs/llama.cpp-2023-10-31/llama.cpp/build/bin/server -m /home/superkuh/app_installs/llama.cpp/models/collectivecognition-v1.1-mistral-7b.Q4_K_M.gguf -c 2048 --port 8080 --threads 1 --n-gpu-layers 42";
	}
	my ($writer, $reader);
	# Start the external program
	my $pid = open2($reader, $writer, $command);
	# Later, to stop or close the process, you can send a signal, such as SIGTERM, to the process using the kill function
	#my $result = kill('TERM', $pid);
	#if ($result == 1) {
	#    print "The external program has been terminated.\n";
	#} else {
	#    print "Failed to terminate the external program.\n";
	#}
}
