xref: /plugin/aichat/cli.php (revision 9da5f0df9b3bbdaf1de18d37258b054d79f1eaa7)
18817535bSAndreas Gohr<?php
28817535bSAndreas Gohr
3c4584168SAndreas Gohruse splitbrain\phpcli\Colors;
48817535bSAndreas Gohruse splitbrain\phpcli\Options;
58817535bSAndreas Gohr
68817535bSAndreas Gohr
78817535bSAndreas Gohr/**
88817535bSAndreas Gohr * DokuWiki Plugin aichat (CLI Component)
98817535bSAndreas Gohr *
108817535bSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
118817535bSAndreas Gohr * @author  Andreas Gohr <gohr@cosmocode.de>
128817535bSAndreas Gohr */
138817535bSAndreas Gohrclass cli_plugin_aichat extends \dokuwiki\Extension\CLIPlugin
148817535bSAndreas Gohr{
150337f47fSAndreas Gohr    /** @var helper_plugin_aichat */
160337f47fSAndreas Gohr    protected $helper;
170337f47fSAndreas Gohr
180337f47fSAndreas Gohr    public function __construct($autocatch = true)
190337f47fSAndreas Gohr    {
200337f47fSAndreas Gohr        parent::__construct($autocatch);
210337f47fSAndreas Gohr        $this->helper = plugin_load('helper', 'aichat');
220337f47fSAndreas Gohr    }
230337f47fSAndreas Gohr
248817535bSAndreas Gohr
258817535bSAndreas Gohr    /** @inheritDoc */
268817535bSAndreas Gohr    protected function setup(Options $options)
278817535bSAndreas Gohr    {
28*9da5f0dfSAndreas Gohr        $options->setHelp('Manage and query the AI chatbot data');
298817535bSAndreas Gohr
308817535bSAndreas Gohr        $options->registerCommand('embed', 'Create embeddings for all pages');
318817535bSAndreas Gohr
328817535bSAndreas Gohr        $options->registerCommand('similar', 'Search for similar pages');
338817535bSAndreas Gohr        $options->registerArgument('query', 'Look up chunks similar to this query', true, 'similar');
348817535bSAndreas Gohr
358817535bSAndreas Gohr        $options->registerCommand('ask', 'Ask a question');
368817535bSAndreas Gohr        $options->registerArgument('question', 'The question to ask', true, 'ask');
37c4584168SAndreas Gohr
38c4584168SAndreas Gohr        $options->registerCommand('chat', 'Start an interactive chat session');
398817535bSAndreas Gohr    }
408817535bSAndreas Gohr
418817535bSAndreas Gohr    /** @inheritDoc */
428817535bSAndreas Gohr    protected function main(Options $options)
438817535bSAndreas Gohr    {
448817535bSAndreas Gohr        switch ($options->getCmd()) {
458817535bSAndreas Gohr
468817535bSAndreas Gohr            case 'embed':
478817535bSAndreas Gohr                $this->createEmbeddings();
488817535bSAndreas Gohr                break;
498817535bSAndreas Gohr            case 'similar':
508817535bSAndreas Gohr                $this->similar($options->getArgs()[0]);
518817535bSAndreas Gohr                break;
527552f1aaSAndreas Gohr            case 'ask':
537552f1aaSAndreas Gohr                $this->ask($options->getArgs()[0]);
547552f1aaSAndreas Gohr                break;
55c4584168SAndreas Gohr            case 'chat':
56c4584168SAndreas Gohr                $this->chat();
57c4584168SAndreas Gohr                break;
588817535bSAndreas Gohr            default:
598817535bSAndreas Gohr                echo $options->help();
608817535bSAndreas Gohr        }
618817535bSAndreas Gohr    }
628817535bSAndreas Gohr
63c4584168SAndreas Gohr    /**
64c4584168SAndreas Gohr     * Interactive Chat Session
65c4584168SAndreas Gohr     *
66c4584168SAndreas Gohr     * @return void
67c4584168SAndreas Gohr     * @throws Exception
68c4584168SAndreas Gohr     */
69c4584168SAndreas Gohr    protected function chat()
70c4584168SAndreas Gohr    {
71c4584168SAndreas Gohr        $history = [];
72c4584168SAndreas Gohr        while ($q = $this->readLine('Your Question')) {
73c4584168SAndreas Gohr            if ($history) {
740337f47fSAndreas Gohr                $question = $this->helper->rephraseChatQuestion($q, $history);
75c4584168SAndreas Gohr                $this->colors->ptln("Interpretation: $question", Colors::C_LIGHTPURPLE);
76c4584168SAndreas Gohr            } else {
77c4584168SAndreas Gohr                $question = $q;
78c4584168SAndreas Gohr            }
790337f47fSAndreas Gohr            $result = $this->helper->askQuestion($question);
80c4584168SAndreas Gohr            $history[] = [$q, $result['answer']];
81c4584168SAndreas Gohr            $this->printAnswer($result);
82c4584168SAndreas Gohr        }
83c4584168SAndreas Gohr    }
84c4584168SAndreas Gohr
85c4584168SAndreas Gohr    /**
86c4584168SAndreas Gohr     * Print the given detailed answer in a nice way
87c4584168SAndreas Gohr     *
88c4584168SAndreas Gohr     * @param array $answer
89c4584168SAndreas Gohr     * @return void
90c4584168SAndreas Gohr     */
91c4584168SAndreas Gohr    protected function printAnswer($answer)
92c4584168SAndreas Gohr    {
93c4584168SAndreas Gohr        $this->colors->ptln($answer['answer'], Colors::C_LIGHTCYAN);
94c4584168SAndreas Gohr        echo "\n";
95c4584168SAndreas Gohr        foreach ($answer['sources'] as $source) {
96c4584168SAndreas Gohr            $this->colors->ptln("\t" . $source['meta']['pageid'], Colors::C_LIGHTBLUE);
97c4584168SAndreas Gohr        }
98c4584168SAndreas Gohr        echo "\n";
99c4584168SAndreas Gohr    }
100c4584168SAndreas Gohr
101c4584168SAndreas Gohr    /**
102c4584168SAndreas Gohr     * Handle a single, standalone question
103c4584168SAndreas Gohr     *
104c4584168SAndreas Gohr     * @param string $query
105c4584168SAndreas Gohr     * @return void
106c4584168SAndreas Gohr     * @throws Exception
107c4584168SAndreas Gohr     */
108c4584168SAndreas Gohr    protected function ask($query)
109c4584168SAndreas Gohr    {
1100337f47fSAndreas Gohr        $result = $this->helper->askQuestion($query);
111c4584168SAndreas Gohr        $this->printAnswer($result);
1127552f1aaSAndreas Gohr    }
1137552f1aaSAndreas Gohr
114c4584168SAndreas Gohr    /**
115c4584168SAndreas Gohr     * Get the pages that are similar to the query
116c4584168SAndreas Gohr     *
117c4584168SAndreas Gohr     * @param string $query
118c4584168SAndreas Gohr     * @return void
119c4584168SAndreas Gohr     */
1208817535bSAndreas Gohr    protected function similar($query)
1218817535bSAndreas Gohr    {
1220337f47fSAndreas Gohr        $sources = $this->helper->getEmbeddings()->getSimilarChunks($query);
123c4584168SAndreas Gohr        foreach ($sources as $source) {
124c4584168SAndreas Gohr            $this->colors->ptln($source['meta']['pageid'], Colors::C_LIGHTBLUE);
125c4584168SAndreas Gohr        }
1268817535bSAndreas Gohr    }
1278817535bSAndreas Gohr
128c4584168SAndreas Gohr    /**
129c4584168SAndreas Gohr     * Recreate chunks and embeddings for all pages
130c4584168SAndreas Gohr     *
131c4584168SAndreas Gohr     * @return void
132c4584168SAndreas Gohr     */
1338817535bSAndreas Gohr    protected function createEmbeddings()
1348817535bSAndreas Gohr    {
1350337f47fSAndreas Gohr        $this->helper->getEmbeddings()->createNewIndex();
1368817535bSAndreas Gohr    }
1378817535bSAndreas Gohr
138c4584168SAndreas Gohr    /**
139c4584168SAndreas Gohr     * Interactively ask for a value from the user
140c4584168SAndreas Gohr     *
141c4584168SAndreas Gohr     * @param string $prompt
142c4584168SAndreas Gohr     * @return string
143c4584168SAndreas Gohr     */
144c4584168SAndreas Gohr    protected function readLine($prompt)
145c4584168SAndreas Gohr    {
146c4584168SAndreas Gohr        $value = '';
1478817535bSAndreas Gohr
148c4584168SAndreas Gohr        while ($value === '') {
149c4584168SAndreas Gohr            echo $prompt;
150c4584168SAndreas Gohr            echo ': ';
151c4584168SAndreas Gohr
152c4584168SAndreas Gohr            $fh = fopen('php://stdin', 'r');
153c4584168SAndreas Gohr            $value = trim(fgets($fh));
154c4584168SAndreas Gohr            fclose($fh);
155c4584168SAndreas Gohr        }
156c4584168SAndreas Gohr
157c4584168SAndreas Gohr        return $value;
158c4584168SAndreas Gohr    }
1598817535bSAndreas Gohr}
1608817535bSAndreas Gohr
161