Ver Fonte

feat: aws bedrock llm backend

Cat há 1 ano atrás
pai
commit
a15b69af3b

+ 21 - 21
composer.lock

@@ -622,16 +622,16 @@
         },
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.314.0",
+            "version": "3.314.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "5867f04e0e3959085328d853a743ffc5c8e8ae0b"
+                "reference": "1f5ccf9c73a66fb85c7c5de8f11ed69e44c636ef"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5867f04e0e3959085328d853a743ffc5c8e8ae0b",
-                "reference": "5867f04e0e3959085328d853a743ffc5c8e8ae0b",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1f5ccf9c73a66fb85c7c5de8f11ed69e44c636ef",
+                "reference": "1f5ccf9c73a66fb85c7c5de8f11ed69e44c636ef",
                 "shasum": ""
             },
             "require": {
@@ -711,9 +711,9 @@
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.314.0"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.314.2"
             },
-            "time": "2024-06-12T18:10:03+00:00"
+            "time": "2024-06-14T18:11:34+00:00"
         },
         {
             "name": "bacon/bacon-qr-code",
@@ -5246,16 +5246,16 @@
         },
         {
             "name": "stripe/stripe-php",
-            "version": "v14.9.0",
+            "version": "v14.10.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/stripe/stripe-php.git",
-                "reference": "7165f64ec1b19cb0ce288d553909cd68d8e6a3b1"
+                "reference": "7e1c4b5d2beadeaeddc42fd1f8a50fdb18b37f30"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/7165f64ec1b19cb0ce288d553909cd68d8e6a3b1",
-                "reference": "7165f64ec1b19cb0ce288d553909cd68d8e6a3b1",
+                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/7e1c4b5d2beadeaeddc42fd1f8a50fdb18b37f30",
+                "reference": "7e1c4b5d2beadeaeddc42fd1f8a50fdb18b37f30",
                 "shasum": ""
             },
             "require": {
@@ -5299,9 +5299,9 @@
             ],
             "support": {
                 "issues": "https://github.com/stripe/stripe-php/issues",
-                "source": "https://github.com/stripe/stripe-php/tree/v14.9.0"
+                "source": "https://github.com/stripe/stripe-php/tree/v14.10.0"
             },
-            "time": "2024-05-30T20:12:41+00:00"
+            "time": "2024-06-13T21:04:47+00:00"
         },
         {
             "name": "symfony/clock",
@@ -8934,28 +8934,28 @@
         },
         {
             "name": "react/dns",
-            "version": "v1.12.0",
+            "version": "v1.13.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/reactphp/dns.git",
-                "reference": "c134600642fa615b46b41237ef243daa65bb64ec"
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/reactphp/dns/zipball/c134600642fa615b46b41237ef243daa65bb64ec",
-                "reference": "c134600642fa615b46b41237ef243daa65bb64ec",
+                "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.0",
                 "react/cache": "^1.0 || ^0.6 || ^0.5",
                 "react/event-loop": "^1.2",
-                "react/promise": "^3.0 || ^2.7 || ^1.2.1"
+                "react/promise": "^3.2 || ^2.7 || ^1.2.1"
             },
             "require-dev": {
                 "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
-                "react/async": "^4 || ^3 || ^2",
-                "react/promise-timer": "^1.9"
+                "react/async": "^4.3 || ^3 || ^2",
+                "react/promise-timer": "^1.11"
             },
             "type": "library",
             "autoload": {
@@ -8998,7 +8998,7 @@
             ],
             "support": {
                 "issues": "https://github.com/reactphp/dns/issues",
-                "source": "https://github.com/reactphp/dns/tree/v1.12.0"
+                "source": "https://github.com/reactphp/dns/tree/v1.13.0"
             },
             "funding": [
                 {
@@ -9006,7 +9006,7 @@
                     "type": "open_collective"
                 }
             ],
