mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-30 16:05:30 +00:00
feat(ai): refine model pools, navigation and GPU deployment configuration (#14257)
This commit is contained in:
+12
-11
@@ -347,17 +347,18 @@ type AgentAccountCreateReq struct {
|
||||
}
|
||||
|
||||
type AgentAccountUpdateReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
AuthMode string `json:"authMode"`
|
||||
VerifyModel string `json:"verifyModel"`
|
||||
ValidateAvailability *bool `json:"validateAvailability"`
|
||||
Remark string `json:"remark"`
|
||||
SyncAgents bool `json:"syncAgents"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
AuthMode string `json:"authMode"`
|
||||
VerifyModel string `json:"verifyModel"`
|
||||
ValidateAvailability *bool `json:"validateAvailability"`
|
||||
Remark string `json:"remark"`
|
||||
SyncAgents bool `json:"syncAgents"`
|
||||
Models []AgentAccountModel `json:"models"`
|
||||
}
|
||||
|
||||
type AgentAccountVerifyReq struct {
|
||||
|
||||
@@ -20,7 +20,7 @@ type IAgentAccountRepo interface {
|
||||
List(opts ...DBOption) ([]model.AgentAccount, error)
|
||||
Count() (int64, error)
|
||||
CountTextByProviders(providers []string) (map[string]int64, error)
|
||||
SaveLocal(account *model.AgentAccount, models []model.AgentAccountModel) error
|
||||
SaveWithModels(account *model.AgentAccount, models []model.AgentAccountModel) error
|
||||
}
|
||||
|
||||
func NewIAgentAccountRepo() IAgentAccountRepo {
|
||||
@@ -106,7 +106,7 @@ func (a AgentAccountRepo) CountTextByProviders(providers []string) (map[string]i
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
func (a AgentAccountRepo) SaveLocal(account *model.AgentAccount, models []model.AgentAccountModel) error {
|
||||
func (a AgentAccountRepo) SaveWithModels(account *model.AgentAccount, models []model.AgentAccountModel) error {
|
||||
return global.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Save(account).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -1105,9 +1105,23 @@ func (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Models != nil {
|
||||
if account.AppInstallID != 0 {
|
||||
return buserr.WithDetail("ErrInvalidParams", "local model pool is managed by the deployment", nil)
|
||||
}
|
||||
models, err = normalizeAgentAccountModels(account, req.Models)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureAccountModelsNotBound(account, models); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
requestedVerifyModel := req.VerifyModel
|
||||
if strings.TrimSpace(requestedVerifyModel) == "" {
|
||||
requestedVerifyModel = account.VerifyModel
|
||||
if _, ok := findAgentAccountModelForProvider(provider, models, account.VerifyModel); ok {
|
||||
requestedVerifyModel = account.VerifyModel
|
||||
}
|
||||
}
|
||||
verifyModel := ""
|
||||
if account.AppInstallID == 0 || len(models) > 0 {
|
||||
@@ -1131,7 +1145,16 @@ func (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error {
|
||||
account.Remark = req.Remark
|
||||
account.Verified = true
|
||||
|
||||
if err := agentAccountRepo.Save(account); err != nil {
|
||||
if req.Models != nil {
|
||||
rows := make([]model.AgentAccountModel, 0, len(models))
|
||||
for _, item := range models {
|
||||
rows = append(rows, model.AgentAccountModel{Model: item.ID, Name: item.Name})
|
||||
}
|
||||
err = agentAccountRepo.SaveWithModels(account, rows)
|
||||
} else {
|
||||
err = agentAccountRepo.Save(account)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
terminalai.InvalidateTerminalRuntimeCache()
|
||||
|
||||
@@ -88,7 +88,7 @@ func SyncLocalModelAccount(install model.AppInstall, accountID uint, baseURL str
|
||||
}
|
||||
}
|
||||
if modelsChanged {
|
||||
err = agentAccountRepo.SaveLocal(account, rows)
|
||||
err = agentAccountRepo.SaveWithModels(account, rows)
|
||||
} else if accountChanged {
|
||||
err = agentAccountRepo.Save(account)
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/accelerator"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
||||
"github.com/compose-spec/compose-go/v2/interpolation"
|
||||
"github.com/compose-spec/compose-go/v2/template"
|
||||
@@ -16,76 +18,72 @@ import (
|
||||
|
||||
func LoadModelGPUDevices() dto.ModelGPUDevices {
|
||||
result := dto.ModelGPUDevices{Devices: []dto.ModelGPUDevice{}, Warnings: []string{}}
|
||||
uuidPattern := re.GetRegex(re.NvidiaGPUUUIDPattern)
|
||||
renderPattern := re.GetRegex(re.IntelGPURenderNamePattern)
|
||||
ascendPattern := re.GetRegex(re.AscendGPUDeviceNamePattern)
|
||||
for _, directory := range []string{"/proc/driver/nvidia/gpus", "/sys/class/drm", "/dev"} {
|
||||
entries, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
result.Warnings = append(result.Warnings, err.Error())
|
||||
}
|
||||
ok, client := accelerator.New()
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
snapshot, err := client.Collect(context.Background())
|
||||
if err != nil {
|
||||
result.Warnings = append(result.Warnings, err.Error())
|
||||
return result
|
||||
}
|
||||
result.Warnings = append(result.Warnings, snapshot.Info.Warnings...)
|
||||
for _, monitored := range snapshot.Devices {
|
||||
if monitored.ParentID != "" {
|
||||
continue
|
||||
}
|
||||
for _, entry := range entries {
|
||||
var device dto.ModelGPUDevice
|
||||
var devicePath string
|
||||
switch directory {
|
||||
case "/proc/driver/nvidia/gpus":
|
||||
informationPath := filepath.Join(directory, entry.Name(), "information")
|
||||
content, err := os.ReadFile(informationPath)
|
||||
device := dto.ModelGPUDevice{Available: true}
|
||||
devicePath := ""
|
||||
switch {
|
||||
case monitored.GPU != nil:
|
||||
if monitored.Vendor == "nvidia" && !re.GetRegex(re.NvidiaGPUUUIDPattern).MatchString(monitored.GPU.UUID) {
|
||||
result.Warnings = append(result.Warnings, fmt.Sprintf("invalid NVIDIA GPU UUID from nvidia-smi: %s", monitored.GPU.UUID))
|
||||
continue
|
||||
}
|
||||
device.ID = monitored.ID
|
||||
device.Name = fmt.Sprintf("GPU %d · %s", monitored.Index, monitored.Name)
|
||||
device.Type = monitored.Vendor
|
||||
device.UUID = monitored.GPU.UUID
|
||||
case monitored.XPU != nil:
|
||||
devicePath = monitored.XPU.Basic.DrmDevice
|
||||
if !re.GetRegex(re.IntelGPUDevicePathPattern).MatchString(devicePath) {
|
||||
directory := filepath.Join("/sys/bus/pci/devices", filepath.Base(monitored.BusID), "drm")
|
||||
entries, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
result.Warnings = append(result.Warnings, err.Error())
|
||||
result.Warnings = append(result.Warnings, fmt.Sprintf("resolve Intel GPU %d render device: %v", monitored.Index, err))
|
||||
continue
|
||||
}
|
||||
fields := map[string]string{}
|
||||
for _, line := range strings.Split(string(content), "\n") {
|
||||
key, value, found := strings.Cut(line, ":")
|
||||
if found {
|
||||
fields[strings.TrimSpace(key)] = strings.TrimSpace(value)
|
||||
devicePath = ""
|
||||
for _, entry := range entries {
|
||||
if re.GetRegex(re.IntelGPURenderNamePattern).MatchString(entry.Name()) {
|
||||
devicePath = filepath.Join("/dev/dri", entry.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
uuid := fields["GPU UUID"]
|
||||
minor, err := strconv.Atoi(fields["Device Minor"])
|
||||
if !uuidPattern.MatchString(uuid) || err != nil || minor < 0 {
|
||||
result.Warnings = append(result.Warnings, fmt.Sprintf("invalid NVIDIA GPU UUID or device minor in %s", informationPath))
|
||||
if devicePath == "" {
|
||||
result.Warnings = append(result.Warnings, fmt.Sprintf("Intel GPU %d has no render device", monitored.Index))
|
||||
continue
|
||||
}
|
||||
name := fields["Model"]
|
||||
if name == "" {
|
||||
name = "NVIDIA"
|
||||
}
|
||||
device = dto.ModelGPUDevice{ID: "nvidia:" + uuid, Name: name + " · " + entry.Name(), Type: "nvidia", UUID: uuid}
|
||||
devicePath = filepath.Join("/dev", "nvidia"+strconv.Itoa(minor))
|
||||
case "/sys/class/drm":
|
||||
if !renderPattern.MatchString(entry.Name()) {
|
||||
continue
|
||||
}
|
||||
vendor, err := os.ReadFile(filepath.Join(directory, entry.Name(), "device/vendor"))
|
||||
if err != nil {
|
||||
result.Warnings = append(result.Warnings, err.Error())
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(string(vendor)) != "0x8086" {
|
||||
continue
|
||||
}
|
||||
devicePath = filepath.Join("/dev/dri", entry.Name())
|
||||
device = dto.ModelGPUDevice{ID: "intel:" + devicePath, Name: "Intel · " + entry.Name(), Type: "intel"}
|
||||
case "/dev":
|
||||
if !ascendPattern.MatchString(entry.Name()) {
|
||||
continue
|
||||
}
|
||||
devicePath = filepath.Join(directory, entry.Name())
|
||||
device = dto.ModelGPUDevice{ID: "ascend:" + strings.TrimPrefix(entry.Name(), "davinci"), Name: "Ascend · " + entry.Name(), Type: "ascend"}
|
||||
}
|
||||
device.ID = "intel:" + devicePath
|
||||
device.Name = fmt.Sprintf("XPU %d · %s", monitored.Index, monitored.Name)
|
||||
device.Type = "intel"
|
||||
case monitored.NPU != nil:
|
||||
device.ID = fmt.Sprintf("ascend:%d", monitored.Index)
|
||||
device.Name = fmt.Sprintf("NPU %d · Chip %d · %s", monitored.NPUIndex, monitored.ChipIndex, monitored.Name)
|
||||
device.Type = "ascend"
|
||||
devicePath = fmt.Sprintf("/dev/davinci%d", monitored.Index)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if devicePath != "" {
|
||||
info, err := os.Stat(devicePath)
|
||||
device.Available = err == nil && info.Mode()&os.ModeCharDevice != 0
|
||||
if err != nil {
|
||||
result.Warnings = append(result.Warnings, err.Error())
|
||||
} else {
|
||||
device.Available = info.Mode()&os.ModeCharDevice != 0
|
||||
}
|
||||
result.Devices = append(result.Devices, device)
|
||||
}
|
||||
result.Devices = append(result.Devices, device)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -649,9 +647,17 @@ func resolveModelGPUValue(value string, params map[string]interface{}) (string,
|
||||
|
||||
func loadModelAscendDeviceIDs() []string {
|
||||
ids := []string{}
|
||||
for _, device := range LoadModelGPUDevices().Devices {
|
||||
if device.Type == "ascend" && device.Available {
|
||||
ids = append(ids, strings.TrimPrefix(device.ID, "ascend:"))
|
||||
entries, err := os.ReadDir("/dev")
|
||||
if err != nil {
|
||||
return ids
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !re.GetRegex(re.AscendGPUDeviceNamePattern).MatchString(entry.Name()) {
|
||||
continue
|
||||
}
|
||||
info, err := os.Stat(filepath.Join("/dev", entry.Name()))
|
||||
if err == nil && info.Mode()&os.ModeCharDevice != 0 {
|
||||
ids = append(ids, strings.TrimPrefix(entry.Name(), "davinci"))
|
||||
}
|
||||
}
|
||||
slices.SortFunc(ids, func(a, b string) int {
|
||||
|
||||
@@ -72,7 +72,8 @@ type Device struct {
|
||||
}
|
||||
|
||||
type Basic struct {
|
||||
UUID string `json:"uuid"`
|
||||
DrmDevice string `json:"-"`
|
||||
UUID string `json:"uuid"`
|
||||
|
||||
DeviceID int `json:"deviceID"`
|
||||
DeviceName string `json:"deviceName"`
|
||||
|
||||
@@ -93,6 +93,7 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
|
||||
xpu := Device{
|
||||
ProcessStatus: "unavailable",
|
||||
Basic: Basic{
|
||||
DrmDevice: device.DrmDevice,
|
||||
UUID: device.UUID,
|
||||
DeviceID: device.DeviceID,
|
||||
DeviceName: device.DeviceName,
|
||||
@@ -137,6 +138,9 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
|
||||
return
|
||||
}
|
||||
|
||||
if info.DrmDevice != "" {
|
||||
xpu.Basic.DrmDevice = info.DrmDevice
|
||||
}
|
||||
xpu.Basic.DriverVersion = info.DriverVersion
|
||||
|
||||
bytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64)
|
||||
|
||||
@@ -71,6 +71,7 @@ var WebUrlMap = map[string]struct{}{
|
||||
"/ai/skills-hub": {},
|
||||
"/ai/skills-hub/targets": {},
|
||||
"/ai/benchmark": {},
|
||||
"/ai/model": {},
|
||||
"/ai/model/account": {},
|
||||
"/ai/model/local": {},
|
||||
"/ai/model/local/templates": {},
|
||||
|
||||
@@ -737,6 +737,7 @@ export namespace AI {
|
||||
baseURL: string;
|
||||
apiType: string;
|
||||
authMode: string;
|
||||
models?: AgentAccountModel[];
|
||||
verifyModel: string;
|
||||
validateAvailability: boolean;
|
||||
remark: string;
|
||||
|
||||
@@ -92,12 +92,15 @@ const expiresInfo = ref(0);
|
||||
const expiresAlertVisible = ref(false);
|
||||
const expiresAlertKey = computed(() => (isEnterprise.value ? 'xpack.expiresEnterpriseAlert' : 'xpack.expiresProAlert'));
|
||||
|
||||
const handleChange = (label: string) => {
|
||||
const handleChange = async (label: string) => {
|
||||
const btn = buttonArray.value.find((btn) => btn.label === label);
|
||||
if (!btn) return;
|
||||
if (btn.path) routerToPathWithQuery(btn.path, { uncached: 'true' });
|
||||
else if (btn.name) routerToNameWithQuery(btn.name, { uncached: 'true' });
|
||||
activeName.value = btn.label;
|
||||
try {
|
||||
if (btn.path) await routerToPathWithQuery(btn.path, { uncached: 'true' });
|
||||
else if (btn.name) await routerToNameWithQuery(btn.name, { uncached: 'true' });
|
||||
} finally {
|
||||
syncActiveName();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
@@ -154,7 +157,9 @@ function syncActiveName() {
|
||||
if (buttonArray.value.length) {
|
||||
let isPathExist = false;
|
||||
const btn = buttonArray.value.find((btn) => {
|
||||
return btn.path && router.currentRoute.value.path.startsWith(btn.path);
|
||||
return btn.path
|
||||
? router.currentRoute.value.path.startsWith(btn.path)
|
||||
: btn.name && router.currentRoute.value.name === btn.name;
|
||||
});
|
||||
if (btn) {
|
||||
isPathExist = true;
|
||||
|
||||
@@ -1092,14 +1092,19 @@ const message = {
|
||||
container_conn_helper: 'Use this address for inter-container access or connection',
|
||||
from_remote: 'This model was not downloaded via 1Panel, no related pull logs.',
|
||||
no_logs: 'The pull logs for this model have been deleted and cannot be viewed.',
|
||||
vllmVersionHelper: 'For FusionXpark GB10 servers, please select the -cu130 version.',
|
||||
vllmVersionHelper: {
|
||||
default: 'Official standard version with CUDA 13.0, for newer drivers and newer GPU architectures.',
|
||||
cuda: 'Legacy architecture compatibility version with CUDA 12.9, for older GPU architectures using CUDA versions below 13.0.',
|
||||
ubuntu: 'NVIDIA GB10 standard version with CUDA 13.0, for NVIDIA GB10 devices.',
|
||||
dspark: 'NVIDIA GB10 dual-device custom version, using a custom image maintained by 1Panel.',
|
||||
},
|
||||
vllmDeployProfile: 'Deployment model',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper:
|
||||
'The startup command must use port {0}; otherwise, the service will be inaccessible.',
|
||||
ascendVisibleDevices: 'Ascend visible devices (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'Sync to model account',
|
||||
syncModelAccount: 'Sync to model service',
|
||||
modelAccountAddressHelper:
|
||||
'Container address is for agent containers; 127.0.0.1, system IP, or custom address is for AI Gateway or external services.',
|
||||
containerAddress: 'Container address',
|
||||
|
||||
@@ -1094,14 +1094,20 @@ const message = {
|
||||
container_conn_helper: 'Utilice esta dirección para el acceso o conexión entre contenedores',
|
||||
from_remote: 'Este modelo no fue descargado vía 1Panel, no hay registros de descarga relacionados.',
|
||||
no_logs: 'Los registros de descarga de este modelo han sido eliminados y no se pueden consultar.',
|
||||
vllmVersionHelper: 'Para servidores FusionXpark GB10, seleccione la versión -cu130.',
|
||||
vllmVersionHelper: {
|
||||
default:
|
||||
'Versión oficial estándar con CUDA 13.0, para controladores recientes y GPU de nuevas arquitecturas.',
|
||||
cuda: 'Versión compatible con arquitecturas antiguas con CUDA 12.9, para GPU antiguas que utilizan versiones de CUDA anteriores a 13.0.',
|
||||
ubuntu: 'Versión estándar de NVIDIA GB10 con CUDA 13.0, para dispositivos NVIDIA GB10.',
|
||||
dspark: 'Versión personalizada de NVIDIA GB10 para dos dispositivos, con una imagen mantenida por 1Panel.',
|
||||
},
|
||||
vllmDeployProfile: 'Modelo de despliegue',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper:
|
||||
'El comando de inicio debe usar el puerto {0}; de lo contrario, no se podrá acceder al servicio.',
|
||||
ascendVisibleDevices: 'Dispositivos Ascend visibles (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'Sincronizar con cuenta de modelo',
|
||||
syncModelAccount: 'Sincronizar con servicio de modelo',
|
||||
modelAccountAddressHelper:
|
||||
'La dirección del contenedor es para contenedores de agentes; 127.0.0.1, IP del sistema o dirección personalizada es para AI Gateway o servicios externos.',
|
||||
containerAddress: 'Dirección del contenedor',
|
||||
|
||||
@@ -1076,14 +1076,19 @@ const message = {
|
||||
container_conn_helper: 'از این آدرس برای دسترسی بین کانتینرها یا اتصال استفاده کنید',
|
||||
from_remote: 'این مدل از طریق 1Panel دانلود نشده است، لاگ مربوط به دریافت وجود ندارد.',
|
||||
no_logs: 'لاگ دریافت این مدل حذف شده است و قابل مشاهده نیست.',
|
||||
vllmVersionHelper: 'برای سرورهای FusionXpark GB10، لطفاً نسخه -cu130 را انتخاب کنید.',
|
||||
vllmVersionHelper: {
|
||||
default: 'نسخه استاندارد رسمی با CUDA 13.0، مناسب درایورهای جدید و معماریهای جدید GPU.',
|
||||
cuda: 'نسخه سازگار با معماریهای قدیمی با CUDA 12.9، مناسب GPUهای قدیمی که از نسخههای CUDA پایینتر از 13.0 استفاده میکنند.',
|
||||
ubuntu: 'نسخه استاندارد NVIDIA GB10 با CUDA 13.0، مناسب دستگاههای NVIDIA GB10.',
|
||||
dspark: 'نسخه سفارشی NVIDIA GB10 برای دو دستگاه، با ایمیج سفارشی که توسط 1Panel نگهداری میشود.',
|
||||
},
|
||||
vllmDeployProfile: 'مدل استقرار',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper:
|
||||
'فرمان راهاندازی باید از پورت {0} استفاده کند؛ در غیر این صورت سرویس قابل دسترسی نخواهد بود.',
|
||||
ascendVisibleDevices: 'دستگاههای قابل مشاهده Ascend (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'همگامسازی با حساب مدل',
|
||||
syncModelAccount: 'همگامسازی با سرویس مدل',
|
||||
modelAccountAddressHelper:
|
||||
'آدرس کانتینر برای کانتینرهای عامل است؛ 127.0.0.1، IP سیستم یا آدرس سفارشی برای دروازه AI یا خدمات خارجی است.',
|
||||
containerAddress: 'آدرس کانتینر',
|
||||
|
||||
@@ -1080,14 +1080,20 @@ const message = {
|
||||
container_conn_helper: 'コンテナ間のアクセスまたは接続にこのアドレスを使用',
|
||||
from_remote: 'このモデルは1Panelを介してダウンロードされておらず、関連するプルログはありません。',
|
||||
no_logs: 'このモデルのプルログは削除されており、関連するログを表示できません。',
|
||||
vllmVersionHelper: 'FusionXpark GB10 サーバーでは -cu130 バージョンを選択してください。',
|
||||
vllmVersionHelper: {
|
||||
default:
|
||||
'公式の標準バージョン。CUDA 13.0 を使用し、新しいドライバーと新しい GPU アーキテクチャに対応します。',
|
||||
cuda: '旧アーキテクチャ向け互換バージョン。CUDA 12.9 を使用し、CUDA 13.0 未満を使用する旧世代 GPU に対応します。',
|
||||
ubuntu: 'NVIDIA GB10 標準版。CUDA 13.0 を使用し、NVIDIA GB10 デバイスに対応します。',
|
||||
dspark: 'NVIDIA GB10 の 2 台構成向けカスタム版。1Panel が独自に保守するカスタムイメージです。',
|
||||
},
|
||||
vllmDeployProfile: 'デプロイモデル',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper:
|
||||
'起動コマンドではポート {0} を使用する必要があります。使用しない場合、サービスにアクセスできません。',
|
||||
ascendVisibleDevices: 'Ascend 可視デバイス(ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'モデルアカウントに同期',
|
||||
syncModelAccount: 'モデルサービスに同期',
|
||||
modelAccountAddressHelper:
|
||||
'コンテナアドレスはエージェントコンテナ向けです。127.0.0.1、システム IP、カスタムアドレスは AI Gateway または外部サービス向けです。',
|
||||
containerAddress: 'コンテナアドレス',
|
||||
|
||||
@@ -1070,13 +1070,18 @@ const message = {
|
||||
container_conn_helper: '컨테이너 간 접근 또는 연결에 이 주소를 사용',
|
||||
from_remote: '이 모델은 1Panel을 통해 다운로드되지 않았으며 관련 풀 로그가 없습니다.',
|
||||
no_logs: '이 모델의 풀 로그가 삭제되어 관련 로그를 볼 수 없습니다.',
|
||||
vllmVersionHelper: 'FusionXpark GB10 서버는 -cu130 버전을 선택하세요.',
|
||||
vllmVersionHelper: {
|
||||
default: '공식 표준 버전으로, CUDA 13.0을 사용하며 최신 드라이버와 새로운 GPU 아키텍처에 적합합니다.',
|
||||
cuda: '이전 아키텍처 호환 버전으로, CUDA 12.9를 사용하며 CUDA 13.0 미만을 사용하는 이전 GPU 아키텍처에 적합합니다.',
|
||||
ubuntu: 'NVIDIA GB10 표준 버전으로, CUDA 13.0을 사용하며 NVIDIA GB10 장치에 적합합니다.',
|
||||
dspark: 'NVIDIA GB10 장치 2대 구성용 맞춤 버전으로, 1Panel이 자체 유지 관리하는 맞춤 이미지입니다.',
|
||||
},
|
||||
vllmDeployProfile: '배포 모델',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper: '시작 명령은 {0} 포트를 사용해야 하며, 그렇지 않으면 서비스에 접근할 수 없습니다.',
|
||||
ascendVisibleDevices: 'Ascend 표시 장치 (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: '모델 계정에 동기화',
|
||||
syncModelAccount: '모델 서비스에 동기화',
|
||||
modelAccountAddressHelper:
|
||||
'컨테이너 주소는 에이전트 컨테이너 호출에 적합합니다. 127.0.0.1, 시스템 IP 또는 사용자 지정 주소는 AI Gateway 또는 외부 서비스 호출에 적합합니다.',
|
||||
containerAddress: '컨테이너 주소',
|
||||
|
||||
@@ -1067,13 +1067,18 @@ const message = {
|
||||
container_conn_helper: 'ໃຊ້ທີ່ຢູ່ນີ້ສຳລັບການເຂົ້າເຖິງ ຫຼື ເຊື່ອມຕໍ່ລະຫວ່າງຄອນເທນເນີ',
|
||||
from_remote: 'ໂມເດວນີ້ບໍ່ໄດ້ດາວໂຫຼດຜ່ານ 1Panel, ບໍ່ມີລັອກການດຶງຂໍ້ມູນທີ່ກ່ຽວຂ້ອງ.',
|
||||
no_logs: 'ລັອກການດຶງຂໍ້ມູນຂອງໂມເດວນີ້ຖືກລຶບແລ້ວ ແລະ ບໍ່ສາມາດເບິ່ງໄດ້.',
|
||||
vllmVersionHelper: 'ສຳລັບເຊີເວີ FusionXpark GB10, ກະລຸນາເລືອກເວີຊັນ -cu130.',
|
||||
vllmVersionHelper: {
|
||||
default: 'ເວີຊັນມາດຕະຖານທາງການທີ່ໃຊ້ CUDA 13.0, ເໝາະສຳລັບໄດຣເວີໃໝ່ ແລະ GPU ສະຖາປັດຕະຍະກຳໃໝ່.',
|
||||
cuda: 'ເວີຊັນຮອງຮັບສະຖາປັດຕະຍະກຳເກົ່າທີ່ໃຊ້ CUDA 12.9, ເໝາະສຳລັບ GPU ເກົ່າທີ່ໃຊ້ CUDA ຕ່ຳກວ່າ 13.0.',
|
||||
ubuntu: 'ເວີຊັນມາດຕະຖານ NVIDIA GB10 ທີ່ໃຊ້ CUDA 13.0, ເໝາະສຳລັບອຸປະກອນ NVIDIA GB10.',
|
||||
dspark: 'ເວີຊັນປັບແຕ່ງ NVIDIA GB10 ສຳລັບສອງອຸປະກອນ, ໃຊ້ອິມເມຈປັບແຕ່ງທີ່ 1Panel ດູແລເອງ.',
|
||||
},
|
||||
vllmDeployProfile: 'ໂມເດວສຳລັບຕິດຕັ້ງ',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper: 'ຄຳສັ່ງເລີ່ມຕົ້ນຕ້ອງໃຊ້ພອດ {0}; ບໍ່ດັ່ງນັ້ນຈະບໍ່ສາມາດເຂົ້າເຖິງບໍລິການໄດ້.',
|
||||
ascendVisibleDevices: 'ອຸປະກອນ Ascend ທີ່ເຫັນໄດ້ (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'ຊິ້ງຄ໌ໄປຍັງບັນຊີໂມເດວ',
|
||||
syncModelAccount: 'ຊິ້ງຄ໌ໄປຍັງບໍລິການໂມເດວ',
|
||||
modelAccountAddressHelper:
|
||||
'ທີ່ຢູ່ຄອນເທນເນີແມ່ນສຳລັບຄອນເທນເນີຕົວແທນ; 127.0.0.1, IP ລະບົບ, ຫຼື ທີ່ຢູ່ກຳນົດເອງແມ່ນສຳລັບ AI Gateway ຫຼື ບໍລິການພາຍນອກ.',
|
||||
containerAddress: 'ທີ່ຢູ່ຄອນເທນເນີ',
|
||||
|
||||
@@ -1102,14 +1102,19 @@ const message = {
|
||||
container_conn_helper: 'Gunakan alamat ini untuk akses atau sambungan antara kontena',
|
||||
from_remote: 'Model ini tidak dimuat turun melalui 1Panel; tiada log muat turun berkaitan.',
|
||||
no_logs: 'Log muat turun model ini telah dipadam dan tidak boleh dilihat.',
|
||||
vllmVersionHelper: 'Untuk pelayan FusionXpark GB10, sila pilih versi -cu130.',
|
||||
vllmVersionHelper: {
|
||||
default: 'Versi standard rasmi dengan CUDA 13.0, untuk pemacu baharu dan seni bina GPU baharu.',
|
||||
cuda: 'Versi keserasian seni bina lama dengan CUDA 12.9, untuk GPU lama yang menggunakan versi CUDA di bawah 13.0.',
|
||||
ubuntu: 'Versi standard NVIDIA GB10 dengan CUDA 13.0, untuk peranti NVIDIA GB10.',
|
||||
dspark: 'Versi tersuai NVIDIA GB10 untuk dua peranti, menggunakan imej tersuai yang diselenggara oleh 1Panel.',
|
||||
},
|
||||
vllmDeployProfile: 'Model penggunaan',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper:
|
||||
'Perintah permulaan mesti menggunakan port {0}; jika tidak, perkhidmatan tidak dapat diakses.',
|
||||
ascendVisibleDevices: 'Peranti Ascend boleh dilihat (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'Segerakkan ke akaun model',
|
||||
syncModelAccount: 'Segerakkan ke perkhidmatan model',
|
||||
modelAccountAddressHelper:
|
||||
'Alamat kontena sesuai untuk kontena agen; 127.0.0.1, IP sistem, atau alamat tersuai sesuai untuk AI Gateway atau perkhidmatan luaran.',
|
||||
containerAddress: 'Alamat kontena',
|
||||
|
||||
@@ -1098,14 +1098,19 @@ const message = {
|
||||
container_conn_helper: 'Use este endereço para acesso ou conexão entre contêineres',
|
||||
from_remote: 'Este modelo não foi baixado pelo 1Panel; não há logs de download relacionados.',
|
||||
no_logs: 'Os logs de download deste modelo foram excluídos e não podem ser visualizados.',
|
||||
vllmVersionHelper: 'Para servidores FusionXpark GB10, selecione a versão -cu130.',
|
||||
vllmVersionHelper: {
|
||||
default: 'Versão padrão oficial com CUDA 13.0, para drivers recentes e GPUs de novas arquiteturas.',
|
||||
cuda: 'Versão de compatibilidade com arquiteturas antigas com CUDA 12.9, para GPUs antigas que usam versões do CUDA inferiores à 13.0.',
|
||||
ubuntu: 'Versão padrão do NVIDIA GB10 com CUDA 13.0, para dispositivos NVIDIA GB10.',
|
||||
dspark: 'Versão personalizada do NVIDIA GB10 para dois dispositivos, com uma imagem mantida pelo 1Panel.',
|
||||
},
|
||||
vllmDeployProfile: 'Modelo de implantação',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper:
|
||||
'O comando de inicialização deve usar a porta {0}; caso contrário, o serviço ficará inacessível.',
|
||||
ascendVisibleDevices: 'Dispositivos Ascend visíveis (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'Sincronizar com conta de modelo',
|
||||
syncModelAccount: 'Sincronizar com serviço de modelo',
|
||||
modelAccountAddressHelper:
|
||||
'Use o endereço do contêiner para agentes; 127.0.0.1, IP do sistema ou endereço personalizado para AI Gateway ou serviços externos.',
|
||||
containerAddress: 'Endereço do contêiner',
|
||||
|
||||
@@ -1091,13 +1091,19 @@ const message = {
|
||||
container_conn_helper: 'Используйте этот адрес для доступа или подключения между контейнерами',
|
||||
from_remote: 'Эта модель не была загружена через 1Panel, нет связанных журналов извлечения.',
|
||||
no_logs: 'Журналы извлечения для этой модели были удалены и не могут быть просмотрены.',
|
||||
vllmVersionHelper: 'Для серверов FusionXpark GB10 выберите версию -cu130.',
|
||||
vllmVersionHelper: {
|
||||
default:
|
||||
'Официальная стандартная версия с CUDA 13.0 для новых драйверов и видеокарт с новой архитектурой.',
|
||||
cuda: 'Версия совместимости со старыми архитектурами с CUDA 12.9 для видеокарт, использующих версии CUDA ниже 13.0.',
|
||||
ubuntu: 'Стандартная версия NVIDIA GB10 с CUDA 13.0 для устройств NVIDIA GB10.',
|
||||
dspark: 'Специальная версия NVIDIA GB10 для двух устройств с образом, поддерживаемым 1Panel.',
|
||||
},
|
||||
vllmDeployProfile: 'Модель развертывания',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper: 'Команда запуска должна использовать порт {0}, иначе сервис будет недоступен.',
|
||||
ascendVisibleDevices: 'Видимые устройства Ascend (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'Синхронизировать с аккаунтом модели',
|
||||
syncModelAccount: 'Синхронизировать с сервисом модели',
|
||||
modelAccountAddressHelper:
|
||||
'Адрес контейнера подходит для контейнеров агентов; 127.0.0.1, системный IP или пользовательский адрес подходит для AI Gateway или внешних сервисов.',
|
||||
containerAddress: 'Адрес контейнера',
|
||||
|
||||
@@ -1101,14 +1101,19 @@ const message = {
|
||||
container_conn_helper: 'Konteynerler arası erişim veya bağlantı için bu adresi kullanın',
|
||||
from_remote: 'Bu model 1Panel aracılığıyla indirilmedi, ilgili çekme logları yok.',
|
||||
no_logs: 'Bu modelin çekme logları silindi ve görüntülenemiyor.',
|
||||
vllmVersionHelper: 'FusionXpark GB10 sunucuları için lütfen -cu130 sürümünü seçin.',
|
||||
vllmVersionHelper: {
|
||||
default: 'Yeni sürücüler ve yeni GPU mimarileri için CUDA 13.0 içeren resmi standart sürüm.',
|
||||
cuda: '13.0 altındaki CUDA sürümlerini kullanan eski GPU mimarileri için CUDA 12.9 içeren uyumluluk sürümü.',
|
||||
ubuntu: 'NVIDIA GB10 cihazları için CUDA 13.0 içeren NVIDIA GB10 standart sürümü.',
|
||||
dspark: '1Panel tarafından bakımı yapılan özel imajı kullanan, iki cihaz için NVIDIA GB10 özel sürümü.',
|
||||
},
|
||||
vllmDeployProfile: 'Dağıtım modeli',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper:
|
||||
'Başlatma komutu {0} numaralı bağlantı noktasını kullanmalıdır; aksi halde hizmete erişilemez.',
|
||||
ascendVisibleDevices: 'Görünür Ascend cihazları (ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: 'Model hesabına senkronize et',
|
||||
syncModelAccount: 'Model hizmetine senkronize et',
|
||||
modelAccountAddressHelper:
|
||||
'Konteyner adresi ajan konteynerleri içindir; 127.0.0.1, sistem IP veya özel adres AI Gateway ya da harici servisler içindir.',
|
||||
containerAddress: 'Konteyner adresi',
|
||||
|
||||
@@ -1030,13 +1030,18 @@ const message = {
|
||||
container_conn_helper: '容器間瀏覽或連接使用此地址',
|
||||
from_remote: '該模型並非透過 1Panel 下載,無相關拉取日誌。',
|
||||
no_logs: '該模型的拉取日誌已被刪除,無法檢視相關日誌。',
|
||||
vllmVersionHelper: 'FusionXpark GB10 伺服器請選擇 -cu130 版本',
|
||||
vllmVersionHelper: {
|
||||
default: '官方標準版本,CUDA 13.0,適用於新驅動程式和新架構顯示卡。',
|
||||
cuda: '舊架構相容版本,CUDA 12.9,適用於 CUDA 版本低於 13.0 的舊架構顯示卡。',
|
||||
ubuntu: 'NVIDIA GB10 標準版,CUDA 13.0,適用於 NVIDIA GB10 裝置。',
|
||||
dspark: 'NVIDIA GB10 雙裝置客製版,由 1Panel 自行維護的客製映像。',
|
||||
},
|
||||
vllmDeployProfile: '部署模型',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper: '啟動命令必須使用 {0} 連接埠,否則服務將無法存取。',
|
||||
ascendVisibleDevices: 'Ascend 可見裝置(ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: '同步到模型帳號',
|
||||
syncModelAccount: '同步到模型服務',
|
||||
modelAccountAddressHelper:
|
||||
'容器地址適合智能體容器呼叫;127.0.0.1、本機 IP 或自訂地址適合 AI 閘道或外部服務呼叫。',
|
||||
containerAddress: '容器地址',
|
||||
|
||||
@@ -1043,13 +1043,18 @@ const message = {
|
||||
container_conn_helper: '容器间访问或连接使用此地址',
|
||||
from_remote: '该模型并非通过 1Panel 下载,无相关拉取日志。',
|
||||
no_logs: '该模型的拉取日志已被删除,无法查看相关日志。',
|
||||
vllmVersionHelper: 'FusionXpark GB10 服务器请选择 -cu130 版本',
|
||||
vllmVersionHelper: {
|
||||
default: '官方标准版本,CUDA 13.0,适用于新驱动和新架构显卡。',
|
||||
cuda: '旧架构兼容版本,CUDA 12.9,适用于 CUDA 版本低于 13.0 的旧架构显卡。',
|
||||
ubuntu: 'NVIDIA GB10 标准版,CUDA 13.0,适用于 NVIDIA GB10 设备。',
|
||||
dspark: 'NVIDIA GB10 双设备定制版,1Panel 自行维护的定制镜像。',
|
||||
},
|
||||
vllmDeployProfile: '部署模型',
|
||||
vllmProfileFlash0731: 'DeepSeek V4 Flash 0731',
|
||||
vllmProfileVisionExp: 'DeepSeek V4 Flash Vision Exp',
|
||||
vllmCommandPortHelper: '启动命令必须使用 {0} 端口,否则服务将无法访问。',
|
||||
ascendVisibleDevices: 'Ascend 可见设备(ASCEND_RT_VISIBLE_DEVICES)',
|
||||
syncModelAccount: '同步到模型账号',
|
||||
syncModelAccount: '同步到模型服务',
|
||||
modelAccountAddressHelper:
|
||||
'容器地址适合智能体容器调用;127.0.0.1、本机 IP 或自定义地址适合 AI 网关或外部服务调用。',
|
||||
containerAddress: '容器地址',
|
||||
|
||||
@@ -31,8 +31,15 @@ const clearLoginStatus = () => {
|
||||
};
|
||||
|
||||
router.beforeEach(async (to, from) => {
|
||||
const { entrance, isEnterprise, isEnterpriseLicenseLoaded, isEnterpriseLicensed, isLogin, isOnRestart } =
|
||||
useGlobalStore();
|
||||
const {
|
||||
entrance,
|
||||
isEnterprise,
|
||||
isEnterpriseLicenseLoaded,
|
||||
isEnterpriseLicensed,
|
||||
isProductPro,
|
||||
isLogin,
|
||||
isOnRestart,
|
||||
} = useGlobalStore();
|
||||
NProgress.start();
|
||||
axiosCanceler.removeAllPending();
|
||||
|
||||
@@ -133,6 +140,23 @@ router.beforeEach(async (to, from) => {
|
||||
removePanelCache('tabs', activeMenuKey);
|
||||
}
|
||||
|
||||
if (to.name === 'ModelFiles') {
|
||||
if (!isEnterprise.value) {
|
||||
try {
|
||||
await loadProductProFromDB();
|
||||
} catch (error) {
|
||||
console.warn('Failed to refresh model marketplace license status', error);
|
||||
}
|
||||
}
|
||||
if (!isProductPro.value) {
|
||||
if (cachedRoute === to.path) {
|
||||
removePanelCache('tabs', activeMenuKey);
|
||||
}
|
||||
NProgress.done();
|
||||
return { path: '/ai/model/account', query: to.query, replace: true };
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasRouteAccess(to)) {
|
||||
MsgError(i18n.global.t('commons.res.forbidden'));
|
||||
NProgress.done();
|
||||
|
||||
@@ -6,30 +6,20 @@ const aiRouter = {
|
||||
path: '/ai',
|
||||
name: 'AI-Menu',
|
||||
component: Layout,
|
||||
redirect: '/ai/model/account',
|
||||
redirect: '/ai/model',
|
||||
meta: {
|
||||
icon: 'p-jiqiren2',
|
||||
title: 'menu.aiTools',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/ai/model/account',
|
||||
path: '/ai/model',
|
||||
name: 'AIModel',
|
||||
component: () => import('@/views/ai/model/index.vue'),
|
||||
redirect: '/ai/model/files',
|
||||
meta: {
|
||||
icon: 'p-moxing-menu',
|
||||
title: 'aiTools.model.model',
|
||||
permission: 'ai_model_view',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/ai/model/local',
|
||||
hidden: true,
|
||||
name: 'LocalModel',
|
||||
component: () => import('@/views/ai/model/index.vue'),
|
||||
meta: {
|
||||
title: 'aiTools.model.deployments',
|
||||
activeMenu: '/ai/model/account',
|
||||
activeMenu: '/ai/model',
|
||||
permission: 'ai_model_view',
|
||||
},
|
||||
},
|
||||
@@ -40,7 +30,29 @@ const aiRouter = {
|
||||
component: () => import('@/views/ai/model/index.vue'),
|
||||
meta: {
|
||||
title: 'aiTools.model.files',
|
||||
activeMenu: '/ai/model/account',
|
||||
activeMenu: '/ai/model',
|
||||
permission: 'ai_model_view',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/ai/model/account',
|
||||
hidden: true,
|
||||
name: 'ModelServices',
|
||||
component: () => import('@/views/ai/model/index.vue'),
|
||||
meta: {
|
||||
title: 'aiTools.model.services',
|
||||
activeMenu: '/ai/model',
|
||||
permission: 'ai_model_view',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/ai/model/local',
|
||||
hidden: true,
|
||||
name: 'LocalModel',
|
||||
component: () => import('@/views/ai/model/index.vue'),
|
||||
meta: {
|
||||
title: 'aiTools.model.deployments',
|
||||
activeMenu: '/ai/model',
|
||||
permission: 'ai_model_view',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<div class="model-discovery">
|
||||
<div class="model-discovery__header">
|
||||
<el-radio-group v-model="modelConfigMode" size="small" @change="handleModelConfigModeChange">
|
||||
<el-radio-button value="discover">
|
||||
<el-radio-button v-if="supportsModelDiscovery || defaultModels.length" value="discover">
|
||||
{{
|
||||
$t(
|
||||
supportsModelDiscovery
|
||||
@@ -284,6 +284,7 @@ const { isIntl } = useGlobalStore();
|
||||
|
||||
const form = reactive({
|
||||
id: 0,
|
||||
appInstallId: 0,
|
||||
provider: 'custom',
|
||||
name: '',
|
||||
baseURL: '',
|
||||
@@ -308,14 +309,16 @@ const selectedAPIConfig = computed(() =>
|
||||
);
|
||||
const isImageAPIType = computed(() => form.apiType.endsWith('-images'));
|
||||
const isEmbeddingAPIType = computed(() => form.apiType === 'openai-embeddings');
|
||||
const supportsModelDiscovery = computed(() => !form.id && Boolean(selectedAPIConfig.value?.supportsModelDiscovery));
|
||||
const supportsModelDiscovery = computed(() => Boolean(selectedAPIConfig.value?.supportsModelDiscovery));
|
||||
const defaultModels = computed(() => {
|
||||
if (selectedAPIConfig.value?.models.length) {
|
||||
return selectedAPIConfig.value.models;
|
||||
}
|
||||
return isImageAPIType.value ? [] : selectedProvider.value?.models || [];
|
||||
});
|
||||
const showModelDiscovery = computed(() => !form.id && (supportsModelDiscovery.value || defaultModels.value.length > 0));
|
||||
const showModelDiscovery = computed(
|
||||
() => !form.appInstallId && (Boolean(form.id) || supportsModelDiscovery.value || defaultModels.value.length > 0),
|
||||
);
|
||||
const showInitialModel = computed(
|
||||
() =>
|
||||
!form.id &&
|
||||
@@ -330,7 +333,8 @@ const showVerifyModelSelect = computed(
|
||||
() =>
|
||||
requiresVerifyModel.value &&
|
||||
verifyModelOptions.value.length > 0 &&
|
||||
(Boolean(form.id) || (!showModelDiscovery.value && !showInitialModel.value)),
|
||||
!showModelDiscovery.value &&
|
||||
!showInitialModel.value,
|
||||
);
|
||||
const apiTypeOptions = computed(() => selectedProvider.value?.apiTypes.map((item) => item.apiType) || []);
|
||||
const authModeOptions = computed(() => selectedAPIConfig.value?.authModes || []);
|
||||
@@ -580,20 +584,18 @@ const submit = async () => {
|
||||
return;
|
||||
}
|
||||
let accountModels = initialModel ? [initialModel] : [];
|
||||
if (!form.id) {
|
||||
if (showModelDiscovery.value && modelConfigMode.value === 'manual') {
|
||||
const models = normalizeManualModels();
|
||||
if (!models) {
|
||||
return;
|
||||
}
|
||||
accountModels = models;
|
||||
} else if (showModelDiscovery.value) {
|
||||
accountModels = discoveredModels.value;
|
||||
}
|
||||
if (accountModels.length === 0) {
|
||||
MsgError(i18n.global.t('aiTools.agents.accountModelsRequired'));
|
||||
if (showModelDiscovery.value && modelConfigMode.value === 'manual') {
|
||||
const models = normalizeManualModels();
|
||||
if (!models) {
|
||||
return;
|
||||
}
|
||||
accountModels = models;
|
||||
} else if (showModelDiscovery.value) {
|
||||
accountModels = discoveredModels.value;
|
||||
}
|
||||
if ((!form.id || showModelDiscovery.value) && accountModels.length === 0) {
|
||||
MsgError(i18n.global.t('aiTools.agents.accountModelsRequired'));
|
||||
return;
|
||||
}
|
||||
let verifyModel = '';
|
||||
if (requiresVerifyModel.value) {
|
||||
@@ -623,6 +625,7 @@ const submit = async () => {
|
||||
validateAvailability: form.validateAvailability,
|
||||
remark: form.remark,
|
||||
syncAgents: form.syncAgents,
|
||||
models: showModelDiscovery.value ? accountModels : undefined,
|
||||
});
|
||||
} else {
|
||||
await createAgentAccount({
|
||||
@@ -653,6 +656,7 @@ const handleClose = () => {
|
||||
formRef.value?.resetFields();
|
||||
loading.value = false;
|
||||
form.id = 0;
|
||||
form.appInstallId = 0;
|
||||
form.provider = 'custom';
|
||||
form.name = '';
|
||||
form.baseURL = '';
|
||||
@@ -671,6 +675,7 @@ const handleClose = () => {
|
||||
|
||||
interface OpenParams {
|
||||
id?: number;
|
||||
appInstallId?: number;
|
||||
provider?: string;
|
||||
name?: string;
|
||||
baseURL?: string;
|
||||
@@ -691,6 +696,7 @@ const openDrawer = async (params?: OpenParams) => {
|
||||
}
|
||||
if (params?.id) {
|
||||
form.id = params.id;
|
||||
form.appInstallId = params.appInstallId || 0;
|
||||
form.provider = params.provider || '';
|
||||
form.name = params.name || '';
|
||||
form.baseURL = params.baseURL || '';
|
||||
@@ -703,12 +709,19 @@ const openDrawer = async (params?: OpenParams) => {
|
||||
? String(params.apiType)
|
||||
: provider?.defaultApiType || '';
|
||||
form.authMode = params.authMode || selectedAPIConfig.value?.defaultAuthMode || '';
|
||||
resetModelDiscovery();
|
||||
modelConfigMode.value = 'manual';
|
||||
manualModels.value = editModels.value.length
|
||||
? editModels.value.map((item) => ({ ...item }))
|
||||
: [buildInitialModel()];
|
||||
form.verifyModel = params.verifyModel || editModels.value[0]?.id || '';
|
||||
manualVerifyIndex.value = manualModels.value.findIndex((item) => item.id === form.verifyModel);
|
||||
form.remark = params.remark || '';
|
||||
form.syncAgents = false;
|
||||
return;
|
||||
}
|
||||
form.id = 0;
|
||||
form.appInstallId = 0;
|
||||
form.name = '';
|
||||
form.baseURL = '';
|
||||
form.apiKey = '';
|
||||
@@ -773,7 +786,7 @@ const handleAPITypeChange = () => {
|
||||
};
|
||||
|
||||
watch([() => form.apiKey, () => form.baseURL], () => {
|
||||
if (showModelDiscovery.value) {
|
||||
if (!form.id && showModelDiscovery.value) {
|
||||
loadDefaultModels();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
<template v-else-if="visible && instance && !loading && view !== VllmPlaceholder">
|
||||
<div v-if="instance.engine !== 'ollama'" class="service-card">
|
||||
<div class="flex flex-wrap gap-3 items-center mb-4">
|
||||
<strong>{{ engineLabels[instance.engine] }} {{ instance.version }}</strong>
|
||||
<strong>
|
||||
{{ engineLabels[instance.engine] }}
|
||||
{{
|
||||
instance.engine === 'vllm' ? formatVllmVersion(instance.version) : instance.version
|
||||
}}
|
||||
</strong>
|
||||
<Status :status="instance.status" />
|
||||
</div>
|
||||
<div class="service-actions">
|
||||
@@ -110,7 +115,7 @@ import TensorForm from '../../tensorrt/operate/index.vue';
|
||||
import VllmPlaceholder from '../../vllm/index.vue';
|
||||
import ContainerLogs from '@/components/log/container-drawer/index.vue';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import { readPages, loadVllmAPI, vllmAvailable, type Deployment } from '../../deployment';
|
||||
import { formatVllmVersion, readPages, loadVllmAPI, vllmAvailable, type Deployment } from '../../deployment';
|
||||
|
||||
const emit = defineEmits(['search', 'upgrade']);
|
||||
const { isMobile, currentNode, isProductPro, isFxplay } = useGlobalStore();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<details open class="form-section">
|
||||
<details ref="sectionRef" open class="form-section">
|
||||
<summary class="form-section-header">
|
||||
<span>{{ $t('aiTools.model.gpuAcceleration') }}</span>
|
||||
<el-switch
|
||||
@@ -126,6 +126,7 @@ import i18n from '@/lang';
|
||||
const gpuTypeLabels: Record<string, string> = { nvidia: 'NVIDIA', intel: 'Intel', ascend: 'Ascend', amd: 'AMD' };
|
||||
const props = withDefaults(defineProps<{ imageType?: string }>(), { imageType: 'nvidia' });
|
||||
const model = defineModel<AI.ModelGPUConfig>({ required: true });
|
||||
const sectionRef = ref<HTMLDetailsElement>();
|
||||
const loading = ref(false);
|
||||
const gpuWarnings = ref<string[]>([]);
|
||||
const gpuDevices = ref<AI.ModelGPUDevice[]>();
|
||||
@@ -200,6 +201,9 @@ const changeMode = () => {
|
||||
|
||||
const changeEnabled = () => {
|
||||
model.value.error = undefined;
|
||||
if (model.value.enabled && sectionRef.value) {
|
||||
sectionRef.value.open = true;
|
||||
}
|
||||
if (!model.value.enabled) {
|
||||
model.value.mode = 'all';
|
||||
model.value.count = undefined;
|
||||
|
||||
@@ -102,7 +102,7 @@ const open = async (row: Deployment) => {
|
||||
MsgError(i18n.global.t('commons.res.notFound'));
|
||||
return;
|
||||
}
|
||||
const hostMode = item.appVersion.toLowerCase().startsWith('nvidia-gb10-dspark-');
|
||||
const hostMode = item.appVersion.trim().toLowerCase().startsWith('nvidia-gb10-dspark-');
|
||||
Object.assign(form, {
|
||||
id: item.id,
|
||||
containerName: item.containerName,
|
||||
|
||||
@@ -44,6 +44,12 @@ interface VllmAPI {
|
||||
const vllmModules = import.meta.glob<VllmAPI>('@/xpack/api/modules/vllm.ts');
|
||||
export const vllmAvailable = Object.keys(vllmModules).length > 0;
|
||||
|
||||
export function formatVllmVersion(version: string) {
|
||||
return String(version || '')
|
||||
.trim()
|
||||
.replace(/^(nvidia|intel|ascend)-/i, '');
|
||||
}
|
||||
|
||||
export async function loadVllmAPI() {
|
||||
return Object.values(vllmModules)[0]();
|
||||
}
|
||||
|
||||
@@ -131,13 +131,25 @@
|
||||
>
|
||||
<span class="cell-ellipsis" :title="model.id">{{ model.id }}</span>
|
||||
<el-button
|
||||
v-if="index === 1 && row.models.length > 2"
|
||||
v-if="!row.appInstallId && index === 1 && row.models.length > 2"
|
||||
size="small"
|
||||
class="model-more"
|
||||
@click="poolRef.open(row)"
|
||||
>
|
||||
+{{ row.models.length - 2 }}
|
||||
</el-button>
|
||||
<el-tooltip
|
||||
v-if="row.appInstallId && index === 1 && row.models.length > 2"
|
||||
:content="
|
||||
row.models
|
||||
.slice(2)
|
||||
.map((item) => item.id)
|
||||
.join(', ')
|
||||
"
|
||||
placement="top"
|
||||
>
|
||||
<el-tag size="small" class="model-more">+{{ row.models.length - 2 }}</el-tag>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<span v-if="!row.models?.length">—</span>
|
||||
</template>
|
||||
@@ -169,6 +181,7 @@
|
||||
<fu-table-operations
|
||||
:buttons="accountButtons"
|
||||
:label="$t('commons.table.operate')"
|
||||
align="left"
|
||||
fixed="right"
|
||||
:ellipsis="3"
|
||||
width="220"
|
||||
@@ -190,7 +203,12 @@
|
||||
<el-table-column :label="$t('app.version')" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>{{ row.version || '—' }}</span>
|
||||
<span>
|
||||
{{
|
||||
(row.engine === 'vllm' ? formatVllmVersion(row.version) : row.version) ||
|
||||
'—'
|
||||
}}
|
||||
</span>
|
||||
<el-button
|
||||
v-if="row.upgradable && hasPermission('app_manage')"
|
||||
v-permission
|
||||
@@ -344,7 +362,14 @@ import ModelPool from './components/model-pool/index.vue';
|
||||
import TerminalDialog from '@/views/container/container/terminal/index.vue';
|
||||
import SyncService from './components/sync/index.vue';
|
||||
import ProviderLabel from '@/components/agent-provider-label/index.vue';
|
||||
import { loadModelOverview, loadVllmAPI, readPages, vllmAvailable, type Deployment } from './deployment';
|
||||
import {
|
||||
formatVllmVersion,
|
||||
loadModelOverview,
|
||||
loadVllmAPI,
|
||||
readPages,
|
||||
vllmAvailable,
|
||||
type Deployment,
|
||||
} from './deployment';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -547,6 +572,7 @@ const accountButtons = [
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('aiTools.agents.modelPool'),
|
||||
disabled: (row: AI.AgentAccountItem) => row.provider === 'vllm',
|
||||
click: (row: AI.AgentAccountItem) => poolRef.value.open(row),
|
||||
},
|
||||
{
|
||||
@@ -592,7 +618,7 @@ const operate = async (row: Deployment, operation: 'start' | 'stop' | 'restart')
|
||||
const taskID = newUUID();
|
||||
await (await loadVllmAPI()).operateVllm({ id: row.id, operate: operation, taskID });
|
||||
if (node !== currentNode.value) return;
|
||||
if (row.version.toLowerCase().startsWith('nvidia-gb10-dspark-')) {
|
||||
if (row.version.trim().toLowerCase().startsWith('nvidia-gb10-dspark-')) {
|
||||
await search();
|
||||
taskRef.value.openWithTaskID(taskID);
|
||||
return;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<AppInstallForm ref="formRef" v-model="form" :loading="loading" sectioned hide-gpu-config>
|
||||
<template #after-params>
|
||||
<GPUConfig ref="gpuRef" v-model="form.gpu" />
|
||||
<details open class="form-section">
|
||||
<details ref="syncSectionRef" open class="form-section">
|
||||
<summary class="form-section-header">
|
||||
<span>{{ $t('aiTools.model.syncModelAccount') }}</span>
|
||||
<el-switch
|
||||
@@ -16,6 +16,7 @@
|
||||
@keydown.stop
|
||||
v-model="form.syncModelAccount"
|
||||
:aria-label="$t('aiTools.model.syncModelAccount')"
|
||||
@change="(enabled) => enabled && syncSectionRef && (syncSectionRef.open = true)"
|
||||
/>
|
||||
</summary>
|
||||
<div v-show="form.syncModelAccount" class="form-section-body">
|
||||
@@ -64,6 +65,7 @@ const open = ref(false);
|
||||
const loading = ref(false);
|
||||
const formRef = ref<InstanceType<typeof AppInstallForm>>();
|
||||
const accountRef = ref();
|
||||
const syncSectionRef = ref<HTMLDetailsElement>();
|
||||
const gpuRef = ref<InstanceType<typeof GPUConfig>>();
|
||||
const form = ref({
|
||||
gpu: {
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
</div>
|
||||
</component>
|
||||
<GPUConfig v-if="showModelGPU" ref="gpuRef" v-model="paramModel.gpu" />
|
||||
<details v-if="appKey === 'ollama'" open class="form-section">
|
||||
<details v-if="appKey === 'ollama'" ref="syncSectionRef" open class="form-section">
|
||||
<summary class="form-section-header">
|
||||
<span>{{ $t('aiTools.model.syncModelAccount') }}</span>
|
||||
<el-switch
|
||||
@@ -129,6 +129,7 @@
|
||||
@keydown.stop
|
||||
v-model="paramModel.syncModelAccount"
|
||||
:aria-label="$t('aiTools.model.syncModelAccount')"
|
||||
@change="(enabled) => enabled && syncSectionRef && (syncSectionRef.open = true)"
|
||||
/>
|
||||
</summary>
|
||||
<div v-show="paramModel.syncModelAccount" class="form-section-body">
|
||||
@@ -298,6 +299,7 @@ const appType = ref('');
|
||||
const appKey = ref('');
|
||||
const showModelGPU = computed(() => props.modelGPU && appKey.value === 'ollama');
|
||||
const accountRef = ref<InstanceType<typeof AccountAddress>>();
|
||||
const syncSectionRef = ref<HTMLDetailsElement>();
|
||||
const gpuRef = ref<InstanceType<typeof GPUConfig>>();
|
||||
const appConfigUpdate = ref<App.AppConfigUpdate>({
|
||||
installID: 0,
|
||||
|
||||
Reference in New Issue
Block a user