@world-engines/project-host 0.1.0-alpha.0
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/LICENSE +46 -0
- package/assets/gui/WELogo-title.svg +11 -0
- package/assets/gui/app.js +1326 -0
- package/assets/gui/index.html +118 -0
- package/assets/gui/local-preview-asset-urls.js +61 -0
- package/assets/gui/local-preview-bridge.js +393 -0
- package/assets/gui/spatial-template-panel.js +233 -0
- package/assets/gui/styles.css +115 -0
- package/assets/gui/terrain-render.js +120 -0
- package/assets/gui/workspace-status.css +17 -0
- package/assets/gui/workspace-status.js +255 -0
- package/assets/gui/world-map-globe.js +187 -0
- package/assets/gui/world-map-worker.js +62 -0
- package/assets/gui/world-map.css +30 -0
- package/assets/gui/world-map.js +2466 -0
- package/dist/account-model-proxy.d.ts +21 -0
- package/dist/account-model-proxy.js +470 -0
- package/dist/authoring-browser-runtime.d.ts +7 -0
- package/dist/authoring-browser-runtime.js +110 -0
- package/dist/authoring-ingestion.d.ts +38 -0
- package/dist/authoring-ingestion.js +410 -0
- package/dist/authoring-model-connection.d.ts +166 -0
- package/dist/authoring-model-connection.js +757 -0
- package/dist/chinese-lexical.d.ts +152 -0
- package/dist/chinese-lexical.js +555 -0
- package/dist/chromium-preview-browser-driver.d.ts +43 -0
- package/dist/chromium-preview-browser-driver.js +575 -0
- package/dist/cli/download.d.ts +12 -0
- package/dist/cli/download.js +50 -0
- package/dist/cli/dump.d.ts +32 -0
- package/dist/cli/dump.js +91 -0
- package/dist/cli/ingest.d.ts +49 -0
- package/dist/cli/ingest.js +225 -0
- package/dist/cli/review.d.ts +10 -0
- package/dist/cli/review.js +44 -0
- package/dist/cli/upload.d.ts +57 -0
- package/dist/cli/upload.js +112 -0
- package/dist/cli-command.d.ts +60 -0
- package/dist/cli-command.js +613 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +182 -0
- package/dist/desktop-authoring-runtime.d.ts +88 -0
- package/dist/desktop-authoring-runtime.js +1602 -0
- package/dist/desktop-sidecar.d.ts +11 -0
- package/dist/desktop-sidecar.js +143 -0
- package/dist/document-dump.d.ts +113 -0
- package/dist/document-dump.js +431 -0
- package/dist/document-graph-extractor.d.ts +125 -0
- package/dist/document-graph-extractor.js +588 -0
- package/dist/document-ingestion-journal.d.ts +68 -0
- package/dist/document-ingestion-journal.js +471 -0
- package/dist/document-ingestion.d.ts +183 -0
- package/dist/document-ingestion.js +976 -0
- package/dist/document-query-path-proof.d.ts +30 -0
- package/dist/document-query-path-proof.js +34 -0
- package/dist/document-query-path-renderer.d.ts +50 -0
- package/dist/document-query-path-renderer.js +75 -0
- package/dist/document-query-proof-paths.d.ts +22 -0
- package/dist/document-query-proof-paths.js +71 -0
- package/dist/document-query.d.ts +97 -0
- package/dist/document-query.js +1106 -0
- package/dist/document-relation-role.d.ts +14 -0
- package/dist/document-relation-role.js +86 -0
- package/dist/document-source.d.ts +107 -0
- package/dist/document-source.js +255 -0
- package/dist/embedding-cache.d.ts +35 -0
- package/dist/embedding-cache.js +317 -0
- package/dist/frozen-review-local-pipeline.d.ts +88 -0
- package/dist/frozen-review-local-pipeline.js +609 -0
- package/dist/frozen-review-source-capture.d.ts +15 -0
- package/dist/frozen-review-source-capture.js +551 -0
- package/dist/frozen-review-wasm-encryptor.d.ts +16 -0
- package/dist/frozen-review-wasm-encryptor.js +72 -0
- package/dist/frozen-review.d.ts +131 -0
- package/dist/frozen-review.js +802 -0
- package/dist/graph-traversal.d.ts +23 -0
- package/dist/graph-traversal.js +100 -0
- package/dist/gui/graph-layout-worker.js +1 -0
- package/dist/gui/graph-layout.wasm +0 -0
- package/dist/gui/graph.css +1 -0
- package/dist/gui/graph.js +76 -0
- package/dist/gui/plane-generation.js +112 -0
- package/dist/gui/plane-generator.js +310 -0
- package/dist/gui/plane-generator_bg.wasm +0 -0
- package/dist/gui/procedural-city.js +595 -0
- package/dist/gui/road-generation.js +129 -0
- package/dist/gui/road-parcels.js +1584 -0
- package/dist/gui/score.css +1 -0
- package/dist/gui/score.js +54 -0
- package/dist/gui/spatial-runtime.js +1231 -0
- package/dist/gui/spatial-templates.js +200 -0
- package/dist/gui/trigger.css +1 -0
- package/dist/gui/trigger.js +142 -0
- package/dist/harness-callback-transport.d.ts +17 -0
- package/dist/harness-callback-transport.js +128 -0
- package/dist/harness-document-ingestion.d.ts +77 -0
- package/dist/harness-document-ingestion.js +343 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +43 -0
- package/dist/ingestion-source-reconciliation.d.ts +88 -0
- package/dist/ingestion-source-reconciliation.js +253 -0
- package/dist/ladybug-runtime.d.ts +117 -0
- package/dist/ladybug-runtime.js +886 -0
- package/dist/local-account-embedding.d.ts +34 -0
- package/dist/local-account-embedding.js +294 -0
- package/dist/local-gallery.d.ts +35 -0
- package/dist/local-gallery.js +227 -0
- package/dist/local-gui-auth.d.ts +96 -0
- package/dist/local-gui-auth.js +703 -0
- package/dist/local-gui-contract.d.ts +53 -0
- package/dist/local-gui-contract.js +5 -0
- package/dist/local-gui-image-assets.d.ts +17 -0
- package/dist/local-gui-image-assets.js +96 -0
- package/dist/local-gui-preview-client.d.ts +3 -0
- package/dist/local-gui-preview-client.js +111 -0
- package/dist/local-gui-score.d.ts +72 -0
- package/dist/local-gui-score.js +184 -0
- package/dist/local-gui-server.d.ts +10 -0
- package/dist/local-gui-server.js +987 -0
- package/dist/local-gui-version.d.ts +17 -0
- package/dist/local-gui-version.js +147 -0
- package/dist/local-gui-workspace.d.ts +14 -0
- package/dist/local-gui-workspace.js +171 -0
- package/dist/local-model-settings.d.ts +28 -0
- package/dist/local-model-settings.js +124 -0
- package/dist/local-preview-assets.d.ts +28 -0
- package/dist/local-preview-assets.js +124 -0
- package/dist/local-preview-bound-resources.d.ts +12 -0
- package/dist/local-preview-bound-resources.js +80 -0
- package/dist/local-preview-message-store.d.ts +63 -0
- package/dist/local-preview-message-store.js +214 -0
- package/dist/local-preview-player-read-models.d.ts +47 -0
- package/dist/local-preview-player-read-models.js +90 -0
- package/dist/local-preview-projections.d.ts +61 -0
- package/dist/local-preview-projections.js +117 -0
- package/dist/local-preview-runtime.d.ts +105 -0
- package/dist/local-preview-runtime.js +553 -0
- package/dist/local-preview-session.d.ts +73 -0
- package/dist/local-preview-session.js +395 -0
- package/dist/local-preview-source.d.ts +40 -0
- package/dist/local-preview-source.js +192 -0
- package/dist/local-preview-view-events.d.ts +101 -0
- package/dist/local-preview-view-events.js +179 -0
- package/dist/local-preview-view-state-sqlite.d.ts +24 -0
- package/dist/local-preview-view-state-sqlite.js +263 -0
- package/dist/local-preview-view-state.d.ts +93 -0
- package/dist/local-preview-view-state.js +108 -0
- package/dist/local-preview-vite-config.d.ts +6 -0
- package/dist/local-preview-vite-config.js +122 -0
- package/dist/local-preview-world-effects.d.ts +9 -0
- package/dist/local-preview-world-effects.js +55 -0
- package/dist/local-preview-worldline-store.d.ts +47 -0
- package/dist/local-preview-worldline-store.js +282 -0
- package/dist/local-preview-worldlines.d.ts +95 -0
- package/dist/local-preview-worldlines.js +136 -0
- package/dist/mcp-async-dispatcher.d.ts +3 -0
- package/dist/mcp-async-dispatcher.js +129 -0
- package/dist/mcp-line-queue.d.ts +14 -0
- package/dist/mcp-line-queue.js +184 -0
- package/dist/mcp-pipe-internal.d.ts +6 -0
- package/dist/mcp-pipe-internal.js +151 -0
- package/dist/mcp-pipe-response.d.ts +14 -0
- package/dist/mcp-pipe-response.js +50 -0
- package/dist/mcp-stdio-response.d.ts +12 -0
- package/dist/mcp-stdio-response.js +27 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +194 -0
- package/dist/prepared-embedding-cache.d.ts +38 -0
- package/dist/prepared-embedding-cache.js +238 -0
- package/dist/preview-browser-adapter.d.ts +53 -0
- package/dist/preview-browser-adapter.js +171 -0
- package/dist/preview-runtime.d.ts +67 -0
- package/dist/preview-runtime.js +460 -0
- package/dist/project-auth-transport.d.ts +27 -0
- package/dist/project-auth-transport.js +121 -0
- package/dist/project-auth.d.ts +51 -0
- package/dist/project-auth.js +108 -0
- package/dist/project-git-capture-recovery.d.ts +16 -0
- package/dist/project-git-capture-recovery.js +347 -0
- package/dist/project-git-cli.d.ts +5 -0
- package/dist/project-git-cli.js +40 -0
- package/dist/project-git-scenario-source.d.ts +10 -0
- package/dist/project-git-scenario-source.js +240 -0
- package/dist/project-git-view-source.d.ts +13 -0
- package/dist/project-git-view-source.js +134 -0
- package/dist/project-git.d.ts +97 -0
- package/dist/project-git.js +656 -0
- package/dist/project-host.d.ts +59 -0
- package/dist/project-host.js +471 -0
- package/dist/project-writer-lease.d.ts +18 -0
- package/dist/project-writer-lease.js +430 -0
- package/dist/public-error.d.ts +7 -0
- package/dist/public-error.js +61 -0
- package/dist/retrieval-benchmark.d.ts +46 -0
- package/dist/retrieval-benchmark.js +58 -0
- package/dist/safe-project-path.d.ts +8 -0
- package/dist/safe-project-path.js +93 -0
- package/dist/scenario-writer.d.ts +32 -0
- package/dist/scenario-writer.js +191 -0
- package/dist/transfer/download.d.ts +159 -0
- package/dist/transfer/download.js +598 -0
- package/dist/transfer/transport.d.ts +26 -0
- package/dist/transfer/transport.js +1 -0
- package/dist/transfer/upload.d.ts +162 -0
- package/dist/transfer/upload.js +354 -0
- package/dist/transfer-types.d.ts +95 -0
- package/dist/transfer-types.js +1 -0
- package/dist/transfer.d.ts +18 -0
- package/dist/transfer.js +273 -0
- package/dist/trigger-runtime.d.ts +121 -0
- package/dist/trigger-runtime.js +153 -0
- package/dist/vendor/wasm-core/asset-hashes.json +5 -0
- package/dist/vendor/wasm-core/wasm_core.d.ts +490 -0
- package/dist/vendor/wasm-core/wasm_core.js +1 -0
- package/dist/vendor/wasm-core/wasm_core_bg.wasm +0 -0
- package/dist/view-watcher.d.ts +9 -0
- package/dist/view-watcher.js +34 -0
- package/package.json +110 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import { sha256Hex } from "@world-engines/project-format";
|
|
2
|
+
import { WORLD_FACT_EXTRACTION_RESULT_SCHEMA, createNaturalLanguageWorldOperations, decodeWorldFactExtractionResult, } from "@world-engines/tmw/natural-language-ingestion";
|
|
3
|
+
import { INGESTION_SOURCE_CONTROL_KIND, ingestionFactEntityId, ingestionSourceMarkerId, ingestionSourceReconciliationReadIds, readIngestionSourceMarker, reconcileIngestionSource, } from "./ingestion-source-reconciliation.js";
|
|
4
|
+
const EXTRACTION_JSON_SCHEMA = Object.freeze({
|
|
5
|
+
type: "object",
|
|
6
|
+
additionalProperties: false,
|
|
7
|
+
required: ["facts"],
|
|
8
|
+
properties: {
|
|
9
|
+
facts: {
|
|
10
|
+
type: "array",
|
|
11
|
+
items: {
|
|
12
|
+
type: "object",
|
|
13
|
+
additionalProperties: false,
|
|
14
|
+
required: [
|
|
15
|
+
"subject_label", "predicate", "object_type", "object_value_json", "confidence", "evidence_quote",
|
|
16
|
+
],
|
|
17
|
+
properties: {
|
|
18
|
+
subject_label: { type: "string", minLength: 1 },
|
|
19
|
+
predicate: { type: "string", minLength: 1, pattern: "^[a-z][a-z0-9_]{0,63}$" },
|
|
20
|
+
object_type: { type: "string", enum: ["string", "number", "boolean", "entity", "json"] },
|
|
21
|
+
object_value_json: { type: "string", minLength: 1 },
|
|
22
|
+
qualifiers_json: { type: "string" },
|
|
23
|
+
confidence: { type: "number", minimum: 0, maximum: 1 },
|
|
24
|
+
evidence_quote: { type: "string", minLength: 1 },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
function record(value, code) {
|
|
31
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
32
|
+
throw new Error(code);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function exactKeys(value, expected, code) {
|
|
36
|
+
const actual = Object.keys(value).sort();
|
|
37
|
+
const sortedExpected = [...expected].sort();
|
|
38
|
+
if (actual.length !== sortedExpected.length || actual.some((key, index) => key !== sortedExpected[index]))
|
|
39
|
+
throw new Error(code);
|
|
40
|
+
}
|
|
41
|
+
const PROVIDER_FACT_KEYS = Object.freeze([
|
|
42
|
+
"subject_label", "predicate", "object_type", "object_value_json", "confidence", "evidence_quote",
|
|
43
|
+
]);
|
|
44
|
+
function parseJson(value, code) {
|
|
45
|
+
if (typeof value !== "string")
|
|
46
|
+
throw new Error(code);
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(value);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error(code);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function rejectionCode(error, fallback) {
|
|
55
|
+
const message = error instanceof Error ? error.message : fallback;
|
|
56
|
+
const stable = message
|
|
57
|
+
.replace(/^tmw_runtime_/u, "")
|
|
58
|
+
.replace(/world_fact_provider_\d+_/gu, "world_fact_provider_")
|
|
59
|
+
.replace(/world_fact_\d+_/gu, "world_fact_");
|
|
60
|
+
return /^[a-z0-9_]{1,160}$/u.test(stable) ? stable : fallback;
|
|
61
|
+
}
|
|
62
|
+
function rejectFact(diagnostics, error, fallback) {
|
|
63
|
+
diagnostics.rejectedFactCount += 1;
|
|
64
|
+
const code = rejectionCode(error, fallback);
|
|
65
|
+
diagnostics.rejectionReasons[code] = (diagnostics.rejectionReasons[code] ?? 0) + 1;
|
|
66
|
+
}
|
|
67
|
+
function normalizedProviderText(value, code) {
|
|
68
|
+
if (typeof value !== "string")
|
|
69
|
+
return value;
|
|
70
|
+
const normalized = value.trim().normalize("NFC");
|
|
71
|
+
if (normalized.length === 0 || normalized.includes("\0"))
|
|
72
|
+
throw new Error(code);
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
function normalizeEvidence(fact, chunkText, index) {
|
|
76
|
+
const quote = fact.evidence_quote;
|
|
77
|
+
if (typeof quote !== "string" || quote.length === 0)
|
|
78
|
+
throw new Error(`world_fact_${index}_evidence_invalid`);
|
|
79
|
+
const uniqueStart = chunkText.indexOf(quote);
|
|
80
|
+
if (uniqueStart < 0 || chunkText.lastIndexOf(quote) !== uniqueStart)
|
|
81
|
+
throw new Error(`world_fact_${index}_evidence_offset_unprovable`);
|
|
82
|
+
return { startOffset: uniqueStart, endOffset: uniqueStart + quote.length, quote };
|
|
83
|
+
}
|
|
84
|
+
async function stableEntityId(scopeId, label, code) {
|
|
85
|
+
const canonicalLabel = normalizedProviderText(label, code);
|
|
86
|
+
if (typeof canonicalLabel !== "string" || canonicalLabel.length > 512)
|
|
87
|
+
throw new Error(code);
|
|
88
|
+
const digest = await sha256Hex(new TextEncoder().encode(JSON.stringify({ scopeId, label: canonicalLabel })));
|
|
89
|
+
return Object.freeze({ id: `entity.${digest}`, label: canonicalLabel });
|
|
90
|
+
}
|
|
91
|
+
async function normalizeProviderExtraction(value, request, diagnostics) {
|
|
92
|
+
const result = record(value, "world_fact_provider_result_invalid");
|
|
93
|
+
// 仅保留给显式注入的本地/test harness;真实 provider schema 不包含此分支。
|
|
94
|
+
if (result.schema === WORLD_FACT_EXTRACTION_RESULT_SCHEMA && Array.isArray(result.facts))
|
|
95
|
+
return result;
|
|
96
|
+
exactKeys(result, ["facts"], "world_fact_provider_result_shape_invalid");
|
|
97
|
+
if (!Array.isArray(result.facts))
|
|
98
|
+
throw new Error("world_fact_provider_result_invalid");
|
|
99
|
+
return {
|
|
100
|
+
schema: WORLD_FACT_EXTRACTION_RESULT_SCHEMA,
|
|
101
|
+
facts: (await Promise.all(result.facts.map(async (candidate, index) => {
|
|
102
|
+
try {
|
|
103
|
+
const fact = record(candidate, `world_fact_provider_${index}_invalid`);
|
|
104
|
+
const keys = Object.keys(fact);
|
|
105
|
+
if (!(keys.length === PROVIDER_FACT_KEYS.length || keys.length === PROVIDER_FACT_KEYS.length + 1)
|
|
106
|
+
|| !PROVIDER_FACT_KEYS.every((key) => Object.hasOwn(fact, key))
|
|
107
|
+
|| (keys.length > PROVIDER_FACT_KEYS.length && !Object.hasOwn(fact, "qualifiers_json"))) {
|
|
108
|
+
throw new Error(`world_fact_provider_${index}_shape_invalid`);
|
|
109
|
+
}
|
|
110
|
+
const decodedValue = parseJson(fact.object_value_json, `world_fact_provider_${index}_object_json_invalid`);
|
|
111
|
+
let object;
|
|
112
|
+
if (fact.object_type === "string" && typeof decodedValue === "string")
|
|
113
|
+
object = { type: "string", value: decodedValue };
|
|
114
|
+
else if (fact.object_type === "number" && typeof decodedValue === "number")
|
|
115
|
+
object = { type: "number", value: decodedValue };
|
|
116
|
+
else if (fact.object_type === "boolean" && typeof decodedValue === "boolean")
|
|
117
|
+
object = { type: "boolean", value: decodedValue };
|
|
118
|
+
else if (fact.object_type === "entity") {
|
|
119
|
+
const entityLabel = typeof decodedValue === "string" ? decodedValue : record(decodedValue, `world_fact_provider_${index}_entity_invalid`).label;
|
|
120
|
+
object = { type: "entity", value: await stableEntityId(request.source.scopeId, entityLabel, `world_fact_provider_${index}_entity_invalid`) };
|
|
121
|
+
}
|
|
122
|
+
else if (fact.object_type === "json")
|
|
123
|
+
object = { type: "json", value: decodedValue };
|
|
124
|
+
else
|
|
125
|
+
throw new Error(`world_fact_provider_${index}_object_type_invalid`);
|
|
126
|
+
const qualifiers = fact.qualifiers_json === undefined ? {} : parseJson(fact.qualifiers_json, `world_fact_provider_${index}_qualifiers_invalid`);
|
|
127
|
+
return [{
|
|
128
|
+
kind: fact.object_type === "entity" ? "relation" : "attribute",
|
|
129
|
+
subject: await stableEntityId(request.source.scopeId, fact.subject_label, `world_fact_provider_${index}_subject_label_invalid`),
|
|
130
|
+
predicate: normalizedProviderText(fact.predicate, `world_fact_provider_${index}_predicate_invalid`),
|
|
131
|
+
object,
|
|
132
|
+
qualifiers,
|
|
133
|
+
confidence: fact.confidence,
|
|
134
|
+
evidence: normalizeEvidence(fact, request.chunk.text, index),
|
|
135
|
+
}];
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
rejectFact(diagnostics, error, "world_fact_provider_normalization_invalid");
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
}))).flat(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async function validatedProviderExtraction(value, request, diagnostics) {
|
|
145
|
+
const normalized = record(await normalizeProviderExtraction(value, request, diagnostics), "world_fact_provider_normalized_invalid");
|
|
146
|
+
if (!Array.isArray(normalized.facts))
|
|
147
|
+
throw new Error("world_fact_provider_normalized_invalid");
|
|
148
|
+
const facts = normalized.facts.flatMap((fact) => {
|
|
149
|
+
try {
|
|
150
|
+
return [...decodeWorldFactExtractionResult({ schema: WORLD_FACT_EXTRACTION_RESULT_SCHEMA, facts: [fact] }, request.chunk)];
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
rejectFact(diagnostics, error, "world_fact_schema_invalid");
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
return { schema: WORLD_FACT_EXTRACTION_RESULT_SCHEMA, facts };
|
|
158
|
+
}
|
|
159
|
+
function entitiesFrom(value) {
|
|
160
|
+
const result = record(value, "authoring_ingestion_world_get_invalid");
|
|
161
|
+
if (!Array.isArray(result.entities))
|
|
162
|
+
throw new Error("authoring_ingestion_world_get_invalid");
|
|
163
|
+
return result.entities.map((entity) => record(entity, "authoring_ingestion_world_entity_invalid"));
|
|
164
|
+
}
|
|
165
|
+
function reconciliationSnapshotFrom(value) {
|
|
166
|
+
const result = record(value, "authoring_ingestion_world_get_invalid");
|
|
167
|
+
if (!Array.isArray(result.entities) || !Array.isArray(result.relations))
|
|
168
|
+
throw new Error("authoring_ingestion_world_get_invalid");
|
|
169
|
+
return Object.freeze({
|
|
170
|
+
entities: Object.freeze(result.entities.map((value) => {
|
|
171
|
+
const entity = record(value, "authoring_ingestion_world_entity_invalid");
|
|
172
|
+
if (typeof entity.id !== "string" || typeof entity.kind !== "string")
|
|
173
|
+
throw new Error("authoring_ingestion_world_entity_invalid");
|
|
174
|
+
return Object.freeze({ id: entity.id, kind: entity.kind, props: record(entity.props, "authoring_ingestion_world_entity_invalid") });
|
|
175
|
+
})),
|
|
176
|
+
relations: Object.freeze(result.relations.map((value) => {
|
|
177
|
+
const relation = record(value, "authoring_ingestion_world_relation_invalid");
|
|
178
|
+
if (typeof relation.src !== "string" || typeof relation.dst !== "string" || typeof relation.kind !== "string")
|
|
179
|
+
throw new Error("authoring_ingestion_world_relation_invalid");
|
|
180
|
+
return Object.freeze({ src: relation.src, dst: relation.dst, kind: relation.kind, props: record(relation.props, "authoring_ingestion_world_relation_invalid") });
|
|
181
|
+
})),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function receiptFrom(value) {
|
|
185
|
+
const receipt = record(value, "authoring_ingestion_receipt_invalid");
|
|
186
|
+
if (receipt.schema_version !== 1 || typeof receipt.operation_id !== "string"
|
|
187
|
+
|| !Number.isSafeInteger(receipt.previous_project_revision)
|
|
188
|
+
|| !Number.isSafeInteger(receipt.next_project_revision)
|
|
189
|
+
|| receipt.readback_verified !== true) {
|
|
190
|
+
throw new Error("authoring_ingestion_receipt_invalid");
|
|
191
|
+
}
|
|
192
|
+
return receipt;
|
|
193
|
+
}
|
|
194
|
+
function factsFromBatch(batch) {
|
|
195
|
+
const facts = new Map();
|
|
196
|
+
for (const entity of batch.entities) {
|
|
197
|
+
const values = entity.props.facts;
|
|
198
|
+
if (!Array.isArray(values))
|
|
199
|
+
continue;
|
|
200
|
+
for (const value of values) {
|
|
201
|
+
const fact = record(value, "authoring_ingestion_fact_invalid");
|
|
202
|
+
if (typeof fact.factId !== "string" || typeof fact.scopeId !== "string")
|
|
203
|
+
throw new Error("authoring_ingestion_fact_invalid");
|
|
204
|
+
facts.set(`${fact.scopeId}\0${fact.factId}`, fact);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return Object.freeze([...facts.values()].sort((left, right) => left.factId.localeCompare(right.factId)));
|
|
208
|
+
}
|
|
209
|
+
function createExtractor(connection, diagnostics) {
|
|
210
|
+
return Object.freeze({
|
|
211
|
+
harness: connection.structuredJson.providerId,
|
|
212
|
+
model: connection.structuredJson.modelId,
|
|
213
|
+
contractVersion: WORLD_FACT_EXTRACTION_RESULT_SCHEMA,
|
|
214
|
+
async extract(request) {
|
|
215
|
+
const runAttempt = async (retry) => {
|
|
216
|
+
const attemptDiagnostics = { rejectedFactCount: 0, correctedEvidenceCount: 0, committedFacts: [], rejectionReasons: {}, unverifiedChunkIndexes: [] };
|
|
217
|
+
const result = await connection.structuredJson.generate({
|
|
218
|
+
schemaName: "world_fact_extraction",
|
|
219
|
+
jsonSchema: EXTRACTION_JSON_SCHEMA,
|
|
220
|
+
messages: [
|
|
221
|
+
{
|
|
222
|
+
role: "system",
|
|
223
|
+
content: "你是世界设定事实抽取器。只输出符合 schema 的 JSON,提取 chunk 中所有明确、原子、对自然语言问答有用的事实,不猜测也不因数量省略。subject_label 使用原文检索主体;predicate 必须是简短具体的 snake_case,并保持方向,例如地点的 current_responsible_person 指向责任人。object_value_json 必须是 JSON 值序列化后的字符串:string 写 JSON 字符串,number/boolean 写对应 JSON,entity 写 {\"label\":\"原文名称\"},复合答案才用 json。evidence_quote 必须完整逐字复制能独立证明事实的最短原文句子或分句;遇到“这里”“其”“该物件”等代词时,quote 必须连同前句中明确先行词一起复制。不得改写、省略、拼接或添加字符。不要生成 ID、offset 或 schema 外字段。" + (retry ? " 上次有事实无法由原文证明;本次务必重新选择包含明确主体、关系和答案且在 chunk 中唯一的连续完整原句。" : ""),
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
role: "user",
|
|
227
|
+
content: JSON.stringify({
|
|
228
|
+
schema: request.schema,
|
|
229
|
+
source: request.source,
|
|
230
|
+
chunk: request.chunk,
|
|
231
|
+
outputSchema: request.outputSchema,
|
|
232
|
+
}),
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
});
|
|
236
|
+
const value = await validatedProviderExtraction(result.value, request, attemptDiagnostics);
|
|
237
|
+
const factCount = record(value, "world_fact_provider_normalized_invalid").facts.length;
|
|
238
|
+
return { value, factCount, diagnostics: attemptDiagnostics };
|
|
239
|
+
};
|
|
240
|
+
const first = await runAttempt(false);
|
|
241
|
+
let attempts = [first];
|
|
242
|
+
if (first.factCount === 0 || first.diagnostics.rejectedFactCount > first.factCount) {
|
|
243
|
+
try {
|
|
244
|
+
attempts = [first, await runAttempt(true)];
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
first.diagnostics.rejectedFactCount += 1;
|
|
248
|
+
first.diagnostics.rejectionReasons.world_fact_retry_failed = (first.diagnostics.rejectionReasons.world_fact_retry_failed ?? 0) + 1;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const mergedFacts = new Map();
|
|
252
|
+
for (const attempt of attempts) {
|
|
253
|
+
const facts = record(attempt.value, "world_fact_provider_normalized_invalid").facts;
|
|
254
|
+
if (!Array.isArray(facts))
|
|
255
|
+
throw new Error("world_fact_provider_normalized_invalid");
|
|
256
|
+
for (const fact of facts)
|
|
257
|
+
mergedFacts.set(JSON.stringify(fact), fact);
|
|
258
|
+
diagnostics.rejectedFactCount += attempt.diagnostics.rejectedFactCount;
|
|
259
|
+
diagnostics.correctedEvidenceCount += attempt.diagnostics.correctedEvidenceCount;
|
|
260
|
+
}
|
|
261
|
+
for (const [code, count] of attempts.flatMap((attempt) => Object.entries(attempt.diagnostics.rejectionReasons))) {
|
|
262
|
+
diagnostics.rejectionReasons[code] = (diagnostics.rejectionReasons[code] ?? 0) + count;
|
|
263
|
+
}
|
|
264
|
+
if (mergedFacts.size === 0)
|
|
265
|
+
diagnostics.unverifiedChunkIndexes.push(request.chunk.index);
|
|
266
|
+
return { schema: WORLD_FACT_EXTRACTION_RESULT_SCHEMA, facts: [...mergedFacts.values()] };
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function createAuthority(host, connection, diagnostics, source, observedMarker, expectedProjectRevision) {
|
|
271
|
+
return Object.freeze({
|
|
272
|
+
async commit(batch) {
|
|
273
|
+
const facts = factsFromBatch(batch);
|
|
274
|
+
if (facts.length === 0)
|
|
275
|
+
throw new Error("authoring_ingestion_no_valid_facts");
|
|
276
|
+
diagnostics.committedFacts = facts.map((fact) => ({ scopeId: fact.scopeId, factId: fact.factId }));
|
|
277
|
+
const ids = ingestionSourceReconciliationReadIds({
|
|
278
|
+
scopeId: source.scopeId,
|
|
279
|
+
sourceId: source.sourceId,
|
|
280
|
+
batch,
|
|
281
|
+
marker: observedMarker,
|
|
282
|
+
});
|
|
283
|
+
const snapshot = reconciliationSnapshotFrom(await host.dispatchTool({ name: "world_get", input: { ids, include_relations: true } }));
|
|
284
|
+
const reconciliation = reconcileIngestionSource({
|
|
285
|
+
scopeId: source.scopeId,
|
|
286
|
+
sourceId: source.sourceId,
|
|
287
|
+
sourceRevision: source.sourceRevision,
|
|
288
|
+
batch,
|
|
289
|
+
expectedCurrentBatchId: observedMarker?.currentBatchId ?? null,
|
|
290
|
+
snapshot,
|
|
291
|
+
});
|
|
292
|
+
if (reconciliation.status === "already_committed") {
|
|
293
|
+
return { status: "already_committed", version: String(expectedProjectRevision) };
|
|
294
|
+
}
|
|
295
|
+
const searchableUpserts = reconciliation.operations.filter((operation) => operation.type === "upsert_entity" && operation.kind !== INGESTION_SOURCE_CONTROL_KIND);
|
|
296
|
+
const embeddings = searchableUpserts.length === 0 ? [] : await connection.embedding.embed(searchableUpserts.map((operation) => {
|
|
297
|
+
const text = operation.props.index_text;
|
|
298
|
+
if (typeof text !== "string")
|
|
299
|
+
throw new Error("authoring_ingestion_index_text_missing");
|
|
300
|
+
return text;
|
|
301
|
+
}));
|
|
302
|
+
if (embeddings.length !== searchableUpserts.length)
|
|
303
|
+
throw new Error("authoring_ingestion_embedding_count_invalid");
|
|
304
|
+
const embeddingById = new Map(searchableUpserts.map((operation, index) => [operation.id, embeddings[index]]));
|
|
305
|
+
const operations = reconciliation.operations.map((operation) => operation.type === "upsert_entity" && operation.kind !== INGESTION_SOURCE_CONTROL_KIND
|
|
306
|
+
? { ...operation, props: { ...operation.props, embedding: embeddingById.get(operation.id) } }
|
|
307
|
+
: { ...operation });
|
|
308
|
+
const receipt = receiptFrom(await host.dispatchTool({
|
|
309
|
+
name: "world_apply",
|
|
310
|
+
input: { operations },
|
|
311
|
+
effect_identity: {
|
|
312
|
+
operation_id: `world_ingest_${batch.batchId}`,
|
|
313
|
+
expected_project_revision: expectedProjectRevision,
|
|
314
|
+
},
|
|
315
|
+
}));
|
|
316
|
+
const markerId = ingestionSourceMarkerId(source.scopeId, source.sourceId);
|
|
317
|
+
const readback = entitiesFrom(await host.dispatchTool({ name: "world_get", input: { ids: [markerId] } }));
|
|
318
|
+
if (!readback.some((entity) => entity.id === markerId))
|
|
319
|
+
throw new Error("authoring_ingestion_readback_failed");
|
|
320
|
+
return { status: "committed", version: String(receipt.next_project_revision) };
|
|
321
|
+
},
|
|
322
|
+
async read(input) {
|
|
323
|
+
const id = ingestionFactEntityId(input.scopeId, input.factId);
|
|
324
|
+
const entities = entitiesFrom(await host.dispatchTool({ name: "world_get", input: { ids: [id] } }));
|
|
325
|
+
const entity = entities.find((candidate) => candidate.id === id);
|
|
326
|
+
if (entity === undefined)
|
|
327
|
+
return null;
|
|
328
|
+
const fact = record(entity.props, "authoring_ingestion_fact_props_invalid").fact;
|
|
329
|
+
if (fact?.scopeId !== input.scopeId || fact.factId !== input.factId)
|
|
330
|
+
throw new Error("authoring_ingestion_fact_readback_invalid");
|
|
331
|
+
return fact;
|
|
332
|
+
},
|
|
333
|
+
async resolve(input) {
|
|
334
|
+
const result = record(await host.dispatchTool({
|
|
335
|
+
name: "world_search",
|
|
336
|
+
input: {
|
|
337
|
+
query: input.query,
|
|
338
|
+
mode: "semantic",
|
|
339
|
+
limit: Math.min(100, Math.max(input.limit * 4, input.limit)),
|
|
340
|
+
filters: { scope_id: input.scopeId },
|
|
341
|
+
},
|
|
342
|
+
}), "authoring_ingestion_world_search_invalid");
|
|
343
|
+
if (!Array.isArray(result.results))
|
|
344
|
+
throw new Error("authoring_ingestion_world_search_invalid");
|
|
345
|
+
const candidates = result.results.map((value) => record(value, "authoring_ingestion_world_hit_invalid"));
|
|
346
|
+
const maximumScore = Math.max(0, ...candidates.map((hit) => typeof hit.score === "number" && Number.isFinite(hit.score) ? hit.score : 0));
|
|
347
|
+
const hits = [];
|
|
348
|
+
const seen = new Set();
|
|
349
|
+
for (const hit of candidates) {
|
|
350
|
+
const props = record(hit.props, "authoring_ingestion_world_hit_props_invalid");
|
|
351
|
+
const fact = props.fact;
|
|
352
|
+
if (fact?.scopeId !== input.scopeId || typeof fact.factId !== "string" || seen.has(fact.factId))
|
|
353
|
+
continue;
|
|
354
|
+
const rawScore = typeof hit.score === "number" && Number.isFinite(hit.score) ? hit.score : 0;
|
|
355
|
+
const relevance = maximumScore === 0 ? 0 : Math.min(1, rawScore / maximumScore);
|
|
356
|
+
if (relevance < input.minimumRelevance)
|
|
357
|
+
continue;
|
|
358
|
+
const channels = Array.isArray(hit.channels) && hit.channels.includes("semantic") ? ["semantic"] : [];
|
|
359
|
+
if (channels.length === 0)
|
|
360
|
+
continue;
|
|
361
|
+
seen.add(fact.factId);
|
|
362
|
+
hits.push({ fact, relevance, channels });
|
|
363
|
+
}
|
|
364
|
+
return Object.freeze(hits.slice(0, input.limit));
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* 把全文抽取、真实 embedding 与 Host authority commit 串成一个作者调用。
|
|
370
|
+
* 唯一持久化入口是 host.dispatchTool(world_apply),不得直接写 Ladybug checkpoint。
|
|
371
|
+
*/
|
|
372
|
+
export async function ingestProjectDocument(host, source, capability) {
|
|
373
|
+
capability.modelConnection.assertAvailable?.(["structured", "embedding"]);
|
|
374
|
+
const diagnostics = { rejectedFactCount: 0, correctedEvidenceCount: 0, committedFacts: [], rejectionReasons: {}, unverifiedChunkIndexes: [] };
|
|
375
|
+
// revision 与 source marker 属于同一个 reconcile preimage,必须在任何模型/embedding 调用前冻结。
|
|
376
|
+
const observedStatus = await host.inspect();
|
|
377
|
+
const sourceMarkerId = ingestionSourceMarkerId(source.scopeId, source.sourceId);
|
|
378
|
+
const initialSnapshot = reconciliationSnapshotFrom(await host.dispatchTool({
|
|
379
|
+
name: "world_get",
|
|
380
|
+
input: { ids: [sourceMarkerId], include_relations: false },
|
|
381
|
+
}));
|
|
382
|
+
const observedMarker = readIngestionSourceMarker(initialSnapshot, source.scopeId, source.sourceId);
|
|
383
|
+
const operations = createNaturalLanguageWorldOperations({
|
|
384
|
+
authority: createAuthority(host, capability.modelConnection, diagnostics, source, observedMarker, observedStatus.project_revision),
|
|
385
|
+
extractor: createExtractor(capability.modelConnection, diagnostics),
|
|
386
|
+
...(capability.maximumConcurrentExtractions === undefined ? {} : { maximumConcurrentExtractions: capability.maximumConcurrentExtractions }),
|
|
387
|
+
chunking: capability.chunking ?? { maximumCharacters: 450, overlapCharacters: 80 },
|
|
388
|
+
...(capability.clock === undefined ? {} : { clock: capability.clock }),
|
|
389
|
+
});
|
|
390
|
+
const receipt = await operations.commit(source);
|
|
391
|
+
const warnings = [];
|
|
392
|
+
if (diagnostics.rejectedFactCount > 0)
|
|
393
|
+
warnings.push({ code: "world_facts_rejected", count: diagnostics.rejectedFactCount });
|
|
394
|
+
if (diagnostics.correctedEvidenceCount > 0)
|
|
395
|
+
warnings.push({ code: "evidence_offsets_corrected", count: diagnostics.correctedEvidenceCount });
|
|
396
|
+
const unverifiedChunkIndexes = Object.freeze([...new Set(diagnostics.unverifiedChunkIndexes)].sort((left, right) => left - right));
|
|
397
|
+
if (unverifiedChunkIndexes.length > 0)
|
|
398
|
+
warnings.push({ code: "unverified_chunks", count: unverifiedChunkIndexes.length });
|
|
399
|
+
return Object.freeze({
|
|
400
|
+
...receipt,
|
|
401
|
+
completeness: diagnostics.rejectedFactCount === 0 && unverifiedChunkIndexes.length === 0 ? "complete" : "partial",
|
|
402
|
+
rejectedFactCount: diagnostics.rejectedFactCount,
|
|
403
|
+
correctedEvidenceCount: diagnostics.correctedEvidenceCount,
|
|
404
|
+
warnings: Object.freeze(warnings),
|
|
405
|
+
committedFacts: Object.freeze(diagnostics.committedFacts.map((fact) => Object.freeze(fact))),
|
|
406
|
+
rejectionReasons: Object.freeze(Object.fromEntries(Object.entries(diagnostics.rejectionReasons).sort(([left], [right]) => left.localeCompare(right)))),
|
|
407
|
+
unverifiedChunkCount: unverifiedChunkIndexes.length,
|
|
408
|
+
unverifiedChunkIndexes,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 作者侧真实模型连接。认证材料仅存在于 host process 内存和请求 header 中,
|
|
3
|
+
* 错误对象、usage 与返回值都不会携带 token、底层响应正文或请求 URL。
|
|
4
|
+
*/
|
|
5
|
+
export declare const AUTHORING_EMBEDDING_MODEL_ALLOWLIST: readonly ["qwen/qwen3-embedding-8b", "qwen/qwen3-embedding-4b", "openai/text-embedding-3-small"];
|
|
6
|
+
export declare const EMBEDDING_BATCH_SIZE = 128;
|
|
7
|
+
declare const OPENROUTER_PRODUCTION_BASE_URL = "https://openrouter.ai/api/v1";
|
|
8
|
+
declare const DEEPSEEK_PRODUCTION_BASE_URL = "https://api.deepseek.com";
|
|
9
|
+
declare const DEEPSEEK_V4_FLASH_MODEL = "deepseek-v4-flash";
|
|
10
|
+
export type AuthoringModelConnectionErrorCode = "invalid_configuration" | "embedding_credentials_missing" | "structured_credentials_missing" | "invalid_request" | "aborted" | "timeout" | "transport_failed" | "provider_rejected" | "provider_model_mismatch" | "provider_output_truncated" | "provider_response_invalid";
|
|
11
|
+
export type AuthoringModelFinishReason = "stop" | "length" | "tool_calls" | "content_filter" | "error" | "other" | "missing";
|
|
12
|
+
export type AuthoringModelContentType = "string" | "array" | "object" | "number" | "boolean" | "null" | "undefined";
|
|
13
|
+
export type AuthoringJsonFraming = "bare_json" | "single_json_fence";
|
|
14
|
+
export interface AuthoringJsonParseIssue {
|
|
15
|
+
readonly layer: "http_envelope" | "model_output";
|
|
16
|
+
readonly position: number | null;
|
|
17
|
+
}
|
|
18
|
+
/** 只含固定枚举和长度,不携带 provider 正文、URL、model 或认证材料。 */
|
|
19
|
+
export interface AuthoringModelResponseMetadata {
|
|
20
|
+
readonly finishReason: AuthoringModelFinishReason;
|
|
21
|
+
readonly contentType: AuthoringModelContentType;
|
|
22
|
+
readonly contentLength: number | null;
|
|
23
|
+
readonly framing?: AuthoringJsonFraming;
|
|
24
|
+
}
|
|
25
|
+
export type AuthoringJsonSchemaIssueKeyword = "type" | "required" | "additionalProperties" | "enum" | "pattern" | "minLength" | "maxLength" | "minimum" | "maximum" | "minItems" | "maxItems";
|
|
26
|
+
/** 只含 schema 路径与固定 keyword,不包含 candidate value。 */
|
|
27
|
+
export interface AuthoringJsonSchemaIssue {
|
|
28
|
+
readonly path: string;
|
|
29
|
+
readonly keyword: AuthoringJsonSchemaIssueKeyword;
|
|
30
|
+
}
|
|
31
|
+
export type AuthoringModelValidationStage = "closed_schema" | "usage" | "response_parse" | "response_shape";
|
|
32
|
+
/** 所有失败都是可穷举、已脱敏的 terminal result;本模块绝不静默降级。 */
|
|
33
|
+
export declare class AuthoringModelConnectionError extends Error {
|
|
34
|
+
readonly code: AuthoringModelConnectionErrorCode;
|
|
35
|
+
readonly retryable: boolean;
|
|
36
|
+
readonly status?: number | undefined;
|
|
37
|
+
readonly responseMetadata?: AuthoringModelResponseMetadata | undefined;
|
|
38
|
+
readonly schemaIssue?: AuthoringJsonSchemaIssue | undefined;
|
|
39
|
+
readonly validationStage?: AuthoringModelValidationStage | undefined;
|
|
40
|
+
readonly parseIssue?: AuthoringJsonParseIssue | undefined;
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly terminal: true;
|
|
43
|
+
readonly validationIssues: readonly AuthoringJsonSchemaIssue[];
|
|
44
|
+
constructor(code: AuthoringModelConnectionErrorCode, retryable: boolean, status?: number | undefined, responseMetadata?: AuthoringModelResponseMetadata | undefined, schemaIssue?: AuthoringJsonSchemaIssue | undefined, validationStage?: AuthoringModelValidationStage | undefined, parseIssue?: AuthoringJsonParseIssue | undefined);
|
|
45
|
+
}
|
|
46
|
+
export interface AuthoringModelUsage {
|
|
47
|
+
readonly inputTokens: number;
|
|
48
|
+
readonly outputTokens: number;
|
|
49
|
+
readonly totalTokens: number;
|
|
50
|
+
readonly requests: number;
|
|
51
|
+
/** 实际发起的 HTTP 次数;tokens/requests 只累计收到且验证通过的 provider usage,丢失响应的计费未知。 */
|
|
52
|
+
readonly requestAttempts?: number;
|
|
53
|
+
readonly providerId?: string;
|
|
54
|
+
readonly modelId?: string;
|
|
55
|
+
readonly cachedInputTokens?: number;
|
|
56
|
+
readonly byProviderModel?: readonly AuthoringProviderModelUsage[];
|
|
57
|
+
}
|
|
58
|
+
export interface AuthoringProviderModelUsage {
|
|
59
|
+
readonly providerId: string;
|
|
60
|
+
readonly modelId: string;
|
|
61
|
+
readonly inputTokens: number;
|
|
62
|
+
readonly outputTokens: number;
|
|
63
|
+
readonly totalTokens: number;
|
|
64
|
+
readonly cachedInputTokens?: number;
|
|
65
|
+
readonly requests: number;
|
|
66
|
+
readonly requestAttempts?: number;
|
|
67
|
+
}
|
|
68
|
+
export interface AuthoringEmbeddingPort {
|
|
69
|
+
readonly providerId: string;
|
|
70
|
+
readonly modelId: string;
|
|
71
|
+
readonly dimensions: number;
|
|
72
|
+
/** embedding provider 的 canonical URL origin;cache identity 不得从 providerId 猜测。 */
|
|
73
|
+
readonly origin?: string;
|
|
74
|
+
/** provider/policy descriptor identity;存在时必须进入 cache identity。 */
|
|
75
|
+
readonly descriptorSha256?: string;
|
|
76
|
+
readonly descriptorSha256ByInputKind?: Readonly<{
|
|
77
|
+
document: string;
|
|
78
|
+
query: string;
|
|
79
|
+
}>;
|
|
80
|
+
embed(texts: readonly string[], options?: Readonly<{
|
|
81
|
+
signal?: AbortSignal;
|
|
82
|
+
inputKind?: "document" | "query";
|
|
83
|
+
}>): Promise<readonly (readonly number[])[]>;
|
|
84
|
+
}
|
|
85
|
+
export type AuthoringStructuredMessage = Readonly<{
|
|
86
|
+
role: "system" | "user" | "assistant";
|
|
87
|
+
content: string;
|
|
88
|
+
}>;
|
|
89
|
+
export type AuthoringStructuredTaskMode = "extraction" | "simulation_retrieval" | "full_book_planning";
|
|
90
|
+
export type AuthoringStructuredJsonRequest = Readonly<{
|
|
91
|
+
schemaName: string;
|
|
92
|
+
jsonSchema: Readonly<Record<string, unknown>>;
|
|
93
|
+
messages: readonly AuthoringStructuredMessage[];
|
|
94
|
+
taskMode?: AuthoringStructuredTaskMode;
|
|
95
|
+
/** 仅 production D4F 支持;未指定时保持 none,显式禁用配置不能被 low 覆盖。 */
|
|
96
|
+
reasoningEffort?: "none" | "low";
|
|
97
|
+
maxOutputTokens?: number;
|
|
98
|
+
timeoutMs?: number;
|
|
99
|
+
signal?: AbortSignal;
|
|
100
|
+
}>;
|
|
101
|
+
export type AuthoringStructuredJsonResult<T = unknown> = Readonly<{
|
|
102
|
+
value: T;
|
|
103
|
+
usage: AuthoringModelUsage;
|
|
104
|
+
/** 只允许单一完整 json 围栏的传输解包;解包后仍须通过完整 schema。 */
|
|
105
|
+
framing?: AuthoringJsonFraming;
|
|
106
|
+
}>;
|
|
107
|
+
export interface AuthoringStructuredJsonPort {
|
|
108
|
+
readonly providerId: string;
|
|
109
|
+
readonly modelId: string;
|
|
110
|
+
generate<T = unknown>(request: AuthoringStructuredJsonRequest): Promise<AuthoringStructuredJsonResult<T>>;
|
|
111
|
+
}
|
|
112
|
+
export interface AuthoringModelConnection {
|
|
113
|
+
readonly embedding: AuthoringEmbeddingPort;
|
|
114
|
+
readonly structuredJson: AuthoringStructuredJsonPort;
|
|
115
|
+
/** 在任何可能计费的调用前验证本阶段实际需要的 provider port。 */
|
|
116
|
+
assertAvailable?(ports: readonly ("embedding" | "structured")[]): void;
|
|
117
|
+
usage(): AuthoringModelUsage;
|
|
118
|
+
}
|
|
119
|
+
export type AuthoringRetryConfig = Readonly<{
|
|
120
|
+
maxAttempts?: number;
|
|
121
|
+
baseDelayMs?: number;
|
|
122
|
+
maxDelayMs?: number;
|
|
123
|
+
}>;
|
|
124
|
+
export type OpenAiCompatibleAuthoringModelConfig = Readonly<{
|
|
125
|
+
/** 只能由 host process 环境或私有 capability 注入。 */
|
|
126
|
+
apiKey: string;
|
|
127
|
+
embeddingModel: (typeof AUTHORING_EMBEDDING_MODEL_ALLOWLIST)[number];
|
|
128
|
+
embeddingDimensions: number;
|
|
129
|
+
structuredChatModel: string;
|
|
130
|
+
/** structuredChatModel 必须显式位于此名单,禁止调用方任意透传 model。 */
|
|
131
|
+
structuredChatModelAllowlist: readonly string[];
|
|
132
|
+
baseUrl?: string;
|
|
133
|
+
/** 自定义 OpenAI-compatible origin/path 也必须由 host 显式放行。 */
|
|
134
|
+
baseUrlAllowlist?: readonly string[];
|
|
135
|
+
timeoutMs?: number;
|
|
136
|
+
retry?: AuthoringRetryConfig;
|
|
137
|
+
}>;
|
|
138
|
+
export type ProductionAuthoringModelConfig = Readonly<{
|
|
139
|
+
embedding: Readonly<{
|
|
140
|
+
apiKey: string;
|
|
141
|
+
baseUrl?: typeof OPENROUTER_PRODUCTION_BASE_URL;
|
|
142
|
+
model?: "qwen/qwen3-embedding-8b";
|
|
143
|
+
dimensions?: 1024;
|
|
144
|
+
timeoutMs?: number;
|
|
145
|
+
}>;
|
|
146
|
+
structured: Readonly<{
|
|
147
|
+
apiKey: string;
|
|
148
|
+
baseUrl?: typeof DEEPSEEK_PRODUCTION_BASE_URL;
|
|
149
|
+
model?: typeof DEEPSEEK_V4_FLASH_MODEL;
|
|
150
|
+
thinking?: "disabled";
|
|
151
|
+
responseFormat?: "json_schema";
|
|
152
|
+
defaultTaskMode?: AuthoringStructuredTaskMode;
|
|
153
|
+
/** 结构化任务的试用采样参数;默认值不是统计稳定性保证。 */
|
|
154
|
+
temperature?: number;
|
|
155
|
+
maxOutputTokens?: number;
|
|
156
|
+
timeoutMs?: number;
|
|
157
|
+
}>;
|
|
158
|
+
retry?: AuthoringRetryConfig;
|
|
159
|
+
}>;
|
|
160
|
+
export declare function createOpenAiCompatibleAuthoringModelConnection(config: OpenAiCompatibleAuthoringModelConfig): AuthoringModelConnection;
|
|
161
|
+
/**
|
|
162
|
+
* 生产作者模型连接:embedding 固定走 OpenRouter/Qwen3,structured JSON 固定走
|
|
163
|
+
* DeepSeek 官方 Responses API/V4 Flash;两个 credential 永远位于不同 poster closure。
|
|
164
|
+
*/
|
|
165
|
+
export declare function createProductionAuthoringModelConnection(config: ProductionAuthoringModelConfig): AuthoringModelConnection;
|
|
166
|
+
export {};
|