-            "time": "2023-11-29T12:41:06+00:00"
+            "time": "2024-06-13T14:18:03+00:00"
         },
         {
             "name": "react/event-loop",

+ 5 - 5
config/.config.example.php

@@ -6,7 +6,7 @@ $_ENV['pwdMethod'] = 'bcrypt'; // 密码加密 可选 bcrypt, argon2i, argon2id
 $_ENV['salt'] = '';            // bcrypt/argon2i/argon2id 会忽略此项
 
 $_ENV['debug'] = false;                  // debug模式开关,生产环境请保持为false
-$_ENV['appName'] = 'SSPanel-UIM';         // 站点名称
+$_ENV['appName'] = 'NeXT Panel';         // 站点名称
 $_ENV['baseUrl'] = 'https://example.com'; // 站点地址,必须以https://开头,不要以/结尾
 
 // WebAPI
@@ -21,9 +21,9 @@ $_ENV['checkNodeIp'] = true;           // 是否webapi验证节点ip
 // db_socket 例:/var/run/mysqld/mysqld.sock(需使用绝对地址)
 $_ENV['db_host'] = '';
 $_ENV['db_socket'] = '';
-$_ENV['db_database'] = 'sspanel'; // 数据库名
+$_ENV['db_database'] = 'nextpanel'; // 数据库名
 $_ENV['db_username'] = 'root';    // 数据库用户名
-$_ENV['db_password'] = 'sspanel'; // 用户密码
+$_ENV['db_password'] = 'nextpanel'; // 用户密码
 $_ENV['db_port'] = '3306';        // 端口
 #读写分离相关配置
 $_ENV['enable_db_rw_split'] = false; // 是否开启读写分离
@@ -86,8 +86,8 @@ $_ENV['auto_detect_ban_time'] = 60;          // 每次封禁的时长 (分钟)
 //节点检测---------------------------------------------------------------------------------------------------------------
 //TODO: move these settings to DB
 #GFW检测
-$_ENV['detect_gfw_port'] = 443;                                                    //所有节点服务器都打开的TCP端口
-$_ENV['detect_gfw_url'] = 'http://example.com:8080/v1/tcping?ip={ip}&port={port}'; //检测节点是否被gfw墙了的API的URL
+$_ENV['detect_gfw_port'] = 443;                                                //所有节点服务器都打开的TCP端口
+$_ENV['detect_gfw_url'] = 'https://example.com/v1/tcping?ip={ip}&port={port}'; // https://github.com/SSPanel-NeXT/NetStatus-API-Go
 #离线检测
 $_ENV['enable_detect_offline'] = true;
 

+ 36 - 0
config/settings.json

@@ -1672,5 +1672,41 @@
         "type": "string",
         "default": "claude-3-sonnet-20240229",
         "mark": "Anthropic Model ID"
+    },
+    {
+        "item": "aws_bedrock_access_key_id",
+        "value": "",
+        "class": "llm",
+        "is_public": 0,
+        "type": "string",
+        "default": "",
+        "mark": "AWS Bedrock Access Key ID"
+    },
+    {
+        "item": "aws_bedrock_access_key_secret",
+        "value": "",
+        "class": "llm",
+        "is_public": 0,
+        "type": "string",
+        "default": "",
+        "mark": "AWS Bedrock Access Key Secret"
+    },
+    {
+        "item": "aws_bedrock_region",
+        "value": "us-west-2",
+        "class": "llm",
+        "is_public": 0,
+        "type": "string",
+        "default": "us-west-2",
+        "mark": "AWS Bedrock Region"
+    },
+    {
+        "item": "aws_bedrock_model_id",
+        "value": "meta.llama3-8b-instruct-v1:0",
+        "class": "llm",
+        "is_public": 0,
+        "type": "string",
+        "default": "meta.llama3-8b-instruct-v1:0",
+        "mark": "AWS Bedrock Model ID"
     }
 ]

+ 41 - 2
resources/views/tabler/admin/setting/llm.tpl

@@ -51,6 +51,9 @@
                                 <li class="nav-item">
                                     <a href="#anthropic" class="nav-link" data-bs-toggle="tab">Anthropic</a>
                                 </li>
+                                <li class="nav-item">
+                                    <a href="#aws-bedrock" class="nav-link" data-bs-toggle="tab">AWS Bedrock</a>
+                                </li>
                             </ul>
                         </div>
                         <div class="card-body">
