1c2b7a1f7SAndreas Gohr<?php 2c2b7a1f7SAndreas Gohr 3c2b7a1f7SAndreas Gohrnamespace dokuwiki\plugin\aichat; 4c2b7a1f7SAndreas Gohr 5c2b7a1f7SAndreas Gohruse dokuwiki\plugin\aichat\Model\ChatInterface; 6c2b7a1f7SAndreas Gohruse dokuwiki\plugin\aichat\Model\EmbeddingInterface; 7c2b7a1f7SAndreas Gohr 8c2b7a1f7SAndreas Gohrclass ModelFactory 9c2b7a1f7SAndreas Gohr{ 10c2b7a1f7SAndreas Gohr /** @var array The plugin configuration */ 11c2b7a1f7SAndreas Gohr protected array $config; 12c2b7a1f7SAndreas Gohr 13c2b7a1f7SAndreas Gohr public $chatModel; 14c2b7a1f7SAndreas Gohr public $rephraseModel; 15c2b7a1f7SAndreas Gohr public $embeddingModel; 16c2b7a1f7SAndreas Gohr 17c2b7a1f7SAndreas Gohr protected $debug = false; 18c2b7a1f7SAndreas Gohr 19c2b7a1f7SAndreas Gohr /** 20c2b7a1f7SAndreas Gohr * @param array $config The plugin configuration 21c2b7a1f7SAndreas Gohr */ 22c2b7a1f7SAndreas Gohr public function __construct(array $config) 23c2b7a1f7SAndreas Gohr { 24c2b7a1f7SAndreas Gohr $this->config = $config; 25c2b7a1f7SAndreas Gohr } 26c2b7a1f7SAndreas Gohr 27c2b7a1f7SAndreas Gohr /** 28*0de7e020SAndreas Gohr * Update the configuration and reset the cached models 29*0de7e020SAndreas Gohr * 30*0de7e020SAndreas Gohr * @param array $config The new (partial) configuration 31*0de7e020SAndreas Gohr */ 32*0de7e020SAndreas Gohr public function updateConfig(array $config) 33*0de7e020SAndreas Gohr { 34*0de7e020SAndreas Gohr $this->config = array_merge($this->config, $config); 35*0de7e020SAndreas Gohr $this->chatModel = null; 36*0de7e020SAndreas Gohr $this->rephraseModel = null; 37*0de7e020SAndreas Gohr $this->embeddingModel = null; 38*0de7e020SAndreas Gohr } 39*0de7e020SAndreas Gohr 40*0de7e020SAndreas Gohr /** 41c2b7a1f7SAndreas Gohr * Set the debug flag for all models 42c2b7a1f7SAndreas Gohr * 43c2b7a1f7SAndreas Gohr * @param bool $debug 44c2b7a1f7SAndreas Gohr */ 45c2b7a1f7SAndreas Gohr public function setDebug(bool $debug = true) 46c2b7a1f7SAndreas Gohr { 47c2b7a1f7SAndreas Gohr $this->debug = $debug; 48c2b7a1f7SAndreas Gohr $this->getChatModel()->setDebug($debug); 49c2b7a1f7SAndreas Gohr $this->getRephraseModel()->setDebug($debug); 50c2b7a1f7SAndreas Gohr $this->getEmbeddingModel()->setDebug($debug); 51c2b7a1f7SAndreas Gohr } 52c2b7a1f7SAndreas Gohr 53c2b7a1f7SAndreas Gohr /** 54c2b7a1f7SAndreas Gohr * Access a cached Chat Model 55c2b7a1f7SAndreas Gohr * 56c2b7a1f7SAndreas Gohr * @return ChatInterface 57c2b7a1f7SAndreas Gohr * @throws \Exception 58c2b7a1f7SAndreas Gohr */ 59c2b7a1f7SAndreas Gohr public function getChatModel() 60c2b7a1f7SAndreas Gohr { 61c2b7a1f7SAndreas Gohr if ($this->chatModel instanceof ChatInterface) { 62c2b7a1f7SAndreas Gohr return $this->chatModel; 63c2b7a1f7SAndreas Gohr } 64c2b7a1f7SAndreas Gohr $this->chatModel = $this->loadModel('chat', $this->config['chatmodel']); 65*0de7e020SAndreas Gohr $this->chatModel->setDebug($this->debug); 66c2b7a1f7SAndreas Gohr return $this->chatModel; 67c2b7a1f7SAndreas Gohr } 68c2b7a1f7SAndreas Gohr 69c2b7a1f7SAndreas Gohr /** 70c2b7a1f7SAndreas Gohr * Access a cached Rephrase Model 71c2b7a1f7SAndreas Gohr * 72c2b7a1f7SAndreas Gohr * @return ChatInterface 73c2b7a1f7SAndreas Gohr * @throws \Exception 74c2b7a1f7SAndreas Gohr */ 75c2b7a1f7SAndreas Gohr public function getRephraseModel() 76c2b7a1f7SAndreas Gohr { 77c2b7a1f7SAndreas Gohr if ($this->rephraseModel instanceof ChatInterface) { 78c2b7a1f7SAndreas Gohr return $this->rephraseModel; 79c2b7a1f7SAndreas Gohr } 80c2b7a1f7SAndreas Gohr $this->rephraseModel = $this->loadModel('chat', $this->config['chatmodel']); 81*0de7e020SAndreas Gohr $this->rephraseModel->setDebug($this->debug); 82c2b7a1f7SAndreas Gohr return $this->rephraseModel; 83c2b7a1f7SAndreas Gohr } 84c2b7a1f7SAndreas Gohr 85c2b7a1f7SAndreas Gohr /** 86c2b7a1f7SAndreas Gohr * Access a cached Embedding Model 87c2b7a1f7SAndreas Gohr * 88c2b7a1f7SAndreas Gohr * @return EmbeddingInterface 89c2b7a1f7SAndreas Gohr */ 90c2b7a1f7SAndreas Gohr public function getEmbeddingModel() 91c2b7a1f7SAndreas Gohr { 92c2b7a1f7SAndreas Gohr if ($this->embeddingModel instanceof EmbeddingInterface) { 93c2b7a1f7SAndreas Gohr return $this->embeddingModel; 94c2b7a1f7SAndreas Gohr } 95c2b7a1f7SAndreas Gohr $this->embeddingModel = $this->loadModel('embedding', $this->config['embedmodel']); 96*0de7e020SAndreas Gohr $this->embeddingModel->setDebug($this->debug); 97c2b7a1f7SAndreas Gohr return $this->embeddingModel; 98c2b7a1f7SAndreas Gohr } 99c2b7a1f7SAndreas Gohr 100c2b7a1f7SAndreas Gohr /** 101c2b7a1f7SAndreas Gohr * Get all known models 102c2b7a1f7SAndreas Gohr * 103c2b7a1f7SAndreas Gohr * A (new) instance is returned for each model that is available through the current configuration. 104c2b7a1f7SAndreas Gohr * 105c2b7a1f7SAndreas Gohr * @param bool $availableOnly Only return models that are available 106c2b7a1f7SAndreas Gohr * @param string $typeOnly Only return models of this type ('chat' or 'embedding') 107c2b7a1f7SAndreas Gohr * @return array 108c2b7a1f7SAndreas Gohr */ 109c2b7a1f7SAndreas Gohr public function getModels($availableOnly = false, $typeOnly = '') 110c2b7a1f7SAndreas Gohr { 111c2b7a1f7SAndreas Gohr $result = [ 112c2b7a1f7SAndreas Gohr 'chat' => [], 113c2b7a1f7SAndreas Gohr 'embedding' => [], 114c2b7a1f7SAndreas Gohr ]; 115c2b7a1f7SAndreas Gohr 116c2b7a1f7SAndreas Gohr $jsons = glob(__DIR__ . '/Model/*/models.json'); 117c2b7a1f7SAndreas Gohr foreach ($jsons as $json) { 118c2b7a1f7SAndreas Gohr $models = json_decode(file_get_contents($json), true); 119c2b7a1f7SAndreas Gohr foreach ($models as $type => $model) { 120c2b7a1f7SAndreas Gohr $namespace = basename(dirname($json)); 121c2b7a1f7SAndreas Gohr foreach ($model as $name => $info) { 122c2b7a1f7SAndreas Gohr try { 123c2b7a1f7SAndreas Gohr $info['instance'] = $this->loadModel($type, "$namespace $name"); 124c2b7a1f7SAndreas Gohr $info['instance']->setDebug($this->debug); 125c2b7a1f7SAndreas Gohr } catch (\Exception $e) { 126c2b7a1f7SAndreas Gohr if ($availableOnly) continue; 127c2b7a1f7SAndreas Gohr $info['instance'] = false; 128c2b7a1f7SAndreas Gohr } 129c2b7a1f7SAndreas Gohr 130c2b7a1f7SAndreas Gohr $result[$type]["$namespace $name"] = $info; 131c2b7a1f7SAndreas Gohr } 132c2b7a1f7SAndreas Gohr } 133c2b7a1f7SAndreas Gohr } 134c2b7a1f7SAndreas Gohr 135c2b7a1f7SAndreas Gohr return $typeOnly ? $result[$typeOnly] : $result; 136c2b7a1f7SAndreas Gohr } 137c2b7a1f7SAndreas Gohr 138c2b7a1f7SAndreas Gohr 139c2b7a1f7SAndreas Gohr /** 140c2b7a1f7SAndreas Gohr * Initialize a model by config name 141c2b7a1f7SAndreas Gohr * 142c2b7a1f7SAndreas Gohr * @param string $type 'chat' or 'embedding' 143c2b7a1f7SAndreas Gohr * @param string $name The full model name including provider 144c2b7a1f7SAndreas Gohr * @return ChatInterface|EmbeddingInterface 145c2b7a1f7SAndreas Gohr * @throws \Exception 146c2b7a1f7SAndreas Gohr */ 147c2b7a1f7SAndreas Gohr public function loadModel(string $type, string $name) 148c2b7a1f7SAndreas Gohr { 149c2b7a1f7SAndreas Gohr $type = ucfirst(strtolower($type)); 150c2b7a1f7SAndreas Gohr $prefix = '\\dokuwiki\\plugin\\aichat\\Model\\'; 151c2b7a1f7SAndreas Gohr $cname = $type . 'Model'; 152c2b7a1f7SAndreas Gohr $interface = $prefix . $type . 'Interface'; 153c2b7a1f7SAndreas Gohr 154c2b7a1f7SAndreas Gohr 155c2b7a1f7SAndreas Gohr [$namespace, $model] = sexplode(' ', $name, 2, ''); 156c2b7a1f7SAndreas Gohr $class = $prefix . $namespace . '\\' . $cname; 157c2b7a1f7SAndreas Gohr 158c2b7a1f7SAndreas Gohr if (!class_exists($class)) { 159c2b7a1f7SAndreas Gohr throw new \Exception("No $cname found for $namespace"); 160c2b7a1f7SAndreas Gohr } 161c2b7a1f7SAndreas Gohr 162c2b7a1f7SAndreas Gohr try { 163c2b7a1f7SAndreas Gohr $instance = new $class($model, $this->config); 164c2b7a1f7SAndreas Gohr } catch (\Exception $e) { 165c2b7a1f7SAndreas Gohr throw new \Exception("Failed to initialize $cname for $namespace: " . $e->getMessage(), 0, $e); 166c2b7a1f7SAndreas Gohr } 167c2b7a1f7SAndreas Gohr 168c2b7a1f7SAndreas Gohr if (!($instance instanceof $interface)) { 169c2b7a1f7SAndreas Gohr throw new \Exception("$cname for $namespace does not implement $interface"); 170c2b7a1f7SAndreas Gohr } 171c2b7a1f7SAndreas Gohr 172c2b7a1f7SAndreas Gohr return $instance; 173c2b7a1f7SAndreas Gohr } 174c2b7a1f7SAndreas Gohr} 175