<?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>
 
 
    
 
 |