@dsh-libing/knowledge-base 0.1.0 → 0.1.3
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/CHANGELOG.md +16 -0
- package/README.md +8 -2
- package/lib/client-types/client/JobDetails.d.ts +16 -0
- package/lib/client-types/client/JobsPanel.d.ts +6 -1
- package/lib/client-types/client/QualityReport.d.ts +3 -2
- package/lib/client-types/client/api.d.ts +83 -52
- package/lib/client-types/client/locales.d.ts +2 -0
- package/lib/client-types/client/useKnowledge.d.ts +30 -15
- package/lib/client-types/contract.d.ts +83 -52
- package/lib/client-types/data.d.ts +11 -0
- package/lib/client-types/settings.d.ts +4 -0
- package/lib/client-types/shared.d.ts +17 -0
- package/lib/client.js +40 -40
- package/lib/contract.d.ts +33 -2
- package/lib/contract.js +2 -1
- package/lib/data.d.ts +11 -0
- package/lib/data.js +2 -0
- package/lib/figures/image-client.d.ts +5 -0
- package/lib/figures/image-client.js +58 -0
- package/lib/figures/image-gate.d.ts +12 -0
- package/lib/figures/image-gate.js +39 -0
- package/lib/figures/image-protocol.d.ts +19 -0
- package/lib/figures/image-protocol.js +9 -0
- package/lib/figures/image-worker.d.ts +1 -0
- package/lib/figures/image-worker.js +60 -0
- package/lib/figures/process.d.ts +1 -1
- package/lib/figures/process.js +6 -35
- package/lib/indexes.js +9 -2
- package/lib/job-storage.d.ts +264 -0
- package/lib/job-storage.js +115 -0
- package/lib/jobs.d.ts +782 -1489
- package/lib/jobs.js +47 -23
- package/lib/models.d.ts +1 -1
- package/lib/models.js +8 -7
- package/lib/retrieval.js +8 -4
- package/lib/revision-cache.d.ts +24 -0
- package/lib/revision-cache.js +100 -0
- package/lib/routes.js +10 -7
- package/lib/runtime.d.ts +4 -0
- package/lib/runtime.js +6 -1
- package/lib/settings.d.ts +4 -0
- package/lib/settings.js +5 -2
- package/lib/shared.d.ts +17 -0
- package/lib/shared.js +3 -0
- package/lib/store.d.ts +101 -5
- package/lib/store.js +54 -20
- package/package.json +1 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/** Small checkpoints own counters; existing call receipts resolve only unfinished accounting. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { reportSchema } from './data.ts';
|
|
4
|
+
import type { Job, JobRecord } from './data.ts';
|
|
5
|
+
export declare const checkpointSchema: z.ZodObject<{
|
|
6
|
+
error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
7
|
+
title: z.ZodString;
|
|
8
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
9
|
+
pdf: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
pageCount: z.ZodNumber;
|
|
11
|
+
selectedPages: z.ZodObject<{
|
|
12
|
+
start: z.ZodNumber;
|
|
13
|
+
end: z.ZodNumber;
|
|
14
|
+
count: z.ZodNumber;
|
|
15
|
+
total: z.ZodNumber;
|
|
16
|
+
partial: z.ZodBoolean;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
outlineCount: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
}, z.core.$strip>>;
|
|
20
|
+
sourceFormat: z.ZodDefault<z.ZodEnum<{
|
|
21
|
+
docx: "docx";
|
|
22
|
+
pdf: "pdf";
|
|
23
|
+
}>>;
|
|
24
|
+
parserVersion: z.ZodOptional<z.ZodString>;
|
|
25
|
+
sourceHash: z.ZodOptional<z.ZodString>;
|
|
26
|
+
docId: z.ZodString;
|
|
27
|
+
sourceVersion: z.ZodString;
|
|
28
|
+
filename: z.ZodString;
|
|
29
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
30
|
+
buildKey: z.ZodString;
|
|
31
|
+
createdAt: z.ZodString;
|
|
32
|
+
config: z.ZodObject<{
|
|
33
|
+
dataRoot: z.ZodString;
|
|
34
|
+
chunkTargetChars: z.ZodDefault<z.ZodNumber>;
|
|
35
|
+
evidenceMaxChars: z.ZodDefault<z.ZodNumber>;
|
|
36
|
+
catalogMaxChars: z.ZodDefault<z.ZodNumber>;
|
|
37
|
+
candidateLimit: z.ZodDefault<z.ZodNumber>;
|
|
38
|
+
maxInputBytes: z.ZodDefault<z.ZodNumber>;
|
|
39
|
+
maxUncompressedBytes: z.ZodDefault<z.ZodNumber>;
|
|
40
|
+
maxArchiveEntries: z.ZodDefault<z.ZodNumber>;
|
|
41
|
+
modelTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
42
|
+
modelAttempts: z.ZodDefault<z.ZodNumber>;
|
|
43
|
+
visionConcurrency: z.ZodDefault<z.ZodNumber>;
|
|
44
|
+
maxModelCallsLimit: z.ZodDefault<z.ZodNumber>;
|
|
45
|
+
maxModelTokensLimit: z.ZodDefault<z.ZodNumber>;
|
|
46
|
+
uploadRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
47
|
+
maxModelCalls: z.ZodDefault<z.ZodNumber>;
|
|
48
|
+
maxModelTokens: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
imageTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
visionMaxEdge: z.ZodDefault<z.ZodNumber>;
|
|
51
|
+
visionTileSize: z.ZodDefault<z.ZodNumber>;
|
|
52
|
+
visionMaxTiles: z.ZodDefault<z.ZodNumber>;
|
|
53
|
+
modelMaxOutputTokens: z.ZodDefault<z.ZodNumber>;
|
|
54
|
+
summaryMaxChars: z.ZodDefault<z.ZodNumber>;
|
|
55
|
+
figureMaxChars: z.ZodDefault<z.ZodNumber>;
|
|
56
|
+
pdfMaxPages: z.ZodDefault<z.ZodNumber>;
|
|
57
|
+
pdfMaxPagePixels: z.ZodDefault<z.ZodNumber>;
|
|
58
|
+
pdfPageTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
59
|
+
libreOfficePath: z.ZodDefault<z.ZodString>;
|
|
60
|
+
visionProvider: z.ZodDefault<z.ZodString>;
|
|
61
|
+
visionModel: z.ZodDefault<z.ZodString>;
|
|
62
|
+
summaryProvider: z.ZodDefault<z.ZodString>;
|
|
63
|
+
summaryModel: z.ZodDefault<z.ZodString>;
|
|
64
|
+
skipImages: z.ZodDefault<z.ZodBoolean>;
|
|
65
|
+
acceptPartial: z.ZodDefault<z.ZodBoolean>;
|
|
66
|
+
}, z.core.$strict>;
|
|
67
|
+
jobId: z.ZodString;
|
|
68
|
+
kbId: z.ZodString;
|
|
69
|
+
state: z.ZodEnum<{
|
|
70
|
+
failed: "failed";
|
|
71
|
+
"needs-review": "needs-review";
|
|
72
|
+
queued: "queued";
|
|
73
|
+
preflight: "preflight";
|
|
74
|
+
converting: "converting";
|
|
75
|
+
describing: "describing";
|
|
76
|
+
splitting: "splitting";
|
|
77
|
+
indexing: "indexing";
|
|
78
|
+
validating: "validating";
|
|
79
|
+
publishing: "publishing";
|
|
80
|
+
"paused-budget": "paused-budget";
|
|
81
|
+
cancelled: "cancelled";
|
|
82
|
+
ready: "ready";
|
|
83
|
+
}>;
|
|
84
|
+
sourceOriginalHash: z.ZodString;
|
|
85
|
+
routes: z.ZodObject<{
|
|
86
|
+
vision: z.ZodNullable<z.ZodObject<{
|
|
87
|
+
provider: z.ZodString;
|
|
88
|
+
model: z.ZodString;
|
|
89
|
+
}, z.core.$strip>>;
|
|
90
|
+
summary: z.ZodNullable<z.ZodObject<{
|
|
91
|
+
provider: z.ZodString;
|
|
92
|
+
model: z.ZodString;
|
|
93
|
+
}, z.core.$strip>>;
|
|
94
|
+
}, z.core.$strip>;
|
|
95
|
+
schema: z.ZodString;
|
|
96
|
+
schemaHash: z.ZodString;
|
|
97
|
+
overlays: z.ZodRecord<z.ZodString, z.ZodType<import("./data.ts").Overlay, unknown, z.core.$ZodTypeInternals<import("./data.ts").Overlay, unknown>>>;
|
|
98
|
+
sessionId: z.ZodNullable<z.ZodString>;
|
|
99
|
+
progress: z.ZodObject<{
|
|
100
|
+
totalPages: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
parsedPages: z.ZodOptional<z.ZodNumber>;
|
|
102
|
+
ocrPages: z.ZodOptional<z.ZodNumber>;
|
|
103
|
+
tables: z.ZodOptional<z.ZodNumber>;
|
|
104
|
+
blocks: z.ZodOptional<z.ZodNumber>;
|
|
105
|
+
currentPage: z.ZodOptional<z.ZodNumber>;
|
|
106
|
+
totalFigures: z.ZodOptional<z.ZodNumber>;
|
|
107
|
+
completedFigures: z.ZodOptional<z.ZodNumber>;
|
|
108
|
+
inflightFigures: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
109
|
+
occurrenceId: z.ZodString;
|
|
110
|
+
caption: z.ZodString;
|
|
111
|
+
}, z.core.$strip>>>;
|
|
112
|
+
totalChunks: z.ZodOptional<z.ZodNumber>;
|
|
113
|
+
indexedChunks: z.ZodOptional<z.ZodNumber>;
|
|
114
|
+
currentChunk: z.ZodOptional<z.ZodObject<{
|
|
115
|
+
title: z.ZodString;
|
|
116
|
+
headingPath: z.ZodArray<z.ZodString>;
|
|
117
|
+
chunkId: z.ZodString;
|
|
118
|
+
}, z.core.$strip>>;
|
|
119
|
+
}, z.core.$strip>;
|
|
120
|
+
pendingCalls: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
121
|
+
legacyUsageCount: z.ZodDefault<z.ZodNumber>;
|
|
122
|
+
legacyCallCount: z.ZodDefault<z.ZodNumber>;
|
|
123
|
+
reportSummary: z.ZodOptional<z.ZodObject<{
|
|
124
|
+
issues: z.ZodNumber;
|
|
125
|
+
blocking: z.ZodNumber;
|
|
126
|
+
}, z.core.$strip>>;
|
|
127
|
+
failures: z.ZodArray<z.ZodObject<{
|
|
128
|
+
at: z.ZodString;
|
|
129
|
+
stage: z.ZodEnum<{
|
|
130
|
+
failed: "failed";
|
|
131
|
+
"needs-review": "needs-review";
|
|
132
|
+
queued: "queued";
|
|
133
|
+
preflight: "preflight";
|
|
134
|
+
converting: "converting";
|
|
135
|
+
describing: "describing";
|
|
136
|
+
splitting: "splitting";
|
|
137
|
+
indexing: "indexing";
|
|
138
|
+
validating: "validating";
|
|
139
|
+
publishing: "publishing";
|
|
140
|
+
"paused-budget": "paused-budget";
|
|
141
|
+
cancelled: "cancelled";
|
|
142
|
+
ready: "ready";
|
|
143
|
+
}>;
|
|
144
|
+
error: z.ZodString;
|
|
145
|
+
}, z.core.$strip>>;
|
|
146
|
+
events: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
147
|
+
at: z.ZodString;
|
|
148
|
+
state: z.ZodEnum<{
|
|
149
|
+
failed: "failed";
|
|
150
|
+
"needs-review": "needs-review";
|
|
151
|
+
queued: "queued";
|
|
152
|
+
preflight: "preflight";
|
|
153
|
+
converting: "converting";
|
|
154
|
+
describing: "describing";
|
|
155
|
+
splitting: "splitting";
|
|
156
|
+
indexing: "indexing";
|
|
157
|
+
validating: "validating";
|
|
158
|
+
publishing: "publishing";
|
|
159
|
+
"paused-budget": "paused-budget";
|
|
160
|
+
cancelled: "cancelled";
|
|
161
|
+
ready: "ready";
|
|
162
|
+
}>;
|
|
163
|
+
}, z.core.$strip>>>;
|
|
164
|
+
figureRefresh: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
165
|
+
occurrenceId: z.ZodString;
|
|
166
|
+
token: z.ZodString;
|
|
167
|
+
}, z.core.$strip>>>;
|
|
168
|
+
visionCapability: z.ZodOptional<z.ZodString>;
|
|
169
|
+
pageStart: z.ZodOptional<z.ZodNumber>;
|
|
170
|
+
pageEnd: z.ZodOptional<z.ZodNumber>;
|
|
171
|
+
outputPath: z.ZodOptional<z.ZodString>;
|
|
172
|
+
recoveredAt: z.ZodOptional<z.ZodString>;
|
|
173
|
+
finishedAt: z.ZodOptional<z.ZodString>;
|
|
174
|
+
cancelRequested: z.ZodOptional<z.ZodBoolean>;
|
|
175
|
+
formatVersion: z.ZodLiteral<2>;
|
|
176
|
+
usage: z.ZodObject<{
|
|
177
|
+
calls: z.ZodNumber;
|
|
178
|
+
tokens: z.ZodNumber;
|
|
179
|
+
reportedCalls: z.ZodNumber;
|
|
180
|
+
}, z.core.$strip>;
|
|
181
|
+
}, z.core.$strip>;
|
|
182
|
+
export declare function loadJob(directory: string): Promise<JobRecord | null>;
|
|
183
|
+
export declare function summarizeReport(report: z.infer<typeof reportSchema>): {
|
|
184
|
+
issues: number;
|
|
185
|
+
blocking: number;
|
|
186
|
+
};
|
|
187
|
+
export declare function saveReport(directory: string, report: z.infer<typeof reportSchema>): Promise<void>;
|
|
188
|
+
export declare function saveCheckpoint(directory: string, job: JobRecord): Promise<void>;
|
|
189
|
+
export declare function recoverUsage(job: Job): Promise<void>;
|
|
190
|
+
export declare function readReport(job: Job): Promise<{
|
|
191
|
+
revision: string;
|
|
192
|
+
sourceHash: string;
|
|
193
|
+
issues: {
|
|
194
|
+
code: string;
|
|
195
|
+
message: string;
|
|
196
|
+
blocking: boolean;
|
|
197
|
+
page?: number | undefined;
|
|
198
|
+
kind?: string | undefined;
|
|
199
|
+
snippet?: string | undefined;
|
|
200
|
+
reason?: string | undefined;
|
|
201
|
+
}[];
|
|
202
|
+
documents: number;
|
|
203
|
+
blocks: number;
|
|
204
|
+
figures: number;
|
|
205
|
+
ignoredFigures: number;
|
|
206
|
+
unreviewedFigures: number;
|
|
207
|
+
chunks: number;
|
|
208
|
+
withinTarget: number;
|
|
209
|
+
oversized: {
|
|
210
|
+
chunkId: string;
|
|
211
|
+
chars: number;
|
|
212
|
+
reason: string;
|
|
213
|
+
}[];
|
|
214
|
+
usage: {
|
|
215
|
+
calls: number;
|
|
216
|
+
tokens: number;
|
|
217
|
+
reportedCalls: number;
|
|
218
|
+
records: z.core.util.JSONType[];
|
|
219
|
+
};
|
|
220
|
+
targetChars: number;
|
|
221
|
+
pdf?: {
|
|
222
|
+
pageCount: number;
|
|
223
|
+
selectedPages: {
|
|
224
|
+
start: number;
|
|
225
|
+
end: number;
|
|
226
|
+
count: number;
|
|
227
|
+
total: number;
|
|
228
|
+
partial: boolean;
|
|
229
|
+
};
|
|
230
|
+
outlineCount: number;
|
|
231
|
+
matchedBookmarks: number;
|
|
232
|
+
ocrPages: number;
|
|
233
|
+
pages: {
|
|
234
|
+
page: number;
|
|
235
|
+
status: "failed" | "needs-review" | "navigation" | "parsed";
|
|
236
|
+
ocr: boolean;
|
|
237
|
+
tables: number;
|
|
238
|
+
figures: number;
|
|
239
|
+
headings: {
|
|
240
|
+
text: string;
|
|
241
|
+
level: number;
|
|
242
|
+
basis?: string | undefined;
|
|
243
|
+
}[];
|
|
244
|
+
width?: number | undefined;
|
|
245
|
+
height?: number | undefined;
|
|
246
|
+
pageLabel?: string | null | undefined;
|
|
247
|
+
spanCount?: number | undefined;
|
|
248
|
+
artifactCount?: number | undefined;
|
|
249
|
+
excluded?: {
|
|
250
|
+
kind: string;
|
|
251
|
+
bbox: [number, number, number, number] | null;
|
|
252
|
+
text?: string | undefined;
|
|
253
|
+
}[] | undefined;
|
|
254
|
+
previewAssetId?: string | null | undefined;
|
|
255
|
+
error?: string | undefined;
|
|
256
|
+
}[];
|
|
257
|
+
} | undefined;
|
|
258
|
+
} | undefined>;
|
|
259
|
+
export declare function usagePage(job: Job, offset: number, limit: number, signal?: AbortSignal): Promise<{
|
|
260
|
+
records: z.core.util.JSONType[];
|
|
261
|
+
offset: number;
|
|
262
|
+
total: number;
|
|
263
|
+
next: number | null;
|
|
264
|
+
}>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** Small checkpoints own counters; existing call receipts resolve only unfinished accounting. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { basename, join } from 'node:path';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
import { jobSchema, reportSchema, usageSchema } from "./data.js";
|
|
6
|
+
import { atomicWrite, checkAbort, code, readJson, safePath, tokensUsed } from "./util.js";
|
|
7
|
+
export const checkpointSchema = jobSchema.omit({ report: true, usage: true }).extend({
|
|
8
|
+
formatVersion: z.literal(2), usage: usageSchema.omit({ records: true }),
|
|
9
|
+
});
|
|
10
|
+
const receiptSchema = z.object({ state: z.enum(['running', 'done', 'failed']), usage: z.object({ inputTokens: z.number().nonnegative(), outputTokens: z.number().nonnegative(), cacheReadTokens: z.number().optional(), cacheWriteTokens: z.number().optional(), totalTokens: z.number().optional(), reasoningTokens: z.number().optional() }).optional() });
|
|
11
|
+
export async function loadJob(directory) {
|
|
12
|
+
const file = await safePath(directory, 'job.json', false);
|
|
13
|
+
let raw;
|
|
14
|
+
try {
|
|
15
|
+
raw = JSON.parse(await readFile(file, 'utf8'));
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (code(error) === 'ENOENT')
|
|
19
|
+
return null;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
const small = checkpointSchema.safeParse(raw);
|
|
23
|
+
if (small.success) {
|
|
24
|
+
if (small.data.jobId !== basename(directory))
|
|
25
|
+
throw new Error('任务目录与身份不一致');
|
|
26
|
+
return jobSchema.parse({ ...small.data, usage: { ...small.data.usage, records: [] } });
|
|
27
|
+
}
|
|
28
|
+
// Unknown checkpoint versions must not fall back to interpreting a partial record.
|
|
29
|
+
if (typeof raw === 'object' && raw !== null && 'formatVersion' in raw)
|
|
30
|
+
throw new Error('不支持的任务检查点格式');
|
|
31
|
+
const record = jobSchema.parse(raw);
|
|
32
|
+
if (record.jobId !== basename(directory))
|
|
33
|
+
throw new Error('任务目录与身份不一致');
|
|
34
|
+
if (['queued', 'preflight', 'converting', 'describing', 'splitting', 'indexing', 'validating', 'publishing'].includes(record.state)) {
|
|
35
|
+
let total = 0, reported = 0;
|
|
36
|
+
for (let number = 1; number <= record.usage.calls; number++) {
|
|
37
|
+
const receipt = await readJson(await safePath(directory, join('calls', `${String(number).padStart(6, '0')}.json`), false), receiptSchema, null);
|
|
38
|
+
if (receipt?.usage) {
|
|
39
|
+
total += usageTokens(receipt.usage);
|
|
40
|
+
reported++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
record.usage.tokens = Math.max(record.usage.tokens, total);
|
|
44
|
+
record.usage.reportedCalls = Math.max(record.usage.reportedCalls, reported);
|
|
45
|
+
}
|
|
46
|
+
if (record.usage.records.length) {
|
|
47
|
+
await atomicWrite(await safePath(directory, 'legacy-usage.json', false), record.usage.records);
|
|
48
|
+
record.legacyUsageCount = record.usage.records.length;
|
|
49
|
+
record.legacyCallCount = record.usage.calls;
|
|
50
|
+
record.usage.records = [];
|
|
51
|
+
}
|
|
52
|
+
if (record.report) {
|
|
53
|
+
await saveReport(directory, record.report);
|
|
54
|
+
record.reportSummary = summarizeReport(record.report);
|
|
55
|
+
delete record.report;
|
|
56
|
+
}
|
|
57
|
+
await saveCheckpoint(directory, record);
|
|
58
|
+
return record;
|
|
59
|
+
}
|
|
60
|
+
export function summarizeReport(report) {
|
|
61
|
+
return { issues: report.issues.length, blocking: report.issues.filter(i => i.blocking).length };
|
|
62
|
+
}
|
|
63
|
+
export async function saveReport(directory, report) {
|
|
64
|
+
await atomicWrite(await safePath(directory, 'quality.json', false), report);
|
|
65
|
+
}
|
|
66
|
+
export async function saveCheckpoint(directory, job) {
|
|
67
|
+
const { report: _report, usage, ...small } = job;
|
|
68
|
+
const { records: _records, ...counters } = usage;
|
|
69
|
+
// Strip heavy values before cloning; JSON.stringify(job) would retain the original cost.
|
|
70
|
+
const snapshot = checkpointSchema.parse({ ...small, formatVersion: 2, usage: counters });
|
|
71
|
+
await atomicWrite(await safePath(directory, 'job.json', false), snapshot);
|
|
72
|
+
}
|
|
73
|
+
function usageTokens(usage) {
|
|
74
|
+
return tokensUsed({ inputTokens: usage?.inputTokens ?? 0, outputTokens: usage?.outputTokens ?? 0, cacheReadTokens: usage?.cacheReadTokens ?? 0, cacheWriteTokens: usage?.cacheWriteTokens ?? 0 });
|
|
75
|
+
}
|
|
76
|
+
export async function recoverUsage(job) {
|
|
77
|
+
if (new Set(job.pendingCalls).size !== job.pendingCalls.length || job.pendingCalls.some(n => n > job.usage.calls))
|
|
78
|
+
throw new Error('任务待结算调用编号无效');
|
|
79
|
+
for (const number of job.pendingCalls) {
|
|
80
|
+
const file = await safePath(job.directory, join('calls', `${String(number).padStart(6, '0')}.json`), false);
|
|
81
|
+
const receipt = await readJson(file, receiptSchema, null);
|
|
82
|
+
// A reserved call remains charged even when no completion/usage reached disk.
|
|
83
|
+
if (receipt?.usage) {
|
|
84
|
+
job.usage.tokens += usageTokens(receipt.usage);
|
|
85
|
+
job.usage.reportedCalls++;
|
|
86
|
+
}
|
|
87
|
+
job.pendingCalls = job.pendingCalls.filter(n => n !== number);
|
|
88
|
+
}
|
|
89
|
+
await job.checkpoint();
|
|
90
|
+
}
|
|
91
|
+
export async function readReport(job) {
|
|
92
|
+
if (!job.reportSummary)
|
|
93
|
+
return undefined;
|
|
94
|
+
return readJson(await safePath(job.directory, 'quality.json'), reportSchema);
|
|
95
|
+
}
|
|
96
|
+
export async function usagePage(job, offset, limit, signal) {
|
|
97
|
+
if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 50)
|
|
98
|
+
throw new Error('用量分页参数无效');
|
|
99
|
+
const total = job.legacyUsageCount + Math.max(0, job.usage.calls - job.legacyCallCount), records = [];
|
|
100
|
+
const end = Math.min(total, offset + limit);
|
|
101
|
+
let legacy;
|
|
102
|
+
if (offset < job.legacyUsageCount)
|
|
103
|
+
legacy = await readJson(await safePath(job.directory, 'legacy-usage.json'), z.array(z.json()));
|
|
104
|
+
for (let index = offset; index < end; index++) {
|
|
105
|
+
checkAbort(signal);
|
|
106
|
+
if (index < job.legacyUsageCount) {
|
|
107
|
+
records.push(legacy?.[index] ?? null);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const number = job.legacyCallCount + index - job.legacyUsageCount + 1;
|
|
111
|
+
const file = await safePath(job.directory, join('calls', `${String(number).padStart(6, '0')}.json`), false), receipt = await readJson(file, receiptSchema, null);
|
|
112
|
+
records.push(z.json().parse(JSON.parse(JSON.stringify({ call: number, state: receipt?.state ?? 'reserved', usage: receipt?.usage ?? null }))));
|
|
113
|
+
}
|
|
114
|
+
return { records, offset, total, next: end < total ? end : null };
|
|
115
|
+
}
|