From 50a54d061389471aae70d466ec85bc02f3303dce Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:26:35 +0800 Subject: [PATCH] feat: Add support for importing environment variables into the runtime environment. (#13727) --- agent/app/service/runtime_utils.go | 52 ++++++++++---- frontend/src/lang/modules/en.ts | 5 ++ frontend/src/lang/modules/es-es.ts | 5 ++ frontend/src/lang/modules/fa.ts | 5 ++ frontend/src/lang/modules/ja.ts | 5 ++ frontend/src/lang/modules/ko.ts | 5 ++ frontend/src/lang/modules/lo.ts | 5 ++ frontend/src/lang/modules/ms.ts | 5 ++ frontend/src/lang/modules/pt-br.ts | 5 ++ frontend/src/lang/modules/ru.ts | 5 ++ frontend/src/lang/modules/tr.ts | 5 ++ frontend/src/lang/modules/zh-Hant.ts | 5 ++ frontend/src/lang/modules/zh.ts | 5 ++ frontend/src/views/ai/model/local/index.vue | 7 +- .../runtime/components/environment/index.vue | 70 +++++++++++++++++-- 15 files changed, 168 insertions(+), 21 deletions(-) diff --git a/agent/app/service/runtime_utils.go b/agent/app/service/runtime_utils.go index 16e3f2665..c2dd0b0e5 100644 --- a/agent/app/service/runtime_utils.go +++ b/agent/app/service/runtime_utils.go @@ -838,21 +838,49 @@ func restartRuntime(runtime *model.Runtime) (err error) { } func getDockerComposeEnvironments(yml []byte) ([]request.Environment, error) { - var ( - composeProject docker.ComposeProject - err error - ) - err = yaml.Unmarshal(yml, &composeProject) - if err != nil { + var project struct { + Services yaml.Node `yaml:"services"` + } + if err := yaml.Unmarshal(yml, &project); err != nil { return nil, err } + services := &project.Services + if services.Kind == yaml.AliasNode { + services = services.Alias + } + if services.Kind != 0 && services.Kind != yaml.MappingNode { + return nil, fmt.Errorf("unsupported services format") + } var res []request.Environment - for _, service := range composeProject.Services { - for key, value := range service.Environment.Variables { - res = append(res, request.Environment{ - Key: key, - Value: value, - }) + // Keep the file order for both services and environment entries. + for i := 1; i < len(services.Content); i += 2 { + var service struct { + Environment yaml.Node `yaml:"environment"` + } + if err := services.Content[i].Decode(&service); err != nil { + return nil, err + } + environment := &service.Environment + if environment.Kind == yaml.AliasNode { + environment = environment.Alias + } + switch environment.Kind { + case yaml.MappingNode: + for j := 0; j < len(environment.Content); j += 2 { + res = append(res, request.Environment{Key: environment.Content[j].Value, Value: environment.Content[j+1].Value}) + } + case yaml.SequenceNode: + for _, item := range environment.Content { + var entry string + if err := item.Decode(&entry); err != nil { + return nil, err + } + key, value, _ := strings.Cut(entry, "=") + res = append(res, request.Environment{Key: key, Value: value}) + } + case 0: + default: + return nil, fmt.Errorf("unsupported environment format") } } return res, nil diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 7485dfb90..560cfd6ba 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -4318,6 +4318,11 @@ const message = { 'The following fields will change: {0}. The rule will be applied and verified immediately. Continue?', }, runtime: { + importEnv: 'Import Environment Variables', + envImportError: 'Line {0}: {1}', + envInvalidAssignment: 'Expected a valid NAME=value assignment', + envUnclosedQuote: 'Unclosed quote', + envUnexpectedText: 'Unexpected text after the closing quote', runtime: 'Runtime', default: 'Default', workDir: 'Working directory', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 5a6ec8ec1..7eb91499e 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -4371,6 +4371,11 @@ const message = { 'Se modificarán los siguientes campos: {0}. La regla se aplicará y verificará inmediatamente. ¿Continuar?', }, runtime: { + importEnv: 'Importar variables de entorno', + envImportError: 'Línea {0}: {1}', + envInvalidAssignment: 'Se esperaba una asignación NOMBRE=valor válida', + envUnclosedQuote: 'Comillas sin cerrar', + envUnexpectedText: 'Texto inesperado después de las comillas de cierre', runtime: 'Runtime', default: 'Predeterminado', workDir: 'Directorio de trabajo', diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts index 324bada6e..3236f6490 100644 --- a/frontend/src/lang/modules/fa.ts +++ b/frontend/src/lang/modules/fa.ts @@ -4275,6 +4275,11 @@ const message = { editRuleConfirm: 'فیلدهای زیر تغییر می‌کنند: {0}. قانون بلافاصله اعمال و بررسی می‌شود. ادامه می‌دهید؟', }, runtime: { + importEnv: 'وارد کردن متغیرهای محیطی', + envImportError: 'خط {0}: {1}', + envInvalidAssignment: 'یک عبارت معتبر NAME=value لازم است', + envUnclosedQuote: 'علامت نقل قول بسته نشده است', + envUnexpectedText: 'متن اضافی پس از علامت نقل قول پایانی', runtime: 'محیط اجرا', default: 'پیش‌فرض', workDir: 'دایرکتوری کاری', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 8e58767aa..c607cb230 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -4303,6 +4303,11 @@ const message = { editRuleConfirm: '次の項目を変更します:{0}。ルールは直ちに適用され、再検証されます。続行しますか?', }, runtime: { + importEnv: '環境変数をインポート', + envImportError: '{0} 行目:{1}', + envInvalidAssignment: '有効な NAME=value 形式で入力してください', + envUnclosedQuote: '引用符が閉じられていません', + envUnexpectedText: '閉じ引用符の後に余分な文字があります', runtime: 'ランタイム', workDir: '作業ディレクトリ', localHelper: diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index a6fa75c0d..f05a8874e 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -4223,6 +4223,11 @@ const message = { editRuleConfirm: '다음 필드가 변경됩니다: {0}. 규칙이 즉시 적용되고 다시 검증됩니다. 계속하시겠습니까?', }, runtime: { + importEnv: '환경 변수 가져오기', + envImportError: '{0}행: {1}', + envInvalidAssignment: '유효한 NAME=value 형식이 필요합니다', + envUnclosedQuote: '따옴표가 닫히지 않았습니다', + envUnexpectedText: '닫는 따옴표 뒤에 불필요한 텍스트가 있습니다', runtime: '실행 환경', workDir: '작업 디렉토리', localHelper: '로컬 환경 설치 및 오프라인 환경 사용 관련 문제는 다음을 참조하세요 ', diff --git a/frontend/src/lang/modules/lo.ts b/frontend/src/lang/modules/lo.ts index 4758dda61..5400663ad 100644 --- a/frontend/src/lang/modules/lo.ts +++ b/frontend/src/lang/modules/lo.ts @@ -4186,6 +4186,11 @@ const message = { editRuleConfirm: 'ຈະປ່ຽນຟິວຕໍ່ໄປນີ້: {0}. ກົດຈະຖືກນຳໃຊ້ແລະກວດສອບທັນທີ. ສືບຕໍ່ບໍ?', }, runtime: { + importEnv: 'ນຳເຂົ້າຕົວແປສະພາບແວດລ້ອມ', + envImportError: 'ແຖວ {0}: {1}', + envInvalidAssignment: 'ຕ້ອງເປັນຮູບແບບ NAME=value ທີ່ຖືກຕ້ອງ', + envUnclosedQuote: 'ເຄື່ອງໝາຍວົງຢືມຍັງບໍ່ປິດ', + envUnexpectedText: 'ມີຂໍ້ຄວາມເກີນຫຼັງເຄື່ອງໝາຍວົງຢືມປິດ', runtime: 'ຣັນທາມ (Runtime)', default: 'ຄ່າເລີ່ມຕົ້ນ', workDir: 'ໂຟນເດີທີ່ເຮັດວຽກ', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index e50a8a71a..935a8fb5e 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -4383,6 +4383,11 @@ const message = { editRuleConfirm: 'Medan berikut akan diubah: {0}. Peraturan akan digunakan dan disahkan serta-merta. Teruskan?', }, runtime: { + importEnv: 'Import Pemboleh Ubah Persekitaran', + envImportError: 'Baris {0}: {1}', + envInvalidAssignment: 'Penetapan NAME=value yang sah diperlukan', + envUnclosedQuote: 'Tanda petikan tidak ditutup', + envUnexpectedText: 'Teks tidak dijangka selepas tanda petikan penutup', runtime: 'Runtime', workDir: 'Direktori kerja', localHelper: diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 88beacd19..d0c5e847e 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -4409,6 +4409,11 @@ const message = { 'Os seguintes campos serão alterados: {0}. A regra será aplicada e verificada imediatamente. Continuar?', }, runtime: { + importEnv: 'Importar variáveis de ambiente', + envImportError: 'Linha {0}: {1}', + envInvalidAssignment: 'Era esperada uma atribuição NOME=valor válida', + envUnclosedQuote: 'Aspas não fechadas', + envUnexpectedText: 'Texto inesperado após as aspas de fechamento', runtime: 'Runtime', workDir: 'Diretório de trabalho', localHelper: 'Para problemas de instalação em ambiente local e uso em ambiente offline, consulte ', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 8e97841b9..32c1bda2a 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -4373,6 +4373,11 @@ const message = { editRuleConfirm: 'Будут изменены поля: {0}. Правило будет немедленно применено и проверено. Продолжить?', }, runtime: { + importEnv: 'Импорт переменных окружения', + envImportError: 'Строка {0}: {1}', + envInvalidAssignment: 'Ожидается корректное присваивание NAME=value', + envUnclosedQuote: 'Незакрытая кавычка', + envUnexpectedText: 'Лишний текст после закрывающей кавычки', runtime: 'Среда выполнения', workDir: 'Рабочая директория', localHelper: 'По вопросам установки в локальной среде и использования в автономном режиме обратитесь к ', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 9e0ab67cb..74ee7d6d1 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -4387,6 +4387,11 @@ const message = { editRuleConfirm: 'Şu alanlar değiştirilecek: {0}. Kural hemen uygulanıp doğrulanacak. Devam edilsin mi?', }, runtime: { + importEnv: 'Ortam Değişkenlerini İçe Aktar', + envImportError: 'Satır {0}: {1}', + envInvalidAssignment: 'Geçerli bir NAME=value ataması bekleniyor', + envUnclosedQuote: 'Kapatılmamış tırnak', + envUnexpectedText: 'Kapanış tırnağından sonra beklenmeyen metin', runtime: 'Çalışma Zamanı', default: 'Varsayılan', workDir: 'Çalışma dizini', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index bd6c3d2c4..15478b1ab 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -4025,6 +4025,11 @@ const message = { editRuleConfirm: '將修改以下欄位:{0}。提交後會立即套用並回讀驗證,是否繼續?', }, runtime: { + importEnv: '匯入環境變數', + envImportError: '第 {0} 行:{1}', + envInvalidAssignment: '應為有效的變數名稱=value', + envUnclosedQuote: '引號未閉合', + envUnexpectedText: '閉合引號後有多餘內容', runtime: '執行環境', default: '預設', workDir: '工作目錄', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index ebc363809..56d7928a7 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -4074,6 +4074,11 @@ const message = { editRuleConfirm: '将修改以下字段:{0}。提交后会立即应用并回读验证,是否继续?', }, runtime: { + importEnv: '导入环境变量', + envImportError: '第 {0} 行:{1}', + envInvalidAssignment: '应为有效的变量名=value', + envUnclosedQuote: '引号未闭合', + envUnexpectedText: '闭合引号后存在多余内容', runtime: '运行环境', default: '默认', workDir: '工作目录', diff --git a/frontend/src/views/ai/model/local/index.vue b/frontend/src/views/ai/model/local/index.vue index 7c4b5e517..ac35ccfaf 100644 --- a/frontend/src/views/ai/model/local/index.vue +++ b/frontend/src/views/ai/model/local/index.vue @@ -40,7 +40,7 @@ type LocalTab = 'ollama' | 'vllm' | 'tensorrt' | 'downloader'; const route = useRoute(); const router = useRouter(); -const { isFxplay } = useGlobalStore(); +const { isFxplay, isProductPro } = useGlobalStore(); const tabLabels: Record = { ollama: 'Ollama', @@ -66,7 +66,10 @@ const currentTab = computed(() => { if (tab === 'vllm' || tab === 'downloader' || (tab === 'tensorrt' && isFxplay.value)) { return tab; } - return 'ollama'; + if (tab === 'tensorrt') { + return 'ollama'; + } + return isProductPro.value ? 'vllm' : 'ollama'; }); const currentComponent = computed(() => { diff --git a/frontend/src/views/website/runtime/components/environment/index.vue b/frontend/src/views/website/runtime/components/environment/index.vue index 9ce07ede5..61a09e574 100644 --- a/frontend/src/views/website/runtime/components/environment/index.vue +++ b/frontend/src/views/website/runtime/components/environment/index.vue @@ -11,7 +11,7 @@
=
- + @@ -23,19 +23,59 @@ - - - {{ $t('commons.button.add') }} - - +
+ {{ $t('commons.button.add') }} + {{ $t('runtime.importEnv') }} +
+ + + + + + + + +