Przeglądaj źródła

feat: llm service context support

Cat 1 rok temu
rodzic
commit
2eb8d672b9

+ 1 - 1
README.md

@@ -48,7 +48,7 @@ NeXT Panel requires the following programs to be installed and run normally:
 
 
 ## Support Developers
 ## Support Developers
 
 
-Sadly, there is no three-letter agency that wants to sponsor this project yet(/s), so the development is driven by the community and unpaid volunteers.
+Sadly, there is no three-letter agency that wants to sponsor this project yet (/s), so the development is driven by the community and unpaid volunteers.
 Nothing will be put behind the paywall or require a donation to use, but the CI/Linux Mirror/CDN server doesn't grow on trees, if you are interested in supporting the project, you can support devs using the following methods:
 Nothing will be put behind the paywall or require a donation to use, but the CI/Linux Mirror/CDN server doesn't grow on trees, if you are interested in supporting the project, you can support devs using the following methods:
 
 
 <a href="https://www.patreon.com/catdev">Patreon (One time or monthly)</a>
 <a href="https://www.patreon.com/catdev">Patreon (One time or monthly)</a>

+ 1 - 1
app/predefine.php

@@ -5,5 +5,5 @@ declare(strict_types=1);
 // Global constants
 // Global constants
 const BASE_PATH = __DIR__ . '/..';
 const BASE_PATH = __DIR__ . '/..';
 const PANEL_NAME = 'NeXT-Panel';
 const PANEL_NAME = 'NeXT-Panel';
-const PANEL_VERSION = '24.2.0';
+const PANEL_VERSION = '24.3.0';
 const OTA_API_URL = 'https://api.nextpanel.dev/v1/version/latest';
 const OTA_API_URL = 'https://api.nextpanel.dev/v1/version/latest';

+ 19 - 19
composer.lock

@@ -622,16 +622,16 @@
         },
         },
         {
         {
             "name": "aws/aws-sdk-php",
             "name": "aws/aws-sdk-php",
-            "version": "3.311.2",
+            "version": "3.312.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "731cd73062909594c5f7443413c4c4c40ed1c25c"
+                "reference": "53b233072f707d2abfd62b0df4bc2dc27caf3274"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/731cd73062909594c5f7443413c4c4c40ed1c25c",
-                "reference": "731cd73062909594c5f7443413c4c4c40ed1c25c",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/53b233072f707d2abfd62b0df4bc2dc27caf3274",
+                "reference": "53b233072f707d2abfd62b0df4bc2dc27caf3274",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -711,9 +711,9 @@
             "support": {
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.311.2"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.312.0"
             },
             },
-            "time": "2024-06-07T18:05:33+00:00"
+            "time": "2024-06-10T18:04:10+00:00"
         },
         },
         {
         {
             "name": "bacon/bacon-qr-code",
             "name": "bacon/bacon-qr-code",
@@ -3605,16 +3605,16 @@
         },
         },
         {
         {
             "name": "php-http/multipart-stream-builder",
             "name": "php-http/multipart-stream-builder",
-            "version": "1.3.0",
+            "version": "1.3.1",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/php-http/multipart-stream-builder.git",
                 "url": "https://github.com/php-http/multipart-stream-builder.git",
-                "reference": "f5938fd135d9fa442cc297dc98481805acfe2b6a"
+                "reference": "ed56da23b95949ae4747378bed8a5b61a2fdae24"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/f5938fd135d9fa442cc297dc98481805acfe2b6a",
-                "reference": "f5938fd135d9fa442cc297dc98481805acfe2b6a",
+                "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/ed56da23b95949ae4747378bed8a5b61a2fdae24",
+                "reference": "ed56da23b95949ae4747378bed8a5b61a2fdae24",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -3655,9 +3655,9 @@
             ],
             ],
             "support": {
             "support": {
                 "issues": "https://github.com/php-http/multipart-stream-builder/issues",
                 "issues": "https://github.com/php-http/multipart-stream-builder/issues",
-                "source": "https://github.com/php-http/multipart-stream-builder/tree/1.3.0"
+                "source": "https://github.com/php-http/multipart-stream-builder/tree/1.3.1"
             },
             },
