mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
feat: introduce auto routing (#122)
This commit is contained in:
BIN
frontend/favicon.ico
Normal file → Executable file
BIN
frontend/favicon.ico
Normal file → Executable file
Binary file not shown.
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -14,14 +14,14 @@ const DeleteDialog = ({ isOpen, onClose, onConfirm, serverName, isGroup = false
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-30 z-50 flex items-center justify-center p-4">
|
||||
<div className="fixed inset-0 bg-black/50 bg-opacity-30 z-50 flex items-center justify-center p-4">
|
||||
<div className="bg-white rounded-lg shadow-lg max-w-md w-full">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-3">
|
||||
{isGroup ? t('groups.confirmDelete') : t('server.confirmDelete')}
|
||||
</h3>
|
||||
<p className="text-gray-500 mb-6">
|
||||
{isGroup
|
||||
{isGroup
|
||||
? t('groups.deleteWarning', { name: serverName })
|
||||
: t('server.deleteWarning', { name: serverName })}
|
||||
</p>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
import React, { createContext, useContext, useState, ReactNode, useCallback } from 'react';
|
||||
import Toast, { ToastType } from '@/components/ui/Toast';
|
||||
|
||||
interface ToastContextProps {
|
||||
@@ -32,18 +32,18 @@ export const ToastProvider: React.FC<ToastProviderProps> = ({ children }) => {
|
||||
duration: 3000,
|
||||
});
|
||||
|
||||
const showToast = (message: string, type: ToastType = 'info', duration: number = 3000) => {
|
||||
const showToast = useCallback((message: string, type: ToastType = 'info', duration: number = 3000) => {
|
||||
setToast({
|
||||
message,
|
||||
type,
|
||||
visible: true,
|
||||
duration,
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
const hideToast = () => {
|
||||
const hideToast = useCallback(() => {
|
||||
setToast((prev) => ({ ...prev, visible: false }));
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={{ showToast }}>
|
||||
|
||||
@@ -16,10 +16,19 @@ interface InstallConfig {
|
||||
npmRegistry: string;
|
||||
}
|
||||
|
||||
interface SmartRoutingConfig {
|
||||
enabled: boolean;
|
||||
dbUrl: string;
|
||||
openaiApiBaseUrl: string;
|
||||
openaiApiKey: string;
|
||||
openaiApiEmbeddingModel: string;
|
||||
}
|
||||
|
||||
interface SystemSettings {
|
||||
systemConfig?: {
|
||||
routing?: RoutingConfig;
|
||||
install?: InstallConfig;
|
||||
smartRouting?: SmartRoutingConfig;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,6 +56,14 @@ export const useSettingsData = () => {
|
||||
npmRegistry: '',
|
||||
});
|
||||
|
||||
const [smartRoutingConfig, setSmartRoutingConfig] = useState<SmartRoutingConfig>({
|
||||
enabled: false,
|
||||
dbUrl: '',
|
||||
openaiApiBaseUrl: '',
|
||||
openaiApiKey: '',
|
||||
openaiApiEmbeddingModel: '',
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [refreshKey, setRefreshKey] = useState(0);
|
||||
@@ -89,14 +106,25 @@ export const useSettingsData = () => {
|
||||
npmRegistry: data.data.systemConfig.install.npmRegistry || '',
|
||||
});
|
||||
}
|
||||
if (data.success && data.data?.systemConfig?.smartRouting) {
|
||||
setSmartRoutingConfig({
|
||||
enabled: data.data.systemConfig.smartRouting.enabled ?? false,
|
||||
dbUrl: data.data.systemConfig.smartRouting.dbUrl || '',
|
||||
openaiApiBaseUrl: data.data.systemConfig.smartRouting.openaiApiBaseUrl || '',
|
||||
openaiApiKey: data.data.systemConfig.smartRouting.openaiApiKey || '',
|
||||
openaiApiEmbeddingModel:
|
||||
data.data.systemConfig.smartRouting.openaiApiEmbeddingModel || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch settings:', error);
|
||||
setError(error instanceof Error ? error.message : 'Failed to fetch settings');
|
||||
// 使用一个稳定的 showToast 引用,避免将其加入依赖数组
|
||||
showToast(t('errors.failedToFetchSettings'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [t, showToast]);
|
||||
}, [t]); // 移除 showToast 依赖
|
||||
|
||||
// Update routing configuration
|
||||
const updateRoutingConfig = async <T extends keyof RoutingConfig>(
|
||||
@@ -195,6 +223,107 @@ export const useSettingsData = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Update smart routing configuration
|
||||
const updateSmartRoutingConfig = async <T extends keyof SmartRoutingConfig>(
|
||||
key: T,
|
||||
value: SmartRoutingConfig[T],
|
||||
) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('mcphub_token');
|
||||
const response = await fetch('/api/system-config', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-auth-token': token || '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
smartRouting: {
|
||||
[key]: value,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message || `HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
setSmartRoutingConfig({
|
||||
...smartRoutingConfig,
|
||||
[key]: value,
|
||||
});
|
||||
showToast(t('settings.systemConfigUpdated'));
|
||||
return true;
|
||||
} else {
|
||||
showToast(data.message || t('errors.failedToUpdateSmartRoutingConfig'));
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update smart routing config:', error);
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to update smart routing config';
|
||||
setError(errorMessage);
|
||||
showToast(errorMessage);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Update multiple smart routing configuration fields at once
|
||||
const updateSmartRoutingConfigBatch = async (updates: Partial<SmartRoutingConfig>) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('mcphub_token');
|
||||
const response = await fetch('/api/system-config', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-auth-token': token || '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
smartRouting: updates,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message || `HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
setSmartRoutingConfig({
|
||||
...smartRoutingConfig,
|
||||
...updates,
|
||||
});
|
||||
showToast(t('settings.systemConfigUpdated'));
|
||||
return true;
|
||||
} else {
|
||||
showToast(data.message || t('errors.failedToUpdateSmartRoutingConfig'));
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update smart routing config:', error);
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Failed to update smart routing config';
|
||||
setError(errorMessage);
|
||||
showToast(errorMessage);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch settings when the component mounts or refreshKey changes
|
||||
useEffect(() => {
|
||||
fetchSettings();
|
||||
@@ -213,6 +342,7 @@ export const useSettingsData = () => {
|
||||
tempRoutingConfig,
|
||||
setTempRoutingConfig,
|
||||
installConfig,
|
||||
smartRoutingConfig,
|
||||
loading,
|
||||
error,
|
||||
setError,
|
||||
@@ -220,5 +350,7 @@ export const useSettingsData = () => {
|
||||
fetchSettings,
|
||||
updateRoutingConfig,
|
||||
updateInstallConfig,
|
||||
updateSmartRoutingConfig,
|
||||
updateSmartRoutingConfigBatch,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -125,7 +125,8 @@
|
||||
"initialStartup": "The server might be starting up. Please wait a moment as this process can take some time on first launch...",
|
||||
"serverInstall": "Failed to install server",
|
||||
"failedToFetchSettings": "Failed to fetch settings",
|
||||
"failedToUpdateRouteConfig": "Failed to update route configuration"
|
||||
"failedToUpdateRouteConfig": "Failed to update route configuration",
|
||||
"failedToUpdateSmartRoutingConfig": "Failed to update smart routing configuration"
|
||||
},
|
||||
"common": {
|
||||
"processing": "Processing...",
|
||||
@@ -170,8 +171,9 @@
|
||||
"account": "Account Settings",
|
||||
"password": "Change Password",
|
||||
"appearance": "Appearance",
|
||||
"routeConfig": "Security Configuration",
|
||||
"installConfig": "Installation Configuration"
|
||||
"routeConfig": "Security",
|
||||
"installConfig": "Installation",
|
||||
"smartRouting": "Smart Routing"
|
||||
},
|
||||
"market": {
|
||||
"title": "Server Market - (Data from mcpm.sh)"
|
||||
@@ -277,7 +279,20 @@
|
||||
"npmRegistry": "NPM Registry URL",
|
||||
"npmRegistryDescription": "Set npm_config_registry environment variable for NPM package installation",
|
||||
"npmRegistryPlaceholder": "e.g. https://registry.npmjs.org/",
|
||||
"installConfig": "Installation Configuration",
|
||||
"systemConfigUpdated": "System configuration updated successfully"
|
||||
"installConfig": "Installation",
|
||||
"systemConfigUpdated": "System configuration updated successfully",
|
||||
"enableSmartRouting": "Enable Smart Routing",
|
||||
"enableSmartRoutingDescription": "Enable smart routing feature to search the most suitable tool based on input (using $smart group name)",
|
||||
"dbUrl": "PostgreSQL URL (with pgvector support)",
|
||||
"dbUrlPlaceholder": "e.g. postgresql://user:password@localhost:5432/dbname",
|
||||
"openaiApiBaseUrl": "OpenAI API Base URL",
|
||||
"openaiApiBaseUrlPlaceholder": "https://api.openai.com/v1",
|
||||
"openaiApiKey": "OpenAI API Key",
|
||||
"openaiApiKeyPlaceholder": "Enter OpenAI API key",
|
||||
"openaiApiEmbeddingModel": "OpenAI Embedding Model",
|
||||
"openaiApiEmbeddingModelPlaceholder": "text-embedding-3-small",
|
||||
"smartRoutingConfigUpdated": "Smart routing configuration updated successfully",
|
||||
"smartRoutingRequiredFields": "Database URL and OpenAI API Key are required to enable smart routing",
|
||||
"smartRoutingValidationError": "Please fill in the required fields before enabling Smart Routing: {{fields}}"
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,8 @@
|
||||
"serverInstall": "安装服务器失败",
|
||||
"failedToFetchSettings": "获取设置失败",
|
||||
"failedToUpdateSystemConfig": "更新系统配置失败",
|
||||
"failedToUpdateRouteConfig": "更新路由配置失败"
|
||||
"failedToUpdateRouteConfig": "更新路由配置失败",
|
||||
"failedToUpdateSmartRoutingConfig": "更新智能路由配置失败"
|
||||
},
|
||||
"common": {
|
||||
"processing": "处理中...",
|
||||
@@ -169,7 +170,8 @@
|
||||
"password": "修改密码",
|
||||
"appearance": "外观",
|
||||
"routeConfig": "安全配置",
|
||||
"installConfig": "安装配置"
|
||||
"installConfig": "安装",
|
||||
"smartRouting": "智能路由"
|
||||
},
|
||||
"groups": {
|
||||
"title": "分组管理"
|
||||
@@ -279,6 +281,20 @@
|
||||
"npmRegistryDescription": "设置 npm_config_registry 环境变量,用于 NPM 包安装",
|
||||
"npmRegistryPlaceholder": "例如: https://registry.npmmirror.com/",
|
||||
"installConfig": "安装配置",
|
||||
"systemConfigUpdated": "系统配置更新成功"
|
||||
"systemConfigUpdated": "系统配置更新成功",
|
||||
"enableSmartRouting": "启用智能路由",
|
||||
"enableSmartRoutingDescription": "开启智能路由功能,根据输入自动搜索最合适的工具",
|
||||
"dbUrl": "PostgreSQL 连接地址(支持 pgvector)",
|
||||
"dbUrlPlaceholder": "例如: postgresql://user:password@localhost:5432/dbname",
|
||||
"openaiApiBaseUrl": "OpenAI API 基础地址",
|
||||
"openaiApiBaseUrlPlaceholder": "https://api.openai.com/v1",
|
||||
"openaiApiKey": "OpenAI API 密钥",
|
||||
"openaiApiKeyDescription": "用于访问 OpenAI API 的密钥",
|
||||
"openaiApiKeyPlaceholder": "请输入 OpenAI API 密钥",
|
||||
"openaiApiEmbeddingModel": "OpenAI 嵌入模型",
|
||||
"openaiApiEmbeddingModelPlaceholder": "text-embedding-3-small",
|
||||
"smartRoutingConfigUpdated": "智能路由配置更新成功",
|
||||
"smartRoutingRequiredFields": "启用智能路由需要填写数据库连接地址和 OpenAI API 密钥",
|
||||
"smartRoutingValidationError": "启用智能路由前请先填写必要字段:{{fields}}"
|
||||
}
|
||||
}
|
||||
@@ -26,14 +26,29 @@ const SettingsPage: React.FC = () => {
|
||||
npmRegistry: '',
|
||||
});
|
||||
|
||||
const [tempSmartRoutingConfig, setTempSmartRoutingConfig] = useState<{
|
||||
dbUrl: string;
|
||||
openaiApiBaseUrl: string;
|
||||
openaiApiKey: string;
|
||||
openaiApiEmbeddingModel: string;
|
||||
}>({
|
||||
dbUrl: '',
|
||||
openaiApiBaseUrl: '',
|
||||
openaiApiKey: '',
|
||||
openaiApiEmbeddingModel: '',
|
||||
});
|
||||
|
||||
const {
|
||||
routingConfig,
|
||||
tempRoutingConfig,
|
||||
setTempRoutingConfig,
|
||||
installConfig: savedInstallConfig,
|
||||
smartRoutingConfig,
|
||||
loading,
|
||||
updateRoutingConfig,
|
||||
updateInstallConfig
|
||||
updateInstallConfig,
|
||||
updateSmartRoutingConfig,
|
||||
updateSmartRoutingConfigBatch
|
||||
} = useSettingsData();
|
||||
|
||||
// Update local installConfig when savedInstallConfig changes
|
||||
@@ -43,13 +58,26 @@ const SettingsPage: React.FC = () => {
|
||||
}
|
||||
}, [savedInstallConfig]);
|
||||
|
||||
// Update local tempSmartRoutingConfig when smartRoutingConfig changes
|
||||
useEffect(() => {
|
||||
if (smartRoutingConfig) {
|
||||
setTempSmartRoutingConfig({
|
||||
dbUrl: smartRoutingConfig.dbUrl || '',
|
||||
openaiApiBaseUrl: smartRoutingConfig.openaiApiBaseUrl || '',
|
||||
openaiApiKey: smartRoutingConfig.openaiApiKey || '',
|
||||
openaiApiEmbeddingModel: smartRoutingConfig.openaiApiEmbeddingModel || '',
|
||||
});
|
||||
}
|
||||
}, [smartRoutingConfig]);
|
||||
|
||||
const [sectionsVisible, setSectionsVisible] = useState({
|
||||
routingConfig: false,
|
||||
installConfig: false,
|
||||
smartRoutingConfig: false,
|
||||
password: false
|
||||
});
|
||||
|
||||
const toggleSection = (section: 'routingConfig' | 'installConfig' | 'password') => {
|
||||
const toggleSection = (section: 'routingConfig' | 'installConfig' | 'smartRoutingConfig' | 'password') => {
|
||||
setSectionsVisible(prev => ({
|
||||
...prev,
|
||||
[section]: !prev[section]
|
||||
@@ -91,6 +119,59 @@ const SettingsPage: React.FC = () => {
|
||||
await updateInstallConfig(key, installConfig[key]);
|
||||
};
|
||||
|
||||
const handleSmartRoutingConfigChange = (key: 'dbUrl' | 'openaiApiBaseUrl' | 'openaiApiKey' | 'openaiApiEmbeddingModel', value: string) => {
|
||||
setTempSmartRoutingConfig({
|
||||
...tempSmartRoutingConfig,
|
||||
[key]: value
|
||||
});
|
||||
};
|
||||
|
||||
const saveSmartRoutingConfig = async (key: 'dbUrl' | 'openaiApiBaseUrl' | 'openaiApiKey' | 'openaiApiEmbeddingModel') => {
|
||||
await updateSmartRoutingConfig(key, tempSmartRoutingConfig[key]);
|
||||
};
|
||||
|
||||
const handleSmartRoutingEnabledChange = async (value: boolean) => {
|
||||
// If enabling Smart Routing, validate required fields and save any unsaved changes
|
||||
if (value) {
|
||||
const currentDbUrl = tempSmartRoutingConfig.dbUrl || smartRoutingConfig.dbUrl;
|
||||
const currentOpenaiApiKey = tempSmartRoutingConfig.openaiApiKey || smartRoutingConfig.openaiApiKey;
|
||||
|
||||
if (!currentDbUrl || !currentOpenaiApiKey) {
|
||||
const missingFields = [];
|
||||
if (!currentDbUrl) missingFields.push(t('settings.dbUrl'));
|
||||
if (!currentOpenaiApiKey) missingFields.push(t('settings.openaiApiKey'));
|
||||
|
||||
showToast(t('settings.smartRoutingValidationError', {
|
||||
fields: missingFields.join(', ')
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare updates object with unsaved changes and enabled status
|
||||
const updates: any = { enabled: value };
|
||||
|
||||
// Check for unsaved changes and include them in the batch update
|
||||
if (tempSmartRoutingConfig.dbUrl !== smartRoutingConfig.dbUrl) {
|
||||
updates.dbUrl = tempSmartRoutingConfig.dbUrl;
|
||||
}
|
||||
if (tempSmartRoutingConfig.openaiApiBaseUrl !== smartRoutingConfig.openaiApiBaseUrl) {
|
||||
updates.openaiApiBaseUrl = tempSmartRoutingConfig.openaiApiBaseUrl;
|
||||
}
|
||||
if (tempSmartRoutingConfig.openaiApiKey !== smartRoutingConfig.openaiApiKey) {
|
||||
updates.openaiApiKey = tempSmartRoutingConfig.openaiApiKey;
|
||||
}
|
||||
if (tempSmartRoutingConfig.openaiApiEmbeddingModel !== smartRoutingConfig.openaiApiEmbeddingModel) {
|
||||
updates.openaiApiEmbeddingModel = tempSmartRoutingConfig.openaiApiEmbeddingModel;
|
||||
}
|
||||
|
||||
// Save all changes in a single batch update
|
||||
await updateSmartRoutingConfigBatch(updates);
|
||||
} else {
|
||||
// If disabling, just update the enabled status
|
||||
await updateSmartRoutingConfig('enabled', value);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordChangeSuccess = () => {
|
||||
setTimeout(() => {
|
||||
navigate('/');
|
||||
@@ -133,6 +214,131 @@ const SettingsPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Smart Routing Configuration Settings */}
|
||||
<div className="bg-white shadow rounded-lg py-4 px-6 mb-6">
|
||||
<div
|
||||
className="flex justify-between items-center cursor-pointer"
|
||||
onClick={() => toggleSection('smartRoutingConfig')}
|
||||
>
|
||||
<h2 className="font-semibold text-gray-800">{t('pages.settings.smartRouting')}</h2>
|
||||
<span className="text-gray-500">
|
||||
{sectionsVisible.smartRoutingConfig ? '▼' : '►'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{sectionsVisible.smartRoutingConfig && (
|
||||
<div className="space-y-4 mt-4">
|
||||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-md">
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-700">{t('settings.enableSmartRouting')}</h3>
|
||||
<p className="text-sm text-gray-500">{t('settings.enableSmartRoutingDescription')}</p>
|
||||
</div>
|
||||
<Switch
|
||||
disabled={loading}
|
||||
checked={smartRoutingConfig.enabled}
|
||||
onCheckedChange={(checked) => handleSmartRoutingEnabledChange(checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-gray-50 rounded-md">
|
||||
<div className="mb-2">
|
||||
<h3 className="font-medium text-gray-700">
|
||||
<span className="text-red-500 px-1">*</span>{t('settings.dbUrl')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="text"
|
||||
value={tempSmartRoutingConfig.dbUrl}
|
||||
onChange={(e) => handleSmartRoutingConfigChange('dbUrl', e.target.value)}
|
||||
placeholder={t('settings.dbUrlPlaceholder')}
|
||||
className="flex-1 mt-1 block w-full py-2 px-3 border rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm border-gray-300"
|
||||
disabled={loading}
|
||||
/>
|
||||
<button
|
||||
onClick={() => saveSmartRoutingConfig('dbUrl')}
|
||||
disabled={loading}
|
||||
className="mt-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md text-sm font-medium disabled:opacity-50"
|
||||
>
|
||||
{t('common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-gray-50 rounded-md">
|
||||
<div className="mb-2">
|
||||
<h3 className="font-medium text-gray-700">
|
||||
<span className="text-red-500 px-1">*</span>{t('settings.openaiApiKey')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="password"
|
||||
value={tempSmartRoutingConfig.openaiApiKey}
|
||||
onChange={(e) => handleSmartRoutingConfigChange('openaiApiKey', e.target.value)}
|
||||
placeholder={t('settings.openaiApiKeyPlaceholder')}
|
||||
className="flex-1 mt-1 block w-full py-2 px-3 border rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm border-gray-300"
|
||||
disabled={loading}
|
||||
/>
|
||||
<button
|
||||
onClick={() => saveSmartRoutingConfig('openaiApiKey')}
|
||||
disabled={loading}
|
||||
className="mt-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md text-sm font-medium disabled:opacity-50"
|
||||
>
|
||||
{t('common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-gray-50 rounded-md">
|
||||
<div className="mb-2">
|
||||
<h3 className="font-medium text-gray-700">{t('settings.openaiApiBaseUrl')}</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="text"
|
||||
value={tempSmartRoutingConfig.openaiApiBaseUrl}
|
||||
onChange={(e) => handleSmartRoutingConfigChange('openaiApiBaseUrl', e.target.value)}
|
||||
placeholder={t('settings.openaiApiBaseUrlPlaceholder')}
|
||||
className="flex-1 mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
disabled={loading}
|
||||
/>
|
||||
<button
|
||||
onClick={() => saveSmartRoutingConfig('openaiApiBaseUrl')}
|
||||
disabled={loading}
|
||||
className="mt-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md text-sm font-medium disabled:opacity-50"
|
||||
>
|
||||
{t('common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-gray-50 rounded-md">
|
||||
<div className="mb-2">
|
||||
<h3 className="font-medium text-gray-700">{t('settings.openaiApiEmbeddingModel')}</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="text"
|
||||
value={tempSmartRoutingConfig.openaiApiEmbeddingModel}
|
||||
onChange={(e) => handleSmartRoutingConfigChange('openaiApiEmbeddingModel', e.target.value)}
|
||||
placeholder={t('settings.openaiApiEmbeddingModelPlaceholder')}
|
||||
className="flex-1 mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
disabled={loading}
|
||||
/>
|
||||
<button
|
||||
onClick={() => saveSmartRoutingConfig('openaiApiEmbeddingModel')}
|
||||
disabled={loading}
|
||||
className="mt-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md text-sm font-medium disabled:opacity-50"
|
||||
>
|
||||
{t('common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Route Configuration Settings */}
|
||||
<div className="bg-white shadow rounded-lg py-4 px-6 mb-6">
|
||||
<div
|
||||
@@ -296,7 +502,7 @@ const SettingsPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user