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