1<?php
2
3namespace dokuwiki\plugin\aichat\test;
4
5use dokuwiki\plugin\aichat\Model\ChatInterface;
6use dokuwiki\plugin\aichat\Model\EmbeddingInterface;
7use DokuWikiTest;
8
9/**
10 * Base for Model provider tests
11 *
12 * @group plugin_aichat
13 * @group plugins
14 * @group internet
15 */
16abstract class AbstractModelTest extends DokuWikiTest
17{
18    /** @inheritdoc */
19    protected $pluginsEnabled = ['aichat'];
20
21    /** @var string The provider name, e.g. 'openai', 'reka', etc. */
22    protected string $provider;
23
24    /** @var string The environment variable name for the API key */
25    protected string $api_key_env;
26
27    /** @var string The chat model name, e.g. 'gpt-3.5-turbo', 'gpt-4', etc. */
28    protected string $chat_model;
29
30    /** @var string The embedding model name, e.g. 'text-embedding-3-small', etc. */
31    protected string $embedding_model;
32
33    /** @inheritdoc */
34    public function setUp(): void
35    {
36        parent::setUp();
37        global $conf;
38
39        // get API key from environment
40        if($this->api_key_env) {
41            $apikey = getenv($this->api_key_env);
42            if (!$apikey) {
43                $this->markTestSkipped('API key environment not set');
44            }
45        } else {
46            // if not env name is set, probably no key is needed. We simply set it to empty
47            $apikey = '';
48        }
49
50        // set basic configuration for the plugin
51        $providerName = ucfirst($this->provider);
52        $providerConf = strtolower($this->provider);
53        $conf['plugin']['aichat']['chatmodel'] = $providerName . ' ' . $this->chat_model;
54        $conf['plugin']['aichat']['rephrasemodel'] = $providerName . ' ' . $this->chat_model;
55        $conf['plugin']['aichat']['embedmodel'] = $providerName . ' ' . $this->embedding_model;
56        $conf['plugin']['aichat'][$providerConf . '_apikey'] = $apikey;
57    }
58
59    public function testChat()
60    {
61        $prompt = 'This is a test. Please reply with "Hello World"';
62
63        /** @var \helper_plugin_aichat $helper */
64        $helper = plugin_load('helper', 'aichat');
65        $model = $helper->getChatModel();
66
67        $this->assertInstanceOf(ChatInterface::class, $model, 'Model should implement ChatInterface');
68        $this->assertEquals(
69            'dokuwiki\\plugin\\aichat\\Model\\' . $this->provider . '\\ChatModel',
70            get_class($model),
71            'Model seems to be the wrong class'
72        );
73
74        try {
75            $reply = $model->getAnswer([
76                ['role' => 'user', 'content' => $prompt]
77            ]);
78        } catch (\Exception $e) {
79            if (preg_match('/(credit|fund|balance)/i', $e->getMessage())) {
80                $this->markTestIncomplete($e->getMessage());
81            } else {
82                throw $e;
83            }
84        }
85
86        $this->assertStringContainsString('hello world', strtolower($reply));
87    }
88
89    public function testEmbedding()
90    {
91        $text = 'This is a test for embeddings.';
92
93        /** @var \helper_plugin_aichat $helper */
94        $helper = plugin_load('helper', 'aichat');
95        $model = $helper->getEmbeddingModel();
96
97        $this->assertInstanceOf(EmbeddingInterface::class, $model, 'Model should implement EmbeddingInterface');
98        $this->assertEquals(
99            'dokuwiki\\plugin\\aichat\\Model\\' . $this->provider . '\\EmbeddingModel',
100            get_class($model),
101            'Model seems to be the wrong class'
102        );
103
104        try {
105            $embedding = $model->getEmbedding($text);
106        } catch (\Exception $e) {
107            if (preg_match('/(credit|fund|balance)/i', $e->getMessage())) {
108                $this->markTestIncomplete($e->getMessage());
109            } else {
110                throw $e;
111            }
112        }
113
114        $this->assertIsArray($embedding);
115        $this->assertNotEmpty($embedding, 'Embedding should not be empty');
116        $this->assertIsFloat($embedding[0], 'Embedding should be an array of floats');
117    }
118}
119