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 17{ 18 19 /** @inheritdoc */ 20 protected function getHttpClient() 21 { 22 $http = parent::getHttpClient(); 23 24 $apiKey = $this->getFromConf('apikey', ''); //auth is optional 25 if ($apiKey) { 26 $http->headers['Authorization'] = 'Bearer ' . $apiKey; 27 } 28 return $http; 29 } 30 31 /** 32 * Send a request to the OpenAI API 33 * 34 * @param string $endpoint 35 * @param array $data Payload to send 36 * @return array API response 37 * @throws \Exception 38 */ 39 protected function request($endpoint, $data) 40 { 41 $url = $this->apiurl . '/' . $endpoint; 42 return $this->sendAPIRequest('POST', $url, $data); 43 } 44 45 /** @inheritdoc */ 46 protected function parseAPIResponse($response) 47 { 48 if (isset($response['usage'])) { 49 if (isset($response['usage']['prompt_tokens'])) { 50 $this->inputTokensUsed += $response['usage']['prompt_tokens']; 51 } elseif ($response['usage']['total_tokens']) { 52 // on embedding models, prompt_tokens is not available 53 $this->inputTokensUsed += $response['usage']['total_tokens']; 54 } 55 $this->outputTokensUsed += $response['usage']['completion_tokens'] ?? 0; 56 } 57 58 if (isset($response['error'])) { 59 throw new ModelException('API error: ' . $response['error']['message'], 3002); 60 } 61 62 return $response; 63 } 64 65 /** @inheritdoc */ 66 public function getAnswer(array $messages): string 67 { 68 $data = [ 69 'messages' => $messages, 70 'model' => $this->getModelName(), 71 'max_completion_tokens' => null, 72 'stream' => false, 73 'n' => 1, // number of completions 74 'temperature' => 0.0 75 ]; 76 77 $response = $this->request('chat/completions', $data); 78 return $response['choices'][0]['message']['content']; 79 } 80 81 /** @inheritdoc */ 82 public function getEmbedding($text): array 83 { 84 $data = [ 85 'model' => $this->getModelName(), 86 'input' => [$text], 87 ]; 88 $response = $this->request('embeddings', $data); 89 90 return $response['data'][0]['embedding']; 91 } 92 93 /** 94 * @internal for checking available models 95 */ 96 public function listUpstreamModels() 97 { 98 $http = $this->getHttpClient(); 99 $url = $this->apiurl . '/models'; 100 return $http->get($url); 101 } 102} 103