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
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { newSourceId } from './source-identity.js'
|
|
1
2
|
import { embeddingModel } from './environment/model-catalog.js'
|
|
2
3
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
3
4
|
import { createReadStream } from 'node:fs'
|
|
@@ -36,9 +37,9 @@ import { DOC_PAGE_CHARS, DOC_PAGE_CHARS_MAX, documentContentPage } from './docum
|
|
|
36
37
|
import type { ModelCall } from './extract-model.js'
|
|
37
38
|
import { runWikiTrial, trialSummary } from './wiki-trial.js'
|
|
38
39
|
import { extractionRunId } from './extraction-runs.js'
|
|
39
|
-
import type
|
|
40
|
+
import { DEFAULT_EXTRACTION_TIMEOUT_MINUTES, PIPELINE_ACTIVE_STATES, isExtractionTimeoutMinutes, type ExtractionTimeoutMinutes, type PipelineStage } from './pipeline/types.js'
|
|
40
41
|
import {
|
|
41
|
-
agentLayout, defaultAgentRoot, ensureAgentLayout,
|
|
42
|
+
agentLayout, defaultAgentRoot, ensureAgentLayout, knowledgeBaseRoot,
|
|
42
43
|
memoryRoot, resourceRegistry, resourceRoot, type AgentLayout,
|
|
43
44
|
} from './layout.js'
|
|
44
45
|
import {
|
|
@@ -55,21 +56,11 @@ import {
|
|
|
55
56
|
type ConvertHost,
|
|
56
57
|
} from './convert/host.js'
|
|
57
58
|
import type { ConvertPluginConfig } from './convert/types.js'
|
|
58
|
-
import {
|
|
59
|
-
applyIntent, INTENT_MOUNT_PATH, INTENT_SERVICE,
|
|
60
|
-
type IntentHost,
|
|
61
|
-
type IntentLineageService,
|
|
62
|
-
type IntentService,
|
|
63
|
-
} from './intent/host.js'
|
|
64
|
-
import { registerIntentTools } from './intent/tools.js'
|
|
65
59
|
|
|
66
60
|
export * from './types.js'
|
|
67
61
|
export * from './runtime.js'
|
|
68
62
|
export * from './knowledge-bases.js'
|
|
69
|
-
export * from './intent/types.js'
|
|
70
|
-
export * from './intent/engine.js'
|
|
71
63
|
export { applyConvert as applyConvertHost, CONVERT_SETTINGS_NAMESPACE, CONVERT_MOUNT_PATH, type ConvertHost }
|
|
72
|
-
export { applyIntent as applyIntentHost, INTENT_MOUNT_PATH, INTENT_SERVICE, type IntentHost, type IntentLineageService }
|
|
73
64
|
|
|
74
65
|
export const name = 'dsh-data-domain-knowledge'
|
|
75
66
|
/**
|
|
@@ -93,6 +84,7 @@ export { CONVERT_SETTINGS_SCHEMA }
|
|
|
93
84
|
*/
|
|
94
85
|
import { Config, type KnowledgePluginConfig } from './config-schema.js'
|
|
95
86
|
import { buildModelRequest, callModelWithRetry, collectModelText } from './model-text.js'
|
|
87
|
+
import { agentRecallEnvelope } from './recall-delivery.js'
|
|
96
88
|
import {
|
|
97
89
|
addSourceCallView, addSourceMetaOf, addSourceResultMarkdown,
|
|
98
90
|
importCallView, importMetaOf, importResultMarkdown,
|
|
@@ -123,7 +115,7 @@ type WebServerLike = { register(route: { kind: 'exact' | 'prefix'; path: string;
|
|
|
123
115
|
* `src/client/KnowledgeToolRow.tsx` 读它渲染专用卡片(值必须是 JSON 可序列化的 ——
|
|
124
116
|
* 会话日志会整份校验,塞个函数或 Date 会在写日志时炸)。
|
|
125
117
|
*
|
|
126
|
-
* `call` / `result`
|
|
118
|
+
* `call` / `result` 是与工具名无关的渲染参数(标题 / 分类 / 一段 Markdown),
|
|
127
119
|
* 给终端界面和「没有专用卡片的界面」兜底。三个都是**纯函数**:界面在实时流和会话回放
|
|
128
120
|
* 两条路径上都会调它们。
|
|
129
121
|
*
|
|
@@ -212,7 +204,7 @@ function lineageOf(args: Args, exec?: ToolRunContext): LineageContext | undefine
|
|
|
212
204
|
const sessionId = typeof args.session_key === 'string' && args.session_key.trim() ? args.session_key.trim() : exec?.agent?.session.header.id
|
|
213
205
|
if (sessionId) lineage.sessionId = sessionId
|
|
214
206
|
const fields: Array<[keyof LineageContext, string]> = [
|
|
215
|
-
['turnId', 'turn_id'], ['
|
|
207
|
+
['turnId', 'turn_id'], ['taskId', 'task_id'],
|
|
216
208
|
['subtaskId', 'subtask_id'], ['parentSubtaskId', 'parent_subtask_id'], ['runId', 'run_id'], ['traceId', 'trace_id'], ['domain', 'domain'],
|
|
217
209
|
]
|
|
218
210
|
for (const [target, source] of fields) if (typeof args[source] === 'string' && args[source].trim()) lineage[target] = args[source].trim().slice(0, 160)
|
|
@@ -223,7 +215,6 @@ function lineageFromUrl(url: URL): LineageContext | undefined {
|
|
|
223
215
|
return lineageOf({
|
|
224
216
|
session_key: url.searchParams.get('session_key') ?? undefined,
|
|
225
217
|
turn_id: url.searchParams.get('turn_id') ?? undefined,
|
|
226
|
-
intent_id: url.searchParams.get('intent_id') ?? undefined,
|
|
227
218
|
task_id: url.searchParams.get('task_id') ?? undefined,
|
|
228
219
|
subtask_id: url.searchParams.get('subtask_id') ?? undefined,
|
|
229
220
|
parent_subtask_id: url.searchParams.get('parent_subtask_id') ?? undefined,
|
|
@@ -407,8 +398,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
407
398
|
/**
|
|
408
399
|
* 这两组的把手,`loader/volatile-update` 时调 `reprocess()` 按新值重灌。
|
|
409
400
|
*
|
|
410
|
-
*
|
|
411
|
-
* `applyIntent`)。
|
|
401
|
+
* 先声明后赋值:事件只在运行时触发,转换宿主在下面的 `applyConvert` 中装配。
|
|
412
402
|
*/
|
|
413
403
|
let convertHost: ConvertHost | undefined
|
|
414
404
|
/**
|
|
@@ -434,6 +424,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
434
424
|
*/
|
|
435
425
|
/** 注册表在默认 runtime 之后才构造;用一个可替换的读取器避开构造期 TDZ。 */
|
|
436
426
|
let compileModelForBase: (id: string) => CompileModelSelection = () => ({ ...DEFAULT_COMPILE_MODEL })
|
|
427
|
+
let extractionTimeoutForBase: (id: string) => ExtractionTimeoutMinutes = () => DEFAULT_EXTRACTION_TIMEOUT_MINUTES
|
|
437
428
|
|
|
438
429
|
const modelSeamFor = (kbId = DEFAULT_KB_ID): ModelSeam => {
|
|
439
430
|
/**
|
|
@@ -489,7 +480,9 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
489
480
|
provider,
|
|
490
481
|
model,
|
|
491
482
|
...(effectiveReasoningEffort ? { reasoningEffort: effectiveReasoningEffort } : {}),
|
|
492
|
-
|
|
483
|
+
...(configured.maxTokens !== undefined ? { maxTokens: configured.maxTokens } : {}),
|
|
484
|
+
call: async ({ system, prompt, sessionId, onUsage, onDelta, signal, retryAttempts, maxTokens }) => {
|
|
485
|
+
const outputLimit = maxTokens ?? configured.maxTokens
|
|
493
486
|
/**
|
|
494
487
|
* 先走 DSH 的公开解析接口,再开始发 prompt。这样固定模型过期、默认模型失效时,
|
|
495
488
|
* 编译会在模型调用边界明确失败,而不是落成一次看似成功的规则抽取。
|
|
@@ -499,6 +492,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
499
492
|
await llm.resolveCallConfig({
|
|
500
493
|
provider, model,
|
|
501
494
|
...(effectiveReasoningEffort ? { reasoningEffort: effectiveReasoningEffort } : {}),
|
|
495
|
+
...(outputLimit !== undefined ? { maxTokens: outputLimit } : {}),
|
|
502
496
|
})
|
|
503
497
|
} catch (cause) {
|
|
504
498
|
const detail = cause instanceof Error ? cause.message : String(cause)
|
|
@@ -515,7 +509,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
515
509
|
* (OpenCode 系路由回 400 `MissingSessionID`)。
|
|
516
510
|
*/
|
|
517
511
|
const request = buildModelRequest({
|
|
518
|
-
provider, model, reasoningEffort: effectiveReasoningEffort, system, prompt,
|
|
512
|
+
provider, model, reasoningEffort: effectiveReasoningEffort, maxTokens: outputLimit, system, prompt,
|
|
519
513
|
// 运行时按知识库给;接缝被单独调用时(测试)退回一个稳定值,
|
|
520
514
|
// 而不是留空 —— 留空会在 OpenCode 路由上 400。
|
|
521
515
|
sessionId: sessionId ?? 'knowledge:default',
|
|
@@ -534,14 +528,15 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
534
528
|
* 由 `isRetryableModelFailure` 放行,不在错的地方多等两分钟。
|
|
535
529
|
*/
|
|
536
530
|
return callModelWithRetry(
|
|
537
|
-
onDeltaOfAttempt => collectModelText(llm.stream(request) as AsyncIterable<never>, onUsage, onDeltaOfAttempt),
|
|
538
|
-
{ ...(onDelta ? { onDelta } : {}) },
|
|
531
|
+
onDeltaOfAttempt => collectModelText(llm.stream({ ...request, ...(signal ? { signal } : {}) }) as AsyncIterable<never>, onUsage, onDeltaOfAttempt),
|
|
532
|
+
{ ...(onDelta ? { onDelta } : {}), attempts: retryAttempts, signal },
|
|
539
533
|
)
|
|
540
534
|
},
|
|
541
535
|
}
|
|
542
536
|
}
|
|
543
537
|
let environment: KnowledgeEnvironment
|
|
544
538
|
const runtimeOptions = (): KnowledgeRuntimeOptions => ({
|
|
539
|
+
extractionTimeoutMinutes: () => extractionTimeoutForBase(DEFAULT_KB_ID),
|
|
545
540
|
environmentGuard: kind => environment.requireReady(kind),
|
|
546
541
|
// 0029:家当目录。配置里三个路径为空时,运行时从这里解析模型与解释器。
|
|
547
542
|
resourceRoot: resourceRoot(config.agentRoot),
|
|
@@ -582,6 +577,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
582
577
|
runtimeOptions: () => ({ resourceRoot: resourceRoot(config.agentRoot), modelSeam: modelSeamFor, environmentGuard: kind => environment.requireReady(kind) }),
|
|
583
578
|
})
|
|
584
579
|
compileModelForBase = id => bases.compileModelOf(id)
|
|
580
|
+
extractionTimeoutForBase = id => bases.extractionTimeoutMinutesOf(id)
|
|
585
581
|
runtime.baseId = bases.defaultId
|
|
586
582
|
activeKnowledgeBases = bases
|
|
587
583
|
environment = new KnowledgeEnvironment({
|
|
@@ -617,7 +613,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
617
613
|
// Reuse a matching persisted probe after restart; only stale or missing reports need a resource check.
|
|
618
614
|
if (environment.view().report.state !== 'ready') void environment.check(false).catch(() => undefined)
|
|
619
615
|
|
|
620
|
-
type CompileModelCatalogItem = { provider: string; id: string; name: string; description?: string; available: boolean; current?: boolean; reasoning?: { efforts: Array<{ id: string; name: string; description?: string }>; defaultEffort?: string } }
|
|
616
|
+
type CompileModelCatalogItem = { provider: string; id: string; name: string; description?: string; available: boolean; current?: boolean; defaultMaxTokens?: number; reasoning?: { efforts: Array<{ id: string; name: string; description?: string }>; defaultEffort?: string } }
|
|
621
617
|
const compileModelCatalog = async (kbId: string, queryModelRoute: { provider?: string; model?: string } = {}): Promise<{
|
|
622
618
|
selection: CompileModelSelection
|
|
623
619
|
default: { provider?: string; model?: string; reasoningEffort?: string; available: boolean }
|
|
@@ -630,7 +626,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
630
626
|
const llm = ctx.get('llm') as {
|
|
631
627
|
listModels?: (provider: string) => Promise<Array<{ id: string; name?: string; description?: string }>>
|
|
632
628
|
listProviders?: () => Array<{ id: string; name: string }>
|
|
633
|
-
resolveModelInfo?: (provider: string, model: string) => Promise<{ reasoning?: CompileModelCatalogItem['reasoning'] }>
|
|
629
|
+
resolveModelInfo?: (provider: string, model: string) => Promise<{ reasoning?: CompileModelCatalogItem['reasoning']; defaultMaxTokens?: number }>
|
|
634
630
|
} | undefined
|
|
635
631
|
const providers = [...new Set([...(llm?.listProviders?.() ?? []).map(provider => provider.id), defaultRoute.provider, compileModelForBase(kbId).provider].filter((value): value is string => Boolean(value)))]
|
|
636
632
|
const models: CompileModelCatalogItem[] = []
|
|
@@ -666,7 +662,11 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
666
662
|
if (requestedProvider && requestedModel && llm?.resolveModelInfo) {
|
|
667
663
|
const item = models.find(model => model.provider === requestedProvider && model.id === requestedModel)
|
|
668
664
|
if (item) {
|
|
669
|
-
try {
|
|
665
|
+
try {
|
|
666
|
+
const info = await llm.resolveModelInfo(requestedProvider, requestedModel)
|
|
667
|
+
item.reasoning = info.reasoning
|
|
668
|
+
if (info.defaultMaxTokens !== undefined) item.defaultMaxTokens = info.defaultMaxTokens
|
|
669
|
+
}
|
|
670
670
|
catch (cause) { failures.push(`思考强度目录:${cause instanceof Error ? cause.message : String(cause)}`) }
|
|
671
671
|
}
|
|
672
672
|
}
|
|
@@ -806,7 +806,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
806
806
|
/**
|
|
807
807
|
* 交付物落在**工作项目**下:`<工作区>/output/<日期>/`,不是 Agent 根路径。
|
|
808
808
|
*
|
|
809
|
-
*
|
|
809
|
+
* 根路径下的几段(知识 / 记忆 / 家当)是**这个 Agent 自己的东西**,跨项目
|
|
810
810
|
* 累积;交付物是**某个项目的产物**,换一个项目打开就不该再看见。两者判据不同,
|
|
811
811
|
* 所以落点也不同。
|
|
812
812
|
*
|
|
@@ -827,30 +827,6 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
827
827
|
},
|
|
828
828
|
})
|
|
829
829
|
|
|
830
|
-
// 意图记录模块 (issue 0020). The 意图 view was already one of this plugin's
|
|
831
|
-
// five panel tabs, but its Host lived in a second mount unit; 0020 folds it in
|
|
832
|
-
// so the declared information architecture and the code layout agree again.
|
|
833
|
-
// It keeps the `intent_record` / `intent_store_view` tools and `/intent/**` audit endpoints,
|
|
834
|
-
// and — critically — the `titaDataAgentIntent` Cordis service that `data-table`
|
|
835
|
-
// resolves by name.
|
|
836
|
-
// 意图只保留固定的会话审计契约;可编辑词表不再属于插件配置。
|
|
837
|
-
applyIntent(ctx, {
|
|
838
|
-
intentRoot: () => intentRoot(config.agentRoot),
|
|
839
|
-
})
|
|
840
|
-
// `applyIntent` provides this service from the same plugin fiber. During
|
|
841
|
-
// `apply()`, that fiber is still starting, so Cordis's default strict lookup
|
|
842
|
-
// hides the just-provided service until startup completes. The non-strict
|
|
843
|
-
// lookup is safe here because `applyIntent` ran immediately above.
|
|
844
|
-
const intentService = ctx.get(INTENT_SERVICE, false) as IntentService
|
|
845
|
-
/**
|
|
846
|
-
* `intent_*` 工具在 Host 层注册,使标准模式可以调用它们;工具执行端只接受
|
|
847
|
-
* standard 会话,其他 preset 不会写入或读取意图记录。
|
|
848
|
-
*/
|
|
849
|
-
ctx.effect(
|
|
850
|
-
() => registerIntentTools(ctx as unknown as { tools: { register(definition: unknown): unknown } }, intentService),
|
|
851
|
-
'tita knowledge: global intent tools',
|
|
852
|
-
)
|
|
853
|
-
|
|
854
830
|
/**
|
|
855
831
|
* 规则正文由当前会话 preset 决定;设置里的 volatile 引用在每次组装提示词时解包,
|
|
856
832
|
* 因此开关保存后立即生效,不需要重建 Agent 会话。
|
|
@@ -895,45 +871,37 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
895
871
|
if (writeTools.has(exec.name)) return Promise.resolve({ kind: 'ask', reason: '该操作会写入 Tita Knowledge Store,需要本轮用户审批。' })
|
|
896
872
|
return next()
|
|
897
873
|
})
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
874
|
+
const scopeParameters = { object_id: { type: 'string', description: 'Known Object ID. Omit for all; empty matches empty only.' }, action_id: { type: 'string' }, scene_id: { type: 'string' } }
|
|
875
|
+
const scopeOf = (args: Args) => ({ objectId: args.object_id, actionId: args.action_id, sceneId: args.scene_id, wikiKind: args.wiki_kind })
|
|
876
|
+
ctx.tools.register(tool('recall_knowledge', 'Search H2 sections, rank and deduplicate Wiki files, then deliver complete Wiki Fact / Wiki Way within the budget. Query the current subquestion for methods. top_k counts distinct Wiki candidates; top_n counts delivered hit files. Optional known coordinate IDs and wiki_kind filter every retrieval route before ranking. foundCandidates and contentDelivered distinguish matches from delivered bodies. For budget omissions call read_knowledge_wiki with budget.omitted[].read.args. Scores indicate retrieval relevance, not factual truth. Historical Case does not prove the present root cause.', {
|
|
877
|
+
query: { type: 'string', required: true }, ...scopeParameters, wiki_kind: { type: 'string', enum: ['fact', 'way'] },
|
|
878
|
+
limit: { type: 'integer', description: 'Compatibility alias for top_k.' }, top_k: { type: 'integer', description: 'Distinct Wiki candidates, 1–50.' }, top_n: { type: 'integer', description: 'Maximum complete hit Wiki files to deliver, no more than top_k.' },
|
|
879
|
+
rerank: { type: 'boolean' }, context_max_chars: { type: 'integer', description: 'Complete-context character budget; 0 explicitly disables the limit. Omitted files have stable read references in budget.omitted. overflowPath is a local diagnostic file, not the Agent continuation contract.' },
|
|
880
|
+
include_problems: { type: 'boolean' }, mode: { type: 'string', enum: ['fts', 'vector', 'hybrid'] }, kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' }, parent_subtask_id: { type: 'string' }, run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' },
|
|
881
|
+
}, async (args, exec) => withRuntime(args.kb, async target => agentRecallEnvelope(await target.recall(args.query, args.top_k ?? args.limit, args.include_problems ?? false, args.mode as RecallMode | undefined, lineageOf(args, exec), { ...scopeOf(args), contextTopN: args.top_n, rerank: args.rerank, contextMaxChars: args.context_max_chars }))), {
|
|
882
|
+
call: recallCallView, meta: (args, value) => recallMetaOf({ ...dataOf(value), query: args?.query }), result: resultViewOf(recallResultMarkdown),
|
|
903
883
|
}))
|
|
904
|
-
ctx.tools.register(tool('
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
if (ids.length > 50) return failure({ cases: [], missing_ids: [] }, 'CASE_IDS_LIMIT', '一次最多读取 50 个 Case ID。', false)
|
|
914
|
-
if (args.version !== undefined && (ids.length !== 1 || !Number.isInteger(args.version) || args.version < 1)) return failure({ cases: [], missing_ids: ids }, 'CASE_VERSION_INVALID', '读取历史版本时,请传一个 Case ID 和正整数 version。', false)
|
|
915
|
-
const records = await Promise.all(ids.map(id => target.readCase(id, args.version)))
|
|
916
|
-
return success({ cases: records.filter(record => record !== undefined), missing_ids: ids.filter((_, index) => records[index] === undefined) })
|
|
917
|
-
})))
|
|
918
|
-
ctx.tools.register(tool('get_knowledge_events', 'Read Event records referenced by recalled WikiCards. Pass event IDs from card metadata.event_refs; the result includes full timelines, evidence, outcomes, and missing fields.', {
|
|
919
|
-
event_ids: { type: 'array', required: true, description: 'Event IDs from WikiCard metadata.event_refs; maximum 50 IDs.' },
|
|
920
|
-
kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' },
|
|
921
|
-
run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' },
|
|
922
|
-
}, async args => withRuntime(args.kb, async target => {
|
|
923
|
-
const ids = [...new Set((Array.isArray(args.event_ids) ? args.event_ids : [])
|
|
924
|
-
.filter((id): id is string => typeof id === 'string' && id.trim() !== '').map(id => id.trim()))]
|
|
925
|
-
if (ids.length === 0) return failure({ events: [], missing_ids: [] }, 'EVENT_IDS_REQUIRED', '请传入 WikiCard 的 event_refs 中的 Event ID。', false)
|
|
926
|
-
if (ids.length > 50) return failure({ events: [], missing_ids: [] }, 'EVENT_IDS_LIMIT', '一次最多读取 50 个 Event ID;请分批读取该 WikiCard 的 event_refs。', false)
|
|
927
|
-
const byId = new Map((await target.loadEvents()).map(event => [event.id, event]))
|
|
928
|
-
const events = ids.flatMap(id => {
|
|
929
|
-
const event = byId.get(id)
|
|
930
|
-
return event ? [event] : []
|
|
931
|
-
})
|
|
932
|
-
return success({ events, missing_ids: ids.filter(id => !byId.has(id)) })
|
|
933
|
-
})))
|
|
884
|
+
ctx.tools.register(tool('read_knowledge_wiki', 'Read a complete Wiki through a stable target or browse an Object directory. Omitted axes mean all; empty axes mean empty only. Directory mode omits content by default. include_content requests complete files within max_chars. If a file does not fit, follow nextRead for explicit body pages, concatenate them and read all conditions before applying a Procedure. expected_version prevents mixing Wiki versions. contentPage is partial unless contentComplete is true. Outgoing links and backlinks are independently bounded by link_limit; advance link_offset using links.nextOffset. Follow only relevant links.', {
|
|
885
|
+
target: { type: 'string' }, ...scopeParameters, object: { type: 'string' }, wiki_kind: { type: 'string', enum: ['fact', 'way'] },
|
|
886
|
+
include_content: { type: 'boolean' }, limit: { type: 'integer' }, offset: { type: 'integer' }, max_chars: { type: 'integer' }, content_offset: { type: 'integer' }, content_max_chars: { type: 'integer' }, expected_version: { type: 'integer' }, link_limit: { type: 'integer' }, link_offset: { type: 'integer' }, kb: { type: 'string' },
|
|
887
|
+
}, async args => withRuntime(args.kb, target => target.readWiki({ ...scopeOf(args), target: args.target, object: args.object, includeContent: args.include_content, limit: args.limit, offset: args.offset, maxChars: args.max_chars, contentOffset: args.content_offset, contentMaxChars: args.content_max_chars, expectedVersion: args.expected_version, linkLimit: args.link_limit, linkOffset: args.link_offset }))))
|
|
888
|
+
ctx.tools.register(tool('get_knowledge_cases', 'Find historical Case by current subquestion or exact coordinates, or read referenced case_ids. Search returns a paged directory with version-pinned nextRead references; ID reads include full records within max_chars. Pass one case_id and its version to read historical evidence. Oversized records use explicit contentPage JSON pages; concatenate and parse before using them. Historical actions, observations and outcomes do not prove the current cause.', {
|
|
889
|
+
case_ids: { type: 'array', description: '1–50 known Case IDs; omit for search.' }, query: { type: 'string' }, ...scopeParameters,
|
|
890
|
+
version: { type: 'integer' }, include_content: { type: 'boolean' }, limit: { type: 'integer' }, offset: { type: 'integer' }, max_chars: { type: 'integer' }, content_offset: { type: 'integer' },
|
|
891
|
+
kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' }, run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' },
|
|
892
|
+
}, async args => withRuntime(args.kb, target => target.queryCases({ ...scopeOf(args), ids: args.case_ids, version: args.version, query: args.query, includeContent: args.include_content, limit: args.limit, offset: args.offset, maxChars: args.max_chars, contentOffset: args.content_offset }))))
|
|
934
893
|
ctx.tools.register(tool('check_knowledge_embeddings', 'Inspect the local embedding provider and binary/int8 vector index without loading the model.', { kb: { type: 'string' }, session_key: { type: 'string' } }, async args => withRuntime(args.kb, target => target.embeddingHealth())))
|
|
935
894
|
ctx.tools.register(tool('query_knowledge_graph', 'Query entities, neighbors, paths, lists and relation types in the compiled knowledge graph.', { action: { type: 'string', enum: ['entity', 'neighbors', 'path', 'list', 'types'], required: true }, entity_id: { type: 'string' }, id: { type: 'string' }, entity_type: { type: 'string' }, from_id: { type: 'string' }, to_id: { type: 'string' }, type: { type: 'string' }, status: { type: 'string' }, include_problem: { type: 'boolean' }, limit: { type: 'integer' }, depth: { type: 'integer' }, kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' }, parent_subtask_id: { type: 'string' }, run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' } }, async (args, exec) => withRuntime(args.kb, target => target.graphQuery(args.action, args.entity_id ?? args.id, args.entity_type ?? args.type, args.depth ?? 1, { fromId: args.from_id, toId: args.to_id, status: args.status, includeProblem: args.include_problem ?? false, limit: args.limit ?? 50 }, lineageOf(args, exec)))))
|
|
936
|
-
ctx.tools.register(tool('query_source_truth', 'Read
|
|
895
|
+
ctx.tools.register(tool('query_source_truth', 'Read a precise Source Raw using source_id plus the evidence version, optional content_hash and [start,end) UTF-16 offsets. Prefer readRef returned by knowledge tools. IDs are opaque and scoped to kb; never infer one from a title, path, hash or external_id. Unknown IDs, unavailable versions and mismatched hashes or quotes fail explicitly; there is no fallback to another source. Exact-read offset is relative to the selected range; follow nextRead to continue. Without source_id, query discovers sources and returns version-pinned readRef for precise follow-up. Discovery uses source_offset for source pages and offset for text pages; advance with returnedChars and nextSourceOffset. Do not apply incomplete evidence.', {
|
|
896
|
+
source_id: { type: 'string' }, version: { type: 'integer' }, content_hash: { type: 'string' }, start: { type: 'integer' }, end: { type: 'integer' }, quote: { type: 'string' }, query: { type: 'string' }, limit: { type: 'integer' }, max_chars: { type: 'integer' }, offset: { type: 'integer' }, source_offset: { type: 'integer' }, kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' }, run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' },
|
|
897
|
+
}, async (args, exec) => withRuntime(args.kb, async target => {
|
|
898
|
+
if (args.source_id !== undefined) {
|
|
899
|
+
if (args.query !== undefined || args.source_offset !== undefined) return failure({}, 'SOURCE_SELECTION_CONFLICT', '精确读取 source_id 时不要传 query 或 source_offset。', false)
|
|
900
|
+
return target.readSource({ sourceId: args.source_id, version: args.version, contentHash: args.content_hash, start: args.start, end: args.end, quote: args.quote, maxChars: args.max_chars, offset: args.offset }, lineageOf(args, exec))
|
|
901
|
+
}
|
|
902
|
+
if (typeof args.query !== 'string' || !args.query.trim() || [args.version, args.content_hash, args.start, args.end, args.quote].some(value => value !== undefined)) return failure({}, 'SOURCE_QUERY_REQUIRED', '搜索请传非空 query;版本、哈希和引文范围读取必须提供 source_id。', false)
|
|
903
|
+
return target.sourceTruth(args.query, args.limit ?? 20, args.max_chars ?? 40000, lineageOf(args, exec), args.offset ?? 0, args.source_offset ?? 0)
|
|
904
|
+
})))
|
|
937
905
|
/**
|
|
938
906
|
* 知识库 id 的**发现入口**。
|
|
939
907
|
*
|
|
@@ -984,7 +952,12 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
984
952
|
const imagePublishDescription = 'Publish one local image into a knowledge base assets bucket without user approval. Pass exactly one of `file_path` or `content_base64` (standard Base64 or a data URI); optional `filename` overrides the stored name, and optional `mime_type` must match the file bytes. Supports PNG, JPEG, GIF, WebP, AVIF, BMP and SVG up to 20 MiB. Returns the local asset URL and Markdown image reference.'
|
|
985
953
|
ctx.tools.register(tool('image_publish', imagePublishDescription, imagePublishParameters, publishImage))
|
|
986
954
|
|
|
987
|
-
ctx.tools.register(tool('
|
|
955
|
+
ctx.tools.register(tool('allocate_source_ids', 'Allocate opaque Source Raw IDs before import. Keep one ID per independent input; reuse it across conversion, transport retries, renames and updates. IDs are not reserved documents until imported. Existing sources keep their current IDs. Never allocate a new ID to update an existing source.', { count: { type: 'integer', description: '1–100, default 1.' } }, async args => {
|
|
956
|
+
const count = args.count ?? 1
|
|
957
|
+
if (!Number.isInteger(count) || count < 1 || count > 100) return failure({ source_ids: [] }, 'SOURCE_COUNT_INVALID', 'count 必须为 1–100。', false)
|
|
958
|
+
return success({ source_ids: Array.from({ length: count }, () => newSourceId()) })
|
|
959
|
+
}))
|
|
960
|
+
ctx.tools.register(tool('add_source', 'Add or update Source Raw. For links/platform documents pass source_url or source_identity: identity lookup reuses the existing source_id, otherwise allocates one. For independent files first use allocate_source_ids and retain the ID across retries. Updates use the known source_id and preferably expected_version from an exact read (0 means create only). Titles, paths, body hashes and arbitrary external_id are not update keys. A preallocated unused ID may resolve to an existing document; always use the returned sourceId. Known IDs with contradictory origin fail. Returns a version-pinned readRef. DSH requests one user approval before execution.', { title: { type: 'string' }, content: { type: 'string', required: true }, external_id: { type: 'string' }, metadata: { type: 'json' }, source_id: { type: 'string' }, source_url: { type: 'string', description: 'Explicit acquisition URL, preserving query and fragment. Do not copy an unverified link from body text.' }, source_identity: { type: 'json', description: '{system, scope, document_id, document_type?, url?}; native identity requires system, scope and document_id. URL-only identity can use {url, scope?}.' }, expected_version: { type: 'integer' }, allow_duplicate_title: { type: 'boolean', description: '默认 false:标题与已有来源重复时拒收(SOURCE_TITLE_DUPLICATED),要求改标题。传 true 才允许并存。' }, kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' }, run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' } }, async (args, exec) => (typeof args.source_id !== 'string' || !args.source_id.trim()) && args.source_url === undefined && args.source_identity === undefined ? failure({ sourceId: '' }, 'SOURCE_ID_REQUIRED', '请先分配 source_id;重试或更新复用已有 ID。', false) : withRuntime(args.kb, target => target.addSource({ title: args.title, content: args.content, externalId: args.external_id, metadata: args.metadata, sourceId: args.source_id, sourceUrl: args.source_url, sourceIdentity: args.source_identity, expectedVersion: args.expected_version, ...(args.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}), lineage: lineageOf(args, exec) })), {
|
|
988
961
|
call: addSourceCallView,
|
|
989
962
|
meta: (args, value) => addSourceMetaOf(args, dataOf(value)),
|
|
990
963
|
result: resultViewOf(addSourceResultMarkdown),
|
|
@@ -1000,10 +973,10 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1000
973
|
* 两者的差别只在「一次多少份」,落盘路径、分类闸门、编译链路完全共用 ——
|
|
1001
974
|
* 这个工具的 `execute` 就是循环调 `importSources`,而后者逐条调 `addSource`。
|
|
1002
975
|
*
|
|
1003
|
-
* `compile`
|
|
1004
|
-
*
|
|
976
|
+
* `compile` 默认关闭:先保存来源,再由调用方决定何时处理。显式 true 才跑全链路;
|
|
977
|
+
* 未触发处理时工具返回 `compile: null`。
|
|
1005
978
|
*/
|
|
1006
|
-
ctx.tools.register(tool('import_sources', 'Import many SourceRaw documents in one call. By default only stores sources. Explicit compile:true runs extract, normalize, materialize, and index publication for the imported documents. Per-source import failures are reported without aborting the batch. DSH requests one user approval before execution.', { sources: { type: 'array', required: true, description: `来源数组,每项 { title?, content, external_id?, metadata?, allow_duplicate_title? };一次最多 ${MAX_IMPORT_SOURCES} 份,超了请分批。标题与已有来源重复的那一份会被拒(SOURCE_TITLE_DUPLICATED),逐条报出来而不是整批失败` }, compile: { type: 'boolean', description: '默认 false:只保存来源;显式 true 才运行完整抽取、归一与物化' }, kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' }, run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' } }, async (args, exec) => withRuntime(args.kb, target => target.importSources({ sources: (Array.isArray(args.sources) ? args.sources : []).map(item => ({ ...(item?.title ? { title: item.title } : {}), content: item?.content, ...(item?.external_id ? { externalId: item.external_id } : {}), ...(item?.metadata ? { metadata: item.metadata } : {}), ...(item?.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}) })), compile: args.compile === true, lineage: lineageOf(args, exec) })), { call: importCallView, meta: (_args, value) => importMetaOf(dataOf(value)), result: resultViewOf(importResultMarkdown) }))
|
|
979
|
+
ctx.tools.register(tool('import_sources', 'Import many SourceRaw documents in one call. By default only stores sources. Explicit compile:true runs extract, normalize, materialize, and index publication for the imported documents. Per-source import failures are reported without aborting the batch. DSH requests one user approval before execution.', { sources: { type: 'array', required: true, description: `来源数组,每项 { source_id?, source_url?, source_identity?, expected_version?, title?, content, external_id?, metadata?, allow_duplicate_title? };链接提供 source_url 或 source_identity,先查来源映射;独立文件先分配 source_id,重试沿用;以后续返回的 sourceId 为准;更新旧来源必须传其 source_id;一次最多 ${MAX_IMPORT_SOURCES} 份,超了请分批。标题与已有来源重复的那一份会被拒(SOURCE_TITLE_DUPLICATED),逐条报出来而不是整批失败` }, compile: { type: 'boolean', description: '默认 false:只保存来源;显式 true 才运行完整抽取、归一与物化' }, kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' }, run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' } }, async (args, exec) => Array.isArray(args.sources) && args.sources.some(item => (typeof item?.source_id !== 'string' || !item.source_id.trim()) && item?.source_url === undefined && item?.source_identity === undefined) ? failure({}, 'SOURCE_ID_REQUIRED', '请先为每个输入分配 source_id;重试或更新沿用已有 ID。', false) : withRuntime(args.kb, target => target.importSources({ sources: (Array.isArray(args.sources) ? args.sources : []).map(item => ({ ...(item?.source_id ? { sourceId: item.source_id } : {}), ...(item?.expected_version === undefined ? {} : { expectedVersion: item.expected_version }), ...(item?.source_url === undefined ? {} : { sourceUrl: item.source_url }), ...(item?.source_identity === undefined ? {} : { sourceIdentity: item.source_identity }), ...(item?.title ? { title: item.title } : {}), content: item?.content, ...(item?.external_id ? { externalId: item.external_id } : {}), ...(item?.metadata ? { metadata: item.metadata } : {}), ...(item?.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}) })), compile: args.compile === true, lineage: lineageOf(args, exec) })), { call: importCallView, meta: (_args, value) => importMetaOf(dataOf(value)), result: resultViewOf(importResultMarkdown) }))
|
|
1007
980
|
/**
|
|
1008
981
|
* `propose_wiki_cards` —— 把一批卡片**直接写进 `02_wiki`**(工具名不变,语义在 0044 改了)。
|
|
1009
982
|
*
|
|
@@ -1146,6 +1119,17 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1146
1119
|
if ('failure' in resolved) return sendEnvelope(res, resolved.failure)
|
|
1147
1120
|
return send(res, 200, success(await resolved.runtime.stats()))
|
|
1148
1121
|
} },
|
|
1122
|
+
{ kind: 'exact' as const, path: '/knowledge/wiki', handler: async (req: IncomingMessage, res: ServerResponse) => {
|
|
1123
|
+
if (req.method !== 'GET') return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', 'Wiki 读取只接受 GET'))
|
|
1124
|
+
const url = query(req)
|
|
1125
|
+
const resolved = resolveBase(url.searchParams.get('kb'))
|
|
1126
|
+
if ('failure' in resolved) return sendEnvelope(res, resolved.failure)
|
|
1127
|
+
return sendEnvelope(res, await resolved.runtime.readWiki({ target: url.searchParams.get('target') ?? undefined, objectId: url.searchParams.get('object_id') ?? undefined, object: url.searchParams.get('object') ?? undefined,
|
|
1128
|
+
actionId: url.searchParams.get('action_id') ?? undefined, sceneId: url.searchParams.get('scene_id') ?? undefined, wikiKind: (url.searchParams.get('wiki_kind') ?? undefined) as 'fact' | 'way' | undefined,
|
|
1129
|
+
includeContent: url.searchParams.has('include_content') ? url.searchParams.get('include_content') === 'true' : undefined,
|
|
1130
|
+
...Object.fromEntries([['max_chars', 'maxChars'], ['content_offset', 'contentOffset'], ['content_max_chars', 'contentMaxChars'], ['expected_version', 'expectedVersion'], ['link_limit', 'linkLimit'], ['link_offset', 'linkOffset']].filter(([key]) => url.searchParams.has(key!)).map(([key, field]) => [field, Number(url.searchParams.get(key!))])),
|
|
1131
|
+
limit: url.searchParams.has('limit') ? Number(url.searchParams.get('limit')) : undefined, offset: url.searchParams.has('offset') ? Number(url.searchParams.get('offset')) : undefined }))
|
|
1132
|
+
} },
|
|
1149
1133
|
{ kind: 'exact' as const, path: '/knowledge/cases', handler: async (req: IncomingMessage, res: ServerResponse) => {
|
|
1150
1134
|
if (req.method !== 'GET') return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', 'Case 只接受 GET'))
|
|
1151
1135
|
const resolved = resolveBase(query(req).searchParams.get('kb'))
|
|
@@ -1173,6 +1157,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1173
1157
|
(url.searchParams.get('mode') ?? undefined) as RecallMode | undefined,
|
|
1174
1158
|
lineageFromUrl(url),
|
|
1175
1159
|
{
|
|
1160
|
+
...scopeOf(Object.fromEntries(['object_id', 'action_id', 'scene_id', 'wiki_kind'].filter(key => url.searchParams.has(key)).map(key => [key, url.searchParams.get(key)]))),
|
|
1176
1161
|
...(topN === null ? {} : { contextTopN: Number(topN) }),
|
|
1177
1162
|
...(rerankParam === null ? {} : { rerank: rerankParam !== 'false' && rerankParam !== '0' }),
|
|
1178
1163
|
...(url.searchParams.get('context_max_chars') === null ? {} : { contextMaxChars: Number(url.searchParams.get('context_max_chars')) }),
|
|
@@ -1255,7 +1240,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1255
1240
|
const pipeline = await resolved.runtime.pipeline()
|
|
1256
1241
|
const limit = Math.min(100, Math.max(1, Number(url.searchParams.get('limit')) || 20))
|
|
1257
1242
|
const offset = Math.max(0, Number(url.searchParams.get('offset')) || 0)
|
|
1258
|
-
return send(res, 200, success({ ...pipeline.store.tasks(limit, offset), extractions: await resolved.runtime.listExtractionRuns({ limit, offset }) }))
|
|
1243
|
+
return send(res, 200, success({ ...pipeline.store.tasks(limit, offset), activeTasks: pipeline.store.activeTasks(), extractions: await resolved.runtime.listExtractionRuns({ limit, offset }) }))
|
|
1259
1244
|
} catch (cause) { return send(res, 400, envelopeError('PIPELINE_HISTORY_FAILED', cause instanceof Error ? cause.message : String(cause))) }
|
|
1260
1245
|
} },
|
|
1261
1246
|
{ kind: 'exact' as const, path: '/knowledge/pipeline-task', handler: async (req: IncomingMessage, res: ServerResponse) => {
|
|
@@ -1270,6 +1255,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1270
1255
|
if (!task) return send(res, 404, envelopeError('PIPELINE_NOT_FOUND', '任务不存在'))
|
|
1271
1256
|
if (req.method === 'GET') return send(res, 200, success({ task, ...pipeline.store.events(id, Math.max(0, Number(url.searchParams.get('after')) || 0)) }))
|
|
1272
1257
|
if (req.method !== 'POST') return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', '任务只接受 GET 或 POST'))
|
|
1258
|
+
if (body.action === 'retry' && PIPELINE_ACTIVE_STATES.includes(task.state)) throw new Error('任务仍在运行,请先停止或等待完成')
|
|
1273
1259
|
if (body.action === 'retry') return send(res, 202, success({ task: pipeline.start(task.stage, { ...task.selection, force: false }).task }))
|
|
1274
1260
|
if (!['pause', 'resume', 'cancel'].includes(body.action)) throw new Error('不支持的任务操作')
|
|
1275
1261
|
return send(res, 200, success({ task: pipeline.control(id, body.action) }))
|
|
@@ -1362,8 +1348,13 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1362
1348
|
sources: (Array.isArray(body.sources) ? body.sources : []).map((item: Record<string, unknown>) => ({
|
|
1363
1349
|
...(item?.title ? { title: String(item.title) } : {}),
|
|
1364
1350
|
content: item?.content as string,
|
|
1351
|
+
...(item?.source_id ? { sourceId: String(item.source_id) } : {}),
|
|
1352
|
+
...(item?.source_url === undefined ? {} : { sourceUrl: item.source_url }),
|
|
1353
|
+
...(item?.source_identity === undefined ? {} : { sourceIdentity: item.source_identity }),
|
|
1354
|
+
...(item?.expected_version === undefined ? {} : { expectedVersion: item.expected_version }),
|
|
1365
1355
|
...(item?.external_id ? { externalId: String(item.external_id) } : {}),
|
|
1366
1356
|
...(item?.metadata && typeof item.metadata === 'object' ? { metadata: item.metadata as Record<string, unknown> } : {}),
|
|
1357
|
+
...(item?.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}),
|
|
1367
1358
|
})),
|
|
1368
1359
|
compile: body.compile === true,
|
|
1369
1360
|
lineage: lineageOf(body),
|
|
@@ -1378,12 +1369,16 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1378
1369
|
const body = await readBody(req, 1_200_000)
|
|
1379
1370
|
const resolved = resolveBase(body.kb ?? query(req).searchParams.get('kb'))
|
|
1380
1371
|
if ('failure' in resolved) return sendEnvelope(res, resolved.failure)
|
|
1381
|
-
return send(res, 200, await resolved.runtime.addSource({ title: body.title, content: body.content, externalId: body.external_id, metadata: body.metadata, sourceId: body.source_id, ...(body.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}), lineage: lineageOf(body) }))
|
|
1372
|
+
return send(res, 200, await resolved.runtime.addSource({ title: body.title, content: body.content, externalId: body.external_id, metadata: body.metadata, sourceId: body.source_id, sourceUrl: body.source_url, sourceIdentity: body.source_identity, expectedVersion: body.expected_version, ...(body.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}), lineage: lineageOf(body) }))
|
|
1382
1373
|
}
|
|
1383
1374
|
if (req.method !== 'GET') return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', '该 knowledge endpoint 只接受 GET 或 POST'))
|
|
1384
1375
|
const url = query(req)
|
|
1385
1376
|
const resolved = resolveBase(url.searchParams.get('kb'))
|
|
1386
1377
|
if ('failure' in resolved) return sendEnvelope(res, resolved.failure)
|
|
1378
|
+
if (url.searchParams.has('source_id')) return sendEnvelope(res, await resolved.runtime.readSource({ sourceId: url.searchParams.get('source_id')!,
|
|
1379
|
+
contentHash: url.searchParams.get('content_hash') ?? undefined, quote: url.searchParams.get('quote') ?? undefined,
|
|
1380
|
+
...Object.fromEntries([['version', 'version'], ['start', 'start'], ['end', 'end'], ['offset', 'offset'], ['max_chars', 'maxChars']].filter(([key]) => url.searchParams.has(key!)).map(([key, field]) => [field, Number(url.searchParams.get(key!))])),
|
|
1381
|
+
}, lineageFromUrl(url)))
|
|
1387
1382
|
return send(res, 200, await resolved.runtime.sourceTruth(
|
|
1388
1383
|
url.searchParams.get('query') ?? '',
|
|
1389
1384
|
Number(url.searchParams.get('limit') ?? 20) || 20,
|
|
@@ -1964,9 +1959,8 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1964
1959
|
* ```
|
|
1965
1960
|
* /knowledge/pages/trial?kb=<知识库 id> 抽取试跑
|
|
1966
1961
|
* /knowledge/pages/run?kb=<库 id>&run=<轨迹 id> 一次抽取的完整报告
|
|
1967
|
-
* /knowledge/pages/tasks?kb=<知识库 id>
|
|
1962
|
+
* /knowledge/pages/tasks?kb=<知识库 id> 知识处理与编译设置
|
|
1968
1963
|
* /knowledge/pages/help/compile 编译链路
|
|
1969
|
-
* /knowledge/pages/help/intent 意图的逻辑
|
|
1970
1964
|
* /knowledge/pages/help/overview?node=... 五个 Tab 的方案说明
|
|
1971
1965
|
* ```
|
|
1972
1966
|
*
|
|
@@ -1992,9 +1986,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
1992
1986
|
: rest[1] === 'run'
|
|
1993
1987
|
? '抽取轨迹'
|
|
1994
1988
|
: rest[1] === 'tasks'
|
|
1995
|
-
? '
|
|
1996
|
-
: rest[1] === 'help' && rest[2] === 'intent'
|
|
1997
|
-
? '意图的逻辑'
|
|
1989
|
+
? '知识处理'
|
|
1998
1990
|
: rest[1] === 'help' && rest[2] === 'compile'
|
|
1999
1991
|
? '编译链路'
|
|
2000
1992
|
: rest[1] === 'help' && rest[2] === 'overview' ? 'Agent 方案说明' : '整页'
|
|
@@ -2028,12 +2020,14 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
2028
2020
|
if (rest.length === 2) {
|
|
2029
2021
|
if (method === 'PATCH') {
|
|
2030
2022
|
const body = await readBody(req)
|
|
2023
|
+
if (body.extractionTimeoutMinutes !== undefined && !isExtractionTimeoutMinutes(body.extractionTimeoutMinutes)) return send(res, 400, envelopeError('EXTRACTION_TIMEOUT_INVALID', '文档抽取时限只能选择 30、60 或 100 分钟,最大 100 分钟。'))
|
|
2031
2024
|
if (body.compileModel && typeof body.compileModel === 'object') {
|
|
2032
2025
|
const selection = body.compileModel as CompileModelSelection
|
|
2033
2026
|
const catalog = await compileModelCatalog(baseId)
|
|
2034
2027
|
const provider = selection.mode === 'fixed' ? selection.provider : catalog.default.provider
|
|
2035
2028
|
const model = selection.mode === 'fixed' ? selection.model : catalog.default.model
|
|
2036
2029
|
if (selection.mode !== 'fixed' && selection.mode !== 'default') return send(res, 400, envelopeError('MODEL_INVALID', '请选择跟随默认或固定模型'))
|
|
2030
|
+
if (selection.maxTokens !== undefined && (!Number.isSafeInteger(selection.maxTokens) || selection.maxTokens <= 0)) return send(res, 400, envelopeError('MODEL_INVALID', '单次最大输出必须是正整数 token;留空表示跟随模型默认。'))
|
|
2037
2031
|
if (selection.mode === 'fixed' && (!provider || !model)) return send(res, 400, envelopeError('MODEL_INVALID', '固定模型需要 provider 和 model'))
|
|
2038
2032
|
const llm = ctx.get('llm')
|
|
2039
2033
|
if (provider && model && llm?.resolveCallConfig) {
|
|
@@ -2041,7 +2035,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
2041
2035
|
const info = selection.reasoningEffort ? await llm.resolveModelInfo(provider, model) : undefined
|
|
2042
2036
|
const effort = info?.reasoning?.efforts.find(item => item.id === selection.reasoningEffort)
|
|
2043
2037
|
if (selection.reasoningEffort && !effort) throw new Error('所选模型不支持这个思考强度,请重新选择')
|
|
2044
|
-
await llm.resolveCallConfig({ provider, model, ...(effort ? { reasoningEffort: effort.id } : {}) })
|
|
2038
|
+
await llm.resolveCallConfig({ provider, model, ...(effort ? { reasoningEffort: effort.id } : {}), ...(selection.maxTokens !== undefined ? { maxTokens: selection.maxTokens } : {}) })
|
|
2045
2039
|
}
|
|
2046
2040
|
catch (cause) { return send(res, 400, envelopeError('MODEL_INVALID', cause instanceof Error ? cause.message : String(cause))) }
|
|
2047
2041
|
} else if (selection.reasoningEffort) return send(res, 400, envelopeError('MODEL_INVALID', '模型不可用,无法校验思考强度'))
|
|
@@ -2049,6 +2043,7 @@ export function apply(ctx: Context, pluginConfig: KnowledgePluginConfig = {}): v
|
|
|
2049
2043
|
const updated = await bases.update(baseId, {
|
|
2050
2044
|
name: body.name, description: body.description,
|
|
2051
2045
|
extractionNote: typeof body.extractionNote === 'string' ? body.extractionNote : undefined,
|
|
2046
|
+
extractionTimeoutMinutes: body.extractionTimeoutMinutes,
|
|
2052
2047
|
compileModel: body.compileModel && typeof body.compileModel === 'object'
|
|
2053
2048
|
? body.compileModel
|
|
2054
2049
|
: undefined,
|
package/src/knowledge-bases.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import { defaultKnowledgeRoot, failure, KnowledgeRuntime, success, type KnowledgeRuntimeOptions } from './runtime.js'
|
|
32
32
|
import { defaultAgentRoot, knowledgeBaseRoot, memoryRoot, resourceRoot } from './layout.js'
|
|
33
33
|
import { writeTextAtomic } from './fs-atomic.js'
|
|
34
|
+
import { DEFAULT_EXTRACTION_TIMEOUT_MINUTES, isExtractionTimeoutMinutes, normalizeExtractionTimeoutMinutes, type ExtractionTimeoutMinutes } from './pipeline/types.js'
|
|
34
35
|
|
|
35
36
|
/** 持久化形态:配置 + 计算字段缓存分区。 */
|
|
36
37
|
interface KnowledgeBaseRecord {
|
|
@@ -49,6 +50,7 @@ interface KnowledgeBaseRecord {
|
|
|
49
50
|
extractionNote: string
|
|
50
51
|
/** 编译模型路由;它是注册表配置,不属于知识正文。 */
|
|
51
52
|
compileModel: CompileModelSelection
|
|
53
|
+
extractionTimeoutMinutes: ExtractionTimeoutMinutes
|
|
52
54
|
createdAt: string
|
|
53
55
|
updatedAt: string
|
|
54
56
|
}
|
|
@@ -120,12 +122,13 @@ function textOr(input: unknown, fallback: string): string {
|
|
|
120
122
|
function compileModelOf(input: unknown): CompileModelSelection {
|
|
121
123
|
const value = record(input)
|
|
122
124
|
const effort = typeof value.reasoningEffort === 'string' && value.reasoningEffort.trim() ? { reasoningEffort: value.reasoningEffort.trim() } : {}
|
|
123
|
-
|
|
125
|
+
const output = typeof value.maxTokens === 'number' && Number.isSafeInteger(value.maxTokens) && value.maxTokens > 0 ? { maxTokens: value.maxTokens } : {}
|
|
126
|
+
if (value.mode !== 'fixed') return { ...DEFAULT_COMPILE_MODEL, ...effort, ...output }
|
|
124
127
|
const provider = typeof value.provider === 'string' ? value.provider.trim() : ''
|
|
125
128
|
const model = typeof value.model === 'string' ? value.model.trim() : ''
|
|
126
129
|
// 旧版本没有这个字段,或留下了半截配置时,安全地回到 DSH 默认模型。
|
|
127
130
|
if (!provider || !model) return { ...DEFAULT_COMPILE_MODEL }
|
|
128
|
-
return { mode: 'fixed', provider, model, ...effort }
|
|
131
|
+
return { mode: 'fixed', provider, model, ...effort, ...output }
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
function normalizeExt(value: unknown, fallback: string): string {
|
|
@@ -140,6 +143,7 @@ function emptyBase(id: string): KnowledgeBase {
|
|
|
140
143
|
id, name: id, description: '', rootDir: '', isDefault: false,
|
|
141
144
|
extractionNote: '',
|
|
142
145
|
compileModel: { ...DEFAULT_COMPILE_MODEL },
|
|
146
|
+
extractionTimeoutMinutes: DEFAULT_EXTRACTION_TIMEOUT_MINUTES,
|
|
143
147
|
createdAt: '', updatedAt: '', sourceCount: 0, cardCount: 0, noteCount: 0, buildVersion: 0,
|
|
144
148
|
}
|
|
145
149
|
}
|
|
@@ -243,6 +247,7 @@ export class KnowledgeBases {
|
|
|
243
247
|
// 卡片模板是系统固定契约,不按知识库维护注册表。
|
|
244
248
|
// `KnowledgeRuntime` 自己提供默认模板。
|
|
245
249
|
extractionNote: () => this.extractionNoteOf(base.id),
|
|
250
|
+
extractionTimeoutMinutes: () => this.extractionTimeoutMinutesOf(base.id),
|
|
246
251
|
// 0032:记忆段跟着**这个库**走,二级用 id。`agentRoot` 没给时(只测注册表
|
|
247
252
|
// 逻辑的单元测试)留空,runtime 会退回知识树里的 `04_memory`。
|
|
248
253
|
...(this.options.agentRoot ? { memoryRoot: memoryRoot(this.options.agentRoot(), base.id) } : {}),
|
|
@@ -338,6 +343,7 @@ export class KnowledgeBases {
|
|
|
338
343
|
isDefault: false,
|
|
339
344
|
extractionNote: '',
|
|
340
345
|
compileModel: { ...DEFAULT_COMPILE_MODEL },
|
|
346
|
+
extractionTimeoutMinutes: DEFAULT_EXTRACTION_TIMEOUT_MINUTES,
|
|
341
347
|
createdAt: now,
|
|
342
348
|
updatedAt: now,
|
|
343
349
|
}
|
|
@@ -351,14 +357,20 @@ export class KnowledgeBases {
|
|
|
351
357
|
* 更新名称 / 描述 / 坐标提取备注。
|
|
352
358
|
*
|
|
353
359
|
*/
|
|
354
|
-
async update(id: string, patch: { name?: string; description?: string; extractionNote?: string; compileModel?: CompileModelSelection }): Promise<Envelope<KnowledgeBase>> {
|
|
360
|
+
async update(id: string, patch: { name?: string; description?: string; extractionNote?: string; compileModel?: CompileModelSelection; extractionTimeoutMinutes?: ExtractionTimeoutMinutes }): Promise<Envelope<KnowledgeBase>> {
|
|
355
361
|
this.ensureLoaded()
|
|
356
362
|
const base = this.records.find(item => item.id === id)
|
|
357
363
|
if (!base) return failure(emptyBase(id), 'BASE_NOT_FOUND', `知识库不存在: ${id}`, true)
|
|
358
364
|
// 守卫必须基于精确的实时计数,不能用可能过期的缓存。
|
|
359
365
|
await this.refreshOne(base)
|
|
360
366
|
let nextCompileModel = base.compileModel
|
|
367
|
+
if (patch.extractionTimeoutMinutes !== undefined && !isExtractionTimeoutMinutes(patch.extractionTimeoutMinutes)) {
|
|
368
|
+
return failure(this.view(base), 'EXTRACTION_TIMEOUT_INVALID', '文档抽取时限只能选择 30、60 或 100 分钟,最大 100 分钟。', false)
|
|
369
|
+
}
|
|
361
370
|
if (patch.compileModel !== undefined) {
|
|
371
|
+
if (patch.compileModel.maxTokens !== undefined && (!Number.isSafeInteger(patch.compileModel.maxTokens) || patch.compileModel.maxTokens <= 0)) {
|
|
372
|
+
return failure(this.view(base), 'COMPILE_MODEL_INVALID', '单次最大输出必须是正整数 token;留空表示跟随模型默认。', false)
|
|
373
|
+
}
|
|
362
374
|
if (patch.compileModel.mode !== 'default' && patch.compileModel.mode !== 'fixed') {
|
|
363
375
|
return failure(this.view(base), 'COMPILE_MODEL_INVALID', '编译模型 mode 只能是 default 或 fixed', false, '从编译模型选择项中选择“跟随 DSH 默认模型”或一个固定模型。')
|
|
364
376
|
}
|
|
@@ -377,6 +389,7 @@ export class KnowledgeBases {
|
|
|
377
389
|
// 长度上限不是配额,是防呆:这条备注逐字进每一次抽取的提示词,几万字会把文档挤出去。
|
|
378
390
|
if (patch.extractionNote !== undefined) base.extractionNote = String(patch.extractionNote).slice(0, 4000)
|
|
379
391
|
base.compileModel = nextCompileModel
|
|
392
|
+
if (patch.extractionTimeoutMinutes !== undefined) base.extractionTimeoutMinutes = patch.extractionTimeoutMinutes
|
|
380
393
|
base.updatedAt = new Date().toISOString()
|
|
381
394
|
await this.refreshOne(base)
|
|
382
395
|
await this.persist()
|
|
@@ -391,6 +404,10 @@ export class KnowledgeBases {
|
|
|
391
404
|
this.ensureLoaded()
|
|
392
405
|
return this.records.find(item => item.id === id)?.extractionNote ?? ''
|
|
393
406
|
}
|
|
407
|
+
extractionTimeoutMinutesOf(id: string): ExtractionTimeoutMinutes {
|
|
408
|
+
this.ensureLoaded()
|
|
409
|
+
return normalizeExtractionTimeoutMinutes(this.records.find(item => item.id === id)?.extractionTimeoutMinutes)
|
|
410
|
+
}
|
|
394
411
|
|
|
395
412
|
/** 当前知识库的编译模型路由。不存在的库回到默认路由,避免读路径抛错。 */
|
|
396
413
|
compileModelOf(id: string): CompileModelSelection {
|
|
@@ -443,6 +460,7 @@ export class KnowledgeBases {
|
|
|
443
460
|
isDefault: base.isDefault,
|
|
444
461
|
extractionNote: base.extractionNote,
|
|
445
462
|
compileModel: { ...base.compileModel },
|
|
463
|
+
extractionTimeoutMinutes: base.extractionTimeoutMinutes,
|
|
446
464
|
createdAt: base.createdAt,
|
|
447
465
|
updatedAt: base.updatedAt,
|
|
448
466
|
sourceCount: stats?.sourceCount ?? 0,
|
|
@@ -462,6 +480,7 @@ export class KnowledgeBases {
|
|
|
462
480
|
isDefault: true,
|
|
463
481
|
extractionNote: '',
|
|
464
482
|
compileModel: { ...DEFAULT_COMPILE_MODEL },
|
|
483
|
+
extractionTimeoutMinutes: DEFAULT_EXTRACTION_TIMEOUT_MINUTES,
|
|
465
484
|
createdAt: now,
|
|
466
485
|
updatedAt: now,
|
|
467
486
|
}
|
|
@@ -524,6 +543,7 @@ export class KnowledgeBases {
|
|
|
524
543
|
// 0068 之前存下来的注册表里没有这个键:算空串,不报错,也不影响别的字段。
|
|
525
544
|
extractionNote: typeof value.extractionNote === 'string' ? value.extractionNote : '',
|
|
526
545
|
compileModel: compileModelOf(value.compileModel),
|
|
546
|
+
extractionTimeoutMinutes: normalizeExtractionTimeoutMinutes(value.extractionTimeoutMinutes),
|
|
527
547
|
createdAt: typeof value.createdAt === 'string' && value.createdAt ? value.createdAt : now,
|
|
528
548
|
updatedAt: typeof value.updatedAt === 'string' && value.updatedAt ? value.updatedAt : now,
|
|
529
549
|
}
|