10337f47fSAndreas Gohr<?php 20337f47fSAndreas Gohr 3bddd899cSAndreas Gohruse dokuwiki\ErrorHandler; 46beed327SAndreas Gohruse dokuwiki\Extension\ActionPlugin; 56beed327SAndreas Gohruse dokuwiki\Extension\Event; 66beed327SAndreas Gohruse dokuwiki\Extension\EventHandler; 76beed327SAndreas Gohruse dokuwiki\Logger; 8f6ef2e50SAndreas Gohruse dokuwiki\plugin\aichat\Chunk; 9bddd899cSAndreas Gohr 100337f47fSAndreas Gohr/** 110337f47fSAndreas Gohr * DokuWiki Plugin aichat (Action Component) 120337f47fSAndreas Gohr * 130337f47fSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 140337f47fSAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 150337f47fSAndreas Gohr */ 167ebc7895Ssplitbrainclass action_plugin_aichat extends ActionPlugin 170337f47fSAndreas Gohr{ 180337f47fSAndreas Gohr /** @inheritDoc */ 197ebc7895Ssplitbrain public function register(EventHandler $controller) 200337f47fSAndreas Gohr { 210337f47fSAndreas Gohr $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleQuestion'); 220337f47fSAndreas Gohr } 230337f47fSAndreas Gohr 240337f47fSAndreas Gohr 250337f47fSAndreas Gohr /** 260337f47fSAndreas Gohr * Event handler for AJAX_CALL_UNKNOWN event 270337f47fSAndreas Gohr * 280337f47fSAndreas Gohr * @see https://www.dokuwiki.org/devel:events:ajax_call_unknown 291e5c27a9Ssplitbrain * @param Event $event Event object 300337f47fSAndreas Gohr * @param mixed $param optional parameter passed when event was registered 310337f47fSAndreas Gohr * @return void 320337f47fSAndreas Gohr */ 3330b9cbc7Ssplitbrain public function handleQuestion(Event $event, mixed $param) 340337f47fSAndreas Gohr { 350337f47fSAndreas Gohr if ($event->data !== 'aichat') return; 360337f47fSAndreas Gohr $event->preventDefault(); 370337f47fSAndreas Gohr $event->stopPropagation(); 380337f47fSAndreas Gohr global $INPUT; 390337f47fSAndreas Gohr 40c4127b8eSAndreas Gohr /** @var helper_plugin_aichat $helper */ 41c4127b8eSAndreas Gohr $helper = plugin_load('helper', 'aichat'); 42c4127b8eSAndreas Gohr 430337f47fSAndreas Gohr $question = $INPUT->post->str('question'); 44*ed47fd87SAndreas Gohr $pagecontext = $INPUT->post->str('pagecontext'); 4530b9cbc7Ssplitbrain $history = json_decode((string)$INPUT->post->str('history'), null, 512, JSON_THROW_ON_ERROR); 46bddd899cSAndreas Gohr header('Content-Type: application/json'); 470337f47fSAndreas Gohr 48c4127b8eSAndreas Gohr if (!$helper->userMayAccess()) { 49c4127b8eSAndreas Gohr echo json_encode([ 50c4127b8eSAndreas Gohr 'question' => $question, 51c4127b8eSAndreas Gohr 'answer' => $this->getLang('restricted'), 52c4127b8eSAndreas Gohr 'sources' => [], 5330b9cbc7Ssplitbrain ], JSON_THROW_ON_ERROR); 54c4127b8eSAndreas Gohr return; 55c4127b8eSAndreas Gohr } 56c4127b8eSAndreas Gohr 57bddd899cSAndreas Gohr try { 58*ed47fd87SAndreas Gohr $result = $helper->askChatQuestion($question, $history, $pagecontext); 590337f47fSAndreas Gohr $sources = []; 600337f47fSAndreas Gohr foreach ($result['sources'] as $source) { 61bddd899cSAndreas Gohr /** @var Chunk $source */ 626beed327SAndreas Gohr if (isset($sources[$source->getPage()])) continue; // only show the first occurrence per page 636beed327SAndreas Gohr $sources[$source->getPage()] = [ 646beed327SAndreas Gohr 'page' => $source->getPage(), 656beed327SAndreas Gohr 'url' => wl($source->getPage()), 666beed327SAndreas Gohr 'title' => p_get_first_heading($source->getPage()) ?: $source->getPage(), 676beed327SAndreas Gohr 'score' => sprintf("%.2f%%", $source->getScore() * 100), 686beed327SAndreas Gohr ]; 690337f47fSAndreas Gohr } 707017fceaSAndreas Gohr $parseDown = new Parsedown(); 717017fceaSAndreas Gohr $parseDown->setSafeMode(true); 727017fceaSAndreas Gohr 730337f47fSAndreas Gohr echo json_encode([ 740337f47fSAndreas Gohr 'question' => $result['question'], 757017fceaSAndreas Gohr 'answer' => $parseDown->text($result['answer']), 766beed327SAndreas Gohr 'sources' => array_values($sources), 7730b9cbc7Ssplitbrain ], JSON_THROW_ON_ERROR); 7882d5855eSAndreas Gohr 7982d5855eSAndreas Gohr if ($this->getConf('logging')) { 807ebc7895Ssplitbrain Logger::getInstance('aichat')->log( 8182d5855eSAndreas Gohr $question, 8282d5855eSAndreas Gohr [ 8382d5855eSAndreas Gohr 'interpretation' => $result['question'], 8482d5855eSAndreas Gohr 'answer' => $result['answer'], 8582d5855eSAndreas Gohr 'sources' => $sources, 8682d5855eSAndreas Gohr 'ip' => $INPUT->server->str('REMOTE_ADDR'), 8782d5855eSAndreas Gohr 'user' => $INPUT->server->str('REMOTE_USER'), 886a18e0f4SAndreas Gohr 'stats' => $helper->getChatModel()->getUsageStats() 8982d5855eSAndreas Gohr ] 9082d5855eSAndreas Gohr ); 9182d5855eSAndreas Gohr } 92bddd899cSAndreas Gohr } catch (\Exception $e) { 93bddd899cSAndreas Gohr ErrorHandler::logException($e); 94bddd899cSAndreas Gohr echo json_encode([ 95bddd899cSAndreas Gohr 'question' => $question, 96bddd899cSAndreas Gohr 'answer' => 'An error occurred. More info may be available in the error log. ' . $e->getMessage(), 97bddd899cSAndreas Gohr 'sources' => [], 9830b9cbc7Ssplitbrain ], JSON_THROW_ON_ERROR); 99bddd899cSAndreas Gohr } 1000337f47fSAndreas Gohr } 1010337f47fSAndreas Gohr} 102