mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-04 23:00:30 +08:00
fix(provider): support editing fetched model types before save (#17728)
### Summary As title now can edit
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -345,9 +345,7 @@ func ParseListModel(modelList ModelList) []ListModelResponse {
|
||||
if pm != nil {
|
||||
modelEntity = pm.GetModelByNameOrAlias(modelName)
|
||||
}
|
||||
if model.OwnedBy != "" {
|
||||
modelName = modelName + "@" + model.OwnedBy
|
||||
}
|
||||
|
||||
modelResponse.Name = modelName
|
||||
if modelEntity != nil {
|
||||
modelResponse.MaxDimension = modelEntity.MaxDimension
|
||||
|
||||
@@ -110,8 +110,8 @@ func TestFunASRListModelsWithoutAPIKey(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("ListModels: %v", err)
|
||||
}
|
||||
if len(models) != 1 || models[0].Name != "fun-asr-nano@funasr" {
|
||||
t.Fatalf("models=%v, want fun-asr-nano@funasr", models)
|
||||
if len(models) != 1 || models[0].Name != "fun-asr-nano" {
|
||||
t.Fatalf("models=%v, want fun-asr-nano", models)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ func TestGiteeListModelsMapsAllDeepSeekAliasesToModelMetadata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGiteeListModelsKeepsOwnedBySuffixAfterAliasMetadataLookup(t *testing.T) {
|
||||
func TestGiteeListModelsKeepsModelNameAfterAliasMetadataLookup(t *testing.T) {
|
||||
withSSRFBypass(t)
|
||||
ctx := t.Context()
|
||||
initProviderManagerWithGiteeForTest(t)
|
||||
@@ -207,8 +207,8 @@ func TestGiteeListModelsKeepsOwnedBySuffixAfterAliasMetadataLookup(t *testing.T)
|
||||
t.Fatalf("len(models)=%d, want 1", len(models))
|
||||
}
|
||||
model := models[0]
|
||||
if model.Name != "deepseek/deepseek-v4-pro@gitee" {
|
||||
t.Fatalf("Name=%q, want deepseek/deepseek-v4-pro@gitee", model.Name)
|
||||
if model.Name != "deepseek/deepseek-v4-pro" {
|
||||
t.Fatalf("Name=%q, want deepseek/deepseek-v4-pro", model.Name)
|
||||
}
|
||||
if model.MaxTokens == nil || *model.MaxTokens != 1048576 {
|
||||
t.Fatalf("MaxTokens=%v, want 1048576", model.MaxTokens)
|
||||
|
||||
@@ -392,7 +392,7 @@ func TestCollectGoogleModelNamesPaginates(t *testing.T) {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
expectedModels := []ListModelResponse{{Name: "Gemini 2.5 Flash@Google"}, {Name: "Gemini 2.5 Pro@Google"}}
|
||||
expectedModels := []ListModelResponse{{Name: "Gemini 2.5 Flash"}, {Name: "Gemini 2.5 Pro"}}
|
||||
if !reflect.DeepEqual(models, expectedModels) {
|
||||
t.Fatalf("expected models %v, got %v", expectedModels, models)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestVolcEngineListModelsHappyPath(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("ListModels: %v", err)
|
||||
}
|
||||
if joinModelNames(models, ",") != "doubao-seed-2-0-pro-260215@volcengine,doubao-embedding-vision-251215" {
|
||||
if joinModelNames(models, ",") != "doubao-seed-2-0-pro-260215,doubao-embedding-vision-251215" {
|
||||
t.Errorf("models=%v", models)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func (h *ProviderHandler) ListModels(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 1. Get static models from config (may be nil when models list is empty)
|
||||
// 1. Get static models from config (maybe nil when models list is empty)
|
||||
staticModels, _ := dao.GetModelProviderManager().ListModels(providerName)
|
||||
if staticModels == nil {
|
||||
staticModels = []map[string]interface{}{}
|
||||
|
||||
@@ -140,9 +140,59 @@ export function useModelsCatalog({
|
||||
}: UseModelsCatalogArgs) {
|
||||
const { listProviderModels } = useListProviderModels();
|
||||
const [catalog, setCatalog] = useState<IProviderModelItem[]>([]);
|
||||
const [catalogOverrides, setCatalogOverrides] = useState<
|
||||
Record<string, IProviderModelItem>
|
||||
>({});
|
||||
const catalogOverridesRef = useRef(catalogOverrides);
|
||||
const [manualListLoading, setManualListLoading] = useState(false);
|
||||
const [hasFetched, setHasFetched] = useState(false);
|
||||
|
||||
const applyCatalogOverrides = useCallback((items: IProviderModelItem[]) => {
|
||||
const overrides = catalogOverridesRef.current;
|
||||
const names = new Set<string>();
|
||||
const merged = items.map((item) => {
|
||||
names.add(item.name);
|
||||
const override = overrides[item.name];
|
||||
return override ? { ...item, ...override, name: item.name } : item;
|
||||
});
|
||||
Object.entries(overrides).forEach(([name, override]) => {
|
||||
if (!names.has(name)) {
|
||||
merged.push(override);
|
||||
}
|
||||
});
|
||||
return merged;
|
||||
}, []);
|
||||
|
||||
const updateCatalogModel = useCallback(
|
||||
(name: string, item: IProviderModelItem) => {
|
||||
setCatalogOverrides((prev) => {
|
||||
const next = {
|
||||
...prev,
|
||||
[name]: { ...(prev[name] ?? {}), ...item, name },
|
||||
};
|
||||
catalogOverridesRef.current = next;
|
||||
return next;
|
||||
});
|
||||
setCatalog((prev) => {
|
||||
if (!prev.some((m) => m.name === name)) {
|
||||
return [...prev, { ...item, name }];
|
||||
}
|
||||
return prev.map((m) => (m.name === name ? { ...m, ...item, name } : m));
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const clearCatalogOverride = useCallback((name: string) => {
|
||||
setCatalogOverrides((prev) => {
|
||||
if (!prev[name]) return prev;
|
||||
const next = { ...prev };
|
||||
delete next[name];
|
||||
catalogOverridesRef.current = next;
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Manual "List models" handler — hits the upstream catalog endpoint.
|
||||
// The result is merged into `catalog`; the displayed list then becomes
|
||||
// the union of catalog + instance models.
|
||||
@@ -160,7 +210,9 @@ export function useModelsCatalog({
|
||||
base_url: baseUrl,
|
||||
});
|
||||
if (ret?.code === 0) {
|
||||
setCatalog((ret.data as IProviderModelItem[]) ?? []);
|
||||
setCatalog(
|
||||
applyCatalogOverrides((ret.data as IProviderModelItem[]) ?? []),
|
||||
);
|
||||
}
|
||||
setHasFetched(true);
|
||||
} catch {
|
||||
@@ -203,6 +255,8 @@ export function useModelsCatalog({
|
||||
return {
|
||||
catalog,
|
||||
setCatalog,
|
||||
updateCatalogModel,
|
||||
clearCatalogOverride,
|
||||
manualListLoading,
|
||||
hasFetched,
|
||||
handleListModels,
|
||||
@@ -599,6 +653,7 @@ interface UseModelMutationsArgs {
|
||||
filteredModels: IProviderModelItem[];
|
||||
addedSet: Set<string>;
|
||||
setCatalog: Dispatch<SetStateAction<IProviderModelItem[]>>;
|
||||
clearCatalogOverride: (name: string) => void;
|
||||
/**
|
||||
* Local mutators for the draft instance's model list. Required when
|
||||
* `isDraftInstance` is true so per-model add / remove / batch updates
|
||||
@@ -621,6 +676,7 @@ export function useModelMutations({
|
||||
filteredModels,
|
||||
addedSet,
|
||||
setCatalog,
|
||||
clearCatalogOverride,
|
||||
addDraftModel,
|
||||
removeDraftModel,
|
||||
setDraftModelsList,
|
||||
@@ -644,6 +700,7 @@ export function useModelMutations({
|
||||
// rides along with the instance save (model_info in the add body).
|
||||
if (isDraftInstance) {
|
||||
addDraftModel?.(model);
|
||||
clearCatalogOverride(model.name);
|
||||
return;
|
||||
}
|
||||
await addInstanceModel({
|
||||
@@ -652,8 +709,12 @@ export function useModelMutations({
|
||||
model_name: model.name,
|
||||
model_type: model.model_types ?? [],
|
||||
max_tokens: model.max_tokens ?? 0,
|
||||
extra: { is_tools: hasToolFeature(model.features), ...(model.extra ?? {}) },
|
||||
extra: {
|
||||
is_tools: hasToolFeature(model.features),
|
||||
...(model.extra ?? {}),
|
||||
},
|
||||
});
|
||||
clearCatalogOverride(model.name);
|
||||
};
|
||||
|
||||
const handleRemoveModel = async (model: IProviderModelItem) => {
|
||||
@@ -683,6 +744,7 @@ export function useModelMutations({
|
||||
// dropped on save.
|
||||
if (isDraftInstance) {
|
||||
addDraftModel?.(item);
|
||||
clearCatalogOverride(item.name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -694,6 +756,7 @@ export function useModelMutations({
|
||||
max_tokens: item.max_tokens ?? 0,
|
||||
extra: { is_tools: hasToolFeature(item.features), ...(item.extra ?? {}) },
|
||||
});
|
||||
clearCatalogOverride(item.name);
|
||||
};
|
||||
|
||||
// Batch attach/detach the currently visible (filtered) models.
|
||||
@@ -718,6 +781,11 @@ export function useModelMutations({
|
||||
|
||||
if (isDraftInstance) {
|
||||
setDraftModelsList?.(nextModels);
|
||||
filteredModels.forEach((m) => {
|
||||
if (!addedSet.has(m.name)) {
|
||||
clearCatalogOverride(m.name);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -731,6 +799,11 @@ export function useModelMutations({
|
||||
region: instance?.region ?? 'default',
|
||||
model_info: buildModelInfo(nextModels),
|
||||
});
|
||||
filteredModels.forEach((m) => {
|
||||
if (!addedSet.has(m.name)) {
|
||||
clearCatalogOverride(m.name);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -750,14 +823,20 @@ export function useModelMutations({
|
||||
interface UseModelEditArgs {
|
||||
providerName: string;
|
||||
instanceName: string;
|
||||
addedSet: Set<string>;
|
||||
isDraftInstance?: boolean;
|
||||
updateCatalogModel: (name: string, item: IProviderModelItem) => void;
|
||||
clearCatalogOverride: (name: string) => void;
|
||||
updateDraftModel?: (item: IProviderModelItem) => void;
|
||||
}
|
||||
|
||||
export function useModelEdit({
|
||||
providerName,
|
||||
instanceName,
|
||||
addedSet,
|
||||
isDraftInstance,
|
||||
updateCatalogModel,
|
||||
clearCatalogOverride,
|
||||
updateDraftModel,
|
||||
}: UseModelEditArgs) {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -807,10 +886,7 @@ export function useModelEdit({
|
||||
// Build the features array from `extra` booleans whose keys match
|
||||
// the standard feature (`is_tools`) or the provider-specific
|
||||
// whitelist. Only `true` values become selected switch-group entries.
|
||||
const featureKeySet = new Set<string>([
|
||||
'is_tools',
|
||||
...providerFeatureKeys,
|
||||
]);
|
||||
const featureKeySet = new Set<string>(['is_tools', ...providerFeatureKeys]);
|
||||
const features: string[] = [];
|
||||
const featureBooleans = new Set<string>();
|
||||
for (const [key, value] of Object.entries(extra)) {
|
||||
@@ -823,9 +899,7 @@ export function useModelEdit({
|
||||
}
|
||||
// Remaining extra fields (non-feature: element-format selects, etc.).
|
||||
const remainingExtra = Object.fromEntries(
|
||||
Object.entries(extra).filter(
|
||||
([k]) => !featureBooleans.has(k),
|
||||
),
|
||||
Object.entries(extra).filter(([k]) => !featureBooleans.has(k)),
|
||||
);
|
||||
return {
|
||||
name: editingModel.name,
|
||||
@@ -845,12 +919,18 @@ export function useModelEdit({
|
||||
if (!editingModel) return;
|
||||
const targetName = editingModel.name;
|
||||
|
||||
if (isDraftInstance && updateDraftModel) {
|
||||
if (isDraftInstance && updateDraftModel && addedSet.has(targetName)) {
|
||||
updateDraftModel(item);
|
||||
setEditingModel(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!addedSet.has(targetName)) {
|
||||
updateCatalogModel(targetName, item);
|
||||
setEditingModel(null);
|
||||
return;
|
||||
}
|
||||
|
||||
queryClient.setQueryData<IInstanceModel[]>(
|
||||
LlmKeys.instanceModels(providerName, instanceName),
|
||||
(prev) => {
|
||||
@@ -864,7 +944,10 @@ export function useModelEdit({
|
||||
max_tokens: item.max_tokens ?? 0,
|
||||
model_type: item.model_types ?? [],
|
||||
is_tools: hasToolFeature(item.features),
|
||||
extra: { is_tools: hasToolFeature(item.features), ...(item.extra ?? {}) },
|
||||
extra: {
|
||||
is_tools: hasToolFeature(item.features),
|
||||
...(item.extra ?? {}),
|
||||
},
|
||||
};
|
||||
return next;
|
||||
},
|
||||
@@ -878,6 +961,7 @@ export function useModelEdit({
|
||||
model_type: item.model_types ?? [],
|
||||
extra: { is_tools: hasToolFeature(item.features), ...(item.extra ?? {}) },
|
||||
});
|
||||
clearCatalogOverride(targetName);
|
||||
setEditingModel(null);
|
||||
};
|
||||
|
||||
|
||||
@@ -86,6 +86,8 @@ export function ModelsSection(props: ModelsSectionProps) {
|
||||
const {
|
||||
catalog,
|
||||
setCatalog,
|
||||
updateCatalogModel,
|
||||
clearCatalogOverride,
|
||||
manualListLoading,
|
||||
hasFetched,
|
||||
handleListModels,
|
||||
@@ -237,6 +239,7 @@ export function ModelsSection(props: ModelsSectionProps) {
|
||||
filteredModels,
|
||||
addedSet,
|
||||
setCatalog,
|
||||
clearCatalogOverride,
|
||||
addDraftModel,
|
||||
removeDraftModel,
|
||||
setDraftModelsList: setDraftModels,
|
||||
@@ -255,7 +258,10 @@ export function ModelsSection(props: ModelsSectionProps) {
|
||||
} = useModelEdit({
|
||||
providerName,
|
||||
instanceName,
|
||||
addedSet,
|
||||
isDraftInstance,
|
||||
updateCatalogModel,
|
||||
clearCatalogOverride,
|
||||
updateDraftModel,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user