dsh-data-domain-knowledge 0.1.1-alpha.2 → 0.1.1-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -16
- package/lib/client.js +1042 -2097
- package/lib/contract-refs.d.ts +0 -1
- package/lib/doc-page.js +1 -1
- package/lib/pages.js +2489 -1060
- package/lib/types/agent-behavior-rules.d.ts.map +1 -1
- package/lib/types/agent-behavior-rules.js +3 -2
- package/lib/types/agent-behavior-rules.js.map +1 -1
- package/lib/types/axis-contract.js +1 -1
- package/lib/types/axis-contract.js.map +1 -1
- package/lib/types/card-organize.d.ts +2 -1
- package/lib/types/card-organize.d.ts.map +1 -1
- package/lib/types/card-organize.js +5 -3
- package/lib/types/card-organize.js.map +1 -1
- package/lib/types/config-schema.d.ts +1 -1
- package/lib/types/config-schema.js +1 -1
- package/lib/types/convert/host.d.ts +2 -3
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +11 -2
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +14 -20
- package/lib/types/convert/ingest.d.ts.map +1 -1
- package/lib/types/convert/ingest.js +8 -5
- package/lib/types/convert/ingest.js.map +1 -1
- package/lib/types/diagnosis/store.d.ts +2 -2
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +10 -2
- package/lib/types/diagnosis/store.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +1 -1
- package/lib/types/diagnosis/types.js +1 -1
- package/lib/types/extract-model.d.ts +3 -0
- package/lib/types/extract-model.d.ts.map +1 -1
- package/lib/types/extract-model.js.map +1 -1
- package/lib/types/images.js +1 -1
- package/lib/types/index.d.ts +0 -4
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +99 -91
- package/lib/types/index.js.map +1 -1
- package/lib/types/knowledge-bases.d.ts +3 -0
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +22 -2
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/knowledge-contract.d.ts +6 -0
- package/lib/types/knowledge-contract.d.ts.map +1 -1
- package/lib/types/knowledge-contract.js +6 -2
- package/lib/types/knowledge-contract.js.map +1 -1
- package/lib/types/layout.d.ts +9 -26
- package/lib/types/layout.d.ts.map +1 -1
- package/lib/types/layout.js +10 -28
- package/lib/types/layout.js.map +1 -1
- package/lib/types/materialize-snapshot.d.ts +5 -0
- package/lib/types/materialize-snapshot.d.ts.map +1 -1
- package/lib/types/migrate.d.ts +3 -1
- package/lib/types/migrate.d.ts.map +1 -1
- package/lib/types/migrate.js +3 -1
- package/lib/types/migrate.js.map +1 -1
- package/lib/types/model-text.d.ts +7 -0
- package/lib/types/model-text.d.ts.map +1 -1
- package/lib/types/model-text.js +31 -9
- package/lib/types/model-text.js.map +1 -1
- package/lib/types/organize-contract.d.ts.map +1 -1
- package/lib/types/organize-contract.js +2 -1
- package/lib/types/organize-contract.js.map +1 -1
- package/lib/types/pipeline/engine.d.ts +28 -4
- package/lib/types/pipeline/engine.d.ts.map +1 -1
- package/lib/types/pipeline/engine.js +68 -15
- package/lib/types/pipeline/engine.js.map +1 -1
- package/lib/types/pipeline/progress.d.ts +12 -0
- package/lib/types/pipeline/progress.d.ts.map +1 -0
- package/lib/types/pipeline/progress.js +37 -0
- package/lib/types/pipeline/progress.js.map +1 -0
- package/lib/types/pipeline/resumable-extract.d.ts +45 -0
- package/lib/types/pipeline/resumable-extract.d.ts.map +1 -0
- package/lib/types/pipeline/resumable-extract.js +262 -0
- package/lib/types/pipeline/resumable-extract.js.map +1 -0
- package/lib/types/pipeline/store.d.ts +8 -2
- package/lib/types/pipeline/store.d.ts.map +1 -1
- package/lib/types/pipeline/store.js +27 -5
- package/lib/types/pipeline/store.js.map +1 -1
- package/lib/types/pipeline/types.d.ts +56 -0
- package/lib/types/pipeline/types.d.ts.map +1 -1
- package/lib/types/pipeline/types.js +18 -1
- package/lib/types/pipeline/types.js.map +1 -1
- package/lib/types/present.d.ts +1 -1
- package/lib/types/present.js +1 -1
- package/lib/types/preset-ids.d.ts +0 -1
- package/lib/types/preset-ids.d.ts.map +1 -1
- package/lib/types/preset-ids.js +0 -3
- package/lib/types/preset-ids.js.map +1 -1
- package/lib/types/query-analysis.d.ts +0 -5
- package/lib/types/query-analysis.d.ts.map +1 -1
- package/lib/types/query-analysis.js +0 -16
- package/lib/types/query-analysis.js.map +1 -1
- package/lib/types/recall-delivery.d.ts +36 -0
- package/lib/types/recall-delivery.d.ts.map +1 -0
- package/lib/types/recall-delivery.js +16 -0
- package/lib/types/recall-delivery.js.map +1 -0
- package/lib/types/retrieval/scope.d.ts +17 -0
- package/lib/types/retrieval/scope.d.ts.map +1 -0
- package/lib/types/retrieval/scope.js +11 -0
- package/lib/types/retrieval/scope.js.map +1 -0
- package/lib/types/runtime.d.ts +75 -14
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +454 -198
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/settings-volatile.d.ts +0 -7
- package/lib/types/settings-volatile.d.ts.map +1 -1
- package/lib/types/settings-volatile.js +0 -7
- package/lib/types/settings-volatile.js.map +1 -1
- package/lib/types/source-identity.d.ts +34 -0
- package/lib/types/source-identity.d.ts.map +1 -0
- package/lib/types/source-identity.js +85 -0
- package/lib/types/source-identity.js.map +1 -0
- package/lib/types/source-read.d.ts +18 -0
- package/lib/types/source-read.d.ts.map +1 -0
- package/lib/types/source-read.js +49 -0
- package/lib/types/source-read.js.map +1 -0
- package/lib/types/store/axis-references.d.ts.map +1 -1
- package/lib/types/store/axis-references.js +5 -3
- package/lib/types/store/axis-references.js.map +1 -1
- package/lib/types/store/knowledge-db.d.ts +5 -29
- package/lib/types/store/knowledge-db.d.ts.map +1 -1
- package/lib/types/store/knowledge-db.js +57 -2
- package/lib/types/store/knowledge-db.js.map +1 -1
- package/lib/types/store/source-identity.d.ts +8 -0
- package/lib/types/store/source-identity.d.ts.map +1 -0
- package/lib/types/store/source-identity.js +56 -0
- package/lib/types/store/source-identity.js.map +1 -0
- package/lib/types/types.d.ts +46 -5
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +6 -3
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-availability.d.ts +11 -0
- package/lib/types/wiki-availability.d.ts.map +1 -0
- package/lib/types/wiki-availability.js +39 -0
- package/lib/types/wiki-availability.js.map +1 -0
- package/lib/types/wiki-finalize.d.ts +4 -2
- package/lib/types/wiki-finalize.d.ts.map +1 -1
- package/lib/types/wiki-finalize.js +9 -27
- package/lib/types/wiki-finalize.js.map +1 -1
- package/lib/types/wiki-links.d.ts +35 -0
- package/lib/types/wiki-links.d.ts.map +1 -0
- package/lib/types/wiki-links.js +129 -0
- package/lib/types/wiki-links.js.map +1 -0
- package/lib/types/wiki-trial-types.d.ts +3 -0
- package/lib/types/wiki-trial-types.d.ts.map +1 -1
- package/lib/types/wiki-trial.d.ts +3 -3
- package/lib/types/wiki-trial.d.ts.map +1 -1
- package/lib/types/wiki-trial.js +14 -3
- package/lib/types/wiki-trial.js.map +1 -1
- package/package.json +16 -16
- package/scripts/doctor.mjs +0 -7
- package/scripts/setup-embedding.sh +0 -0
- package/src/agent-behavior-rules.ts +3 -2
- package/src/axis-contract.ts +1 -1
- package/src/card-organize.ts +5 -3
- package/src/client/AttributionEventGuide.tsx +3 -3
- package/src/client/AxisMatrix.tsx +86 -19
- package/src/client/AxisRegistrySection.tsx +1 -1
- package/src/client/CompileSettingsView.tsx +473 -0
- package/src/client/DirectoryPicker.tsx +0 -2
- package/src/client/ExtractionTasksPage.tsx +557 -423
- package/src/client/GuideArchitectureCanvas.tsx +5 -12
- package/src/client/ImportFilesDialog.tsx +1 -0
- package/src/client/InfoHint.tsx +1 -1
- package/src/client/KnowledgeBaseTab.tsx +141 -148
- package/src/client/KnowledgePanel.tsx +16 -9
- package/src/client/OperationLogTab.tsx +1 -1
- package/src/client/PipelineRunView.tsx +173 -0
- package/src/client/RecallPanel.tsx +5 -16
- package/src/client/TabGuide.tsx +3 -7
- package/src/client/WikiFlowPanel.tsx +10 -11
- package/src/client/arch/NodeLogicPanel.tsx +3 -3
- package/src/client/arch/model.ts +5 -113
- package/src/client/architecture.css +1 -1
- package/src/client/architecture.tsx +13 -27
- package/src/client/attribution/AttributionTab.tsx +141 -10
- package/src/client/pages.css +305 -180
- package/src/client/pages.tsx +7 -12
- package/src/client/styles.css +15 -96
- package/src/config-schema.ts +1 -1
- package/src/convert/host.ts +13 -5
- package/src/convert/ingest.ts +20 -7
- package/src/diagnosis/store.ts +7 -2
- package/src/diagnosis/types.ts +1 -1
- package/src/extract-model.ts +3 -0
- package/src/images.ts +1 -1
- package/src/index.ts +92 -97
- package/src/knowledge-bases.ts +23 -3
- package/src/knowledge-contract.ts +7 -2
- package/src/layout.ts +10 -32
- package/src/materialize-snapshot.ts +1 -0
- package/src/migrate.ts +3 -1
- package/src/model-text.ts +33 -11
- package/src/organize-contract.ts +2 -1
- package/src/pipeline/engine.ts +85 -22
- package/src/pipeline/progress.ts +40 -0
- package/src/pipeline/resumable-extract.ts +233 -0
- package/src/pipeline/store.ts +28 -6
- package/src/pipeline/types.ts +47 -2
- package/src/present.ts +1 -1
- package/src/preset-ids.ts +0 -4
- package/src/query-analysis.ts +0 -20
- package/src/recall-delivery.ts +19 -0
- package/src/retrieval/scope.ts +10 -0
- package/src/runtime.ts +412 -206
- package/src/settings-volatile.ts +0 -7
- package/src/source-identity.ts +85 -0
- package/src/source-read.ts +47 -0
- package/src/store/axis-references.ts +5 -3
- package/src/store/knowledge-db.ts +54 -2
- package/src/store/source-identity.ts +42 -0
- package/src/types.ts +18 -5
- package/src/wiki-availability.ts +37 -0
- package/src/wiki-finalize.ts +9 -25
- package/src/wiki-links.ts +120 -0
- package/src/wiki-trial-types.ts +3 -2
- package/src/wiki-trial.ts +17 -6
- package/lib/types/intent/engine.d.ts +0 -122
- package/lib/types/intent/engine.d.ts.map +0 -1
- package/lib/types/intent/engine.js +0 -488
- package/lib/types/intent/engine.js.map +0 -1
- package/lib/types/intent/host.d.ts +0 -68
- package/lib/types/intent/host.d.ts.map +0 -1
- package/lib/types/intent/host.js +0 -249
- package/lib/types/intent/host.js.map +0 -1
- package/lib/types/intent/scope.d.ts +0 -25
- package/lib/types/intent/scope.d.ts.map +0 -1
- package/lib/types/intent/scope.js +0 -116
- package/lib/types/intent/scope.js.map +0 -1
- package/lib/types/intent/store.d.ts +0 -33
- package/lib/types/intent/store.d.ts.map +0 -1
- package/lib/types/intent/store.js +0 -239
- package/lib/types/intent/store.js.map +0 -1
- package/lib/types/intent/tools.d.ts +0 -62
- package/lib/types/intent/tools.d.ts.map +0 -1
- package/lib/types/intent/tools.js +0 -130
- package/lib/types/intent/tools.js.map +0 -1
- package/lib/types/intent/types.d.ts +0 -170
- package/lib/types/intent/types.d.ts.map +0 -1
- package/lib/types/intent/types.js +0 -56
- package/lib/types/intent/types.js.map +0 -1
- package/src/client/IntentLogic.tsx +0 -192
- package/src/client/IntentTab.tsx +0 -343
- package/src/client/TypeDefinitionDrawer.tsx +0 -195
- package/src/intent/README.md +0 -42
- package/src/intent/engine.ts +0 -515
- package/src/intent/host.ts +0 -315
- package/src/intent/scope.ts +0 -125
- package/src/intent/store.ts +0 -218
- package/src/intent/tools.ts +0 -154
- package/src/intent/types.ts +0 -229
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「编译设置」—— 知识处理整页里的一个视图,不再是面板里的一只抽屉。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么搬到这里
|
|
5
|
+
*
|
|
6
|
+
* 它改的是**这个库怎么把文档变成知识**:编译模型、思考强度、坐标提取备注、坐标实体。
|
|
7
|
+
* 这些决定不属于任何一段流水线,但它们是这条流水线的**参数**。放在面板工具栏上时,
|
|
8
|
+
* 人和「知识处理」是两个入口、两处容器;放在这一页上时,改完参数就紧挨着跑那一段。
|
|
9
|
+
*
|
|
10
|
+
* ## 模型选择为什么是对话框,而不是下拉列表
|
|
11
|
+
*
|
|
12
|
+
* 编译模型不是「从三个选项里挑一个」:每个模型要带 provider、模型 id、说明,还要能
|
|
13
|
+
* 选思考强度,列表有十几项。原来那个扁平下拉把「名字(provider / id)」压成一行长字,
|
|
14
|
+
* 读不出重点,也没法标出失效项与当前项。所以这里改成**对话框里的可选列表**:
|
|
15
|
+
* 搜索、按 provider 分组、每行两段(名字 + 出处)、选中打勾,思考强度在底部单独一段。
|
|
16
|
+
*
|
|
17
|
+
* 它复用的是宿主对话框那一套:`Dialog` + `Input` + `Button`,视觉走同一套 token,
|
|
18
|
+
* 不另造一个弹层。
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
|
22
|
+
import { AlertCircle, Check, ChevronDown, LoaderCircle, Search } from '@tita-agent/ui/icons'
|
|
23
|
+
import { Badge, Button, Field, Input, Textarea, cn } from '@tita-agent/ui/components'
|
|
24
|
+
import {
|
|
25
|
+
Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle,
|
|
26
|
+
} from '@tita-agent/ui/overlays'
|
|
27
|
+
import { AxisRegistrySection } from './AxisRegistrySection.tsx'
|
|
28
|
+
import { requestJson } from './request.ts'
|
|
29
|
+
import { DEFAULT_EXTRACTION_TIMEOUT_MINUTES, EXTRACTION_RECOVERY_LIMITS, EXTRACTION_TIMEOUT_OPTIONS, normalizeExtractionTimeoutMinutes, type ExtractionTimeoutMinutes } from '../pipeline/types.js'
|
|
30
|
+
|
|
31
|
+
export interface CompileModelSelectionView {
|
|
32
|
+
mode: 'default' | 'fixed'
|
|
33
|
+
provider?: string
|
|
34
|
+
model?: string
|
|
35
|
+
reasoningEffort?: string
|
|
36
|
+
maxTokens?: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface CompileModelCatalogItem {
|
|
40
|
+
provider: string
|
|
41
|
+
id: string
|
|
42
|
+
name: string
|
|
43
|
+
description?: string
|
|
44
|
+
available: boolean
|
|
45
|
+
/** DSH 当前的默认模型。它不是「本库选中的模型」,所以界面上叫「DSH 默认」。 */
|
|
46
|
+
current?: boolean
|
|
47
|
+
defaultMaxTokens?: number
|
|
48
|
+
reasoning?: { efforts: Array<{ id: string; name: string; description?: string }>; defaultEffort?: string }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface CompileModelCatalog {
|
|
52
|
+
selection: CompileModelSelectionView
|
|
53
|
+
default: { provider?: string; model?: string; reasoningEffort?: string; available: boolean }
|
|
54
|
+
models: CompileModelCatalogItem[]
|
|
55
|
+
why?: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface BaseView {
|
|
59
|
+
id: string
|
|
60
|
+
name: string
|
|
61
|
+
extractionNote: string
|
|
62
|
+
compileModel: CompileModelSelectionView
|
|
63
|
+
extractionTimeoutMinutes?: ExtractionTimeoutMinutes
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 思考强度的显示名。
|
|
68
|
+
*
|
|
69
|
+
* 目录给的是 `low` / `high` / `max` 这类 id;同一屏其余文字都是中文,直接印 id
|
|
70
|
+
* 就是在界面中间露出一段配置值。**认得的翻译,不认得的原样保留** —— 后者是目录
|
|
71
|
+
* 新增档位时唯一诚实的做法(编一个中文名会让两件事看起来是同一个档)。
|
|
72
|
+
*/
|
|
73
|
+
const EFFORT_LABEL: Record<string, string> = {
|
|
74
|
+
off: '关闭思考', none: '关闭思考', minimal: '最低', low: '低', medium: '中', high: '高', xhigh: '很高', max: '最高',
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const DEFAULT_VALUE = '__dsh_default__'
|
|
78
|
+
const fixedValue = (provider: string, model: string): string => `fixed:${encodeURIComponent(provider)}:${encodeURIComponent(model)}`
|
|
79
|
+
const parseValue = (value: string): { provider?: string; model?: string } => {
|
|
80
|
+
if (!value.startsWith('fixed:')) return {}
|
|
81
|
+
const [provider, model] = value.slice(6).split(':').map(decodeURIComponent)
|
|
82
|
+
return { ...(provider ? { provider } : {}), ...(model ? { model } : {}) }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function modelLabel(item: CompileModelCatalogItem | undefined): string {
|
|
86
|
+
return item?.name?.trim() || item?.id || ''
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 一行「当前选了什么」。触发按钮与列表行共用同一套读法。 */
|
|
90
|
+
function SelectionSummary({ catalog, selection }: { catalog: CompileModelCatalog | null; selection: CompileModelSelectionView }): ReactNode {
|
|
91
|
+
const output = <span>单次最大输出:{selection.maxTokens !== undefined ? `${selection.maxTokens.toLocaleString('zh-CN')} token` : '跟随模型默认'}</span>
|
|
92
|
+
if (selection.mode === 'fixed' && selection.provider && selection.model) {
|
|
93
|
+
const item = catalog?.models.find(model => model.provider === selection.provider && model.id === selection.model)
|
|
94
|
+
return <>
|
|
95
|
+
<strong>{modelLabel(item) || selection.model}</strong>
|
|
96
|
+
<span>{selection.provider} / {selection.model}{selection.reasoningEffort ? ` · ${selection.reasoningEffort}` : ''}</span>
|
|
97
|
+
{output}
|
|
98
|
+
</>
|
|
99
|
+
}
|
|
100
|
+
const route = catalog?.default
|
|
101
|
+
return <>
|
|
102
|
+
<strong>跟随 DSH 默认模型</strong>
|
|
103
|
+
<span>{route?.provider && route.model ? `${route.provider} / ${route.model}` : '运行时读取当前模型设置'}{selection.reasoningEffort ? ` · ${selection.reasoningEffort}` : ''}</span>
|
|
104
|
+
{output}
|
|
105
|
+
</>
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 模型选择对话框。**只在打开时挂载** —— 每次打开都是新的一次草稿,
|
|
110
|
+
* 不需要再写一段「把草稿重置回已保存的值」的 effect(那种 effect 最容易把
|
|
111
|
+
* 服务端刚保存的值又改回去)。
|
|
112
|
+
*/
|
|
113
|
+
function ModelPickerDialog({ kb, selection, onClose, onSaved }: {
|
|
114
|
+
kb: string
|
|
115
|
+
selection: CompileModelSelectionView
|
|
116
|
+
onClose: () => void
|
|
117
|
+
onSaved: () => Promise<void>
|
|
118
|
+
}): ReactNode {
|
|
119
|
+
const [draft, setDraft] = useState(selection.mode === 'fixed' && selection.provider && selection.model
|
|
120
|
+
? fixedValue(selection.provider, selection.model)
|
|
121
|
+
: DEFAULT_VALUE)
|
|
122
|
+
const [effort, setEffort] = useState(selection.reasoningEffort ?? '')
|
|
123
|
+
const [maxTokens, setMaxTokens] = useState(selection.maxTokens === undefined ? '' : String(selection.maxTokens))
|
|
124
|
+
const [catalog, setCatalog] = useState<CompileModelCatalog | null>(null)
|
|
125
|
+
const [error, setError] = useState('')
|
|
126
|
+
const [loading, setLoading] = useState(true)
|
|
127
|
+
const [saving, setSaving] = useState(false)
|
|
128
|
+
const [query, setQuery] = useState('')
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 目录按**草稿路由**重新读一次:思考强度是跟着模型走的,只在当前路由上返回。
|
|
132
|
+
* 目录自己不进依赖(写 `catalog` 的 effect 不能把 `catalog` 当输入),所以
|
|
133
|
+
* 默认路由的 provider / model 从一个 ref 里取不到时,服务端会自己补。
|
|
134
|
+
*/
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
let cancelled = false
|
|
137
|
+
setLoading(true)
|
|
138
|
+
setError('')
|
|
139
|
+
const route = parseValue(draft)
|
|
140
|
+
const params = new URLSearchParams({ kb })
|
|
141
|
+
if (route.provider && route.model) {
|
|
142
|
+
params.set('provider', route.provider)
|
|
143
|
+
params.set('model', route.model)
|
|
144
|
+
}
|
|
145
|
+
void requestJson<CompileModelCatalog>(`/knowledge/models?${params}`).then(result => {
|
|
146
|
+
if (cancelled) return
|
|
147
|
+
setCatalog(result.data ?? null)
|
|
148
|
+
}).catch(cause => {
|
|
149
|
+
if (cancelled) return
|
|
150
|
+
setError(cause instanceof Error ? cause.message : String(cause))
|
|
151
|
+
}).finally(() => { if (!cancelled) setLoading(false) })
|
|
152
|
+
return () => { cancelled = true }
|
|
153
|
+
}, [kb, draft])
|
|
154
|
+
|
|
155
|
+
const route = parseValue(draft)
|
|
156
|
+
const effectiveProvider = route.provider ?? catalog?.default.provider
|
|
157
|
+
const effectiveModel = route.model ?? catalog?.default.model
|
|
158
|
+
const selectedItem = catalog?.models.find(item => item.provider === effectiveProvider && item.id === effectiveModel)
|
|
159
|
+
const efforts = selectedItem?.reasoning?.efforts ?? []
|
|
160
|
+
const defaultAvailable = catalog?.default.available !== false
|
|
161
|
+
|
|
162
|
+
/** 过滤只发生在渲染层:目录本身按 provider 分组,搜索不改变分组。 */
|
|
163
|
+
const groups = useMemo(() => {
|
|
164
|
+
const needle = query.trim().toLowerCase()
|
|
165
|
+
const match = (item: CompileModelCatalogItem): boolean => needle === ''
|
|
166
|
+
|| item.name.toLowerCase().includes(needle)
|
|
167
|
+
|| item.id.toLowerCase().includes(needle)
|
|
168
|
+
|| item.provider.toLowerCase().includes(needle)
|
|
169
|
+
|| (item.description ?? '').toLowerCase().includes(needle)
|
|
170
|
+
const byProvider = new Map<string, CompileModelCatalogItem[]>()
|
|
171
|
+
for (const item of catalog?.models ?? []) {
|
|
172
|
+
if (!match(item)) continue
|
|
173
|
+
const list = byProvider.get(item.provider) ?? []
|
|
174
|
+
list.push(item)
|
|
175
|
+
byProvider.set(item.provider, list)
|
|
176
|
+
}
|
|
177
|
+
const showDefault = needle === '' || ['跟随', '默认', 'dsh', 'default'].some(token => token.includes(needle) || needle.includes(token))
|
|
178
|
+
return { groups: [...byProvider.entries()], showDefault }
|
|
179
|
+
}, [catalog, query])
|
|
180
|
+
|
|
181
|
+
const save = async (): Promise<void> => {
|
|
182
|
+
setSaving(true)
|
|
183
|
+
setError('')
|
|
184
|
+
try {
|
|
185
|
+
const outputLimit = maxTokens.trim() === '' ? undefined : Number(maxTokens)
|
|
186
|
+
if (outputLimit !== undefined && (!Number.isSafeInteger(outputLimit) || outputLimit <= 0)) throw new Error('单次最大输出必须是正整数 token;留空表示跟随模型默认。')
|
|
187
|
+
const next: CompileModelSelectionView = draft === DEFAULT_VALUE
|
|
188
|
+
? { mode: 'default', ...(effort === '' ? {} : { reasoningEffort: effort }) }
|
|
189
|
+
: { mode: 'fixed', ...route, ...(effort === '' ? {} : { reasoningEffort: effort }) }
|
|
190
|
+
if (outputLimit !== undefined) next.maxTokens = outputLimit
|
|
191
|
+
if (next.mode === 'fixed' && (!next.provider || !next.model)) throw new Error('没有选中模型')
|
|
192
|
+
const saved = await requestJson<{ base: BaseView }>(`/knowledge/bases/${encodeURIComponent(kb)}`, {
|
|
193
|
+
method: 'PATCH',
|
|
194
|
+
headers: { 'content-type': 'application/json' },
|
|
195
|
+
body: JSON.stringify({ compileModel: next }),
|
|
196
|
+
})
|
|
197
|
+
if (saved.data?.base.compileModel.maxTokens !== outputLimit) throw new Error('服务未保存最大输出设置。请确认已加载新版服务,再重试保存。')
|
|
198
|
+
await onSaved()
|
|
199
|
+
onClose()
|
|
200
|
+
} catch (cause) {
|
|
201
|
+
setError(cause instanceof Error ? cause.message : String(cause))
|
|
202
|
+
} finally {
|
|
203
|
+
setSaving(false)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const row = (item: CompileModelCatalogItem): ReactNode => {
|
|
208
|
+
const value = fixedValue(item.provider, item.id)
|
|
209
|
+
const on = draft === value
|
|
210
|
+
return <button
|
|
211
|
+
key={value}
|
|
212
|
+
type="button"
|
|
213
|
+
role="radio"
|
|
214
|
+
aria-checked={on}
|
|
215
|
+
disabled={!item.available}
|
|
216
|
+
className={cn('ktModelRow', on && 'ktModelRowOn', !item.available && 'ktModelRowOff')}
|
|
217
|
+
onClick={() => { setDraft(value); setEffort('') }}
|
|
218
|
+
>
|
|
219
|
+
<span className="ktModelRowBody">
|
|
220
|
+
<span className="ktModelRowName">
|
|
221
|
+
{item.name}
|
|
222
|
+
{item.current === true && <Badge tone="neutral">DSH 默认</Badge>}
|
|
223
|
+
{!item.available && <Badge tone="warning">已失效</Badge>}
|
|
224
|
+
</span>
|
|
225
|
+
<span className="ktModelRowMeta">{item.provider} / {item.id}</span>
|
|
226
|
+
{item.description !== undefined && item.description !== '' && <span className="ktModelRowDesc">{item.description}</span>}
|
|
227
|
+
</span>
|
|
228
|
+
<span className="ktModelRowCheck" aria-hidden="true">{on && <Check size={13} />}</span>
|
|
229
|
+
</button>
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return <Dialog open onOpenChange={next => { if (!next) onClose() }}>
|
|
233
|
+
<DialogContent className="ktModelDialog">
|
|
234
|
+
<DialogHeader>
|
|
235
|
+
<DialogTitle>选择编译模型</DialogTitle>
|
|
236
|
+
<DialogDescription>用于抽取、归一补判与卡片组织。任务启动时保存实际选择,之后的运行记录里能读回来。</DialogDescription>
|
|
237
|
+
</DialogHeader>
|
|
238
|
+
|
|
239
|
+
<label className="ktModelSearch">
|
|
240
|
+
<Search size={13} aria-hidden="true" />
|
|
241
|
+
<Input value={query} onChange={event => setQuery(event.target.value)} placeholder="搜索模型名、provider 或说明" aria-label="搜索编译模型" autoFocus />
|
|
242
|
+
</label>
|
|
243
|
+
|
|
244
|
+
<div className="ktModelList" role="radiogroup" aria-label="编译模型">
|
|
245
|
+
{groups.showDefault && <>
|
|
246
|
+
<button
|
|
247
|
+
type="button"
|
|
248
|
+
role="radio"
|
|
249
|
+
aria-checked={draft === DEFAULT_VALUE}
|
|
250
|
+
className={cn('ktModelRow', draft === DEFAULT_VALUE && 'ktModelRowOn')}
|
|
251
|
+
onClick={() => { setDraft(DEFAULT_VALUE); setEffort('') }}
|
|
252
|
+
>
|
|
253
|
+
<span className="ktModelRowBody">
|
|
254
|
+
<span className="ktModelRowName">跟随 DSH 默认模型{!defaultAvailable && <Badge tone="warning">默认模型不可用</Badge>}</span>
|
|
255
|
+
<span className="ktModelRowMeta">{catalog?.default.provider && catalog.default.model ? `${catalog.default.provider} / ${catalog.default.model}` : '运行时读取当前模型设置'}</span>
|
|
256
|
+
<span className="ktModelRowDesc">换 DSH 默认模型时,这个库跟着换;任务记录里留下当次实际使用的模型。</span>
|
|
257
|
+
</span>
|
|
258
|
+
<span className="ktModelRowCheck" aria-hidden="true">{draft === DEFAULT_VALUE && <Check size={13} />}</span>
|
|
259
|
+
</button>
|
|
260
|
+
<div className="ktModelDivider" aria-hidden="true">模型目录</div>
|
|
261
|
+
</>}
|
|
262
|
+
|
|
263
|
+
{loading && catalog === null && <p className="ktModelEmpty" role="status"><LoaderCircle size={13} className="tita-spin" />正在读取 DSH 模型目录…</p>}
|
|
264
|
+
{!loading && catalog !== null && groups.groups.length === 0 && <p className="ktModelEmpty">没有匹配的模型。</p>}
|
|
265
|
+
{groups.groups.map(([provider, items]) => <div key={provider} className="ktModelGroup">
|
|
266
|
+
<p className="ktModelGroupName">{provider}</p>
|
|
267
|
+
{items.map(row)}
|
|
268
|
+
</div>)}
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div className="ktEffort">
|
|
272
|
+
<div className="ktEffortHead">
|
|
273
|
+
<h3>思考强度</h3>
|
|
274
|
+
<p>{efforts.length > 0 ? '档位来自所选模型的 DSH 目录;不选就跟随模型默认。' : '所选模型没有公开档位,使用模型默认行为。'}</p>
|
|
275
|
+
</div>
|
|
276
|
+
<div className="ktEffortOptions" role="group" aria-label="思考强度">
|
|
277
|
+
<button type="button" className={cn('ktEffortOption', effort === '' && 'ktEffortOptionOn')} aria-pressed={effort === ''} onClick={() => setEffort('')}>跟随默认</button>
|
|
278
|
+
{efforts.map(item => <button
|
|
279
|
+
key={item.id}
|
|
280
|
+
type="button"
|
|
281
|
+
className={cn('ktEffortOption', effort === item.id && 'ktEffortOptionOn')}
|
|
282
|
+
aria-pressed={effort === item.id}
|
|
283
|
+
title={item.description}
|
|
284
|
+
onClick={() => setEffort(item.id)}
|
|
285
|
+
>{EFFORT_LABEL[item.id] ?? item.name ?? item.id}</button>)}
|
|
286
|
+
{effort !== '' && !efforts.some(item => item.id === effort)
|
|
287
|
+
&& <button type="button" className="ktEffortOption ktEffortOptionOn" aria-pressed title="当前选择在这份目录里不可用">{effort}(不可用)</button>}
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<div className="ktOutputLimit">
|
|
292
|
+
<Field label="单次最大输出(token)" hint="留空跟随模型默认;这是单次请求的输出上限,不是文档大小或整次任务预算。">
|
|
293
|
+
<Input type="number" min={1} step={1} inputMode="numeric" aria-label="单次最大输出 token" value={maxTokens} placeholder="跟随模型默认" onChange={event => setMaxTokens(event.target.value)} aria-invalid={maxTokens !== '' && (!Number.isSafeInteger(Number(maxTokens)) || Number(maxTokens) <= 0)} />
|
|
294
|
+
</Field>
|
|
295
|
+
<div className="mt-2 flex flex-wrap items-center gap-2" role="group" aria-label="最大输出快捷设置">
|
|
296
|
+
<Button variant="ghost" aria-pressed={maxTokens === ''} onClick={() => setMaxTokens('')}>跟随默认</Button>
|
|
297
|
+
<Button variant="outline" aria-pressed={maxTokens === '100000'} onClick={() => setMaxTokens('100000')}>10 万 token</Button>
|
|
298
|
+
</div>
|
|
299
|
+
{selectedItem?.defaultMaxTokens !== undefined && <p className="ktHint">DSH 模型默认输出:{selectedItem.defaultMaxTokens.toLocaleString('zh-CN')} token。此值不是模型的硬上限。</p>}
|
|
300
|
+
<p className="ktHint">实际可用长度由模型、网关和剩余上下文共同限制;增大此值不会提高生成速度,也不会延长超时。</p>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{error !== '' && <p className="ktModelError" role="alert"><AlertCircle size={13} />{error}</p>}
|
|
304
|
+
{catalog?.why !== undefined && catalog.why !== '' && error === '' && <p className="ktModelWhy" role="status">模型目录提示:{catalog.why}</p>}
|
|
305
|
+
|
|
306
|
+
<DialogFooter>
|
|
307
|
+
<Button variant="ghost" size="sm" onClick={onClose} disabled={saving}>取消</Button>
|
|
308
|
+
<Button size="sm" onClick={() => { void save() }} disabled={saving || loading}>
|
|
309
|
+
{saving ? <LoaderCircle size={13} className="tita-spin" /> : <Check size={13} />}保存
|
|
310
|
+
</Button>
|
|
311
|
+
</DialogFooter>
|
|
312
|
+
</DialogContent>
|
|
313
|
+
</Dialog>
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* 编译设置页。它自己读写 `/knowledge/bases`,因为整页与面板是两份文档 ——
|
|
318
|
+
* 从面板传 state 过来只会得到一份「面板没打开时就是空的」设置。
|
|
319
|
+
*/
|
|
320
|
+
export function CompileSettingsView({ kb, onNotice }: {
|
|
321
|
+
kb: string
|
|
322
|
+
onNotice: (tone: 'success' | 'error', text: string) => void
|
|
323
|
+
}): ReactNode {
|
|
324
|
+
const [base, setBase] = useState<BaseView | null>(null)
|
|
325
|
+
const [catalog, setCatalog] = useState<CompileModelCatalog | null>(null)
|
|
326
|
+
const [failure, setFailure] = useState('')
|
|
327
|
+
const [note, setNote] = useState('')
|
|
328
|
+
const [savingNote, setSavingNote] = useState(false)
|
|
329
|
+
const [pickerOpen, setPickerOpen] = useState(false)
|
|
330
|
+
const [timeoutMinutes, setTimeoutMinutes] = useState<ExtractionTimeoutMinutes>(DEFAULT_EXTRACTION_TIMEOUT_MINUTES)
|
|
331
|
+
const [savingTimeout, setSavingTimeout] = useState(false)
|
|
332
|
+
|
|
333
|
+
const load = async (): Promise<void> => {
|
|
334
|
+
try {
|
|
335
|
+
const result = await requestJson<{ bases: BaseView[] }>('/knowledge/bases')
|
|
336
|
+
const found = result.data?.bases.find(item => item.id === kb) ?? null
|
|
337
|
+
setBase(found)
|
|
338
|
+
setNote(found?.extractionNote ?? '')
|
|
339
|
+
setTimeoutMinutes(normalizeExtractionTimeoutMinutes(found?.extractionTimeoutMinutes))
|
|
340
|
+
setFailure(found === null ? '这个地址里的知识库不存在。' : '')
|
|
341
|
+
} catch (cause) {
|
|
342
|
+
setFailure(cause instanceof Error ? cause.message : String(cause))
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
let cancelled = false
|
|
348
|
+
void requestJson<CompileModelCatalog>(`/knowledge/models?${new URLSearchParams({ kb })}`)
|
|
349
|
+
.then(result => { if (!cancelled) setCatalog(result.data ?? null) })
|
|
350
|
+
.catch(() => { /* 目录读不到时对话框里会报;这里不重复喊一遍 */ })
|
|
351
|
+
void requestJson<{ bases: BaseView[] }>('/knowledge/bases').then(result => {
|
|
352
|
+
if (cancelled) return
|
|
353
|
+
const found = result.data?.bases.find(item => item.id === kb) ?? null
|
|
354
|
+
setBase(found)
|
|
355
|
+
setNote(found?.extractionNote ?? '')
|
|
356
|
+
setTimeoutMinutes(normalizeExtractionTimeoutMinutes(found?.extractionTimeoutMinutes))
|
|
357
|
+
setFailure(found === null ? '这个地址里的知识库不存在。' : '')
|
|
358
|
+
}).catch(cause => { if (!cancelled) setFailure(cause instanceof Error ? cause.message : String(cause)) })
|
|
359
|
+
return () => { cancelled = true }
|
|
360
|
+
}, [kb])
|
|
361
|
+
|
|
362
|
+
const saveNote = async (): Promise<void> => {
|
|
363
|
+
setSavingNote(true)
|
|
364
|
+
try {
|
|
365
|
+
await requestJson(`/knowledge/bases/${encodeURIComponent(kb)}`, {
|
|
366
|
+
method: 'PATCH',
|
|
367
|
+
headers: { 'content-type': 'application/json' },
|
|
368
|
+
body: JSON.stringify({ extractionNote: note }),
|
|
369
|
+
})
|
|
370
|
+
await load()
|
|
371
|
+
onNotice('success', note.trim() === '' ? '备注已清空 —— 下一次抽取不再带这段约定。' : `备注已保存(${note.trim().length} 字),下一次抽取会逐字带上它。`)
|
|
372
|
+
} catch (cause) {
|
|
373
|
+
onNotice('error', cause instanceof Error ? cause.message : String(cause))
|
|
374
|
+
} finally {
|
|
375
|
+
setSavingNote(false)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const saveTimeout = async (): Promise<void> => {
|
|
380
|
+
setSavingTimeout(true)
|
|
381
|
+
try {
|
|
382
|
+
const saved = await requestJson<{ base: BaseView }>(`/knowledge/bases/${encodeURIComponent(kb)}`, {
|
|
383
|
+
method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ extractionTimeoutMinutes: timeoutMinutes }),
|
|
384
|
+
})
|
|
385
|
+
if (saved.data?.base.extractionTimeoutMinutes !== timeoutMinutes) throw new Error('服务未保存抽取时限。请确认已加载新版服务,再重试保存。')
|
|
386
|
+
setBase(saved.data.base)
|
|
387
|
+
onNotice('success', `文档抽取时限已保存为 ${timeoutMinutes} 分钟;下一次运行或续跑生效。`)
|
|
388
|
+
} catch (cause) {
|
|
389
|
+
onNotice('error', cause instanceof Error ? cause.message : String(cause))
|
|
390
|
+
} finally { setSavingTimeout(false) }
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (failure !== '') return <p className="ktError" role="alert"><AlertCircle size={13} />{failure}</p>
|
|
394
|
+
if (base === null) return <p className="ktEmpty" role="status"><LoaderCircle size={13} className="tita-spin" />正在读取本库的编译设置…</p>
|
|
395
|
+
|
|
396
|
+
const selection = base.compileModel ?? { mode: 'default' }
|
|
397
|
+
const noteChanged = note !== base.extractionNote
|
|
398
|
+
|
|
399
|
+
return <div className="ktSettings">
|
|
400
|
+
<div className="ktSettingsCol">
|
|
401
|
+
<section className="ktSettingCard" aria-labelledby="kt-model-title">
|
|
402
|
+
<div className="ktSettingHead">
|
|
403
|
+
<h2 id="kt-model-title">编译模型</h2>
|
|
404
|
+
<p>本库的抽取、归一补判与卡片组织都用它。</p>
|
|
405
|
+
</div>
|
|
406
|
+
<Field as="div" label="模型" hint="跟随 DSH 默认模型时,默认模型换了本库也跟着换。">
|
|
407
|
+
<button type="button" className="ktModelField" onClick={() => setPickerOpen(true)} aria-label="更换编译模型">
|
|
408
|
+
<span className="ktModelFieldBody"><SelectionSummary catalog={catalog} selection={selection} /></span>
|
|
409
|
+
<span className="ktModelFieldAction">更换<ChevronDown size={13} /></span>
|
|
410
|
+
</button>
|
|
411
|
+
</Field>
|
|
412
|
+
{selection.mode === 'fixed' && catalog !== null && !catalog.models.some(item => item.provider === selection.provider && item.id === selection.model)
|
|
413
|
+
&& <p className="ktHint ktHintWarn" role="status">这个固定模型已不在 DSH 模型目录里;下一次运行可能失败,建议重新选择。</p>}
|
|
414
|
+
<p className="ktHint">思考强度与最大输出都在「更换」里设置;保存后下一次运行或续跑生效。</p>
|
|
415
|
+
</section>
|
|
416
|
+
|
|
417
|
+
<section className="ktSettingCard" aria-labelledby="kt-timeout-title">
|
|
418
|
+
<div className="ktSettingHead">
|
|
419
|
+
<h2 id="kt-timeout-title">文档抽取时限</h2>
|
|
420
|
+
<p>每份文档的一轮抽取或续跑,最多运行多长时间。</p>
|
|
421
|
+
</div>
|
|
422
|
+
<div className="flex flex-wrap gap-2" role="group" aria-label="文档抽取时限">
|
|
423
|
+
{EXTRACTION_TIMEOUT_OPTIONS.map(minutes => <Button key={minutes} variant={timeoutMinutes === minutes ? 'default' : 'outline'} aria-pressed={timeoutMinutes === minutes} disabled={savingTimeout} onClick={() => setTimeoutMinutes(minutes)}>
|
|
424
|
+
{minutes} 分钟{minutes === DEFAULT_EXTRACTION_TIMEOUT_MINUTES ? '(默认)' : minutes === 100 ? '(上限)' : ''}
|
|
425
|
+
</Button>)}
|
|
426
|
+
</div>
|
|
427
|
+
<p className="ktHint">保存后,下一次运行或续跑使用新时限;已启动的任务保持原值。达到时限时保留已保存分片。</p>
|
|
428
|
+
<p className="ktHint">单次模型请求仍默认 {EXTRACTION_RECOVERY_LIMITS.requestTimeoutMs / 60_000} 分钟超时。每轮请求次数上限取 {EXTRACTION_RECOVERY_LIMITS.minimumRequests} 次与初始分片数 × {EXTRACTION_RECOVERY_LIMITS.requestsPerInitialChunk} 次中的较大值,与时限独立。</p>
|
|
429
|
+
{timeoutMinutes !== normalizeExtractionTimeoutMinutes(base.extractionTimeoutMinutes) && <div className="ktSettingActions">
|
|
430
|
+
<Button variant="ghost" disabled={savingTimeout} onClick={() => setTimeoutMinutes(normalizeExtractionTimeoutMinutes(base.extractionTimeoutMinutes))}>撤销</Button>
|
|
431
|
+
<Button variant="outline" disabled={savingTimeout} onClick={() => { void saveTimeout() }}>{savingTimeout ? <LoaderCircle size={12} className="motion-safe:animate-spin" /> : <Check size={12} />}保存时限</Button>
|
|
432
|
+
</div>}
|
|
433
|
+
</section>
|
|
434
|
+
|
|
435
|
+
<section className="ktSettingCard" aria-labelledby="kt-note-title">
|
|
436
|
+
<div className="ktSettingHead">
|
|
437
|
+
<h2 id="kt-note-title">提取备注</h2>
|
|
438
|
+
<p>逐字加入每次抽取的提示词,只影响本库的坐标判断。</p>
|
|
439
|
+
</div>
|
|
440
|
+
<Field label="坐标约定" hint="写本库特有的对象 / 动作 / 场景约定;留空就是不带提示。">
|
|
441
|
+
<Textarea value={note} rows={5} spellCheck={false} aria-label="坐标提取备注"
|
|
442
|
+
placeholder="例如:本库的「活动」指投放计划,不指营销活动。"
|
|
443
|
+
onChange={event => setNote(event.target.value)} />
|
|
444
|
+
</Field>
|
|
445
|
+
{noteChanged && <div className="ktSettingActions">
|
|
446
|
+
<Button variant="ghost" size="sm" onClick={() => setNote(base.extractionNote)}>撤销</Button>
|
|
447
|
+
<Button variant="outline" size="sm" disabled={savingNote} onClick={() => { void saveNote() }}>
|
|
448
|
+
{savingNote ? <LoaderCircle size={12} className="tita-spin" /> : <Check size={12} />}保存备注
|
|
449
|
+
</Button>
|
|
450
|
+
</div>}
|
|
451
|
+
</section>
|
|
452
|
+
</div>
|
|
453
|
+
|
|
454
|
+
{/* 坐标实体那一节自带标题与「N 个待确认」,这里不再套一层同名标题 ——
|
|
455
|
+
同一句话出现两次,人会以为是两个东西。 */}
|
|
456
|
+
<div className="ktSettingsWide">
|
|
457
|
+
<AxisRegistrySection kbId={kb} canEdit />
|
|
458
|
+
</div>
|
|
459
|
+
|
|
460
|
+
{pickerOpen && <ModelPickerDialog
|
|
461
|
+
kb={kb}
|
|
462
|
+
selection={selection}
|
|
463
|
+
onClose={() => setPickerOpen(false)}
|
|
464
|
+
onSaved={async () => {
|
|
465
|
+
await load()
|
|
466
|
+
void requestJson<CompileModelCatalog>(`/knowledge/models?${new URLSearchParams({ kb })}`)
|
|
467
|
+
.then(result => setCatalog(result.data ?? null))
|
|
468
|
+
.catch(() => { /* 保存已成功;目录刷新失败不推翻这次保存 */ })
|
|
469
|
+
onNotice('success', '编译模型已保存。')
|
|
470
|
+
}}
|
|
471
|
+
/>}
|
|
472
|
+
</div>
|
|
473
|
+
}
|