1<?php 2 3use dokuwiki\plugin\aichat\Embeddings; 4use dokuwiki\plugin\aichat\OpenAI; 5use Hexogen\KDTree\FSKDTree; 6use Hexogen\KDTree\FSTreePersister; 7use Hexogen\KDTree\Item; 8use Hexogen\KDTree\ItemFactory; 9use Hexogen\KDTree\ItemList; 10use Hexogen\KDTree\KDTree; 11use Hexogen\KDTree\NearestSearch; 12use Hexogen\KDTree\Point; 13use splitbrain\phpcli\Colors; 14use splitbrain\phpcli\Options; 15 16require_once __DIR__ . '/vendor/autoload.php'; 17 18/** 19 * DokuWiki Plugin aichat (CLI Component) 20 * 21 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 22 * @author Andreas Gohr <gohr@cosmocode.de> 23 */ 24class cli_plugin_aichat extends \dokuwiki\Extension\CLIPlugin 25{ 26 27 /** @inheritDoc */ 28 protected function setup(Options $options) 29 { 30 $options->setHelp('Manage the AI chatbot data'); 31 32 $options->registerCommand('embed', 'Create embeddings for all pages'); 33 34 $options->registerCommand('similar', 'Search for similar pages'); 35 $options->registerArgument('query', 'Look up chunks similar to this query', true, 'similar'); 36 37 $options->registerCommand('ask', 'Ask a question'); 38 $options->registerArgument('question', 'The question to ask', true, 'ask'); 39 40 $options->registerCommand('chat', 'Start an interactive chat session'); 41 } 42 43 /** @inheritDoc */ 44 protected function main(Options $options) 45 { 46 switch ($options->getCmd()) { 47 48 case 'embed': 49 $this->createEmbeddings(); 50 break; 51 case 'similar': 52 $this->similar($options->getArgs()[0]); 53 break; 54 case 'ask': 55 $this->ask($options->getArgs()[0]); 56 break; 57 case 'chat': 58 $this->chat(); 59 break; 60 default: 61 echo $options->help(); 62 } 63 } 64 65 /** 66 * Interactive Chat Session 67 * 68 * @return void 69 * @throws Exception 70 */ 71 protected function chat() 72 { 73 /** @var helper_plugin_aichat_prompt $prompt */ 74 $prompt = plugin_load('helper', 'aichat_prompt'); 75 76 $history = []; 77 while ($q = $this->readLine('Your Question')) { 78 if ($history) { 79 $question = $prompt->rephraseChatQuestion($q, $history); 80 $this->colors->ptln("Interpretation: $question", Colors::C_LIGHTPURPLE); 81 } else { 82 $question = $q; 83 } 84 $result = $prompt->askQuestion($question); 85 $history[] = [$q, $result['answer']]; 86 $this->printAnswer($result); 87 } 88 89 } 90 91 /** 92 * Print the given detailed answer in a nice way 93 * 94 * @param array $answer 95 * @return void 96 */ 97 protected function printAnswer($answer) 98 { 99 $this->colors->ptln($answer['answer'], Colors::C_LIGHTCYAN); 100 echo "\n"; 101 foreach ($answer['sources'] as $source) { 102 $this->colors->ptln("\t".$source['meta']['pageid'], Colors::C_LIGHTBLUE); 103 } 104 echo "\n"; 105 } 106 107 /** 108 * Handle a single, standalone question 109 * 110 * @param string $query 111 * @return void 112 * @throws Exception 113 */ 114 protected function ask($query) 115 { 116 /** @var helper_plugin_aichat_prompt $prompt */ 117 $prompt = plugin_load('helper', 'aichat_prompt'); 118 119 $result = $prompt->askQuestion($query); 120 $this->printAnswer($result); 121 } 122 123 /** 124 * Get the pages that are similar to the query 125 * 126 * @param string $query 127 * @return void 128 */ 129 protected function similar($query) 130 { 131 $openAI = new OpenAI($this->getConf('openaikey'), $this->getConf('openaiorg')); 132 $embedding = new Embeddings($openAI, $this); 133 134 $sources = $embedding->getSimilarChunks($query); 135 foreach ($sources as $source) { 136 $this->colors->ptln($source['meta']['pageid'], Colors::C_LIGHTBLUE); 137 } 138 } 139 140 /** 141 * Recreate chunks and embeddings for all pages 142 * 143 * @return void 144 */ 145 protected function createEmbeddings() 146 { 147 $openAI = new OpenAI($this->getConf('openaikey'), $this->getConf('openaiorg')); 148 149 $embeddings = new Embeddings($openAI, $this); 150 $embeddings->createNewIndex(); 151 } 152 153 /** 154 * Interactively ask for a value from the user 155 * 156 * @param string $prompt 157 * @return string 158 */ 159 protected function readLine($prompt) 160 { 161 $value = ''; 162 163 while ($value === '') { 164 echo $prompt; 165 echo ': '; 166 167 $fh = fopen('php://stdin', 'r'); 168 $value = trim(fgets($fh)); 169 fclose($fh); 170 } 171 172 return $value; 173 } 174} 175 176