diff --git a/ui/src/components/ConfigProvider.tsx b/ui/src/components/ConfigProvider.tsx index 1145dce..0d40fbd 100644 --- a/ui/src/components/ConfigProvider.tsx +++ b/ui/src/components/ConfigProvider.tsx @@ -1,53 +1,7 @@ import { createContext, useContext, useState, useEffect } from 'react'; import type { ReactNode, Dispatch, SetStateAction } from 'react'; import { api } from '@/lib/api'; - -export interface Transformer { - path: string; - options: { - [key: string]: string; - }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; -} - -export interface ProviderTransformer { - use: (string | (string | Record | { max_tokens: number })[])[]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; // for model specific transformers -} - -export interface Provider { - name: string; - api_base_url: string; - api_key: string; - models: string[]; - transformer?: ProviderTransformer; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; -} - -export interface RouterConfig { - default: string; - background: string; - think: string; - longContext: string; - longContextThreshold: number; - webSearch: string; -} - -export interface Config { - LOG: boolean; - CLAUDE_PATH: string; - HOST: string; - PORT: number; - APIKEY: string; - API_TIMEOUT_MS: string; - PROXY_URL: string; - transformers: Transformer[]; - Providers: Provider[]; - Router: RouterConfig; -} +import type { Config } from '@/types'; interface ConfigContextType { config: Config | null; @@ -121,7 +75,7 @@ export function ConfigProvider({ children }: ConfigProviderProps) { APIKEY: typeof data.APIKEY === 'string' ? data.APIKEY : '', API_TIMEOUT_MS: typeof data.API_TIMEOUT_MS === 'string' ? data.API_TIMEOUT_MS : '600000', PROXY_URL: typeof data.PROXY_URL === 'string' ? data.PROXY_URL : '', - transformers: Array.isArray(data.transformers) ? data.transformers : [], + Transformers: Array.isArray(data.Transformers) ? data.Transformers : [], Providers: Array.isArray(data.Providers) ? data.Providers : [], Router: data.Router && typeof data.Router === 'object' ? { default: typeof data.Router.default === 'string' ? data.Router.default : '', @@ -155,7 +109,7 @@ export function ConfigProvider({ children }: ConfigProviderProps) { APIKEY: '', API_TIMEOUT_MS: '600000', PROXY_URL: '', - transformers: [], + Transformers: [], Providers: [], Router: { default: '', diff --git a/ui/src/components/ProviderList.tsx b/ui/src/components/ProviderList.tsx index 5553658..72602e9 100644 --- a/ui/src/components/ProviderList.tsx +++ b/ui/src/components/ProviderList.tsx @@ -1,7 +1,7 @@ import { Pencil, Trash2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; -import { type Provider } from "./ConfigProvider"; +import type { Provider } from "@/types"; interface ProviderListProps { providers: Provider[]; diff --git a/ui/src/components/Providers.tsx b/ui/src/components/Providers.tsx index e1b18ca..7ffddb3 100644 --- a/ui/src/components/Providers.tsx +++ b/ui/src/components/Providers.tsx @@ -19,7 +19,9 @@ import { Badge } from "@/components/ui/badge"; import { Combobox } from "@/components/ui/combobox"; import { ComboInput } from "@/components/ui/combo-input"; import { api } from "@/lib/api"; +import type { Provider } from "@/types"; +interface ProviderType extends Provider {} export function Providers() { const { t } = useTranslation(); @@ -30,10 +32,29 @@ export function Providers() { const [providerParamInputs, setProviderParamInputs] = useState>({}); const [modelParamInputs, setModelParamInputs] = useState>({}); const [availableTransformers, setAvailableTransformers] = useState<{name: string; endpoint: string | null;}[]>([]); - const [editingProviderData, setEditingProviderData] = useState(null); + const [editingProviderData, setEditingProviderData] = useState(null); const [isNewProvider, setIsNewProvider] = useState(false); + const [providerTemplates, setProviderTemplates] = useState([]); const comboInputRef = useRef(null); + useEffect(() => { + const fetchProviderTemplates = async () => { + try { + const response = await fetch('https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json'); + if (response.ok) { + const data = await response.json(); + setProviderTemplates(data || []); + } else { + console.error('Failed to fetch provider templates'); + } + } catch (error) { + console.error('Failed to fetch provider templates:', error); + } + }; + + fetchProviderTemplates(); + }, []); + // Fetch available transformers when component mounts useEffect(() => { const fetchTransformers = async () => { @@ -67,7 +88,7 @@ export function Providers() { const handleAddProvider = () => { - const newProvider = { name: "", api_base_url: "", api_key: "", models: [] }; + const newProvider: ProviderType = { name: "", api_base_url: "", api_key: "", models: [] }; setEditingProviderIndex(config.Providers.length); setEditingProviderData(newProvider); setIsNewProvider(true); @@ -116,14 +137,14 @@ export function Providers() { setDeletingProviderIndex(null); }; - const handleProviderChange = (index: number, field: string, value: string) => { + const handleProviderChange = (_index: number, field: string, value: string) => { if (editingProviderData) { const updatedProvider = { ...editingProviderData, [field]: value }; setEditingProviderData(updatedProvider); } }; - const handleProviderTransformerChange = (index: number, transformerPath: string) => { + const handleProviderTransformerChange = (_index: number, transformerPath: string) => { if (!transformerPath || !editingProviderData) return; // Don't add empty transformers const updatedProvider = { ...editingProviderData }; @@ -137,7 +158,7 @@ export function Providers() { setEditingProviderData(updatedProvider); }; - const removeProviderTransformerAtIndex = (index: number, transformerIndex: number) => { + const removeProviderTransformerAtIndex = (_index: number, transformerIndex: number) => { if (!editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -156,7 +177,7 @@ export function Providers() { setEditingProviderData(updatedProvider); }; - const handleModelTransformerChange = (providerIndex: number, model: string, transformerPath: string) => { + const handleModelTransformerChange = (_providerIndex: number, model: string, transformerPath: string) => { if (!transformerPath || !editingProviderData) return; // Don't add empty transformers const updatedProvider = { ...editingProviderData }; @@ -175,7 +196,7 @@ export function Providers() { setEditingProviderData(updatedProvider); }; - const removeModelTransformerAtIndex = (providerIndex: number, model: string, transformerIndex: number) => { + const removeModelTransformerAtIndex = (_providerIndex: number, model: string, transformerIndex: number) => { if (!editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -195,7 +216,7 @@ export function Providers() { }; - const addProviderTransformerParameter = (providerIndex: number, transformerIndex: number, paramName: string, paramValue: string) => { + const addProviderTransformerParameter = (_providerIndex: number, transformerIndex: number, paramName: string, paramValue: string) => { if (!editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -239,7 +260,7 @@ export function Providers() { }; - const removeProviderTransformerParameterAtIndex = (providerIndex: number, transformerIndex: number, paramName: string) => { + const removeProviderTransformerParameterAtIndex = (_providerIndex: number, transformerIndex: number, paramName: string) => { if (!editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -269,7 +290,7 @@ export function Providers() { } }; - const addModelTransformerParameter = (providerIndex: number, model: string, transformerIndex: number, paramName: string, paramValue: string) => { + const addModelTransformerParameter = (_providerIndex: number, model: string, transformerIndex: number, paramName: string, paramValue: string) => { if (!editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -317,7 +338,7 @@ export function Providers() { }; - const removeModelTransformerParameterAtIndex = (providerIndex: number, model: string, transformerIndex: number, paramName: string) => { + const removeModelTransformerParameterAtIndex = (_providerIndex: number, model: string, transformerIndex: number, paramName: string) => { if (!editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -347,7 +368,7 @@ export function Providers() { } }; - const handleAddModel = (index: number, model: string) => { + const handleAddModel = (_index: number, model: string) => { if (!model.trim() || !editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -363,7 +384,26 @@ export function Providers() { } }; - const handleRemoveModel = (providerIndex: number, modelIndex: number) => { + const handleTemplateImport = (value: string) => { + if (!value) return; + try { + const selectedTemplate = JSON.parse(value); + if (selectedTemplate) { + const currentName = editingProviderData?.name; + const newProviderData = JSON.parse(JSON.stringify(selectedTemplate)); + + if (!isNewProvider && currentName) { + newProviderData.name = currentName; + } + + setEditingProviderData(newProviderData as ProviderType); + } + } catch (e) { + console.error("Failed to parse template", e); + } + }; + + const handleRemoveModel = (_providerIndex: number, modelIndex: number) => { if (!editingProviderData) return; const updatedProvider = { ...editingProviderData }; @@ -407,6 +447,18 @@ export function Providers() { {editingProvider && editingProviderIndex !== null && (
+ {providerTemplates.length > 0 && ( +
+ + ({ label: p.name, value: JSON.stringify(p) }))} + value="" + onChange={handleTemplateImport} + placeholder={t("providers.select_template")} + emptyPlaceholder={t("providers.no_templates_found")} + /> +
+ )}
handleProviderChange(editingProviderIndex, 'name', e.target.value)} /> diff --git a/ui/src/components/TransformerList.tsx b/ui/src/components/TransformerList.tsx index 42300a5..61ae82a 100644 --- a/ui/src/components/TransformerList.tsx +++ b/ui/src/components/TransformerList.tsx @@ -1,6 +1,6 @@ import { Pencil, Trash2 } from "lucide-react"; import { Button } from "@/components/ui/button"; -import { type Transformer } from "./ConfigProvider"; +import type { Transformer } from "@/types"; interface TransformerListProps { transformers: Transformer[]; @@ -46,8 +46,8 @@ export function TransformerList({ transformers, onEdit, onRemove }: TransformerL // Handle case where transformer.path might be null or undefined const transformerPath = transformer.path || "Unnamed Transformer"; - // Handle case where transformer.options might be null or undefined - const options = transformer.options || {}; + // Handle case where transformer.parameters might be null or undefined + const options = transformer.parameters || {}; // Render parameters as tags in a single line const renderParameters = () => { diff --git a/ui/src/components/Transformers.tsx b/ui/src/components/Transformers.tsx index c39b9a7..c772270 100644 --- a/ui/src/components/Transformers.tsx +++ b/ui/src/components/Transformers.tsx @@ -21,7 +21,7 @@ export function Transformers() { const { config, setConfig } = useConfig(); const [editingTransformerIndex, setEditingTransformerIndex] = useState(null); const [deletingTransformerIndex, setDeletingTransformerIndex] = useState(null); - const [newTransformer, setNewTransformer] = useState<{ path: string; options: { [key: string]: string } } | null>(null); + const [newTransformer, setNewTransformer] = useState<{ name: string; path: string; project: string; parameters: { [key: string]: string } } | null>(null); // Handle case where config is null or undefined if (!config) { @@ -37,11 +37,11 @@ export function Transformers() { ); } - // Validate config.transformers to ensure it's an array - const validTransformers = Array.isArray(config.transformers) ? config.transformers : []; + // Validate config.Transformers to ensure it's an array + const validTransformers = Array.isArray(config.Transformers) ? config.Transformers : []; const handleAddTransformer = () => { - const newTransformer = { path: "", options: {} }; + const newTransformer = { name: "", path: "", project: "", parameters: {} }; setNewTransformer(newTransformer); setEditingTransformerIndex(validTransformers.length); // Use the length as index for the new item }; @@ -49,26 +49,26 @@ export function Transformers() { const handleRemoveTransformer = (index: number) => { const newTransformers = [...validTransformers]; newTransformers.splice(index, 1); - setConfig({ ...config, transformers: newTransformers }); + setConfig({ ...config, Transformers: newTransformers }); setDeletingTransformerIndex(null); }; - const handleTransformerChange = (index: number, field: string, value: string, optionKey?: string) => { + const handleTransformerChange = (index: number, field: string, value: string, parameterKey?: string) => { if (index < validTransformers.length) { // Editing an existing transformer const newTransformers = [...validTransformers]; - if (optionKey !== undefined) { - newTransformers[index].options[optionKey] = value; + if (parameterKey !== undefined) { + newTransformers[index].parameters![parameterKey] = value; } else { - (newTransformers[index] as Record)[field] = value; + (newTransformers[index] as unknown as Record)[field] = value; } - setConfig({ ...config, transformers: newTransformers }); + setConfig({ ...config, Transformers: newTransformers }); } else { // Editing the new transformer if (newTransformer) { const updatedTransformer = { ...newTransformer }; - if (optionKey !== undefined) { - updatedTransformer.options[optionKey] = value; + if (parameterKey !== undefined) { + updatedTransformer.parameters![parameterKey] = value; } else { (updatedTransformer as Record)[field] = value; } @@ -87,7 +87,7 @@ export function Transformers() { if (newTransformer && editingTransformerIndex === validTransformers.length) { // Saving a new transformer const newTransformers = [...validTransformers, newTransformer]; - setConfig({ ...config, transformers: newTransformers }); + setConfig({ ...config, Transformers: newTransformers }); } // Close the dialog setEditingTransformerIndex(null); @@ -137,16 +137,16 @@ export function Transformers() { variant="outline" size="sm" onClick={() => { - const options = editingTransformer.options || {}; - const newKey = `param${Object.keys(options).length + 1}`; + const parameters = editingTransformer.parameters || {}; + const newKey = `param${Object.keys(parameters).length + 1}`; if (editingTransformerIndex !== null) { - const newOptions = { ...options, [newKey]: "" }; + const newParameters = { ...parameters, [newKey]: "" }; if (editingTransformerIndex < validTransformers.length) { const newTransformers = [...validTransformers]; - newTransformers[editingTransformerIndex].options = newOptions; - setConfig({ ...config, transformers: newTransformers }); + newTransformers[editingTransformerIndex].parameters = newParameters; + setConfig({ ...config, Transformers: newTransformers }); } else if (newTransformer) { - setNewTransformer({ ...newTransformer, options: newOptions }); + setNewTransformer({ ...newTransformer, parameters: newParameters }); } } }} @@ -154,22 +154,22 @@ export function Transformers() {
- {Object.entries(editingTransformer.options || {}).map(([key, value]) => ( + {Object.entries(editingTransformer.parameters || {}).map(([key, value]) => (
{ - const options = editingTransformer.options || {}; - const newOptions = { ...options }; - delete newOptions[key]; - newOptions[e.target.value] = value; + const parameters = editingTransformer.parameters || {}; + const newParameters = { ...parameters }; + delete newParameters[key]; + newParameters[e.target.value] = value; if (editingTransformerIndex !== null) { if (editingTransformerIndex < validTransformers.length) { const newTransformers = [...validTransformers]; - newTransformers[editingTransformerIndex].options = newOptions; - setConfig({ ...config, transformers: newTransformers }); + newTransformers[editingTransformerIndex].parameters = newParameters; + setConfig({ ...config, Transformers: newTransformers }); } else if (newTransformer) { - setNewTransformer({ ...newTransformer, options: newOptions }); + setNewTransformer({ ...newTransformer, parameters: newParameters }); } } }} @@ -179,7 +179,7 @@ export function Transformers() { value={value} onChange={(e) => { if (editingTransformerIndex !== null) { - handleTransformerChange(editingTransformerIndex, "options", e.target.value, key); + handleTransformerChange(editingTransformerIndex, "parameters", e.target.value, key); } }} className="flex-1" @@ -189,15 +189,15 @@ export function Transformers() { size="icon" onClick={() => { if (editingTransformerIndex !== null) { - const options = editingTransformer.options || {}; - const newOptions = { ...options }; - delete newOptions[key]; + const parameters = editingTransformer.parameters || {}; + const newParameters = { ...parameters }; + delete newParameters[key]; if (editingTransformerIndex < validTransformers.length) { const newTransformers = [...validTransformers]; - newTransformers[editingTransformerIndex].options = newOptions; - setConfig({ ...config, transformers: newTransformers }); + newTransformers[editingTransformerIndex].parameters = newParameters; + setConfig({ ...config, Transformers: newTransformers }); } else if (newTransformer) { - setNewTransformer({ ...newTransformer, options: newOptions }); + setNewTransformer({ ...newTransformer, parameters: newParameters }); } } }} diff --git a/ui/src/lib/api.ts b/ui/src/lib/api.ts index 71a2406..10fb051 100644 --- a/ui/src/lib/api.ts +++ b/ui/src/lib/api.ts @@ -1,4 +1,4 @@ -import type { Config, Provider, Transformer } from '@/components/ConfigProvider'; +import type { Config, Provider, Transformer } from '@/types'; // API Client Class for handling requests with baseUrl and apikey authentication class ApiClient { diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json index 297de1d..9cf93fa 100644 --- a/ui/src/locales/en.json +++ b/ui/src/locales/en.json @@ -77,7 +77,11 @@ "add_parameter": "Add Parameter", "parameter_name": "Parameter Name", "parameter_value": "Parameter Value", - "selected_transformers": "Selected Transformers" + "selected_transformers": "Selected Transformers", + "import_from_template": "Import from template", + "no_templates_found": "No templates found", + "select_template": "Select a template..." + }, "router": { "title": "Router", diff --git a/ui/src/locales/zh.json b/ui/src/locales/zh.json index 7627cf5..3a4828e 100644 --- a/ui/src/locales/zh.json +++ b/ui/src/locales/zh.json @@ -77,7 +77,11 @@ "add_parameter": "添加参数", "parameter_name": "参数名称", "parameter_value": "参数值", - "selected_transformers": "已选转换器" + "selected_transformers": "已选转换器", + "import_from_template": "从模板导入", + "no_templates_found": "未找到模板", + "select_template": "选择一个模板..." + }, "router": { "title": "路由", diff --git a/ui/src/types.ts b/ui/src/types.ts new file mode 100644 index 0000000..470bc11 --- /dev/null +++ b/ui/src/types.ts @@ -0,0 +1,43 @@ +export interface ProviderTransformer { + use: (string | (string | Record | { max_tokens: number })[])[]; + [key: string]: any; // Allow for model-specific transformers +} + +export interface Provider { + name: string; + api_base_url: string; + api_key: string; + models: string[]; + transformer?: ProviderTransformer; +} + +export interface RouterConfig { + default: string; + background: string; + think: string; + longContext: string; + longContextThreshold: number; + webSearch: string; + custom?: any; +} + +export interface Transformer { + name: string; + path: string; + project: string; + parameters?: Record; +} + +export interface Config { + Providers: Provider[]; + Router: RouterConfig; + Transformers: Transformer[]; + // Top-level settings + LOG: boolean; + CLAUDE_PATH: string; + HOST: string; + PORT: number; + APIKEY: string; + API_TIMEOUT_MS: string; + PROXY_URL: string; +} diff --git a/ui/tsconfig.tsbuildinfo b/ui/tsconfig.tsbuildinfo index 6f6ea0a..62ad4f2 100644 --- a/ui/tsconfig.tsbuildinfo +++ b/ui/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/vite-env.d.ts","./src/components/configprovider.tsx","./src/components/jsoneditor.tsx","./src/components/login.tsx","./src/components/protectedroute.tsx","./src/components/providerlist.tsx","./src/components/providers.tsx","./src/components/publicroute.tsx","./src/components/router.tsx","./src/components/settingsdialog.tsx","./src/components/transformerlist.tsx","./src/components/transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/toast.tsx","./src/lib/api.ts","./src/lib/utils.ts"],"version":"5.8.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/configprovider.tsx","./src/components/jsoneditor.tsx","./src/components/login.tsx","./src/components/protectedroute.tsx","./src/components/providerlist.tsx","./src/components/providers.tsx","./src/components/publicroute.tsx","./src/components/router.tsx","./src/components/settingsdialog.tsx","./src/components/transformerlist.tsx","./src/components/transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/toast.tsx","./src/lib/api.ts","./src/lib/utils.ts"],"version":"5.8.3"} \ No newline at end of file