@dsh-libing/knowledge-base 0.1.0 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +12 -2
  3. package/lib/agent-access.d.ts +23 -0
  4. package/lib/agent-access.js +28 -0
  5. package/lib/chunks.js +27 -5
  6. package/lib/client-types/client/JobDetails.d.ts +16 -0
  7. package/lib/client-types/client/JobsPanel.d.ts +6 -1
  8. package/lib/client-types/client/QualityReport.d.ts +3 -2
  9. package/lib/client-types/client/api.d.ts +83 -52
  10. package/lib/client-types/client/locales.d.ts +2 -0
  11. package/lib/client-types/client/useKnowledge.d.ts +30 -15
  12. package/lib/client-types/contract.d.ts +83 -52
  13. package/lib/client-types/data.d.ts +11 -0
  14. package/lib/client-types/settings.d.ts +4 -0
  15. package/lib/client-types/shared.d.ts +17 -0
  16. package/lib/client.js +40 -40
  17. package/lib/config.js +9 -1
  18. package/lib/contract.d.ts +33 -2
  19. package/lib/contract.js +2 -1
  20. package/lib/data.d.ts +11 -0
  21. package/lib/data.js +2 -0
  22. package/lib/document/convert.js +52 -16
  23. package/lib/document/pdf-layout.js +45 -10
  24. package/lib/document/pdf-session.js +10 -4
  25. package/lib/document/pdf-vision.js +8 -3
  26. package/lib/document/pdf-worker.js +76 -21
  27. package/lib/document/pdf.js +39 -9
  28. package/lib/document/zip.js +14 -6
  29. package/lib/figures/image-client.d.ts +5 -0
  30. package/lib/figures/image-client.js +63 -0
  31. package/lib/figures/image-gate.d.ts +12 -0
  32. package/lib/figures/image-gate.js +39 -0
  33. package/lib/figures/image-protocol.d.ts +19 -0
  34. package/lib/figures/image-protocol.js +9 -0
  35. package/lib/figures/image-worker.d.ts +1 -0
  36. package/lib/figures/image-worker.js +69 -0
  37. package/lib/figures/process.d.ts +1 -1
  38. package/lib/figures/process.js +19 -38
  39. package/lib/index.js +6 -2
  40. package/lib/indexes.js +17 -6
  41. package/lib/job-storage.d.ts +264 -0
  42. package/lib/job-storage.js +118 -0
  43. package/lib/jobs.d.ts +782 -1489
  44. package/lib/jobs.js +64 -31
  45. package/lib/models.d.ts +1 -1
  46. package/lib/models.js +15 -9
  47. package/lib/retrieval.js +44 -13
  48. package/lib/revision-cache.d.ts +24 -0
  49. package/lib/revision-cache.js +100 -0
  50. package/lib/routes.js +66 -20
  51. package/lib/runtime.d.ts +4 -0
  52. package/lib/runtime.js +15 -4
  53. package/lib/settings.d.ts +4 -0
  54. package/lib/settings.js +5 -2
  55. package/lib/shared.d.ts +17 -0
  56. package/lib/shared.js +3 -0
  57. package/lib/skills.js +6 -3
  58. package/lib/store.d.ts +101 -5
  59. package/lib/store.js +78 -29
  60. package/lib/tools.js +12 -4
  61. package/lib/util.js +2 -1
  62. package/package.json +1 -1
package/lib/routes.js CHANGED
@@ -1,15 +1,20 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { z } from 'zod';
4
- import { API, browserBuildSchema, jobActionSchema, catalogQuerySchema, readQuerySchema, searchSchema, assetQuerySchema, revisionQuerySchema } from "./shared.js";
4
+ import { API, jobListQuerySchema, jobDetailsQuerySchema, jobUsageQuerySchema, browserBuildSchema, jobActionSchema, catalogQuerySchema, readQuerySchema, searchSchema, assetQuerySchema, revisionQuerySchema } from "./shared.js";
5
5
  import { figureDataSchema, overlaySchema } from "./data.js";
6
6
  import { inspectPdf } from "./document/pdf-session.js";
7
7
  import { hash, safePath, message, required, fileBytes, checkAbort } from "./util.js";
8
8
  const HOST_VERSION = z.object({ version: z.string() }).parse(JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))).version;