-            "time": "2023-04-28T14:10:22+00:00"
+            "time": "2024-06-10T14:51:55+00:00"
         },
         },
         {
         {
             "name": "php-http/promise",
             "name": "php-http/promise",
@@ -9233,16 +9233,16 @@
         },
         },
         {
         {
             "name": "react/stream",
             "name": "react/stream",
-            "version": "v1.3.0",
+            "version": "v1.4.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/reactphp/stream.git",
                 "url": "https://github.com/reactphp/stream.git",
-                "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66"
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/reactphp/stream/zipball/6fbc9672905c7d5a885f2da2fc696f65840f4a66",
-                "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66",
+                "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -9252,7 +9252,7 @@
             },
             },
             "require-dev": {
             "require-dev": {
                 "clue/stream-filter": "~1.2",
                 "clue/stream-filter": "~1.2",
-                "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
             },
             },
             "type": "library",
             "type": "library",
             "autoload": {
             "autoload": {
@@ -9299,7 +9299,7 @@
             ],
             ],
             "support": {
             "support": {
                 "issues": "https://github.com/reactphp/stream/issues",
                 "issues": "https://github.com/reactphp/stream/issues",
-                "source": "https://github.com/reactphp/stream/tree/v1.3.0"
+                "source": "https://github.com/reactphp/stream/tree/v1.4.0"
             },
             },
             "funding": [
             "funding": [
                 {
                 {
@@ -9307,7 +9307,7 @@
                     "type": "open_collective"
                     "type": "open_collective"
                 }
                 }
             ],
             ],
