@opengeni/documents 0.5.18 → 0.5.38
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/dist/atlassian.d.ts +78 -0
- package/dist/atlassian.js +140 -0
- package/dist/atlassian.js.map +1 -0
- package/dist/google-drive.d.ts +2 -0
- package/dist/google-drive.js +12 -1
- package/dist/google-drive.js.map +1 -1
- package/dist/index.d.ts +74 -4
- package/dist/index.js +645 -27
- package/dist/index.js.map +1 -1
- package/dist/knowledge-projection.d.ts +14 -0
- package/package.json +10 -6
- package/src/atlassian.ts +250 -0
- package/src/google-drive.ts +21 -1
- package/src/index.ts +674 -26
- package/src/knowledge-projection.ts +214 -0
package/dist/index.js
CHANGED
|
@@ -8,9 +8,185 @@ import {
|
|
|
8
8
|
withWorkspaceSubjectRls
|
|
9
9
|
} from "@opengeni/db";
|
|
10
10
|
import * as schema from "@opengeni/db/schema";
|
|
11
|
-
import {
|
|
12
|
-
import { and, asc, desc, eq, inArray, or, sql } from "drizzle-orm";
|
|
13
|
-
import
|
|
11
|
+
import { createHash } from "crypto";
|
|
12
|
+
import { and, asc, desc, eq, gt, inArray, isNotNull, or, sql } from "drizzle-orm";
|
|
13
|
+
import { KNOWLEDGE_BROWSE_CURSOR_MAX_CHARS } from "@opengeni/contracts";
|
|
14
|
+
|
|
15
|
+
// src/knowledge-projection.ts
|
|
16
|
+
import {
|
|
17
|
+
KNOWLEDGE_BODY_MAX_BYTES,
|
|
18
|
+
KNOWLEDGE_METADATA_MAX_BYTES,
|
|
19
|
+
KNOWLEDGE_METADATA_MAX_DEPTH,
|
|
20
|
+
KNOWLEDGE_METADATA_MAX_ITEMS,
|
|
21
|
+
KNOWLEDGE_SOURCE_STRING_MAX_BYTES,
|
|
22
|
+
KNOWLEDGE_SOURCE_URI_MAX_BYTES,
|
|
23
|
+
KNOWLEDGE_SUMMARY_MAX_BYTES,
|
|
24
|
+
KNOWLEDGE_TITLE_MAX_BYTES,
|
|
25
|
+
KNOWLEDGE_TOPIC_MAX_BYTES,
|
|
26
|
+
KNOWLEDGE_TOPICS_MAX_ITEMS
|
|
27
|
+
} from "@opengeni/contracts";
|
|
28
|
+
var utf8Bytes = (value) => Buffer.byteLength(value, "utf8");
|
|
29
|
+
function truncateUtf8(value, maxBytes) {
|
|
30
|
+
if (utf8Bytes(value) <= maxBytes) return { value, truncated: false };
|
|
31
|
+
let low = 0;
|
|
32
|
+
let high = value.length;
|
|
33
|
+
while (low < high) {
|
|
34
|
+
const middle = Math.ceil((low + high) / 2);
|
|
35
|
+
const end2 = middle > 0 && middle < value.length && /[\uD800-\uDBFF]/u.test(value[middle - 1]) ? middle - 1 : middle;
|
|
36
|
+
if (utf8Bytes(value.slice(0, end2)) <= maxBytes) low = middle;
|
|
37
|
+
else high = middle - 1;
|
|
38
|
+
}
|
|
39
|
+
let end = low;
|
|
40
|
+
if (end > 0 && end < value.length && /[\uD800-\uDBFF]/u.test(value[end - 1])) end -= 1;
|
|
41
|
+
while (end > 0 && utf8Bytes(value.slice(0, end)) > maxBytes) end -= 1;
|
|
42
|
+
return { value: value.slice(0, end), truncated: true };
|
|
43
|
+
}
|
|
44
|
+
function projectNullableString(value, maxBytes, field, fields) {
|
|
45
|
+
if (value === null) return null;
|
|
46
|
+
const projected = truncateUtf8(value, maxBytes);
|
|
47
|
+
if (projected.truncated) fields.add(field);
|
|
48
|
+
return projected.value;
|
|
49
|
+
}
|
|
50
|
+
function projectSourceUri(value, fields) {
|
|
51
|
+
if (value === null || value === "") return null;
|
|
52
|
+
if (utf8Bytes(value) > KNOWLEDGE_SOURCE_URI_MAX_BYTES) {
|
|
53
|
+
fields.add("provenance.source.uri");
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
function projectTopics(value, fields) {
|
|
59
|
+
if (!Array.isArray(value)) {
|
|
60
|
+
if (value !== null && value !== void 0) fields.add("content.topics");
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const result = [];
|
|
64
|
+
for (const candidate of value) {
|
|
65
|
+
if (result.length >= KNOWLEDGE_TOPICS_MAX_ITEMS) {
|
|
66
|
+
fields.add("content.topics");
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
if (typeof candidate !== "string") {
|
|
70
|
+
fields.add("content.topics");
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const projected = truncateUtf8(candidate, KNOWLEDGE_TOPIC_MAX_BYTES);
|
|
74
|
+
if (projected.truncated) fields.add("content.topics");
|
|
75
|
+
result.push(projected.value);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
function projectMetadata(value) {
|
|
80
|
+
let remainingItems = KNOWLEDGE_METADATA_MAX_ITEMS;
|
|
81
|
+
let truncated = false;
|
|
82
|
+
const visit = (candidate, depth) => {
|
|
83
|
+
if (remainingItems <= 0 || depth > KNOWLEDGE_METADATA_MAX_DEPTH) {
|
|
84
|
+
truncated = true;
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
remainingItems -= 1;
|
|
88
|
+
if (candidate === null || typeof candidate === "boolean" || typeof candidate === "number" && Number.isFinite(candidate)) {
|
|
89
|
+
return candidate;
|
|
90
|
+
}
|
|
91
|
+
if (typeof candidate === "string") {
|
|
92
|
+
const projected = truncateUtf8(candidate, KNOWLEDGE_SOURCE_STRING_MAX_BYTES);
|
|
93
|
+
if (projected.truncated) truncated = true;
|
|
94
|
+
return projected.value;
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(candidate)) {
|
|
97
|
+
const result2 = [];
|
|
98
|
+
for (const item of candidate) {
|
|
99
|
+
const projected = visit(item, depth + 1);
|
|
100
|
+
if (projected === void 0) break;
|
|
101
|
+
result2.push(projected);
|
|
102
|
+
}
|
|
103
|
+
return result2;
|
|
104
|
+
}
|
|
105
|
+
if (candidate && typeof candidate === "object") {
|
|
106
|
+
const result2 = {};
|
|
107
|
+
for (const key of Object.keys(candidate).sort()) {
|
|
108
|
+
if (utf8Bytes(key) > KNOWLEDGE_TOPIC_MAX_BYTES) {
|
|
109
|
+
truncated = true;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const projected = visit(candidate[key], depth + 1);
|
|
113
|
+
if (projected === void 0) break;
|
|
114
|
+
result2[key] = projected;
|
|
115
|
+
}
|
|
116
|
+
return result2;
|
|
117
|
+
}
|
|
118
|
+
truncated = true;
|
|
119
|
+
return void 0;
|
|
120
|
+
};
|
|
121
|
+
const root = visit(value, 0);
|
|
122
|
+
let result = root && typeof root === "object" && !Array.isArray(root) ? root : {};
|
|
123
|
+
if (result !== root) truncated = true;
|
|
124
|
+
const bounded = {};
|
|
125
|
+
for (const key of Object.keys(result).sort()) {
|
|
126
|
+
bounded[key] = result[key];
|
|
127
|
+
if (utf8Bytes(JSON.stringify(bounded)) > KNOWLEDGE_METADATA_MAX_BYTES) {
|
|
128
|
+
delete bounded[key];
|
|
129
|
+
truncated = true;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
result = bounded;
|
|
134
|
+
return { value: result, truncated };
|
|
135
|
+
}
|
|
136
|
+
function projectKnowledgeRecord(input) {
|
|
137
|
+
const fields = /* @__PURE__ */ new Set();
|
|
138
|
+
const title = truncateUtf8(input.title, KNOWLEDGE_TITLE_MAX_BYTES);
|
|
139
|
+
if (title.truncated) fields.add("title");
|
|
140
|
+
const metadata = projectMetadata(input.metadata);
|
|
141
|
+
if (metadata.truncated) fields.add("content.metadata");
|
|
142
|
+
const source = {
|
|
143
|
+
...input.source,
|
|
144
|
+
uri: projectSourceUri(input.source.uri, fields),
|
|
145
|
+
externalId: projectNullableString(
|
|
146
|
+
input.source.externalId,
|
|
147
|
+
KNOWLEDGE_SOURCE_STRING_MAX_BYTES,
|
|
148
|
+
"provenance.source.externalId",
|
|
149
|
+
fields
|
|
150
|
+
),
|
|
151
|
+
title: projectNullableString(
|
|
152
|
+
input.source.title,
|
|
153
|
+
KNOWLEDGE_SOURCE_STRING_MAX_BYTES,
|
|
154
|
+
"provenance.source.title",
|
|
155
|
+
fields
|
|
156
|
+
),
|
|
157
|
+
author: projectNullableString(
|
|
158
|
+
input.source.author,
|
|
159
|
+
KNOWLEDGE_SOURCE_STRING_MAX_BYTES,
|
|
160
|
+
"provenance.source.author",
|
|
161
|
+
fields
|
|
162
|
+
),
|
|
163
|
+
version: projectNullableString(
|
|
164
|
+
input.source.version,
|
|
165
|
+
KNOWLEDGE_SOURCE_STRING_MAX_BYTES,
|
|
166
|
+
"provenance.source.version",
|
|
167
|
+
fields
|
|
168
|
+
)
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
title: title.value,
|
|
172
|
+
content: {
|
|
173
|
+
format: "markdown",
|
|
174
|
+
body: projectNullableString(input.body, KNOWLEDGE_BODY_MAX_BYTES, "content.body", fields),
|
|
175
|
+
summary: projectNullableString(
|
|
176
|
+
input.summary,
|
|
177
|
+
KNOWLEDGE_SUMMARY_MAX_BYTES,
|
|
178
|
+
"content.summary",
|
|
179
|
+
fields
|
|
180
|
+
),
|
|
181
|
+
topics: projectTopics(input.topics, fields),
|
|
182
|
+
metadata: metadata.value
|
|
183
|
+
},
|
|
184
|
+
source,
|
|
185
|
+
projection: { truncated: fields.size > 0, fields: [...fields].sort() }
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/index.ts
|
|
14
190
|
var DEFAULT_DOCUMENT_PARSER = "liteparse";
|
|
15
191
|
var DEFAULT_DOCUMENT_EMBEDDING_MODEL = "text-embedding-3-large";
|
|
16
192
|
var DEFAULT_DOCUMENT_EMBEDDING_DIMENSIONS = 3072;
|
|
@@ -19,6 +195,7 @@ var DEFAULT_DOCUMENT_CHUNK_OVERLAP = 160;
|
|
|
19
195
|
var DEFAULT_DOCUMENT_CURATION_MODEL = "gpt-4o-mini";
|
|
20
196
|
var DOCUMENT_CURATION_MAX_INPUT_CHARS = 24e3;
|
|
21
197
|
var DOCUMENT_AUTHORITY_SUBJECT_MAX_BYTES = 1024;
|
|
198
|
+
var DOCUMENT_INDEX_CHECKPOINT_MAX_CHARS = 1024;
|
|
22
199
|
var DOCUMENT_CURATION_AUTO_FILE_CONFIDENCE = 0.75;
|
|
23
200
|
var DEFAULT_BASE_NAME = "Default";
|
|
24
201
|
var DEFAULT_BASE_DESCRIPTION = "Default base for dropped files and notes.";
|
|
@@ -44,6 +221,7 @@ var LiteParseDocumentParser = class {
|
|
|
44
221
|
}
|
|
45
222
|
async parseWithLiteParse(bytes) {
|
|
46
223
|
return await this.enqueueParse(async () => {
|
|
224
|
+
const { LiteParse } = await import("@llamaindex/liteparse");
|
|
47
225
|
const parser = new LiteParse({ ocrEnabled: true, numWorkers: 1 });
|
|
48
226
|
const result = await parser.parse(Buffer.from(bytes), true);
|
|
49
227
|
const text = typeof result?.text === "string" ? result.text : "";
|
|
@@ -85,7 +263,7 @@ var RecursiveTextChunker = class {
|
|
|
85
263
|
}
|
|
86
264
|
};
|
|
87
265
|
var OpenAIEmbeddingProvider = class {
|
|
88
|
-
|
|
266
|
+
clientPromise = null;
|
|
89
267
|
apiKey;
|
|
90
268
|
baseURL;
|
|
91
269
|
constructor(args) {
|
|
@@ -105,7 +283,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
105
283
|
const out = [];
|
|
106
284
|
for (let start = 0; start < texts.length; start += 64) {
|
|
107
285
|
const batch = texts.slice(start, start + 64);
|
|
108
|
-
const response = await this.openai().embeddings.create({
|
|
286
|
+
const response = await (await this.openai()).embeddings.create({
|
|
109
287
|
model: this.model,
|
|
110
288
|
input: batch,
|
|
111
289
|
dimensions: this.dimensions
|
|
@@ -123,17 +301,19 @@ var OpenAIEmbeddingProvider = class {
|
|
|
123
301
|
}
|
|
124
302
|
return embedding;
|
|
125
303
|
}
|
|
126
|
-
openai() {
|
|
304
|
+
async openai() {
|
|
127
305
|
if (!this.apiKey) {
|
|
128
306
|
throw new Error("OpenAI document embeddings require an API key");
|
|
129
307
|
}
|
|
130
|
-
this.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
308
|
+
this.clientPromise ??= import("openai").then(
|
|
309
|
+
({ default: OpenAIClient }) => new OpenAIClient({
|
|
310
|
+
apiKey: this.apiKey,
|
|
311
|
+
...this.baseURL ? { baseURL: this.baseURL } : {},
|
|
312
|
+
...this.defaultQuery ? { defaultQuery: this.defaultQuery } : {},
|
|
313
|
+
...this.defaultHeaders ? { defaultHeaders: this.defaultHeaders } : {}
|
|
314
|
+
})
|
|
315
|
+
);
|
|
316
|
+
return await this.clientPromise;
|
|
137
317
|
}
|
|
138
318
|
};
|
|
139
319
|
var DeterministicEmbeddingProvider = class {
|
|
@@ -196,7 +376,7 @@ var CURATION_SYSTEM_PROMPT = [
|
|
|
196
376
|
"well, return targetBaseId null and confidence 0. Respond with JSON only."
|
|
197
377
|
].join(" ");
|
|
198
378
|
var OpenAICurationProvider = class {
|
|
199
|
-
|
|
379
|
+
clientPromise = null;
|
|
200
380
|
apiKey;
|
|
201
381
|
baseURL;
|
|
202
382
|
defaultHeaders;
|
|
@@ -210,7 +390,7 @@ var OpenAICurationProvider = class {
|
|
|
210
390
|
this.model = args.model ?? DEFAULT_DOCUMENT_CURATION_MODEL;
|
|
211
391
|
}
|
|
212
392
|
async curate(input) {
|
|
213
|
-
const response = await this.openai().chat.completions.create({
|
|
393
|
+
const response = await (await this.openai()).chat.completions.create({
|
|
214
394
|
model: this.model,
|
|
215
395
|
response_format: { type: "json_object" },
|
|
216
396
|
messages: [
|
|
@@ -232,17 +412,19 @@ var OpenAICurationProvider = class {
|
|
|
232
412
|
}
|
|
233
413
|
return parseCurationOutcome(raw, input.bases);
|
|
234
414
|
}
|
|
235
|
-
openai() {
|
|
415
|
+
async openai() {
|
|
236
416
|
if (!this.apiKey) {
|
|
237
417
|
throw new Error("OpenAI document curation requires an API key");
|
|
238
418
|
}
|
|
239
|
-
this.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
419
|
+
this.clientPromise ??= import("openai").then(
|
|
420
|
+
({ default: OpenAIClient }) => new OpenAIClient({
|
|
421
|
+
apiKey: this.apiKey,
|
|
422
|
+
...this.baseURL ? { baseURL: this.baseURL } : {},
|
|
423
|
+
...this.defaultQuery ? { defaultQuery: this.defaultQuery } : {},
|
|
424
|
+
...this.defaultHeaders ? { defaultHeaders: this.defaultHeaders } : {}
|
|
425
|
+
})
|
|
426
|
+
);
|
|
427
|
+
return await this.clientPromise;
|
|
246
428
|
}
|
|
247
429
|
};
|
|
248
430
|
function parseCurationOutcome(raw, bases) {
|
|
@@ -534,15 +716,24 @@ async function addDocumentToBase(db, input) {
|
|
|
534
716
|
const base = await getDocumentBase(scopedDb, input.workspaceId, input.baseId);
|
|
535
717
|
if (!base) throw new Error(`Document base not found: ${input.baseId}`);
|
|
536
718
|
const file = await requireReadyFile(scopedDb, input.workspaceId, input.fileId);
|
|
719
|
+
const knowledgeSourceIdentity = cleanString(input.knowledgeSourceIdentity ?? null);
|
|
720
|
+
if (knowledgeSourceIdentity && knowledgeSourceIdentity.length > 512) {
|
|
721
|
+
throw new Error("knowledge source document identity exceeds 512 characters");
|
|
722
|
+
}
|
|
537
723
|
const now = /* @__PURE__ */ new Date();
|
|
538
724
|
const [existing] = await scopedDb.select().from(schema.documents).where(
|
|
539
725
|
and(
|
|
540
726
|
eq(schema.documents.workspaceId, input.workspaceId),
|
|
541
|
-
eq(schema.documents.
|
|
542
|
-
|
|
727
|
+
...knowledgeSourceIdentity ? [eq(schema.documents.knowledgeSourceIdentity, knowledgeSourceIdentity)] : [
|
|
728
|
+
eq(schema.documents.baseId, input.baseId),
|
|
729
|
+
eq(schema.documents.fileId, input.fileId)
|
|
730
|
+
]
|
|
543
731
|
)
|
|
544
732
|
).limit(1);
|
|
545
733
|
if (existing) {
|
|
734
|
+
if (knowledgeSourceIdentity && existing.fileId !== input.fileId) {
|
|
735
|
+
throw new Error("knowledge source document identity is bound to different content");
|
|
736
|
+
}
|
|
546
737
|
if (!documentMatchesAccess(existing, input.workspaceId, input.access)) {
|
|
547
738
|
throw new Error(`Document not found: ${existing.id}`);
|
|
548
739
|
}
|
|
@@ -587,6 +778,7 @@ async function addDocumentToBase(db, input) {
|
|
|
587
778
|
sourceCreatedAt: parseOptionalDate(input.sourceCreatedAt),
|
|
588
779
|
sourceUpdatedAt: parseOptionalDate(input.sourceUpdatedAt),
|
|
589
780
|
sourceVersion: cleanString(input.sourceVersion) ?? null,
|
|
781
|
+
knowledgeSourceIdentity,
|
|
590
782
|
aclTags: cleanStringArray(input.aclTags),
|
|
591
783
|
authorityKind: authority.kind,
|
|
592
784
|
authorityWorkspaceId: authority.workspaceId,
|
|
@@ -630,7 +822,7 @@ async function moveDocumentToBase(db, input) {
|
|
|
630
822
|
and(
|
|
631
823
|
eq(schema.documents.workspaceId, input.workspaceId),
|
|
632
824
|
eq(schema.documents.baseId, targetBaseId),
|
|
633
|
-
eq(schema.documents.fileId, row.fileId)
|
|
825
|
+
...row.knowledgeSourceIdentity ? [eq(schema.documents.knowledgeSourceIdentity, row.knowledgeSourceIdentity)] : [eq(schema.documents.fileId, row.fileId)]
|
|
634
826
|
)
|
|
635
827
|
).limit(1);
|
|
636
828
|
if (conflict) {
|
|
@@ -710,6 +902,101 @@ async function listDocuments(db, workspaceId, baseId, access) {
|
|
|
710
902
|
return rows.map(mapDocument);
|
|
711
903
|
});
|
|
712
904
|
}
|
|
905
|
+
async function listEffectiveIndexedDocuments(db, input) {
|
|
906
|
+
const initiatingSubjectId = canonicalEffectiveDocumentSubject(input.initiatingSubjectId);
|
|
907
|
+
const limit = input.limit ?? 50;
|
|
908
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
909
|
+
throw new Error("indexed document list limit must be between 1 and 100");
|
|
910
|
+
}
|
|
911
|
+
const afterSequence = input.checkpoint ? decodeDocumentIndexCheckpoint(input.checkpoint, {
|
|
912
|
+
accountId: input.accountId,
|
|
913
|
+
workspaceId: input.workspaceId,
|
|
914
|
+
initiatingSubjectId
|
|
915
|
+
}) : 0n;
|
|
916
|
+
const access = {
|
|
917
|
+
agentOnly: true,
|
|
918
|
+
viewerSubjectId: initiatingSubjectId
|
|
919
|
+
};
|
|
920
|
+
const rows = await withDocumentAccountRls(
|
|
921
|
+
db,
|
|
922
|
+
input.accountId,
|
|
923
|
+
input.workspaceId,
|
|
924
|
+
access,
|
|
925
|
+
async (scopedDb) => await scopedDb.select().from(schema.documents).where(
|
|
926
|
+
and(
|
|
927
|
+
eq(schema.documents.accountId, input.accountId),
|
|
928
|
+
eq(schema.documents.status, "ready"),
|
|
929
|
+
isNotNull(schema.documents.indexSequence),
|
|
930
|
+
gt(schema.documents.indexSequence, afterSequence),
|
|
931
|
+
...documentAccessConditions(input.workspaceId, access)
|
|
932
|
+
)
|
|
933
|
+
).orderBy(asc(schema.documents.indexSequence)).limit(limit + 1)
|
|
934
|
+
);
|
|
935
|
+
const hasMore = rows.length > limit;
|
|
936
|
+
const pageRows = rows.slice(0, limit);
|
|
937
|
+
const nextSequence = pageRows.at(-1)?.indexSequence ?? afterSequence;
|
|
938
|
+
if (nextSequence === null) {
|
|
939
|
+
throw new Error("ready document is missing its index sequence");
|
|
940
|
+
}
|
|
941
|
+
return {
|
|
942
|
+
documents: pageRows.map(mapIndexedDocumentSummary),
|
|
943
|
+
nextCheckpoint: encodeDocumentIndexCheckpoint({
|
|
944
|
+
accountId: input.accountId,
|
|
945
|
+
workspaceId: input.workspaceId,
|
|
946
|
+
initiatingSubjectId,
|
|
947
|
+
sequence: nextSequence
|
|
948
|
+
}),
|
|
949
|
+
hasMore
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
function encodeDocumentIndexCheckpoint(input) {
|
|
953
|
+
if (input.sequence < 0n) throw new Error("document index checkpoint sequence is invalid");
|
|
954
|
+
return Buffer.from(
|
|
955
|
+
JSON.stringify({
|
|
956
|
+
v: 1,
|
|
957
|
+
s: documentIndexCheckpointScope(input),
|
|
958
|
+
q: input.sequence.toString()
|
|
959
|
+
}),
|
|
960
|
+
"utf8"
|
|
961
|
+
).toString("base64url");
|
|
962
|
+
}
|
|
963
|
+
function decodeDocumentIndexCheckpoint(value, scope) {
|
|
964
|
+
try {
|
|
965
|
+
if (!value || value.length > DOCUMENT_INDEX_CHECKPOINT_MAX_CHARS) {
|
|
966
|
+
throw new Error("checkpoint length");
|
|
967
|
+
}
|
|
968
|
+
const bytes = Buffer.from(value, "base64url");
|
|
969
|
+
if (bytes.toString("base64url") !== value) throw new Error("checkpoint encoding");
|
|
970
|
+
const parsed = JSON.parse(bytes.toString("utf8"));
|
|
971
|
+
if (Object.keys(parsed).sort().join(",") !== "q,s,v" || parsed.v !== 1 || typeof parsed.s !== "string" || typeof parsed.q !== "string" || !/^(0|[1-9][0-9]*)$/.test(parsed.q)) {
|
|
972
|
+
throw new Error("checkpoint payload");
|
|
973
|
+
}
|
|
974
|
+
if (parsed.s !== documentIndexCheckpointScope(scope)) {
|
|
975
|
+
throw new Error("document index checkpoint belongs to a different workspace or subject");
|
|
976
|
+
}
|
|
977
|
+
return BigInt(parsed.q);
|
|
978
|
+
} catch (error) {
|
|
979
|
+
if (error instanceof Error && error.message === "document index checkpoint belongs to a different workspace or subject") {
|
|
980
|
+
throw error;
|
|
981
|
+
}
|
|
982
|
+
throw new Error("invalid document index checkpoint", { cause: error });
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function documentIndexCheckpointScope(input) {
|
|
986
|
+
return createHash("sha256").update("opengeni:document-index-checkpoint:v1\0").update(input.accountId).update("\0").update(input.workspaceId).update("\0").update(canonicalEffectiveDocumentSubject(input.initiatingSubjectId)).digest("hex");
|
|
987
|
+
}
|
|
988
|
+
function canonicalEffectiveDocumentSubject(value) {
|
|
989
|
+
const subjectId = cleanString(value);
|
|
990
|
+
if (!subjectId || subjectId !== value) {
|
|
991
|
+
throw new Error("effective document retrieval requires an initiating subject");
|
|
992
|
+
}
|
|
993
|
+
if (new TextEncoder().encode(subjectId).byteLength > DOCUMENT_AUTHORITY_SUBJECT_MAX_BYTES) {
|
|
994
|
+
throw new Error(
|
|
995
|
+
`effective document initiating subject exceeds ${DOCUMENT_AUTHORITY_SUBJECT_MAX_BYTES} UTF-8 bytes`
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
return subjectId;
|
|
999
|
+
}
|
|
713
1000
|
async function getDocument(db, workspaceId, documentId, access) {
|
|
714
1001
|
return await withDocumentRls(db, workspaceId, access, async (scopedDb) => {
|
|
715
1002
|
const [row] = await scopedDb.select().from(schema.documents).where(
|
|
@@ -1024,6 +1311,293 @@ async function searchEffectiveDocuments(db, input, services = createDocumentServ
|
|
|
1024
1311
|
services
|
|
1025
1312
|
);
|
|
1026
1313
|
}
|
|
1314
|
+
async function searchEffectiveKnowledge(db, input, services = createDocumentServices()) {
|
|
1315
|
+
const initiatingSubjectId = canonicalEffectiveDocumentSubject(input.initiatingSubjectId);
|
|
1316
|
+
const ranked = await searchEffectiveDocuments(
|
|
1317
|
+
db,
|
|
1318
|
+
{ ...input, initiatingSubjectId, surface: "agent" },
|
|
1319
|
+
services
|
|
1320
|
+
);
|
|
1321
|
+
if (ranked.length === 0) return { results: [] };
|
|
1322
|
+
const access = { agentOnly: true, viewerSubjectId: initiatingSubjectId };
|
|
1323
|
+
const current = await withDocumentAccountRls(
|
|
1324
|
+
db,
|
|
1325
|
+
input.accountId,
|
|
1326
|
+
input.workspaceId,
|
|
1327
|
+
access,
|
|
1328
|
+
async (scopedDb) => await scopedDb.select({ chunk: schema.documentChunks, document: schema.documents }).from(schema.documentChunks).innerJoin(schema.documents, eq(schema.documentChunks.documentId, schema.documents.id)).where(
|
|
1329
|
+
and(
|
|
1330
|
+
eq(schema.documents.accountId, input.accountId),
|
|
1331
|
+
eq(schema.documentChunks.accountId, input.accountId),
|
|
1332
|
+
inArray(
|
|
1333
|
+
schema.documentChunks.id,
|
|
1334
|
+
ranked.map((result) => result.chunkId)
|
|
1335
|
+
),
|
|
1336
|
+
eq(schema.documents.status, "ready"),
|
|
1337
|
+
...documentAccessConditions(input.workspaceId, access)
|
|
1338
|
+
)
|
|
1339
|
+
)
|
|
1340
|
+
);
|
|
1341
|
+
const currentByChunkId = new Map(current.map((row) => [row.chunk.id, row]));
|
|
1342
|
+
return {
|
|
1343
|
+
results: ranked.flatMap((rankedResult) => {
|
|
1344
|
+
const row = currentByChunkId.get(rankedResult.chunkId);
|
|
1345
|
+
if (!row) return [];
|
|
1346
|
+
return [
|
|
1347
|
+
{
|
|
1348
|
+
record: knowledgeChunkRecord(row.document, row.chunk),
|
|
1349
|
+
retrieval: {
|
|
1350
|
+
score: rankedResult.score,
|
|
1351
|
+
matchType: rankedResult.matchType,
|
|
1352
|
+
vectorScore: rankedResult.vectorScore,
|
|
1353
|
+
keywordScore: rankedResult.keywordScore
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
];
|
|
1357
|
+
})
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
async function getEffectiveKnowledgeRecord(db, input) {
|
|
1361
|
+
const initiatingSubjectId = canonicalEffectiveDocumentSubject(input.initiatingSubjectId);
|
|
1362
|
+
const target = parseKnowledgeRecordId(input.id);
|
|
1363
|
+
const access = { agentOnly: true, viewerSubjectId: initiatingSubjectId };
|
|
1364
|
+
return await withDocumentAccountRls(
|
|
1365
|
+
db,
|
|
1366
|
+
input.accountId,
|
|
1367
|
+
input.workspaceId,
|
|
1368
|
+
access,
|
|
1369
|
+
async (scopedDb) => {
|
|
1370
|
+
if (target.kind === "document") {
|
|
1371
|
+
const [document] = await scopedDb.select().from(schema.documents).where(
|
|
1372
|
+
and(
|
|
1373
|
+
eq(schema.documents.accountId, input.accountId),
|
|
1374
|
+
eq(schema.documents.id, target.id),
|
|
1375
|
+
eq(schema.documents.status, "ready"),
|
|
1376
|
+
...documentAccessConditions(input.workspaceId, access)
|
|
1377
|
+
)
|
|
1378
|
+
).limit(1);
|
|
1379
|
+
return document ? knowledgeDocumentRecord(document) : null;
|
|
1380
|
+
}
|
|
1381
|
+
const [row] = await scopedDb.select({ chunk: schema.documentChunks, document: schema.documents }).from(schema.documentChunks).innerJoin(schema.documents, eq(schema.documentChunks.documentId, schema.documents.id)).where(
|
|
1382
|
+
and(
|
|
1383
|
+
eq(schema.documents.accountId, input.accountId),
|
|
1384
|
+
eq(schema.documentChunks.accountId, input.accountId),
|
|
1385
|
+
eq(schema.documentChunks.id, target.id),
|
|
1386
|
+
eq(schema.documents.status, "ready"),
|
|
1387
|
+
...documentAccessConditions(input.workspaceId, access)
|
|
1388
|
+
)
|
|
1389
|
+
).limit(1);
|
|
1390
|
+
return row ? knowledgeChunkRecord(row.document, row.chunk) : null;
|
|
1391
|
+
}
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
async function browseEffectiveKnowledge(db, input) {
|
|
1395
|
+
const initiatingSubjectId = canonicalEffectiveDocumentSubject(input.initiatingSubjectId);
|
|
1396
|
+
const limit = input.limit ?? 20;
|
|
1397
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 50) {
|
|
1398
|
+
throw new Error("knowledge browse limit must be between 1 and 50");
|
|
1399
|
+
}
|
|
1400
|
+
const topic = input.topic === void 0 ? null : cleanString(input.topic) ?? null;
|
|
1401
|
+
if (input.topic !== void 0 && (!topic || topic !== input.topic || topic.length > 256)) {
|
|
1402
|
+
throw new Error("knowledge browse topic is invalid");
|
|
1403
|
+
}
|
|
1404
|
+
const sourceKinds = [...new Set(input.sourceKinds ?? [])].sort();
|
|
1405
|
+
const parent = input.parentId ? parseKnowledgeRecordId(input.parentId) : null;
|
|
1406
|
+
if (parent?.kind === "document_chunk") {
|
|
1407
|
+
throw new Error("knowledge browse parent must be a document record");
|
|
1408
|
+
}
|
|
1409
|
+
if (parent && (topic || sourceKinds.length > 0)) {
|
|
1410
|
+
throw new Error("knowledge browse document contents do not accept topic/source filters");
|
|
1411
|
+
}
|
|
1412
|
+
const cursorScope = {
|
|
1413
|
+
accountId: input.accountId,
|
|
1414
|
+
workspaceId: input.workspaceId,
|
|
1415
|
+
initiatingSubjectId,
|
|
1416
|
+
parentId: parent ? `${parent.kind}:${parent.id}` : null,
|
|
1417
|
+
topic,
|
|
1418
|
+
sourceKinds
|
|
1419
|
+
};
|
|
1420
|
+
const after = input.cursor ? decodeKnowledgeBrowseCursor(input.cursor, cursorScope) : 0n;
|
|
1421
|
+
if (parent && after > 2147483648n) {
|
|
1422
|
+
throw new Error("invalid knowledge browse cursor");
|
|
1423
|
+
}
|
|
1424
|
+
const access = { agentOnly: true, viewerSubjectId: initiatingSubjectId };
|
|
1425
|
+
return await withDocumentAccountRls(
|
|
1426
|
+
db,
|
|
1427
|
+
input.accountId,
|
|
1428
|
+
input.workspaceId,
|
|
1429
|
+
access,
|
|
1430
|
+
async (scopedDb) => {
|
|
1431
|
+
if (parent) {
|
|
1432
|
+
const [authorizedParent] = await scopedDb.select({ id: schema.documents.id }).from(schema.documents).where(
|
|
1433
|
+
and(
|
|
1434
|
+
eq(schema.documents.accountId, input.accountId),
|
|
1435
|
+
eq(schema.documents.id, parent.id),
|
|
1436
|
+
eq(schema.documents.status, "ready"),
|
|
1437
|
+
...documentAccessConditions(input.workspaceId, access)
|
|
1438
|
+
)
|
|
1439
|
+
).limit(1);
|
|
1440
|
+
if (!authorizedParent) return { records: [], nextCursor: null, hasMore: false };
|
|
1441
|
+
const rows2 = await scopedDb.select({ chunk: schema.documentChunks, document: schema.documents }).from(schema.documentChunks).innerJoin(schema.documents, eq(schema.documentChunks.documentId, schema.documents.id)).where(
|
|
1442
|
+
and(
|
|
1443
|
+
eq(schema.documentChunks.accountId, input.accountId),
|
|
1444
|
+
eq(schema.documentChunks.documentId, parent.id),
|
|
1445
|
+
gt(schema.documentChunks.chunkIndex, Number(after) - 1),
|
|
1446
|
+
eq(schema.documents.status, "ready"),
|
|
1447
|
+
...documentAccessConditions(input.workspaceId, access)
|
|
1448
|
+
)
|
|
1449
|
+
).orderBy(asc(schema.documentChunks.chunkIndex)).limit(limit + 1);
|
|
1450
|
+
const hasMore2 = rows2.length > limit;
|
|
1451
|
+
const page2 = rows2.slice(0, limit);
|
|
1452
|
+
const last2 = page2.at(-1)?.chunk.chunkIndex;
|
|
1453
|
+
return {
|
|
1454
|
+
records: page2.map((row) => knowledgeChunkRecord(row.document, row.chunk)),
|
|
1455
|
+
nextCursor: hasMore2 && last2 !== void 0 ? encodeKnowledgeBrowseCursor(cursorScope, BigInt(last2 + 1)) : null,
|
|
1456
|
+
hasMore: hasMore2
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
const conditions = [
|
|
1460
|
+
eq(schema.documents.accountId, input.accountId),
|
|
1461
|
+
eq(schema.documents.status, "ready"),
|
|
1462
|
+
isNotNull(schema.documents.indexSequence),
|
|
1463
|
+
gt(schema.documents.indexSequence, after),
|
|
1464
|
+
...documentAccessConditions(input.workspaceId, access)
|
|
1465
|
+
];
|
|
1466
|
+
if (topic) conditions.push(sql`${schema.documents.topics} ? ${topic}`);
|
|
1467
|
+
if (sourceKinds.length > 0)
|
|
1468
|
+
conditions.push(inArray(schema.documents.sourceKind, sourceKinds));
|
|
1469
|
+
const rows = await scopedDb.select().from(schema.documents).where(and(...conditions)).orderBy(asc(schema.documents.indexSequence)).limit(limit + 1);
|
|
1470
|
+
const hasMore = rows.length > limit;
|
|
1471
|
+
const page = rows.slice(0, limit);
|
|
1472
|
+
const last = page.at(-1)?.indexSequence;
|
|
1473
|
+
return {
|
|
1474
|
+
records: page.map(knowledgeDocumentRecord),
|
|
1475
|
+
nextCursor: hasMore && last !== void 0 && last !== null ? encodeKnowledgeBrowseCursor(cursorScope, last) : null,
|
|
1476
|
+
hasMore
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
function encodeKnowledgeBrowseCursor(scope, position) {
|
|
1482
|
+
if (position < 0n) throw new Error("knowledge browse cursor position is invalid");
|
|
1483
|
+
return Buffer.from(
|
|
1484
|
+
JSON.stringify({ v: 1, s: knowledgeBrowseCursorScope(scope), q: position.toString() }),
|
|
1485
|
+
"utf8"
|
|
1486
|
+
).toString("base64url");
|
|
1487
|
+
}
|
|
1488
|
+
function decodeKnowledgeBrowseCursor(value, scope) {
|
|
1489
|
+
try {
|
|
1490
|
+
if (!value || value.length > KNOWLEDGE_BROWSE_CURSOR_MAX_CHARS) {
|
|
1491
|
+
throw new Error("cursor length");
|
|
1492
|
+
}
|
|
1493
|
+
const bytes = Buffer.from(value, "base64url");
|
|
1494
|
+
if (bytes.toString("base64url") !== value) throw new Error("cursor encoding");
|
|
1495
|
+
const parsed = JSON.parse(bytes.toString("utf8"));
|
|
1496
|
+
if (Object.keys(parsed).sort().join(",") !== "q,s,v" || parsed.v !== 1 || typeof parsed.s !== "string" || typeof parsed.q !== "string" || !/^(0|[1-9][0-9]*)$/.test(parsed.q)) {
|
|
1497
|
+
throw new Error("cursor payload");
|
|
1498
|
+
}
|
|
1499
|
+
if (parsed.s !== knowledgeBrowseCursorScope(scope)) {
|
|
1500
|
+
throw new Error("knowledge browse cursor belongs to a different scope");
|
|
1501
|
+
}
|
|
1502
|
+
const position = BigInt(parsed.q);
|
|
1503
|
+
if (position > 9223372036854775807n) throw new Error("cursor position");
|
|
1504
|
+
return position;
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
if (error instanceof Error && error.message === "knowledge browse cursor belongs to a different scope") {
|
|
1507
|
+
throw error;
|
|
1508
|
+
}
|
|
1509
|
+
throw new Error("invalid knowledge browse cursor", { cause: error });
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
function knowledgeBrowseCursorScope(scope) {
|
|
1513
|
+
return createHash("sha256").update("opengeni:knowledge-browse-cursor:v1\0").update(scope.accountId).update("\0").update(scope.workspaceId).update("\0").update(canonicalEffectiveDocumentSubject(scope.initiatingSubjectId)).update("\0").update(scope.parentId ?? "").update("\0").update(scope.topic ?? "").update("\0").update([...scope.sourceKinds].sort().join("\0")).digest("hex");
|
|
1514
|
+
}
|
|
1515
|
+
function parseKnowledgeRecordId(value) {
|
|
1516
|
+
const match = /^(document|document_chunk):([0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/iu.exec(
|
|
1517
|
+
value
|
|
1518
|
+
);
|
|
1519
|
+
if (!match) throw new Error("invalid knowledge record id");
|
|
1520
|
+
return { kind: match[1], id: match[2].toLowerCase() };
|
|
1521
|
+
}
|
|
1522
|
+
function knowledgeDocumentRecord(document) {
|
|
1523
|
+
if (!document.indexedAt) throw new Error(`Ready document is missing indexed_at: ${document.id}`);
|
|
1524
|
+
const projected = projectKnowledgeRecord({
|
|
1525
|
+
title: document.title,
|
|
1526
|
+
body: document.summary,
|
|
1527
|
+
summary: document.summary,
|
|
1528
|
+
topics: document.topics,
|
|
1529
|
+
metadata: { parser: document.parser, chunkCount: document.chunkCount },
|
|
1530
|
+
source: knowledgeSource(document)
|
|
1531
|
+
});
|
|
1532
|
+
return {
|
|
1533
|
+
id: `document:${document.id}`,
|
|
1534
|
+
kind: "document",
|
|
1535
|
+
title: projected.title,
|
|
1536
|
+
content: projected.content,
|
|
1537
|
+
authority: { kind: normalizeDocumentAuthorityKind(document.authorityKind) },
|
|
1538
|
+
provenance: {
|
|
1539
|
+
source: projected.source,
|
|
1540
|
+
indexedAt: document.indexedAt.toISOString()
|
|
1541
|
+
},
|
|
1542
|
+
lifecycle: { state: "active", updatedAt: document.updatedAt.toISOString() },
|
|
1543
|
+
quality: knowledgeQuality(document),
|
|
1544
|
+
links: knowledgeSourceLinks(projected.source.uri),
|
|
1545
|
+
projection: projected.projection
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function knowledgeChunkRecord(document, chunk) {
|
|
1549
|
+
if (!document.indexedAt) throw new Error(`Ready document is missing indexed_at: ${document.id}`);
|
|
1550
|
+
const projected = projectKnowledgeRecord({
|
|
1551
|
+
title: document.title,
|
|
1552
|
+
body: chunk.text,
|
|
1553
|
+
summary: document.summary,
|
|
1554
|
+
topics: document.topics,
|
|
1555
|
+
metadata: { ...chunk.metadata, chunkIndex: chunk.chunkIndex },
|
|
1556
|
+
source: knowledgeSource(document)
|
|
1557
|
+
});
|
|
1558
|
+
return {
|
|
1559
|
+
id: `document_chunk:${chunk.id}`,
|
|
1560
|
+
kind: "document_chunk",
|
|
1561
|
+
title: projected.title,
|
|
1562
|
+
content: projected.content,
|
|
1563
|
+
authority: { kind: normalizeDocumentAuthorityKind(document.authorityKind) },
|
|
1564
|
+
provenance: {
|
|
1565
|
+
source: projected.source,
|
|
1566
|
+
indexedAt: document.indexedAt.toISOString()
|
|
1567
|
+
},
|
|
1568
|
+
lifecycle: { state: "active", updatedAt: document.updatedAt.toISOString() },
|
|
1569
|
+
quality: knowledgeQuality(document),
|
|
1570
|
+
links: [
|
|
1571
|
+
{ relation: "parent", target: { kind: "knowledge", id: `document:${document.id}` } },
|
|
1572
|
+
...knowledgeSourceLinks(projected.source.uri)
|
|
1573
|
+
],
|
|
1574
|
+
projection: projected.projection
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
function knowledgeSource(document) {
|
|
1578
|
+
return {
|
|
1579
|
+
kind: normalizeKnowledgeSourceKind(document.sourceKind),
|
|
1580
|
+
uri: document.sourceUri,
|
|
1581
|
+
externalId: document.sourceExternalId,
|
|
1582
|
+
title: document.sourceTitle,
|
|
1583
|
+
author: document.sourceAuthor,
|
|
1584
|
+
createdAt: document.sourceCreatedAt?.toISOString() ?? null,
|
|
1585
|
+
updatedAt: document.sourceUpdatedAt?.toISOString() ?? null,
|
|
1586
|
+
version: document.sourceVersion
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
function knowledgeQuality(document) {
|
|
1590
|
+
if (!document.indexedAt) throw new Error(`Ready document is missing indexed_at: ${document.id}`);
|
|
1591
|
+
return {
|
|
1592
|
+
trust: "sourced",
|
|
1593
|
+
freshnessAt: (document.sourceUpdatedAt ?? document.indexedAt).toISOString(),
|
|
1594
|
+
conflict: "not_evaluated",
|
|
1595
|
+
correction: "current_source_version"
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
function knowledgeSourceLinks(sourceUri) {
|
|
1599
|
+
return sourceUri ? [{ relation: "source", target: { kind: "external", uri: sourceUri } }] : [];
|
|
1600
|
+
}
|
|
1027
1601
|
async function vectorSearchDocuments(db, input, limit, services) {
|
|
1028
1602
|
const queryEmbedding = await services.embedder.embedQuery(input.query);
|
|
1029
1603
|
validateEmbedding(queryEmbedding, services.embedder.dimensions, services.embedder.model);
|
|
@@ -1508,6 +2082,40 @@ function mapDocument(row) {
|
|
|
1508
2082
|
updatedAt: row.updatedAt.toISOString()
|
|
1509
2083
|
};
|
|
1510
2084
|
}
|
|
2085
|
+
function mapIndexedDocumentSummary(row) {
|
|
2086
|
+
if (row.indexSequence === null || row.indexedAt === null) {
|
|
2087
|
+
throw new Error(`Ready document is missing index completion metadata: ${row.id}`);
|
|
2088
|
+
}
|
|
2089
|
+
return {
|
|
2090
|
+
id: row.id,
|
|
2091
|
+
title: row.title,
|
|
2092
|
+
parser: row.parser,
|
|
2093
|
+
chunkCount: row.chunkCount,
|
|
2094
|
+
indexedAt: row.indexedAt.toISOString(),
|
|
2095
|
+
summary: row.summary,
|
|
2096
|
+
topics: cleanStringArray(row.topics),
|
|
2097
|
+
source: {
|
|
2098
|
+
kind: normalizeKnowledgeSourceKind(row.sourceKind),
|
|
2099
|
+
uri: row.sourceUri,
|
|
2100
|
+
externalId: row.sourceExternalId,
|
|
2101
|
+
title: row.sourceTitle,
|
|
2102
|
+
author: row.sourceAuthor,
|
|
2103
|
+
createdAt: row.sourceCreatedAt?.toISOString() ?? null,
|
|
2104
|
+
updatedAt: row.sourceUpdatedAt?.toISOString() ?? null,
|
|
2105
|
+
version: row.sourceVersion
|
|
2106
|
+
},
|
|
2107
|
+
provenance: {
|
|
2108
|
+
ingestionWorkspaceId: row.workspaceId,
|
|
2109
|
+
baseId: row.baseId,
|
|
2110
|
+
fileId: row.fileId,
|
|
2111
|
+
authorityKind: normalizeDocumentAuthorityKind(row.authorityKind),
|
|
2112
|
+
authorityWorkspaceId: row.authorityWorkspaceId,
|
|
2113
|
+
authoritySubjectId: row.authoritySubjectId,
|
|
2114
|
+
createdBy: row.createdBy,
|
|
2115
|
+
createdAt: row.createdAt.toISOString()
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
1511
2119
|
function normalizeDocumentAuthorityKind(value) {
|
|
1512
2120
|
switch (value) {
|
|
1513
2121
|
case "organization":
|
|
@@ -1543,6 +2151,7 @@ export {
|
|
|
1543
2151
|
DOCUMENT_AUTHORITY_SUBJECT_MAX_BYTES,
|
|
1544
2152
|
DOCUMENT_CURATION_AUTO_FILE_CONFIDENCE,
|
|
1545
2153
|
DOCUMENT_CURATION_MAX_INPUT_CHARS,
|
|
2154
|
+
DOCUMENT_INDEX_CHECKPOINT_MAX_CHARS,
|
|
1546
2155
|
DeterministicEmbeddingProvider,
|
|
1547
2156
|
HeuristicCurationProvider,
|
|
1548
2157
|
LiteParseDocumentParser,
|
|
@@ -1550,29 +2159,38 @@ export {
|
|
|
1550
2159
|
OpenAIEmbeddingProvider,
|
|
1551
2160
|
RecursiveTextChunker,
|
|
1552
2161
|
addDocumentToBase,
|
|
2162
|
+
browseEffectiveKnowledge,
|
|
1553
2163
|
canViewDocument,
|
|
1554
2164
|
chunkText,
|
|
1555
2165
|
createDocumentBase,
|
|
1556
2166
|
createDocumentServices,
|
|
2167
|
+
decodeDocumentIndexCheckpoint,
|
|
2168
|
+
decodeKnowledgeBrowseCursor,
|
|
1557
2169
|
deleteDocumentFromBase,
|
|
1558
2170
|
deterministicEmbedding,
|
|
1559
2171
|
documentOpenAIEmbeddingConfig,
|
|
2172
|
+
encodeDocumentIndexCheckpoint,
|
|
2173
|
+
encodeKnowledgeBrowseCursor,
|
|
1560
2174
|
ensureDefaultBase,
|
|
1561
2175
|
getDocument,
|
|
1562
2176
|
getDocumentBase,
|
|
1563
2177
|
getDocumentChunk,
|
|
1564
2178
|
getDocumentInventory,
|
|
2179
|
+
getEffectiveKnowledgeRecord,
|
|
1565
2180
|
heuristicCuration,
|
|
1566
2181
|
indexDocumentNow,
|
|
1567
2182
|
listDocumentBases,
|
|
1568
2183
|
listDocumentBasesEnsuringDefault,
|
|
1569
2184
|
listDocuments,
|
|
2185
|
+
listEffectiveIndexedDocuments,
|
|
1570
2186
|
moveDocumentToBase,
|
|
1571
2187
|
parseCurationOutcome,
|
|
1572
2188
|
parseDocumentBytes,
|
|
2189
|
+
projectKnowledgeRecord,
|
|
1573
2190
|
queueDocumentForReindex,
|
|
1574
2191
|
resolveDocumentAuthority,
|
|
1575
2192
|
searchDocuments,
|
|
1576
|
-
searchEffectiveDocuments
|
|
2193
|
+
searchEffectiveDocuments,
|
|
2194
|
+
searchEffectiveKnowledge
|
|
1577
2195
|
};
|
|
1578
2196
|
//# sourceMappingURL=index.js.map
|