12e22aefbSAndreas Gohr<?php 22e22aefbSAndreas Gohr 32e22aefbSAndreas Gohrnamespace dokuwiki\plugin\aichat\Model\Generic; 42e22aefbSAndreas Gohr 52e22aefbSAndreas Gohruse dokuwiki\plugin\aichat\Model\AbstractModel; 62e22aefbSAndreas Gohruse dokuwiki\plugin\aichat\Model\ChatInterface; 72e22aefbSAndreas Gohruse dokuwiki\plugin\aichat\Model\EmbeddingInterface; 82e22aefbSAndreas Gohruse dokuwiki\plugin\aichat\Model\ModelException; 92e22aefbSAndreas Gohr 102e22aefbSAndreas Gohr/** 112e22aefbSAndreas Gohr * Abstract OpenAI-compatible Model 122e22aefbSAndreas Gohr * 132e22aefbSAndreas Gohr * This class provides a basic interface to the OpenAI API as implemented by many other providers. 142e22aefbSAndreas Gohr * It implements chat and embedding interfaces. 152e22aefbSAndreas Gohr */ 162e22aefbSAndreas Gohrabstract class AbstractGenericModel extends AbstractModel implements ChatInterface, EmbeddingInterface 172e22aefbSAndreas Gohr{ 182e22aefbSAndreas Gohr 197c3b69cbSAndreas Gohr /** @inheritdoc */ 207c3b69cbSAndreas Gohr protected function getHttpClient() 217c3b69cbSAndreas Gohr { 227c3b69cbSAndreas Gohr $http = parent::getHttpClient(); 237c3b69cbSAndreas Gohr 24*7bd2bac6SAndreas Gohr $apiKey = $this->getFromConf('apikey', ''); //auth is optional 25*7bd2bac6SAndreas Gohr if ($apiKey) { 267c3b69cbSAndreas Gohr $http->headers['Authorization'] = 'Bearer ' . $apiKey; 27*7bd2bac6SAndreas Gohr } 287c3b69cbSAndreas Gohr return $http; 297c3b69cbSAndreas Gohr } 307c3b69cbSAndreas Gohr 312e22aefbSAndreas Gohr /** 322e22aefbSAndreas Gohr * Send a request to the OpenAI API 332e22aefbSAndreas Gohr * 342e22aefbSAndreas Gohr * @param string $endpoint 352e22aefbSAndreas Gohr * @param array $data Payload to send 362e22aefbSAndreas Gohr * @return array API response 372e22aefbSAndreas Gohr * @throws \Exception 382e22aefbSAndreas Gohr */ 392e22aefbSAndreas Gohr protected function request($endpoint, $data) 402e22aefbSAndreas Gohr { 412e22aefbSAndreas Gohr $url = $this->apiurl . '/' . $endpoint; 422e22aefbSAndreas Gohr return $this->sendAPIRequest('POST', $url, $data); 432e22aefbSAndreas Gohr } 442e22aefbSAndreas Gohr 452e22aefbSAndreas Gohr /** @inheritdoc */ 462e22aefbSAndreas Gohr protected function parseAPIResponse($response) 472e22aefbSAndreas Gohr { 482e22aefbSAndreas Gohr if (isset($response['usage'])) { 492e22aefbSAndreas Gohr if (isset($response['usage']['prompt_tokens'])) { 502e22aefbSAndreas Gohr $this->inputTokensUsed += $response['usage']['prompt_tokens']; 512e22aefbSAndreas Gohr } elseif ($response['usage']['total_tokens']) { 522e22aefbSAndreas Gohr // on embedding models, prompt_tokens is not available 532e22aefbSAndreas Gohr $this->inputTokensUsed += $response['usage']['total_tokens']; 542e22aefbSAndreas Gohr } 552e22aefbSAndreas Gohr $this->outputTokensUsed += $response['usage']['completion_tokens'] ?? 0; 562e22aefbSAndreas Gohr } 572e22aefbSAndreas Gohr 582e22aefbSAndreas Gohr if (isset($response['error'])) { 592e22aefbSAndreas Gohr throw new ModelException('API error: ' . $response['error']['message'], 3002); 602e22aefbSAndreas Gohr } 612e22aefbSAndreas Gohr 622e22aefbSAndreas Gohr return $response; 632e22aefbSAndreas Gohr } 642e22aefbSAndreas Gohr 652e22aefbSAndreas Gohr /** @inheritdoc */ 662e22aefbSAndreas Gohr public function getAnswer(array $messages): string 672e22aefbSAndreas Gohr { 682e22aefbSAndreas Gohr $data = [ 692e22aefbSAndreas Gohr 'messages' => $messages, 702e22aefbSAndreas Gohr 'model' => $this->getModelName(), 712e22aefbSAndreas Gohr 'max_completion_tokens' => null, 722e22aefbSAndreas Gohr 'stream' => false, 732e22aefbSAndreas Gohr 'n' => 1, // number of completions 74ac84f472SAndreas Gohr 'temperature' => 0.0 752e22aefbSAndreas Gohr ]; 762e22aefbSAndreas Gohr 772e22aefbSAndreas Gohr $response = $this->request('chat/completions', $data); 782e22aefbSAndreas Gohr return $response['choices'][0]['message']['content']; 792e22aefbSAndreas Gohr } 802e22aefbSAndreas Gohr 812e22aefbSAndreas Gohr /** @inheritdoc */ 822e22aefbSAndreas Gohr public function getEmbedding($text): array 832e22aefbSAndreas Gohr { 842e22aefbSAndreas Gohr $data = [ 852e22aefbSAndreas Gohr 'model' => $this->getModelName(), 862e22aefbSAndreas Gohr 'input' => [$text], 872e22aefbSAndreas Gohr ]; 882e22aefbSAndreas Gohr $response = $this->request('embeddings', $data); 892e22aefbSAndreas Gohr 902e22aefbSAndreas Gohr return $response['data'][0]['embedding']; 912e22aefbSAndreas Gohr } 922e22aefbSAndreas Gohr 932e22aefbSAndreas Gohr /** 942e22aefbSAndreas Gohr * @internal for checking available models 952e22aefbSAndreas Gohr */ 962e22aefbSAndreas Gohr public function listUpstreamModels() 972e22aefbSAndreas Gohr { 987c3b69cbSAndreas Gohr $http = $this->getHttpClient(); 992e22aefbSAndreas Gohr $url = $this->apiurl . '/models'; 1007c3b69cbSAndreas Gohr return $http->get($url); 1012e22aefbSAndreas Gohr } 1022e22aefbSAndreas Gohr} 103