1<?php 2 3use dokuwiki\plugin\aichat\AbstractCLI; 4use splitbrain\phpcli\Options; 5 6/** 7 * DokuWiki Plugin aichat (CLI Component) 8 * 9 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 10 * @author Andreas Gohr <gohr@cosmocode.de> 11 */ 12class cli_plugin_aichat_dev extends AbstractCLI 13{ 14 /** @inheritDoc */ 15 protected function setup(Options $options) 16 { 17 $options->setHelp('Helps with development of this plugin'); 18 19 $options->registerCommand('update', 'Update the model data'); 20 } 21 22 /** @inheritDoc */ 23 protected function main(Options $options) 24 { 25 parent::main($options); 26 27 switch ($options->getCmd()) { 28 29 case 'update': 30 $this->updateModelData(); 31 break; 32 default: 33 echo $options->help(); 34 } 35 } 36 37 protected function updateModelData() 38 { 39 40 $http = new \dokuwiki\HTTP\DokuHTTPClient(); 41 $url = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json'; 42 $response = $http->get($url); 43 if ($response === false) { 44 $this->error('Failed to fetch model data'); 45 return 1; 46 } 47 $models = json_decode($response, true, 512, JSON_THROW_ON_ERROR); 48 49 $ourProviders = [ 50 'anthropic' => [ 51 'name' => 'Anthropic', 52 ], 53 'groq' => [ 54 'name' => 'Groq', 55 'skip' => '/-preview$/' 56 ], 57 'mistral' => [ 58 'name' => 'Mistral', 59 'skip' => '/-\d\d\d\d$/', 60 ], 61 'openai' => [ 62 'name' => 'OpenAI', 63 'skip' => '/(-\d\d\d\d-\d\d-\d\d|-preview|-\d\d\d\d)$|^ft:/' 64 ], 65 'reka' => [ 66 'name' => 'Reka', 67 ], 68 'voyage' => [ 69 'name' => 'VoyageAI', 70 'skip' => '/-(01|02)(-|$)/', // outdated models 71 ], 72 'gemini' => [ 73 'name' => 'Gemini', 74 'skip' => '/(-preview|-exp|-\d\d\d$)/' 75 ], 76 ]; 77 78 // load existing models 79 foreach ($ourProviders as $provider => $data) { 80 $ourProviders[$provider]['models'] = json_decode( 81 file_get_contents(__DIR__ . '/../Model/' . $data['name'] . '/' . 'models.json'), 82 true 83 ); 84 } 85 86 // update models 87 foreach ($models as $model => $data) { 88 if (!isset($ourProviders[$data['litellm_provider']])) continue; 89 if (!in_array($data['mode'], ['chat', 'embedding'])) continue; 90 $provider = $data['litellm_provider']; 91 $model = explode('/', $model); 92 $model = array_pop($model); 93 94 if (isset($ourProviders[$provider]['skip']) && preg_match($ourProviders[$provider]['skip'], $model)) { 95 $this->info('Skipping ' . $provider . ' ' . $model); 96 continue; 97 } 98 $this->success("$provider $model"); 99 100 $oldmodel = $ourProviders[$provider]['models'][$data['mode']][$model] ?? []; 101 $newmodel = [ 102 "description" => $oldmodel['description'] ?? $data['source'] ?? '', 103 "inputTokens" => $data['max_input_tokens'] ?? $data['max_tokens'], 104 "inputTokenPrice" => round($data['input_cost_per_token'] * 1_000_000, 2), 105 ]; 106 107 if ($data['mode'] === 'chat') { 108 $newmodel['outputTokens'] = $data['max_output_tokens']; 109 $newmodel['outputTokenPrice'] = round($data['output_cost_per_token'] * 1_000_000, 2); 110 } else { 111 if (isset($oldmodel['dimensions'])) { 112 $newmodel['dimensions'] = $oldmodel['dimensions']; 113 } else { 114 $this->warning('No dimensions for ' . $provider . ' ' . $model . '. Check manually!'); 115 $newmodel['dimensions'] = 1536; 116 } 117 } 118 $ourProviders[$provider]['models'][$data['mode']][$model] = $newmodel; 119 } 120 121 // save models 122 foreach ($ourProviders as $data) { 123 file_put_contents( 124 __DIR__ . '/../Model/' . $data['name'] . '/' . 'models.json', 125 json_encode($data['models'], JSON_PRETTY_PRINT) 126 ); 127 } 128 129 return 0; 130 } 131} 132