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