xref: /plugin/aichat/action.php (revision 6beed32793f7954c876d220b83d5dce235e4d4b3)
10337f47fSAndreas Gohr<?php
20337f47fSAndreas Gohr
3bddd899cSAndreas Gohruse dokuwiki\ErrorHandler;
4*6beed327SAndreas Gohruse dokuwiki\Extension\ActionPlugin;
5*6beed327SAndreas Gohruse dokuwiki\Extension\Event;
6*6beed327SAndreas Gohruse dokuwiki\Extension\EventHandler;
7*6beed327SAndreas 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');
4430b9cbc7Ssplitbrain        $history = json_decode((string)$INPUT->post->str('history'), null, 512, JSON_THROW_ON_ERROR);
45bddd899cSAndreas Gohr        header('Content-Type: application/json');
460337f47fSAndreas Gohr
47c4127b8eSAndreas Gohr        if (!$helper->userMayAccess()) {
48c4127b8eSAndreas Gohr            echo json_encode([
49c4127b8eSAndreas Gohr                'question' => $question,
50c4127b8eSAndreas Gohr                'answer' => $this->getLang('restricted'),
51c4127b8eSAndreas Gohr                'sources' => [],
5230b9cbc7Ssplitbrain            ], JSON_THROW_ON_ERROR);
53c4127b8eSAndreas Gohr            return;
54c4127b8eSAndreas Gohr        }
55c4127b8eSAndreas Gohr
56bddd899cSAndreas Gohr        try {
570337f47fSAndreas Gohr            $result = $helper->askChatQuestion($question, $history);
580337f47fSAndreas Gohr            $sources = [];
590337f47fSAndreas Gohr            foreach ($result['sources'] as $source) {
60bddd899cSAndreas Gohr                /** @var Chunk $source */
61*6beed327SAndreas Gohr                if(isset($sources[$source->getPage()])) continue; // only show the first occurrence per page
62*6beed327SAndreas Gohr                $sources[$source->getPage()] = [
63*6beed327SAndreas Gohr                    'page' => $source->getPage(),
64*6beed327SAndreas Gohr                    'url' => wl($source->getPage()),
65*6beed327SAndreas Gohr                    'title' => p_get_first_heading($source->getPage()) ?: $source->getPage(),
66*6beed327SAndreas Gohr                    'score' => sprintf("%.2f%%", $source->getScore()*100),
67*6beed327SAndreas Gohr                ];
680337f47fSAndreas Gohr            }
697017fceaSAndreas Gohr            $parseDown = new Parsedown();
707017fceaSAndreas Gohr            $parseDown->setSafeMode(true);
717017fceaSAndreas Gohr
720337f47fSAndreas Gohr            echo json_encode([
730337f47fSAndreas Gohr                'question' => $result['question'],
747017fceaSAndreas Gohr                'answer' => $parseDown->text($result['answer']),
75*6beed327SAndreas Gohr                'sources' => array_values($sources),
7630b9cbc7Ssplitbrain            ], JSON_THROW_ON_ERROR);
7782d5855eSAndreas Gohr
7882d5855eSAndreas Gohr            if ($this->getConf('logging')) {
797ebc7895Ssplitbrain                Logger::getInstance('aichat')->log(
8082d5855eSAndreas Gohr                    $question,
8182d5855eSAndreas Gohr                    [
8282d5855eSAndreas Gohr                        'interpretation' => $result['question'],
8382d5855eSAndreas Gohr                        'answer' => $result['answer'],
8482d5855eSAndreas Gohr                        'sources' => $sources,
8582d5855eSAndreas Gohr                        'ip' => $INPUT->server->str('REMOTE_ADDR'),
8682d5855eSAndreas Gohr                        'user' => $INPUT->server->str('REMOTE_USER'),
876a18e0f4SAndreas Gohr                        'stats' => $helper->getChatModel()->getUsageStats()
8882d5855eSAndreas Gohr                    ]
8982d5855eSAndreas Gohr                );
9082d5855eSAndreas Gohr            }
91bddd899cSAndreas Gohr        } catch (\Exception $e) {
92bddd899cSAndreas Gohr            ErrorHandler::logException($e);
93bddd899cSAndreas Gohr            echo json_encode([
94bddd899cSAndreas Gohr                'question' => $question,
95bddd899cSAndreas Gohr                'answer' => 'An error occurred. More info may be available in the error log. ' . $e->getMessage(),
96bddd899cSAndreas Gohr                'sources' => [],
9730b9cbc7Ssplitbrain            ], JSON_THROW_ON_ERROR);
98bddd899cSAndreas Gohr        }
990337f47fSAndreas Gohr    }
1000337f47fSAndreas Gohr}
101