@@ -90,6 +93,10 @@
                                                             {if $settings['llm_backend'] === "anthropic"}selected{/if}>
                                                         Anthropic
                                                     </option>
+                                                    <option value="aws-bedrock"
+                                                            {if $settings['llm_backend'] === "aws-bedrock"}selected{/if}>
+                                                        AWS Bedrock
+                                                    </option>
                                                 </select>
                                             </div>
                                         </div>
@@ -166,7 +173,7 @@
                                 <div class="tab-pane" id="huggingface">
                                     <div class="card-body">
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">Api Key</label>
+                                            <label class="form-label col-3 col-form-label">API Key</label>
                                             <div class="col">
                                                 <input id="huggingface_api_key" type="text" class="form-control"
                                                        value="{$settings['huggingface_api_key']}">
@@ -191,7 +198,7 @@
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">Api Token</label>
+                                            <label class="form-label col-3 col-form-label">API Token</label>
                                             <div class="col">
                                                 <input id="cf_workers_ai_api_token" type="text" class="form-control"
                                                        value="{$settings['cf_workers_ai_api_token']}">
@@ -224,6 +231,38 @@
                                         </div>
                                     </div>
                                 </div>
+                                <div class="tab-pane" id="aws-bedrock">
+                                    <div class="card-body">
+                                        <div class="form-group mb-3 row">
+                                            <label class="form-label col-3 col-form-label">Access Key ID</label>
+                                            <div class="col">
+                                                <input id="aws_bedrock_access_key_id" type="text" class="form-control"
+                                                       value="{$settings['aws_bedrock_access_key_id']}">
+                                            </div>
+                                        </div>
+                                        <div class="form-group mb-3 row">
+                                            <label class="form-label col-3 col-form-label">Access Key Secret</label>
+                                            <div class="col">
+                                                <input id="aws_bedrock_access_key_secret" type="text" class="form-control"
+                                                       value="{$settings['aws_bedrock_access_key_secret']}">
+                                            </div>
+                                        </div>
+                                        <div class="form-group mb-3 row">
+                                            <label class="form-label col-3 col-form-label">Region</label>
+                                            <div class="col">
+                                                <input id="aws_bedrock_region" type="text" class="form-control"
+                                                       value="{$settings['aws_bedrock_region']}">
+                                            </div>
+                                        </div>
+                                        <div class="form-group mb-3 row">
+                                            <label class="form-label col-3 col-form-label">Model ID</label>
+                                            <div class="col">
+                                                <input id="aws_bedrock_model_id" type="text" class="form-control"
+                                                       value="{$settings['aws_bedrock_model_id']}">
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
                             </div>
                         </div>
                     </div>

+ 13 - 24
src/Controllers/Admin/Setting/LlmController.php

@@ -6,50 +6,39 @@ namespace App\Controllers\Admin\Setting;
 
 use App\Controllers\BaseController;
 use App\Models\Config;
-use Exception;
 use Psr\Http\Message\ResponseInterface;
 use Slim\Http\Response;
 use Slim\Http\ServerRequest;