9
- const id = z.string().regex(/^[a-z0-9][a-z0-9-]{0,95}$/u), name = z.string().trim().min(1).max(120), digest = z.string().regex(/^[a-f0-9]{64}$/u);
10
- const libraryArgs = z.strictObject({ kbId: id }), managementArgs = libraryArgs.extend({ confirmName: name });
9
+ const id = z.string().regex(/^[a-z0-9][a-z0-9-]{0,95}$/u);
10
+ const name = z.string().trim().min(1).max(120);
11
+ const digest = z.string().regex(/^[a-f0-9]{64}$/u);
12
+ const libraryArgs = z.strictObject({ kbId: id });
13
+ const managementArgs = libraryArgs.extend({ confirmName: name });
11
14
  const reviewSchema = z.strictObject({ kbId: id, revision: id, docId: id, occurrenceId: id, expectedHash: digest, action: z.enum(['reidentify', 'restore']).optional(), figure: z.union([figureDataSchema.strict(), z.strictObject({ ignore: z.literal(true) }), z.strictObject({ skip: z.literal(true) })]).optional(), visionProvider: z.string().optional(), visionModel: z.string().optional() }).refine(value => Boolean(value.action) !== Boolean(value.figure), '指定一个复核操作或内容');
12
- const draftQuery = z.strictObject({ jobId: id }), sourceQuery = revisionQuerySchema.extend({ docId: id }), draftAssetQuery = draftQuery.extend({ docId: id, assetId: id });
15
+ const draftQuery = z.strictObject({ jobId: id });
16
+ const sourceQuery = revisionQuerySchema.extend({ docId: id });
17
+ const draftAssetQuery = draftQuery.extend({ docId: id, assetId: id });
13
18
  const draftReadQuery = draftQuery.extend(readQuerySchema.omit({ kbId: true, revision: true }).shape);
14
19
  const headers = { 'cache-control': 'no-store', 'x-content-type-options': 'nosniff', 'referrer-policy': 'no-referrer' };
