| 
<?php
 $apiKey = "your-openai-api-key-here"; # set your openai key here
 
 // version: 1.0
 // filename: example.phpopenaichat.php
 // author: https://www.phpclasses.org/browse/author/144301.html
 // description: An application to hold conversations using the OpenAI API to access the GPT-3.5-Turbo model and the GPT-4 model.
 // license: BSD License
 
 // TODO: DONE. Conversation loading/saving
 // TODO: DONE. Edit previous message sections by clicking their div in the window to change their content
 // TODO: When contracting messages array to fit into maxtokens, start throwing away everything after first agent and first prompt.
 
 // TODO: A oneshot section for a single message that is not part of the conversation.
 // TODO: DONE. save/load agent
 /*
 Description:
 An application to hold conversations using the OpenAI API to access the GPT-3.5-Turbo model and the GPT-4 model.
 Very versatile and lets you edit the conversation's past to change the future responses.
 Let's you save/load/delete conversations and agents using JSON flatfiles.
 
 Programmed using PHP 8. Uses jQuery CDN for jQuery. PHP Curl required to connect to API with class.phpopenaichat.php.
 
 I found ChatGPT to be lacking, so I made my own.
 Messages in conversation only show an excerpt. Click on them to see the full message.
 */
 
 /*
 Features:
 - uses class class.phpopenaichat.php to drive the application's API request.
 - Persistent conversation using $_SESSION
 - Persistent controls that dynamically update on the server side whenever they are changed client side
 - Using jQuery to dynamically update the page without reloading it
 - Ability to save/load/delete conversations in a JSON file in the _saved directory (create in this program's root, and set as read/writeable
 - Ability to edit previous conversation messages by clicking on them in the window (Both agent and user messages can be edited)
 - Ability to save/load/delete agent into a JSON file in the _saved_agents directory (create in this program's root, and set as read/writeable
 - Minimal CSS styling for better code clarity
 - Ability to change the model, temperature, frequency penalty, presence penalty, and max tokens, previous conversation, and agent between prompts
 
 - frequency penalty, presence penalty let the AI know to not repeat itself or to not repeat the user's input
 - temperature is how random the AI's response is. 0.0 is the most predictable, 1.0+ is the most random
 - agents help the model align to a specific style of conversation. (ie. a doctor, a lawyer, a child, a teenager, a parent, etc.)
 
 - multiuser - Can run multiple instances at same time, but just use different browsers or a private window so that each instance has its own session.
 - GPT 4 is in beta at the moment, so you have to request access.
 - API access costs money, so be aware of that. (gpt 3.5 turbo is dirt cheap at the moment)
 - This is not secured for production use. It is just a proof of concept, and made for closed/private access from trusted individuals.
 
 - Using a command processor on the return from the server let's the server dynamically redefine the controls on the client side.
 - server has a copy of the clients data, so client's side is more of an indicator than a director.
 - API can take a few seconds to return a response, so be patient when it seems nothing is working after sending a prompt.
 
 
 devnotes: june 2023
 - added to example:
 - added support for gpt-3.5-turbo-16k (16k token model of gpt-3.5-turbo)
 - added support for gpt-4-32k (32k token model of gpt-4)
 
 */
 
 // start our session for persistent parts of the application
 ob_start(); session_start();
 
 // destroy session
 // session_destroy(); exit;
 
 // setting some defaults for the application
 if(!isset($_SESSION['messages']))
 $_SESSION['messages'] = []; // current conversation
 
 if(!isset($_SESSION['agent']))
 $_SESSION['agent'] = "You are a friendly and helpful assistant.";
 
 if(!isset($_SESSION['model']))
 $_SESSION['model'] = "gpt-3.5-turbo"; // default model
 
 if(!isset($_SESSION['temperature']))
 $_SESSION['temperature'] = 1.0; // default temperature
 
 if(!isset($_SESSION['freq_penalty']))
 $_SESSION['freq_penalty'] = 0.0; // default frequency penalty
 
 if(!isset($_SESSION['pres_penalty']))
 $_SESSION['pres_penalty'] = 0.0; // default presence penalty
 
 if(!isset($_SESSION['max_tokens']))
 $_SESSION['max_tokens'] = 4090; // default max tokens
 
 # ---
 
 require_once('class.phpopenaichat.php');
 // create new instance of PHPOpenAIChat
 $openAIChat = new PHPOpenAIChat($apiKey);
 
 // unused?... for now
 function send_and_append($prompt, $messages)
 {
 global $openAIChat;
 
 $response   = $openAIChat->sendMessage($messages);
 $text       = $openAIChat->get_response_text($response);
 $messages   = $openAIChat->append_response_to_messages($messages, $text);
 
 return $messages;
 } // end send_and_append()
 
 
 function html_messages($messages)
 {
 if(empty($messages))
 return '';
 
 $html = '
 <style>
 .messages {
 padding-bottom:200px;
 }
 
 .message_content textarea {
 width: 98%;
 /* height: 100%; */
 height:auto;
 border: none;
 background-color: transparent;
 }
 </style>
 
 
 ';
 $row_count = 0;
 $even_odd = "odd";
 foreach($messages as $message)
 {
 $the_content = htmlentities($message['content']);
 $html .= '<div class="message '.$even_odd.'" row_count="'.$row_count.'" >';
 $html .= '<div class="message_role">'.$message['role'].'</div>';
 $html .= '<div class="message_content"><textarea class="autoExpand" name="message_content_textarea" row_count="'.
 $row_count.'" onchange="btn_update_message_content(this);">'.
 $the_content.'</textarea></div>';
 $html .= '</div>';
 $row_count++;
 $even_odd = ($even_odd == "even") ? "odd" : "even";
 }
 return $html;
 } // end html_messages()
 
 function change_message_content($messages, $index, $content)
 {
 // will let us click and edit a message div and have ajax send back to server to change conversation history
 $messages[$index]['content'] = $content;
 return $messages;
 } // end change_message_content()
 
 
 function get_conversation_titles()
 {
 $titles = [];
 $files = glob('_saved/*.json');
 foreach($files as $file)
 {
 $json = file_get_contents($file);
 $json = json_decode($json, true);
 
 // substr out the _saved/ part of the filename
 $just_file = substr($file, strlen('_saved/')  );
 
 $titles[] = [
 'title' => $json['title'],
 'file' => $just_file
 ];
 }
 return $titles;
 } // end get_conversation_titles()
 
 function html_conversation_combobox()
 {
 // return the html for a dropdown combobox of all the saved conversations
 $titles = get_conversation_titles();
 $html = '<select id="conversation_combobox" name="conversation_combobox" >';
 foreach($titles as $title)
 {
 $html .= '<option value="'.$title['file'].'">'.$title['title'].'</option>';
 }
 $html .= '</select>';
 return $html;
 }
 
 function get_agents_titles()
 {
 $titles = [];
 $files = glob('_saved_agents/*.json');
 foreach($files as $file)
 {
 $json = file_get_contents($file);
 $json = json_decode($json, true);
 
 // substr out the _agents/ part of the filename
 $just_file = substr($file, strlen('_saved_agents/')  );
 
 $titles[] = [
 'title' => $json['title'],
 'file' => $just_file
 ];
 }
 return $titles;
 } // end get_agents_titles()
 
 function html_agents_combobox()
 {
 // return the html for a dropdown combobox of all the saved agents
 $titles = get_agents_titles();
 $html = '<select id="agents_combobox" name="agents_combobox" >';
 foreach($titles as $title)
 {
 $html .= '<option value="'.$title['file'].'">'.$title['title'].'</option>';
 }
 $html .= '</select>';
 return $html;
 } // end html_agents_combobox()
 
 # ---
 # gets ajax request and then returns json data and quits.
 if(!empty($_GET['ajax']))
 {
 switch($_GET['ajax'])
 {
 case 'delete_conversation':
 $file = $_POST['flds']['file'];
 $file = trim($file);
 
 if(empty($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "delete_conversation",
 "msg"         => "file empty"
 );
 break; // error: no file specified
 }
 $file = '_saved/'.$file;
 // if file doesn't exist error out
 if(!file_exists($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "delete_conversation",
 "msg"         => "file does not exist"
 );
 break; // error: file does not exist
 }
 
 unlink($file);
 
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "delete_conversation",
 "msg"         => "file deleted"
 );
 
 $return_arr[] = array(
 "command"     => 'html',
 "selector"     => '#conversation_combobox',
 "msg"         => html_conversation_combobox()
 );
 
 break;
 
 case 'load_conversation':
 $file = $_POST['flds']['file'];
 $file = trim($file);
 
 if(empty($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "load_conversation",
 "msg"         => "file empty"
 );
 break; // error: no file specified
 }
 $file = '_saved/'.$file;
 // if file doesn't exist error out
 if(!file_exists($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "load_conversation",
 "msg"         => "file does not exist"
 );
 break; // error: file does not exist
 }
 
 $json = file_get_contents($file);
 $json = json_decode($json, true);
 $messages = $json['messages'];
 $_SESSION['messages'] = $messages;
 $messages_html = html_messages($messages);
 $return_arr[] = array(
 "command"     => 'html',
 "process"     => "load_conversation",
 "selector"     => '.output',
 "msg"         => $messages_html
 );
 
 // update textareas
 $return_arr[] = array(
 "command"     => 'resize_textareas',
 "process"     => "load_conversation"
 );
 
 
 
 
 break;
 
 case 'save_conversation':
 $conversation_title = $_POST['flds']['title'];
 $messages = $_SESSION['messages'];
 // now create a folder named '_saved' and save the conversation as a json file in that folder
 // file will be a timestamp_hash.json
 // json will be format:
 // "title": "conversation title",
 // "messages": {$_SESSION['messages']}
 
 // create folder if it doesn't exist
 if(!file_exists('_saved'))
 @mkdir('_saved');
 
 // if dir doesn't exist... abort with alert error
 if(!file_exists('_saved'))
 {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "save_conversation",
 "msg"         => "could not create _saved folder"
 );
 break; // error: could not create _saved folder
 }
 
 // create a unique filename
 $filename = time().'_'.md5(time()).'.json';
 $filepath = '_saved/'.$filename;
 
 // save the file...
 $json_data = array(
 "title"     => $conversation_title,
 "messages"  => $messages
 );
 $json_data = json_encode($json_data);
 
 // store it to file
 $result = file_put_contents($filepath, $json_data);
 
 
 // create json data
 $json_data = array(
 "title"     => $conversation_title,
 "messages"  => $messages
 );
 
 
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "save_conversation",
 "msg"         => "saved conversation"
 );
 
 $return_arr[] = array(
 "command"     => 'val',
 'selector'     => 'input[name="save-message_title"]',
 'msg'         => ''
 );
 
 $return_arr[] = array(
 "command"     => 'html',
 "selector"     => '#conversation_combobox',
 "msg"         => html_conversation_combobox()
 );
 
 break; // case 'save_conversation'
 
 
 
 case 'change_message_content':
 $index = $_POST['flds']['row_count'];
 $content = $_POST['flds']['message_content'];
 
 $messages = $_SESSION['messages'];
 $messages = change_message_content($messages, $index, $content);
 $_SESSION['messages'] = $messages;
 
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "change_message_content",
 "msg"         => "changed message content",
 "index"     => $index,
 "content"     => $content
 );
 break; // case 'change_message_content'
 
 case 'update_conversation':
 $messages = $_SESSION['messages'];
 $html_messages = html_messages($messages);
 $return_arr[] = array(
 "command"     => 'html',
 'selector'     => '.output',
 'msg'         => $html_messages
 );
 break; // case 'update_conversation'
 
 case 'delete_agent':
 $file = $_POST['flds']['file'];
 $file = trim($file);
 
 if(empty($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "delete_agent",
 "msg"         => "file empty"
 );
 break; // error: no file specified
 }
 $file = '_saved_agents/'.$file;
 // if file doesn't exist error out
 if(!file_exists($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "delete_agent",
 "msg"         => "file does not exist"
 );
 break; // error: file does not exist
 }
 
 unlink($file);
 
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "delete_agent",
 "msg"         => "file deleted"
 );
 
 $return_arr[] = array(
 "command"     => 'html',
 "selector"     => '#agents_combobox',
 "msg"         => html_agents_combobox()
 );
 
 break;
 
 case 'save_agent':
 $agent_name = $_POST['flds']['title'];
 $agent = $_SESSION['agent'];
 
 // create folder if it doesn't exist
 if(!file_exists('_saved_agents'))
 @mkdir('_saved_agents');
 
 // if dir doesn't exist... abort with alert error
 if(!file_exists('_saved_agents'))
 {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "save_agent",
 "msg"         => "could not create _saved_agents folder"
 );
 break; // error: could not create _saved_agents folder
 }
 
 
 // create a unique filename
 $filename = time().'_'.md5(time()).'.json';
 $filepath = '_saved_agents/'.$filename;
 
 // save the file...
 $json_data = array(
 "title"     => $agent_name,
 "agent"     => $agent
 );
 $json_data = json_encode($json_data);
 
 // store it to file
 $result = file_put_contents($filepath, $json_data);
 
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "save_agent",
 "msg"         => "saved agent"
 );
 
 $return_arr[] = array(
 "command"     => 'val',
 'selector'     => 'input[name="save-agent_title"]',
 'msg'         => ''
 );
 
 $return_arr[] = array(
 "command"     => 'html',
 "selector"     => '#agents_combobox',
 "msg"         => html_agents_combobox()
 );
 
 
 break; // case 'save_assistant'
 
 case 'load_agent':
 $file = $_POST['flds']['file'];
 $file = trim($file);
 
 if(empty($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "load_agent",
 "msg"         => "file empty"
 );
 break; // error: no file specified
 }
 $file = '_saved_agents/'.$file;
 // if file doesn't exist error out
 if(!file_exists($file)) {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "load_agent",
 "msg"         => "file does not exist"
 );
 break; // error: file does not exist
 }
 
 $json = file_get_contents($file);
 $json = json_decode($json, true);
 
 $agent = $json['agent'];
 $_SESSION['agent'] = $agent;
 
 $return_arr[] = array(
 "command"     => 'val',
 "process"     => "load_agent",
 "selector"     => '#text_agent',
 "msg"         => $agent
 );
 
 
 
 break; // case 'load_agent'
 
 
 case 'one-shot':
 // function one_shot($api_key, $prompt, $agent='You are a helpful assistant.', $temperature=1.0, $max_tokens=4000, $model="gpt-3.5-turbo")
 // returns text
 /*
 "prompt": $('textarea[name="prompt"]').val(),
 "agent": $('textarea[name="agent"]').val(),
 "temperature": $('input[name="temperature"]').val(),
 "max_tokens": $('input[name="max_tokens"]').val(),
 */
 global $apiKey;
 $prompt = $_POST['flds']['prompt'];
 $agent = $_POST['flds']['agent'];
 $temperature = $_POST['flds']['temperature'];
 $max_tokens = 4000;
 $model = $_POST['flds']['model'];
 $api_key = $apiKey;
 
 // trim and clean $prompt
 $prompt = trim($prompt);
 
 // if prompt empty return error
 $text = one_shot($api_key, $prompt, $agent, $temperature, $max_tokens, $model);
 
 $return_arr[] = array(
 "command"     => 'val',
 "process"     => "one-shot",
 "selector"     => 'textarea[name="response"]',
 "msg"         => $text
 );
 
 $return_arr[] = array(
 'command' => 'enable_input',
 'selector' => '.one_shot .the-button'
 );
 
 
 break; // case 'one-shot'
 // ---------------------------------------------------------------------
 case 'prompt':
 $prompt = $_POST['flds']['prompt'];
 
 // trim and clean $prompt
 $prompt = trim($prompt);
 
 // if prompt empty return error
 if(empty($prompt))
 {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "prompt",
 "msg"         => "prompt is empty"
 );
 } else {
 $messages = $_SESSION['messages'];
 
 // first set agent
 $messages = $openAIChat->set_agent($messages, $_SESSION['agent']);
 
 $openAIChat->model          = $_SESSION['model'];
 
 $openAIChat->temperature    = (float) round($_SESSION['temperature'],1);
 $openAIChat->freq_penalty   = (float) round($_SESSION['freq_penalty'],1);
 $openAIChat->pres_penalty   = (float) round($_SESSION['pres_penalty'],1);
 
 $openAIChat->set_max_tokens( 4090 );
 
 // print_r($prompt);
 
 // add prompt to messages conversation
 $messages   = $openAIChat->add_prompt_to_messages($messages, $prompt);
 
 // print("\r\n\r\n");
 // print_r($messages);
 
 $response   = $openAIChat->sendMessage($messages);
 $text       = $openAIChat->get_response_text($response);
 
 // print_r($response);
 // print("\r\n\r\n-----------\r\n\r\n");
 // print($text);
 
 // if text empty return error
 if(empty($text))
 {
 $return_arr[] = array(
 "command"     => 'alert',
 "process"     => "prompt",
 "msg"         => "api error:returned nothing",
 "model"     => $_SESSION['model'],
 "response"     => $response
 );
 break;
 }
 
 // append response to messages conversation
 $messages = $openAIChat->append_response_to_messages($messages, $text);
 
 // save messages to session
 $_SESSION['messages'] = $messages;
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "prompt",
 "prompt"     => $prompt,
 "agent"     => $_SESSION['agent'],
 "model"     => $_SESSION['model'],
 "temperature"     => $_SESSION['temperature'],
 "freq_penalty"     => $_SESSION['freq_penalty'],
 "pres_penalty"     => $_SESSION['pres_penalty'],
 "max_tokens"     => $_SESSION['max_tokens'],
 "text"         => $text,
 "response"     => $response
 );
 
 $return_arr[] = array(
 "command"     => 'update_conversation'
 );
 
 $return_arr[] = array(
 "command"     => 'val',
 'selector'     => 'textarea[name="text_prompt"]',
 'msg'         => ''
 );
 
 $return_arr[] = array(
 'command' => 'html',
 'selector' => '.output',
 'msg' => html_messages($messages)
 );
 
 $return_arr[] = array(
 'command' => 'enable_input',
 'selector' => '#send-button'
 );
 
 $return_arr[] = array(
 'command' => 'resize_textareas'
 );
 
 
 }
 
 break; // case 'prompt'
 // ---------------------------------------------------------------------
 
 case 'change_agent':
 $agent = $_POST['flds']['agent'];
 
 $_SESSION['agent'] = $agent;
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "change_agent"
 );
 
 $return_arr[] = array(
 "command"     => 'response_text',
 "agent"     => $agent
 );
 break; // case 'change_agent'
 
 case 'change_model':
 $model = $_POST['flds']['model'];
 
 $_SESSION['model'] = $model;
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "change_model",
 "model"     => $model
 );
 
 
 break; // case 'change_model'
 
 
 case 'change_temperature':
 $temperature = $_POST['flds']['temperature'];
 
 $_SESSION['temperature'] = $temperature;
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "change_temperature",
 "temperature"     => $temperature
 );
 
 print("TEMP CHANGED");
 
 break; // case 'change_temperature'
 
 case 'change_freq_penalty':
 $freq_penalty = $_POST['flds']['freq_penalty'];
 
 $_SESSION['freq_penalty'] = $freq_penalty;
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "change_freq_penalty",
 "freq_penalty"     => $freq_penalty
 );
 
 break; // case 'change_freq_penalty'
 
 case 'change_pres_penalty':
 $pres_penalty = $_POST['flds']['pres_penalty'];
 
 $_SESSION['pres_penalty'] = $pres_penalty;
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "change_pres_penalty",
 "pres_penalty"     => $pres_penalty
 );
 
 break; // case 'change_pres_penalty'
 
 case 'change_max_tokens':
 $max_tokens = $_POST['flds']['max_tokens'];
 
 $_SESSION['max_tokens'] = $max_tokens;
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "change_max_tokens",
 "max_tokens"     => $max_tokens
 );
 
 break; // case 'change_max_tokens'
 
 
 case 'reset_messages':
 $_SESSION['messages'] = [];
 
 
 $return_arr[] = array(
 "command"     => 'success',
 "process"     => "reset_messages"
 );
 
 $return_arr[] = array(
 "command" => 'html',
 'selector' => '.output',
 'msg' => html_messages($_SESSION['messages'])
 );
 
 break; // case 'reset_messages'
 
 } // end switch($_GET['ajax'])
 
 if(!empty($return_arr) && is_array($return_arr))
 die(json_encode($return_arr));
 
 die();
 
 } // end if(!empty($_GET['ajax']))
 
 
 
 ?>
 
 
 <!DOCTYPE html>
 <html lang="en">
 <head>
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
 <script>
 // BEGIN --> JAVASCRIPT COMMAND PROCESSOR //
 function do_cmd_post(url, send_data)
 {
 $.post( url, { flds: send_data /* in php will appear as $_POST['flds']  */ },
 function( return_data ) {
 do_cmd_process(return_data);
 }, "json" ); // punt any returned data to processor
 
 }
 // ---
 function do_cmd_process(data) // handle data coming back from ajax
 {
 
 if (data instanceof Array) {
 data.forEach(function(entry) {
 console.log(entry.command);
 
 //console.log(entry.message);
 // handle returned commands //
 switch(entry.command)
 {
 // generic commands //
 case 'alert':
 alert(entry.msg);
 break;
 case 'log':
 console.log(entry.msg);
 break;
 case 'append':
 $(entry.selector).append(entry.msg);
 break;
 case 'prepend':
 $(entry.selector).prepend(entry.msg);
 break;
 case 'html':
 $(entry.selector).html(entry.msg);
 break;
 case 'val':
 $(entry.selector).val(entry.msg);
 break;
 case 'focus':
 $(entry.selector).focus();
 break;
 case 'blur':
 $(entry.selector).blur();
 break;
 case 'clear':
 $(entry.selector).val('');
 break;
 case 'js':
 eval(entry.msg);
 break;
 case 'resize_textarea_to_fit_contents':
 $(entry.selector).height(0);
 $(entry.selector).height($(entry.selector)[0].scrollHeight);
 break;
 case 'disable_input':
 $(entry.selector).prop('disabled', true);
 break;
 case 'enable_input':
 $(entry.selector).prop('disabled', false);
 break;
 
 case 'resize_textareas':
 $(".message_content textarea").each(function(){
 $(this).css("height", ($(this).prop("scrollHeight")) + "px");
 });
 
 break;
 
 } // end : switch (entry.command)
 }); // end : data.forEach(function(entry)
 } // end : if (data instanceof Array)
 } // end : function do_cmd_process(data)
 
 
 // END --> JAVASCRIPT COMMAND PROCESSOR //
 
 
 </script>
 
 
 </head>
 <body>
 
 
 <br /><br />
 
 <?php
 
 ?>
 
 <div class='output' id="output" style='width: 100%; overflow: scroll; border: 1px solid #000;'><?php echo html_messages($_SESSION['messages']);  ?></div>
 <div class='row'>
 <input type='text' name='save-message_title' id='save-message_title' value='' placeholder='Save Conversation Title' />
 <input type='button' value='Save Conversation' onclick='btn_save_conversation();' />
 <?php  echo html_conversation_combobox(); ?>
 <input type='button' value='Load Conversation' onclick='btn_load_conversation();' />
 <input type='button' value='Delete Conversation' onclick='btn_delete_conversation();' />
 <input type='button' value='Reset Conversation' onclick='btn_reset_messages();' />
 </div>
 <br />
 <br />
 
 <textarea name="text_agent" id='text_agent'
 onchange="btn_change_agent();"
 style='width: 100%; height: 100px; overflow: scroll; border: 1px solid #000;'
 ><?php echo $_SESSION['agent']; ?></textarea>
 <div class='row'>
 <input type='text' name='save-agent_title' id='save-agent_title' value='' placeholder='Save Agent Title' />
 <input type='button' value='Save Agent' onclick='btn_save_agent();' />
 <?php  echo html_agents_combobox(); ?>
 <input type='button' value='Load Agent' onclick='btn_load_agent();' />
 <input type='button' value='Update Agent' onclick='btn_change_agent();'  />
 <input type='button' value='Delete Agent' onclick='btn_delete_agent();'  />
 </div>
 
 
 <br /><br />
 
 <textarea name="text_prompt" style='width: 100%; height: 100px; overflow: scroll; border: 1px solid #000;'></textarea>
 <input type='button' id='send-button' value='Send Prompt' onclick='btn_send_prompt();' />
 <br /><br />
 
 
 <!-- a combo box to select between gpt-3.5-turbo and gpt-4 -->
 <select id="model" name="model" onchange="btn_change_model();">
 <option value="gpt-3.5-turbo" <?php if($_SESSION['model']=="gpt-3.5-turbo") echo "SELECTED" ?> >gpt-3.5-turbo</option>
 <option value="gpt-3.5-turbo-16k" <?php if($_SESSION['model']=="gpt-3.5-turbo-16k") echo "SELECTED" ?> >gpt-3.5-turbo-16k</option>
 <option value="gpt-4" <?php if($_SESSION['model']=="gpt-4") echo "SELECTED" ?>>gpt-4</option>
 <option value="gpt-4-32k" <?php if($_SESSION['model']=="gpt-4-32k") echo "SELECTED" ?>>gpt-4-32k</option>
 </select>
 
 
 <script>
 function btn_delete_agent()
 {
 // confirm delete //
 if(!confirm('Are you sure you want to delete this agent?'))
 return;
 
 var send_data   = {
 "file": $('select[name="agents_combobox"]').val()
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=delete_agent', send_data);
 }
 
 function btn_save_agent()
 {
 // data already on server so just send a title.
 var send_data   = {
 "title": $('input[name="save-agent_title"]').val()
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=save_agent', send_data);
 }
 
 function btn_load_agent()
 {
 var send_data   = {
 "file": $('select[name="agents_combobox"]').val()
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=load_agent', send_data);
 }
 
 
 function btn_delete_conversation()
 {
 // confirm delete
 if(!confirm('Are you sure you want to delete this conversation?'))
 return;
 
 var send_data   = {
 "file": $('select[name="conversation_combobox"]').val()
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=delete_conversation', send_data);
 }
 
 function btn_load_conversation()
 {
 var send_data   = {
 "file": $('select[name="conversation_combobox"]').val()
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=load_conversation', send_data);
 }
 
 function btn_save_conversation()
 {
 // data already on server so just send a title.
 // if title empty, alert and abort.
 if($('input[name="save-message_title"]').val()=='')
 {
 alert('Please enter a title for this conversation.');
 return;
 }
 
 var send_data   = {
 "title": $('input[name="save-message_title"]').val()
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=save_conversation', send_data);
 }
 
 function btn_send_prompt()
 {
 // disable #send-button
 $('#send-button').prop('disabled', true);
 
 // confirm
 if(!confirm('Are you sure you want to send this prompt?'))
 {
 $('#send-button').prop('disabled', false);
 return;
 }
 
 var send_data   = {
 "prompt": $('textarea[name="text_prompt"]').val(),
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=prompt', send_data);
 }
 
 function btn_change_agent()
 {
 // confirm changes
 if(!confirm('Are you sure you want to change the agent on the server? (server must have an agent sent for it to process an agent)'))
 return;
 
 var send_data   = {
 "agent": $('textarea[name="text_agent"]').val(),
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=change_agent', send_data);
 }
 
 function btn_change_model()
 {
 var send_data   = {
 "model": $('select[name="model"]').val(),
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=change_model', send_data);
 }
 
 function btn_change_temp()
 {
 var send_data   = {
 "temperature": $('input[name="temperature"]').val(),
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=change_temperature', send_data);
 }
 
 function btn_change_freq_penalty()
 {
 var send_data   = {
 "freq_penalty": $('input[name="freq_penalty"]').val(),
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=change_freq_penalty', send_data);
 }
 
 function btn_change_pres_penalty()
 {
 var send_data   = {
 "pres_penalty": $('input[name="pres_penalty"]').val(),
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=change_pres_penalty', send_data);
 }
 
 function btn_reset_messages()
 {
 var send_data   = {
 "reset_messages": 1,
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=reset_messages', send_data);
 }
 
 function btn_max_tokens()
 {
 var send_data   = {
 "max_tokens": $('input[name="max_tokens"]').val(),
 };
 
 do_cmd_post('example.phpopenaichat.php?ajax=change_max_tokens', send_data);
 }
 
 
 function btn_edit_in_place()
 {
 /*
 $html .= '<div class="message" row_count="'+$row_count+'" >';
 $html .= '<div class="message_role">'.$message['role'].'</div>';
 $html .= '<div class="message_content" onclick="btn_edit_in_place();">'.$message['content'].'</div>';
 $html .= '</div>';
 */
 
 /* swap out the div with a textarea */
 var row_count = 1;
 var message_content = $('div.message_content[row_count="'+row_count+'"]').html();
 $('div.message_content[row_count="'+row_count+'"]').html('<textarea name="message_content" row_count="'+row_count+'" style="width: 100%; height: 100px; overflow: scroll; border: 1px solid #000;">'+message_content+'</textarea>');
 
 
 }
 
 function btn_update_message_content(that)
 {
 the_row = $(that).attr('row_count');
 the_msg = $(that).val();
 // var row_count = $('textarea[name="message_content"]').attr('row_count');
 // var message_content = $('textarea[name="message_content_textarea"]').val();
 
 // alert(the_msg);
 // alert(the_row);
 
 var send_data   = {
 "row_count": the_row,
 "message_content": the_msg,
 };
 
 
 // get user confirmation to continue
 if(confirm("Are you sure you want to update the message content?"))
 {
 do_cmd_post('example.phpopenaichat.php?ajax=change_message_content', send_data);
 } else {
 alert("reload page to restore original message content.");
 }
 
 
 }
 
 
 </script>
 
 <br /><br />
 
 <!-- a slider to select temperature -->
 <label for="temperature">Temperature</label>
 <input type="range" id="temperature" onchange="btn_change_temp();" name="temperature" min="0.0" max="2.0" step="0.1" value="<?php echo $_SESSION["temperature"] ?>">
 <div id="temperature_value"></div>
 
 <!-- a slider to select frequency penalty -->
 <label for="freq_penalty">Frequency Penalty</label>
 <input type="range" id="freq_penalty" onchange="btn_change_freq_penalty();" name="freq_penalty" min="0.0" max="1.0" step="0.1" value="<?php echo $_SESSION["freq_penalty"] ?>">
 <div id="freq_penalty_value"></div>
 
 <!-- a slider to select presence penalty -->
 <label for="pres_penalty">Presence Penalty</label>
 <input type="range" id="pres_penalty" onchange="btn_change_pres_penalty();" name="pres_penalty" min="0.0" max="1.0" step="0.1" value="<?php echo $_SESSION["pres_penalty"] ?>">
 <div id="pres_penalty_value"></div>
 
 <!-- a text input to select max tokens -->
 <label for="max_tokens">Max Tokens</label>
 <input type="number" id="max_tokens" onchange="btn_max_tokens();" name="max_tokens" min="1" max="100" value="<?php echo $_SESSION["max_tokens"] ?>">
 <br /><br />
 
 
 <!-- reset messages button -->
 <input type='button' value='Reset Messages' onclick='btn_reset_messages();' />
 <br /><br />
 
 
 <!-- jquery to add a div under the sliders to show the current value of the sliders. -->
 <script>
 $(document).ready(function(){
 $('#temperature_value').html($('#temperature').val());
 $('#freq_penalty_value').html($('#freq_penalty').val());
 $('#pres_penalty_value').html($('#pres_penalty').val());
 });
 </script>
 
 <!-- jquery to update the divs when the sliders are moved -->
 <script>
 $(document).ready(function(){
 $('#temperature').on('input', function() {
 $('#temperature_value').html($('#temperature').val());
 });
 $('#freq_penalty').on('input', function() {
 $('#freq_penalty_value').html($('#freq_penalty').val());
 });
 $('#pres_penalty').on('input', function() {
 $('#pres_penalty_value').html($('#pres_penalty').val());
 });
 });
 </script>
 
 <pre>
 <?php
 // print_r($_SESSION['messages']);
 ?>
 </pre>
 
 
 <script>
 // make tab character act like a normal tab character in textareas
 
 $(document).delegate('textarea', 'keydown', function(e) {
 var keyCode = e.keyCode || e.which;
 
 if (keyCode == 9) {
 e.preventDefault();
 var start = $(this).get(0).selectionStart;
 var end = $(this).get(0).selectionEnd;
 
 // set textarea value to: text before caret + tab + text after caret
 $(this).val($(this).val().substring(0, start)
 + "\t"
 + $(this).val().substring(end));
 
 // put caret at right position again
 $(this).get(0).selectionStart =
 $(this).get(0).selectionEnd = start + 1;
 }
 });
 
 
 </script>
 
 
 
 
 <script>
 // javascript to handle expanding textarea to fit content height whenever focus is on it and user is typing
 $(document).ready(function(){
 // $(".message_content textarea").css("height", "auto");
 // set each textarea on load to the size of its contents in the .messages area
 $(".message_content textarea").each(function(){
 $(this).css("height", ($(this).prop("scrollHeight")) + "px");
 });
 
 // handle dynamic loaded divs
 
 
 
 $(".message_content textarea").on("focus", function(){
 // $(this).css("height", "auto");
 $(this).css("height", ($(this).prop("scrollHeight")) + "px");
 });
 
 $(".message_content textarea").on("blur", function(){
 // $(this).css("height", "auto");
 $(this).css("height", ($(this).prop("scrollHeight")) + "px");
 });
 
 
 $(".message_content textarea").on("change", function(){
 // $(this).css("height", "auto");
 $(this).css("height", ($(this).prop("scrollHeight")) + "px");
 });
 
 
 // $(".message_content textarea").on("keyup", function(){
 //     $(this).css("height", "auto");
 //     // $(this).css("height", ($(this).prop("scrollHeight")) + "px");
 // });
 
 
 
 
 });
 </script>
 
 
 <style>
 .messages {
 }
 
 .message {
 border-bottom: 3px solid red;
 }
 
 .message.even {
 background-color: #eee;
 }
 
 .message_content {
 padding:3vw;
 }
 </style>
 
 
 
 
 <!-- one shot section -->
 <hr />
 <?php
 function one_shot($api_key, $prompt, $agent='You are a helpful assistant.', $temperature=1.0, $max_tokens=4000, $model="gpt-3.5-turbo")
 {
 $temperature    = (float) round($temperature,1);
 $max_tokens     = (int) $max_tokens;
 
 
 // if $prompt empty return '' else get response and return the text...
 $messages = [];
 $AIChat = new PHPOpenAIChat($api_key);
 
 $AIChat->model = $model;
 $AIChat->temperature = $temperature;
 $AIChat->set_max_tokens($max_tokens);
 
 $AIChat->set_agent($messages, $agent);
 $messages = $AIChat->add_prompt_to_messages($messages, $prompt);
 
 
 if (empty($prompt))
 return '';
 else
 {
 $response   = $AIChat->sendMessage($messages);
 $text       = $AIChat->get_response_text($response);
 // print_r($response);
 return $text;
 }
 
 } // end one_shot()
 
 
 ?>
 <style>
 .one_shot {
 display: grid;
 grid-template-columns: 1fr 1fr 1fr;
 grid-gap: 1vw;
 }
 .one_shot .col {
 padding: 1vw;
 }
 .one_shot textarea {
 width: 100%;
 height: 10vw;
 }
 .one_shot input {
 width: 100%;
 }
 
 
 </style>
 <div class='title'>One Shot Section - Enter a prompt and get a single one-off response</div>
 <div class='one_shot'>
 <div class='col'><textarea class='prompt' name='prompt' id='prompt' placeholder='prompt'></textarea></div>
 <div class='col'><textarea class='agent' name='agent' id='agent' placeholder='agent'></textarea></div>
 <div class='col'><textarea class='response' name='response' id='response' placeholder='response'></textarea></div>
 
 <!-- model combo box -->
 <!-- a combo box to select between gpt-3.5-turbo and gpt-4 -->
 <select id="model" name="model" onchange="btn_change_model();">
 <option value="gpt-3.5-turbo" <?php if($_SESSION['model']=="gpt-3.5-turbo") echo "SELECTED" ?> >gpt-3.5-turbo</option>
 <option value="gpt-4" <?php if($_SESSION['model']=="gpt-4") echo "SELECTED" ?>>gpt-4</option>
 </select>
 <div class='col'><input type='text' class='temperature' name='temperature' id='temperature' placeholder='temperature' value='1.0' /></div>
 
 <div class='col'><input type='button' class='the-button' value='One Shot' onclick='btn_one_shot();' /></div>
 
 </div><!-- end one shot section -->
 
 <script>
 function btn_one_shot()
 {
 var send_data   = {
 "prompt": $('textarea[name="prompt"]').val(),
 "agent": $('textarea[name="agent"]').val(),
 "temperature": $('input[name="temperature"]').val(),
 "max_tokens": $('input[name="max_tokens"]').val(),
 "model": $('select[name="model"]').val(),
 };
 
 // disable #send-button
 $('.one_shot .the-button').prop('disabled', true);
 
 do_cmd_post('example.phpopenaichat.php?ajax=one-shot', send_data);
 }
 </script>
 
 </body>
 </html>
 
 
 |