From 61f08f321828476ed0bfba4f3f93cfe1ad4d2c81 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Thu, 1 Jan 2026 15:40:39 +0800 Subject: [PATCH] feat: add disable_models_service configuration to manage model service availability and update related components --- .../pkg/api/http/controller/groups/system.py | 3 + src/langbot/pkg/provider/modelmgr/modelmgr.py | 6 + src/langbot/templates/config.yaml | 1 + .../dynamic-form/DynamicFormItemComponent.tsx | 11 +- .../components/models-dialog/ModelsDialog.tsx | 37 +-- .../component/provider-form/ProviderForm.tsx | 216 ++++++++++++------ .../models-dialog/components/ProviderCard.tsx | 14 +- .../knowledge/components/kb-form/KBForm.tsx | 11 +- web/src/app/infra/entities/api/index.ts | 1 + web/src/app/infra/http/BackendClient.ts | 2 +- web/src/app/infra/http/index.ts | 1 + web/src/i18n/locales/en-US.ts | 1 + web/src/i18n/locales/ja-JP.ts | 1 + web/src/i18n/locales/zh-Hans.ts | 1 + web/src/i18n/locales/zh-Hant.ts | 1 + 15 files changed, 194 insertions(+), 113 deletions(-) diff --git a/src/langbot/pkg/api/http/controller/groups/system.py b/src/langbot/pkg/api/http/controller/groups/system.py index e5d3bef0..e36707a5 100644 --- a/src/langbot/pkg/api/http/controller/groups/system.py +++ b/src/langbot/pkg/api/http/controller/groups/system.py @@ -22,6 +22,9 @@ class SystemRouterGroup(group.RouterGroup): 'allow_modify_login_info': self.ap.instance_config.data.get('system', {}).get( 'allow_modify_login_info', True ), + 'disable_models_service': self.ap.instance_config.data.get('space', {}).get( + 'disable_models_service', False + ), } ) diff --git a/src/langbot/pkg/provider/modelmgr/modelmgr.py b/src/langbot/pkg/provider/modelmgr/modelmgr.py index 1b0d4a38..b24bff77 100644 --- a/src/langbot/pkg/provider/modelmgr/modelmgr.py +++ b/src/langbot/pkg/provider/modelmgr/modelmgr.py @@ -45,6 +45,12 @@ class ModelManager: await self.load_models_from_db() + # Check if space models service is disabled + space_config = self.ap.instance_config.data.get('space', {}) + if space_config.get('disable_models_service', False): + self.ap.logger.info('LangBot Space Models service is disabled, skipping sync.') + return + try: await self.sync_new_models_from_space() except Exception as e: diff --git a/src/langbot/templates/config.yaml b/src/langbot/templates/config.yaml index 5925a95d..afc70354 100644 --- a/src/langbot/templates/config.yaml +++ b/src/langbot/templates/config.yaml @@ -78,3 +78,4 @@ space: models_gateway_api_url: 'https://api.langbot.cloud' # OAuth authorization page URL (user will be redirected here) oauth_authorize_url: 'https://space.langbot.app/auth/authorize' + disable_models_service: false diff --git a/web/src/app/home/components/dynamic-form/DynamicFormItemComponent.tsx b/web/src/app/home/components/dynamic-form/DynamicFormItemComponent.tsx index 41586b43..16642e84 100644 --- a/web/src/app/home/components/dynamic-form/DynamicFormItemComponent.tsx +++ b/web/src/app/home/components/dynamic-form/DynamicFormItemComponent.tsx @@ -17,7 +17,7 @@ import { Switch } from '@/components/ui/switch'; import { ControllerRenderProps } from 'react-hook-form'; import { Button } from '@/components/ui/button'; import { useEffect, useState } from 'react'; -import { httpClient } from '@/app/infra/http/HttpClient'; +import { httpClient, systemInfo } from '@/app/infra/http/HttpClient'; import { LLMModel, Bot, @@ -98,7 +98,14 @@ export default function DynamicFormItemComponent({ httpClient .getProviderLLMModels() .then((resp) => { - setLlmModels(resp.models); + let models = resp.models; + // Filter out space-chat-completions models when models service is disabled + if (systemInfo.disable_models_service) { + models = models.filter( + (m) => m.provider?.requester !== 'space-chat-completions', + ); + } + setLlmModels(models); }) .catch((err) => { toast.error('Failed to get LLM model list: ' + err.message); diff --git a/web/src/app/home/components/models-dialog/ModelsDialog.tsx b/web/src/app/home/components/models-dialog/ModelsDialog.tsx index 2d67dafc..201e2570 100644 --- a/web/src/app/home/components/models-dialog/ModelsDialog.tsx +++ b/web/src/app/home/components/models-dialog/ModelsDialog.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { Plus, Boxes } from 'lucide-react'; -import { httpClient } from '@/app/infra/http/HttpClient'; +import { httpClient, systemInfo } from '@/app/infra/http/HttpClient'; import { ModelProvider } from '@/app/infra/entities/api'; import { Dialog, @@ -13,7 +13,6 @@ import { import { Button } from '@/components/ui/button'; import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; -import { extractI18nObject } from '@/i18n/I18nProvider'; import ProviderForm from './component/provider-form/ProviderForm'; import { ProviderCard } from './components'; import { @@ -86,17 +85,13 @@ export default function ModelsDialog({ const [isTesting, setIsTesting] = useState(false); const [testResult, setTestResult] = useState(null); - const [requesterNameList, setRequesterNameList] = useState< - { label: string; value: string }[] - >([]); - // Track if providers have been loaded initially const [providersLoaded, setProvidersLoaded] = useState(false); - // Separate LangBot Models provider - const langbotProvider = providers.find( - (p) => p.requester === LANGBOT_MODELS_PROVIDER_REQUESTER, - ); + // Separate LangBot Models provider (hide when models service is disabled) + const langbotProvider = systemInfo.disable_models_service + ? undefined + : providers.find((p) => p.requester === LANGBOT_MODELS_PROVIDER_REQUESTER); const otherProviders = providers.filter( (p) => p.requester !== LANGBOT_MODELS_PROVIDER_REQUESTER, ); @@ -104,7 +99,6 @@ export default function ModelsDialog({ useEffect(() => { if (open) { loadUserInfo(); - loadRequesterLists(); loadProviders(); } }, [open]); @@ -134,20 +128,6 @@ export default function ModelsDialog({ } } - async function loadRequesterLists() { - try { - const llmRequesters = await httpClient.getProviderRequesters('llm'); - setRequesterNameList( - llmRequesters.requesters.map((item) => ({ - label: extractI18nObject(item.label), - value: item.name, - })), - ); - } catch (err) { - console.error('Failed to load requester lists', err); - } - } - async function loadProviders() { try { const resp = await httpClient.getModelProviders(); @@ -397,7 +377,6 @@ export default function ModelsDialog({ models={providerModels[provider.uuid]} accountType={accountType} spaceCredits={spaceCredits} - requesterNameList={requesterNameList} addModelPopoverOpen={addModelPopoverOpen} editModelPopoverOpen={editModelPopoverOpen} deleteConfirmOpen={deleteConfirmOpen} @@ -462,7 +441,11 @@ export default function ModelsDialog({
{otherProviders.length === 0 - ? t('models.addProviderHint') + ? t( + systemInfo.disable_models_service + ? 'models.addProviderHintSimple' + : 'models.addProviderHint', + ) : t('models.providerCount', { count: otherProviders.length })}