@mcoda/codali 0.1.90 → 0.1.91

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 (105) hide show
  1. package/dist/cli/DatasetCommand.d.ts +38 -0
  2. package/dist/cli/DatasetCommand.d.ts.map +1 -0
  3. package/dist/cli/DatasetCommand.js +959 -0
  4. package/dist/cli/EvalCommand.d.ts +3 -0
  5. package/dist/cli/EvalCommand.d.ts.map +1 -1
  6. package/dist/cli/EvalCommand.js +55 -1
  7. package/dist/cli/ImprovementCommand.d.ts +97 -0
  8. package/dist/cli/ImprovementCommand.d.ts.map +1 -0
  9. package/dist/cli/ImprovementCommand.js +1879 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +15 -0
  12. package/dist/dataset-cli.d.ts +3 -0
  13. package/dist/dataset-cli.d.ts.map +1 -0
  14. package/dist/dataset-cli.js +30 -0
  15. package/dist/eval/CodaliGatewayLiveHarness.d.ts +90 -1
  16. package/dist/eval/CodaliGatewayLiveHarness.d.ts.map +1 -1
  17. package/dist/eval/CodaliGatewayLiveHarness.js +443 -9
  18. package/dist/eval/GatewayDatasetEval.d.ts +70 -0
  19. package/dist/eval/GatewayDatasetEval.d.ts.map +1 -0
  20. package/dist/eval/GatewayDatasetEval.js +477 -0
  21. package/dist/eval/GatewayEvalSuite.d.ts +52 -0
  22. package/dist/eval/GatewayEvalSuite.d.ts.map +1 -1
  23. package/dist/eval/GatewayEvalSuite.js +135 -1
  24. package/dist/gateway/CodaliGateway.d.ts +4 -0
  25. package/dist/gateway/CodaliGateway.d.ts.map +1 -1
  26. package/dist/gateway/CodaliGateway.js +37 -1
  27. package/dist/gateway/CodaliGatewaySchemas.d.ts.map +1 -1
  28. package/dist/gateway/CodaliGatewaySchemas.js +0 -4
  29. package/dist/gateway/CodaliGatewayStore.d.ts.map +1 -1
  30. package/dist/gateway/CodaliGatewayStore.js +1 -0
  31. package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
  32. package/dist/gateway/GatewayPlanner.js +355 -41
  33. package/dist/gateway/GatewayStateMachine.d.ts +1 -0
  34. package/dist/gateway/GatewayStateMachine.d.ts.map +1 -1
  35. package/dist/gateway/GatewayStateMachine.js +221 -5
  36. package/dist/improvement/CandidateReleaseBuilder.d.ts +201 -0
  37. package/dist/improvement/CandidateReleaseBuilder.d.ts.map +1 -0
  38. package/dist/improvement/CandidateReleaseBuilder.js +1175 -0
  39. package/dist/improvement/DatasetEligibilityGate.d.ts +75 -0
  40. package/dist/improvement/DatasetEligibilityGate.d.ts.map +1 -0
  41. package/dist/improvement/DatasetEligibilityGate.js +497 -0
  42. package/dist/improvement/DatasetExportManifestReader.d.ts +88 -0
  43. package/dist/improvement/DatasetExportManifestReader.d.ts.map +1 -0
  44. package/dist/improvement/DatasetExportManifestReader.js +333 -0
  45. package/dist/improvement/DocdexRetrievalCandidateBuilder.d.ts +192 -0
  46. package/dist/improvement/DocdexRetrievalCandidateBuilder.d.ts.map +1 -0
  47. package/dist/improvement/DocdexRetrievalCandidateBuilder.js +625 -0
  48. package/dist/improvement/EvalReplayCandidateBuilder.d.ts +173 -0
  49. package/dist/improvement/EvalReplayCandidateBuilder.d.ts.map +1 -0
  50. package/dist/improvement/EvalReplayCandidateBuilder.js +363 -0
  51. package/dist/improvement/FineTuneJobPlanner.d.ts +281 -0
  52. package/dist/improvement/FineTuneJobPlanner.d.ts.map +1 -0
  53. package/dist/improvement/FineTuneJobPlanner.js +823 -0
  54. package/dist/improvement/ImprovementEvalRunner.d.ts +50 -0
  55. package/dist/improvement/ImprovementEvalRunner.d.ts.map +1 -0
  56. package/dist/improvement/ImprovementEvalRunner.js +648 -0
  57. package/dist/improvement/ImprovementPolicy.d.ts +272 -0
  58. package/dist/improvement/ImprovementPolicy.d.ts.map +1 -0
  59. package/dist/improvement/ImprovementPolicy.js +1450 -0
  60. package/dist/improvement/ModelRouterCandidateBuilder.d.ts +186 -0
  61. package/dist/improvement/ModelRouterCandidateBuilder.d.ts.map +1 -0
  62. package/dist/improvement/ModelRouterCandidateBuilder.js +880 -0
  63. package/dist/improvement/OperatorInspector.d.ts +182 -0
  64. package/dist/improvement/OperatorInspector.d.ts.map +1 -0
  65. package/dist/improvement/OperatorInspector.js +604 -0
  66. package/dist/improvement/ProductionGovernance.d.ts +91 -0
  67. package/dist/improvement/ProductionGovernance.d.ts.map +1 -0
  68. package/dist/improvement/ProductionGovernance.js +329 -0
  69. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts +152 -0
  70. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts.map +1 -0
  71. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.js +480 -0
  72. package/dist/improvement/PublishOrchestrator.d.ts +113 -0
  73. package/dist/improvement/PublishOrchestrator.d.ts.map +1 -0
  74. package/dist/improvement/PublishOrchestrator.js +590 -0
  75. package/dist/improvement/ReleaseOutcomeReporter.d.ts +133 -0
  76. package/dist/improvement/ReleaseOutcomeReporter.d.ts.map +1 -0
  77. package/dist/improvement/ReleaseOutcomeReporter.js +501 -0
  78. package/dist/improvement/StorageServiceImprovementClient.d.ts +71 -0
  79. package/dist/improvement/StorageServiceImprovementClient.d.ts.map +1 -0
  80. package/dist/improvement/StorageServiceImprovementClient.js +245 -0
  81. package/dist/index.d.ts +43 -0
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +21 -0
  84. package/dist/runtime/CodaliRuntime.d.ts +0 -2
  85. package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
  86. package/dist/runtime/CodaliRuntime.js +0 -5
  87. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts +138 -0
  88. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -0
  89. package/dist/storage/CodaliDatasetPrivacyEngine.js +417 -0
  90. package/dist/storage/CodaliFeedbackReviewIngestion.d.ts +50 -0
  91. package/dist/storage/CodaliFeedbackReviewIngestion.d.ts.map +1 -0
  92. package/dist/storage/CodaliFeedbackReviewIngestion.js +141 -0
  93. package/dist/storage/CodaliStorageContracts.d.ts +337 -0
  94. package/dist/storage/CodaliStorageContracts.d.ts.map +1 -0
  95. package/dist/storage/CodaliStorageContracts.js +1555 -0
  96. package/dist/storage/DatasetExportJob.d.ts +51 -0
  97. package/dist/storage/DatasetExportJob.d.ts.map +1 -0
  98. package/dist/storage/DatasetExportJob.js +358 -0
  99. package/dist/storage/DatasetReviewQueue.d.ts +98 -0
  100. package/dist/storage/DatasetReviewQueue.d.ts.map +1 -0
  101. package/dist/storage/DatasetReviewQueue.js +426 -0
  102. package/dist/storage/GatewayDatasetStore.d.ts +233 -0
  103. package/dist/storage/GatewayDatasetStore.d.ts.map +1 -0
  104. package/dist/storage/GatewayDatasetStore.js +2070 -0
  105. package/package.json +5 -4