-            "time": "2023-06-16T10:52:11+00:00"
+            "time": "2024-06-11T12:45:25+00:00"
         },
         },
         {
         {
             "name": "sebastian/cli-parser",
             "name": "sebastian/cli-parser",

+ 1 - 1
src/Services/Gateway/PayPal.php

@@ -77,7 +77,7 @@ final class PayPal extends Base
         }
         }
 
 
         try {
         try {
-            $exchange_amount = (new Exchange())->exchange($price, 'CNY', Config::obtain('paypal_currency'));
+            $exchange_amount = (new Exchange())->exchange((float) $price, 'CNY', Config::obtain('paypal_currency'));
         } catch (GuzzleException|RedisException) {
         } catch (GuzzleException|RedisException) {
             return $response->withJson([
             return $response->withJson([
                 'ret' => 0,
                 'ret' => 0,

+ 3 - 14
src/Services/LLM.php

@@ -11,7 +11,6 @@ use App\Services\LLM\GoogleAI;
 use App\Services\LLM\HuggingFace;
 use App\Services\LLM\HuggingFace;
 use App\Services\LLM\OpenAI;
 use App\Services\LLM\OpenAI;
 use App\Services\LLM\VertexAI;
 use App\Services\LLM\VertexAI;
-use GuzzleHttp\Exception\GuzzleException;
 
 
 final class LLM
 final class LLM
 {
 {
@@ -27,9 +26,6 @@ final class LLM
         };
         };
     }
     }
 
 
-    /**
-     * @throws GuzzleException
-     */
     public static function genTextResponse(string $q): string
     public static function genTextResponse(string $q): string
     {
     {
         if (Config::obtain('llm_backend') === '') {
         if (Config::obtain('llm_backend') === '') {
@@ -43,23 +39,16 @@ final class LLM
         return self::getBackend()->textPrompt($q);
         return self::getBackend()->textPrompt($q);
     }
     }
 
 
-    /**
-     * @throws GuzzleException
-     */
-    public static function genTextResponseWithContext(string $q, array $context = []): string
+    public static function genTextResponseWithContext(array $context = []): string
     {
     {
         if (Config::obtain('llm_backend') === '') {
         if (Config::obtain('llm_backend') === '') {
             return 'No LLM backend configured';
             return 'No LLM backend configured';
         }
         }
 
 
-        if ($q === '') {
-            return 'No question provided';
-        }
-
         if ($context === []) {
         if ($context === []) {
-            return self::getBackend()->textPrompt($q);
+            return 'No context provided';
         }
         }
 
 
-        return self::getBackend()->textPromptWithContext($q, $context);
+        return self::getBackend()->textPromptWithContext($context);
     }
     }
 }
 }

+ 36 - 20
src/Services/LLM/Anthropic.php

@@ -10,10 +10,33 @@ use function json_decode;
 
 
 final class Anthropic extends Base
 final class Anthropic extends Base
 {
 {
-    /**
-     * @throws GuzzleException
-     */
     public function textPrompt(string $q): string
     public function textPrompt(string $q): string
+    {
+        return $this->makeRequest([
+            [
+                'role' => 'user',
+                'content' => $q,
+            ],
+        ]);
+    }
+
+    public function textPromptWithContext(array $context): string
+    {
+        $conversation = [];
+
+        if (count($context) > 0) {
+            foreach ($context as $role => $content) {
+                $conversation[] = [
+                    'role' => $role === 'user' ? 'user' : 'assistant',
+                    'content' => $content,
+                ];
+            }
+        }
+
+        return $this->makeRequest($conversation);
+    }
+
+    private function makeRequest(array $conversation): string
     {
     {
         if (Config::obtain('anthropic_api_key') === '') {
         if (Config::obtain('anthropic_api_key') === '') {
             return 'Anthropic API key not set';
             return 'Anthropic API key not set';
@@ -28,27 +51,20 @@ final class Anthropic extends Base
 
 
         $data = [
         $data = [
             'model' => Config::obtain('anthropic_model_id'),
             'model' => Config::obtain('anthropic_model_id'),
-            'max_tokens' => 1024,
             'temperature' => 1,
             'temperature' => 1,
-            'messages' => [
-                [
-                    'role' => 'user',
-                    'content' => $q,
-                ],
-            ],
+            'messages' => $conversation,
         ];
         ];
 
 
-        $response = json_decode($this->client->post($api_url, [
-            'headers' => $headers,
-            'json' => $data,
-            'timeout' => 30,
-        ])->getBody()->getContents());
+        try {
+            $response = json_decode($this->client->post($api_url, [
+                'headers' => $headers,
+                'json' => $data,
+                'timeout' => 30,
+            ])->getBody()->getContents());
+        } catch (GuzzleException $e) {
+            return '';
+        }
 
 
         return $response->content[0]->text;
         return $response->content[0]->text;
     }
     }
-
-    public function textPromptWithContext(string $q, array $context): string
-    {
-        return '';
-    }
 }
 }

+ 1 - 1
src/Services/LLM/Base.php

@@ -17,5 +17,5 @@ abstract class Base
 
 
     abstract public function textPrompt(string $q): string;
     abstract public function textPrompt(string $q): string;
 
 
-    abstract public function textPromptWithContext(string $q, array $context): string;
+    abstract public function textPromptWithContext(array $context): string;
 }
 }

+ 11 - 10
src/Services/LLM/CloudflareWorkersAI.php

@@ -10,9 +10,6 @@ use function json_decode;
 
 
 final class CloudflareWorkersAI extends Base
 final class CloudflareWorkersAI extends Base
 {
 {
-    /**
-     * @throws GuzzleException
-     */
     public function textPrompt(string $q): string
     public function textPrompt(string $q): string
     {
     {
         if (Config::obtain('cf_workers_ai_account_id') === '' || Config::obtain('cf_workers_ai_api_token') === '') {
         if (Config::obtain('cf_workers_ai_account_id') === '' || Config::obtain('cf_workers_ai_api_token') === '') {
@@ -31,17 +28,21 @@ final class CloudflareWorkersAI extends Base
             'prompt' => $q,
             'prompt' => $q,
         ];
         ];
 
 
-        $response = json_decode($this->client->post($api_url, [
-            'headers' => $headers,
-            'json' => $data,
-            'timeout' => 30,
-        ])->getBody()->getContents());
+        try {
+            $response = json_decode($this->client->post($api_url, [
+                'headers' => $headers,
+                'json' => $data,
+                'timeout' => 30,
+            ])->getBody()->getContents());
+        } catch (GuzzleException $e) {
+            return '';
+        }
 
 
         return $response->result->response;
         return $response->result->response;
     }
     }
 
 
-    public function textPromptWithContext(string $q, array $context): string
+    public function textPromptWithContext(array $context): string
     {
     {
-        return '';
+        return 'This service does not support context';
     }
     }
 }
 }

+ 44 - 23
src/Services/LLM/GoogleAI.php

@@ -10,10 +10,41 @@ use function json_decode;
 
 
 final class GoogleAI extends Base
 final class GoogleAI extends Base
 {
 {
-    /**
-     * @throws GuzzleException
-     */
     public function textPrompt(string $q): string
     public function textPrompt(string $q): string
+    {
+        return $this->makeRequest([
+            [
+                'parts' => [
+                    [
+                        'text' => $q,
+                    ],
+                ],
+                'role' => 'user',
+            ],
+        ]);
+    }
+
+    public function textPromptWithContext(array $context): string
+    {
+        $conversation = [];
+
+        if (count($context) > 0) {
+            foreach ($context as $role => $content) {
+                $conversation[] = [
+                    'parts' => [
+                        [
+                            'text' => $content,
+                        ],
+                    ],
+                    'role' => $role === 'user' ? 'user' : 'model',
+                ];
+            }
+        }
+
+        return $this->makeRequest($conversation);
+    }
+
+    private function makeRequest(array $conversation): string
     {
     {
         if (Config::obtain('google_ai_api_key') === '') {
         if (Config::obtain('google_ai_api_key') === '') {
             return 'Google AI API key not set';
             return 'Google AI API key not set';
@@ -27,16 +58,7 @@ final class GoogleAI extends Base
         ];
         ];
 
 
         $data = [
         $data = [
-            'contents' => [
-                [
-                    'parts' => [
-                        [
-                            'text' => $q,
-                        ],
-                    ],
-                    'role' => 'user',
-                ],
-            ],
+            'contents' => $conversation,
             'generationConfig' => [
             'generationConfig' => [
                 'temperature' => 1,
                 'temperature' => 1,
                 'candidateCount' => 1,
                 'candidateCount' => 1,
@@ -61,17 +83,16 @@ final class GoogleAI extends Base
             ],
             ],
         ];
         ];
 
 
-        $response = json_decode($this->client->post($api_url, [
-            'headers' => $headers,
-            'json' => $data,
-            'timeout' => 30,
-        ])->getBody()->getContents());
+        try {
+            $response = json_decode($this->client->post($api_url, [
+                'headers' => $headers,
+                'json' => $data,
+                'timeout' => 30,
+            ])->getBody()->getContents());
+        } catch (GuzzleException $e) {
+            return '';
+        }
 
 
         return $response->candidates[0]->content->parts[0]->text;
         return $response->candidates[0]->content->parts[0]->text;
     }
     }
-
-    public function textPromptWithContext(string $q, array $context): string
-    {
-        return '';
-    }
 }
 }

+ 11 - 10
src/Services/LLM/HuggingFace.php

@@ -10,9 +10,6 @@ use function json_decode;
 
 
 final class HuggingFace extends Base
 final class HuggingFace extends Base
 {
 {
-    /**
-     * @throws GuzzleException
-     */
     public function textPrompt(string $q): string
     public function textPrompt(string $q): string
     {
     {
         if (Config::obtain('huggingface_api_key') === '' || Config::obtain('huggingface_endpoint_url') === '') {
         if (Config::obtain('huggingface_api_key') === '' || Config::obtain('huggingface_endpoint_url') === '') {
@@ -30,17 +27,21 @@ final class HuggingFace extends Base
             ],
             ],
         ];
         ];
 
 
-        $response = json_decode($this->client->post(Config::obtain('huggingface_endpoint_url'), [
-            'headers' => $headers,
-            'json' => $data,
-            'timeout' => 30,
-        ])->getBody()->getContents());
+        try {
+            $response = json_decode($this->client->post(Config::obtain('huggingface_endpoint_url'), [
+                'headers' => $headers,
+                'json' => $data,
+                'timeout' => 30,
+            ])->getBody()->getContents());
+        } catch (GuzzleException $e) {
+            return '';
+        }
 
 
         return $response->answer;
         return $response->answer;
     }
     }
 
 
-    public function textPromptWithContext(string $q, array $context): string
+    public function textPromptWithContext(array $context): string
     {
     {
-        return '';
+        return 'This service does not support context';
     }
     }
 }
 }

+ 32 - 15
src/Services/LLM/OpenAI.php

@@ -10,6 +10,37 @@ use OpenAI as OpenAISDK;
 final class OpenAI extends Base
 final class OpenAI extends Base
 {
 {
     public function textPrompt(string $q): string
     public function textPrompt(string $q): string
+    {
+        return $this->makeRequest([
+            [
+                'role' => 'user',
+                'content' => $q,
+            ],
+        ]);
+    }
+
+    public function textPromptWithContext(array $context): string
+    {
+        $conversation = [
+            [
+                'role' => 'system',
+                'content' => 'You are a helpful assistant.',
+            ],
+        ];
+
+        if (count($context) > 0) {
+            foreach ($context as $role => $content) {
+                $conversation[] = [
+                    'role' => $role === 'user' ? 'user' : 'assistant',
+                    'content' => $content,
+                ];
+            }
+        }
+
+        return $this->makeRequest($conversation);
+    }
+
+    private function makeRequest(array $conversation): string
     {
     {
         if (Config::obtain('openai_api_key') === '') {
         if (Config::obtain('openai_api_key') === '') {
             return 'OpenAI API key not set';
             return 'OpenAI API key not set';
@@ -20,23 +51,9 @@ final class OpenAI extends Base
         $response = $client->chat()->create([
         $response = $client->chat()->create([
             'model' => Config::obtain('openai_model_id'),
             'model' => Config::obtain('openai_model_id'),
             'temperature' => 1,
             'temperature' => 1,
-            'messages' => [
-                [
-                    'role' => 'system',
-                    'content' => 'You are a helpful assistant.',
-                ],
-                [
-                    'role' => 'user',
-                    'content' => $q,
-                ],
-            ],
+            'messages' => $conversation,
         ]);
         ]);
 
 
         return $response->choices[0]->message->content;
         return $response->choices[0]->message->content;
     }
     }
-
-    public function textPromptWithContext(string $q, array $context): string
-    {
-        return '';
-    }
 }
 }

+ 44 - 20
src/Services/LLM/VertexAI.php

@@ -10,10 +10,41 @@ use function json_decode;
 
 
 final class VertexAI extends Base
 final class VertexAI extends Base
 {
 {
-    /**
-     * @throws GuzzleException
-     */
     public function textPrompt(string $q): string
     public function textPrompt(string $q): string
+    {
+        return $this->makeRequest([
+            [
+                'parts' => [
+                    [
+                        'text' => $q,
+                    ],
+                ],
+                'role' => 'user',
+            ],
+        ]);
+    }
+
+    public function textPromptWithContext(array $context): string
+    {
+        $conversation = [];
+
+        if (count($context) > 0) {
+            foreach ($context as $role => $content) {
+                $conversation[] = [
+                    'parts' => [
+                        [
+                            'text' => $content,
+                        ],
+                    ],
+                    'role' => $role === 'user' ? 'user' : 'model',
+                ];
+            }
+        }
+
+        return $this->makeRequest($conversation);
+    }
+
+    private function makeRequest(array $conversation): string
     {
     {
         if (Config::obtain('vertex_ai_access_token') === '') {
         if (Config::obtain('vertex_ai_access_token') === '') {
             return 'Vertex AI API key not set';
             return 'Vertex AI API key not set';
@@ -29,13 +60,7 @@ final class VertexAI extends Base
         ];
         ];
 
 
         $data = [
         $data = [
-            'contents' => [
-                'parts' => [
-                    [
-                        'text' => $q,
-                    ],
-                ],
-            ],
+            'contents' => $conversation,
             'generationConfig' => [
             'generationConfig' => [
                 'temperature' => 1,
                 'temperature' => 1,
                 'candidateCount' => 1,
                 'candidateCount' => 1,
@@ -60,17 +85,16 @@ final class VertexAI extends Base
             ],
             ],
         ];
         ];
 
 
-        $response = json_decode($this->client->post($api_url, [
-            'headers' => $headers,
-            'json' => $data,
-            'timeout' => 30,
-        ])->getBody()->getContents());
+        try {
+            $response = json_decode($this->client->post($api_url, [
+                'headers' => $headers,
+                'json' => $data,
+                'timeout' => 30,
+            ])->getBody()->getContents());
+        } catch (GuzzleException $e) {
+            return '';
+        }
 
 
         return $response->candidates[0]->content->parts[0]->text;
         return $response->candidates[0]->content->parts[0]->text;
     }
     }
-
-    public function textPromptWithContext(string $q, array $context): string
-    {
-        return '';
-    }
 }
 }

+ 0 - 3
update.sh

@@ -1,9 +1,6 @@
 #!/usr/bin/bash
 #!/usr/bin/bash
 
 
 cat << "EOF"
 cat << "EOF"
-SSPanel-UIM update script
-Author: M1Screw
-Github: https://github.com/sspanel-uim/SSPanel-Uim-Dev
 Usage:
 Usage:
 ./update.sh dev --> Upgrade to the latest development version
 ./update.sh dev --> Upgrade to the latest development version
 ./update.sh release $release_version $db_version --> Upgrade to the release version with the specified database version
 ./update.sh release $release_version $db_version --> Upgrade to the release version with the specified database version