diff --git a/client/example.go b/client/example.go
index dcae29dd1..c9ed35e86 100644
--- a/client/example.go
+++ b/client/example.go
@@ -71,7 +71,7 @@ func ExampleUsage() {
"source": "local",
"type": "document",
}
- knowledge, err := apiClient.CreateKnowledgeFromFile(context.Background(), createdKB.ID, filePath, metadata, nil, "", "")
+ knowledge, err := apiClient.CreateKnowledgeFromFile(context.Background(), createdKB.ID, filePath, metadata, nil, "", "", nil)
if err != nil {
fmt.Printf("Failed to upload knowledge file: %v\n", err)
} else {
diff --git a/client/knowledge.go b/client/knowledge.go
index 1398455ee..35455bbd2 100644
--- a/client/knowledge.go
+++ b/client/knowledge.go
@@ -83,6 +83,19 @@ var ErrDuplicateFile = errors.New("file already exists")
// ErrDuplicateURL is returned when attempting to create a knowledge entry with a URL that already exists
var ErrDuplicateURL = errors.New("URL already exists")
+// KnowledgeProcessOverrides stores per-upload parse config overrides sent as process_config.
+// When nil, the server uses the knowledge base defaults only.
+type KnowledgeProcessOverrides struct {
+ ParserEngineRules []ParserEngineRule `json:"parser_engine_rules,omitempty"`
+ ChunkingConfig *ChunkingConfig `json:"chunking_config,omitempty"`
+ EnableMultimodel *bool `json:"enable_multimodel,omitempty"`
+ VLMConfig *VLMConfig `json:"vlm_config,omitempty"`
+ ASRConfig *ASRConfig `json:"asr_config,omitempty"`
+ QuestionGenerationConfig *QuestionGenerationConfig `json:"question_generation_config,omitempty"`
+ GraphEnabled *bool `json:"graph_enabled,omitempty"`
+ ExtractConfig *ExtractConfig `json:"extract_config,omitempty"`
+}
+
// CreateKnowledgeFromFile creates a knowledge entry from a local file path
// Parameters:
// - knowledgeBaseID: The ID of the knowledge base
@@ -91,8 +104,10 @@ var ErrDuplicateURL = errors.New("URL already exists")
// - enableMultimodel: Optional flag to enable multimodal processing
// - customFileName: Optional custom file name (useful for folder uploads with path)
// - channel: Optional ingestion channel (e.g. "web", "api", "wechat"); empty defaults to "web"
+// - processConfig: Optional parse config overrides (serialized as process_config form field)
func (c *Client) CreateKnowledgeFromFile(ctx context.Context,
knowledgeBaseID string, filePath string, metadata map[string]string, enableMultimodel *bool, customFileName string, channel string,
+ processConfig *KnowledgeProcessOverrides,
) (*Knowledge, error) {
// Open the local file
file, err := os.Open(filePath)
@@ -159,6 +174,16 @@ func (c *Client) CreateKnowledgeFromFile(ctx context.Context,
}
}
+ if processConfig != nil {
+ processConfigBytes, err := json.Marshal(processConfig)
+ if err != nil {
+ return nil, fmt.Errorf("failed to serialize process_config: %w", err)
+ }
+ if err := writer.WriteField("process_config", string(processConfigBytes)); err != nil {
+ return nil, fmt.Errorf("failed to write process_config field: %w", err)
+ }
+ }
+
// Close the multipart writer
err = writer.Close()
if err != nil {
@@ -209,6 +234,8 @@ type CreateKnowledgeFromURLRequest struct {
TagID string `json:"tag_id,omitempty"`
// Channel identifies the ingestion channel (e.g. "web", "browser_extension", "api")
Channel string `json:"channel,omitempty"`
+ // ProcessConfig is optional per-upload parse config overrides (KnowledgeProcessOverrides).
+ ProcessConfig *KnowledgeProcessOverrides `json:"process_config,omitempty"`
}
// CreateKnowledgeFromURL creates a knowledge entry from a URL.
diff --git a/client/knowledgebase.go b/client/knowledgebase.go
index 85ad3762f..b2cfdf71e 100644
--- a/client/knowledgebase.go
+++ b/client/knowledgebase.go
@@ -108,6 +108,25 @@ type GraphRelation struct {
Type string `json:"type"`
}
+// ParserEngineRule maps a set of file types to a specific parser engine.
+type ParserEngineRule struct {
+ FileTypes []string `json:"file_types"`
+ Engine string `json:"engine"`
+}
+
+// QuestionGenerationConfig controls LLM-generated questions per chunk during parsing.
+type QuestionGenerationConfig struct {
+ Enabled bool `json:"enabled"`
+ QuestionCount int `json:"question_count"`
+}
+
+// ASRConfig represents automatic speech recognition settings for audio files.
+type ASRConfig struct {
+ Enabled bool `json:"enabled"`
+ ModelID string `json:"model_id"`
+ Language string `json:"language,omitempty"`
+}
+
// UnmarshalJSON keeps backward compatibility for legacy responses that still
// use `cos_config` instead of `storage_config`.
func (kb *KnowledgeBase) UnmarshalJSON(data []byte) error {
diff --git a/docs/api/knowledge.md b/docs/api/knowledge.md
index 9a72a8f5f..44d1251cd 100644
--- a/docs/api/knowledge.md
+++ b/docs/api/knowledge.md
@@ -52,9 +52,12 @@
| `fileName` | string | 否 | 自定义文件名,用于"文件夹上传"时保留相对路径(如 `docs/intro.md`) |
| `metadata` | string | 否 | JSON 字符串,会被反序列化为 `map[string]string` |
| `enable_multimodel` | string | 否 | `"true"` / `"false"`,是否启用图文多模态解析 |
+| `process_config` | string | 否 | JSON 字符串,批次解析配置覆盖(`KnowledgeProcessOverrides`);写入 `knowledge.metadata.process_overrides`。未传时行为与现网一致 |
| `tag_id` | string | 否 | 标签 ID;传 `__untagged__` 或空字符串表示未分类 |
| `channel` | string | 否 | 来源渠道标识(写入 `channel` 字段,默认 `web`) |
+`process_config` 可选字段包括:`parser_engine_rules`、`chunking_config`、`enable_multimodel`、`vlm_config`、`asr_config`、`question_generation_config`、`graph_enabled`、`extract_config`。若同时传 `enable_multimodel` 与 `process_config.enable_multimodel`,以 `process_config` 为准。
+
**请求**:
```curl
diff --git a/docs/docs.go b/docs/docs.go
index 17cc68d93..4b0634c8e 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -4775,6 +4775,12 @@ const docTemplate = `{
"description": "启用多模态处理",
"name": "enable_multimodel",
"in": "formData"
+ },
+ {
+ "type": "string",
+ "description": "处理配置JSON(KnowledgeProcessOverrides)",
+ "name": "process_config",
+ "in": "formData"
}
],
"responses": {
diff --git a/docs/swagger.json b/docs/swagger.json
index 56fb55df9..522618343 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -4768,6 +4768,12 @@
"description": "启用多模态处理",
"name": "enable_multimodel",
"in": "formData"
+ },
+ {
+ "type": "string",
+ "description": "处理配置JSON(KnowledgeProcessOverrides)",
+ "name": "process_config",
+ "in": "formData"
}
],
"responses": {
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index a916b2395..f7f7ab810 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -7746,6 +7746,10 @@ paths:
in: formData
name: enable_multimodel
type: boolean
+ - description: 处理配置JSON(KnowledgeProcessOverrides)
+ in: formData
+ name: process_config
+ type: string
produces:
- application/json
responses:
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 92d153085..709338817 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -4,6 +4,7 @@ import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { MessagePlugin, NotifyPlugin } from 'tdesign-vue-next'
import ManualKnowledgeEditor from '@/components/manual-knowledge-editor.vue'
+import UploadConfirmHost from '@/components/UploadConfirmHost.vue'
import { useAuthStore } from '@/stores/auth'
import { useSettingsStore } from '@/stores/settings'
import { getCurrentUser, userInfoFromApi } from '@/api/auth'
@@ -257,6 +258,7 @@ onUnmounted(() => {
+
diff --git a/frontend/src/api/knowledge-base/index.ts b/frontend/src/api/knowledge-base/index.ts
index a1d555d3f..9ffdad3fe 100644
--- a/frontend/src/api/knowledge-base/index.ts
+++ b/frontend/src/api/knowledge-base/index.ts
@@ -1,4 +1,5 @@
import { get, post, put, del, postUpload, getDown } from "../../utils/request";
+import type { KnowledgeProcessOverrides } from '@/types/knowledgeProcess';
// 知识库管理 API(列表、创建、获取、更新、删除、复制)
export function listKnowledgeBases(params?: {
@@ -160,23 +161,51 @@ export function togglePinKnowledgeBase(id: string) {
// 知识文件 API(基于具体知识库)
// data.tag_id: 可选,指定知识所属的分类ID
-export function uploadKnowledgeFile(kbId: string, data: { file: File; tag_id?: string; [key: string]: any } = { file: new File([], '') }, onProgress?: (progressEvent: any) => void) {
+export function uploadKnowledgeFile(
+ kbId: string,
+ data: {
+ file: File
+ tag_id?: string
+ fileName?: string
+ process_config?: KnowledgeProcessOverrides | string
+ [key: string]: any
+ } = { file: new File([], '') },
+ onProgress?: (progressEvent: any) => void,
+) {
const formData = new FormData();
Object.keys(data).forEach(key => {
- if (data[key] !== undefined) formData.append(key, data[key]);
+ const value = data[key];
+ if (value === undefined) return;
+ if (key === 'process_config' && value && typeof value !== 'string') {
+ formData.append(key, JSON.stringify(value));
+ } else {
+ formData.append(key, value);
+ }
});
return postUpload(`/api/v1/knowledge-bases/${kbId}/knowledge/file`, formData, onProgress);
}
// 从URL创建知识
// data.tag_id: 可选,指定知识所属的分类ID
-export function createKnowledgeFromURL(kbId: string, data: { url: string; enable_multimodel?: boolean; tag_id?: string }) {
+export function createKnowledgeFromURL(
+ kbId: string,
+ data: { url: string; enable_multimodel?: boolean; tag_id?: string; process_config?: KnowledgeProcessOverrides },
+) {
return post(`/api/v1/knowledge-bases/${kbId}/knowledge/url`, data);
}
// 手工创建知识
// data.tag_id: 可选,指定知识所属的分类ID
-export function createManualKnowledge(kbId: string, data: { title: string; content: string; status: string; tag_id?: string }) {
+export function createManualKnowledge(
+ kbId: string,
+ data: {
+ title: string
+ content: string
+ status: string
+ tag_id?: string
+ process_config?: KnowledgeProcessOverrides
+ },
+) {
return post(`/api/v1/knowledge-bases/${kbId}/knowledge/manual`, data);
}
@@ -215,7 +244,10 @@ export function getKnowledgeDetails(id: string, options?: { agent_id?: string })
return get(qs ? `/api/v1/knowledge/${id}?${qs}` : `/api/v1/knowledge/${id}`);
}
-export function updateManualKnowledge(id: string, data: { title: string; content: string; status: string }) {
+export function updateManualKnowledge(
+ id: string,
+ data: { title: string; content: string; status: string; process_config?: KnowledgeProcessOverrides },
+) {
return put(`/api/v1/knowledge/manual/${id}`, data);
}
diff --git a/frontend/src/components/ModelSelector.vue b/frontend/src/components/ModelSelector.vue
index aff8b61d6..402713d09 100644
--- a/frontend/src/components/ModelSelector.vue
+++ b/frontend/src/components/ModelSelector.vue
@@ -6,6 +6,7 @@
:placeholder="placeholderText"
:disabled="disabled"
:loading="loading"
+ :status="status"
filterable
style="width: 100%;"
>
@@ -51,13 +52,15 @@ interface Props {
selectedModelId?: string
disabled?: boolean
placeholder?: string
+ status?: 'default' | 'success' | 'warning' | 'error'
// 可选:外部传入的所有模型列表,如果提供则不调用API
allModels?: ModelConfig[]
}
const props = withDefaults(defineProps(), {
disabled: false,
- placeholder: ''
+ placeholder: '',
+ status: 'default',
})
const emit = defineEmits<{
diff --git a/frontend/src/components/UploadConfirmHost.vue b/frontend/src/components/UploadConfirmHost.vue
new file mode 100644
index 000000000..81724ab0f
--- /dev/null
+++ b/frontend/src/components/UploadConfirmHost.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/frontend/src/components/manual-knowledge-editor.vue b/frontend/src/components/manual-knowledge-editor.vue
index 69a120fcc..5da9628bb 100644
--- a/frontend/src/components/manual-knowledge-editor.vue
+++ b/frontend/src/components/manual-knowledge-editor.vue
@@ -3,7 +3,15 @@ import { ref, reactive, computed, watch, nextTick, onBeforeUnmount } from 'vue'
import { marked } from 'marked'
import { MessagePlugin } from 'tdesign-vue-next'
import { useUIStore } from '@/stores/ui'
-import { listKnowledgeBases, getKnowledgeDetails, createManualKnowledge, updateManualKnowledge } from '@/api/knowledge-base'
+import {
+ listKnowledgeBases,
+ getKnowledgeDetails,
+ getKnowledgeBaseById,
+ createManualKnowledge,
+ updateManualKnowledge,
+} from '@/api/knowledge-base'
+import { useUploadConfirmStore } from '@/stores/uploadConfirm'
+import type { KnowledgeProcessOverrides } from '@/types/knowledgeProcess'
import { sanitizeHTML, safeMarkdownToHTML } from '@/utils/security'
import { useI18n } from 'vue-i18n'
@@ -24,6 +32,7 @@ interface KnowledgeDetailResponse {
type ManualStatus = 'draft' | 'publish'
const uiStore = useUIStore()
+const uploadConfirmStore = useUploadConfirmStore()
const { t } = useI18n()
const visible = computed({
@@ -576,11 +585,53 @@ const handleSave = async (targetStatus: ManualStatus) => {
saving.value = true
savingAction.value = targetStatus
try {
- const payload: { title: string; content: string; status: string; tag_id?: string } = {
+ const tagIdToUpload = uiStore.selectedTagId !== '__untagged__' ? uiStore.selectedTagId : undefined
+ const payload: {
+ title: string
+ content: string
+ status: string
+ tag_id?: string
+ process_config?: KnowledgeProcessOverrides
+ } = {
title: form.title.trim(),
content: form.content,
status: targetStatus,
}
+ if (tagIdToUpload) {
+ payload.tag_id = tagIdToUpload
+ }
+
+ if (targetStatus === 'publish') {
+ let kbInfo: any
+ try {
+ const kbRes: any = await getKnowledgeBaseById(form.kbId)
+ kbInfo = kbRes?.data
+ } catch {
+ MessagePlugin.error(t('manualEditor.error.fetchDetailFailed'))
+ return
+ }
+ if (!kbInfo) {
+ MessagePlugin.error(t('manualEditor.error.fetchDetailFailed'))
+ return
+ }
+ try {
+ const confirmResult = await uploadConfirmStore.open({
+ mode: 'manual',
+ kbInfo,
+ manual: {
+ kbId: form.kbId,
+ knowledgeId: knowledgeId.value || undefined,
+ title: payload.title,
+ content: payload.content,
+ tagId: tagIdToUpload,
+ },
+ })
+ payload.process_config = confirmResult.processConfig
+ } catch {
+ return
+ }
+ }
+
let response: any
let knowledgeID = knowledgeId.value
let kbId = form.kbId
@@ -588,11 +639,6 @@ const handleSave = async (targetStatus: ManualStatus) => {
if (mode.value === 'edit' && knowledgeId.value) {
response = await updateManualKnowledge(knowledgeId.value, payload)
} else {
- // 创建新知识时,从 store 获取当前选中的分类ID
- const tagIdToUpload = uiStore.selectedTagId !== '__untagged__' ? uiStore.selectedTagId : undefined
- if (tagIdToUpload) {
- payload.tag_id = tagIdToUpload
- }
response = await createManualKnowledge(form.kbId, payload)
knowledgeID = response?.data?.id || knowledgeID
kbId = form.kbId
diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts
index 869c56846..0af500e3b 100755
--- a/frontend/src/i18n/locales/en-US.ts
+++ b/frontend/src/i18n/locales/en-US.ts
@@ -636,6 +636,74 @@ export default {
chunkLoadFailed: 'Failed to load chunks',
},
+ uploadConfirm: {
+ title: 'Confirm Upload',
+ fileList: 'Files to upload',
+ parseConfig: 'Parse settings',
+ configNav: 'Parse settings navigation',
+ tabOverview: 'Overview',
+ overviewTitle: 'Parse settings for this batch',
+ overviewDesc: 'Click any row to edit',
+ backToOverview: 'Back to overview',
+ summaryChunkOverlapShort: 'Overlap {overlap}',
+ summaryParentChildShort: 'Parent-child',
+ summaryParserMode: 'Mode',
+ summaryParserBuiltin: 'Built-in engine (default)',
+ summaryChunkSize: 'Chunk size',
+ summaryChunkOverlap: 'Overlap',
+ summaryStrategy: 'Strategy',
+ summaryStrategyDefault: 'Default',
+ summaryParentChild: 'Parent-child',
+ summaryParentChildOn: 'On (parent {parent} / child {child})',
+ summaryParentChildOff: 'Off',
+ summaryStatus: 'Status',
+ summaryModel: 'Model',
+ summaryQuestionCount: 'Questions per chunk',
+ summaryQuestionCountValue: '{count}',
+ summaryGraphTags: 'Relation types',
+ summaryGraphTagsValue: '{count}',
+ navChunkingSummary: 'Chunk {size}',
+ statusOn: 'On',
+ statusOff: 'Off',
+ notSet: 'Not set',
+ confirm: 'Upload and parse',
+ cancel: 'Cancel',
+ uploading: 'Uploading {current}/{total}',
+ tabParser: 'Parser',
+ tabChunking: 'Chunking',
+ tabMultimodal: 'Multimodal',
+ tabAsr: 'Audio',
+ tabQuestion: 'Question generation',
+ tabGraph: 'Knowledge graph',
+ noFiles: 'Keep at least one file to upload',
+ noItems: 'Add at least one file or URL',
+ urlItemLabel: 'URL',
+ urlAdded: 'URL added',
+ urlDuplicate: 'This URL is already in the list',
+ addUrl: 'Add',
+ vlmModelRequired: 'This batch includes images. Enable multimodal and select a VLM model.',
+ multimodalRequiredForImages: 'Off (batch includes images)',
+ asrModelRequired: 'This batch includes audio. Enable ASR and select a speech recognition model.',
+ asrRequiredForAudio: 'Off (batch includes audio)',
+ vlmModelSelectRequired: 'Multimodal is enabled. Please select a VLM model.',
+ asrModelSelectRequired: 'Speech recognition is enabled. Please select an ASR model.',
+ continueAdd: 'Add more',
+ addMoreFiles: 'Add more files',
+ addMoreFolder: 'Add more from folder',
+ filesAdded: 'Added {count} file(s)',
+ filesAllDuplicate: 'Selected files are already in the list',
+ titleUrl: 'Confirm URL import',
+ titleManual: 'Confirm online publish',
+ overviewDescUrl: 'The URL will be fetched and parsed after you confirm the settings',
+ overviewDescManual: 'The document will be published and indexed after you confirm the settings',
+ confirmUrl: 'Import and parse',
+ confirmManual: 'Publish and parse',
+ urlSource: 'Import source',
+ manualSource: 'Document to publish',
+ editUrl: 'Edit URL',
+ manualCharCount: '{count} characters',
+ },
+
knowledgeStages: {
title: 'Processing pipeline',
root: 'Knowledge processing',
diff --git a/frontend/src/i18n/locales/ko-KR.ts b/frontend/src/i18n/locales/ko-KR.ts
index b27a85442..376bce412 100755
--- a/frontend/src/i18n/locales/ko-KR.ts
+++ b/frontend/src/i18n/locales/ko-KR.ts
@@ -642,6 +642,73 @@ export default {
deleteSuccess: "지식이 성공적으로 삭제되었습니다!",
chunkLoadFailed: "청크 로드 실패",
},
+ uploadConfirm: {
+ title: "문서 업로드 확인",
+ fileList: "업로드할 파일",
+ parseConfig: "파싱 설정",
+ configNav: "파싱 설정 탐색",
+ tabOverview: "구성 개요",
+ overviewTitle: "이번 배치 파싱 설정",
+ overviewDesc: "항목을 클릭하여 설정을 수정하세요",
+ backToOverview: "구성 개요로 돌아가기",
+ summaryChunkOverlapShort: "겹침 {overlap}",
+ summaryParentChildShort: "부모-자식 청킹",
+ summaryParserMode: "모드",
+ summaryParserBuiltin: "내장 엔진(기본)",
+ summaryChunkSize: "청크 크기",
+ summaryChunkOverlap: "겹침",
+ summaryStrategy: "전략",
+ summaryStrategyDefault: "기본",
+ summaryParentChild: "부모-자식 청킹",
+ summaryParentChildOn: "켜짐(부모 {parent} / 자식 {child})",
+ summaryParentChildOff: "꺼짐",
+ summaryStatus: "상태",
+ summaryModel: "모델",
+ summaryQuestionCount: "청크당 질문 수",
+ summaryQuestionCountValue: "{count}개",
+ summaryGraphTags: "관계 유형",
+ summaryGraphTagsValue: "{count}개",
+ navChunkingSummary: "청크 {size}",
+ statusOn: "켜짐",
+ statusOff: "꺼짐",
+ notSet: "미설정",
+ confirm: "업로드 및 파싱 확인",
+ cancel: "취소",
+ uploading: "업로드 중 {current}/{total}",
+ tabParser: "파서",
+ tabChunking: "청킹",
+ tabMultimodal: "멀티모달",
+ tabAsr: "오디오",
+ tabQuestion: "질문 생성",
+ tabGraph: "지식 그래프",
+ noFiles: "업로드할 파일을 하나 이상 남겨 두세요",
+ noItems: "파일 또는 URL을 하나 이상 추가하세요",
+ urlItemLabel: "URL",
+ urlAdded: "URL이 추가되었습니다",
+ urlDuplicate: "이 URL은 이미 목록에 있습니다",
+ addUrl: "추가",
+ vlmModelRequired: "이 배치에 이미지가 포함되어 있습니다. 멀티모달을 활성화하고 VLM 모델을 선택하세요",
+ multimodalRequiredForImages: "꺼짐(이 배치에 이미지 포함)",
+ asrModelRequired: "이 배치에 오디오가 포함되어 있습니다. ASR을 활성화하고 음성 인식 모델을 선택하세요",
+ asrRequiredForAudio: "꺼짐(이 배치에 오디오 포함)",
+ vlmModelSelectRequired: "멀티모달이 활성화되었습니다. VLM 모델을 선택하세요",
+ asrModelSelectRequired: "음성 인식이 활성화되었습니다. ASR 모델을 선택하세요",
+ continueAdd: "계속 추가",
+ addMoreFiles: "파일 더 추가",
+ addMoreFolder: "폴더에서 더 추가",
+ filesAdded: "{count}개 파일이 추가되었습니다",
+ filesAllDuplicate: "선택한 파일이 이미 목록에 있습니다",
+ titleUrl: "URL 가져오기 확인",
+ titleManual: "온라인 편집 게시 확인",
+ overviewDescUrl: "설정 확인 후 URL을 가져와 파싱합니다",
+ overviewDescManual: "설정 확인 후 문서를 게시하고 인덱싱합니다",
+ confirmUrl: "가져오기 및 파싱",
+ confirmManual: "게시 및 파싱",
+ urlSource: "가져오기 소스",
+ manualSource: "게시할 문서",
+ editUrl: "URL 수정",
+ manualCharCount: "{count}자",
+ },
knowledgeStages: {
title: "처리 파이프라인",
root: "지식 처리",
diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts
index 41a592ebd..d13379b92 100755
--- a/frontend/src/i18n/locales/ru-RU.ts
+++ b/frontend/src/i18n/locales/ru-RU.ts
@@ -606,6 +606,73 @@ export default {
cancelParseSubmitted: 'Разбор остановлен',
cancelParseFailed: 'Не удалось остановить, попробуйте позже'
},
+ uploadConfirm: {
+ title: 'Подтверждение загрузки',
+ fileList: 'Файлы для загрузки',
+ parseConfig: 'Настройки разбора',
+ configNav: 'Навигация по настройкам разбора',
+ tabOverview: 'Обзор',
+ overviewTitle: 'Настройки разбора для этой партии',
+ overviewDesc: 'Нажмите на строку, чтобы изменить',
+ backToOverview: 'Вернуться к обзору',
+ summaryChunkOverlapShort: 'Перекрытие {overlap}',
+ summaryParentChildShort: 'Родительско-дочернее',
+ summaryParserMode: 'Режим',
+ summaryParserBuiltin: 'Встроенный движок (по умолчанию)',
+ summaryChunkSize: 'Размер чанка',
+ summaryChunkOverlap: 'Перекрытие',
+ summaryStrategy: 'Стратегия',
+ summaryStrategyDefault: 'По умолчанию',
+ summaryParentChild: 'Родительско-дочернее',
+ summaryParentChildOn: 'Вкл. (родитель {parent} / дочерний {child})',
+ summaryParentChildOff: 'Выкл.',
+ summaryStatus: 'Статус',
+ summaryModel: 'Модель',
+ summaryQuestionCount: 'Вопросов на чанк',
+ summaryQuestionCountValue: '{count}',
+ summaryGraphTags: 'Типы связей',
+ summaryGraphTagsValue: '{count}',
+ navChunkingSummary: 'Чанк {size}',
+ statusOn: 'Вкл.',
+ statusOff: 'Выкл.',
+ notSet: 'Не задано',
+ confirm: 'Загрузить и обработать',
+ cancel: 'Отмена',
+ uploading: 'Загрузка {current}/{total}',
+ tabParser: 'Парсер',
+ tabChunking: 'Разбиение',
+ tabMultimodal: 'Мультимодальность',
+ tabAsr: 'Аудио',
+ tabQuestion: 'Генерация вопросов',
+ tabGraph: 'Граф знаний',
+ noFiles: 'Оставьте хотя бы один файл для загрузки',
+ noItems: 'Добавьте хотя бы один файл или URL',
+ urlItemLabel: 'URL',
+ urlAdded: 'URL добавлен',
+ urlDuplicate: 'Этот URL уже в списке',
+ addUrl: 'Добавить',
+ vlmModelRequired: 'В партии есть изображения. Включите мультимодальность и выберите модель VLM.',
+ multimodalRequiredForImages: 'Выкл. (в партии есть изображения)',
+ asrModelRequired: 'В партии есть аудио. Включите ASR и выберите модель распознавания речи.',
+ asrRequiredForAudio: 'Выкл. (в партии есть аудио)',
+ vlmModelSelectRequired: 'Мультимодальность включена. Выберите модель VLM.',
+ asrModelSelectRequired: 'Распознавание речи включено. Выберите модель ASR.',
+ continueAdd: 'Добавить ещё',
+ addMoreFiles: 'Добавить файлы',
+ addMoreFolder: 'Добавить из папки',
+ filesAdded: 'Добавлено файлов: {count}',
+ filesAllDuplicate: 'Выбранные файлы уже в списке',
+ titleUrl: 'Подтверждение импорта URL',
+ titleManual: 'Подтверждение публикации',
+ overviewDescUrl: 'После подтверждения настроек URL будет загружен и обработан',
+ overviewDescManual: 'После подтверждения настроек документ будет опубликован и проиндексирован',
+ confirmUrl: 'Импортировать и обработать',
+ confirmManual: 'Опубликовать и обработать',
+ urlSource: 'Источник импорта',
+ manualSource: 'Документ для публикации',
+ editUrl: 'Изменить URL',
+ manualCharCount: '{count} символов',
+ },
knowledgeStages: {
title: 'Конвейер обработки',
root: 'Обработка знаний',
diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts
index f118a911a..d8d6b317b 100755
--- a/frontend/src/i18n/locales/zh-CN.ts
+++ b/frontend/src/i18n/locales/zh-CN.ts
@@ -639,6 +639,73 @@ export default {
deleteSuccess: "知识删除成功!",
chunkLoadFailed: "分块加载失败",
},
+ uploadConfirm: {
+ title: "上传文档确认",
+ fileList: "待上传文件",
+ parseConfig: "解析配置",
+ configNav: "解析配置导航",
+ tabOverview: "配置总览",
+ overviewTitle: "本批次解析配置",
+ overviewDesc: "点击任一项可修改配置",
+ backToOverview: "返回配置总览",
+ summaryChunkOverlapShort: "重叠 {overlap}",
+ summaryParentChildShort: "父子分块",
+ summaryParserMode: "模式",
+ summaryParserBuiltin: "内置引擎(默认)",
+ summaryChunkSize: "分块大小",
+ summaryChunkOverlap: "重叠",
+ summaryStrategy: "策略",
+ summaryStrategyDefault: "默认",
+ summaryParentChild: "父子分块",
+ summaryParentChildOn: "已开启(父 {parent} / 子 {child})",
+ summaryParentChildOff: "未开启",
+ summaryStatus: "状态",
+ summaryModel: "模型",
+ summaryQuestionCount: "每块问题数",
+ summaryQuestionCountValue: "{count} 个",
+ summaryGraphTags: "关系类型",
+ summaryGraphTagsValue: "{count} 个",
+ navChunkingSummary: "分块 {size}",
+ statusOn: "已开启",
+ statusOff: "未开启",
+ notSet: "未设置",
+ confirm: "确认上传并解析",
+ cancel: "取消",
+ uploading: "正在上传 {current}/{total}",
+ tabParser: "解析引擎",
+ tabChunking: "分块",
+ tabMultimodal: "多模态",
+ tabAsr: "音频",
+ tabQuestion: "问题生成",
+ tabGraph: "图谱",
+ noFiles: "请至少保留一个待上传文件",
+ noItems: "请至少添加一个文件或 URL",
+ urlItemLabel: "URL",
+ urlAdded: "已添加 URL",
+ urlDuplicate: "该 URL 已在列表中",
+ addUrl: "添加",
+ vlmModelRequired: "本批次包含图片,请启用多模态并选择 VLM 模型",
+ multimodalRequiredForImages: "未开启(本批次含图片)",
+ asrModelRequired: "本批次包含音频,请启用 ASR 并选择语音识别模型",
+ asrRequiredForAudio: "未开启(本批次含音频)",
+ vlmModelSelectRequired: "已启用多模态,请选择 VLM 模型",
+ asrModelSelectRequired: "已启用语音识别,请选择 ASR 模型",
+ continueAdd: "继续添加",
+ addMoreFiles: "继续添加文件",
+ addMoreFolder: "继续添加文件夹",
+ filesAdded: "已添加 {count} 个文件",
+ filesAllDuplicate: "所选文件已在列表中",
+ titleUrl: "URL 导入确认",
+ titleManual: "在线编辑发布确认",
+ overviewDescUrl: "确认解析配置后将抓取并解析该 URL",
+ overviewDescManual: "确认解析配置后将发布并索引该文档",
+ confirmUrl: "确认导入并解析",
+ confirmManual: "确认发布并解析",
+ urlSource: "导入来源",
+ manualSource: "待发布文档",
+ editUrl: "修改 URL",
+ manualCharCount: "{count} 个字符",
+ },
knowledgeStages: {
title: "处理流水线",
root: "知识处理",
diff --git a/frontend/src/stores/uploadConfirm.ts b/frontend/src/stores/uploadConfirm.ts
new file mode 100644
index 000000000..a65ba0475
--- /dev/null
+++ b/frontend/src/stores/uploadConfirm.ts
@@ -0,0 +1,85 @@
+import { defineStore } from 'pinia'
+import type { KnowledgeProcessOverrides } from '@/types/knowledgeProcess'
+
+export type UploadConfirmMode = 'file' | 'url' | 'manual'
+
+export interface UploadConfirmManualSource {
+ kbId: string
+ knowledgeId?: string
+ title: string
+ content: string
+ tagId?: string
+}
+
+export interface UploadConfirmResult {
+ processConfig: KnowledgeProcessOverrides
+ mode: UploadConfirmMode
+ files?: File[]
+ urls?: string[]
+ manual?: UploadConfirmManualSource
+}
+
+export interface OpenUploadConfirmOptions {
+ mode: UploadConfirmMode
+ kbInfo: any
+ files?: File[]
+ urls?: string[]
+ manual?: UploadConfirmManualSource
+ acceptFileTypes?: string
+ supportedFileTypes?: string[]
+}
+
+export const useUploadConfirmStore = defineStore('uploadConfirm', {
+ state: () => ({
+ visible: false,
+ mode: 'file' as UploadConfirmMode,
+ kbInfo: null as any,
+ files: [] as File[],
+ urls: [] as string[],
+ manual: null as UploadConfirmManualSource | null,
+ acceptFileTypes: '',
+ supportedFileTypes: [] as string[],
+ pendingResolve: null as ((value: UploadConfirmResult) => void) | null,
+ pendingReject: null as (() => void) | null,
+ }),
+
+ actions: {
+ open(options: OpenUploadConfirmOptions): Promise {
+ return new Promise((resolve, reject) => {
+ this.visible = true
+ this.mode = options.mode
+ this.kbInfo = options.kbInfo
+ this.files = options.files ? [...options.files] : []
+ this.urls = options.urls ? [...options.urls] : []
+ this.manual = options.manual || null
+ this.acceptFileTypes = options.acceptFileTypes || ''
+ this.supportedFileTypes = options.supportedFileTypes ? [...options.supportedFileTypes] : []
+ this.pendingResolve = resolve
+ this.pendingReject = reject
+ })
+ },
+
+ resolveConfirm(payload: UploadConfirmResult) {
+ this.pendingResolve?.(payload)
+ this.reset()
+ },
+
+ rejectConfirm() {
+ this.pendingReject?.()
+ this.reset()
+ },
+
+ reset() {
+ this.visible = false
+ this.mode = 'file'
+ this.kbInfo = null
+ this.files = []
+ this.urls = []
+ this.manual = null
+ this.acceptFileTypes = ''
+ this.supportedFileTypes = []
+ this.pendingResolve = null
+ this.pendingReject = null
+ },
+ },
+})
diff --git a/frontend/src/types/knowledgeProcess.ts b/frontend/src/types/knowledgeProcess.ts
new file mode 100644
index 000000000..40c6b73d0
--- /dev/null
+++ b/frontend/src/types/knowledgeProcess.ts
@@ -0,0 +1,66 @@
+/** Matches backend types.KnowledgeProcessOverrides (snake_case JSON). */
+
+export interface ParserEngineRule {
+ file_types: string[]
+ engine: string
+}
+
+export interface ChunkingConfigOverride {
+ chunk_size?: number
+ chunk_overlap?: number
+ separators?: string[]
+ enable_multimodal?: boolean
+ parser_engine_rules?: ParserEngineRule[]
+ enable_parent_child?: boolean
+ parent_chunk_size?: number
+ child_chunk_size?: number
+ strategy?: string
+ token_limit?: number
+ languages?: string[]
+}
+
+export interface VLMConfigOverride {
+ enabled?: boolean
+ model_id?: string
+}
+
+export interface ASRConfigOverride {
+ enabled?: boolean
+ model_id?: string
+ language?: string
+}
+
+export interface QuestionGenerationConfigOverride {
+ enabled?: boolean
+ question_count?: number
+}
+
+export interface GraphNodeOverride {
+ name: string
+ attributes?: string[]
+}
+
+export interface GraphRelationOverride {
+ node1: string
+ node2: string
+ type: string
+}
+
+export interface ExtractConfigOverride {
+ enabled?: boolean
+ text?: string
+ tags?: string[]
+ nodes?: GraphNodeOverride[]
+ relations?: GraphRelationOverride[]
+}
+
+export interface KnowledgeProcessOverrides {
+ parser_engine_rules?: ParserEngineRule[]
+ chunking_config?: ChunkingConfigOverride
+ enable_multimodel?: boolean
+ vlm_config?: VLMConfigOverride
+ asr_config?: ASRConfigOverride
+ question_generation_config?: QuestionGenerationConfigOverride
+ graph_enabled?: boolean
+ extract_config?: ExtractConfigOverride
+}
diff --git a/frontend/src/views/knowledge/KnowledgeBase.vue b/frontend/src/views/knowledge/KnowledgeBase.vue
index 3c03561e8..e4e399381 100644
--- a/frontend/src/views/knowledge/KnowledgeBase.vue
+++ b/frontend/src/views/knowledge/KnowledgeBase.vue
@@ -1,6 +1,6 @@
+
+
diff --git a/frontend/src/views/knowledge/components/UploadConfirmDialog.vue b/frontend/src/views/knowledge/components/UploadConfirmDialog.vue
new file mode 100644
index 000000000..dade7bc87
--- /dev/null
+++ b/frontend/src/views/knowledge/components/UploadConfirmDialog.vue
@@ -0,0 +1,1242 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dialogTitle }}
+ {{ dialogDesc }}
+
+
+
+ {{ currentSectionTitle }}
+ {{ currentSectionDesc }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('knowledgeEditor.advanced.multimodal.description') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('uploadConfirm.vlmModelSelectRequired') }}
+
+
+
+
+
+
+
+
+
+
+
{{ t('knowledgeEditor.asr.desc') }}
+
+
+
+
+
+
+
+
+
+
+
{ uiState.asrConfig.modelId = val }"
+ @add-model="handleAddASRModel"
+ />
+
+ {{ t('uploadConfirm.asrModelSelectRequired') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/views/knowledge/settings/GraphSettings.vue b/frontend/src/views/knowledge/settings/GraphSettings.vue
index 041bab544..36b17b4a9 100644
--- a/frontend/src/views/knowledge/settings/GraphSettings.vue
+++ b/frontend/src/views/knowledge/settings/GraphSettings.vue
@@ -1,10 +1,9 @@
-
-