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