@@ -0,0 +1,625 @@
1
+ import { createHash } from "node:crypto";
2
+ export const CODALI_DOCDEX_RETRIEVAL_CANDIDATE_SCHEMA_VERSION = "codali.improvement.docdex_retrieval_candidate.v1";
3
+ export const CODALI_DOCDEX_RETRIEVAL_PROPOSAL_ARTIFACT = "docdex-retrieval";
4
+ export const CODALI_DOCDEX_RETRIEVAL_SOURCE_ARTIFACT_TYPES = [
5
+ "docdex_retrieval",
6
+ "query_expander",
7
+ "query_expander_sft",
8
+ "rag_reranker",
9
+ "rerank",
10
+ "reranker",
11
+ "retrieval",
12
+ "freshness",
13
+ "duplicate_detection",
14
+ "source_selection",
15
+ ];
16
+ export const CODALI_DOCDEX_RETRIEVAL_SUPPORTED_EXPORT_KINDS = [
17
+ "query-expander-sft",
18
+ "rag-reranker",
19
+ ];
20
+ const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
21
+ const stableJson = (value) => {
22
+ if (Array.isArray(value))
23
+ return `[${value.map(stableJson).join(",")}]`;
24
+ if (!isRecord(value))
25
+ return JSON.stringify(value);
26
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
27
+ };
28
+ const sha256Hex = (value) => createHash("sha256").update(stableJson(value)).digest("hex");
29
+ const stableId = (prefix, value) => `${prefix}-${sha256Hex(value).slice(0, 16)}`;
30
+ const normalizeToken = (value) => value.trim().toLowerCase().replace(/[\s-]+/g, "_");
31
+ const uniqueSorted = (values) => Array.from(new Set(values
32
+ .map((value) => value?.trim())
33
+ .filter((value) => Boolean(value))))
34
+ .sort();
35
+ const normalizedUniqueSorted = (values) => uniqueSorted(values.map((value) => value ? normalizeToken(value) : undefined));
36
+ const readString = (record, keys) => {
37
+ for (const key of keys) {
38
+ const value = record?.[key];
39
+ if (typeof value === "string" && value.trim())
40
+ return value.trim();
41
+ }
42
+ return undefined;
43
+ };
44
+ const readNumber = (record, keys) => {
45
+ for (const key of keys) {
46
+ const value = record?.[key];
47
+ if (typeof value === "number" && Number.isFinite(value))
48
+ return value;
49
+ if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) {
50
+ return Number(value);
51
+ }
52
+ }
53
+ return undefined;
54
+ };
55
+ const stringList = (value) => {
56
+ if (Array.isArray(value)) {
57
+ return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
58
+ }
59
+ if (typeof value === "string" && value.trim())
60
+ return [value.trim()];
61
+ return [];
62
+ };
63
+ const objectList = (value) => Array.isArray(value) ? value.filter(isRecord) : [];
64
+ const metadataForRow = (row) => isRecord(row?.metadata) ? row.metadata : {};
65
+ const nestedRecord = (record, keys) => {
66
+ for (const key of keys) {
67
+ const value = record[key];
68
+ if (isRecord(value))
69
+ return value;
70
+ }
71
+ return {};
72
+ };
73
+ const objectRefFromValue = (value) => isRecord(value) &&
74
+ typeof value.refId === "string" &&
75
+ typeof value.contentHash === "string" &&
76
+ typeof value.byteSize === "number" &&
77
+ typeof value.mimeType === "string" &&
78
+ typeof value.deletionGroupId === "string" &&
79
+ isRecord(value.privacyFlags)
80
+ ? value
81
+ : undefined;
82
+ const refsForRow = (row) => {
83
+ const evidenceRefs = Array.isArray(row?.evidenceRefs)
84
+ ? row.evidenceRefs.map(objectRefFromValue)
85
+ .filter((ref) => Boolean(ref))
86
+ : [];
87
+ return {
88
+ inputRef: objectRefFromValue(row?.inputRef),
89
+ outputRef: objectRefFromValue(row?.outputRef),
90
+ evidenceRefs,
91
+ };
92
+ };
93
+ const summarizeObjectRef = (ref) => ({
94
+ refId: ref.refId,
95
+ kind: ref.kind,
96
+ contentHash: ref.contentHash,
97
+ byteSize: ref.byteSize,
98
+ mimeType: ref.mimeType,
99
+ deletionGroupId: ref.deletionGroupId,
100
+ privacyFlags: ref.privacyFlags,
101
+ ...(ref.uri ? { uri: ref.uri } : {}),
102
+ ...(ref.mediaType ? { mediaType: ref.mediaType } : {}),
103
+ metadataKeys: isRecord(ref.metadata) ? Object.keys(ref.metadata).sort() : [],
104
+ });
105
+ const recordIdForRow = (row) => isRecord(row) ? readString(row, ["recordId", "record_id"]) : undefined;
106
+ const rowsByRecordId = (rows) => {
107
+ const output = new Map();
108
+ for (const row of rows) {
109
+ if (!isRecord(row))
110
+ continue;
111
+ const recordId = recordIdForRow(row);
112
+ if (recordId)
113
+ output.set(recordId, row);
114
+ }
115
+ return output;
116
+ };
117
+ const privacyForRow = (row) => isRecord(row?.privacy)
118
+ ? row.privacy
119
+ : undefined;
120
+ const policyTagsForPrivacy = (privacy) => uniqueSorted(privacy?.policyTags ?? []);
121
+ const privateReasonCodes = (privacy, refs) => {
122
+ const reasons = [];
123
+ if (privacy?.containsPersonalData)
124
+ reasons.push("record_contains_personal_data");
125
+ if (privacy?.exportAllowed !== true)
126
+ reasons.push("record_export_not_allowed");
127
+ if (refs.some((ref) => ref.privacyFlags.containsPersonalData)) {
128
+ reasons.push("object_contains_personal_data");
129
+ }
130
+ if (refs.some((ref) => ref.privacyFlags.containsSecrets)) {
131
+ reasons.push("object_contains_secrets");
132
+ }
133
+ if (refs.some((ref) => ref.privacyFlags.containsTenantPrivateData)) {
134
+ reasons.push("object_contains_tenant_private_data");
135
+ }
136
+ if (refs.some((ref) => ref.privacyFlags.containsSourceCode)) {
137
+ reasons.push("object_contains_source_code");
138
+ }
139
+ if (refs.some((ref) => ref.privacyFlags.containsCustomerData)) {
140
+ reasons.push("object_contains_customer_data");
141
+ }
142
+ if (refs.some((ref) => ref.privacyFlags.exportAllowed !== true)) {
143
+ reasons.push("object_export_not_allowed");
144
+ }
145
+ return uniqueSorted(reasons);
146
+ };
147
+ const privacyScopeFor = (row, refs) => {
148
+ const privacy = privacyForRow(row);
149
+ const reasons = privateReasonCodes(privacy, refs);
150
+ const broadReuseAllowed = reasons.length === 0 && privacy?.exportAllowed === true;
151
+ return {
152
+ tenantScoped: !broadReuseAllowed,
153
+ broadReuseAllowed,
154
+ rawTextAllowed: broadReuseAllowed,
155
+ reasons,
156
+ policyTags: policyTagsForPrivacy(privacy),
157
+ };
158
+ };
159
+ const readRetrievalMetadata = (metadata) => nestedRecord(metadata, [
160
+ "retrieval",
161
+ "docdexRetrieval",
162
+ "docdex_retrieval",
163
+ "retrievalEvidence",
164
+ "retrieval_evidence",
165
+ ]);
166
+ const readScorecardMetadata = (metadata, retrieval) => {
167
+ const topLevel = nestedRecord(metadata, ["scorecard", "retrievalScorecard", "docdexScorecard"]);
168
+ return Object.keys(topLevel).length > 0
169
+ ? topLevel
170
+ : nestedRecord(retrieval, ["scorecard", "retrievalScorecard", "docdexScorecard"]);
171
+ };
172
+ const queryTextForRow = (metadata, retrieval, row) => readString(retrieval, [
173
+ "query",
174
+ "originalQuery",
175
+ "original_query",
176
+ "userQuery",
177
+ "user_query",
178
+ "searchQuery",
179
+ "search_query",
180
+ ]) ?? readString(metadata, [
181
+ "query",
182
+ "originalQuery",
183
+ "original_query",
184
+ "userQuery",
185
+ "user_query",
186
+ "searchQuery",
187
+ "search_query",
188
+ ]) ?? readString(row, ["query", "request"]) ?? `record:${recordIdForRow(row) ?? "unknown"}`;
189
+ const queryVariantsFor = (metadata, retrieval) => uniqueSorted([
190
+ ...stringList(metadata.expandedQueries),
191
+ ...stringList(metadata.expanded_queries),
192
+ ...stringList(metadata.queryVariants),
193
+ ...stringList(metadata.query_variants),
194
+ ...stringList(retrieval.expandedQueries),
195
+ ...stringList(retrieval.expanded_queries),
196
+ ...stringList(retrieval.queryVariants),
197
+ ...stringList(retrieval.query_variants),
198
+ ]);
199
+ const buildQuerySummary = (metadata, retrieval, row, privacyScope) => {
200
+ const queryText = queryTextForRow(metadata, retrieval, row);
201
+ const queryHash = `sha256:${sha256Hex(queryText)}`;
202
+ const variants = queryVariantsFor(metadata, retrieval).map((variant) => ({
203
+ variantId: stableId("query-variant", { queryHash, variant }),
204
+ queryHash: `sha256:${sha256Hex(variant)}`,
205
+ ...(privacyScope.rawTextAllowed ? { text: variant } : {}),
206
+ }));
207
+ return {
208
+ queryHash,
209
+ ...(privacyScope.rawTextAllowed ? { text: queryText } : {}),
210
+ storage: privacyScope.rawTextAllowed ? "inline_allowed" : "object_ref_or_hash_only",
211
+ variants,
212
+ };
213
+ };
214
+ const sourceRecordsFor = (metadata, retrieval) => [
215
+ ...objectList(metadata.selectedSources),
216
+ ...objectList(metadata.selected_sources),
217
+ ...objectList(metadata.sources),
218
+ ...objectList(metadata.candidates),
219
+ ...objectList(retrieval.selectedSources),
220
+ ...objectList(retrieval.selected_sources),
221
+ ...objectList(retrieval.sources),
222
+ ...objectList(retrieval.candidates),
223
+ ];
224
+ const sourceIdsFor = (metadata, retrieval) => uniqueSorted([
225
+ ...stringList(metadata.selectedSources),
226
+ ...stringList(metadata.selected_sources),
227
+ ...stringList(metadata.sourceIds),
228
+ ...stringList(metadata.source_ids),
229
+ ...stringList(retrieval.selectedSources),
230
+ ...stringList(retrieval.selected_sources),
231
+ ...stringList(retrieval.sourceIds),
232
+ ...stringList(retrieval.source_ids),
233
+ ...sourceRecordsFor(metadata, retrieval).flatMap((source) => [
234
+ readString(source, ["sourceId", "source_id", "docId", "doc_id", "path", "uri", "refId"]),
235
+ ]),
236
+ ]);
237
+ const sourceTypesFor = (metadata, retrieval) => normalizedUniqueSorted([
238
+ ...stringList(metadata.sourceTypes),
239
+ ...stringList(metadata.source_types),
240
+ ...stringList(retrieval.sourceTypes),
241
+ ...stringList(retrieval.source_types),
242
+ ...sourceRecordsFor(metadata, retrieval).flatMap((source) => [
243
+ readString(source, ["sourceType", "source_type", "kind", "type"]),
244
+ ]),
245
+ ]);
246
+ const sourceSelectionFor = (metadata, retrieval, privacyScope) => {
247
+ const sourceIds = sourceIdsFor(metadata, retrieval);
248
+ return {
249
+ sourceHashes: sourceIds.map((sourceId) => `sha256:${sha256Hex(sourceId)}`).sort(),
250
+ ...(privacyScope.rawTextAllowed ? { sourceIds } : {}),
251
+ sourceTypes: sourceTypesFor(metadata, retrieval),
252
+ selectedCount: sourceIds.length,
253
+ };
254
+ };
255
+ const metricDelta = (scorecard, metric) => {
256
+ const before = readNumber(scorecard, [
257
+ `${metric}Before`,
258
+ `${metric}_before`,
259
+ `${metric}Baseline`,
260
+ `${metric}_baseline`,
261
+ ]);
262
+ const after = readNumber(scorecard, [
263
+ `${metric}After`,
264
+ `${metric}_after`,
265
+ `${metric}Candidate`,
266
+ `${metric}_candidate`,
267
+ ]);
268
+ const explicitDelta = readNumber(scorecard, [`${metric}Delta`, `${metric}_delta`]);
269
+ const delta = explicitDelta ?? (before !== undefined && after !== undefined ? after - before : undefined);
270
+ return before !== undefined || after !== undefined || delta !== undefined
271
+ ? {
272
+ ...(before !== undefined ? { before } : {}),
273
+ ...(after !== undefined ? { after } : {}),
274
+ ...(delta !== undefined ? { delta } : {}),
275
+ }
276
+ : undefined;
277
+ };
278
+ const scorecardFor = (metadata, retrieval) => {
279
+ const scorecard = readScorecardMetadata(metadata, retrieval);
280
+ return {
281
+ ...(metricDelta(scorecard, "recall") ? { recall: metricDelta(scorecard, "recall") } : {}),
282
+ ...(metricDelta(scorecard, "precision") ? { precision: metricDelta(scorecard, "precision") } : {}),
283
+ ...(metricDelta(scorecard, "freshness") ? { freshness: metricDelta(scorecard, "freshness") } : {}),
284
+ };
285
+ };
286
+ const fineTuningPriorityFor = (accepted) => {
287
+ const signals = accepted.preferenceSignals.map(String);
288
+ if (signals.includes("human_reviewed") ||
289
+ signals.includes("accepted_correction") ||
290
+ signals.includes("high_confidence") ||
291
+ accepted.priorityScore >= 500) {
292
+ return "high";
293
+ }
294
+ return accepted.priorityScore >= 50 ? "medium" : "low";
295
+ };
296
+ const sourceExampleForAccepted = (accepted, row) => {
297
+ const refs = refsForRow(row);
298
+ const allRefs = [refs.inputRef, refs.outputRef, ...refs.evidenceRefs]
299
+ .filter((ref) => Boolean(ref));
300
+ const privacyScope = privacyScopeFor(row, allRefs);
301
+ const metadata = metadataForRow(row);
302
+ const retrieval = readRetrievalMetadata(metadata);
303
+ const scorecard = readScorecardMetadata(metadata, retrieval);
304
+ return {
305
+ recordId: accepted.recordId,
306
+ ...(accepted.sourceGatewayRecordId ? {
307
+ sourceGatewayRecordId: accepted.sourceGatewayRecordId,
308
+ } : {}),
309
+ ...(isRecord(row) && typeof row.datasetKind === "string" ? {
310
+ datasetKind: row.datasetKind,
311
+ } : {}),
312
+ artifactTypes: accepted.artifactTypes,
313
+ preferenceSignals: accepted.preferenceSignals.map(String).sort(),
314
+ priorityScore: accepted.priorityScore,
315
+ fineTuningPriority: fineTuningPriorityFor(accepted),
316
+ lineageKey: accepted.lineageKey,
317
+ privacyScope,
318
+ query: buildQuerySummary(metadata, retrieval, row, privacyScope),
319
+ sourceSelection: sourceSelectionFor(metadata, retrieval, privacyScope),
320
+ scorecard: scorecardFor(metadata, retrieval),
321
+ objectRefs: {
322
+ ...(refs.inputRef ? { inputRef: summarizeObjectRef(refs.inputRef) } : {}),
323
+ ...(refs.outputRef ? { outputRef: summarizeObjectRef(refs.outputRef) } : {}),
324
+ evidenceRefs: refs.evidenceRefs.map(summarizeObjectRef),
325
+ },
326
+ metadataShape: {
327
+ keys: Object.keys(metadata).sort(),
328
+ retrievalKeys: Object.keys(retrieval).sort(),
329
+ scorecardKeys: Object.keys(scorecard).sort(),
330
+ },
331
+ };
332
+ };
333
+ const buildSourceExamples = (inspection) => {
334
+ const rowMap = rowsByRecordId(inspection.primaryArtifactRows);
335
+ return inspection.curationReport.accepted
336
+ .map((accepted) => sourceExampleForAccepted(accepted, rowMap.get(accepted.recordId)))
337
+ .sort((left, right) => right.priorityScore - left.priorityScore || left.recordId.localeCompare(right.recordId));
338
+ };
339
+ const averageDelta = (examples, key) => {
340
+ const deltas = examples
341
+ .map((example) => example.scorecard[key]?.delta)
342
+ .filter((value) => typeof value === "number" && Number.isFinite(value));
343
+ if (deltas.length === 0)
344
+ return 0;
345
+ return Number((deltas.reduce((sum, value) => sum + value, 0) / deltas.length).toFixed(6));
346
+ };
347
+ const buildQueryExpanderEval = (inspection, sourceExamples) => {
348
+ const orderedExamples = [...sourceExamples].sort(compareQueryExpanderPriority);
349
+ return {
350
+ suiteId: stableId("docdex-query-expander-suite", {
351
+ manifestId: inspection.manifest.manifestId,
352
+ recordIds: orderedExamples.map((example) => example.recordId),
353
+ }),
354
+ sourceExportId: inspection.manifest.manifestId,
355
+ fineTuningPriority: {
356
+ strategy: "human_reviewed_then_accepted_correction_then_high_confidence",
357
+ orderedRecordIds: orderedExamples.map((example) => example.recordId),
358
+ },
359
+ cases: orderedExamples.map((example) => ({
360
+ caseId: stableId("query-expander-case", {
361
+ recordId: example.recordId,
362
+ queryHash: example.query.queryHash,
363
+ }),
364
+ sourceRecordId: example.recordId,
365
+ query: example.query,
366
+ expectedExpansionCount: Math.max(1, example.query.variants.length),
367
+ ...(example.scorecard.recall?.delta !== undefined ? {
368
+ expectedRecallDeltaMin: example.scorecard.recall.delta,
369
+ } : {}),
370
+ privacyScope: example.privacyScope,
371
+ objectRefs: example.objectRefs,
372
+ })),
373
+ };
374
+ };
375
+ const queryExpanderPriorityRank = (example) => {
376
+ const signals = new Set(example.preferenceSignals);
377
+ if (signals.has("human_reviewed"))
378
+ return 6;
379
+ if (signals.has("accepted_correction"))
380
+ return 5;
381
+ if (signals.has("high_confidence"))
382
+ return 4;
383
+ if (example.fineTuningPriority === "high")
384
+ return 3;
385
+ if (example.fineTuningPriority === "medium")
386
+ return 2;
387
+ return 1;
388
+ };
389
+ const compareQueryExpanderPriority = (left, right) => queryExpanderPriorityRank(right) - queryExpanderPriorityRank(left) ||
390
+ right.priorityScore - left.priorityScore ||
391
+ left.recordId.localeCompare(right.recordId);
392
+ const labelWeightForAccepted = (accepted) => Number(Math.max(0.1, accepted.priorityScore).toFixed(6));
393
+ const labelsFromAccepted = (sourceExamples) => sourceExamples.map((example) => ({
394
+ labelId: stableId("rerank-label", {
395
+ source: "accepted",
396
+ recordId: example.recordId,
397
+ queryHash: example.query.queryHash,
398
+ }),
399
+ source: "accepted_evidence",
400
+ label: "positive",
401
+ recordId: example.recordId,
402
+ queryHash: example.query.queryHash,
403
+ sourceHashes: example.sourceSelection.sourceHashes,
404
+ reasonCodes: ["accepted_evidence"],
405
+ weight: labelWeightForAccepted(example),
406
+ }));
407
+ const labelsFromRejected = (rejected) => rejected.map((item) => {
408
+ const reasonCodes = uniqueSorted(item.reasons.map((reason) => reason.code));
409
+ return {
410
+ labelId: stableId("rerank-label", {
411
+ source: "rejected",
412
+ recordId: item.recordId,
413
+ dedupeKey: item.dedupeKey,
414
+ reasonCodes,
415
+ }),
416
+ source: "rejected_evidence",
417
+ label: "negative",
418
+ ...(item.recordId ? { recordId: item.recordId } : {}),
419
+ ...(item.dedupeKey ? { dedupeKey: item.dedupeKey } : {}),
420
+ sourceHashes: item.dedupeKey ? [`sha256:${sha256Hex(item.dedupeKey)}`] : [],
421
+ reasonCodes,
422
+ weight: reasonCodes.includes("duplicate_lineage") ? 0.9 : 0.6,
423
+ };
424
+ });
425
+ const buildRerankLabels = (sourceExamples, rejected) => [
426
+ ...labelsFromAccepted(sourceExamples),
427
+ ...labelsFromRejected(rejected),
428
+ ].sort((left, right) => left.labelId.localeCompare(right.labelId));
429
+ const freshnessRegressionCases = (sourceExamples) => sourceExamples
430
+ .filter((example) => example.scorecard.freshness?.delta !== undefined ||
431
+ example.sourceSelection.sourceTypes.includes("fresh") ||
432
+ example.sourceSelection.sourceTypes.includes("recent"))
433
+ .map((example) => ({
434
+ caseId: stableId("freshness-regression", {
435
+ recordId: example.recordId,
436
+ queryHash: example.query.queryHash,
437
+ }),
438
+ kind: "freshness",
439
+ sourceRecordIds: [example.recordId],
440
+ assertion: "fresh_sources_rank_above_stale_or_unknown_sources",
441
+ queryHash: example.query.queryHash,
442
+ sourceHashes: example.sourceSelection.sourceHashes,
443
+ reasonCodes: ["freshness_scorecard"],
444
+ }));
445
+ const sourceSelectionRegressionCases = (sourceExamples) => sourceExamples
446
+ .filter((example) => example.sourceSelection.selectedCount > 0)
447
+ .map((example) => ({
448
+ caseId: stableId("source-selection-regression", {
449
+ recordId: example.recordId,
450
+ sourceHashes: example.sourceSelection.sourceHashes,
451
+ }),
452
+ kind: "source_selection",
453
+ sourceRecordIds: [example.recordId],
454
+ assertion: "selected_sources_must_support_query_intent",
455
+ queryHash: example.query.queryHash,
456
+ sourceHashes: example.sourceSelection.sourceHashes,
457
+ reasonCodes: ["accepted_source_selection"],
458
+ }));
459
+ const duplicateRegressionCases = (rejected) => rejected
460
+ .filter((item) => item.reasons.some((reason) => reason.code === "duplicate_lineage"))
461
+ .map((item) => ({
462
+ caseId: stableId("duplicate-regression", {
463
+ recordId: item.recordId,
464
+ dedupeKey: item.dedupeKey,
465
+ }),
466
+ kind: "duplicate_detection",
467
+ sourceRecordIds: item.recordId ? [item.recordId] : [],
468
+ assertion: "duplicate_lineage_candidates_must_not_increase_recall_or_rerank_weight",
469
+ sourceHashes: item.dedupeKey ? [`sha256:${sha256Hex(item.dedupeKey)}`] : [],
470
+ reasonCodes: uniqueSorted(item.reasons.map((reason) => reason.code)),
471
+ }));
472
+ const buildRegressionCases = (sourceExamples, rejected) => [
473
+ ...freshnessRegressionCases(sourceExamples),
474
+ ...sourceSelectionRegressionCases(sourceExamples),
475
+ ...duplicateRegressionCases(rejected),
476
+ ].sort((left, right) => left.caseId.localeCompare(right.caseId));
477
+ const buildDocdexEvalCommands = (queryExpanderEval, regressionCases, sourceExamples) => {
478
+ const requiresTenantScopedFixture = sourceExamples.some((example) => example.privacyScope.tenantScoped);
479
+ const regressionSuiteId = stableId("docdex-retrieval-regression-suite", {
480
+ querySuiteId: queryExpanderEval.suiteId,
481
+ regressionCaseIds: regressionCases.map((testCase) => testCase.caseId),
482
+ });
483
+ return [
484
+ {
485
+ commandId: stableId("docdex-eval-command", {
486
+ kind: "phase-26-run-tests",
487
+ suiteId: queryExpanderEval.suiteId,
488
+ regressionSuiteId,
489
+ }),
490
+ command: "docdexd",
491
+ args: [
492
+ "run-tests",
493
+ "--repo",
494
+ "<repo>",
495
+ "--target",
496
+ "packages/codali/src/improvement/__tests__/DocdexRetrievalCandidateBuilder.test.ts",
497
+ ],
498
+ dryRunSafe: true,
499
+ requiresTenantScopedFixture,
500
+ expectedScorecards: ["recall", "precision", "freshness"],
501
+ },
502
+ {
503
+ commandId: stableId("docdex-eval-command", {
504
+ kind: "phase-26-pre-commit",
505
+ suiteId: regressionSuiteId,
506
+ }),
507
+ command: "docdexd",
508
+ args: [
509
+ "hook",
510
+ "pre-commit",
511
+ "--repo",
512
+ "<repo>",
513
+ ],
514
+ dryRunSafe: true,
515
+ requiresTenantScopedFixture: false,
516
+ expectedScorecards: [],
517
+ },
518
+ ];
519
+ };
520
+ const blockedReasonsFor = (sourceExamples, rerankLabels, regressionCases) => uniqueSorted([
521
+ sourceExamples.length === 0 ? "no_accepted_retrieval_examples" : undefined,
522
+ sourceExamples.some((example) => example.query.variants.length > 0)
523
+ ? undefined
524
+ : "no_query_expander_variants",
525
+ rerankLabels.some((label) => label.label === "positive") &&
526
+ rerankLabels.some((label) => label.label === "negative")
527
+ ? undefined
528
+ : "accepted_and_rejected_rerank_labels_required",
529
+ regressionCases.some((testCase) => testCase.kind === "freshness")
530
+ ? undefined
531
+ : "freshness_regression_case_required",
532
+ regressionCases.some((testCase) => testCase.kind === "duplicate_detection")
533
+ ? undefined
534
+ : "duplicate_detection_regression_case_required",
535
+ regressionCases.some((testCase) => testCase.kind === "source_selection")
536
+ ? undefined
537
+ : "source_selection_regression_case_required",
538
+ ]);
539
+ const buildCandidateSummary = (inspection, sourceExamples, rerankLabels, regressionCases, blockedReasons) => ({
540
+ candidateId: stableId("docdex-retrieval-candidate", {
541
+ manifestId: inspection.manifest.manifestId,
542
+ sourceRecordIds: sourceExamples.map((example) => example.recordId),
543
+ rerankLabelIds: rerankLabels.map((label) => label.labelId),
544
+ regressionCaseIds: regressionCases.map((testCase) => testCase.caseId),
545
+ }),
546
+ candidateKind: "docdex_retrieval",
547
+ status: blockedReasons.length ? "blocked" : "proposed",
548
+ sourceExportIds: [inspection.manifest.manifestId],
549
+ sourceRecordIds: sourceExamples.map((example) => example.recordId),
550
+ artifactIds: inspection.manifest.artifactRefs.map((ref) => ref.refId),
551
+ exampleCount: sourceExamples.length,
552
+ objectBytes: inspection.manifest.artifactRefs.reduce((total, ref) => total + ref.byteSize, 0),
553
+ queryExpanderCaseCount: sourceExamples.length,
554
+ rerankLabelCount: rerankLabels.length,
555
+ regressionCaseCount: regressionCases.length,
556
+ blockedReasons: [...blockedReasons],
557
+ });
558
+ export const buildCodaliDocdexRetrievalCandidateBundle = ({ inspection, artifact = CODALI_DOCDEX_RETRIEVAL_PROPOSAL_ARTIFACT, }) => {
559
+ const sourceExamples = buildSourceExamples(inspection);
560
+ const queryExpanderEval = buildQueryExpanderEval(inspection, sourceExamples);
561
+ const rerankLabels = buildRerankLabels(sourceExamples, inspection.curationReport.rejected);
562
+ const regressionCases = buildRegressionCases(sourceExamples, inspection.curationReport.rejected);
563
+ const docdexEvalCommands = buildDocdexEvalCommands(queryExpanderEval, regressionCases, sourceExamples);
564
+ const blockedReasons = blockedReasonsFor(sourceExamples, rerankLabels, regressionCases);
565
+ return {
566
+ schemaVersion: CODALI_DOCDEX_RETRIEVAL_CANDIDATE_SCHEMA_VERSION,
567
+ artifact,
568
+ source: {
569
+ exportId: inspection.exportId,
570
+ manifestId: inspection.manifest.manifestId,
571
+ manifestPath: inspection.manifestPath,
572
+ exportKind: inspection.manifest.exportKind,
573
+ checksum: inspection.manifest.checksum,
574
+ recordCount: inspection.manifest.recordCount,
575
+ primaryArtifactRef: summarizeObjectRef(inspection.primaryArtifact?.ref ?? inspection.manifest.artifactRefs[0]),
576
+ },
577
+ generationPolicy: {
578
+ deterministic: true,
579
+ modifiesRuntimePrompts: false,
580
+ modifiesRuntimeCode: false,
581
+ bodyPolicy: "object_refs_and_hashes_only_for_private_sources",
582
+ tenantPrivateExamplesScoped: true,
583
+ uploadEnabled: false,
584
+ finalSynthesizerFineTuning: false,
585
+ },
586
+ expectedShape: {
587
+ schemaVersion: CODALI_DOCDEX_RETRIEVAL_CANDIDATE_SCHEMA_VERSION,
588
+ artifact,
589
+ requiredFields: [
590
+ "sourceExamples",
591
+ "queryExpanderEval",
592
+ "rerankLabels",
593
+ "regressionCases",
594
+ "docdexEvalCommands",
595
+ ],
596
+ sourceExampleRequiredFields: [
597
+ "recordId",
598
+ "privacyScope",
599
+ "query",
600
+ "sourceSelection",
601
+ "scorecard",
602
+ "objectRefs",
603
+ ],
604
+ evalCommandRequiredFields: [
605
+ "command",
606
+ "args",
607
+ "dryRunSafe",
608
+ "expectedScorecards",
609
+ ],
610
+ },
611
+ sourceExamples,
612
+ queryExpanderEval,
613
+ rerankLabels,
614
+ regressionCases,
615
+ docdexEvalCommands,
616
+ scorecardSummary: {
617
+ recallDelta: averageDelta(sourceExamples, "recall"),
618
+ precisionDelta: averageDelta(sourceExamples, "precision"),
619
+ freshnessDelta: averageDelta(sourceExamples, "freshness"),
620
+ },
621
+ candidates: [
622
+ buildCandidateSummary(inspection, sourceExamples, rerankLabels, regressionCases, blockedReasons),
623
+ ],
624
+ };
625
+ };