dsh-data-domain-knowledge 0.1.0-alpha.13 → 0.1.0-alpha.15
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 +89 -56
- package/cordis.patch.yml +36 -129
- package/eval/benchmark-extraction-format.mjs +20 -0
- package/lib/client.js +3262 -4142
- package/lib/doc-page.js +1 -1
- package/lib/pages.js +1147 -364
- package/lib/types/axis-registry.d.ts +8 -1
- package/lib/types/axis-registry.d.ts.map +1 -1
- package/lib/types/axis-registry.js +62 -7
- package/lib/types/axis-registry.js.map +1 -1
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +3 -0
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +9 -0
- package/lib/types/convert/ingest.d.ts.map +1 -1
- package/lib/types/convert/ingest.js +68 -9
- package/lib/types/convert/ingest.js.map +1 -1
- package/lib/types/diagnosis/cards.d.ts +7 -3
- package/lib/types/diagnosis/cards.d.ts.map +1 -1
- package/lib/types/diagnosis/cards.js +13 -8
- package/lib/types/diagnosis/cards.js.map +1 -1
- package/lib/types/diagnosis/run.d.ts +4 -13
- package/lib/types/diagnosis/run.d.ts.map +1 -1
- package/lib/types/diagnosis/run.js +26 -16
- package/lib/types/diagnosis/run.js.map +1 -1
- package/lib/types/diagnosis/schema.js +2 -2
- package/lib/types/diagnosis/schema.js.map +1 -1
- package/lib/types/diagnosis/seed-source.d.ts +1 -1
- package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
- package/lib/types/diagnosis/seed-source.js +5 -4
- package/lib/types/diagnosis/seed-source.js.map +1 -1
- package/lib/types/diagnosis/seed.d.ts +4 -4
- package/lib/types/diagnosis/seed.d.ts.map +1 -1
- package/lib/types/diagnosis/seed.js +6 -8
- package/lib/types/diagnosis/seed.js.map +1 -1
- package/lib/types/diagnosis/store.d.ts +18 -17
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +94 -66
- package/lib/types/diagnosis/store.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +10 -16
- package/lib/types/diagnosis/types.d.ts.map +1 -1
- package/lib/types/diagnosis/types.js.map +1 -1
- package/lib/types/environment/dsh-python.d.ts +77 -0
- package/lib/types/environment/dsh-python.d.ts.map +1 -0
- package/lib/types/environment/dsh-python.js +139 -0
- package/lib/types/environment/dsh-python.js.map +1 -0
- package/lib/types/environment/install.d.ts +9 -1
- package/lib/types/environment/install.d.ts.map +1 -1
- package/lib/types/environment/install.js +149 -24
- package/lib/types/environment/install.js.map +1 -1
- package/lib/types/environment/model-catalog.d.ts +34 -2
- package/lib/types/environment/model-catalog.d.ts.map +1 -1
- package/lib/types/environment/model-catalog.js +40 -2
- package/lib/types/environment/model-catalog.js.map +1 -1
- package/lib/types/environment/service.d.ts +13 -3
- package/lib/types/environment/service.d.ts.map +1 -1
- package/lib/types/environment/service.js +127 -23
- package/lib/types/environment/service.js.map +1 -1
- package/lib/types/environment/types.d.ts +9 -0
- package/lib/types/environment/types.d.ts.map +1 -1
- package/lib/types/events.d.ts +2 -0
- package/lib/types/events.d.ts.map +1 -1
- package/lib/types/events.js +13 -0
- package/lib/types/events.js.map +1 -1
- package/lib/types/extract-contract.d.ts +1 -1
- package/lib/types/extract-contract.d.ts.map +1 -1
- package/lib/types/extract-contract.js +34 -31
- package/lib/types/extract-contract.js.map +1 -1
- package/lib/types/extract-model.d.ts +2 -0
- package/lib/types/extract-model.d.ts.map +1 -1
- package/lib/types/extract-model.js.map +1 -1
- package/lib/types/extract-table-contract.d.ts +3 -0
- package/lib/types/extract-table-contract.d.ts.map +1 -0
- package/lib/types/extract-table-contract.js +30 -0
- package/lib/types/extract-table-contract.js.map +1 -0
- package/lib/types/extract-table.d.ts +8 -0
- package/lib/types/extract-table.d.ts.map +1 -0
- package/lib/types/extract-table.js +151 -0
- package/lib/types/extract-table.js.map +1 -0
- package/lib/types/extraction-runs.d.ts +30 -6
- package/lib/types/extraction-runs.d.ts.map +1 -1
- package/lib/types/extraction-runs.js +69 -8
- package/lib/types/extraction-runs.js.map +1 -1
- package/lib/types/fs-atomic.d.ts +0 -7
- package/lib/types/fs-atomic.d.ts.map +1 -1
- package/lib/types/fs-atomic.js +17 -2
- package/lib/types/fs-atomic.js.map +1 -1
- package/lib/types/images.d.ts +1 -1
- package/lib/types/images.d.ts.map +1 -1
- package/lib/types/images.js +2 -2
- package/lib/types/images.js.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +232 -50
- package/lib/types/index.js.map +1 -1
- package/lib/types/intent/scope.d.ts.map +1 -1
- package/lib/types/intent/scope.js +1 -0
- package/lib/types/intent/scope.js.map +1 -1
- package/lib/types/intent/tools.d.ts +19 -0
- package/lib/types/intent/tools.d.ts.map +1 -1
- package/lib/types/intent/tools.js +37 -1
- package/lib/types/intent/tools.js.map +1 -1
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +5 -16
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/knowledge.d.ts +3 -0
- package/lib/types/knowledge.d.ts.map +1 -1
- package/lib/types/knowledge.js +21 -5
- package/lib/types/knowledge.js.map +1 -1
- package/lib/types/materialize-snapshot.d.ts +49 -0
- package/lib/types/materialize-snapshot.d.ts.map +1 -0
- package/lib/types/materialize-snapshot.js +2 -0
- package/lib/types/materialize-snapshot.js.map +1 -0
- package/lib/types/model-text.d.ts +1 -1
- package/lib/types/model-text.d.ts.map +1 -1
- package/lib/types/model-text.js +5 -4
- package/lib/types/model-text.js.map +1 -1
- package/lib/types/oplog.d.ts.map +1 -1
- package/lib/types/oplog.js +7 -0
- package/lib/types/oplog.js.map +1 -1
- package/lib/types/pipeline/engine.d.ts +60 -0
- package/lib/types/pipeline/engine.d.ts.map +1 -0
- package/lib/types/pipeline/engine.js +338 -0
- package/lib/types/pipeline/engine.js.map +1 -0
- package/lib/types/pipeline/store.d.ts +73 -0
- package/lib/types/pipeline/store.d.ts.map +1 -0
- package/lib/types/pipeline/store.js +143 -0
- package/lib/types/pipeline/store.js.map +1 -0
- package/lib/types/pipeline/types.d.ts +101 -0
- package/lib/types/pipeline/types.d.ts.map +1 -0
- package/lib/types/pipeline/types.js +5 -0
- package/lib/types/pipeline/types.js.map +1 -0
- package/lib/types/present.d.ts +2 -0
- package/lib/types/present.d.ts.map +1 -1
- package/lib/types/present.js +9 -4
- package/lib/types/present.js.map +1 -1
- package/lib/types/retention.d.ts +39 -0
- package/lib/types/retention.d.ts.map +1 -0
- package/lib/types/retention.js +42 -0
- package/lib/types/retention.js.map +1 -0
- package/lib/types/runtime.d.ts +115 -5
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +862 -132
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/source-evidence.d.ts +9 -1
- package/lib/types/source-evidence.d.ts.map +1 -1
- package/lib/types/source-evidence.js +13 -5
- package/lib/types/source-evidence.js.map +1 -1
- package/lib/types/store/axis-references.js +3 -3
- package/lib/types/store/axis-references.js.map +1 -1
- package/lib/types/store/derived.d.ts +1 -1
- package/lib/types/store/derived.d.ts.map +1 -1
- package/lib/types/store/derived.js +41 -7
- package/lib/types/store/derived.js.map +1 -1
- package/lib/types/store/knowledge-db.d.ts +18 -0
- package/lib/types/store/knowledge-db.d.ts.map +1 -1
- package/lib/types/store/knowledge-db.js +111 -6
- package/lib/types/store/knowledge-db.js.map +1 -1
- package/lib/types/store/migrate.js +1 -1
- package/lib/types/store/migrate.js.map +1 -1
- package/lib/types/trajectory-types.d.ts +172 -0
- package/lib/types/trajectory-types.d.ts.map +1 -0
- package/lib/types/trajectory-types.js +36 -0
- package/lib/types/trajectory-types.js.map +1 -0
- package/lib/types/types.d.ts +3 -4
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-finalize.d.ts +16 -0
- package/lib/types/wiki-finalize.d.ts.map +1 -1
- package/lib/types/wiki-finalize.js +26 -0
- package/lib/types/wiki-finalize.js.map +1 -1
- package/lib/types/wiki-materialize.d.ts.map +1 -1
- package/lib/types/wiki-materialize.js +3 -15
- package/lib/types/wiki-materialize.js.map +1 -1
- 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 +2 -0
- package/lib/types/wiki-trial.d.ts.map +1 -1
- package/lib/types/wiki-trial.js +31 -7
- package/lib/types/wiki-trial.js.map +1 -1
- package/package.json +6 -3
- package/preset/tita-data-agent/agent.cordis.yml +36 -129
- package/src/axis-registry.ts +57 -9
- package/src/client/AttributionEventGuide.tsx +10 -9
- package/src/client/CompileDialogs.tsx +10 -2
- package/src/client/EnvironmentSetup.tsx +76 -16
- package/src/client/ExpansionGraph.tsx +13 -7
- package/src/client/ExtractionTasksPage.tsx +543 -156
- package/src/client/ImportFilesDialog.tsx +2 -13
- package/src/client/IntentTab.tsx +3 -1
- package/src/client/KnowledgeBaseTab.tsx +139 -122
- package/src/client/KnowledgeLayerSection.tsx +19 -2
- package/src/client/KnowledgePanel.tsx +17 -3
- package/src/client/ListPager.tsx +15 -5
- package/src/client/MaterializeTrajectoryView.tsx +124 -0
- package/src/client/NodeSettings.tsx +90 -25
- package/src/client/NormalizeTrajectoryView.tsx +136 -0
- package/src/client/PipelineNotice.tsx +26 -0
- package/src/client/RecallPanel.tsx +55 -25
- package/src/client/TabGuide.tsx +4 -4
- package/src/client/TrialReportView.tsx +61 -16
- package/src/client/TypeDefinitionDrawer.tsx +33 -9
- package/src/client/WikiCardPreview.tsx +43 -10
- package/src/client/WikiFlowPanel.tsx +4 -4
- package/src/client/architecture.tsx +1 -1
- package/src/client/attribution/AttributionTab.tsx +37 -36
- package/src/client/attribution/CaseView.tsx +3 -1
- package/src/client/attribution/EventView.tsx +14 -4
- package/src/client/attribution/RunView.tsx +2 -2
- package/src/client/attribution/ViewSwitch.tsx +20 -5
- package/src/client/attribution/graph-options.ts +38 -31
- package/src/client/attribution/model.ts +16 -9
- package/src/client/graph/palette.ts +165 -0
- package/src/client/pages.css +224 -28
- package/src/client/pages.tsx +33 -11
- package/src/client/styles.css +61 -6
- package/src/client/wiki-trial.ts +30 -1
- package/src/convert/host.ts +3 -0
- package/src/convert/ingest.ts +73 -9
- package/src/diagnosis/cards.ts +13 -8
- package/src/diagnosis/run.ts +27 -17
- package/src/diagnosis/schema.ts +2 -2
- package/src/diagnosis/seed-source.ts +5 -4
- package/src/diagnosis/seed.ts +6 -8
- package/src/diagnosis/store.ts +99 -69
- package/src/diagnosis/types.ts +10 -16
- package/src/environment/dsh-python.ts +156 -0
- package/src/environment/install.ts +134 -19
- package/src/environment/model-catalog.ts +45 -3
- package/src/environment/service.ts +111 -23
- package/src/environment/types.ts +6 -0
- package/src/events.ts +12 -0
- package/src/extract-contract.ts +6 -3
- package/src/extract-model.ts +2 -0
- package/src/extract-table-contract.ts +29 -0
- package/src/extract-table.ts +126 -0
- package/src/extraction-runs.ts +70 -11
- package/src/fs-atomic.ts +19 -2
- package/src/images.ts +2 -2
- package/src/index.ts +207 -72
- package/src/intent/scope.ts +1 -0
- package/src/intent/tools.ts +38 -1
- package/src/knowledge-bases.ts +4 -12
- package/src/knowledge.ts +24 -4
- package/src/materialize-snapshot.ts +46 -0
- package/src/model-text.ts +5 -3
- package/src/oplog.ts +7 -0
- package/src/pipeline/engine.ts +312 -0
- package/src/pipeline/store.ts +147 -0
- package/src/pipeline/types.ts +92 -0
- package/src/present.ts +10 -4
- package/src/retention.ts +76 -0
- package/src/runtime.ts +853 -131
- package/src/source-evidence.ts +12 -5
- package/src/store/axis-references.ts +3 -3
- package/src/store/derived.ts +40 -7
- package/src/store/knowledge-db.ts +111 -5
- package/src/store/migrate.ts +1 -1
- package/src/trajectory-types.ts +142 -0
- package/src/types.ts +3 -4
- package/src/wiki-finalize.ts +28 -0
- package/src/wiki-materialize.ts +3 -14
- package/src/wiki-trial-types.ts +3 -0
- package/src/wiki-trial.ts +26 -7
- package/src/client/convert/ConvertPanel.tsx +0 -583
- package/src/client/convert/styles.css +0 -138
- package/src/client/convert/styles.ts +0 -49
package/lib/types/index.js
CHANGED
|
@@ -400,7 +400,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
400
400
|
}
|
|
401
401
|
const provider = configured.mode === 'fixed' ? configured.provider : defaultProvider;
|
|
402
402
|
const model = configured.mode === 'fixed' ? configured.model : defaultModel;
|
|
403
|
-
const effectiveReasoningEffort = configured.mode === 'default' ? reasoningEffort : undefined;
|
|
403
|
+
const effectiveReasoningEffort = configured.reasoningEffort || (configured.mode === 'default' ? reasoningEffort : undefined);
|
|
404
404
|
if (!provider || !model) {
|
|
405
405
|
return { why: `编译模型没有选好(provider=${provider ?? '空'} / model=${model ?? '空'})` };
|
|
406
406
|
}
|
|
@@ -408,7 +408,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
408
408
|
provider,
|
|
409
409
|
model,
|
|
410
410
|
...(effectiveReasoningEffort ? { reasoningEffort: effectiveReasoningEffort } : {}),
|
|
411
|
-
call: async ({ system, prompt, sessionId, onUsage }) => {
|
|
411
|
+
call: async ({ system, prompt, sessionId, onUsage, onDelta }) => {
|
|
412
412
|
/**
|
|
413
413
|
* 先走 DSH 的公开解析接口,再开始发 prompt。这样固定模型过期、默认模型失效时,
|
|
414
414
|
* 编译会在模型调用边界明确失败,而不是落成一次看似成功的规则抽取。
|
|
@@ -417,7 +417,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
417
417
|
try {
|
|
418
418
|
await llm.resolveCallConfig({
|
|
419
419
|
provider, model,
|
|
420
|
-
...(
|
|
420
|
+
...(effectiveReasoningEffort ? { reasoningEffort: effectiveReasoningEffort } : {}),
|
|
421
421
|
});
|
|
422
422
|
}
|
|
423
423
|
catch (cause) {
|
|
@@ -440,13 +440,13 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
440
440
|
// 而不是留空 —— 留空会在 OpenCode 路由上 400。
|
|
441
441
|
sessionId: sessionId ?? 'knowledge:default',
|
|
442
442
|
});
|
|
443
|
-
return collectModelText(llm.stream(request), onUsage);
|
|
443
|
+
return collectModelText(llm.stream(request), onUsage, onDelta);
|
|
444
444
|
},
|
|
445
445
|
};
|
|
446
446
|
};
|
|
447
447
|
let environment;
|
|
448
448
|
const runtimeOptions = () => ({
|
|
449
|
-
environmentGuard:
|
|
449
|
+
environmentGuard: kind => environment.requireReady(kind),
|
|
450
450
|
// 0029:家当目录。配置里三个路径为空时,运行时从这里解析模型与解释器。
|
|
451
451
|
resourceRoot: resourceRoot(config.agentRoot),
|
|
452
452
|
// 0032:记忆段。默认库的记忆落在 `<agentRoot>/memory/default`,与知识树平级。
|
|
@@ -482,7 +482,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
482
482
|
// 0044:非默认库的 runtime 也要拿到模型接缝。只传 `config` 不够 —— 默认库走上面
|
|
483
483
|
// 那个 `new KnowledgeRuntime(... runtimeOptions())`,别的库走注册表这条支路,
|
|
484
484
|
// 两边必须都带上,否则「新建一个库 → 导入文档」静默变成规则抽取。
|
|
485
|
-
runtimeOptions: () => ({ resourceRoot: resourceRoot(config.agentRoot), modelSeam: modelSeamFor, environmentGuard:
|
|
485
|
+
runtimeOptions: () => ({ resourceRoot: resourceRoot(config.agentRoot), modelSeam: modelSeamFor, environmentGuard: kind => environment.requireReady(kind) }),
|
|
486
486
|
});
|
|
487
487
|
compileModelForBase = id => bases.compileModelOf(id);
|
|
488
488
|
runtime.baseId = bases.defaultId;
|
|
@@ -492,14 +492,48 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
492
492
|
snapshot: () => ({ config: runtime.config, paths: runtime.resourcePaths() }),
|
|
493
493
|
probe: kind => runtime.environmentProbe(kind),
|
|
494
494
|
reload: () => { runtime.reloadResources(); bases.reloadResources(); },
|
|
495
|
+
activateSelection: async () => {
|
|
496
|
+
const keys = ['embeddingProvider', 'embeddingModel', 'rerankProvider', 'rerankModel'];
|
|
497
|
+
const previous = Object.fromEntries(keys.map(key => [key, config[key]]));
|
|
498
|
+
const managed = { embeddingProvider: 'wemm-python', embeddingModel: '', rerankProvider: 'bert-python', rerankModel: '' };
|
|
499
|
+
const write = async (values) => {
|
|
500
|
+
const group = pluginConfig.knowledge;
|
|
501
|
+
if (!group)
|
|
502
|
+
throw new Error('模型设置不可写');
|
|
503
|
+
for (const key of keys) {
|
|
504
|
+
const reference = group[key];
|
|
505
|
+
if (reference && typeof reference.write === 'function')
|
|
506
|
+
await reference.write(values[key]);
|
|
507
|
+
else
|
|
508
|
+
group[key] = values[key];
|
|
509
|
+
}
|
|
510
|
+
config = normalizeKnowledge(pluginConfig.knowledge);
|
|
511
|
+
runtime.updateConfig(config);
|
|
512
|
+
bases.updateRuntimeConfig(config);
|
|
513
|
+
};
|
|
514
|
+
try {
|
|
515
|
+
await write(managed);
|
|
516
|
+
}
|
|
517
|
+
catch (cause) {
|
|
518
|
+
await write(previous);
|
|
519
|
+
throw cause;
|
|
520
|
+
}
|
|
521
|
+
return () => {
|
|
522
|
+
const current = normalizeKnowledge(pluginConfig.knowledge);
|
|
523
|
+
// A later manual setting change belongs to the user and must survive rollback.
|
|
524
|
+
const restored = Object.fromEntries(keys.map(key => [key, current[key] === managed[key] ? previous[key] : current[key]]));
|
|
525
|
+
return write(restored);
|
|
526
|
+
};
|
|
527
|
+
},
|
|
495
528
|
});
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
529
|
+
// Reuse a matching persisted probe after restart; only stale or missing reports need a resource check.
|
|
530
|
+
if (environment.view().report.state !== 'ready')
|
|
531
|
+
void environment.check(false).catch(() => undefined);
|
|
532
|
+
const compileModelCatalog = async (kbId, queryModelRoute = {}) => {
|
|
499
533
|
const service = ctx.get('agentDefaultModel');
|
|
500
534
|
const defaultRoute = service?.currentSelection?.() ?? {};
|
|
501
535
|
const llm = ctx.get('llm');
|
|
502
|
-
const providers = [...new Set([defaultRoute.provider, compileModelForBase(kbId).provider].filter((value) => Boolean(value)))];
|
|
536
|
+
const providers = [...new Set([...(llm?.listProviders?.() ?? []).map(provider => provider.id), defaultRoute.provider, compileModelForBase(kbId).provider].filter((value) => Boolean(value)))];
|
|
503
537
|
const models = [];
|
|
504
538
|
const failures = [];
|
|
505
539
|
if (!llm?.listModels) {
|
|
@@ -528,6 +562,21 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
528
562
|
}
|
|
529
563
|
}
|
|
530
564
|
const selection = compileModelForBase(kbId);
|
|
565
|
+
const effortProvider = selection.mode === 'fixed' ? selection.provider : defaultRoute.provider;
|
|
566
|
+
const effortModel = selection.mode === 'fixed' ? selection.model : defaultRoute.model;
|
|
567
|
+
const requestedProvider = queryModelRoute.provider || effortProvider;
|
|
568
|
+
const requestedModel = queryModelRoute.model || effortModel;
|
|
569
|
+
if (requestedProvider && requestedModel && llm?.resolveModelInfo) {
|
|
570
|
+
const item = models.find(model => model.provider === requestedProvider && model.id === requestedModel);
|
|
571
|
+
if (item) {
|
|
572
|
+
try {
|
|
573
|
+
item.reasoning = (await llm.resolveModelInfo(requestedProvider, requestedModel)).reasoning;
|
|
574
|
+
}
|
|
575
|
+
catch (cause) {
|
|
576
|
+
failures.push(`思考强度目录:${cause instanceof Error ? cause.message : String(cause)}`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
531
580
|
const selected = selection.mode === 'fixed' && selection.provider && selection.model
|
|
532
581
|
? models.find(item => item.provider === selection.provider && item.id === selection.model)
|
|
533
582
|
: undefined;
|
|
@@ -688,24 +737,59 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
688
737
|
// 意图只保留固定的会话审计契约;可编辑词表不再属于插件配置。
|
|
689
738
|
applyIntent(ctx, { intentRoot: () => intentRoot(config.agentRoot) });
|
|
690
739
|
/**
|
|
691
|
-
* 需要本轮用户审批的知识写工具。`
|
|
740
|
+
* 需要本轮用户审批的知识写工具。`image_publish` 只写入本地素材目录,按用户要求免审批。
|
|
692
741
|
*
|
|
693
742
|
* 0044 换过一批名字:`write_knowledge_card`(只接受已确认提议)退役了,因为正文现在
|
|
694
743
|
* 只读、卡片由编译直接写入;`list_card_proposals` 也退役了,因为待办队列不存在了。
|
|
695
744
|
* 新增的两个是**入库之后**的动作:加备注、review 抬分。
|
|
696
745
|
*/
|
|
697
|
-
|
|
746
|
+
ctx.tools.register(tool('run_knowledge_pipeline', 'Run one independent knowledge stage. Import only saves sources by default. Stages: extract Knowledge/Event, normalize existing results, materialize selected complete card groups, build retrieval indexes, or all. Returns a durable background task; inspect it with get_knowledge_pipeline.', {
|
|
747
|
+
stage: { type: 'string', required: true, enum: ['extract', 'normalize', 'materialize', 'build', 'all'] },
|
|
748
|
+
kb: { type: 'string' }, source_ids: { type: 'array', description: 'Optional explicit source IDs; omit for all sources. An empty selection is rejected.' },
|
|
749
|
+
group_keys: { type: 'array', description: 'Optional full card group keys returned by get_knowledge_pipeline.' }, force: { type: 'boolean', description: 'Explicitly rerun the selected stage despite an unchanged checkpoint.' },
|
|
750
|
+
}, async (args) => withRuntime(args.kb, async (target) => {
|
|
751
|
+
const pipeline = await target.pipeline();
|
|
752
|
+
const { task } = pipeline.start(args.stage, { sourceIds: args.source_ids, groupKeys: args.group_keys, force: args.force === true });
|
|
753
|
+
return success({ task, url: `/knowledge/pages/tasks?kb=${encodeURIComponent(target.baseId)}&task=${encodeURIComponent(task.id)}` });
|
|
754
|
+
})));
|
|
755
|
+
ctx.tools.register(tool('get_knowledge_pipeline', 'Read pending stages, materializable card groups, persistent task history, or incremental public model output. Use the returned next cursor as after on the next call.', {
|
|
756
|
+
kb: { type: 'string' }, task_id: { type: 'string' }, after: { type: 'integer' },
|
|
757
|
+
}, async (args) => withRuntime(args.kb, async (target) => {
|
|
758
|
+
const pipeline = await target.pipeline();
|
|
759
|
+
return args.task_id
|
|
760
|
+
? success({ task: pipeline.store.task(args.task_id) ?? null, ...pipeline.store.events(args.task_id, Math.max(0, args.after ?? 0)) })
|
|
761
|
+
: success({ status: await target.pipelineStatus(), ...pipeline.store.tasks() });
|
|
762
|
+
})));
|
|
763
|
+
const writeTools = new Set(['add_source', 'import_sources', 'propose_wiki_cards', 'annotate_wiki_card', 'review_wiki_card', 'set_knowledge_status', 'rebuild_knowledge', 'run_knowledge_pipeline', 'record_feedback']);
|
|
698
764
|
ctx.on('tools/pre-execute', (exec, next) => {
|
|
699
765
|
if (writeTools.has(exec.name))
|
|
700
766
|
return Promise.resolve({ kind: 'ask', reason: '该操作会写入 Tita Knowledge Store,需要本轮用户审批。' });
|
|
701
767
|
return next();
|
|
702
768
|
});
|
|
703
|
-
ctx.tools.register(tool('recall_knowledge', 'Recall compiled
|
|
769
|
+
ctx.tools.register(tool('recall_knowledge', 'Recall compiled Knowledge through keyword and vector candidate routes, rerank WikiCard sections, then return cards with matched sections, provenance, and Event reference IDs. Use get_knowledge_events to read referenced Events.', { query: { type: 'string', required: true }, limit: { type: 'integer' }, top_k: { type: 'integer' }, top_n: { type: 'integer' }, rerank: { type: 'boolean' }, context_max_chars: { type: 'integer', description: '这次召回的上下文字符上限;省略则用设置里的值(默认 20000,0 = 不限)。超出部分落在系统临时目录,响应里的 budget.overflow_path 就是它的绝对路径 —— 需要更多信息时直接读那个文件。' }, 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' } }, async (args, exec) => withRuntime(args.kb, target => target.recall(args.query, args.top_k ?? args.limit, args.include_problems ?? false, args.mode, lineageOf(args, exec), { contextTopN: args.top_n, rerank: args.rerank, ...(typeof args.context_max_chars === 'number' ? { contextMaxChars: args.context_max_chars } : {}) })), {
|
|
704
770
|
call: recallCallView,
|
|
705
771
|
// 查询词从参数来,命中从结果来 —— 两者拼成一张能看懂的账本。
|
|
706
772
|
meta: (args, value) => recallMetaOf({ ...dataOf(value), query: args?.query }),
|
|
707
773
|
result: resultViewOf(recallResultMarkdown),
|
|
708
774
|
}));
|
|
775
|
+
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.', {
|
|
776
|
+
event_ids: { type: 'array', required: true, description: 'Event IDs from WikiCard metadata.event_refs; maximum 50 IDs.' },
|
|
777
|
+
kb: { type: 'string' }, session_key: { type: 'string' }, task_id: { type: 'string' }, subtask_id: { type: 'string' },
|
|
778
|
+
run_id: { type: 'string' }, turn_id: { type: 'string' }, trace_id: { type: 'string' }, domain: { type: 'string' },
|
|
779
|
+
}, async (args) => withRuntime(args.kb, async (target) => {
|
|
780
|
+
const ids = [...new Set((Array.isArray(args.event_ids) ? args.event_ids : [])
|
|
781
|
+
.filter((id) => typeof id === 'string' && id.trim() !== '').map(id => id.trim()))];
|
|
782
|
+
if (ids.length === 0)
|
|
783
|
+
return failure({ events: [], missing_ids: [] }, 'EVENT_IDS_REQUIRED', '请传入 WikiCard 的 event_refs 中的 Event ID。', false);
|
|
784
|
+
if (ids.length > 50)
|
|
785
|
+
return failure({ events: [], missing_ids: [] }, 'EVENT_IDS_LIMIT', '一次最多读取 50 个 Event ID;请分批读取该 WikiCard 的 event_refs。', false);
|
|
786
|
+
const byId = new Map((await target.loadEvents()).map(event => [event.id, event]));
|
|
787
|
+
const events = ids.flatMap(id => {
|
|
788
|
+
const event = byId.get(id);
|
|
789
|
+
return event ? [event] : [];
|
|
790
|
+
});
|
|
791
|
+
return success({ events, missing_ids: ids.filter(id => !byId.has(id)) });
|
|
792
|
+
})));
|
|
709
793
|
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())));
|
|
710
794
|
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)))));
|
|
711
795
|
ctx.tools.register(tool('query_source_truth', 'Read SourceRaw (原始可追溯文档) with access audit. Each call returns up to `limit` matched sources and `max_chars` total characters. If `hasMoreInCurrentPage` is true, continue with `offset += returnedChars`. When it is false and `hasMoreSources` is true, continue with `source_offset = nextSourceOffset` and reset `offset` to 0. `truncated` stays true until both the current source page and all matching source pages are complete. Do not answer from partial content.', { query: { type: 'string', required: true }, limit: { type: 'integer' }, max_chars: { type: 'integer' }, offset: { type: 'integer' }, source_offset: { type: 'integer', description: '来源分页游标;当前页字符已读完后才使用 nextSourceOffset,并将 offset 重置为 0' }, 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.sourceTruth(args.query, args.limit ?? 20, args.max_chars ?? 40000, lineageOf(args, exec), args.offset ?? 0, args.source_offset ?? 0))));
|
|
@@ -735,13 +819,14 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
735
819
|
buildVersion: base.buildVersion,
|
|
736
820
|
})),
|
|
737
821
|
})));
|
|
738
|
-
|
|
822
|
+
const imagePublishParameters = {
|
|
739
823
|
file_path: { type: 'string', description: '宿主可读取的本地图片文件路径;与 content_base64 二选一' },
|
|
740
824
|
content_base64: { type: 'string', description: '图片的标准 Base64,或 data:image/...;base64,...;与 file_path 二选一' },
|
|
741
825
|
filename: { type: 'string', description: '可选文件名;默认使用 file_path 的文件名,Base64 输入默认 image-<hash>.<ext>' },
|
|
742
826
|
mime_type: { type: 'string', description: '可选 MIME 类型;必须与图片二进制内容一致' },
|
|
743
827
|
kb: { type: 'string', description: '知识库 id;省略时写入默认库' },
|
|
744
|
-
}
|
|
828
|
+
};
|
|
829
|
+
const publishImage = async (args) => withRuntime(args.kb, async (target) => {
|
|
745
830
|
try {
|
|
746
831
|
return success(await publishKnowledgeImage(target.rootDir, target.baseId, {
|
|
747
832
|
filePath: args.file_path,
|
|
@@ -755,7 +840,9 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
755
840
|
return failure({}, cause.code, cause.message, false, cause.suggestion);
|
|
756
841
|
return failure({}, 'IMAGE_PUBLISH_FAILED', `图片写入失败:${cause instanceof Error ? cause.message : String(cause)}`, true);
|
|
757
842
|
}
|
|
758
|
-
})
|
|
843
|
+
});
|
|
844
|
+
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.';
|
|
845
|
+
ctx.tools.register(tool('image_publish', imagePublishDescription, imagePublishParameters, publishImage));
|
|
759
846
|
ctx.tools.register(tool('add_source', 'Add or update a SourceRaw document (原始可追溯文档). 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' }, 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) => withRuntime(args.kb, target => target.addSource({ title: args.title, content: args.content, externalId: args.external_id, metadata: args.metadata, sourceId: args.source_id, ...(args.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}), lineage: lineageOf(args, exec) })), {
|
|
760
847
|
call: addSourceCallView,
|
|
761
848
|
meta: (args, value) => addSourceMetaOf(args, dataOf(value)),
|
|
@@ -775,7 +862,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
775
862
|
* `compile` 默认开:批量导入的意图几乎总是「进来就能用」。关掉它的场景是「先全部
|
|
776
863
|
* 落盘、稍后统一编译」——那时卡片还没生成,工具返回里会明说 `compile: null`。
|
|
777
864
|
*/
|
|
778
|
-
ctx.tools.register(tool('import_sources', 'Import many SourceRaw documents
|
|
865
|
+
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) }));
|
|
779
866
|
/**
|
|
780
867
|
* `propose_wiki_cards` —— 把一批卡片**直接写进 `02_wiki`**(工具名不变,语义在 0044 改了)。
|
|
781
868
|
*
|
|
@@ -829,7 +916,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
829
916
|
lineage: lineageOf(args, exec),
|
|
830
917
|
}))));
|
|
831
918
|
ctx.tools.register(tool('set_knowledge_status', 'Change a Wiki card status. This status belongs to the Card lifecycle; it does not change Knowledge truth status. problem requires problem_note and recovery must pass through unreviewed. DSH requests one user approval before execution.', { entity_id: { type: 'string', required: true }, status: { type: 'string', enum: ['unreviewed', 'draft', 'reviewed', 'active', 'deprecated', 'problem'], required: true }, problem_note: { type: 'string' }, 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.setStatus(args.entity_id, args.status, args.problem_note, lineageOf(args, exec)))));
|
|
832
|
-
ctx.tools.register(tool('rebuild_knowledge', 'Rebuild schema/wiki/compiled knowledge indexes. DSH requests one user approval before execution.', { only: { type: 'string', enum: ['all', 'schema', 'wiki', 'embeddings'] }, 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.rebuild(args.only ?? 'all', lineageOf(args, exec))), {
|
|
919
|
+
ctx.tools.register(tool('rebuild_knowledge', 'Rebuild schema/wiki/compiled knowledge indexes from saved cards without document extraction. Use run_knowledge_pipeline for extract, normalize, materialize, or the full chain. DSH requests one user approval before execution.', { only: { type: 'string', enum: ['all', 'schema', 'wiki', 'embeddings'] }, 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.rebuild(args.only ?? 'all', lineageOf(args, exec), { extract: false })), {
|
|
833
920
|
call: rebuildCallView,
|
|
834
921
|
meta: (args, value) => rebuildMetaOf(args?.only ?? 'all', dataOf(value)),
|
|
835
922
|
result: resultViewOf(rebuildResultMarkdown),
|
|
@@ -911,7 +998,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
911
998
|
const id = kbIdOf(url.searchParams.get('kb'));
|
|
912
999
|
if (!bases.get(id))
|
|
913
1000
|
return sendEnvelope(res, failure({}, 'BASE_NOT_FOUND', `知识库不存在: ${id}`, true, '先 GET /knowledge/bases 拿到可用 id。'));
|
|
914
|
-
return send(res, 200, success(await compileModelCatalog(id)));
|
|
1001
|
+
return send(res, 200, success(await compileModelCatalog(id, { provider: url.searchParams.get('provider') ?? undefined, model: url.searchParams.get('model') ?? undefined })));
|
|
915
1002
|
} },
|
|
916
1003
|
{ kind: 'exact', path: '/knowledge/stats', handler: async (req, res) => {
|
|
917
1004
|
const resolved = resolveBase(query(req).searchParams.get('kb'));
|
|
@@ -1010,6 +1097,67 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1010
1097
|
return sendEnvelope(res, resolved.failure);
|
|
1011
1098
|
return send(res, 200, success({ progress: resolved.runtime.compileProgressView(url.searchParams.get('runId') ?? undefined) }));
|
|
1012
1099
|
} },
|
|
1100
|
+
{ kind: 'exact', path: '/knowledge/pipeline', handler: async (req, res) => {
|
|
1101
|
+
try {
|
|
1102
|
+
const body = req.method === 'POST' ? await readBody(req, 100_000) : {};
|
|
1103
|
+
const resolved = resolveBase(body.kb ?? query(req).searchParams.get('kb'));
|
|
1104
|
+
if ('failure' in resolved)
|
|
1105
|
+
return sendEnvelope(res, resolved.failure);
|
|
1106
|
+
if (req.method === 'GET')
|
|
1107
|
+
return send(res, 200, success(await resolved.runtime.pipelineStatus()));
|
|
1108
|
+
if (req.method !== 'POST')
|
|
1109
|
+
return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', '知识处理只接受 GET 或 POST'));
|
|
1110
|
+
const pipeline = await resolved.runtime.pipeline();
|
|
1111
|
+
const started = pipeline.start(body.stage, { sourceIds: body.sourceIds, groupKeys: body.groupKeys, force: body.force === true });
|
|
1112
|
+
return send(res, 202, success({ task: started.task }));
|
|
1113
|
+
}
|
|
1114
|
+
catch (cause) {
|
|
1115
|
+
return send(res, 400, envelopeError('PIPELINE_INVALID', cause instanceof Error ? cause.message : String(cause)));
|
|
1116
|
+
}
|
|
1117
|
+
} },
|
|
1118
|
+
{ kind: 'exact', path: '/knowledge/pipeline-tasks', handler: async (req, res) => {
|
|
1119
|
+
try {
|
|
1120
|
+
if (req.method !== 'GET')
|
|
1121
|
+
return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', '任务历史只接受 GET'));
|
|
1122
|
+
const url = query(req);
|
|
1123
|
+
const resolved = resolveBase(url.searchParams.get('kb'));
|
|
1124
|
+
if ('failure' in resolved)
|
|
1125
|
+
return sendEnvelope(res, resolved.failure);
|
|
1126
|
+
const pipeline = await resolved.runtime.pipeline();
|
|
1127
|
+
const limit = Math.min(100, Math.max(1, Number(url.searchParams.get('limit')) || 20));
|
|
1128
|
+
const offset = Math.max(0, Number(url.searchParams.get('offset')) || 0);
|
|
1129
|
+
return send(res, 200, success({ ...pipeline.store.tasks(limit, offset), extractions: await resolved.runtime.listExtractionRuns({ limit, offset }) }));
|
|
1130
|
+
}
|
|
1131
|
+
catch (cause) {
|
|
1132
|
+
return send(res, 400, envelopeError('PIPELINE_HISTORY_FAILED', cause instanceof Error ? cause.message : String(cause)));
|
|
1133
|
+
}
|
|
1134
|
+
} },
|
|
1135
|
+
{ kind: 'exact', path: '/knowledge/pipeline-task', handler: async (req, res) => {
|
|
1136
|
+
try {
|
|
1137
|
+
const url = query(req);
|
|
1138
|
+
const body = req.method === 'POST' ? await readBody(req, 20_000) : {};
|
|
1139
|
+
const resolved = resolveBase(body.kb ?? url.searchParams.get('kb'));
|
|
1140
|
+
if ('failure' in resolved)
|
|
1141
|
+
return sendEnvelope(res, resolved.failure);
|
|
1142
|
+
const pipeline = await resolved.runtime.pipeline();
|
|
1143
|
+
const id = typeof body.id === 'string' ? body.id : url.searchParams.get('id') ?? '';
|
|
1144
|
+
const task = pipeline.store.task(id);
|
|
1145
|
+
if (!task)
|
|
1146
|
+
return send(res, 404, envelopeError('PIPELINE_NOT_FOUND', '任务不存在'));
|
|
1147
|
+
if (req.method === 'GET')
|
|
1148
|
+
return send(res, 200, success({ task, ...pipeline.store.events(id, Math.max(0, Number(url.searchParams.get('after')) || 0)) }));
|
|
1149
|
+
if (req.method !== 'POST')
|
|
1150
|
+
return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', '任务只接受 GET 或 POST'));
|
|
1151
|
+
if (body.action === 'retry')
|
|
1152
|
+
return send(res, 202, success({ task: pipeline.start(task.stage, { ...task.selection, force: false }).task }));
|
|
1153
|
+
if (!['pause', 'resume', 'cancel'].includes(body.action))
|
|
1154
|
+
throw new Error('不支持的任务操作');
|
|
1155
|
+
return send(res, 200, success({ task: pipeline.control(id, body.action) }));
|
|
1156
|
+
}
|
|
1157
|
+
catch (cause) {
|
|
1158
|
+
return send(res, 409, envelopeError('PIPELINE_TASK_REJECTED', cause instanceof Error ? cause.message : String(cause)));
|
|
1159
|
+
}
|
|
1160
|
+
} },
|
|
1013
1161
|
{ kind: 'exact', path: '/knowledge/extraction-tasks', handler: async (req, res) => {
|
|
1014
1162
|
if (req.method !== 'GET')
|
|
1015
1163
|
return send(res, 405, envelopeError('METHOD_NOT_ALLOWED', '抽取任务只接受 GET'));
|
|
@@ -1083,7 +1231,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1083
1231
|
if ('failure' in resolved)
|
|
1084
1232
|
return sendEnvelope(res, resolved.failure);
|
|
1085
1233
|
return send(res, 202, success(resolved.runtime.startCompile(body.only ?? 'all', lineageOf(body), {
|
|
1086
|
-
|
|
1234
|
+
extract: body.extract === true,
|
|
1087
1235
|
...(body.forceExtract === true ? { forceExtract: true } : {}),
|
|
1088
1236
|
})));
|
|
1089
1237
|
}
|
|
@@ -1113,7 +1261,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1113
1261
|
...(item?.external_id ? { externalId: String(item.external_id) } : {}),
|
|
1114
1262
|
...(item?.metadata && typeof item.metadata === 'object' ? { metadata: item.metadata } : {}),
|
|
1115
1263
|
})),
|
|
1116
|
-
|
|
1264
|
+
compile: body.compile === true,
|
|
1117
1265
|
lineage: lineageOf(body),
|
|
1118
1266
|
}));
|
|
1119
1267
|
}
|
|
@@ -1206,7 +1354,8 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1206
1354
|
* 任何一条轨迹 —— 「这个实体是哪次抽出来的」就答不上了。
|
|
1207
1355
|
*/
|
|
1208
1356
|
const runId = extractionRunId();
|
|
1209
|
-
const seam = modelSeamFor();
|
|
1357
|
+
const seam = modelSeamFor(resolved.runtime.baseId);
|
|
1358
|
+
const draft = await (await resolved.runtime.axisRegistry())?.draft();
|
|
1210
1359
|
const axesBlock = await resolved.runtime.axisPromptBlockView();
|
|
1211
1360
|
const note = resolved.runtime.extractionNote();
|
|
1212
1361
|
const report = await runWikiTrial({
|
|
@@ -1216,7 +1365,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1216
1365
|
...(typeof body.subject === 'string' && body.subject.trim() ? { subject: body.subject.trim() } : {}),
|
|
1217
1366
|
/** 与编译相同:先查注册表,再把有候选的坐标合并成一次模型补判。 */
|
|
1218
1367
|
resolveAxisSet: axes => resolved.runtime.resolveTrialAxisSet({
|
|
1219
|
-
axes,
|
|
1368
|
+
registry: draft?.registry, axes,
|
|
1220
1369
|
axesBlock,
|
|
1221
1370
|
runId,
|
|
1222
1371
|
call: seam.call,
|
|
@@ -1239,7 +1388,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1239
1388
|
},
|
|
1240
1389
|
...('call' in seam ? { call: seam.call } : {}),
|
|
1241
1390
|
...('why' in seam ? { callWhy: seam.why } : {}),
|
|
1242
|
-
});
|
|
1391
|
+
}).finally(() => draft?.close());
|
|
1243
1392
|
const summary = trialSummary(report);
|
|
1244
1393
|
/**
|
|
1245
1394
|
* 落库。写不进去**不改这次试跑的结论** —— 报告照样返回,只是多一句
|
|
@@ -1253,24 +1402,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1253
1402
|
report: { ...report, summary },
|
|
1254
1403
|
lineage: lineageOf(body),
|
|
1255
1404
|
});
|
|
1256
|
-
|
|
1257
|
-
* 把这次抽出来的 Knowledge 收进真源(0063 §2)。
|
|
1258
|
-
*
|
|
1259
|
-
* **只在跑一份已入库的文档时收。** 理由有两条:来源要指得回一份**真实的**
|
|
1260
|
-
* 文档(临时正文的 `documentId` 是一个标题字符串,存下来就是悬空引用);
|
|
1261
|
-
* 而「这条知识是哪份文档说的」正是它可验证的前提。
|
|
1262
|
-
*
|
|
1263
|
-
* **只收引文核对得上的那些。** 引文对不上原文 = 模型改写了,那条判断不是从
|
|
1264
|
-
* 原文推出来的(§5.7 的引文核验)。把它当知识存进真源,等于让一句编出来的话
|
|
1265
|
-
* 看起来像有出处 —— 而报告里已经把它报成例外了,那里才是它该待的地方。
|
|
1266
|
-
*/
|
|
1267
|
-
const ingest = docId
|
|
1268
|
-
? await resolved.runtime.ingestTrialKnowledge({
|
|
1269
|
-
runId, knowledge: report.knowledge,
|
|
1270
|
-
objects: report.objects, actions: report.actions, scenes: report.scenes,
|
|
1271
|
-
})
|
|
1272
|
-
: { saved: 0, merged: 0, skipped: 0 };
|
|
1273
|
-
return send(res, 200, success({ ...report, summary, run, ingest }));
|
|
1405
|
+
return send(res, 200, success({ ...report, summary, run }));
|
|
1274
1406
|
}
|
|
1275
1407
|
catch (cause) {
|
|
1276
1408
|
return send(res, 400, envelopeError('REQUEST_INVALID', cause instanceof Error ? cause.message : String(cause)));
|
|
@@ -1523,7 +1655,10 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1523
1655
|
return send(res, 400, envelopeError('REQUEST_INVALID', `不认识的 action: ${action || '(空)'}`));
|
|
1524
1656
|
} },
|
|
1525
1657
|
/**
|
|
1526
|
-
*
|
|
1658
|
+
* 轨迹(`extraction_run`):按文档或阶段列、按 id 读全文。
|
|
1659
|
+
*
|
|
1660
|
+
* 2026-10-18 起三段共用这张表(`stage` 列:`extract` / `normalize` / `materialize`)。
|
|
1661
|
+
* `stage` 省略 = 三段一起 —— 面板里那份「这份文档的轨迹」就是这么读的。
|
|
1527
1662
|
*
|
|
1528
1663
|
* 列表**不返回报告本体** —— 一条报告几十到几百 KB,翻一页列表不该按全文付费。
|
|
1529
1664
|
* 点开某一条走 `/knowledge/extraction-run?id=…`(单独一条路由,不是路径参数:
|
|
@@ -1537,8 +1672,11 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1537
1672
|
if ('failure' in resolved)
|
|
1538
1673
|
return sendEnvelope(res, resolved.failure);
|
|
1539
1674
|
const docId = url.searchParams.get('doc_id');
|
|
1675
|
+
const stageParam = url.searchParams.get('stage');
|
|
1676
|
+
const stage = stageParam === 'extract' || stageParam === 'normalize' || stageParam === 'materialize' ? stageParam : undefined;
|
|
1540
1677
|
const page = await resolved.runtime.listExtractionRuns({
|
|
1541
1678
|
...(docId === null ? {} : { docId }),
|
|
1679
|
+
...(stage === undefined ? {} : { stage }),
|
|
1542
1680
|
limit: clampInt(url.searchParams.get('limit'), 20, 1, 100),
|
|
1543
1681
|
offset: clampInt(url.searchParams.get('offset'), 0, 0, Number.MAX_SAFE_INTEGER),
|
|
1544
1682
|
});
|
|
@@ -1665,10 +1803,8 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1665
1803
|
return send(res, 200, success({ cases: (await storeOf(resolved.runtime).snapshot()).cases }));
|
|
1666
1804
|
} },
|
|
1667
1805
|
/**
|
|
1668
|
-
*
|
|
1669
|
-
*
|
|
1670
|
-
* 0044:卡片不再只提议 —— 它直接入库并标成 `unreviewed`。响应给 `sourceId` 与
|
|
1671
|
-
* `entityId`,界面据此说清「记录留下了,卡片也建了,等人 review」。
|
|
1806
|
+
* 一次排查结束只落一条 SourceRaw。Case 视图从记录派生;Knowledge / Event 的抽取、
|
|
1807
|
+
* 归一和 WikiCard 物化由知识处理阶段分别执行。
|
|
1672
1808
|
*/
|
|
1673
1809
|
{ kind: 'exact', path: '/diagnosis/run', handler: async (req, res) => {
|
|
1674
1810
|
if (req.method !== 'POST')
|
|
@@ -1683,9 +1819,9 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1683
1819
|
}
|
|
1684
1820
|
const outcome = await storeOf(resolved.runtime).recordRun(record);
|
|
1685
1821
|
if (outcome.problems.length > 0 && outcome.sourceId === undefined) {
|
|
1686
|
-
return send(res, 200, failure({ sourceId: ''
|
|
1822
|
+
return send(res, 200, failure({ sourceId: '' }, 'RUN_WRITE_FAILED', outcome.problems.join(';'), true));
|
|
1687
1823
|
}
|
|
1688
|
-
return send(res, 200, success({ sourceId: outcome.sourceId ?? ''
|
|
1824
|
+
return send(res, 200, success({ sourceId: outcome.sourceId ?? '' }, outcome.problems));
|
|
1689
1825
|
} },
|
|
1690
1826
|
/**
|
|
1691
1827
|
* 入库之后的两个人工动作(0044):加备注、review 抬分。
|
|
@@ -1842,6 +1978,31 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
1842
1978
|
if (rest.length === 2) {
|
|
1843
1979
|
if (method === 'PATCH') {
|
|
1844
1980
|
const body = await readBody(req);
|
|
1981
|
+
if (body.compileModel && typeof body.compileModel === 'object') {
|
|
1982
|
+
const selection = body.compileModel;
|
|
1983
|
+
const catalog = await compileModelCatalog(baseId);
|
|
1984
|
+
const provider = selection.mode === 'fixed' ? selection.provider : catalog.default.provider;
|
|
1985
|
+
const model = selection.mode === 'fixed' ? selection.model : catalog.default.model;
|
|
1986
|
+
if (selection.mode !== 'fixed' && selection.mode !== 'default')
|
|
1987
|
+
return send(res, 400, envelopeError('MODEL_INVALID', '请选择跟随默认或固定模型'));
|
|
1988
|
+
if (selection.mode === 'fixed' && (!provider || !model))
|
|
1989
|
+
return send(res, 400, envelopeError('MODEL_INVALID', '固定模型需要 provider 和 model'));
|
|
1990
|
+
const llm = ctx.get('llm');
|
|
1991
|
+
if (provider && model && llm?.resolveCallConfig) {
|
|
1992
|
+
try {
|
|
1993
|
+
const info = selection.reasoningEffort ? await llm.resolveModelInfo(provider, model) : undefined;
|
|
1994
|
+
const effort = info?.reasoning?.efforts.find(item => item.id === selection.reasoningEffort);
|
|
1995
|
+
if (selection.reasoningEffort && !effort)
|
|
1996
|
+
throw new Error('所选模型不支持这个思考强度,请重新选择');
|
|
1997
|
+
await llm.resolveCallConfig({ provider, model, ...(effort ? { reasoningEffort: effort.id } : {}) });
|
|
1998
|
+
}
|
|
1999
|
+
catch (cause) {
|
|
2000
|
+
return send(res, 400, envelopeError('MODEL_INVALID', cause instanceof Error ? cause.message : String(cause)));
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
else if (selection.reasoningEffort)
|
|
2004
|
+
return send(res, 400, envelopeError('MODEL_INVALID', '模型不可用,无法校验思考强度'));
|
|
2005
|
+
}
|
|
1845
2006
|
const updated = await bases.update(baseId, {
|
|
1846
2007
|
name: body.name, description: body.description,
|
|
1847
2008
|
extractionNote: typeof body.extractionNote === 'string' ? body.extractionNote : undefined,
|
|
@@ -2089,6 +2250,27 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
2089
2250
|
const search = (url.searchParams.get('query') ?? '').trim().toLocaleLowerCase();
|
|
2090
2251
|
if (search)
|
|
2091
2252
|
documents = documents.filter(item => `${item.docId}\n${item.title}\n${item.externalId}`.toLocaleLowerCase().includes(search));
|
|
2253
|
+
/**
|
|
2254
|
+
* 文件类型的计数对着**搜索之后、类型筛选之前**的集合算。
|
|
2255
|
+
*
|
|
2256
|
+
* 下拉要能切换类型,所以每个类型的数量必须是「当前搜索下有多少份」,
|
|
2257
|
+
* 而不是「当前这一页里有多少份」—— 后者会让选项随翻页变来变去。
|
|
2258
|
+
*/
|
|
2259
|
+
const extCounts = {};
|
|
2260
|
+
for (const item of documents) {
|
|
2261
|
+
const key = item.ext || '—';
|
|
2262
|
+
extCounts[key] = (extCounts[key] ?? 0) + 1;
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2265
|
+
* 文件类型筛选**在服务端**做(0070)。
|
|
2266
|
+
*
|
|
2267
|
+
* 原来是「客户端取前 500 份、再在这一页里筛」:超过 500 份的库,第 501 份起
|
|
2268
|
+
* 在面板上根本不可达,而表头把这种截断显示成「筛出 500 / 600 个」——
|
|
2269
|
+
* 读起来像用户自己开了筛选。既然这里已经支持 `offset`,筛选就该和分页同层。
|
|
2270
|
+
*/
|
|
2271
|
+
const ext = (url.searchParams.get('ext') ?? '').trim();
|
|
2272
|
+
if (ext)
|
|
2273
|
+
documents = documents.filter(item => item.ext === ext);
|
|
2092
2274
|
const total = documents.length;
|
|
2093
2275
|
const offset = clampInt(url.searchParams.get('offset'), 0, 0, Number.MAX_SAFE_INTEGER);
|
|
2094
2276
|
const limit = clampInt(url.searchParams.get('limit'), 50, 1, 500);
|
|
@@ -2099,7 +2281,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
2099
2281
|
* 两件事。放成并列的一张表,前端按 `docId` 查,文档类型不用为审计字段让步。
|
|
2100
2282
|
*/
|
|
2101
2283
|
const extractionRuns = await resolved.runtime.extractionRunCounts();
|
|
2102
|
-
return send(res, 200, success({ documents: documents.slice(offset, offset + limit), total, extractionRuns }));
|
|
2284
|
+
return send(res, 200, success({ documents: documents.slice(offset, offset + limit), total, extCounts, extractionRuns }));
|
|
2103
2285
|
}
|
|
2104
2286
|
const docId = rest[1];
|
|
2105
2287
|
const resolved = resolveBase(kbParam);
|