mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-01-18 17:22:52 +00:00
save user custom input model name
This commit is contained in:
parent
2363e964ad
commit
5c383c0634
@ -44,7 +44,7 @@ const keyMap: Record<ApiProvider, ProviderSettingKey> = {
|
||||
'OpenAICompatible': 'openaicompatibleProvider',
|
||||
};
|
||||
|
||||
const getProviderSettingKey = (provider: ApiProvider): ProviderSettingKey => {
|
||||
export const getProviderSettingKey = (provider: ApiProvider): ProviderSettingKey => {
|
||||
return keyMap[provider];
|
||||
};
|
||||
|
||||
@ -171,7 +171,7 @@ const CustomProviderSettings: React.FC<CustomProviderSettingsProps> = ({ plugin,
|
||||
});
|
||||
};
|
||||
|
||||
const testApiConnection = async (provider: ApiProvider) => {
|
||||
const testApiConnection = async (provider: ApiProvider, modelId?: string) => {
|
||||
console.log(`Testing connection for ${provider}...`);
|
||||
|
||||
try {
|
||||
@ -189,7 +189,7 @@ const CustomProviderSettings: React.FC<CustomProviderSettingsProps> = ({ plugin,
|
||||
|
||||
// 获取提供商的默认聊天模型
|
||||
const defaultModels = GetDefaultModelId(provider);
|
||||
const testModelId = defaultModels.chat;
|
||||
const testModelId = modelId || defaultModels.chat;
|
||||
|
||||
// 对于没有默认模型的提供商,使用通用的测试模型
|
||||
if (!testModelId) {
|
||||
@ -285,27 +285,58 @@ const CustomProviderSettings: React.FC<CustomProviderSettingsProps> = ({ plugin,
|
||||
return settings[providerKey] || {};
|
||||
};
|
||||
|
||||
const updateChatModelId = (provider: ApiProvider, modelId: string) => {
|
||||
const updateChatModelId = (
|
||||
provider: ApiProvider,
|
||||
modelId: string,
|
||||
isCustom: boolean = false
|
||||
) => {
|
||||
console.log(`updateChatModelId: ${provider} -> ${modelId}, isCustom: ${isCustom}`)
|
||||
const providerSettingKey = getProviderSettingKey(provider);
|
||||
const providerSettings = settings[providerSettingKey] || {};
|
||||
const currentModels = providerSettings.models || new Set<string>();
|
||||
|
||||
handleSettingsUpdate({
|
||||
...settings,
|
||||
chatModelProvider: provider,
|
||||
chatModelId: modelId
|
||||
chatModelId: modelId,
|
||||
[providerSettingKey]: {
|
||||
...providerSettings,
|
||||
models: isCustom ? new Set([...currentModels, modelId]) : currentModels
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateApplyModelId = (provider: ApiProvider, modelId: string) => {
|
||||
const updateApplyModelId = (provider: ApiProvider, modelId: string, isCustom: boolean = false) => {
|
||||
console.log(`updateApplyModelId: ${provider} -> ${modelId}, isCustom: ${isCustom}`)
|
||||
const providerSettingKey = getProviderSettingKey(provider);
|
||||
const providerSettings = settings[providerSettingKey] || {};
|
||||
const currentModels = providerSettings.models || new Set<string>();
|
||||
|
||||
handleSettingsUpdate({
|
||||
...settings,
|
||||
applyModelProvider: provider,
|
||||
applyModelId: modelId
|
||||
applyModelId: modelId,
|
||||
[providerSettingKey]: {
|
||||
...providerSettings,
|
||||
models: isCustom ? new Set([...currentModels, modelId]) : currentModels
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateEmbeddingModelId = (provider: ApiProvider, modelId: string) => {
|
||||
const updateEmbeddingModelId = (provider: ApiProvider, modelId: string, isCustom: boolean = false) => {
|
||||
console.log(`updateEmbeddingModelId: ${provider} -> ${modelId}, isCustom: ${isCustom}`)
|
||||
const providerSettingKey = getProviderSettingKey(provider);
|
||||
const providerSettings = settings[providerSettingKey] || {};
|
||||
const currentModels = providerSettings.models || new Set<string>();
|
||||
|
||||
handleSettingsUpdate({
|
||||
...settings,
|
||||
embeddingModelProvider: provider,
|
||||
embeddingModelId: modelId
|
||||
embeddingModelId: modelId,
|
||||
[providerSettingKey]: {
|
||||
...providerSettings,
|
||||
models: isCustom ? new Set([...currentModels, modelId]) : currentModels
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@ import { InfioSettings } from "../../types/settings";
|
||||
// import { PROVIDERS } from '../constants';
|
||||
import { GetAllProviders, GetEmbeddingProviderModelIds, GetEmbeddingProviders, GetProviderModelIds } from "../../utils/api";
|
||||
|
||||
import { getProviderSettingKey } from "./ModelProviderSettings";
|
||||
|
||||
type TextSegment = {
|
||||
text: string;
|
||||
isHighlighted: boolean;
|
||||
@ -21,6 +23,7 @@ type SearchableItem = {
|
||||
type HighlightedItem = {
|
||||
id: string;
|
||||
html: TextSegment[];
|
||||
isCustom?: boolean;
|
||||
};
|
||||
|
||||
// Type guard for Record<string, unknown>
|
||||
@ -154,7 +157,7 @@ export type ComboBoxComponentProps = {
|
||||
settings?: InfioSettings | null;
|
||||
isEmbedding?: boolean,
|
||||
description?: string;
|
||||
updateModel: (provider: ApiProvider, modelId: string) => void;
|
||||
updateModel: (provider: ApiProvider, modelId: string, isCustom?: boolean) => void;
|
||||
};
|
||||
|
||||
export const ComboBoxComponent: React.FC<ComboBoxComponentProps> = ({
|
||||
@ -178,24 +181,49 @@ export const ComboBoxComponent: React.FC<ComboBoxComponentProps> = ({
|
||||
|
||||
const [modelIds, setModelIds] = useState<string[]>([]);
|
||||
|
||||
// 统一处理模型选择和保存
|
||||
const handleModelSelect = (provider: ApiProvider, modelId: string, isCustom?: boolean) => {
|
||||
console.log(`handleModelSelect: ${provider} -> ${modelId}`)
|
||||
|
||||
// 检查是否是自定义模型(不在官方模型列表中)
|
||||
// const isCustomModel = !modelIds.includes(modelId);
|
||||
|
||||
updateModel(provider, modelId, isCustom);
|
||||
};
|
||||
|
||||
// Replace useMemo with useEffect for async fetching
|
||||
useEffect(() => {
|
||||
const fetchModelIds = async () => {
|
||||
const ids = isEmbedding
|
||||
? GetEmbeddingProviderModelIds(modelProvider)
|
||||
: await GetProviderModelIds(modelProvider, settings);
|
||||
console.log(`📝 Fetched ${ids.length} official models for ${modelProvider}:`, ids);
|
||||
setModelIds(ids);
|
||||
};
|
||||
|
||||
fetchModelIds();
|
||||
}, [modelProvider, isEmbedding, settings]);
|
||||
|
||||
const combinedModelIds = useMemo(() => {
|
||||
const providerKey = getProviderSettingKey(modelProvider);
|
||||
const providerModels = settings?.[providerKey]?.models;
|
||||
console.log(`🔍 Custom models in settings for ${modelProvider}:`, providerModels ? Array.from(providerModels) : 'none')
|
||||
// Ensure providerModels is a Set of strings
|
||||
if (!providerModels || !(providerModels instanceof Set)) {
|
||||
console.log(`📋 Using only official models (${modelIds.length}):`, modelIds);
|
||||
return modelIds;
|
||||
}
|
||||
const additionalModels = Array.from(providerModels).filter((model): model is string => typeof model === 'string');
|
||||
console.log(`📋 Combined models: ${modelIds.length} official + ${additionalModels.length} custom`);
|
||||
return [...modelIds, ...additionalModels];
|
||||
}, [modelIds, settings, modelProvider]);
|
||||
|
||||
const searchableItems = useMemo(() => {
|
||||
return modelIds.map((id) => ({
|
||||
id,
|
||||
html: id,
|
||||
return combinedModelIds.map((id): SearchableItem => ({
|
||||
id: String(id),
|
||||
html: String(id),
|
||||
}))
|
||||
}, [modelIds])
|
||||
}, [combinedModelIds])
|
||||
|
||||
// fuse, used for fuzzy search, simple configuration threshold can be adjusted as needed
|
||||
const fuse: Fuse<SearchableItem> = useMemo(() => {
|
||||
@ -212,7 +240,7 @@ export const ComboBoxComponent: React.FC<ComboBoxComponentProps> = ({
|
||||
|
||||
// 根据 searchTerm 得到过滤后的数据列表
|
||||
const filteredOptions = useMemo(() => {
|
||||
let results: HighlightedItem[] = searchTerm
|
||||
const results: HighlightedItem[] = searchTerm
|
||||
? highlight(fuse.search(searchTerm))
|
||||
: searchableItems.map(item => ({
|
||||
...item,
|
||||
@ -225,7 +253,8 @@ export const ComboBoxComponent: React.FC<ComboBoxComponentProps> = ({
|
||||
if (!exactMatch) {
|
||||
results.unshift({
|
||||
id: searchTerm,
|
||||
html: [{ text: `${modelIds.length > 0 ? t("settings.ModelProvider.custom") : ''}${searchTerm}`, isHighlighted: false }]
|
||||
html: [{ text: `${modelIds.length > 0 ? t("settings.ModelProvider.custom") : ''}${searchTerm}`, isHighlighted: false }],
|
||||
isCustom: true
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -251,8 +280,8 @@ export const ComboBoxComponent: React.FC<ComboBoxComponentProps> = ({
|
||||
// Use proper type checking without type assertion
|
||||
const availableProviders = providers;
|
||||
const isValidProvider = (value: string): value is ApiProvider => {
|
||||
// @ts-ignore
|
||||
return (availableProviders as readonly string[]).includes(value);
|
||||
// @ts-expect-error - checking if providers array includes the value
|
||||
return availableProviders.includes(value);
|
||||
};
|
||||
|
||||
if (isValidProvider(newProvider)) {
|
||||
@ -346,11 +375,11 @@ export const ComboBoxComponent: React.FC<ComboBoxComponentProps> = ({
|
||||
if (filteredOptions.length > 0) {
|
||||
const selectedOption = filteredOptions[selectedIndex];
|
||||
if (selectedOption) {
|
||||
updateModel(modelProvider, selectedOption.id);
|
||||
handleModelSelect(modelProvider, selectedOption.id, selectedOption.isCustom);
|
||||
}
|
||||
} else if (searchTerm.trim()) {
|
||||
// If no options but there is input content, use the input content directly
|
||||
updateModel(modelProvider, searchTerm.trim());
|
||||
handleModelSelect(modelProvider, searchTerm.trim(), true);
|
||||
}
|
||||
setSearchTerm("");
|
||||
setIsOpen(false);
|
||||
@ -373,7 +402,7 @@ export const ComboBoxComponent: React.FC<ComboBoxComponentProps> = ({
|
||||
ref={(el) => (itemRefs.current[index] = el)}
|
||||
onMouseEnter={() => setSelectedIndex(index)}
|
||||
onClick={() => {
|
||||
updateModel(modelProvider, option.id);
|
||||
handleModelSelect(modelProvider, option.id, option.isCustom);
|
||||
setSearchTerm("");
|
||||
setIsOpen(false);
|
||||
}}
|
||||
|
||||
@ -19,144 +19,168 @@ const InfioProviderSchema = z.object({
|
||||
name: z.literal('Infio'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'Infio',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const OpenRouterProviderSchema = z.object({
|
||||
name: z.literal('OpenRouter'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'OpenRouter',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const SiliconFlowProviderSchema = z.object({
|
||||
name: z.literal('SiliconFlow'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'SiliconFlow',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const AlibabaQwenProviderSchema = z.object({
|
||||
name: z.literal('AlibabaQwen'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'AlibabaQwen',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const AnthropicProviderSchema = z.object({
|
||||
name: z.literal('Anthropic'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().optional(),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'Anthropic',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const DeepSeekProviderSchema = z.object({
|
||||
name: z.literal('DeepSeek'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'DeepSeek',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const GoogleProviderSchema = z.object({
|
||||
name: z.literal('Google'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'Google',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const OpenAIProviderSchema = z.object({
|
||||
name: z.literal('OpenAI'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().optional(),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'OpenAI',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const OpenAICompatibleProviderSchema = z.object({
|
||||
name: z.literal('OpenAICompatible'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().optional(),
|
||||
useCustomUrl: z.boolean().catch(true)
|
||||
useCustomUrl: z.boolean().catch(true),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'OpenAICompatible',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: true
|
||||
useCustomUrl: true,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const OllamaProviderSchema = z.object({
|
||||
name: z.literal('Ollama'),
|
||||
apiKey: z.string().catch('ollama'),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'Ollama',
|
||||
apiKey: 'ollama',
|
||||
baseUrl: '',
|
||||
useCustomUrl: true
|
||||
useCustomUrl: true,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const GroqProviderSchema = z.object({
|
||||
name: z.literal('Groq'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'Groq',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const GrokProviderSchema = z.object({
|
||||
name: z.literal('Grok'),
|
||||
apiKey: z.string().catch(''),
|
||||
baseUrl: z.string().catch(''),
|
||||
useCustomUrl: z.boolean().catch(false)
|
||||
useCustomUrl: z.boolean().catch(false),
|
||||
models: z.set(z.string()).catch(new Set())
|
||||
}).catch({
|
||||
name: 'Grok',
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
useCustomUrl: false
|
||||
useCustomUrl: false,
|
||||
models: new Set()
|
||||
})
|
||||
|
||||
const ollamaModelSchema = z.object({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user