xref: /plugin/aichat/cli.php (revision c4584168c6c9af22a69973c17af0a7ff5f7fb802)
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