@mcoda/mswarm 0.1.90 → 0.1.94
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/codali-executor.d.ts +107 -6
- package/dist/codali-executor.d.ts.map +1 -1
- package/dist/codali-executor.js +500 -26
- package/dist/codali-executor.js.map +1 -1
- package/dist/runtime.d.ts +4 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +18 -5
- package/dist/runtime.js.map +1 -1
- package/dist/vendor/codali/cli/DatasetCommand.d.ts +38 -0
- package/dist/vendor/codali/cli/DatasetCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/DatasetCommand.js +959 -0
- package/dist/vendor/codali/cli/EvalCommand.d.ts +3 -0
- package/dist/vendor/codali/cli/EvalCommand.d.ts.map +1 -1
- package/dist/vendor/codali/cli/EvalCommand.js +55 -1
- package/dist/vendor/codali/cli/ImprovementCommand.d.ts +97 -0
- package/dist/vendor/codali/cli/ImprovementCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/ImprovementCommand.js +1879 -0
- package/dist/vendor/codali/cli.d.ts.map +1 -1
- package/dist/vendor/codali/cli.js +15 -0
- package/dist/vendor/codali/dataset-cli.d.ts +3 -0
- package/dist/vendor/codali/dataset-cli.d.ts.map +1 -0
- package/dist/vendor/codali/dataset-cli.js +30 -0
- package/dist/vendor/codali/eval/CodaliGatewayLiveHarness.d.ts +90 -1
- package/dist/vendor/codali/eval/CodaliGatewayLiveHarness.d.ts.map +1 -1
- package/dist/vendor/codali/eval/CodaliGatewayLiveHarness.js +443 -9
- package/dist/vendor/codali/eval/GatewayDatasetEval.d.ts +70 -0
- package/dist/vendor/codali/eval/GatewayDatasetEval.d.ts.map +1 -0
- package/dist/vendor/codali/eval/GatewayDatasetEval.js +477 -0
- package/dist/vendor/codali/eval/GatewayEvalSuite.d.ts +52 -0
- package/dist/vendor/codali/eval/GatewayEvalSuite.d.ts.map +1 -1
- package/dist/vendor/codali/eval/GatewayEvalSuite.js +135 -1
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts +4 -0
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGateway.js +37 -1
- package/dist/vendor/codali/gateway/CodaliGatewaySchemas.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGatewaySchemas.js +0 -4
- package/dist/vendor/codali/gateway/CodaliGatewayStore.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGatewayStore.js +1 -0
- package/dist/vendor/codali/gateway/GatewayPlanner.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/GatewayPlanner.js +355 -41
- package/dist/vendor/codali/gateway/GatewayStateMachine.d.ts +1 -0
- package/dist/vendor/codali/gateway/GatewayStateMachine.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/GatewayStateMachine.js +221 -5
- package/dist/vendor/codali/improvement/CandidateReleaseBuilder.d.ts +201 -0
- package/dist/vendor/codali/improvement/CandidateReleaseBuilder.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/CandidateReleaseBuilder.js +1175 -0
- package/dist/vendor/codali/improvement/DatasetEligibilityGate.d.ts +75 -0
- package/dist/vendor/codali/improvement/DatasetEligibilityGate.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/DatasetEligibilityGate.js +497 -0
- package/dist/vendor/codali/improvement/DatasetExportManifestReader.d.ts +88 -0
- package/dist/vendor/codali/improvement/DatasetExportManifestReader.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/DatasetExportManifestReader.js +333 -0
- package/dist/vendor/codali/improvement/DocdexRetrievalCandidateBuilder.d.ts +192 -0
- package/dist/vendor/codali/improvement/DocdexRetrievalCandidateBuilder.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/DocdexRetrievalCandidateBuilder.js +625 -0
- package/dist/vendor/codali/improvement/EvalReplayCandidateBuilder.d.ts +173 -0
- package/dist/vendor/codali/improvement/EvalReplayCandidateBuilder.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/EvalReplayCandidateBuilder.js +363 -0
- package/dist/vendor/codali/improvement/FineTuneJobPlanner.d.ts +281 -0
- package/dist/vendor/codali/improvement/FineTuneJobPlanner.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/FineTuneJobPlanner.js +823 -0
- package/dist/vendor/codali/improvement/ImprovementEvalRunner.d.ts +50 -0
- package/dist/vendor/codali/improvement/ImprovementEvalRunner.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/ImprovementEvalRunner.js +648 -0
- package/dist/vendor/codali/improvement/ImprovementPolicy.d.ts +272 -0
- package/dist/vendor/codali/improvement/ImprovementPolicy.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/ImprovementPolicy.js +1450 -0
- package/dist/vendor/codali/improvement/ModelRouterCandidateBuilder.d.ts +186 -0
- package/dist/vendor/codali/improvement/ModelRouterCandidateBuilder.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/ModelRouterCandidateBuilder.js +880 -0
- package/dist/vendor/codali/improvement/OperatorInspector.d.ts +182 -0
- package/dist/vendor/codali/improvement/OperatorInspector.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/OperatorInspector.js +604 -0
- package/dist/vendor/codali/improvement/ProductionGovernance.d.ts +91 -0
- package/dist/vendor/codali/improvement/ProductionGovernance.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/ProductionGovernance.js +329 -0
- package/dist/vendor/codali/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts +152 -0
- package/dist/vendor/codali/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/PromptSchemaToolMetadataCandidateBuilder.js +480 -0
- package/dist/vendor/codali/improvement/PublishOrchestrator.d.ts +113 -0
- package/dist/vendor/codali/improvement/PublishOrchestrator.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/PublishOrchestrator.js +590 -0
- package/dist/vendor/codali/improvement/ReleaseOutcomeReporter.d.ts +133 -0
- package/dist/vendor/codali/improvement/ReleaseOutcomeReporter.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/ReleaseOutcomeReporter.js +501 -0
- package/dist/vendor/codali/improvement/StorageServiceImprovementClient.d.ts +71 -0
- package/dist/vendor/codali/improvement/StorageServiceImprovementClient.d.ts.map +1 -0
- package/dist/vendor/codali/improvement/StorageServiceImprovementClient.js +245 -0
- package/dist/vendor/codali/index.d.ts +43 -0
- package/dist/vendor/codali/index.d.ts.map +1 -1
- package/dist/vendor/codali/index.js +21 -0
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts +0 -2
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/CodaliRuntime.js +0 -5
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.d.ts +138 -0
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -0
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.js +417 -0
- package/dist/vendor/codali/storage/CodaliFeedbackReviewIngestion.d.ts +50 -0
- package/dist/vendor/codali/storage/CodaliFeedbackReviewIngestion.d.ts.map +1 -0
- package/dist/vendor/codali/storage/CodaliFeedbackReviewIngestion.js +141 -0
- package/dist/vendor/codali/storage/CodaliStorageContracts.d.ts +337 -0
- package/dist/vendor/codali/storage/CodaliStorageContracts.d.ts.map +1 -0
- package/dist/vendor/codali/storage/CodaliStorageContracts.js +1555 -0
- package/dist/vendor/codali/storage/DatasetExportJob.d.ts +51 -0
- package/dist/vendor/codali/storage/DatasetExportJob.d.ts.map +1 -0
- package/dist/vendor/codali/storage/DatasetExportJob.js +358 -0
- package/dist/vendor/codali/storage/DatasetReviewQueue.d.ts +98 -0
- package/dist/vendor/codali/storage/DatasetReviewQueue.d.ts.map +1 -0
- package/dist/vendor/codali/storage/DatasetReviewQueue.js +426 -0
- package/dist/vendor/codali/storage/GatewayDatasetStore.d.ts +233 -0
- package/dist/vendor/codali/storage/GatewayDatasetStore.d.ts.map +1 -0
- package/dist/vendor/codali/storage/GatewayDatasetStore.js +2070 -0
- package/package.json +3 -3
|
@@ -0,0 +1,2070 @@
|
|
|
1
|
+
import { createHash, createHmac, randomUUID } from "node:crypto";
|
|
2
|
+
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { redactCodaliGatewaySecrets, } from "../gateway/CodaliGatewayStore.js";
|
|
6
|
+
import { CODALI_STORAGE_CONTRACT_SCHEMA_VERSION, validateCodaliStorageDatasetRecord, } from "./CodaliStorageContracts.js";
|
|
7
|
+
export const GATEWAY_DATASET_SERVICE_SIGNATURE_VERSION = "codali.storage.hmac.v1";
|
|
8
|
+
export const DEFAULT_GATEWAY_DATASET_SERVICE_ENDPOINT = "/v1/gateway/batches";
|
|
9
|
+
export class GatewayDatasetStoreError extends Error {
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(`${code}: ${message}`);
|
|
12
|
+
this.name = "GatewayDatasetStoreError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class GatewayDatasetServiceClientError extends Error {
|
|
17
|
+
constructor(input) {
|
|
18
|
+
super(`${input.code}: ${input.message}`);
|
|
19
|
+
this.name = "GatewayDatasetServiceClientError";
|
|
20
|
+
this.code = input.code;
|
|
21
|
+
this.status = input.status;
|
|
22
|
+
this.retryable = input.retryable ?? false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
26
|
+
const cloneJson = (value) => JSON.parse(JSON.stringify(value));
|
|
27
|
+
const sanitizeGatewayDatasetPayload = (value) => redactCodaliGatewaySecrets(value);
|
|
28
|
+
const sanitizeGatewayDatasetMetadata = (metadata) => {
|
|
29
|
+
if (!metadata)
|
|
30
|
+
return undefined;
|
|
31
|
+
const sanitized = sanitizeGatewayDatasetPayload(metadata);
|
|
32
|
+
return isRecord(sanitized) ? sanitized : undefined;
|
|
33
|
+
};
|
|
34
|
+
const normalizeCanonicalJson = (value) => {
|
|
35
|
+
if (value === undefined || value === null)
|
|
36
|
+
return null;
|
|
37
|
+
if (Array.isArray(value))
|
|
38
|
+
return value.map((item) => normalizeCanonicalJson(item));
|
|
39
|
+
if (isRecord(value)) {
|
|
40
|
+
return Object.keys(value)
|
|
41
|
+
.sort()
|
|
42
|
+
.reduce((output, key) => {
|
|
43
|
+
output[key] = normalizeCanonicalJson(value[key]);
|
|
44
|
+
return output;
|
|
45
|
+
}, {});
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
};
|
|
49
|
+
export const canonicalizeGatewayDatasetJson = (value) => JSON.stringify(normalizeCanonicalJson(value));
|
|
50
|
+
const sha256Hex = (value) => createHash("sha256").update(value).digest("hex");
|
|
51
|
+
export const hashGatewayDatasetRequestBody = (body) => sha256Hex(canonicalizeGatewayDatasetJson(body));
|
|
52
|
+
export const hashGatewayDatasetValue = (value) => `sha256:${hashGatewayDatasetRequestBody(value)}`;
|
|
53
|
+
const normalizeObjectPayloadBody = (payload, mimeType) => {
|
|
54
|
+
if (typeof payload === "string" && mimeType === "application/x-ndjson") {
|
|
55
|
+
return payload.endsWith("\n") ? payload : `${payload}\n`;
|
|
56
|
+
}
|
|
57
|
+
return `${canonicalizeGatewayDatasetJson(payload)}\n`;
|
|
58
|
+
};
|
|
59
|
+
const objectContentHash = (payload, mimeType) => sha256Hex(normalizeObjectPayloadBody(payload, mimeType));
|
|
60
|
+
const objectByteSize = (payload, mimeType) => Buffer.byteLength(normalizeObjectPayloadBody(payload, mimeType), "utf8");
|
|
61
|
+
const putGatewayDatasetObject = (objectStore, input) => objectStore.putObject({
|
|
62
|
+
...input,
|
|
63
|
+
payload: sanitizeGatewayDatasetPayload(input.payload),
|
|
64
|
+
metadata: sanitizeGatewayDatasetMetadata(input.metadata),
|
|
65
|
+
});
|
|
66
|
+
export const createGatewayDatasetLocalOnlyPrivacy = (overrides = {}) => {
|
|
67
|
+
const { metadata, policyTags, ...rest } = overrides;
|
|
68
|
+
return {
|
|
69
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
70
|
+
classification: "internal",
|
|
71
|
+
containsPersonalData: false,
|
|
72
|
+
redactionStatus: "not_required",
|
|
73
|
+
uploadAllowed: false,
|
|
74
|
+
exportAllowed: false,
|
|
75
|
+
trainingAllowed: false,
|
|
76
|
+
policyTags: policyTags ?? ["local_only"],
|
|
77
|
+
metadata: {
|
|
78
|
+
collectionMode: "local_only",
|
|
79
|
+
...(metadata ?? {}),
|
|
80
|
+
},
|
|
81
|
+
...rest,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
export const createGatewayDatasetLocalOnlyObjectPrivacyFlags = (overrides = {}) => ({
|
|
85
|
+
containsPersonalData: false,
|
|
86
|
+
containsSecrets: false,
|
|
87
|
+
containsTenantPrivateData: true,
|
|
88
|
+
containsSourceCode: false,
|
|
89
|
+
containsCustomerData: true,
|
|
90
|
+
trainingAllowed: false,
|
|
91
|
+
evalAllowed: true,
|
|
92
|
+
replayAllowed: true,
|
|
93
|
+
exportAllowed: false,
|
|
94
|
+
...overrides,
|
|
95
|
+
});
|
|
96
|
+
export const gatewayDatasetScopeFromGatewayResult = (request, result, overrides = {}) => ({
|
|
97
|
+
tenantId: overrides.tenantId ?? request.tenant?.id ?? request.tenant?.slug ?? "local",
|
|
98
|
+
productId: overrides.productId ?? request.product?.name ?? "codali",
|
|
99
|
+
deploymentId: overrides.deploymentId ??
|
|
100
|
+
request.product?.version ??
|
|
101
|
+
request.product?.surface ??
|
|
102
|
+
"local",
|
|
103
|
+
runId: overrides.runId ?? result.runId,
|
|
104
|
+
});
|
|
105
|
+
export const buildGatewayDatasetIdempotencyKey = (scope, records) => `gateway-dataset:${hashGatewayDatasetRequestBody({
|
|
106
|
+
scope,
|
|
107
|
+
records: records.map((record) => ({
|
|
108
|
+
recordId: record.recordId,
|
|
109
|
+
datasetKind: record.datasetKind,
|
|
110
|
+
inputHash: record.inputRef.contentHash,
|
|
111
|
+
outputHash: record.outputRef?.contentHash,
|
|
112
|
+
evidenceHashes: record.evidenceRefs?.map((ref) => ref.contentHash) ?? [],
|
|
113
|
+
})),
|
|
114
|
+
})}`;
|
|
115
|
+
export const buildGatewayDatasetServiceSignatureHeaders = (input) => {
|
|
116
|
+
const bodyHash = hashGatewayDatasetRequestBody(input.body);
|
|
117
|
+
const canonicalPayload = [
|
|
118
|
+
GATEWAY_DATASET_SERVICE_SIGNATURE_VERSION,
|
|
119
|
+
input.scope.tenantId,
|
|
120
|
+
input.scope.productId,
|
|
121
|
+
input.scope.deploymentId,
|
|
122
|
+
input.scope.runId,
|
|
123
|
+
input.timestamp,
|
|
124
|
+
input.nonce,
|
|
125
|
+
bodyHash,
|
|
126
|
+
].join("\n");
|
|
127
|
+
const signature = createHmac("sha256", input.hmacSecret)
|
|
128
|
+
.update(canonicalPayload)
|
|
129
|
+
.digest("hex");
|
|
130
|
+
return {
|
|
131
|
+
"x-codali-storage-timestamp": input.timestamp,
|
|
132
|
+
"x-codali-storage-nonce": input.nonce,
|
|
133
|
+
"x-codali-storage-body-sha256": bodyHash,
|
|
134
|
+
"x-codali-storage-signature": signature,
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
const validateDatasetRecordOrThrow = (record) => {
|
|
138
|
+
const validation = validateCodaliStorageDatasetRecord(record);
|
|
139
|
+
if (!validation.ok) {
|
|
140
|
+
throw new GatewayDatasetStoreError("GATEWAY_DATASET_RECORD_INVALID", validation.issues
|
|
141
|
+
.map((issue) => `${issue.path}:${issue.code}:${issue.message}`)
|
|
142
|
+
.join("; "));
|
|
143
|
+
}
|
|
144
|
+
return validation.value;
|
|
145
|
+
};
|
|
146
|
+
const validateDatasetRecords = (records) => records.map((record) => validateDatasetRecordOrThrow(record));
|
|
147
|
+
const buildObjectRef = (input) => {
|
|
148
|
+
const mimeType = input.mimeType ?? "application/json";
|
|
149
|
+
const bodyHash = objectContentHash(input.payload, mimeType);
|
|
150
|
+
const tenantHash = hashGatewayDatasetValue(input.scope.tenantId);
|
|
151
|
+
const ownerScope = {
|
|
152
|
+
tenantHash,
|
|
153
|
+
productId: input.scope.productId,
|
|
154
|
+
deploymentId: input.scope.deploymentId,
|
|
155
|
+
runId: input.scope.runId,
|
|
156
|
+
ownerType: input.ownerType,
|
|
157
|
+
ownerId: input.ownerId,
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
161
|
+
refId: input.refId,
|
|
162
|
+
kind: input.kind ?? "payload",
|
|
163
|
+
uri: input.uri,
|
|
164
|
+
contentHash: `sha256:${bodyHash}`,
|
|
165
|
+
byteSize: objectByteSize(input.payload, mimeType),
|
|
166
|
+
mimeType,
|
|
167
|
+
privacyFlags: createGatewayDatasetLocalOnlyObjectPrivacyFlags(input.privacyFlags),
|
|
168
|
+
ownerScope,
|
|
169
|
+
ownerScopeHash: hashGatewayDatasetValue(ownerScope),
|
|
170
|
+
deletionGroupId: `gateway-dataset-${input.scope.runId}`,
|
|
171
|
+
retentionClass: input.retentionClass ?? "dataset",
|
|
172
|
+
mediaType: mimeType === "application/x-ndjson" ? "jsonl" : "json",
|
|
173
|
+
sizeBytes: objectByteSize(input.payload, mimeType),
|
|
174
|
+
sha256: bodyHash,
|
|
175
|
+
createdAt: (input.now ?? (() => new Date()))().toISOString(),
|
|
176
|
+
metadata: input.metadata,
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
export class InMemoryGatewayDatasetObjectStore {
|
|
180
|
+
constructor(options = {}) {
|
|
181
|
+
this.options = options;
|
|
182
|
+
this.objects = new Map();
|
|
183
|
+
}
|
|
184
|
+
async putObject(input) {
|
|
185
|
+
const refId = `${input.scope.runId}-${input.ownerType}-${randomUUID()}`;
|
|
186
|
+
const ref = buildObjectRef({
|
|
187
|
+
...input,
|
|
188
|
+
refId,
|
|
189
|
+
uri: `memory://codali-dataset/objects/${refId}.json`,
|
|
190
|
+
now: this.options.now,
|
|
191
|
+
});
|
|
192
|
+
this.objects.set(ref.refId, { ref, payload: cloneJson(input.payload) });
|
|
193
|
+
return cloneJson(ref);
|
|
194
|
+
}
|
|
195
|
+
async readObject(ref) {
|
|
196
|
+
const object = this.objects.get(ref.refId);
|
|
197
|
+
return object ? cloneJson(object.payload) : undefined;
|
|
198
|
+
}
|
|
199
|
+
listObjects() {
|
|
200
|
+
return Array.from(this.objects.values()).map((object) => cloneJson(object.ref));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export const createInMemoryGatewayDatasetObjectStore = (options = {}) => new InMemoryGatewayDatasetObjectStore(options);
|
|
204
|
+
export class InMemoryGatewayDatasetStore {
|
|
205
|
+
constructor() {
|
|
206
|
+
this.records = new Map();
|
|
207
|
+
this.batches = [];
|
|
208
|
+
this.idempotency = new Map();
|
|
209
|
+
}
|
|
210
|
+
async collect(input) {
|
|
211
|
+
const records = validateDatasetRecords(input.records);
|
|
212
|
+
if (records.length === 0) {
|
|
213
|
+
return { accepted: true, status: "skipped", recordCount: 0 };
|
|
214
|
+
}
|
|
215
|
+
const idempotencyKey = input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(input.scope, records);
|
|
216
|
+
const replay = this.idempotency.get(idempotencyKey);
|
|
217
|
+
if (replay) {
|
|
218
|
+
return { ...cloneJson(replay), replayed: true };
|
|
219
|
+
}
|
|
220
|
+
for (const record of records) {
|
|
221
|
+
this.records.set(record.recordId, cloneJson(record));
|
|
222
|
+
}
|
|
223
|
+
const batchId = `memory-${hashGatewayDatasetRequestBody({
|
|
224
|
+
idempotencyKey,
|
|
225
|
+
recordIds: records.map((record) => record.recordId),
|
|
226
|
+
})}`;
|
|
227
|
+
this.batches.push({
|
|
228
|
+
idempotencyKey,
|
|
229
|
+
scope: cloneJson(input.scope),
|
|
230
|
+
recordIds: records.map((record) => record.recordId),
|
|
231
|
+
metadata: input.metadata ? cloneJson(input.metadata) : undefined,
|
|
232
|
+
});
|
|
233
|
+
const result = {
|
|
234
|
+
accepted: true,
|
|
235
|
+
status: "stored",
|
|
236
|
+
recordCount: records.length,
|
|
237
|
+
idempotencyKey,
|
|
238
|
+
batchId,
|
|
239
|
+
};
|
|
240
|
+
this.idempotency.set(idempotencyKey, cloneJson(result));
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
listRecords() {
|
|
244
|
+
return Array.from(this.records.values()).map((record) => cloneJson(record));
|
|
245
|
+
}
|
|
246
|
+
readRecord(recordId) {
|
|
247
|
+
const record = this.records.get(recordId);
|
|
248
|
+
return record ? cloneJson(record) : undefined;
|
|
249
|
+
}
|
|
250
|
+
listBatches() {
|
|
251
|
+
return cloneJson(this.batches);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
export const createInMemoryGatewayDatasetStore = () => new InMemoryGatewayDatasetStore();
|
|
255
|
+
export class LocalJsonlGatewayDatasetObjectStore {
|
|
256
|
+
constructor(options) {
|
|
257
|
+
this.options = options;
|
|
258
|
+
}
|
|
259
|
+
async putObject(input) {
|
|
260
|
+
await mkdir(this.options.directory, { recursive: true });
|
|
261
|
+
const refId = `${input.scope.runId}-${input.ownerType}-${randomUUID()}`;
|
|
262
|
+
const filePath = path.join(this.options.directory, `${refId}.json`);
|
|
263
|
+
await writeFile(filePath, normalizeObjectPayloadBody(input.payload, input.mimeType), "utf8");
|
|
264
|
+
return buildObjectRef({
|
|
265
|
+
...input,
|
|
266
|
+
refId,
|
|
267
|
+
uri: pathToFileURL(filePath).href,
|
|
268
|
+
now: this.options.now,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
async readObject(ref) {
|
|
272
|
+
if (!ref.uri?.startsWith("file://"))
|
|
273
|
+
return undefined;
|
|
274
|
+
const raw = await readFile(fileURLToPath(ref.uri), "utf8");
|
|
275
|
+
try {
|
|
276
|
+
return JSON.parse(raw);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return raw;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
export const createLocalJsonlGatewayDatasetObjectStore = (options) => new LocalJsonlGatewayDatasetObjectStore(options);
|
|
284
|
+
export class LocalJsonlGatewayDatasetStore {
|
|
285
|
+
constructor(options) {
|
|
286
|
+
this.options = options;
|
|
287
|
+
this.idempotency = new Map();
|
|
288
|
+
}
|
|
289
|
+
async collect(input) {
|
|
290
|
+
const records = validateDatasetRecords(input.records);
|
|
291
|
+
if (records.length === 0) {
|
|
292
|
+
return { accepted: true, status: "skipped", recordCount: 0 };
|
|
293
|
+
}
|
|
294
|
+
const idempotencyKey = input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(input.scope, records);
|
|
295
|
+
const replay = this.idempotency.get(idempotencyKey);
|
|
296
|
+
if (replay) {
|
|
297
|
+
return { ...cloneJson(replay), replayed: true };
|
|
298
|
+
}
|
|
299
|
+
await mkdir(this.options.directory, { recursive: true });
|
|
300
|
+
const recordsPath = path.join(this.options.directory, this.options.recordsFileName ?? "records.jsonl");
|
|
301
|
+
const body = {
|
|
302
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
303
|
+
collectedAt: (this.options.now ?? (() => new Date()))().toISOString(),
|
|
304
|
+
scope: input.scope,
|
|
305
|
+
idempotencyKey,
|
|
306
|
+
records,
|
|
307
|
+
metadata: input.metadata,
|
|
308
|
+
};
|
|
309
|
+
await appendFile(recordsPath, `${JSON.stringify(body)}\n`, "utf8");
|
|
310
|
+
const result = {
|
|
311
|
+
accepted: true,
|
|
312
|
+
status: "stored",
|
|
313
|
+
recordCount: records.length,
|
|
314
|
+
idempotencyKey,
|
|
315
|
+
batchId: `jsonl-${hashGatewayDatasetRequestBody(body)}`,
|
|
316
|
+
metadata: {
|
|
317
|
+
recordsPath,
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
this.idempotency.set(idempotencyKey, cloneJson(result));
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
export const createLocalJsonlGatewayDatasetStore = (options) => new LocalJsonlGatewayDatasetStore(options);
|
|
325
|
+
const chunkRecords = (records, size) => {
|
|
326
|
+
const chunks = [];
|
|
327
|
+
for (let index = 0; index < records.length; index += size) {
|
|
328
|
+
chunks.push(records.slice(index, index + size));
|
|
329
|
+
}
|
|
330
|
+
return chunks;
|
|
331
|
+
};
|
|
332
|
+
const positiveInteger = (value, fallback) => Number.isFinite(value) && value !== undefined && value > 0
|
|
333
|
+
? Math.floor(value)
|
|
334
|
+
: fallback;
|
|
335
|
+
const sleep = (ms) => new Promise((resolve) => {
|
|
336
|
+
setTimeout(resolve, ms);
|
|
337
|
+
});
|
|
338
|
+
const endpointUrl = (baseUrl, endpointPath) => `${baseUrl.replace(/\/+$/, "")}/${endpointPath.replace(/^\/+/, "")}`;
|
|
339
|
+
const parseResponseBody = async (response) => {
|
|
340
|
+
const raw = await response.text();
|
|
341
|
+
if (!raw.trim())
|
|
342
|
+
return undefined;
|
|
343
|
+
try {
|
|
344
|
+
return JSON.parse(raw);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return raw;
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
const retryableStatus = (status) => status === 408 || status === 425 || status === 429 || status >= 500;
|
|
351
|
+
const errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
352
|
+
export class GatewayDatasetServiceClient {
|
|
353
|
+
constructor(options) {
|
|
354
|
+
this.options = options;
|
|
355
|
+
const defaultFetch = globalThis.fetch?.bind(globalThis);
|
|
356
|
+
const fetchImpl = options.fetch ?? defaultFetch;
|
|
357
|
+
if (!fetchImpl) {
|
|
358
|
+
throw new GatewayDatasetServiceClientError({
|
|
359
|
+
code: "GATEWAY_DATASET_FETCH_UNAVAILABLE",
|
|
360
|
+
message: "No fetch implementation is available for GatewayDatasetServiceClient.",
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
this.fetchImpl = fetchImpl;
|
|
364
|
+
}
|
|
365
|
+
async collect(input) {
|
|
366
|
+
const records = validateDatasetRecords(input.records);
|
|
367
|
+
if (records.length === 0) {
|
|
368
|
+
return { accepted: true, status: "skipped", recordCount: 0 };
|
|
369
|
+
}
|
|
370
|
+
const idempotencyKey = input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(input.scope, records);
|
|
371
|
+
const batchSize = positiveInteger(this.options.batchSize, 25);
|
|
372
|
+
const batches = chunkRecords(records, batchSize);
|
|
373
|
+
const responses = [];
|
|
374
|
+
try {
|
|
375
|
+
for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
|
|
376
|
+
const batchRecords = batches[batchIndex] ?? [];
|
|
377
|
+
const batchIdempotencyKey = batches.length === 1 ? idempotencyKey : `${idempotencyKey}:${batchIndex + 1}`;
|
|
378
|
+
responses.push(await this.writeBatch({
|
|
379
|
+
scope: input.scope,
|
|
380
|
+
records: batchRecords,
|
|
381
|
+
idempotencyKey: batchIdempotencyKey,
|
|
382
|
+
batchIndex,
|
|
383
|
+
batchCount: batches.length,
|
|
384
|
+
metadata: input.metadata,
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
accepted: true,
|
|
389
|
+
status: "stored",
|
|
390
|
+
recordCount: records.length,
|
|
391
|
+
idempotencyKey,
|
|
392
|
+
batchId: idempotencyKey,
|
|
393
|
+
metadata: {
|
|
394
|
+
batchCount: batches.length,
|
|
395
|
+
responses,
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
if (this.options.fallbackStore) {
|
|
401
|
+
const fallback = await this.options.fallbackStore.collect({
|
|
402
|
+
...input,
|
|
403
|
+
records,
|
|
404
|
+
idempotencyKey,
|
|
405
|
+
});
|
|
406
|
+
return {
|
|
407
|
+
...fallback,
|
|
408
|
+
fallbackUsed: true,
|
|
409
|
+
errors: [...(fallback.errors ?? []), errorMessage(error)],
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
throw error;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
async writeBatch(input) {
|
|
416
|
+
const endpoint = endpointUrl(this.options.baseUrl, this.options.endpointPath ?? DEFAULT_GATEWAY_DATASET_SERVICE_ENDPOINT);
|
|
417
|
+
const maxRetries = Math.max(0, Math.floor(this.options.maxRetries ?? 2));
|
|
418
|
+
const retryBaseMs = Math.max(0, Math.floor(this.options.retryBaseMs ?? 100));
|
|
419
|
+
let lastError;
|
|
420
|
+
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
|
|
421
|
+
const body = {
|
|
422
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
423
|
+
scope: input.scope,
|
|
424
|
+
records: input.records,
|
|
425
|
+
metadata: {
|
|
426
|
+
...(input.metadata ?? {}),
|
|
427
|
+
batchIndex: input.batchIndex,
|
|
428
|
+
batchCount: input.batchCount,
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
const timestamp = (this.options.now ?? (() => new Date()))().toISOString();
|
|
432
|
+
const nonce = this.options.nonceFactory?.() ?? randomUUID();
|
|
433
|
+
const bodyText = JSON.stringify(body);
|
|
434
|
+
const headers = {
|
|
435
|
+
authorization: `Bearer ${this.options.serviceToken}`,
|
|
436
|
+
"content-type": "application/json",
|
|
437
|
+
"x-codali-storage-tenant": input.scope.tenantId,
|
|
438
|
+
"x-codali-storage-product": input.scope.productId,
|
|
439
|
+
"x-codali-storage-deployment": input.scope.deploymentId,
|
|
440
|
+
"x-codali-storage-run": input.scope.runId,
|
|
441
|
+
"x-codali-storage-idempotency-key": input.idempotencyKey,
|
|
442
|
+
...buildGatewayDatasetServiceSignatureHeaders({
|
|
443
|
+
scope: input.scope,
|
|
444
|
+
body,
|
|
445
|
+
hmacSecret: this.options.hmacSecret ?? this.options.serviceToken,
|
|
446
|
+
timestamp,
|
|
447
|
+
nonce,
|
|
448
|
+
}),
|
|
449
|
+
};
|
|
450
|
+
try {
|
|
451
|
+
const response = await this.fetchWithTimeout(endpoint, {
|
|
452
|
+
method: "POST",
|
|
453
|
+
headers,
|
|
454
|
+
body: bodyText,
|
|
455
|
+
});
|
|
456
|
+
const parsed = await parseResponseBody(response);
|
|
457
|
+
if (response.ok) {
|
|
458
|
+
return parsed;
|
|
459
|
+
}
|
|
460
|
+
const retryable = retryableStatus(response.status);
|
|
461
|
+
lastError = new GatewayDatasetServiceClientError({
|
|
462
|
+
code: "GATEWAY_DATASET_SERVICE_WRITE_FAILED",
|
|
463
|
+
message: `Storage service returned ${response.status}${response.statusText ? ` ${response.statusText}` : ""}.`,
|
|
464
|
+
status: response.status,
|
|
465
|
+
retryable,
|
|
466
|
+
});
|
|
467
|
+
if (!retryable || attempt >= maxRetries) {
|
|
468
|
+
throw lastError;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
const retryable = error instanceof GatewayDatasetServiceClientError ? error.retryable : true;
|
|
473
|
+
lastError = error;
|
|
474
|
+
if (!retryable || attempt >= maxRetries) {
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (retryBaseMs > 0) {
|
|
479
|
+
await sleep(retryBaseMs * (attempt + 1));
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
throw lastError instanceof Error
|
|
483
|
+
? lastError
|
|
484
|
+
: new GatewayDatasetServiceClientError({
|
|
485
|
+
code: "GATEWAY_DATASET_SERVICE_WRITE_FAILED",
|
|
486
|
+
message: "Storage service write failed without an error object.",
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
async fetchWithTimeout(url, request) {
|
|
490
|
+
const timeoutMs = Math.max(0, Math.floor(this.options.timeoutMs ?? 5000));
|
|
491
|
+
if (timeoutMs <= 0) {
|
|
492
|
+
return this.fetchImpl(url, request);
|
|
493
|
+
}
|
|
494
|
+
const controller = new AbortController();
|
|
495
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
496
|
+
try {
|
|
497
|
+
return await this.fetchImpl(url, { ...request, signal: controller.signal });
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
clearTimeout(timeout);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
export const createGatewayDatasetServiceClient = (options) => new GatewayDatasetServiceClient(options);
|
|
505
|
+
const confidenceScore = (confidence) => {
|
|
506
|
+
if (confidence === "high")
|
|
507
|
+
return 0.95;
|
|
508
|
+
if (confidence === "medium")
|
|
509
|
+
return 0.7;
|
|
510
|
+
return 0.4;
|
|
511
|
+
};
|
|
512
|
+
const contextPackSummary = (contextPack) => contextPack
|
|
513
|
+
? {
|
|
514
|
+
contextPackId: contextPack.id,
|
|
515
|
+
tokenEstimate: contextPack.tokenEstimate,
|
|
516
|
+
decisionFactCount: contextPack.decisionFacts.length,
|
|
517
|
+
contradictionCount: contextPack.contradictions.length,
|
|
518
|
+
missingInformationCount: contextPack.missingInformation.length,
|
|
519
|
+
}
|
|
520
|
+
: undefined;
|
|
521
|
+
const safeDatasetIdPart = (value) => {
|
|
522
|
+
const normalized = value
|
|
523
|
+
.trim()
|
|
524
|
+
.replace(/[^A-Za-z0-9._:-]+/g, "-")
|
|
525
|
+
.replace(/^-+|-+$/g, "")
|
|
526
|
+
.slice(0, 96);
|
|
527
|
+
return normalized || hashGatewayDatasetRequestBody(value).slice(0, 16);
|
|
528
|
+
};
|
|
529
|
+
const labelToken = (value) => value
|
|
530
|
+
.trim()
|
|
531
|
+
.toLowerCase()
|
|
532
|
+
.replace(/[^a-z0-9._:-]+/g, "_")
|
|
533
|
+
.replace(/^_+|_+$/g, "");
|
|
534
|
+
const uniqueLabels = (values) => {
|
|
535
|
+
const seen = new Set();
|
|
536
|
+
const output = [];
|
|
537
|
+
for (const value of values) {
|
|
538
|
+
if (!value)
|
|
539
|
+
continue;
|
|
540
|
+
const label = labelToken(value);
|
|
541
|
+
if (!label || seen.has(label))
|
|
542
|
+
continue;
|
|
543
|
+
seen.add(label);
|
|
544
|
+
output.push(label);
|
|
545
|
+
}
|
|
546
|
+
return output;
|
|
547
|
+
};
|
|
548
|
+
const uniqueStrings = (values) => {
|
|
549
|
+
const seen = new Set();
|
|
550
|
+
const output = [];
|
|
551
|
+
for (const value of values) {
|
|
552
|
+
if (!value || seen.has(value))
|
|
553
|
+
continue;
|
|
554
|
+
seen.add(value);
|
|
555
|
+
output.push(value);
|
|
556
|
+
}
|
|
557
|
+
return output;
|
|
558
|
+
};
|
|
559
|
+
const metadataNumber = (metadata, key) => {
|
|
560
|
+
const value = metadata?.[key];
|
|
561
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
562
|
+
};
|
|
563
|
+
const metadataRecord = (metadata, key) => {
|
|
564
|
+
const value = metadata?.[key];
|
|
565
|
+
return isRecord(value) ? value : undefined;
|
|
566
|
+
};
|
|
567
|
+
const modelCallRepairAttempts = (call) => metadataNumber(call.metadata, "repairAttempts")
|
|
568
|
+
?? metadataNumber(call.metadata, "repair_attempts")
|
|
569
|
+
?? 0;
|
|
570
|
+
const schemaFailureText = (value) => typeof value === "string" && /schema|json|parse|validation|invalid/i.test(value);
|
|
571
|
+
const isSchemaFailureModelCall = (call) => {
|
|
572
|
+
if (call.status === "repaired" || modelCallRepairAttempts(call) > 0)
|
|
573
|
+
return true;
|
|
574
|
+
if (schemaFailureText(call.errorCode) || schemaFailureText(call.errorMessage))
|
|
575
|
+
return true;
|
|
576
|
+
return call.status === "failed" && schemaFailureText(JSON.stringify(call.metadata ?? {}));
|
|
577
|
+
};
|
|
578
|
+
const modelRecordId = (runId, call, index) => `dataset-${runId}-model-${safeDatasetIdPart(call.id || `${index + 1}-${call.role}`)}`;
|
|
579
|
+
const schemaFailureRecordId = (runId, call, index) => `dataset-${runId}-schema-failure-${safeDatasetIdPart(call.id || `${index + 1}-${call.role}`)}`;
|
|
580
|
+
const normalizeStoredModelCalls = (trace, result) => {
|
|
581
|
+
if (trace?.modelCalls.length) {
|
|
582
|
+
return trace.modelCalls.map((call, index) => ({
|
|
583
|
+
...call,
|
|
584
|
+
id: call.id || `model-${index + 1}`,
|
|
585
|
+
runId: call.runId || result.runId,
|
|
586
|
+
status: call.status,
|
|
587
|
+
}));
|
|
588
|
+
}
|
|
589
|
+
return result.trace.modelCalls.map((call, index) => ({
|
|
590
|
+
id: `trace-model-${index + 1}`,
|
|
591
|
+
runId: result.runId,
|
|
592
|
+
role: call.role,
|
|
593
|
+
status: call.status,
|
|
594
|
+
latencyMs: call.latencyMs,
|
|
595
|
+
agentSlug: call.agentSlug,
|
|
596
|
+
model: call.model,
|
|
597
|
+
provider: call.provider,
|
|
598
|
+
errorCode: call.errorCode,
|
|
599
|
+
metadata: {
|
|
600
|
+
tier: call.tier,
|
|
601
|
+
promptTokens: call.promptTokens,
|
|
602
|
+
completionTokens: call.completionTokens,
|
|
603
|
+
},
|
|
604
|
+
}));
|
|
605
|
+
};
|
|
606
|
+
const nextCorrectedModelCall = (call, index, calls) => {
|
|
607
|
+
if (call.status === "repaired")
|
|
608
|
+
return call;
|
|
609
|
+
return calls.slice(index + 1).find((candidate) => candidate.role === call.role
|
|
610
|
+
&& (candidate.status === "success" || candidate.status === "repaired"));
|
|
611
|
+
};
|
|
612
|
+
const modelCallScore = (call) => {
|
|
613
|
+
if (call.status === "success")
|
|
614
|
+
return 0.55;
|
|
615
|
+
if (call.status === "repaired")
|
|
616
|
+
return 0.45;
|
|
617
|
+
if (call.status === "failed")
|
|
618
|
+
return 0.1;
|
|
619
|
+
return 0;
|
|
620
|
+
};
|
|
621
|
+
const arrayOfStrings = (value) => Array.isArray(value)
|
|
622
|
+
? value.filter((item) => typeof item === "string")
|
|
623
|
+
: undefined;
|
|
624
|
+
const normalizeGoldTarget = (value) => {
|
|
625
|
+
if (!isRecord(value))
|
|
626
|
+
return undefined;
|
|
627
|
+
const kind = value.kind;
|
|
628
|
+
if (kind !== "accepted" && kind !== "corrected" && kind !== "reviewed")
|
|
629
|
+
return undefined;
|
|
630
|
+
return {
|
|
631
|
+
kind,
|
|
632
|
+
id: typeof value.id === "string" ? value.id : undefined,
|
|
633
|
+
input: value.input,
|
|
634
|
+
target: value.target ?? value.output,
|
|
635
|
+
sourceRecordId: typeof value.sourceRecordId === "string"
|
|
636
|
+
? value.sourceRecordId
|
|
637
|
+
: typeof value.source_record_id === "string"
|
|
638
|
+
? value.source_record_id
|
|
639
|
+
: undefined,
|
|
640
|
+
sourceModelCallId: typeof value.sourceModelCallId === "string"
|
|
641
|
+
? value.sourceModelCallId
|
|
642
|
+
: typeof value.source_model_call_id === "string"
|
|
643
|
+
? value.source_model_call_id
|
|
644
|
+
: undefined,
|
|
645
|
+
failedAttemptRecordId: typeof value.failedAttemptRecordId === "string"
|
|
646
|
+
? value.failedAttemptRecordId
|
|
647
|
+
: typeof value.failed_attempt_record_id === "string"
|
|
648
|
+
? value.failed_attempt_record_id
|
|
649
|
+
: undefined,
|
|
650
|
+
failedAttemptModelCallId: typeof value.failedAttemptModelCallId === "string"
|
|
651
|
+
? value.failedAttemptModelCallId
|
|
652
|
+
: typeof value.failed_attempt_model_call_id === "string"
|
|
653
|
+
? value.failed_attempt_model_call_id
|
|
654
|
+
: undefined,
|
|
655
|
+
reviewerId: typeof value.reviewerId === "string"
|
|
656
|
+
? value.reviewerId
|
|
657
|
+
: typeof value.reviewer_id === "string"
|
|
658
|
+
? value.reviewer_id
|
|
659
|
+
: undefined,
|
|
660
|
+
reasons: arrayOfStrings(value.reasons),
|
|
661
|
+
score: typeof value.score === "number" && Number.isFinite(value.score) ? value.score : undefined,
|
|
662
|
+
labels: arrayOfStrings(value.labels),
|
|
663
|
+
metadata: isRecord(value.metadata) ? value.metadata : undefined,
|
|
664
|
+
};
|
|
665
|
+
};
|
|
666
|
+
const goldTargetsFromMetadata = (metadata) => {
|
|
667
|
+
const raw = metadata?.goldTargets ?? metadata?.gold_targets;
|
|
668
|
+
if (!Array.isArray(raw))
|
|
669
|
+
return [];
|
|
670
|
+
return raw.flatMap((item) => {
|
|
671
|
+
const target = normalizeGoldTarget(item);
|
|
672
|
+
return target ? [target] : [];
|
|
673
|
+
});
|
|
674
|
+
};
|
|
675
|
+
const collectGatewayRunLabels = (result) => uniqueLabels([
|
|
676
|
+
"auto:gateway_run",
|
|
677
|
+
`auto:status:${result.status}`,
|
|
678
|
+
`auto:confidence:${result.confidence}`,
|
|
679
|
+
result.finalModel?.tier ? `auto:final_tier:${result.finalModel.tier}` : undefined,
|
|
680
|
+
]);
|
|
681
|
+
const collectModelCallLabels = (call) => uniqueLabels([
|
|
682
|
+
"auto:model_call",
|
|
683
|
+
`auto:role:${call.role}`,
|
|
684
|
+
`auto:status:${call.status}`,
|
|
685
|
+
call.provider ? `auto:provider:${call.provider}` : undefined,
|
|
686
|
+
call.model ? "auto:model_present" : undefined,
|
|
687
|
+
modelCallRepairAttempts(call) > 0 ? "auto:repaired_after_schema_retry" : undefined,
|
|
688
|
+
]);
|
|
689
|
+
const collectSchemaFailureLabels = (call) => uniqueLabels([
|
|
690
|
+
"auto:schema_failure",
|
|
691
|
+
"auto:needs_review",
|
|
692
|
+
`auto:role:${call.role}`,
|
|
693
|
+
`auto:status:${call.status}`,
|
|
694
|
+
]);
|
|
695
|
+
const collectGoldTargetLabels = (target) => uniqueLabels([
|
|
696
|
+
"gold_target",
|
|
697
|
+
`gold:${target.kind}`,
|
|
698
|
+
...(target.labels ?? []),
|
|
699
|
+
]);
|
|
700
|
+
const toolNameTokens = (tool) => tool
|
|
701
|
+
.toLowerCase()
|
|
702
|
+
.split(/[^a-z0-9]+/g)
|
|
703
|
+
.filter(Boolean);
|
|
704
|
+
const hasToolToken = (tool, tokens) => {
|
|
705
|
+
const normalized = tool.toLowerCase();
|
|
706
|
+
const parts = toolNameTokens(tool);
|
|
707
|
+
return tokens.some((token) => parts.includes(token) ||
|
|
708
|
+
normalized === token ||
|
|
709
|
+
normalized.endsWith(`_${token}`));
|
|
710
|
+
};
|
|
711
|
+
const isDocdexTool = (tool) => tool.toLowerCase().startsWith("docdex_");
|
|
712
|
+
const isShellTool = (tool) => hasToolToken(tool, ["bash", "cmd", "exec", "execute", "shell", "terminal"]);
|
|
713
|
+
const isDestructiveTool = (tool) => hasToolToken(tool, [
|
|
714
|
+
"delete",
|
|
715
|
+
"destroy",
|
|
716
|
+
"drop",
|
|
717
|
+
"purge",
|
|
718
|
+
"remove",
|
|
719
|
+
"reset",
|
|
720
|
+
"rm",
|
|
721
|
+
"truncate",
|
|
722
|
+
"wipe",
|
|
723
|
+
]);
|
|
724
|
+
const isWriteTool = (tool) => isDestructiveTool(tool) ||
|
|
725
|
+
hasToolToken(tool, [
|
|
726
|
+
"add",
|
|
727
|
+
"approve",
|
|
728
|
+
"assign",
|
|
729
|
+
"cancel",
|
|
730
|
+
"commit",
|
|
731
|
+
"create",
|
|
732
|
+
"dispatch",
|
|
733
|
+
"edit",
|
|
734
|
+
"merge",
|
|
735
|
+
"mutate",
|
|
736
|
+
"post",
|
|
737
|
+
"publish",
|
|
738
|
+
"push",
|
|
739
|
+
"send",
|
|
740
|
+
"submit",
|
|
741
|
+
"sync",
|
|
742
|
+
"transition",
|
|
743
|
+
"update",
|
|
744
|
+
"upload",
|
|
745
|
+
"write",
|
|
746
|
+
]);
|
|
747
|
+
const policyEventTypeForTool = (tool) => {
|
|
748
|
+
if (isShellTool(tool))
|
|
749
|
+
return "shell_block";
|
|
750
|
+
if (isDestructiveTool(tool))
|
|
751
|
+
return "destructive_block";
|
|
752
|
+
if (isWriteTool(tool))
|
|
753
|
+
return "write_block";
|
|
754
|
+
return "denied_tool";
|
|
755
|
+
};
|
|
756
|
+
const readMetadataString = (metadata, key) => {
|
|
757
|
+
const value = metadata?.[key];
|
|
758
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
759
|
+
};
|
|
760
|
+
const readMetadataStringArray = (metadata, key) => arrayOfStrings(metadata?.[key]);
|
|
761
|
+
const taskById = (trace, taskId) => taskId ? trace?.tasks.find((task) => task.id === taskId) : undefined;
|
|
762
|
+
const toolDecisionFromCall = (call, index, trace) => {
|
|
763
|
+
const task = taskById(trace, call.taskId);
|
|
764
|
+
return {
|
|
765
|
+
id: call.id || `tool-call-${index + 1}`,
|
|
766
|
+
tool: call.tool,
|
|
767
|
+
taskId: call.taskId,
|
|
768
|
+
status: call.status,
|
|
769
|
+
args: call.args,
|
|
770
|
+
result: call.result,
|
|
771
|
+
latencyMs: call.latencyMs,
|
|
772
|
+
errorCode: call.errorCode,
|
|
773
|
+
errorMessage: call.errorMessage,
|
|
774
|
+
allowedTools: readMetadataStringArray(task?.metadata, "allowedTools"),
|
|
775
|
+
removedTools: readMetadataStringArray(task?.metadata, "removedTools"),
|
|
776
|
+
reason: call.status === "blocked" ? call.errorCode ?? "blocked" : undefined,
|
|
777
|
+
source: "tool_call",
|
|
778
|
+
metadata: call.metadata,
|
|
779
|
+
};
|
|
780
|
+
};
|
|
781
|
+
const blockedToolDecisionsFromTasks = (trace) => {
|
|
782
|
+
const decisions = [];
|
|
783
|
+
for (const task of trace?.tasks ?? []) {
|
|
784
|
+
const allowedTools = readMetadataStringArray(task.metadata, "allowedTools") ?? [];
|
|
785
|
+
const removedTools = readMetadataStringArray(task.metadata, "removedTools") ?? [];
|
|
786
|
+
const skippedReason = readMetadataString(task.metadata, "skippedReason");
|
|
787
|
+
for (const tool of removedTools) {
|
|
788
|
+
decisions.push({
|
|
789
|
+
id: `${task.id}-removed-${tool}`,
|
|
790
|
+
tool,
|
|
791
|
+
taskId: task.id,
|
|
792
|
+
status: "blocked",
|
|
793
|
+
allowedTools,
|
|
794
|
+
removedTools,
|
|
795
|
+
reason: "removed_by_policy",
|
|
796
|
+
source: "blocked_attempt",
|
|
797
|
+
metadata: task.metadata,
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
if (skippedReason && allowedTools.length > 0) {
|
|
801
|
+
for (const tool of allowedTools) {
|
|
802
|
+
decisions.push({
|
|
803
|
+
id: `${task.id}-skipped-${tool}-${skippedReason}`,
|
|
804
|
+
tool,
|
|
805
|
+
taskId: task.id,
|
|
806
|
+
status: "skipped",
|
|
807
|
+
allowedTools,
|
|
808
|
+
removedTools,
|
|
809
|
+
reason: skippedReason,
|
|
810
|
+
source: "blocked_attempt",
|
|
811
|
+
metadata: task.metadata,
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return decisions;
|
|
817
|
+
};
|
|
818
|
+
const collectEvidenceItems = (trace, result) => {
|
|
819
|
+
const byId = new Map();
|
|
820
|
+
for (const item of [...(trace?.evidence ?? []), ...result.evidence]) {
|
|
821
|
+
byId.set(item.id, item);
|
|
822
|
+
}
|
|
823
|
+
return [...byId.values()];
|
|
824
|
+
};
|
|
825
|
+
const collectContextPacks = (trace, result) => {
|
|
826
|
+
const byId = new Map();
|
|
827
|
+
for (const pack of [trace?.contextPack, result.contextPack]) {
|
|
828
|
+
if (pack)
|
|
829
|
+
byId.set(pack.id, pack);
|
|
830
|
+
}
|
|
831
|
+
return [...byId.values()];
|
|
832
|
+
};
|
|
833
|
+
const extractReservedScopeKeys = (value, path = "$", matches = []) => {
|
|
834
|
+
if (Array.isArray(value)) {
|
|
835
|
+
value.forEach((item, index) => extractReservedScopeKeys(item, `${path}[${index}]`, matches));
|
|
836
|
+
return matches;
|
|
837
|
+
}
|
|
838
|
+
if (!isRecord(value))
|
|
839
|
+
return matches;
|
|
840
|
+
for (const [key, child] of Object.entries(value)) {
|
|
841
|
+
const childPath = `${path}.${key}`;
|
|
842
|
+
if (/^(docdex|repo|repoId|repo_id|repoRoot|repo_root|tenant|tenantId|tenant_id)$/i.test(key)) {
|
|
843
|
+
matches.push(childPath);
|
|
844
|
+
}
|
|
845
|
+
extractReservedScopeKeys(child, childPath, matches);
|
|
846
|
+
}
|
|
847
|
+
return matches;
|
|
848
|
+
};
|
|
849
|
+
const scopeOverrideEventType = (value) => {
|
|
850
|
+
const keys = extractReservedScopeKeys(value);
|
|
851
|
+
if (keys.some((key) => /docdex|repo/i.test(key)))
|
|
852
|
+
return "docdex_scope_override";
|
|
853
|
+
if (keys.some((key) => /tenant/i.test(key)))
|
|
854
|
+
return "tenant_scope_override";
|
|
855
|
+
return "scope_override";
|
|
856
|
+
};
|
|
857
|
+
const collectPolicyEvents = (input) => {
|
|
858
|
+
const events = [];
|
|
859
|
+
const addEvent = (event) => {
|
|
860
|
+
const key = [
|
|
861
|
+
event.eventType,
|
|
862
|
+
event.tool ?? "",
|
|
863
|
+
event.taskId ?? "",
|
|
864
|
+
event.reason,
|
|
865
|
+
event.errorCode ?? "",
|
|
866
|
+
].join("|");
|
|
867
|
+
if (events.some((existing) => existing.id === event.id || [
|
|
868
|
+
existing.eventType,
|
|
869
|
+
existing.tool ?? "",
|
|
870
|
+
existing.taskId ?? "",
|
|
871
|
+
existing.reason,
|
|
872
|
+
existing.errorCode ?? "",
|
|
873
|
+
].join("|") === key)) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
events.push(event);
|
|
877
|
+
};
|
|
878
|
+
for (const tool of input.request.policy.deniedTools ?? []) {
|
|
879
|
+
addEvent({
|
|
880
|
+
id: `policy-denied-${tool}`,
|
|
881
|
+
eventType: "denied_tool",
|
|
882
|
+
reason: "policy_denied_tool",
|
|
883
|
+
tool,
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
for (const tool of input.request.policy.allowedTools) {
|
|
887
|
+
if (input.request.policy.allowShell === false && isShellTool(tool)) {
|
|
888
|
+
addEvent({
|
|
889
|
+
id: `policy-shell-${tool}`,
|
|
890
|
+
eventType: "shell_block",
|
|
891
|
+
reason: "shell_disabled",
|
|
892
|
+
tool,
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
else if (input.request.policy.allowDestructiveOperations === false &&
|
|
896
|
+
isDestructiveTool(tool)) {
|
|
897
|
+
addEvent({
|
|
898
|
+
id: `policy-destructive-${tool}`,
|
|
899
|
+
eventType: "destructive_block",
|
|
900
|
+
reason: "destructive_operations_disabled",
|
|
901
|
+
tool,
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
else if (input.request.policy.allowWrites === false && isWriteTool(tool)) {
|
|
905
|
+
addEvent({
|
|
906
|
+
id: `policy-write-${tool}`,
|
|
907
|
+
eventType: "write_block",
|
|
908
|
+
reason: "writes_disabled",
|
|
909
|
+
tool,
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
for (const task of input.trace?.tasks ?? []) {
|
|
914
|
+
const removedTools = readMetadataStringArray(task.metadata, "removedTools") ?? [];
|
|
915
|
+
const skippedReason = readMetadataString(task.metadata, "skippedReason");
|
|
916
|
+
for (const tool of removedTools) {
|
|
917
|
+
addEvent({
|
|
918
|
+
id: `task-${task.id}-removed-${tool}`,
|
|
919
|
+
eventType: policyEventTypeForTool(tool),
|
|
920
|
+
reason: "removed_from_worker_task",
|
|
921
|
+
tool,
|
|
922
|
+
taskId: task.id,
|
|
923
|
+
metadata: task.metadata,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
if (skippedReason && /tool|budget|policy|scope|denied|blocked/i.test(skippedReason)) {
|
|
927
|
+
addEvent({
|
|
928
|
+
id: `task-${task.id}-skipped-${skippedReason}`,
|
|
929
|
+
eventType: skippedReason.includes("budget") ? "tool_budget_block" : "tool_block",
|
|
930
|
+
reason: skippedReason,
|
|
931
|
+
taskId: task.id,
|
|
932
|
+
status: task.status,
|
|
933
|
+
metadata: task.metadata,
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
for (const call of input.trace?.toolCalls ?? []) {
|
|
938
|
+
const errorCode = call.errorCode ?? "";
|
|
939
|
+
const eventType = /SCOPE_OVERRIDE/i.test(errorCode)
|
|
940
|
+
? scopeOverrideEventType({ args: call.args, metadata: call.metadata })
|
|
941
|
+
: /DENIED/i.test(errorCode)
|
|
942
|
+
? "denied_tool"
|
|
943
|
+
: /NOT_APPROVED|NOT_ALLOWED|BLOCK/i.test(errorCode) || call.status === "blocked"
|
|
944
|
+
? policyEventTypeForTool(call.tool)
|
|
945
|
+
: undefined;
|
|
946
|
+
if (eventType) {
|
|
947
|
+
addEvent({
|
|
948
|
+
id: `tool-${call.id}-${eventType}`,
|
|
949
|
+
eventType,
|
|
950
|
+
reason: call.errorCode ?? call.status,
|
|
951
|
+
tool: call.tool,
|
|
952
|
+
taskId: call.taskId,
|
|
953
|
+
status: call.status,
|
|
954
|
+
errorCode: call.errorCode,
|
|
955
|
+
errorMessage: call.errorMessage,
|
|
956
|
+
metadata: call.metadata,
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
for (const warning of input.result.trace.warnings) {
|
|
961
|
+
if (!/denied|blocked|removed|scope|write|shell|destructive/i.test(warning))
|
|
962
|
+
continue;
|
|
963
|
+
addEvent({
|
|
964
|
+
id: `warning-${hashGatewayDatasetRequestBody(warning).slice(0, 16)}`,
|
|
965
|
+
eventType: /scope/i.test(warning) ? "scope_override" : "policy_warning",
|
|
966
|
+
reason: warning,
|
|
967
|
+
metadata: { source: "gateway_trace_warning" },
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
return events;
|
|
971
|
+
};
|
|
972
|
+
const collectToolDecisionLabels = (decision) => uniqueLabels([
|
|
973
|
+
"auto:tool_decision",
|
|
974
|
+
`auto:tool:${decision.tool}`,
|
|
975
|
+
`auto:status:${decision.status}`,
|
|
976
|
+
decision.source === "blocked_attempt" ? "auto:blocked_attempt" : undefined,
|
|
977
|
+
decision.reason ? `auto:reason:${decision.reason}` : undefined,
|
|
978
|
+
]);
|
|
979
|
+
const collectRagRetrievalLabels = (call) => uniqueLabels([
|
|
980
|
+
"auto:rag_retrieval",
|
|
981
|
+
`auto:tool:${call.tool}`,
|
|
982
|
+
`auto:status:${call.status}`,
|
|
983
|
+
]);
|
|
984
|
+
const collectEvidenceLabels = (item) => uniqueLabels([
|
|
985
|
+
"auto:evidence_item",
|
|
986
|
+
`auto:source:${item.sourceType}`,
|
|
987
|
+
item.usedTool ? `auto:tool:${item.usedTool}` : undefined,
|
|
988
|
+
item.freshness ? `auto:freshness:${item.freshness}` : undefined,
|
|
989
|
+
item.tenantScoped ? "auto:tenant_scoped" : undefined,
|
|
990
|
+
]);
|
|
991
|
+
const collectContextPackLabels = (contextPack) => uniqueLabels([
|
|
992
|
+
"auto:context_pack",
|
|
993
|
+
contextPack.contradictions.length > 0 ? "auto:has_contradictions" : undefined,
|
|
994
|
+
contextPack.missingInformation.length > 0 ? "auto:missing_information" : undefined,
|
|
995
|
+
]);
|
|
996
|
+
const collectArtifactLabels = (artifact) => uniqueLabels([
|
|
997
|
+
"auto:artifact",
|
|
998
|
+
`auto:artifact_type:${artifact.type}`,
|
|
999
|
+
/^image($|\/)|image/i.test(artifact.type) ? "auto:image_artifact" : undefined,
|
|
1000
|
+
artifact.uri || artifact.path ? "auto:object_ref_only" : undefined,
|
|
1001
|
+
]);
|
|
1002
|
+
const collectPolicyEventLabels = (event) => uniqueLabels([
|
|
1003
|
+
"auto:policy_event",
|
|
1004
|
+
`auto:policy:${event.eventType}`,
|
|
1005
|
+
event.tool ? `auto:tool:${event.tool}` : undefined,
|
|
1006
|
+
]);
|
|
1007
|
+
const artifactMetadataRefOnly = (value) => {
|
|
1008
|
+
if (!value)
|
|
1009
|
+
return undefined;
|
|
1010
|
+
const isRawArtifactPayloadKey = (key) => {
|
|
1011
|
+
const normalized = key.replace(/[-_\s]/g, "").toLowerCase();
|
|
1012
|
+
if (new Set([
|
|
1013
|
+
"base64",
|
|
1014
|
+
"binary",
|
|
1015
|
+
"blob",
|
|
1016
|
+
"body",
|
|
1017
|
+
"buffer",
|
|
1018
|
+
"bytes",
|
|
1019
|
+
"content",
|
|
1020
|
+
"data",
|
|
1021
|
+
"datauri",
|
|
1022
|
+
"dataurl",
|
|
1023
|
+
"imagedata",
|
|
1024
|
+
"payload",
|
|
1025
|
+
"raw",
|
|
1026
|
+
]).has(normalized)) {
|
|
1027
|
+
return true;
|
|
1028
|
+
}
|
|
1029
|
+
return /^(?:image|base64|binary|blob|body|buffer|bytes|content|raw|file|payload|artifact|audio|video|media).*(?:base64|binary|blob|buffer|bytes|content|data|payload|raw)$/i
|
|
1030
|
+
.test(normalized);
|
|
1031
|
+
};
|
|
1032
|
+
const sanitizeArtifactValue = (entry) => {
|
|
1033
|
+
if (Array.isArray(entry))
|
|
1034
|
+
return entry.map(sanitizeArtifactValue);
|
|
1035
|
+
if (!isRecord(entry))
|
|
1036
|
+
return sanitizeGatewayDatasetPayload(entry);
|
|
1037
|
+
const output = {};
|
|
1038
|
+
for (const [key, child] of Object.entries(entry)) {
|
|
1039
|
+
output[key] = isRawArtifactPayloadKey(key)
|
|
1040
|
+
? "[object-ref-only]"
|
|
1041
|
+
: sanitizeArtifactValue(child);
|
|
1042
|
+
}
|
|
1043
|
+
return output;
|
|
1044
|
+
};
|
|
1045
|
+
const sanitized = sanitizeArtifactValue(value);
|
|
1046
|
+
return isRecord(sanitized) ? sanitized : undefined;
|
|
1047
|
+
};
|
|
1048
|
+
export class GatewayDatasetCollector {
|
|
1049
|
+
async buildCollectInput(input) {
|
|
1050
|
+
const now = input.now ?? (() => new Date());
|
|
1051
|
+
const objectStore = input.objectStore ?? createInMemoryGatewayDatasetObjectStore({ now });
|
|
1052
|
+
const scope = gatewayDatasetScopeFromGatewayResult(input.request, input.result, input.scope);
|
|
1053
|
+
const trace = input.trace ?? (input.traceLoader ? await input.traceLoader() : undefined);
|
|
1054
|
+
const inputMetadata = sanitizeGatewayDatasetMetadata(input.metadata);
|
|
1055
|
+
const privacyMetadata = sanitizeGatewayDatasetMetadata(input.privacy?.metadata);
|
|
1056
|
+
const privacy = createGatewayDatasetLocalOnlyPrivacy({
|
|
1057
|
+
containsPersonalData: true,
|
|
1058
|
+
...input.privacy,
|
|
1059
|
+
metadata: {
|
|
1060
|
+
containsPersonalDataAssumption: "unknown_assumed_true",
|
|
1061
|
+
source: "codali_gateway",
|
|
1062
|
+
collector: "gateway_dataset_collector",
|
|
1063
|
+
...(privacyMetadata ?? {}),
|
|
1064
|
+
},
|
|
1065
|
+
});
|
|
1066
|
+
const privacyFlags = createGatewayDatasetLocalOnlyObjectPrivacyFlags({
|
|
1067
|
+
containsPersonalData: true,
|
|
1068
|
+
...(input.privacyFlags ?? {}),
|
|
1069
|
+
});
|
|
1070
|
+
const modelCalls = normalizeStoredModelCalls(trace, input.result);
|
|
1071
|
+
const modelRecordIds = new Map();
|
|
1072
|
+
modelCalls.forEach((call, index) => {
|
|
1073
|
+
modelRecordIds.set(call.id, modelRecordId(input.result.runId, call, index));
|
|
1074
|
+
});
|
|
1075
|
+
const records = [
|
|
1076
|
+
await this.buildRunRecord({
|
|
1077
|
+
input,
|
|
1078
|
+
objectStore,
|
|
1079
|
+
scope,
|
|
1080
|
+
privacy,
|
|
1081
|
+
privacyFlags,
|
|
1082
|
+
now,
|
|
1083
|
+
trace,
|
|
1084
|
+
modelCalls,
|
|
1085
|
+
}),
|
|
1086
|
+
];
|
|
1087
|
+
if (input.collectFinalAnswers !== false) {
|
|
1088
|
+
records.push(await this.buildFinalAnswerRecord({
|
|
1089
|
+
input,
|
|
1090
|
+
objectStore,
|
|
1091
|
+
scope,
|
|
1092
|
+
privacy,
|
|
1093
|
+
privacyFlags,
|
|
1094
|
+
now,
|
|
1095
|
+
}));
|
|
1096
|
+
}
|
|
1097
|
+
if (input.collectRagRetrievals !== false) {
|
|
1098
|
+
const ragCalls = (trace?.toolCalls ?? []).filter((call) => isDocdexTool(call.tool));
|
|
1099
|
+
for (let index = 0; index < ragCalls.length; index += 1) {
|
|
1100
|
+
const call = ragCalls[index];
|
|
1101
|
+
if (!call)
|
|
1102
|
+
continue;
|
|
1103
|
+
records.push(await this.buildRagRetrievalRecord({
|
|
1104
|
+
call,
|
|
1105
|
+
index,
|
|
1106
|
+
input,
|
|
1107
|
+
objectStore,
|
|
1108
|
+
scope,
|
|
1109
|
+
privacy,
|
|
1110
|
+
privacyFlags,
|
|
1111
|
+
now,
|
|
1112
|
+
}));
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
if (input.collectToolDecisions !== false) {
|
|
1116
|
+
const decisions = [
|
|
1117
|
+
...(trace?.toolCalls ?? []).map((call, index) => toolDecisionFromCall(call, index, trace)),
|
|
1118
|
+
...blockedToolDecisionsFromTasks(trace),
|
|
1119
|
+
];
|
|
1120
|
+
for (let index = 0; index < decisions.length; index += 1) {
|
|
1121
|
+
const decision = decisions[index];
|
|
1122
|
+
if (!decision)
|
|
1123
|
+
continue;
|
|
1124
|
+
records.push(await this.buildToolDecisionRecord({
|
|
1125
|
+
decision,
|
|
1126
|
+
index,
|
|
1127
|
+
input,
|
|
1128
|
+
objectStore,
|
|
1129
|
+
scope,
|
|
1130
|
+
privacy,
|
|
1131
|
+
privacyFlags,
|
|
1132
|
+
now,
|
|
1133
|
+
}));
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const evidenceItems = collectEvidenceItems(trace, input.result);
|
|
1137
|
+
if (input.collectEvidenceItems !== false) {
|
|
1138
|
+
for (let index = 0; index < evidenceItems.length; index += 1) {
|
|
1139
|
+
const evidenceItem = evidenceItems[index];
|
|
1140
|
+
if (!evidenceItem)
|
|
1141
|
+
continue;
|
|
1142
|
+
records.push(await this.buildEvidenceItemRecord({
|
|
1143
|
+
evidenceItem,
|
|
1144
|
+
index,
|
|
1145
|
+
input,
|
|
1146
|
+
objectStore,
|
|
1147
|
+
scope,
|
|
1148
|
+
privacy,
|
|
1149
|
+
privacyFlags,
|
|
1150
|
+
now,
|
|
1151
|
+
}));
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
const contextPacks = collectContextPacks(trace, input.result);
|
|
1155
|
+
if (input.collectContextPacks !== false) {
|
|
1156
|
+
for (let index = 0; index < contextPacks.length; index += 1) {
|
|
1157
|
+
const contextPack = contextPacks[index];
|
|
1158
|
+
if (!contextPack)
|
|
1159
|
+
continue;
|
|
1160
|
+
records.push(await this.buildContextPackRecord({
|
|
1161
|
+
contextPack,
|
|
1162
|
+
index,
|
|
1163
|
+
input,
|
|
1164
|
+
objectStore,
|
|
1165
|
+
scope,
|
|
1166
|
+
privacy,
|
|
1167
|
+
privacyFlags,
|
|
1168
|
+
now,
|
|
1169
|
+
}));
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
if (input.collectArtifacts !== false) {
|
|
1173
|
+
for (let index = 0; index < (trace?.artifacts.length ?? 0); index += 1) {
|
|
1174
|
+
const artifact = trace?.artifacts[index];
|
|
1175
|
+
if (!artifact)
|
|
1176
|
+
continue;
|
|
1177
|
+
records.push(await this.buildArtifactRecord({
|
|
1178
|
+
artifact,
|
|
1179
|
+
index,
|
|
1180
|
+
input,
|
|
1181
|
+
objectStore,
|
|
1182
|
+
scope,
|
|
1183
|
+
privacy,
|
|
1184
|
+
privacyFlags,
|
|
1185
|
+
now,
|
|
1186
|
+
}));
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const policyEvents = collectPolicyEvents({ request: input.request, result: input.result, trace });
|
|
1190
|
+
if (input.collectPolicyEvents !== false) {
|
|
1191
|
+
for (let index = 0; index < policyEvents.length; index += 1) {
|
|
1192
|
+
const event = policyEvents[index];
|
|
1193
|
+
if (!event)
|
|
1194
|
+
continue;
|
|
1195
|
+
records.push(await this.buildPolicyEventRecord({
|
|
1196
|
+
event,
|
|
1197
|
+
index,
|
|
1198
|
+
input,
|
|
1199
|
+
objectStore,
|
|
1200
|
+
scope,
|
|
1201
|
+
privacy,
|
|
1202
|
+
privacyFlags,
|
|
1203
|
+
now,
|
|
1204
|
+
}));
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
if (input.collectModelCalls !== false) {
|
|
1208
|
+
for (let index = 0; index < modelCalls.length; index += 1) {
|
|
1209
|
+
const call = modelCalls[index];
|
|
1210
|
+
if (!call)
|
|
1211
|
+
continue;
|
|
1212
|
+
records.push(await this.buildModelCallRecord({
|
|
1213
|
+
call,
|
|
1214
|
+
index,
|
|
1215
|
+
input,
|
|
1216
|
+
objectStore,
|
|
1217
|
+
scope,
|
|
1218
|
+
privacy,
|
|
1219
|
+
privacyFlags,
|
|
1220
|
+
now,
|
|
1221
|
+
schemaFailureRecordId: isSchemaFailureModelCall(call)
|
|
1222
|
+
? schemaFailureRecordId(input.result.runId, call, index)
|
|
1223
|
+
: undefined,
|
|
1224
|
+
}));
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (input.collectSchemaFailures !== false) {
|
|
1228
|
+
for (let index = 0; index < modelCalls.length; index += 1) {
|
|
1229
|
+
const call = modelCalls[index];
|
|
1230
|
+
if (!call || !isSchemaFailureModelCall(call))
|
|
1231
|
+
continue;
|
|
1232
|
+
const corrected = nextCorrectedModelCall(call, index, modelCalls);
|
|
1233
|
+
records.push(await this.buildSchemaFailureRecord({
|
|
1234
|
+
call,
|
|
1235
|
+
index,
|
|
1236
|
+
corrected,
|
|
1237
|
+
input,
|
|
1238
|
+
objectStore,
|
|
1239
|
+
scope,
|
|
1240
|
+
privacy,
|
|
1241
|
+
privacyFlags,
|
|
1242
|
+
now,
|
|
1243
|
+
failedAttemptRecordId: modelRecordIds.get(call.id),
|
|
1244
|
+
correctedRecordId: corrected ? modelRecordIds.get(corrected.id) : undefined,
|
|
1245
|
+
}));
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
const goldTargets = [
|
|
1249
|
+
...(input.goldTargets ?? []),
|
|
1250
|
+
...goldTargetsFromMetadata(input.result.metadata),
|
|
1251
|
+
];
|
|
1252
|
+
if (input.collectGoldTargets !== false) {
|
|
1253
|
+
for (let index = 0; index < goldTargets.length; index += 1) {
|
|
1254
|
+
const target = goldTargets[index];
|
|
1255
|
+
if (!target)
|
|
1256
|
+
continue;
|
|
1257
|
+
records.push(await this.buildGoldTargetRecord({
|
|
1258
|
+
target,
|
|
1259
|
+
index,
|
|
1260
|
+
input,
|
|
1261
|
+
objectStore,
|
|
1262
|
+
scope,
|
|
1263
|
+
privacy,
|
|
1264
|
+
privacyFlags,
|
|
1265
|
+
now,
|
|
1266
|
+
modelRecordIds,
|
|
1267
|
+
}));
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
const validatedRecords = validateDatasetRecords(records);
|
|
1271
|
+
return {
|
|
1272
|
+
scope,
|
|
1273
|
+
records: validatedRecords,
|
|
1274
|
+
idempotencyKey: input.idempotencyKey ?? buildGatewayDatasetIdempotencyKey(scope, validatedRecords),
|
|
1275
|
+
metadata: {
|
|
1276
|
+
source: "codali_gateway",
|
|
1277
|
+
collectionMode: "gateway_trace_collector",
|
|
1278
|
+
gatewayRunId: input.result.runId,
|
|
1279
|
+
traceAvailable: Boolean(trace),
|
|
1280
|
+
recordCounts: {
|
|
1281
|
+
total: validatedRecords.length,
|
|
1282
|
+
modelCalls: input.collectModelCalls === false ? 0 : modelCalls.length,
|
|
1283
|
+
ragRetrievals: input.collectRagRetrievals === false
|
|
1284
|
+
? 0
|
|
1285
|
+
: validatedRecords.filter((record) => record.metadata?.exampleType === "rag_retrieval").length,
|
|
1286
|
+
toolDecisions: input.collectToolDecisions === false
|
|
1287
|
+
? 0
|
|
1288
|
+
: validatedRecords.filter((record) => record.metadata?.exampleType === "tool_decision").length,
|
|
1289
|
+
evidenceItems: input.collectEvidenceItems === false ? 0 : evidenceItems.length,
|
|
1290
|
+
contextPacks: input.collectContextPacks === false ? 0 : contextPacks.length,
|
|
1291
|
+
finalAnswers: input.collectFinalAnswers === false
|
|
1292
|
+
? 0
|
|
1293
|
+
: validatedRecords.filter((record) => record.metadata?.exampleType === "final_answer").length,
|
|
1294
|
+
artifacts: input.collectArtifacts === false
|
|
1295
|
+
? 0
|
|
1296
|
+
: validatedRecords.filter((record) => record.metadata?.exampleType === "artifact").length,
|
|
1297
|
+
policyEvents: input.collectPolicyEvents === false
|
|
1298
|
+
? 0
|
|
1299
|
+
: validatedRecords.filter((record) => record.metadata?.exampleType === "policy_event").length,
|
|
1300
|
+
schemaFailures: validatedRecords.filter((record) => record.metadata?.exampleType === "schema_failure").length,
|
|
1301
|
+
goldTargets: validatedRecords.filter((record) => record.metadata?.exampleType === "gold_target").length,
|
|
1302
|
+
},
|
|
1303
|
+
...(inputMetadata ?? {}),
|
|
1304
|
+
},
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
async buildRunRecord(input) {
|
|
1308
|
+
const recordId = `dataset-${input.input.result.runId}`;
|
|
1309
|
+
const run = input.trace?.run;
|
|
1310
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1311
|
+
scope: input.scope,
|
|
1312
|
+
ownerType: "dataset_record",
|
|
1313
|
+
ownerId: recordId,
|
|
1314
|
+
kind: "dataset",
|
|
1315
|
+
payload: {
|
|
1316
|
+
requestId: input.input.request.id,
|
|
1317
|
+
query: input.input.request.query,
|
|
1318
|
+
mode: input.input.request.mode,
|
|
1319
|
+
conversationId: input.input.request.conversation?.id,
|
|
1320
|
+
traceRunId: run?.runId ?? input.input.result.runId,
|
|
1321
|
+
traceStatus: run?.status ?? input.input.result.status,
|
|
1322
|
+
taskCount: input.trace?.tasks.length ?? input.input.result.trace.iterations,
|
|
1323
|
+
modelCallCount: input.modelCalls.length,
|
|
1324
|
+
},
|
|
1325
|
+
privacyFlags: input.privacyFlags,
|
|
1326
|
+
metadata: { part: "gateway_run_input" },
|
|
1327
|
+
});
|
|
1328
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1329
|
+
scope: input.scope,
|
|
1330
|
+
ownerType: "dataset_record",
|
|
1331
|
+
ownerId: recordId,
|
|
1332
|
+
kind: "dataset",
|
|
1333
|
+
payload: {
|
|
1334
|
+
status: input.input.result.status,
|
|
1335
|
+
answer: input.input.result.answer,
|
|
1336
|
+
confidence: input.input.result.confidence,
|
|
1337
|
+
sources: input.input.result.sources,
|
|
1338
|
+
warnings: input.input.result.trace.warnings,
|
|
1339
|
+
errors: input.input.result.trace.errors,
|
|
1340
|
+
finalModel: input.input.result.finalModel,
|
|
1341
|
+
telemetry: input.input.result.telemetry,
|
|
1342
|
+
},
|
|
1343
|
+
privacyFlags: input.privacyFlags,
|
|
1344
|
+
metadata: { part: "gateway_run_output" },
|
|
1345
|
+
});
|
|
1346
|
+
const evidenceRefs = input.input.result.evidence.length > 0
|
|
1347
|
+
? [
|
|
1348
|
+
await putGatewayDatasetObject(input.objectStore, {
|
|
1349
|
+
scope: input.scope,
|
|
1350
|
+
ownerType: "dataset_record",
|
|
1351
|
+
ownerId: recordId,
|
|
1352
|
+
kind: "evidence",
|
|
1353
|
+
payload: {
|
|
1354
|
+
evidence: input.input.result.evidence,
|
|
1355
|
+
contextPack: contextPackSummary(input.input.result.contextPack),
|
|
1356
|
+
},
|
|
1357
|
+
privacyFlags: input.privacyFlags,
|
|
1358
|
+
metadata: { part: "gateway_run_evidence" },
|
|
1359
|
+
}),
|
|
1360
|
+
]
|
|
1361
|
+
: undefined;
|
|
1362
|
+
return validateDatasetRecordOrThrow({
|
|
1363
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1364
|
+
recordType: "dataset_record",
|
|
1365
|
+
recordId,
|
|
1366
|
+
datasetKind: input.input.datasetKind ?? "gateway_answer",
|
|
1367
|
+
createdAt: input.now().toISOString(),
|
|
1368
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1369
|
+
inputRef,
|
|
1370
|
+
outputRef,
|
|
1371
|
+
evidenceRefs,
|
|
1372
|
+
quality: {
|
|
1373
|
+
score: confidenceScore(input.input.result.confidence),
|
|
1374
|
+
labels: collectGatewayRunLabels(input.input.result),
|
|
1375
|
+
reviewed: false,
|
|
1376
|
+
},
|
|
1377
|
+
privacy: input.privacy,
|
|
1378
|
+
metadata: {
|
|
1379
|
+
collectionMode: "gateway_trace_collector",
|
|
1380
|
+
exampleType: "gateway_run",
|
|
1381
|
+
gatewayStatus: input.input.result.status,
|
|
1382
|
+
finalModelTier: input.input.result.finalModel?.tier,
|
|
1383
|
+
finalModelAgentSlug: input.input.result.finalModel?.agentSlug,
|
|
1384
|
+
traceAvailable: Boolean(input.trace),
|
|
1385
|
+
modelCallCount: input.modelCalls.length,
|
|
1386
|
+
telemetry: input.input.result.telemetry,
|
|
1387
|
+
...(sanitizeGatewayDatasetMetadata(input.input.metadata) ?? {}),
|
|
1388
|
+
},
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
async buildFinalAnswerRecord(input) {
|
|
1392
|
+
const recordId = `dataset-${input.input.result.runId}-final-answer`;
|
|
1393
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1394
|
+
scope: input.scope,
|
|
1395
|
+
ownerType: "final_answer_example",
|
|
1396
|
+
ownerId: recordId,
|
|
1397
|
+
kind: "dataset",
|
|
1398
|
+
payload: {
|
|
1399
|
+
requestId: input.input.request.id,
|
|
1400
|
+
query: input.input.request.query,
|
|
1401
|
+
responsePolicy: input.input.request.response,
|
|
1402
|
+
contextPack: contextPackSummary(input.input.result.contextPack),
|
|
1403
|
+
sourceEvidenceIds: input.input.result.sources.map((source) => source.evidenceId),
|
|
1404
|
+
finalModel: input.input.result.finalModel,
|
|
1405
|
+
},
|
|
1406
|
+
privacyFlags: input.privacyFlags,
|
|
1407
|
+
metadata: { part: "final_answer_input" },
|
|
1408
|
+
});
|
|
1409
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1410
|
+
scope: input.scope,
|
|
1411
|
+
ownerType: "final_answer_example",
|
|
1412
|
+
ownerId: recordId,
|
|
1413
|
+
kind: "dataset",
|
|
1414
|
+
payload: {
|
|
1415
|
+
status: input.input.result.status,
|
|
1416
|
+
answer: input.input.result.answer,
|
|
1417
|
+
confidence: input.input.result.confidence,
|
|
1418
|
+
sources: input.input.result.sources,
|
|
1419
|
+
warnings: input.input.result.trace.warnings,
|
|
1420
|
+
errors: input.input.result.trace.errors,
|
|
1421
|
+
},
|
|
1422
|
+
privacyFlags: input.privacyFlags,
|
|
1423
|
+
metadata: { part: "final_answer_output" },
|
|
1424
|
+
});
|
|
1425
|
+
return validateDatasetRecordOrThrow({
|
|
1426
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1427
|
+
recordType: "dataset_record",
|
|
1428
|
+
recordId,
|
|
1429
|
+
datasetKind: "gateway_answer",
|
|
1430
|
+
createdAt: input.now().toISOString(),
|
|
1431
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1432
|
+
inputRef,
|
|
1433
|
+
outputRef,
|
|
1434
|
+
evidenceRefs: input.input.result.evidence.length > 0
|
|
1435
|
+
? [
|
|
1436
|
+
await putGatewayDatasetObject(input.objectStore, {
|
|
1437
|
+
scope: input.scope,
|
|
1438
|
+
ownerType: "final_answer_example",
|
|
1439
|
+
ownerId: recordId,
|
|
1440
|
+
kind: "evidence",
|
|
1441
|
+
payload: { evidence: input.input.result.evidence },
|
|
1442
|
+
privacyFlags: input.privacyFlags,
|
|
1443
|
+
metadata: { part: "final_answer_evidence" },
|
|
1444
|
+
}),
|
|
1445
|
+
]
|
|
1446
|
+
: undefined,
|
|
1447
|
+
quality: {
|
|
1448
|
+
score: confidenceScore(input.input.result.confidence),
|
|
1449
|
+
labels: uniqueLabels([
|
|
1450
|
+
"auto:final_answer",
|
|
1451
|
+
`auto:status:${input.input.result.status}`,
|
|
1452
|
+
`auto:confidence:${input.input.result.confidence}`,
|
|
1453
|
+
]),
|
|
1454
|
+
reviewed: false,
|
|
1455
|
+
},
|
|
1456
|
+
privacy: input.privacy,
|
|
1457
|
+
metadata: {
|
|
1458
|
+
collectionMode: "gateway_trace_collector",
|
|
1459
|
+
exampleType: "final_answer",
|
|
1460
|
+
finalModelTier: input.input.result.finalModel?.tier,
|
|
1461
|
+
finalModelAgentSlug: input.input.result.finalModel?.agentSlug,
|
|
1462
|
+
sourceCount: input.input.result.sources.length,
|
|
1463
|
+
},
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
async buildRagRetrievalRecord(input) {
|
|
1467
|
+
const recordId = `dataset-${input.input.result.runId}-rag-${safeDatasetIdPart(input.call.id || `${input.index + 1}-${input.call.tool}`)}`;
|
|
1468
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1469
|
+
scope: input.scope,
|
|
1470
|
+
ownerType: "rag_retrieval_event",
|
|
1471
|
+
ownerId: recordId,
|
|
1472
|
+
kind: "dataset",
|
|
1473
|
+
payload: {
|
|
1474
|
+
toolCallId: input.call.id,
|
|
1475
|
+
taskId: input.call.taskId,
|
|
1476
|
+
tool: input.call.tool,
|
|
1477
|
+
args: input.call.args,
|
|
1478
|
+
},
|
|
1479
|
+
privacyFlags: input.privacyFlags,
|
|
1480
|
+
metadata: { part: "rag_retrieval_input", toolCallId: input.call.id },
|
|
1481
|
+
});
|
|
1482
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1483
|
+
scope: input.scope,
|
|
1484
|
+
ownerType: "rag_retrieval_event",
|
|
1485
|
+
ownerId: recordId,
|
|
1486
|
+
kind: "dataset",
|
|
1487
|
+
payload: {
|
|
1488
|
+
toolCallId: input.call.id,
|
|
1489
|
+
status: input.call.status,
|
|
1490
|
+
result: input.call.result,
|
|
1491
|
+
errorCode: input.call.errorCode,
|
|
1492
|
+
errorMessage: input.call.errorMessage,
|
|
1493
|
+
latencyMs: input.call.latencyMs,
|
|
1494
|
+
},
|
|
1495
|
+
privacyFlags: input.privacyFlags,
|
|
1496
|
+
metadata: { part: "rag_retrieval_output", toolCallId: input.call.id },
|
|
1497
|
+
});
|
|
1498
|
+
return validateDatasetRecordOrThrow({
|
|
1499
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1500
|
+
recordType: "dataset_record",
|
|
1501
|
+
recordId,
|
|
1502
|
+
datasetKind: "tool_trace",
|
|
1503
|
+
createdAt: input.now().toISOString(),
|
|
1504
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1505
|
+
inputRef,
|
|
1506
|
+
outputRef,
|
|
1507
|
+
quality: {
|
|
1508
|
+
score: input.call.status === "success" ? 0.6 : 0.1,
|
|
1509
|
+
labels: collectRagRetrievalLabels(input.call),
|
|
1510
|
+
reviewed: false,
|
|
1511
|
+
},
|
|
1512
|
+
privacy: input.privacy,
|
|
1513
|
+
metadata: {
|
|
1514
|
+
collectionMode: "gateway_trace_collector",
|
|
1515
|
+
exampleType: "rag_retrieval",
|
|
1516
|
+
toolCallId: input.call.id,
|
|
1517
|
+
taskId: input.call.taskId,
|
|
1518
|
+
tool: input.call.tool,
|
|
1519
|
+
status: input.call.status,
|
|
1520
|
+
},
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
async buildToolDecisionRecord(input) {
|
|
1524
|
+
const recordId = `dataset-${input.input.result.runId}-tool-decision-${safeDatasetIdPart(input.decision.id || `${input.index + 1}-${input.decision.tool}`)}`;
|
|
1525
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1526
|
+
scope: input.scope,
|
|
1527
|
+
ownerType: "tool_decision_example",
|
|
1528
|
+
ownerId: recordId,
|
|
1529
|
+
kind: "dataset",
|
|
1530
|
+
payload: {
|
|
1531
|
+
tool: input.decision.tool,
|
|
1532
|
+
taskId: input.decision.taskId,
|
|
1533
|
+
args: input.decision.args,
|
|
1534
|
+
allowedTools: input.decision.allowedTools,
|
|
1535
|
+
removedTools: input.decision.removedTools,
|
|
1536
|
+
source: input.decision.source,
|
|
1537
|
+
},
|
|
1538
|
+
privacyFlags: input.privacyFlags,
|
|
1539
|
+
metadata: { part: "tool_decision_input", decisionId: input.decision.id },
|
|
1540
|
+
});
|
|
1541
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1542
|
+
scope: input.scope,
|
|
1543
|
+
ownerType: "tool_decision_example",
|
|
1544
|
+
ownerId: recordId,
|
|
1545
|
+
kind: "dataset",
|
|
1546
|
+
payload: {
|
|
1547
|
+
status: input.decision.status,
|
|
1548
|
+
result: input.decision.result,
|
|
1549
|
+
errorCode: input.decision.errorCode,
|
|
1550
|
+
errorMessage: input.decision.errorMessage,
|
|
1551
|
+
reason: input.decision.reason,
|
|
1552
|
+
latencyMs: input.decision.latencyMs,
|
|
1553
|
+
},
|
|
1554
|
+
privacyFlags: input.privacyFlags,
|
|
1555
|
+
metadata: { part: "tool_decision_output", decisionId: input.decision.id },
|
|
1556
|
+
});
|
|
1557
|
+
return validateDatasetRecordOrThrow({
|
|
1558
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1559
|
+
recordType: "dataset_record",
|
|
1560
|
+
recordId,
|
|
1561
|
+
datasetKind: "tool_trace",
|
|
1562
|
+
createdAt: input.now().toISOString(),
|
|
1563
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1564
|
+
inputRef,
|
|
1565
|
+
outputRef,
|
|
1566
|
+
quality: {
|
|
1567
|
+
score: input.decision.status === "success" ? 0.55 : 0.05,
|
|
1568
|
+
labels: collectToolDecisionLabels(input.decision),
|
|
1569
|
+
reviewed: false,
|
|
1570
|
+
},
|
|
1571
|
+
privacy: input.privacy,
|
|
1572
|
+
metadata: {
|
|
1573
|
+
collectionMode: "gateway_trace_collector",
|
|
1574
|
+
exampleType: "tool_decision",
|
|
1575
|
+
decisionSource: input.decision.source,
|
|
1576
|
+
tool: input.decision.tool,
|
|
1577
|
+
taskId: input.decision.taskId,
|
|
1578
|
+
status: input.decision.status,
|
|
1579
|
+
reason: input.decision.reason,
|
|
1580
|
+
errorCode: input.decision.errorCode,
|
|
1581
|
+
...(sanitizeGatewayDatasetMetadata(input.decision.metadata) ?? {}),
|
|
1582
|
+
},
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
async buildEvidenceItemRecord(input) {
|
|
1586
|
+
const evidenceId = safeDatasetIdPart(input.evidenceItem.id || `evidence-${input.index + 1}`);
|
|
1587
|
+
const recordId = `dataset-${input.input.result.runId}-evidence-${evidenceId}`;
|
|
1588
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1589
|
+
scope: input.scope,
|
|
1590
|
+
ownerType: "evidence_item_record",
|
|
1591
|
+
ownerId: recordId,
|
|
1592
|
+
kind: "evidence",
|
|
1593
|
+
payload: {
|
|
1594
|
+
evidenceId: input.evidenceItem.id,
|
|
1595
|
+
taskId: input.evidenceItem.taskId,
|
|
1596
|
+
stageId: input.evidenceItem.stageId,
|
|
1597
|
+
sourceType: input.evidenceItem.sourceType,
|
|
1598
|
+
sourceId: input.evidenceItem.sourceId,
|
|
1599
|
+
sourceUri: input.evidenceItem.sourceUri,
|
|
1600
|
+
sourceTitle: input.evidenceItem.sourceTitle,
|
|
1601
|
+
usedTool: input.evidenceItem.usedTool,
|
|
1602
|
+
tenantScoped: input.evidenceItem.tenantScoped,
|
|
1603
|
+
},
|
|
1604
|
+
privacyFlags: input.privacyFlags,
|
|
1605
|
+
metadata: { part: "evidence_item_input", evidenceId: input.evidenceItem.id },
|
|
1606
|
+
});
|
|
1607
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1608
|
+
scope: input.scope,
|
|
1609
|
+
ownerType: "evidence_item_record",
|
|
1610
|
+
ownerId: recordId,
|
|
1611
|
+
kind: "evidence",
|
|
1612
|
+
payload: {
|
|
1613
|
+
claim: input.evidenceItem.claim,
|
|
1614
|
+
summary: input.evidenceItem.summary,
|
|
1615
|
+
rawExcerpt: input.evidenceItem.rawExcerpt,
|
|
1616
|
+
rawPayloadRef: input.evidenceItem.rawPayloadRef,
|
|
1617
|
+
confidence: input.evidenceItem.confidence,
|
|
1618
|
+
relevance: input.evidenceItem.relevance,
|
|
1619
|
+
freshness: input.evidenceItem.freshness,
|
|
1620
|
+
metadata: input.evidenceItem.metadata,
|
|
1621
|
+
},
|
|
1622
|
+
privacyFlags: input.privacyFlags,
|
|
1623
|
+
metadata: { part: "evidence_item_output", evidenceId: input.evidenceItem.id },
|
|
1624
|
+
});
|
|
1625
|
+
return validateDatasetRecordOrThrow({
|
|
1626
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1627
|
+
recordType: "dataset_record",
|
|
1628
|
+
recordId,
|
|
1629
|
+
datasetKind: "evaluation",
|
|
1630
|
+
createdAt: input.now().toISOString(),
|
|
1631
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1632
|
+
inputRef,
|
|
1633
|
+
outputRef,
|
|
1634
|
+
quality: {
|
|
1635
|
+
score: Math.max(0, Math.min(1, input.evidenceItem.confidence * input.evidenceItem.relevance)),
|
|
1636
|
+
labels: collectEvidenceLabels(input.evidenceItem),
|
|
1637
|
+
reviewed: false,
|
|
1638
|
+
},
|
|
1639
|
+
privacy: input.privacy,
|
|
1640
|
+
metadata: {
|
|
1641
|
+
collectionMode: "gateway_trace_collector",
|
|
1642
|
+
exampleType: "evidence_item",
|
|
1643
|
+
evidenceId: input.evidenceItem.id,
|
|
1644
|
+
sourceType: input.evidenceItem.sourceType,
|
|
1645
|
+
usedTool: input.evidenceItem.usedTool,
|
|
1646
|
+
tenantScoped: input.evidenceItem.tenantScoped,
|
|
1647
|
+
},
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
async buildContextPackRecord(input) {
|
|
1651
|
+
const contextId = safeDatasetIdPart(input.contextPack.id || `context-${input.index + 1}`);
|
|
1652
|
+
const recordId = `dataset-${input.input.result.runId}-context-${contextId}`;
|
|
1653
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1654
|
+
scope: input.scope,
|
|
1655
|
+
ownerType: "context_pack_record",
|
|
1656
|
+
ownerId: recordId,
|
|
1657
|
+
kind: "dataset",
|
|
1658
|
+
payload: {
|
|
1659
|
+
contextPackId: input.contextPack.id,
|
|
1660
|
+
originalQuery: input.contextPack.originalQuery,
|
|
1661
|
+
decisionFactIds: input.contextPack.decisionFacts.map((item) => item.id),
|
|
1662
|
+
selectedExcerpts: input.contextPack.selectedExcerpts,
|
|
1663
|
+
},
|
|
1664
|
+
privacyFlags: input.privacyFlags,
|
|
1665
|
+
metadata: { part: "context_pack_input", contextPackId: input.contextPack.id },
|
|
1666
|
+
});
|
|
1667
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1668
|
+
scope: input.scope,
|
|
1669
|
+
ownerType: "context_pack_record",
|
|
1670
|
+
ownerId: recordId,
|
|
1671
|
+
kind: "dataset",
|
|
1672
|
+
payload: {
|
|
1673
|
+
tokenEstimate: input.contextPack.tokenEstimate,
|
|
1674
|
+
decisionFacts: input.contextPack.decisionFacts,
|
|
1675
|
+
contradictions: input.contextPack.contradictions,
|
|
1676
|
+
missingInformation: input.contextPack.missingInformation,
|
|
1677
|
+
toolSummary: input.contextPack.toolSummary,
|
|
1678
|
+
metadata: input.contextPack.metadata,
|
|
1679
|
+
},
|
|
1680
|
+
privacyFlags: input.privacyFlags,
|
|
1681
|
+
metadata: { part: "context_pack_output", contextPackId: input.contextPack.id },
|
|
1682
|
+
});
|
|
1683
|
+
return validateDatasetRecordOrThrow({
|
|
1684
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1685
|
+
recordType: "dataset_record",
|
|
1686
|
+
recordId,
|
|
1687
|
+
datasetKind: "evaluation",
|
|
1688
|
+
createdAt: input.now().toISOString(),
|
|
1689
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1690
|
+
inputRef,
|
|
1691
|
+
outputRef,
|
|
1692
|
+
quality: {
|
|
1693
|
+
score: input.contextPack.missingInformation.length === 0 ? 0.65 : 0.35,
|
|
1694
|
+
labels: collectContextPackLabels(input.contextPack),
|
|
1695
|
+
reviewed: false,
|
|
1696
|
+
},
|
|
1697
|
+
privacy: input.privacy,
|
|
1698
|
+
metadata: {
|
|
1699
|
+
collectionMode: "gateway_trace_collector",
|
|
1700
|
+
exampleType: "context_pack",
|
|
1701
|
+
contextPackId: input.contextPack.id,
|
|
1702
|
+
tokenEstimate: input.contextPack.tokenEstimate,
|
|
1703
|
+
decisionFactCount: input.contextPack.decisionFacts.length,
|
|
1704
|
+
contradictionCount: input.contextPack.contradictions.length,
|
|
1705
|
+
missingInformationCount: input.contextPack.missingInformation.length,
|
|
1706
|
+
},
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
async buildArtifactRecord(input) {
|
|
1710
|
+
const artifactId = safeDatasetIdPart(input.artifact.id || `artifact-${input.index + 1}`);
|
|
1711
|
+
const recordId = `dataset-${input.input.result.runId}-artifact-${artifactId}`;
|
|
1712
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1713
|
+
scope: input.scope,
|
|
1714
|
+
ownerType: "artifact_record",
|
|
1715
|
+
ownerId: recordId,
|
|
1716
|
+
kind: "artifact",
|
|
1717
|
+
payload: {
|
|
1718
|
+
artifactId: input.artifact.id,
|
|
1719
|
+
taskId: input.artifact.taskId,
|
|
1720
|
+
type: input.artifact.type,
|
|
1721
|
+
model: input.artifact.model,
|
|
1722
|
+
prompt: input.artifact.prompt,
|
|
1723
|
+
},
|
|
1724
|
+
privacyFlags: input.privacyFlags,
|
|
1725
|
+
metadata: { part: "artifact_input", artifactId: input.artifact.id },
|
|
1726
|
+
});
|
|
1727
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1728
|
+
scope: input.scope,
|
|
1729
|
+
ownerType: "artifact_record",
|
|
1730
|
+
ownerId: recordId,
|
|
1731
|
+
kind: "artifact",
|
|
1732
|
+
payload: {
|
|
1733
|
+
artifactId: input.artifact.id,
|
|
1734
|
+
type: input.artifact.type,
|
|
1735
|
+
uri: input.artifact.uri,
|
|
1736
|
+
path: input.artifact.path,
|
|
1737
|
+
createdAt: input.artifact.createdAt,
|
|
1738
|
+
metadata: artifactMetadataRefOnly(input.artifact.metadata),
|
|
1739
|
+
},
|
|
1740
|
+
privacyFlags: input.privacyFlags,
|
|
1741
|
+
metadata: { part: "artifact_ref", artifactId: input.artifact.id, objectRefOnly: true },
|
|
1742
|
+
});
|
|
1743
|
+
return validateDatasetRecordOrThrow({
|
|
1744
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1745
|
+
recordType: "dataset_record",
|
|
1746
|
+
recordId,
|
|
1747
|
+
datasetKind: "tool_trace",
|
|
1748
|
+
createdAt: input.now().toISOString(),
|
|
1749
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1750
|
+
inputRef,
|
|
1751
|
+
outputRef,
|
|
1752
|
+
quality: {
|
|
1753
|
+
score: input.artifact.uri || input.artifact.path ? 0.5 : 0.15,
|
|
1754
|
+
labels: collectArtifactLabels(input.artifact),
|
|
1755
|
+
reviewed: false,
|
|
1756
|
+
},
|
|
1757
|
+
privacy: input.privacy,
|
|
1758
|
+
metadata: {
|
|
1759
|
+
collectionMode: "gateway_trace_collector",
|
|
1760
|
+
exampleType: "artifact",
|
|
1761
|
+
artifactId: input.artifact.id,
|
|
1762
|
+
artifactType: input.artifact.type,
|
|
1763
|
+
taskId: input.artifact.taskId,
|
|
1764
|
+
objectRefOnly: true,
|
|
1765
|
+
},
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
async buildPolicyEventRecord(input) {
|
|
1769
|
+
const eventId = safeDatasetIdPart(input.event.id || `policy-${input.index + 1}`);
|
|
1770
|
+
const recordId = `dataset-${input.input.result.runId}-policy-${eventId}`;
|
|
1771
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1772
|
+
scope: input.scope,
|
|
1773
|
+
ownerType: "policy_event_record",
|
|
1774
|
+
ownerId: recordId,
|
|
1775
|
+
kind: "dataset",
|
|
1776
|
+
payload: {
|
|
1777
|
+
eventType: input.event.eventType,
|
|
1778
|
+
tool: input.event.tool,
|
|
1779
|
+
taskId: input.event.taskId,
|
|
1780
|
+
status: input.event.status,
|
|
1781
|
+
},
|
|
1782
|
+
privacyFlags: input.privacyFlags,
|
|
1783
|
+
metadata: { part: "policy_event_input", policyEventId: input.event.id },
|
|
1784
|
+
});
|
|
1785
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1786
|
+
scope: input.scope,
|
|
1787
|
+
ownerType: "policy_event_record",
|
|
1788
|
+
ownerId: recordId,
|
|
1789
|
+
kind: "dataset",
|
|
1790
|
+
payload: {
|
|
1791
|
+
reason: input.event.reason,
|
|
1792
|
+
errorCode: input.event.errorCode,
|
|
1793
|
+
errorMessage: input.event.errorMessage,
|
|
1794
|
+
metadata: input.event.metadata,
|
|
1795
|
+
},
|
|
1796
|
+
privacyFlags: input.privacyFlags,
|
|
1797
|
+
metadata: { part: "policy_event_output", policyEventId: input.event.id },
|
|
1798
|
+
});
|
|
1799
|
+
return validateDatasetRecordOrThrow({
|
|
1800
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1801
|
+
recordType: "dataset_record",
|
|
1802
|
+
recordId,
|
|
1803
|
+
datasetKind: "tool_trace",
|
|
1804
|
+
createdAt: input.now().toISOString(),
|
|
1805
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1806
|
+
inputRef,
|
|
1807
|
+
outputRef,
|
|
1808
|
+
quality: {
|
|
1809
|
+
score: 0,
|
|
1810
|
+
labels: collectPolicyEventLabels(input.event),
|
|
1811
|
+
reviewed: false,
|
|
1812
|
+
},
|
|
1813
|
+
privacy: input.privacy,
|
|
1814
|
+
metadata: {
|
|
1815
|
+
collectionMode: "gateway_trace_collector",
|
|
1816
|
+
exampleType: "policy_event",
|
|
1817
|
+
policyEventType: input.event.eventType,
|
|
1818
|
+
reason: input.event.reason,
|
|
1819
|
+
tool: input.event.tool,
|
|
1820
|
+
taskId: input.event.taskId,
|
|
1821
|
+
errorCode: input.event.errorCode,
|
|
1822
|
+
},
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
async buildModelCallRecord(input) {
|
|
1826
|
+
const recordId = modelRecordId(input.input.result.runId, input.call, input.index);
|
|
1827
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1828
|
+
scope: input.scope,
|
|
1829
|
+
ownerType: "model_stage_example",
|
|
1830
|
+
ownerId: recordId,
|
|
1831
|
+
kind: "dataset",
|
|
1832
|
+
payload: {
|
|
1833
|
+
modelCallId: input.call.id,
|
|
1834
|
+
role: input.call.role,
|
|
1835
|
+
taskId: input.call.taskId,
|
|
1836
|
+
agentSlug: input.call.agentSlug,
|
|
1837
|
+
model: input.call.model,
|
|
1838
|
+
provider: input.call.provider,
|
|
1839
|
+
input: input.call.input,
|
|
1840
|
+
},
|
|
1841
|
+
privacyFlags: input.privacyFlags,
|
|
1842
|
+
metadata: { part: "model_stage_input", modelCallId: input.call.id },
|
|
1843
|
+
});
|
|
1844
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1845
|
+
scope: input.scope,
|
|
1846
|
+
ownerType: "model_stage_example",
|
|
1847
|
+
ownerId: recordId,
|
|
1848
|
+
kind: "dataset",
|
|
1849
|
+
payload: {
|
|
1850
|
+
modelCallId: input.call.id,
|
|
1851
|
+
status: input.call.status,
|
|
1852
|
+
output: input.call.output,
|
|
1853
|
+
errorCode: input.call.errorCode,
|
|
1854
|
+
errorMessage: input.call.errorMessage,
|
|
1855
|
+
latencyMs: input.call.latencyMs,
|
|
1856
|
+
usage: metadataRecord(input.call.metadata, "usage"),
|
|
1857
|
+
},
|
|
1858
|
+
privacyFlags: input.privacyFlags,
|
|
1859
|
+
metadata: { part: "model_stage_output", modelCallId: input.call.id },
|
|
1860
|
+
});
|
|
1861
|
+
return validateDatasetRecordOrThrow({
|
|
1862
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1863
|
+
recordType: "dataset_record",
|
|
1864
|
+
recordId,
|
|
1865
|
+
datasetKind: "model_call",
|
|
1866
|
+
createdAt: input.now().toISOString(),
|
|
1867
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1868
|
+
inputRef,
|
|
1869
|
+
outputRef,
|
|
1870
|
+
quality: {
|
|
1871
|
+
score: modelCallScore(input.call),
|
|
1872
|
+
labels: collectModelCallLabels(input.call),
|
|
1873
|
+
reviewed: false,
|
|
1874
|
+
},
|
|
1875
|
+
privacy: input.privacy,
|
|
1876
|
+
metadata: {
|
|
1877
|
+
collectionMode: "gateway_trace_collector",
|
|
1878
|
+
exampleType: "model_stage",
|
|
1879
|
+
modelCallId: input.call.id,
|
|
1880
|
+
taskId: input.call.taskId,
|
|
1881
|
+
role: input.call.role,
|
|
1882
|
+
status: input.call.status,
|
|
1883
|
+
repairAttempts: modelCallRepairAttempts(input.call),
|
|
1884
|
+
schemaFailureRecordId: input.schemaFailureRecordId,
|
|
1885
|
+
},
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
async buildSchemaFailureRecord(input) {
|
|
1889
|
+
const recordId = schemaFailureRecordId(input.input.result.runId, input.call, input.index);
|
|
1890
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1891
|
+
scope: input.scope,
|
|
1892
|
+
ownerType: "schema_failure_example",
|
|
1893
|
+
ownerId: recordId,
|
|
1894
|
+
kind: "dataset",
|
|
1895
|
+
payload: {
|
|
1896
|
+
modelCallId: input.call.id,
|
|
1897
|
+
role: input.call.role,
|
|
1898
|
+
taskId: input.call.taskId,
|
|
1899
|
+
failedInput: input.call.input,
|
|
1900
|
+
failedOutput: input.call.output,
|
|
1901
|
+
},
|
|
1902
|
+
privacyFlags: input.privacyFlags,
|
|
1903
|
+
metadata: { part: "schema_failure_input", modelCallId: input.call.id },
|
|
1904
|
+
});
|
|
1905
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1906
|
+
scope: input.scope,
|
|
1907
|
+
ownerType: "schema_failure_example",
|
|
1908
|
+
ownerId: recordId,
|
|
1909
|
+
kind: "dataset",
|
|
1910
|
+
payload: {
|
|
1911
|
+
status: input.call.status,
|
|
1912
|
+
errorCode: input.call.errorCode,
|
|
1913
|
+
errorMessage: input.call.errorMessage,
|
|
1914
|
+
repairAttempts: modelCallRepairAttempts(input.call),
|
|
1915
|
+
correctedByModelCallId: input.corrected?.id,
|
|
1916
|
+
correctedOutput: input.corrected?.output,
|
|
1917
|
+
},
|
|
1918
|
+
privacyFlags: input.privacyFlags,
|
|
1919
|
+
metadata: { part: "schema_failure_output", modelCallId: input.call.id },
|
|
1920
|
+
});
|
|
1921
|
+
return validateDatasetRecordOrThrow({
|
|
1922
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1923
|
+
recordType: "dataset_record",
|
|
1924
|
+
recordId,
|
|
1925
|
+
datasetKind: "evaluation",
|
|
1926
|
+
createdAt: input.now().toISOString(),
|
|
1927
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1928
|
+
inputRef,
|
|
1929
|
+
outputRef,
|
|
1930
|
+
quality: {
|
|
1931
|
+
score: 0,
|
|
1932
|
+
labels: collectSchemaFailureLabels(input.call),
|
|
1933
|
+
reviewed: false,
|
|
1934
|
+
},
|
|
1935
|
+
privacy: input.privacy,
|
|
1936
|
+
metadata: {
|
|
1937
|
+
collectionMode: "gateway_trace_collector",
|
|
1938
|
+
exampleType: "schema_failure",
|
|
1939
|
+
failedAttemptModelCallId: input.call.id,
|
|
1940
|
+
failedAttemptRecordId: input.failedAttemptRecordId,
|
|
1941
|
+
correctedByModelCallId: input.corrected?.id,
|
|
1942
|
+
correctedByRecordId: input.correctedRecordId,
|
|
1943
|
+
correctedInSameCall: input.corrected?.id === input.call.id,
|
|
1944
|
+
},
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
async buildGoldTargetRecord(input) {
|
|
1948
|
+
const targetId = safeDatasetIdPart(input.target.id ?? `${input.target.kind}-${input.index + 1}`);
|
|
1949
|
+
const recordId = `dataset-${input.input.result.runId}-gold-${targetId}`;
|
|
1950
|
+
const sourceModelRecordId = input.target.sourceModelCallId
|
|
1951
|
+
? input.modelRecordIds.get(input.target.sourceModelCallId)
|
|
1952
|
+
: undefined;
|
|
1953
|
+
const failedAttemptRecordId = input.target.failedAttemptRecordId
|
|
1954
|
+
?? (input.target.failedAttemptModelCallId
|
|
1955
|
+
? input.modelRecordIds.get(input.target.failedAttemptModelCallId)
|
|
1956
|
+
: undefined);
|
|
1957
|
+
const inputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1958
|
+
scope: input.scope,
|
|
1959
|
+
ownerType: "gold_target",
|
|
1960
|
+
ownerId: recordId,
|
|
1961
|
+
kind: "dataset",
|
|
1962
|
+
payload: input.target.input ?? {
|
|
1963
|
+
query: input.input.request.query,
|
|
1964
|
+
sourceRecordId: input.target.sourceRecordId ?? sourceModelRecordId ?? `dataset-${input.input.result.runId}`,
|
|
1965
|
+
},
|
|
1966
|
+
privacyFlags: input.privacyFlags,
|
|
1967
|
+
metadata: { part: "gold_target_input", goldTargetKind: input.target.kind },
|
|
1968
|
+
});
|
|
1969
|
+
const outputRef = await putGatewayDatasetObject(input.objectStore, {
|
|
1970
|
+
scope: input.scope,
|
|
1971
|
+
ownerType: "gold_target",
|
|
1972
|
+
ownerId: recordId,
|
|
1973
|
+
kind: "dataset",
|
|
1974
|
+
payload: input.target.target ?? {
|
|
1975
|
+
answer: input.input.result.answer,
|
|
1976
|
+
status: input.input.result.status,
|
|
1977
|
+
},
|
|
1978
|
+
privacyFlags: input.privacyFlags,
|
|
1979
|
+
metadata: { part: "gold_target_output", goldTargetKind: input.target.kind },
|
|
1980
|
+
});
|
|
1981
|
+
return validateDatasetRecordOrThrow({
|
|
1982
|
+
schemaVersion: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
|
|
1983
|
+
recordType: "dataset_record",
|
|
1984
|
+
recordId,
|
|
1985
|
+
datasetKind: "curated_example",
|
|
1986
|
+
createdAt: input.now().toISOString(),
|
|
1987
|
+
sourceGatewayRecordId: input.input.result.runId,
|
|
1988
|
+
inputRef,
|
|
1989
|
+
outputRef,
|
|
1990
|
+
quality: {
|
|
1991
|
+
score: input.target.score ?? (input.target.kind === "accepted" ? 0.85 : 0.95),
|
|
1992
|
+
labels: collectGoldTargetLabels(input.target),
|
|
1993
|
+
reviewed: input.target.kind === "reviewed",
|
|
1994
|
+
},
|
|
1995
|
+
privacy: input.privacy,
|
|
1996
|
+
metadata: {
|
|
1997
|
+
collectionMode: "gateway_trace_collector",
|
|
1998
|
+
exampleType: "gold_target",
|
|
1999
|
+
goldTargetKind: input.target.kind,
|
|
2000
|
+
sourceRecordId: input.target.sourceRecordId,
|
|
2001
|
+
sourceModelCallId: input.target.sourceModelCallId,
|
|
2002
|
+
sourceModelRecordId,
|
|
2003
|
+
failedAttemptModelCallId: input.target.failedAttemptModelCallId,
|
|
2004
|
+
failedAttemptRecordId,
|
|
2005
|
+
reviewerId: input.target.reviewerId,
|
|
2006
|
+
reasons: input.target.reasons,
|
|
2007
|
+
deletionGroupId: outputRef.deletionGroupId,
|
|
2008
|
+
linkedDeletionGroupIds: uniqueStrings([
|
|
2009
|
+
inputRef.deletionGroupId,
|
|
2010
|
+
outputRef.deletionGroupId,
|
|
2011
|
+
]),
|
|
2012
|
+
...(input.target.metadata ?? {}),
|
|
2013
|
+
},
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
export const createGatewayDatasetCollector = () => new GatewayDatasetCollector();
|
|
2018
|
+
export const buildGatewayDatasetCollectInputFromGatewayResult = async (input) => {
|
|
2019
|
+
return new GatewayDatasetCollector().buildCollectInput(input);
|
|
2020
|
+
};
|
|
2021
|
+
export const collectGatewayDatasetResult = async (input) => {
|
|
2022
|
+
if (input.enabled === false) {
|
|
2023
|
+
return { accepted: true, status: "skipped", recordCount: 0 };
|
|
2024
|
+
}
|
|
2025
|
+
const collectInput = await buildGatewayDatasetCollectInputFromGatewayResult(input);
|
|
2026
|
+
try {
|
|
2027
|
+
return await input.store.collect(collectInput);
|
|
2028
|
+
}
|
|
2029
|
+
catch (error) {
|
|
2030
|
+
if (!input.fallbackStore)
|
|
2031
|
+
throw error;
|
|
2032
|
+
const fallback = await input.fallbackStore.collect(collectInput);
|
|
2033
|
+
return {
|
|
2034
|
+
...fallback,
|
|
2035
|
+
fallbackUsed: true,
|
|
2036
|
+
errors: [...(fallback.errors ?? []), errorMessage(error)],
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
export const collectGatewayDatasetResultNonBlocking = (input) => {
|
|
2041
|
+
if (input.enabled === false) {
|
|
2042
|
+
return { accepted: true, status: "skipped", recordCount: 0 };
|
|
2043
|
+
}
|
|
2044
|
+
const queued = {
|
|
2045
|
+
accepted: true,
|
|
2046
|
+
status: "queued",
|
|
2047
|
+
recordCount: 1,
|
|
2048
|
+
idempotencyKey: input.idempotencyKey,
|
|
2049
|
+
};
|
|
2050
|
+
const run = async () => {
|
|
2051
|
+
try {
|
|
2052
|
+
const result = await collectGatewayDatasetResult(input);
|
|
2053
|
+
input.onResult?.(result);
|
|
2054
|
+
}
|
|
2055
|
+
catch (error) {
|
|
2056
|
+
input.onError?.(error);
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
if (typeof queueMicrotask === "function") {
|
|
2060
|
+
queueMicrotask(() => {
|
|
2061
|
+
void run();
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
else {
|
|
2065
|
+
setTimeout(() => {
|
|
2066
|
+
void run();
|
|
2067
|
+
}, 0);
|
|
2068
|
+
}
|
|
2069
|
+
return queued;
|
|
2070
|
+
};
|