Files
ragflow/web/src/pages/user-setting/compilation-templates/edit-template/utils.ts
buua436 d291e4641d fix: improve pipeline compilation, template updates, and document resets (#16815)
### What problem does this PR solve?

- Clear stale pipeline IDs and generated data when updating documents
without `pipeline_id`.
- Support tree compilation results in pipeline workflows.
- Update compilation templates in place while preserving existing
template IDs.
- Improve duplicate-template validation messages.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)

Co-authored-by: Jin Hai <haijin.chn@gmail.com>
2026-07-10 21:17:19 +08:00

264 lines
7.1 KiB
TypeScript

import { isEqual } from 'lodash';
import {
ICompilationTemplate,
ICompilationTemplateBuiltin,
ICompilationTemplateGroup,
ICompilationTemplateRaptorConfig,
ICompilationTemplateSection,
} from '@/interfaces/database/compilation-template';
import { ICompilationTemplateConfigRequest } from '@/interfaces/request/compilation-template';
import { CompilationTemplateKind } from '@/constants/compilation';
import { FormSchemaType, TemplateSchemaType } from './schema';
export const DefaultFieldKeys = ['type', 'description', 'rule'];
export const FieldKeyOrders = [
DefaultFieldKeys,
['statement', 'subject'],
['definition_excerpt', 'term'],
];
export const getFieldKeyOrder = (keys: string[]): string[] => {
const sortedKeys = [...keys].sort();
return (
FieldKeyOrders.find((order) => isEqual([...order].sort(), sortedKeys)) ??
keys
);
};
export const DefaultTemplateValues: TemplateSchemaType = {
id: undefined,
name: '',
description: '',
llm_id: '',
kind: '',
config: {
kind: '',
llm_id: '',
global_rules: '',
example: '',
instruction: '',
page_example: '',
},
};
export const DefaultValues: FormSchemaType = {
name: '',
description: '',
avatar: '',
templates: [DefaultTemplateValues],
};
export const isConfigMetaKey = (key: string) =>
[
'kind',
'llm_id',
'global_rules',
'example',
'instruction',
'page_example',
].includes(key);
export const createEmptyField = (keys: string[]) =>
Object.fromEntries(keys.map((key) => [key, '']));
export const normalizeSection = (
section?: ICompilationTemplateSection,
): ICompilationTemplateSection => {
const fields = section?.fields ?? [];
return {
description: section?.description ?? '',
fields:
fields.length > 0
? fields.map((field) =>
Object.fromEntries(
Object.entries(field).map(([key, value]) => [key, value ?? '']),
),
)
: [createEmptyField(DefaultFieldKeys)],
};
};
export const buildConfigFromBuiltin = (
builtinTemplate: ICompilationTemplateBuiltin,
kind: string,
llmId: string,
): TemplateSchemaType['config'] => {
const sections: TemplateSchemaType['config'] = {
kind,
llm_id: llmId,
global_rules:
typeof builtinTemplate.config?.global_rules === 'string'
? builtinTemplate.config.global_rules
: '',
example:
typeof builtinTemplate.config?.example === 'string'
? builtinTemplate.config.example
: '',
};
if (kind === CompilationTemplateKind.Tree) {
const builtinRaptor: ICompilationTemplateRaptorConfig =
builtinTemplate.config?.raptor ?? {};
return {
...sections,
raptor: {
prompt: builtinRaptor.prompt ?? '',
max_token: builtinRaptor.max_token ?? 512,
threshold: builtinRaptor.threshold ?? 0.1,
rechunk: builtinRaptor.rechunk ?? false,
},
};
}
Object.entries(builtinTemplate.config ?? {}).forEach(([key, value]) => {
if (isConfigMetaKey(key)) return;
sections[key] = normalizeSection(
value as ICompilationTemplateSection,
) as TemplateSchemaType['config'][string];
});
return sections;
};
export const transformDetailToForm = (
detail: ICompilationTemplate,
): TemplateSchemaType => {
const config = detail.config ?? {};
const base: TemplateSchemaType['config'] = {
kind: config.kind ?? '',
llm_id: config.llm_id ?? '',
global_rules: config.global_rules ?? '',
example: typeof config.example === 'string' ? config.example : '',
};
if (detail.kind === CompilationTemplateKind.Tree) {
const raptor: ICompilationTemplateRaptorConfig = config.raptor ?? {};
return {
id: detail.id,
name: detail.name ?? '',
description: detail.description ?? '',
llm_id: config.llm_id ?? '',
kind: detail.kind ?? '',
config: {
...base,
raptor: {
prompt: raptor.prompt ?? '',
max_token: raptor.max_token ?? 512,
threshold: raptor.threshold ?? 0.1,
rechunk: raptor.rechunk ?? false,
},
},
};
}
Object.entries(config).forEach(([key, value]) => {
if (isConfigMetaKey(key)) return;
base[key] = normalizeSection(
value as ICompilationTemplateSection,
) as TemplateSchemaType['config'][string];
});
return {
id: detail.id,
name: detail.name ?? '',
description: detail.description ?? '',
llm_id: config.llm_id ?? '',
kind: detail.kind ?? '',
config: base,
};
};
export const transformGroupDetailToForm = (
detail: ICompilationTemplateGroup,
): FormSchemaType => {
const templates = (detail.templates ?? []).map((template) =>
transformDetailToForm(template),
);
return {
name: detail.name ?? '',
description: detail.description ?? '',
avatar: detail.avatar ?? '',
templates: templates.length > 0 ? templates : [DefaultTemplateValues],
};
};
export const transformTemplateToPayload = (template: TemplateSchemaType) => {
const config: ICompilationTemplateConfigRequest = {
kind: template.kind,
llm_id: template.llm_id,
};
Object.entries(template.config).forEach(([key, value]) => {
if (key === 'kind' || key === 'llm_id') return;
if (key === 'instruction' || key === 'page_example') return;
if (isConfigMetaKey(key)) {
if (typeof value === 'string') config[key] = value;
} else {
config[key] = value as ICompilationTemplateConfigRequest[string];
}
});
if (template.kind === CompilationTemplateKind.Artifacts) {
const instruction = String(template.config.instruction ?? '').trim();
const pageExample = String(template.config.page_example ?? '').trim();
config.example = [instruction, pageExample].filter(Boolean).join('\n\n');
}
return {
id: template.id,
name: template.name,
description: template.description,
kind: template.kind,
config,
};
};
export const transformFormToPayload = (values: FormSchemaType) => {
return {
name: values.name,
description: values.description,
avatar: values.avatar || undefined,
templates: values.templates.map((template) =>
transformTemplateToPayload(template),
),
};
};
export const SectionTitleKeyMap: Record<string, string> = {
entity: 'setting.entitySpecification',
relation: 'setting.relationSpecification',
concept: 'setting.conceptSpecification',
claim: 'setting.claimSpecification',
};
export const SectionPriority = ['entity', 'relation'];
export const sortSectionNames = (names: string[]): string[] => {
const priority = SectionPriority.filter((name) => names.includes(name));
const rest = names.filter((name) => !SectionPriority.includes(name));
return [...priority, ...rest];
};
export const FieldLabelKeyMap: Record<string, string> = {
type: 'setting.fieldType',
description: 'setting.fieldDescription',
rule: 'setting.fieldRule',
};
export const getTypeOptionsFromBuiltinSection = (
builtinSection?: ICompilationTemplateSection,
) => {
const typeSet = new Set<string>();
builtinSection?.fields?.forEach((field) => {
if (field.type) typeSet.add(field.type);
});
return Array.from(typeSet)
.sort()
.map((value) => ({ label: value, value }));
};