15
20
  function query(request) {
@@ -69,16 +74,19 @@ export function routes(runtime, ctx) {
69
74
  throw new Error('PDF 原件不存在于所选版本中');
70
75
  return binary((await store.rawSource(snapshot.kbId, doc)).data, 'application/pdf');
71
76
  };
72
- const image = async (asset, file) => {
77
+ const image = async (asset, file, verified) => {
73
78
  if (!['image/png', 'image/jpeg', 'image/webp', 'image/gif', 'image/bmp', 'image/tiff'].includes(asset.mime))
74
79
  throw new Error('不支持浏览此图片格式,请提供 PNG/JPEG 预览');
75
- const data = await fileBytes(file, config.maxUncompressedBytes);
76
- if (`a-${hash(data).slice(0, 20)}` !== asset.assetId)
80
+ const data = verified ?? await fileBytes(file, config.maxUncompressedBytes);
81
+ if (!verified && `a-${hash(data).slice(0, 20)}` !== asset.assetId)
77
82
  throw new Error('图片内容与清单不一致');
78
83
  return binary(data, asset.mime);
79
84
  };
80
85
  const tree = (snapshot, kbId) => ({ kbId, revision: snapshot.revision, coverageIncomplete: snapshot.manifest.coverageIncomplete, report: snapshot.manifest.report, documents: snapshot.manifest.documents.map(doc => ({ ...doc, chunks: doc.chunks.map(({ segments: _segments, anchors: _anchors, ...chunk }) => chunk) })) });
81
- add('list', 'GET', async (request) => { z.strictObject({}).parse(query(request)); return ({ hostVersion: HOST_VERSION, kbs: await Promise.all((await store.list()).map(async (kb) => ({ ...kb, ...await store.inspect(kb.kbId) }))), deletedKbs: await store.deleted(), activities: await store.activities(), jobs: jobs.list(), dataRoot: store.root, config: { chunkTargetChars: config.chunkTargetChars, visionProvider: config.visionProvider, visionModel: config.visionModel, maxInputBytes: config.maxInputBytes } }); });
86
+ add('list', 'GET', async (request) => { const args = jobListQuerySchema.parse(query(request)); if (args.jobsKbId)
87
+ await store.kb(args.jobsKbId); return ({ hostVersion: HOST_VERSION, ...await store.overview(), ...jobs.list(args.jobsKbId, args.jobOffset, args.jobLimit), dataRoot: store.root, config: { chunkTargetChars: config.chunkTargetChars, visionProvider: config.visionProvider, visionModel: config.visionModel, maxInputBytes: config.maxInputBytes } }); });
88
+ add('job-details', 'GET', async (request) => { const args = jobDetailsQuerySchema.parse(query(request)); return jobs.details(args.jobId, args.section === 'report'); });
89
+ add('job-usage', 'GET', async (request) => { const args = jobUsageQuerySchema.parse(query(request)); return jobs.usage(args.jobId, args.offset, args.limit, request.signal); });
82
90
  add('create', 'POST', async (request) => store.create((await body(request, z.strictObject({ name }))).name));
83
91
  add('delete', 'POST', async (request) => { const args = await body(request, managementArgs); return jobs.trash(args.kbId, args.confirmName); });
84
92
  add('restore', 'POST', async (request) => jobs.restore((await body(request, libraryArgs)).kbId));
@@ -90,32 +98,52 @@ export function routes(runtime, ctx) {
90
98
  return store.upload(await bytes(request, config.maxInputBytes), args.filename);
91
99
  });
92
100
  add('inspect-document', 'POST', async (request) => {
93
- const { receiptId } = await body(request, z.strictObject({ receiptId: id })), uploaded = await store.uploaded(receiptId);
101
+ const { receiptId } = await body(request, z.strictObject({ receiptId: id }));
102
+ const uploaded = await store.uploaded(receiptId);
94
103
  if (!/\.pdf$/iu.test(uploaded.filename))
95
104
  throw new Error('文档预检只支持 PDF');
96
105
  return inspectPdf(await safePath(store.root, join('uploads', receiptId + '.bin')), config, request.signal);
97
106
  });
98
- add('source', 'GET', async (request) => { const args = sourceQuery.parse(query(request)), kb = await store.kb(args.kbId); return original({ ...await store.revision(kb.kbId, args.revision), kbId: kb.kbId }, args.docId); });
107
+ add('source', 'GET', async (request) => { const args = sourceQuery.parse(query(request)); const kb = await store.kb(args.kbId); return original({ ...await store.revision(kb.kbId, args.revision), kbId: kb.kbId }, args.docId); });
99
108
  add('draft-source', 'GET', async (request) => { const args = draftQuery.extend({ docId: id }).parse(query(request)); return original(await jobs.preview(args.jobId), args.docId); });
100
109
  add('build', 'POST', async (request) => jobs.submit(await body(request, browserBuildSchema)));
101
110
  add('job', 'POST', async (request) => { const args = await body(request, jobActionSchema); return jobs.action(args.jobId, args.action, args); });
102
111
  add('draft', 'GET', async (request) => { const snapshot = await jobs.preview(draftQuery.parse(query(request)).jobId); return { ...tree(snapshot, snapshot.kbId), previewJobId: snapshot.jobId, coverageIncomplete: true }; });
103
112
  add('draft-read', 'GET', async (request) => {
104
- const { jobId, ...args } = draftReadQuery.parse(query(request)), snapshot = await jobs.preview(jobId), chunk = required(snapshot.manifest.documents.flatMap(d => d.chunks).find(c => c.chunkId === args.chunkId), '草稿片段');
113
+ const { jobId, ...args } = draftReadQuery.parse(query(request));
114
+ const snapshot = await jobs.preview(jobId);
115
+ const chunk = required(snapshot.manifest.documents.flatMap(d => d.chunks).find(c => c.chunkId === args.chunkId), '草稿片段');
105
116
  const value = retrieval.readSnapshot(snapshot.kbId, { ...snapshot, chunk, content: await store.chunkContent(snapshot, chunk) }, { ...args, kbId: snapshot.kbId });
106
117
  return { ...value, previewJobId: jobId, warnings: ['待发布预览:未向 Agent 检索开放', ...value.warnings] };
107
118
  });
108
- add('draft-asset', 'GET', async (request) => { const args = draftAssetQuery.parse(query(request)), snapshot = await jobs.preview(args.jobId), doc = required(snapshot.manifest.documents.find(d => d.docId === args.docId), '草稿文档'), asset = required(doc.assets.find(a => a.assetId === args.assetId), '草稿图片'); return image(asset, await safePath(snapshot.root, join('sources', doc.docId, asset.path))); });
119
+ add('draft-asset', 'GET', async (request) => { const args = draftAssetQuery.parse(query(request)); const snapshot = await jobs.preview(args.jobId); const doc = required(snapshot.manifest.documents.find(d => d.docId === args.docId), '草稿文档'); const asset = required(doc.assets.find(a => a.assetId === args.assetId), '草稿图片'); return image(asset, await safePath(snapshot.root, join('sources', doc.docId, asset.path))); });
109
120
  add('catalog', 'GET', async (request) => retrieval.catalog(catalogQuerySchema.parse(query(request))));
110
- add('tree', 'GET', async (request) => { const args = revisionQuerySchema.parse(query(request)), kb = await store.kb(args.kbId), snapshot = await store.revision(kb.kbId, args.revision), overlays = await store.overlays(kb.kbId), value = tree(snapshot, kb.kbId); return { ...value, location: await store.location(kb.kbId, snapshot.revision), documents: value.documents.map(doc => ({ ...doc, figures: doc.figures.map(figure => ({ ...figure, expectedHash: hash(overlays[figure.overlayKey] || null) })) })) }; });
121
+ add('tree', 'GET', async (request) => { const args = revisionQuerySchema.parse(query(request)); const { kb, snapshot } = await store.open(args.kbId, args.revision); const overlays = await store.overlays(kb.kbId); const value = tree(snapshot, kb.kbId); return { ...value, location: await store.snapshotLocation(kb.kbId, snapshot), documents: value.documents.map(doc => ({ ...doc, figures: doc.figures.map(figure => ({ ...figure, expectedHash: hash(overlays[figure.overlayKey] || null) })) })) }; });
111
122
  add('read', 'GET', async (request) => retrieval.read(readQuerySchema.parse(query(request))));
112
123
  add('search', 'POST', async (request) => retrieval.search(await body(request, searchSchema)));
113
- add('asset', 'GET', async (request) => { const args = assetQuerySchema.parse(query(request)), kb = await store.kb(args.kbId), asset = await store.asset(kb.kbId, args.revision, args.docId, args.assetId); return image(asset.asset, asset.file); });
114
- add('schema', 'GET', async (request) => { const kb = await store.kb(libraryArgs.parse(query(request)).kbId), content = await store.schema(kb.kbId); return { content, hash: hash(content) }; });
115
- add('schema', 'POST', async (request) => { const args = await body(request, libraryArgs.extend({ content: z.string().max(20000), expectedHash: digest })), kb = await store.kb(args.kbId); return store.setSchema(kb.kbId, args.content, args.expectedHash); });
124
+ add('asset', 'GET', async (request) => { const args = assetQuerySchema.parse(query(request)); const kb = await store.kb(args.kbId); const asset = await store.asset(kb.kbId, args.revision, args.docId, args.assetId); return image(asset.asset, asset.file, asset.data); });
125
+ add('schema', 'GET', async (request) => { const kb = await store.kb(libraryArgs.parse(query(request)).kbId); const content = await store.schema(kb.kbId); return { content, hash: hash(content) }; });
126
+ add('schema', 'POST', async (request) => { const args = await body(request, libraryArgs.extend({ content: z.string().max(20000), expectedHash: digest })); const kb = await store.kb(args.kbId); return store.setSchema(kb.kbId, args.content, args.expectedHash); });
116
127
  add('review', 'POST', async (request) => {
117
- const args = await body(request, reviewSchema), kb = await store.kb(args.kbId);
118
- const figure = args.action ? null : args.figure && 'ignore' in args.figure ? { ignore: true } : args.figure && 'skip' in args.figure ? { skip: true, types: ['decoration'], description: '人工确认装饰图', visibleLabels: [], relations: [], uncertainties: [], needsReview: false } : args.figure;
128
+ const args = await body(request, reviewSchema);
129
+ const kb = await store.kb(args.kbId);
130
+ let figure;
131
+ if (args.action) {
132
+ figure = null;
133
+ }
134
+ else {
135
+ if (args.figure && 'ignore' in args.figure) {
136
+ figure = { ignore: true };
137
+ }
138
+ else {
139
+ if (args.figure && 'skip' in args.figure) {
140
+ figure = { skip: true, types: ['decoration'], description: '人工确认装饰图', visibleLabels: [], relations: [], uncertainties: [], needsReview: false };
141
+ }
142
+ else {
143
+ figure = args.figure;
144
+ }
145
+ }
146
+ }
119
147
  if (figure && JSON.stringify(figure).length > config.figureMaxChars * 3)
120
148
  throw new Error('图片复核内容超过预算');
121
149
  return jobs.review(kb.kbId, args.revision, args.docId, args.occurrenceId, figure === null ? null : overlaySchema.parse(figure), args.expectedHash, { restore: args.action === 'restore', visionProvider: args.visionProvider, visionModel: args.visionModel });
@@ -139,11 +167,29 @@ export function routes(runtime, ctx) {
139
167
  try {
140
168
  await runtime.ready;
141
169
  checkAbort(request.signal);
142
- const handler = required(methods.get(z.enum(['GET', 'POST', 'HEAD']).parse(request.method)), '请求方法'), value = await runtime.run(signal => handler(new Request(request, { signal })), request.signal);
170
+ const handler = required(methods.get(z.enum(['GET', 'POST', 'HEAD']).parse(request.method)), '请求方法');
171
+ const value = await runtime.run(signal => handler(new Request(request, { signal })), request.signal);
143
172
  return value instanceof Response ? value : json(value);
144
173
  }
145
174
  catch (error) {
146
- const detail = message(error), status = error instanceof z.ZodError || error instanceof SyntaxError ? 400 : /变化|冲突|已存在|未退出|正在关闭/u.test(detail) ? 409 : /无效|必须|不支持|不能|只能|不在回收站|超过|过大|缺少|缺失|尚无|不存在|提供|请选择|待复核|重复/u.test(detail) ? 400 : 500;
175
+ const detail = message(error);
176
+ let status;
177
+ if (error instanceof z.ZodError || error instanceof SyntaxError) {
178
+ status = 400;
179
+ }
180
+ else {
181
+ if (/变化|冲突|已存在|未退出|正在关闭/u.test(detail)) {
182
+ status = 409;
183
+ }
184
+ else {
185
+ if (/无效|必须|不支持|不能|只能|不在回收站|超过|过大|缺少|缺失|尚无|不存在|提供|请选择|待复核|重复/u.test(detail)) {
186
+ status = 400;
187
+ }
188
+ else {
189
+ status = 500;
190
+ }
191
+ }
192
+ }
147
193
  if (status === 500)
148
194
  ctx.logger.warn(`knowledge-base: ${detail}`);
149
195
  return json({ error: detail }, status);
package/lib/runtime.d.ts CHANGED
@@ -18,8 +18,12 @@ export declare function createRuntime(ctx: Context, raw?: Config): {
18
18
  modelTimeoutMs: number;
19
19
  modelAttempts: number;
20
20
  visionConcurrency: number;
21
+ maxModelCallsLimit: number;
22
+ maxModelTokensLimit: number;
23
+ uploadRetentionDays: number;
21
24
  maxModelCalls: number;
22
25
  maxModelTokens: number;
26
+ imageTimeoutMs: number;
23
27
  visionMaxEdge: number;
24
28
  visionTileSize: number;
25
29
  visionMaxTiles: number;
package/lib/runtime.js CHANGED
@@ -7,10 +7,16 @@ import { Jobs } from "./jobs.js";
7
7
  import { Retrieval } from "./retrieval.js";
8
8
  /** One root owner; admitted tasks are durable and the close promise drains them once. */
9
9
  export function createRuntime(ctx, raw = {}) {
10
- const config = resolveConfig(raw), store = new Store(config), models = new Models(ctx, config), jobs = new Jobs(store, models, config), retrieval = new Retrieval(store, config);
10
+ const config = resolveConfig(raw);
11
+ const store = new Store(config);
12
+ const models = new Models(ctx, config);
13
+ const jobs = new Jobs(store, models, config);
14
+ const retrieval = new Retrieval(store, config);
11
15
  const ready = mkdir(store.root, { recursive: true }).then(() => jobs.init());
12
- let closing, stopped = false;
13
- const controller = new AbortController(), pending = new Set();
16
+ let closing;
17
+ let stopped = false;
18
+ const controller = new AbortController();
19
+ const pending = new Set();
14
20
  const run = (operation, signal) => {
15
21
  if (stopped)
16
22
  return Promise.reject(new Error('任务服务正在关闭'));
@@ -32,7 +38,12 @@ export function createRuntime(ctx, raw = {}) {
32
38
  }
33
39
  finally {
34
40
  await Promise.allSettled(pending);
35
- await jobs.close();
41
+ try {
42
+ await jobs.close();
43
+ }
44
+ finally {
45
+ store.revisionCache.clear();
46
+ }
36
47
  }
37
48
  })();
38
49
  return closing;
package/lib/settings.d.ts CHANGED
@@ -11,8 +11,12 @@ export declare const settingsSchema: z.ZodObject<{
11
11
  modelTimeoutMs: z.ZodDefault<z.ZodNumber>;
12
12
  modelAttempts: z.ZodDefault<z.ZodNumber>;
13
13
  visionConcurrency: z.ZodDefault<z.ZodNumber>;
14
+ maxModelCallsLimit: z.ZodDefault<z.ZodNumber>;
15
+ maxModelTokensLimit: z.ZodDefault<z.ZodNumber>;
16
+ uploadRetentionDays: z.ZodDefault<z.ZodNumber>;
14
17
  maxModelCalls: z.ZodDefault<z.ZodNumber>;
15
18
  maxModelTokens: z.ZodDefault<z.ZodNumber>;
19
+ imageTimeoutMs: z.ZodDefault<z.ZodNumber>;
16
20
  visionMaxEdge: z.ZodDefault<z.ZodNumber>;
17
21
  visionTileSize: z.ZodDefault<z.ZodNumber>;
18
22
  visionMaxTiles: z.ZodDefault<z.ZodNumber>;
package/lib/settings.js CHANGED
@@ -6,13 +6,16 @@ export const settingsSchema = z.strictObject({
6
6
  catalogMaxChars: positive.min(512).default(6000), candidateLimit: positive.max(30).default(6),
7
7
  maxInputBytes: positive.default(64 * 1024 * 1024), maxUncompressedBytes: positive.default(256 * 1024 * 1024), maxArchiveEntries: positive.default(10000),
8
8
  modelTimeoutMs: positive.default(90000), modelAttempts: positive.default(3), visionConcurrency: positive.max(8).default(2),
9
- maxModelCalls: positive.default(1000), maxModelTokens: positive.default(2000000), visionMaxEdge: positive.default(2048),
10
- visionTileSize: positive.default(1536), visionMaxTiles: positive.default(12), modelMaxOutputTokens: positive.default(4096),
9
+ maxModelCallsLimit: positive.max(100000).default(10000), maxModelTokensLimit: positive.max(1000000000).default(20000000), uploadRetentionDays: positive.max(365).default(7),
10
+ maxModelCalls: positive.max(100000).default(1000), maxModelTokens: positive.max(1000000000).default(2000000), imageTimeoutMs: positive.max(120000).default(60000), visionMaxEdge: positive.max(4096).default(2048),
11
+ visionTileSize: positive.max(4096).default(1536), visionMaxTiles: positive.max(64).default(12), modelMaxOutputTokens: positive.default(4096),
11
12
  summaryMaxChars: positive.default(160), figureMaxChars: positive.default(6000), pdfMaxPages: positive.default(2000),
12
13
  pdfMaxPagePixels: positive.default(12000000), pdfPageTimeoutMs: positive.default(60000),
13
14
  libreOfficePath: z.string().default(''), visionProvider: z.string().default(''), visionModel: z.string().default(''),
14
15
  summaryProvider: z.string().default(''), summaryModel: z.string().default(''), skipImages: z.boolean().default(false), acceptPartial: z.boolean().default(false),
15
16
  }).superRefine((value, context) => {
17
+ if (value.maxModelCalls > value.maxModelCallsLimit || value.maxModelTokens > value.maxModelTokensLimit)
18
+ context.addIssue({ code: 'custom', message: '默认任务预算不能超过宿主预算上限' });
16
19
  for (const kind of ['vision', 'summary'])
17
20
  if (Boolean(value[`${kind}Provider`]) !== Boolean(value[`${kind}Model`]))
18
21
  context.addIssue({ code: 'custom', message: `${kind}Provider / ${kind}Model 必须成对配置` });
package/lib/shared.d.ts CHANGED
@@ -93,3 +93,20 @@ export declare const revisionQuerySchema: z.ZodObject<{
93
93
  kbId: z.ZodString;
94
94
  revision: z.ZodOptional<z.ZodString>;
95
95
  }, z.core.$strict>;
96
+ export declare const jobListQuerySchema: z.ZodObject<{
97
+ jobsKbId: z.ZodOptional<z.ZodString>;
98
+ jobOffset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
99
+ jobLimit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
100
+ }, z.core.$strict>;
101
+ export declare const jobDetailsQuerySchema: z.ZodObject<{
102
+ jobId: z.ZodString;
103
+ section: z.ZodDefault<z.ZodEnum<{
104
+ report: "report";
105
+ details: "details";
106
+ }>>;
107
+ }, z.core.$strict>;
108
+ export declare const jobUsageQuerySchema: z.ZodObject<{
109
+ jobId: z.ZodString;
110
+ offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
111
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
112
+ }, z.core.$strict>;
package/lib/shared.js CHANGED
@@ -15,3 +15,6 @@ export const searchSchema = z.strictObject({ kbId: selector, revision: id.option
15
15
  export const readQuerySchema = z.strictObject({ kbId: selector, revision: id.optional(), chunkId: id, query: z.string().max(2000).optional(), keywords: keywordsSchema.optional(), segmentIndex: z.coerce.number().int().nonnegative().optional(), offset: z.coerce.number().int().nonnegative().optional() });
16
16
  export const assetQuerySchema = z.strictObject({ kbId: selector, revision: id.optional(), docId: id, assetId: id });
17
17
  export const revisionQuerySchema = z.strictObject({ kbId: selector, revision: id.optional() });
18
+ export const jobListQuerySchema = z.strictObject({ jobsKbId: id.optional(), jobOffset: z.coerce.number().int().nonnegative().max(1000000).default(0), jobLimit: z.coerce.number().int().min(1).max(50).default(20) });
19
+ export const jobDetailsQuerySchema = z.strictObject({ jobId: id, section: z.enum(['details', 'report']).default('details') });
20
+ export const jobUsageQuerySchema = jobDetailsQuerySchema.omit({ section: true }).extend({ offset: z.coerce.number().int().nonnegative().max(1000000).default(0), limit: z.coerce.number().int().min(1).max(50).default(20) });
package/lib/skills.js CHANGED
@@ -3,10 +3,13 @@ import { fileURLToPath } from 'node:url';
3
3
  import { checkAbort, required } from "./util.js";
4
4
  /** Packaged, invocation-neutral skills; no copies in the operator's user directory. */
5
5
  export function skillProvider() {
6
- const provider = 'libing-knowledge-base', names = ['libing-knowledge-base', 'libing-kb-build', 'libing-kb-search'];
6
+ const provider = 'libing-knowledge-base';
7
+ const names = ['libing-knowledge-base', 'libing-kb-build', 'libing-kb-search'];
7
8
  const candidates = names.map(name => {
8
- const directory = fileURLToPath(new URL(`../skills/${name}/`, import.meta.url)), path = fileURLToPath(new URL(`../skills/${name}/SKILL.md`, import.meta.url));
9
- const raw = readFileSync(path, 'utf8'), header = required(/^---\n([\s\S]*?)\n---\n/u.exec(raw), `skill frontmatter: ${name}`);
9
+ const directory = fileURLToPath(new URL(`../skills/${name}/`, import.meta.url));
10
+ const path = fileURLToPath(new URL(`../skills/${name}/SKILL.md`, import.meta.url));
11
+ const raw = readFileSync(path, 'utf8');
12
+ const header = required(/^---\n([\s\S]*?)\n---\n/u.exec(raw), `skill frontmatter: ${name}`);
10
13
  const description = required(/^description:\s*(.+)$/mu.exec(required(header[1]))?.[1], `skill description: ${name}`);
11
14
  return { name, description, source: 'bundled', provider, rank: 600, path, resourceBase: { kind: 'directory', path: directory }, invocation: { modelInvocable: true, userInvocable: true }, locator: path };
12
15
  });
package/lib/store.d.ts CHANGED
@@ -1,9 +1,11 @@
1
+ import { RevisionCache } from './revision-cache.ts';
1
2
  import type { Settings } from './config.ts';
2
3
  import { z } from 'zod';
3
4
  import { registrySchema } from './data.ts';
4
5
  import type { Library, Manifest, Snapshot, Chunk, PublishedDocument, BuildDocument, Overlay } from './data.ts';
5
6
  export declare const DEFAULT_SCHEMA = "# \u77E5\u8BC6\u5E93\u6CBB\u7406\u7EA6\u5B9A\n\n\u4FDD\u7559\u539F\u6587\u4E2D\u7684\u578B\u53F7\u3001\u7BA1\u811A\u3001\u5355\u4F4D\u3001\u6570\u503C\u3001\u6781\u6027\u548C\u9002\u7528\u6761\u4EF6\u3002\u56FE\u7247\u63CF\u8FF0\u4E0E\u539F\u6587\u5206\u522B\u6807\u6CE8\uFF1B\u4E0D\u786E\u5B9A\u7684\u4FE1\u606F\u4FDD\u7559\u5F85\u590D\u6838\u3002\u6458\u8981\u7528\u4E8E\u5BFC\u822A\uFF0C\u56DE\u7B54\u4F9D\u636E\u539F\u6587\u548C\u53EF\u6838\u9A8C\u56FE\u50CF\u3002\u5F15\u7528\u5305\u542B\u6587\u6863\u7248\u672C\u3001\u7AE0\u8282\u3001\u7247\u6BB5\u548C\u56FE\u8868\u7F16\u53F7\u3002\n";
6
7
  export declare class Store {
8
+ readonly revisionCache: RevisionCache;
7
9
  readonly config: Settings;
8
10
  readonly root: string;
9
11
  private registryTail;
@@ -103,16 +105,88 @@ export declare class Store {
103
105
  coverageIncomplete: boolean;
104
106
  };
105
107
  }>;
106
- location(kbId: string, revision?: string): Promise<{
108
+ private inspectKnown;
109
+ overview(): Promise<{
110
+ kbs: ({
111
+ location: {
112
+ kbPath: string;
113
+ revision: null;
114
+ wikiPath: null;
115
+ indexPath: null;
116
+ };
117
+ summary: {
118
+ revision: null;
119
+ documentCount: number;
120
+ chunkCount: number;
121
+ coverageIncomplete: boolean;
122
+ };
123
+ kbId: string;
124
+ name: string;
125
+ createdAt: string;
126
+ deletedAt?: string | undefined;
127
+ purge?: {
128
+ startedAt: string;
129
+ jobIds: string[];
130
+ error?: string | undefined;
131
+ } | undefined;
132
+ } | {
133
+ location: {
134
+ kbPath: string;
135
+ revision: string;
136
+ wikiPath: string;
137
+ indexPath: string;
138
+ };
139
+ summary: {
140
+ revision: string;
141
+ documentCount: number;
142
+ chunkCount: number;
143
+ coverageIncomplete: boolean;
144
+ };
145
+ kbId: string;
146
+ name: string;
147
+ createdAt: string;
148
+ deletedAt?: string | undefined;
149
+ purge?: {
150
+ startedAt: string;
151
+ jobIds: string[];
152
+ error?: string | undefined;
153
+ } | undefined;
154
+ })[];
155
+ deletedKbs: {
156
+ kbId: string;
157
+ name: string;
158
+ createdAt: string;
159
+ deletedAt?: string | undefined;
160
+ purge?: {
161
+ startedAt: string;
162
+ jobIds: string[];
163
+ error?: string | undefined;
164
+ } | undefined;
165
+ }[];
166
+ activities: {
167
+ eventId: string;
168
+ at: string;
169
+ action: "purge" | "create" | "trash" | "restore";
170
+ kbId: string;
171
+ name: string;
172
+ }[];
173
+ }>;
174
+ snapshotLocation(kbId: string, snapshot: Snapshot): Promise<{
107
175
  kbPath: string;
108
- revision: null;
109
- wikiPath: null;
110
- indexPath: null;
111
- } | {
176
+ revision: string;
177
+ wikiPath: string;
178
+ indexPath: string;
179
+ }>;
180
+ location(kbId: string, revision?: string): Promise<{
112
181
  kbPath: string;
113
182
  revision: string;
114
183
  wikiPath: string;
115
184
  indexPath: string;
185
+ } | {
186
+ kbPath: string;
187
+ revision: null;
188
+ wikiPath: null;
189
+ indexPath: null;
116
190
  }>;
117
191
  schema(kbId: string): Promise<string>;
118
192
  setSchema(kbId: string, content: string, expectedHash: string): Promise<{
@@ -138,7 +212,22 @@ export declare class Store {
138
212
  buildKey: string;
139
213
  coverageIncomplete: boolean;
140
214
  } | null>;
215
+ open(selector: string, requested?: string): Promise<{
216
+ kb: {
217
+ kbId: string;
218
+ name: string;
219
+ createdAt: string;
220
+ deletedAt?: string | undefined;
221
+ purge?: {
222
+ startedAt: string;
223
+ jobIds: string[];
224
+ error?: string | undefined;
225
+ } | undefined;
226
+ };
227
+ snapshot: Snapshot;
228
+ }>;
141
229
  revision(kbId: string, requested?: string): Promise<Snapshot>;
230
+ private revisionKnown;
142
231
  readChunk(kbId: string, revision: string | undefined, chunkId: string): Promise<{
143
232
  chunk: {
144
233
  chunkId: string;
@@ -187,6 +276,10 @@ export declare class Store {
187
276
  manifest: Manifest;
188
277
  }>;
189
278
  chunkContent(snapshot: Snapshot, chunk: Chunk): Promise<string>;
279
+ chunkView(snapshot: Snapshot, chunk: Chunk, fresh?: boolean): Promise<{
280
+ content: string;
281
+ lower: string;
282
+ }>;
190
283
  asset(kbId: string, revision: string | undefined, docId: string, assetId: string): Promise<{
191
284
  asset: {
192
285
  assetId: string;
@@ -197,6 +290,7 @@ export declare class Store {
197
290
  source?: import("./data.ts").Source | undefined;
198
291
  };
199
292
  file: string;
293
+ data: Buffer<ArrayBufferLike>;
200
294
  doc: {
201
295
  docId: string;
202
296
  title: string;
@@ -404,6 +498,8 @@ export declare class Store {
404
498
  }): Promise<string>;
405
499
  overlays(kbId: string): Promise<Record<string, Overlay>>;
406
500
  setOverlay(kbId: string, key: string, value: Overlay | null, expectedHash: string): Promise<Overlay | undefined>;
501
+ /** Cleanup runs with the sole owner before accepting uploads or starting jobs. Inputs are independent copies. */
502
+ cleanUploads(at?: number): Promise<void>;
407
503
  jobDirectories(): Promise<string[]>;
408
504
  acquireOwner(): Promise<() => Promise<void>>;
409
505
  }