xref: /plugin/aichat/Model/Generic/AbstractGenericModel.php (revision d72a84c5bd8b465526a89378df9883eea1b18eb6)
1<?php
2
3namespace dokuwiki\plugin\aichat\Model\Generic;
4
5use dokuwiki\plugin\aichat\Model\AbstractModel;
6use dokuwiki\plugin\aichat\Model\ChatInterface;
7use dokuwiki\plugin\aichat\Model\EmbeddingInterface;
8use dokuwiki\plugin\aichat\Model\ModelException;
9
10/**
11 * Abstract OpenAI-compatible Model
12 *
13 * This class provides a basic interface to the OpenAI API as implemented by many other providers.
14 * It implements chat and embedding interfaces.
15 */
16abstract class AbstractGenericModel extends AbstractModel implements ChatInterface, EmbeddingInterface
17{
18
19    /** @inheritdoc */
20    protected function getHttpClient()
21    {
22        $http = parent::getHttpClient();
23
24        $apiKey = $this->getFromConf('apikey');
25        $http->headers['Authorization'] = 'Bearer ' . $apiKey;
26        return $http;
27    }
28
29    /**
30     * Send a request to the OpenAI API
31     *
32     * @param string $endpoint
33     * @param array $data Payload to send
34     * @return array API response
35     * @throws \Exception
36     */
37    protected function request($endpoint, $data)
38    {
39        $url = $this->apiurl . '/' . $endpoint;
40        return $this->sendAPIRequest('POST', $url, $data);
41    }
42
43    /** @inheritdoc */
44    protected function parseAPIResponse($response)
45    {
46        if (isset($response['usage'])) {
47            if(isset($response['usage']['prompt_tokens'])) {
48                $this->inputTokensUsed += $response['usage']['prompt_tokens'];
49            } elseif ($response['usage']['total_tokens']) {
50                // on embedding models, prompt_tokens is not available
51                $this->inputTokensUsed += $response['usage']['total_tokens'];
52            }
53            $this->outputTokensUsed += $response['usage']['completion_tokens'] ?? 0;
54        }
55
56        if (isset($response['error'])) {
57            throw new ModelException('API error: ' . $response['error']['message'], 3002);
58        }
59
60        return $response;
61    }
62
63    /** @inheritdoc */
64    public function getAnswer(array $messages): string
65    {
66        $data = [
67            'messages' => $messages,
68            'model' => $this->getModelName(),
69            'max_completion_tokens' => null,
70            'stream' => false,
71            'n' => 1, // number of completions
72            'temperature' => 0.0
73        ];
74
75        $response = $this->request('chat/completions', $data);
76        return $response['choices'][0]['message']['content'];
77    }
78
79    /** @inheritdoc */
80    public function getEmbedding($text): array
81    {
82        $data = [
83            'model' => $this->getModelName(),
84            'input' => [$text],
85        ];
86        $response = $this->request('embeddings', $data);
87
88        return $response['data'][0]['embedding'];
89    }
90
91    /**
92     * @internal for checking available models
93     */
94    public function listUpstreamModels()
95    {
96        $http = $this->getHttpClient();
97        $url = $this->apiurl . '/models';
98        return $http->get($url);
99    }
100}
101