mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-30 20:49:21 +08:00
fix: restore provider-specific fields on form echo via echoTransform (#17550)
This commit is contained in:
@@ -160,14 +160,20 @@ export function useProviderBaseUrlOptions(providerName: string) {
|
||||
* - Draft: empty form, with `base_url` pre-filled from the
|
||||
* provider's `default` URL when available.
|
||||
* - Saved: prefer `instanceDetails` (which carries api_key / base_url);
|
||||
* normalise api_key into the bare key plus any nested credential
|
||||
* fields (see {@link unwrapApiKey}).
|
||||
* when `echoTransform` is supplied (provider-specific field mapping,
|
||||
* e.g. Google Cloud's top-level `google_project_id` or XunFei Spark's
|
||||
* nested `spark_api_password`), use it to reverse the corresponding
|
||||
* `submitTransform` so the provider-specific credential fields
|
||||
* pre-fill. Otherwise fall back to the generic `unwrapApiKey` path
|
||||
* which lifts the bare `api_key` plus `API_KEY_NESTED_FIELDS`
|
||||
* (`group_id` / `api_version` / `provider_order`).
|
||||
*/
|
||||
export function useProviderInitialValues(
|
||||
instance: IProviderInstance,
|
||||
instanceDetails: IProviderInstance | undefined,
|
||||
isDraft: boolean,
|
||||
baseUrlOptions: SelectOption[] | undefined,
|
||||
echoTransform?: (instance: Record<string, any>) => Record<string, any>,
|
||||
) {
|
||||
return useMemo(() => {
|
||||
const defaultBaseUrl = pickDefaultUrl(baseUrlOptions);
|
||||
@@ -187,12 +193,19 @@ export function useProviderInitialValues(
|
||||
const values: Record<string, any> = {
|
||||
instance_name: merged.instance_name,
|
||||
};
|
||||
// api_key may come back as a JSON string, an already-parsed object,
|
||||
// or a plain bare key (see `unwrapApiKey`). Normalise it so the
|
||||
// api_key text field shows the bare key and the nested credential
|
||||
// fields (MiniMax group_id, Azure api_version, OpenRouter
|
||||
// provider_order) pre-fill their own form inputs.
|
||||
if (merged.api_key) {
|
||||
if (echoTransform) {
|
||||
// Provider-specific echo: reverse the `submitTransform` mapping so
|
||||
// credential fields that the backend persists either at the top
|
||||
// level (Google Cloud, Tencent Cloud, Fish Audio) or nested inside
|
||||
// `api_key` (XunFei Spark, Baidu YiYan, OpenDataLoader, PaddleOCR,
|
||||
// MinerU) are restored into their own form inputs.
|
||||
Object.assign(values, echoTransform(merged));
|
||||
} else if (merged.api_key) {
|
||||
// api_key may come back as a JSON string, an already-parsed object,
|
||||
// or a plain bare key (see `unwrapApiKey`). Normalise it so the
|
||||
// api_key text field shows the bare key and the nested credential
|
||||
// fields (MiniMax group_id, Azure api_version, OpenRouter
|
||||
// provider_order) pre-fill their own form inputs.
|
||||
const { apiKey, nested } = unwrapApiKey(merged.api_key);
|
||||
values.api_key = apiKey;
|
||||
Object.assign(values, nested);
|
||||
@@ -215,7 +228,7 @@ export function useProviderInitialValues(
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}, [instance, instanceDetails, isDraft, baseUrlOptions]);
|
||||
}, [instance, instanceDetails, isDraft, baseUrlOptions, echoTransform]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -138,6 +138,7 @@ const GenericProviderInstanceCard = forwardRef<
|
||||
instanceDetails,
|
||||
isDraft,
|
||||
baseUrlOptions,
|
||||
providerConfig.echoTransform,
|
||||
);
|
||||
const { formFields, formDefaultValues } = useFormFields(
|
||||
providerName,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { FormFieldType } from '@/components/dynamic-form';
|
||||
import { LLMFactory } from '@/constants/llm';
|
||||
import type { ProviderConfig } from '../types';
|
||||
import { parseApiKeyAsObject } from './utils';
|
||||
|
||||
/**
|
||||
* Factory configuration mapping table
|
||||
@@ -180,6 +181,11 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
google_service_account_key: values.google_service_account_key,
|
||||
model_info: [],
|
||||
}),
|
||||
echoTransform: (instance) => ({
|
||||
google_project_id: instance.google_project_id ?? '',
|
||||
google_region: instance.google_region ?? '',
|
||||
google_service_account_key: instance.google_service_account_key ?? '',
|
||||
}),
|
||||
},
|
||||
|
||||
// ============ Tencent Cloud ============
|
||||
@@ -231,6 +237,10 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
TencentCloud_sk: values.TencentCloud_sk,
|
||||
model_info: [],
|
||||
}),
|
||||
echoTransform: (instance) => ({
|
||||
TencentCloud_sid: instance.TencentCloud_sid ?? '',
|
||||
TencentCloud_sk: instance.TencentCloud_sk ?? '',
|
||||
}),
|
||||
},
|
||||
|
||||
// ============ XunFei Spark ============
|
||||
@@ -304,6 +314,15 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
},
|
||||
model_info: [],
|
||||
}),
|
||||
echoTransform: (instance) => {
|
||||
const obj = parseApiKeyAsObject(instance.api_key) ?? {};
|
||||
return {
|
||||
spark_api_password: obj.spark_api_password ?? '',
|
||||
spark_app_id: obj.spark_app_id ?? '',
|
||||
spark_api_secret: obj.spark_api_secret ?? '',
|
||||
spark_api_key: obj.spark_api_key ?? '',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// ============ Baidu YiYan ============
|
||||
@@ -355,6 +374,13 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
},
|
||||
model_info: [],
|
||||
}),
|
||||
echoTransform: (instance) => {
|
||||
const obj = parseApiKeyAsObject(instance.api_key) ?? {};
|
||||
return {
|
||||
yiyan_ak: obj.yiyan_ak ?? '',
|
||||
yiyan_sk: obj.yiyan_sk ?? '',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// ============ Fish Audio ============
|
||||
@@ -406,6 +432,10 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
fish_audio_refid: values.fish_audio_refid,
|
||||
model_info: [],
|
||||
}),
|
||||
echoTransform: (instance) => ({
|
||||
fish_audio_ak: instance.fish_audio_ak ?? '',
|
||||
fish_audio_refid: instance.fish_audio_refid ?? '',
|
||||
}),
|
||||
},
|
||||
|
||||
// ============ OpenDataLoader ============
|
||||
@@ -468,6 +498,13 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
model_info: [],
|
||||
};
|
||||
},
|
||||
echoTransform: (instance) => {
|
||||
const obj = parseApiKeyAsObject(instance.api_key) ?? {};
|
||||
return {
|
||||
opendataloader_apiserver: obj.opendataloader_apiserver ?? '',
|
||||
opendataloader_api_key: obj.opendataloader_api_key ?? '',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// ============ PaddleOCR ============
|
||||
@@ -547,6 +584,14 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
model_info: [],
|
||||
};
|
||||
},
|
||||
echoTransform: (instance) => {
|
||||
const obj = parseApiKeyAsObject(instance.api_key) ?? {};
|
||||
return {
|
||||
paddleocr_api_url: obj.paddleocr_api_url ?? '',
|
||||
paddleocr_access_token: obj.paddleocr_access_token ?? '',
|
||||
paddleocr_algorithm: obj.paddleocr_algorithm ?? 'PaddleOCR-VL',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// ============ MinerU ============
|
||||
@@ -642,5 +687,19 @@ export const ProviderConfigMap: Record<string, ProviderConfig> = {
|
||||
model_info: [],
|
||||
};
|
||||
},
|
||||
echoTransform: (instance) => {
|
||||
const obj = parseApiKeyAsObject(instance.api_key) ?? {};
|
||||
const rawDelete = obj.mineru_delete_output;
|
||||
return {
|
||||
mineru_apiserver: obj.mineru_apiserver ?? '',
|
||||
mineru_output_dir: obj.mineru_output_dir ?? '',
|
||||
mineru_backend: obj.mineru_backend ?? 'pipeline',
|
||||
mineru_server_url: obj.mineru_server_url ?? '',
|
||||
mineru_delete_output:
|
||||
rawDelete === undefined
|
||||
? true
|
||||
: rawDelete === '1' || rawDelete === true,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -41,6 +41,33 @@ export function applyChatToImage2Text(
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a backend `api_key` value (which may be a JSON string, an
|
||||
* already-parsed object, or a bare string) into the underlying object.
|
||||
* Used by `echoTransform` implementations to lift provider-specific
|
||||
* credential fields that the backend persists nested inside `api_key`
|
||||
* (e.g. XunFei Spark's `spark_api_password`, PaddleOCR's nested config,
|
||||
* MinerU's full config bundle).
|
||||
*
|
||||
* Returns `undefined` for bare strings or unparseable input so callers
|
||||
* can fall back to empty values via `??`.
|
||||
*/
|
||||
export function parseApiKeyAsObject(
|
||||
raw: unknown,
|
||||
): Record<string, any> | undefined {
|
||||
let obj: any = raw;
|
||||
if (typeof raw === 'string') {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed.startsWith('{')) return undefined;
|
||||
try {
|
||||
obj = JSON.parse(trimmed);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return obj && typeof obj === 'object' ? obj : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the IModelInfo[] payload for verify/submit from the form values.
|
||||
*
|
||||
|
||||
@@ -132,6 +132,19 @@ export interface ProviderConfig {
|
||||
* `modelInfo` is assembled from `values` by the transform itself (same rules as verifyTransform).
|
||||
*/
|
||||
submitTransform?: (values: Record<string, any>) => Record<string, any>;
|
||||
/**
|
||||
* Inverse of `submitTransform`: maps the raw merged backend instance
|
||||
* (as returned by `showProviderInstance`) back into form values so the
|
||||
* form pre-fills when editing a saved instance. Returns ONLY the
|
||||
* provider-specific credential fields (e.g. `google_project_id`,
|
||||
* `spark_api_password`); the hook layers `instance_name` and
|
||||
* `base_url` fallback on top.
|
||||
*
|
||||
* When absent, the generic echo path runs: `unwrapApiKey` lifts the
|
||||
* bare `api_key` plus `API_KEY_NESTED_FIELDS` (`group_id` /
|
||||
* `api_version` / `provider_order`).
|
||||
*/
|
||||
echoTransform?: (instance: Record<string, any>) => Record<string, any>;
|
||||
/**
|
||||
* Optional link at the bottom of the modal
|
||||
* e.g. the official documentation link for Ollama-family providers
|
||||
|
||||
Reference in New Issue
Block a user