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