+use Smarty\Exception;
 
 final class LlmController extends BaseController
 {
-    private static array $update_field = [
-        'llm_backend',
-        'openai_api_key',
-        'openai_model_id',
-        'google_ai_api_key',
-        'google_ai_model_id',
-        'vertex_ai_access_token',
-        'vertex_ai_location',
-        'vertex_ai_project_id',
-        'vertex_ai_model_id',
-        'huggingface_api_key',
-        'huggingface_endpoint_url',
-        'cf_workers_ai_account_id',
-        'cf_workers_ai_api_token',
-        'cf_workers_ai_model_id',
-        'anthropic_api_key',
-        'anthropic_model_id',
-    ];
+    private array $update_field;
+    private array $settings;
+
+    public function __construct()
+    {
+        parent::__construct();
+        $this->update_field = Config::getItemListByClass('llm');
+        $this->settings = Config::getClass('llm');
+    }
 
     /**
      * @throws Exception
      */
     public function index(ServerRequest $request, Response $response, array $args): ResponseInterface
     {
-        $settings = Config::getClass('llm');
-
         return $response->write(
             $this->view()
-                ->assign('update_field', self::$update_field)
-                ->assign('settings', $settings)
+                ->assign('update_field', $this->update_field)
+                ->assign('settings', $this->settings)
                 ->fetch('admin/setting/llm.tpl')
         );
     }
 
     public function save(ServerRequest $request, Response $response, array $args): ResponseInterface
     {
-        foreach (self::$update_field as $item) {
+        foreach ($this->update_field as $item) {
             if (! Config::set($item, $request->getParam($item))) {
                 return $response->withJson([
                     'ret' => 0,

+ 12 - 0
src/Models/Config.php

@@ -51,6 +51,18 @@ final class Config extends Model
         return $configs;
     }
 
+    public static function getItemListByClass($class): array
+    {
+        $items = [];
+        $all_configs = (new Config())->where('class', $class)->get();
+
+        foreach ($all_configs as $config) {
+            $items[] = $config->item;
+        }
+
+        return $items;
+    }
+
     public static function getPublicConfig(): array
     {
         $configs = [];

+ 3 - 1
src/Services/LLM.php

@@ -6,6 +6,7 @@ namespace App\Services;
 
 use App\Models\Config;
 use App\Services\LLM\Anthropic;
+use App\Services\LLM\AwsBedrock;
 use App\Services\LLM\CloudflareWorkersAI;
 use App\Services\LLM\GoogleAI;
 use App\Services\LLM\HuggingFace;
@@ -14,7 +15,7 @@ use App\Services\LLM\VertexAI;
 
 final class LLM
 {
-    public static function getBackend(): VertexAI|CloudflareWorkersAI|GoogleAI|HuggingFace|OpenAI|Anthropic
+    public static function getBackend(): VertexAI|CloudflareWorkersAI|GoogleAI|HuggingFace|OpenAI|Anthropic|AwsBedrock
     {
         return match (Config::obtain('llm_backend')) {
             'google-ai' => new GoogleAI(),
@@ -22,6 +23,7 @@ final class LLM
             'huggingface' => new HuggingFace(),
             'cf-workers-ai' => new CloudflareWorkersAI(),
             'anthropic' => new Anthropic(),
+            'aws-bedrock' => new AwsBedrock(),
             default => new OpenAI(),
         };
     }

+ 59 - 0
src/Services/LLM/AwsBedrock.php

@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Services\LLM;
+
+use App\Models\Config;
+use Aws\BedrockRuntime\BedrockRuntimeClient;
+use Aws\Credentials\Credentials;
+
+final class AwsBedrock extends Base
+{
+    public function textPrompt(string $q): string
+    {
+        return $this->makeRequest([
+            [
+                'role' => 'user',
+                'content' => $q,
+            ],
+        ]);
+    }
+
+    public function textPromptWithContext(array $context): string
+    {
+        return 'This service does not support context';
+    }
+
+    private function makeRequest(array $conversation): string
+    {
+        if (Config::obtain('aws_bedrock_access_key_id') === '' ||
+            Config::obtain('aws_bedrock_access_key_secret') === '') {
+            return 'Access Key ID or Access Key Secret is empty';
+        }
+
+        $client = new BedrockRuntimeClient([
+            'region' => Config::obtain('aws_bedrock_region'),
+            'version' => 'latest',
+            'profile' => 'default',
+            'credentials' => new Credentials(
+                Config::obtain('aws_bedrock_access_key_id'),
+                Config::obtain('aws_bedrock_access_key_secret'),
+            ),
+        ]);
+        // Note: Different models in AWS Bedrock have different inference parameters, this service currently only supports
+        // Meta Llama series models
+        // https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-meta.html
+        $request = [
+            'contentType' => 'application/json',
+            'body' => json_encode([
+                'prompt' => $conversation[0]['content'],
+                'temperature' => 0.5,
+                'max_gen_len' => 2048,
+            ]),
+            'modelId' => Config::obtain('aws_bedrock_model_id'),
+        ];
+
+        return json_decode($client->invokeModel($request)['body'])->generation;
+    }
+}

+ 18 - 7
src/Services/LLM/CloudflareWorkersAI.php

@@ -12,7 +12,23 @@ final class CloudflareWorkersAI extends Base
 {
     public function textPrompt(string $q): string
     {
-        if (Config::obtain('cf_workers_ai_account_id') === '' || Config::obtain('cf_workers_ai_api_token') === '') {
+        return $this->makeRequest([
+            [
+                'role' => 'user',
+                'content' => $q,
+            ],
+        ]);
+    }
+
+    public function textPromptWithContext(array $context): string
+    {
+        return 'This service does not support context';
+    }
+
+    private function makeRequest(array $conversation): string
+    {
+        if (Config::obtain('cf_workers_ai_account_id') === '' ||
+            Config::obtain('cf_workers_ai_api_token') === '') {
             return 'Cloudflare Workers AI Account ID or API Token not set';
         }
 
@@ -25,7 +41,7 @@ final class CloudflareWorkersAI extends Base
         ];
 
         $data = [
-            'prompt' => $q,
+            'prompt' => $conversation[0]['content'],
         ];
 
         try {
@@ -40,9 +56,4 @@ final class CloudflareWorkersAI extends Base
 
         return $response->result->response;
     }
-
-    public function textPromptWithContext(array $context): string
-    {
-        return 'This service does not support context';
-    }
 }

+ 18 - 7
src/Services/LLM/HuggingFace.php

@@ -12,7 +12,23 @@ final class HuggingFace extends Base
 {
     public function textPrompt(string $q): string
     {
-        if (Config::obtain('huggingface_api_key') === '' || Config::obtain('huggingface_endpoint_url') === '') {
+        return $this->makeRequest([
+            [
+                'role' => 'user',
+                'content' => $q,
+            ],
+        ]);
+    }
+
+    public function textPromptWithContext(array $context): string
+    {
+        return 'This service does not support context';
+    }
+
+    private function makeRequest(array $conversation): string
+    {
+        if (Config::obtain('huggingface_api_key') === '' ||
+            Config::obtain('huggingface_endpoint_url') === '') {
             return 'Hugging Face API key or Endpoint URL not set';
         }
 
@@ -23,7 +39,7 @@ final class HuggingFace extends Base
 
         $data = [
             'inputs' => [
-                'question' => $q,
+                'question' => $conversation[0]['content'],
             ],
         ];
 
@@ -39,9 +55,4 @@ final class HuggingFace extends Base
 
         return $response->answer;
     }
-
-    public function textPromptWithContext(array $context): string
-    {
-        return 'This service does not support context';
-    }
 }

+ 6 - 6
src/Services/Payment.php

@@ -43,14 +43,14 @@ final class Payment
         return $result;
     }
 
-    public static function getPaymentByName($name)
+    public static function getPaymentByName($name): ?string
     {
         $all = self::getPaymentMap();
 
         return $all[$name];
     }
 
-    public static function notify($request, $response, $args)
+    public static function notify($request, $response, $args): ResponseInterface
     {
         $payment = self::getPaymentByName($args['type']);
 
@@ -62,7 +62,7 @@ final class Payment
         return $response->withStatus(404);
     }
 
-    public static function returnHTML($request, $response, $args): string|ResponseInterface
+    public static function returnHTML($request, $response, $args): ResponseInterface
     {
         $payment = self::getPaymentByName($args['type']);
 
@@ -71,10 +71,10 @@ final class Payment
             return $instance->getReturnHTML($request, $response, $args);
         }
 
-        return '';
+        return $response->withStatus(404);
     }
 
-    public static function getStatus($request, $response, $args)
+    public static function getStatus($request, $response, $args): ResponseInterface
     {
         $payment = self::getPaymentByName($args['type']);
 
@@ -86,7 +86,7 @@ final class Payment
         return $response->withStatus(404);
     }
 
-    public static function purchase($request, $response, $args)
+    public static function purchase($request, $response, $args): ResponseInterface
     {
         $payment = self::getPaymentByName($args['type']);