@atlanai/sdk 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/client.d.ts +4 -1
- package/dist/client.js +13 -1
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +12 -1
- package/dist/evals.d.ts +5 -1
- package/dist/evals.js +285 -70
- package/dist/index.d.ts +1 -0
- package/dist/manifest.json +84 -11
- package/dist/raw/agent/apis/AgentsApi.d.ts +29 -0
- package/dist/raw/agent/apis/AgentsApi.js +62 -1
- package/dist/raw/agent/apis/SessionsApi.d.ts +2 -2
- package/dist/raw/agent/apis/SessionsApi.js +2 -2
- package/dist/raw/agent/models/AgentAgentArtifactView.d.ts +48 -0
- package/dist/raw/agent/models/AgentAgentArtifactView.js +13 -0
- package/dist/raw/agent/models/AgentAgentEnvironmentArtifactView.d.ts +12 -0
- package/dist/raw/agent/models/AgentAgentEnvironmentArtifactView.js +2 -0
- package/dist/raw/agent/models/AgentAgentEnvironmentMeta.d.ts +12 -0
- package/dist/raw/agent/models/AgentAgentEnvironmentMeta.js +2 -0
- package/dist/raw/agent/models/AgentAgentMeta.d.ts +48 -0
- package/dist/raw/agent/models/AgentAgentMeta.js +13 -0
- package/dist/raw/agent/models/AgentAgentProviderArtifactView.d.ts +55 -2
- package/dist/raw/agent/models/AgentAgentProviderArtifactView.js +8 -0
- package/dist/raw/agent/models/AgentAgentProviderCreate.d.ts +24 -3
- package/dist/raw/agent/models/AgentAgentProviderCreate.js +4 -0
- package/dist/raw/agent/models/AgentAgentProviderMeta.d.ts +56 -13
- package/dist/raw/agent/models/AgentAgentProviderMeta.js +8 -0
- package/dist/raw/agent/models/AgentAgentProviderPatch.d.ts +13 -0
- package/dist/raw/agent/models/AgentAgentProviderPatch.js +4 -0
- package/dist/raw/agent/models/AgentAgentProviderType.d.ts +19 -9
- package/dist/raw/agent/models/AgentAgentProviderType.js +18 -8
- package/dist/raw/agent/models/AgentListAgentFrameworks401Response.d.ts +1 -0
- package/dist/raw/agent/models/AgentListAgentFrameworks401Response.js +1 -0
- package/dist/raw/agent/models/AgentPageSessionEventView.d.ts +3 -3
- package/dist/raw/agent/models/AgentPageSessionEventView.js +3 -3
- package/dist/raw/agent/models/AgentRemoteSessionEvent.d.ts +38 -0
- package/dist/raw/agent/models/AgentRemoteSessionEvent.js +58 -0
- package/dist/raw/agent/models/AgentSessionArtifactView.d.ts +25 -0
- package/dist/raw/agent/models/AgentSessionArtifactView.js +2 -0
- package/dist/raw/agent/models/AgentSessionEventView.d.ts +30 -0
- package/dist/raw/agent/models/AgentSessionEventView.js +57 -0
- package/dist/raw/agent/models/AgentSessionMeta.d.ts +25 -0
- package/dist/raw/agent/models/AgentSessionMeta.js +2 -0
- package/dist/raw/agent/models/AgentSessionStreamFrame.d.ts +8 -6
- package/dist/raw/agent/models/AgentSessionStreamFrame.js +7 -0
- package/dist/raw/agent/models/AgentSyncState.d.ts +27 -0
- package/dist/raw/agent/models/AgentSyncState.js +55 -0
- package/dist/raw/agent/models/TriggerBody.d.ts +52 -0
- package/dist/raw/agent/models/TriggerBody.js +58 -0
- package/dist/raw/agent/models/index.d.ts +4 -1
- package/dist/raw/agent/models/index.js +4 -1
- package/dist/raw/eval/models/EvalListDatasets401Response.d.ts +1 -0
- package/dist/raw/eval/models/EvalListDatasets401Response.js +1 -0
- package/dist/raw/file/models/FileList401Response.d.ts +1 -0
- package/dist/raw/file/models/FileList401Response.js +1 -0
- package/dist/raw/mcp/apis/ServersApi.d.ts +2 -2
- package/dist/raw/mcp/apis/ServersApi.js +2 -2
- package/dist/raw/mcp/models/McpConnectionState.d.ts +2 -1
- package/dist/raw/mcp/models/McpConnectionState.js +2 -1
- package/dist/raw/mcp/models/McpListServers401Response.d.ts +1 -0
- package/dist/raw/mcp/models/McpListServers401Response.js +1 -0
- package/dist/raw/mcp/models/McpServerTestResponse.d.ts +13 -2
- package/dist/raw/model/models/ModelListProviders401Response.d.ts +1 -0
- package/dist/raw/model/models/ModelListProviders401Response.js +1 -0
- package/dist/raw/registry/apis/WorkspacesApi.d.ts +28 -0
- package/dist/raw/registry/apis/WorkspacesApi.js +61 -1
- package/dist/raw/registry/models/CreateWorkspaceBody.d.ts +9 -0
- package/dist/raw/registry/models/CreateWorkspaceBody.js +2 -0
- package/dist/raw/registry/models/RegistryAggregate401Response.d.ts +1 -0
- package/dist/raw/registry/models/RegistryAggregate401Response.js +1 -0
- package/dist/raw/registry/models/RegistryWorkspaceView.d.ts +9 -0
- package/dist/raw/registry/models/RegistryWorkspaceView.js +4 -0
- package/dist/raw/registry/models/SearchRequest.d.ts +1 -1
- package/dist/raw/registry/models/SetVisibilityBody.d.ts +33 -0
- package/dist/raw/registry/models/SetVisibilityBody.js +50 -0
- package/dist/raw/registry/models/index.d.ts +1 -0
- package/dist/raw/registry/models/index.js +1 -0
- package/dist/raw/secret/apis/OauthApi.d.ts +143 -0
- package/dist/raw/secret/apis/OauthApi.js +335 -0
- package/dist/raw/secret/apis/index.d.ts +1 -0
- package/dist/raw/secret/apis/index.js +19 -0
- package/dist/raw/secret/index.d.ts +1 -0
- package/dist/raw/secret/index.js +1 -0
- package/dist/raw/secret/models/GatewayOauthCompleteRequest.d.ts +32 -0
- package/dist/raw/secret/models/GatewayOauthCompleteRequest.js +50 -0
- package/dist/raw/secret/models/GatewayOauthCompletion.d.ts +46 -0
- package/dist/raw/secret/models/GatewayOauthCompletion.js +55 -0
- package/dist/raw/secret/models/GatewayOauthConnection.d.ts +83 -0
- package/dist/raw/secret/models/GatewayOauthConnection.js +83 -0
- package/dist/raw/secret/models/GatewayOauthOwner.d.ts +31 -0
- package/dist/raw/secret/models/GatewayOauthOwner.js +59 -0
- package/dist/raw/secret/models/GatewayOauthProviderView.d.ts +69 -0
- package/dist/raw/secret/models/GatewayOauthProviderView.js +69 -0
- package/dist/raw/secret/models/GatewayOauthStartRequest.d.ts +52 -0
- package/dist/raw/secret/models/GatewayOauthStartRequest.js +59 -0
- package/dist/raw/secret/models/GatewayOauthStarted.d.ts +39 -0
- package/dist/raw/secret/models/GatewayOauthStarted.js +54 -0
- package/dist/raw/secret/models/SecretCompleteOauth400Response.d.ts +70 -0
- package/dist/raw/secret/models/SecretCompleteOauth400Response.js +80 -0
- package/dist/raw/secret/models/SecretCompleteOauth413Response.d.ts +64 -0
- package/dist/raw/secret/models/SecretCompleteOauth413Response.js +74 -0
- package/dist/raw/secret/models/SecretCompleteOauth415Response.d.ts +64 -0
- package/dist/raw/secret/models/SecretCompleteOauth415Response.js +74 -0
- package/dist/raw/secret/models/SecretCompleteOauth422Response.d.ts +63 -0
- package/dist/raw/secret/models/SecretCompleteOauth422Response.js +75 -0
- package/dist/raw/secret/models/SecretDisconnectOauth400Response.d.ts +67 -0
- package/dist/raw/secret/models/SecretDisconnectOauth400Response.js +77 -0
- package/dist/raw/secret/models/SecretOauthCallback400Response.d.ts +68 -0
- package/dist/raw/secret/models/SecretOauthCallback400Response.js +78 -0
- package/dist/raw/secret/models/SecretOauthCallback401Response.d.ts +80 -0
- package/dist/raw/secret/models/SecretOauthCallback401Response.js +90 -0
- package/dist/raw/secret/models/SecretOauthCallback403Response.d.ts +66 -0
- package/dist/raw/secret/models/SecretOauthCallback403Response.js +76 -0
- package/dist/raw/secret/models/SecretOauthCallback404Response.d.ts +65 -0
- package/dist/raw/secret/models/SecretOauthCallback404Response.js +75 -0
- package/dist/raw/secret/models/SecretOauthCallback500Response.d.ts +64 -0
- package/dist/raw/secret/models/SecretOauthCallback500Response.js +74 -0
- package/dist/raw/secret/models/SecretOauthCallback503Response.d.ts +66 -0
- package/dist/raw/secret/models/SecretOauthCallback503Response.js +76 -0
- package/dist/raw/secret/models/SecretOauthCallback504Response.d.ts +63 -0
- package/dist/raw/secret/models/SecretOauthCallback504Response.js +75 -0
- package/dist/raw/secret/models/index.d.ts +19 -0
- package/dist/raw/secret/models/index.js +19 -0
- package/dist/raw/skill/models/SkillInsightSchemas401Response.d.ts +1 -0
- package/dist/raw/skill/models/SkillInsightSchemas401Response.js +1 -0
- package/dist/resource-types.d.ts +670 -0
- package/dist/resource-types.js +2 -0
- package/dist/resources.json +86 -0
- package/dist/tracing/client.d.ts +8 -1
- package/dist/tracing/client.js +43 -16
- package/dist/tracing/config.d.ts +3 -1
- package/dist/tracing/config.js +7 -2
- package/dist/tracing/exporter.d.ts +19 -1
- package/dist/tracing/exporter.js +56 -14
- package/dist/tracing/propagation.d.ts +2 -0
- package/dist/tracing/propagation.js +16 -0
- package/dist/tracing/semconv.d.ts +4 -0
- package/dist/tracing/semconv.js +14 -2
- package/dist/tracing/spans.d.ts +2 -0
- package/dist/tracing/spans.js +12 -1
- package/dist/tracing/version.d.ts +1 -1
- package/dist/tracing/version.js +1 -1
- package/package.json +1 -1
- package/dist/raw/agent/models/Box.d.ts +0 -342
- package/dist/raw/agent/models/Box.js +0 -167
package/dist/evals.js
CHANGED
|
@@ -62,6 +62,9 @@ function manifestItem(item) {
|
|
|
62
62
|
if (item.version.toLowerCase() === "latest") {
|
|
63
63
|
throw new Error("context item version must be pinned; 'latest' is not a version");
|
|
64
64
|
}
|
|
65
|
+
if (item.artifactKind !== undefined && !CONTEXT_KIND.test(item.artifactKind)) {
|
|
66
|
+
throw new Error("context item artifactKind must be a lowercase identifier");
|
|
67
|
+
}
|
|
65
68
|
if (item.artifactId !== undefined) {
|
|
66
69
|
validText(item.artifactId, "context item artifactId", 200);
|
|
67
70
|
}
|
|
@@ -69,11 +72,18 @@ function manifestItem(item) {
|
|
|
69
72
|
(!Number.isInteger(item.versionOrdinal) || item.versionOrdinal < 1)) {
|
|
70
73
|
throw new Error("context item versionOrdinal must be an integer of at least 1");
|
|
71
74
|
}
|
|
75
|
+
if ((item.artifactId === undefined) !== (item.versionOrdinal === undefined)) {
|
|
76
|
+
throw new Error("context item Registry pins require artifactId and versionOrdinal");
|
|
77
|
+
}
|
|
78
|
+
if (item.artifactKind !== undefined && item.artifactId === undefined) {
|
|
79
|
+
throw new Error("context item artifactKind requires a Registry artifact pin");
|
|
80
|
+
}
|
|
72
81
|
return {
|
|
73
82
|
kind: item.kind,
|
|
74
83
|
name: item.name,
|
|
75
84
|
digest: item.digest,
|
|
76
85
|
version: item.version,
|
|
86
|
+
...(item.artifactKind === undefined ? {} : { artifact_kind: item.artifactKind }),
|
|
77
87
|
...(item.artifactId === undefined ? {} : { artifact_id: item.artifactId }),
|
|
78
88
|
...(item.versionOrdinal === undefined ? {} : { version_ordinal: item.versionOrdinal }),
|
|
79
89
|
};
|
|
@@ -237,7 +247,7 @@ async function startExperiment(client, dataset, body, options = {}) {
|
|
|
237
247
|
}
|
|
238
248
|
// ---------------------------------------------------------------------------
|
|
239
249
|
// Declarative evaluator
|
|
240
|
-
const DEFAULT_GATEWAY_ORIGIN = "https://
|
|
250
|
+
const DEFAULT_GATEWAY_ORIGIN = "https://api.atlan.com";
|
|
241
251
|
function tracingModule() {
|
|
242
252
|
// The root client is CommonJS while tracing has its own NodeNext build. A
|
|
243
253
|
// runtime require keeps the two compiler targets independent and resolves to
|
|
@@ -311,6 +321,20 @@ async function collectData(data) {
|
|
|
311
321
|
}
|
|
312
322
|
return cases;
|
|
313
323
|
}
|
|
324
|
+
async function casesDigest(cases) {
|
|
325
|
+
const normalized = cases.map((item, index) => ({
|
|
326
|
+
id: item.id ?? `case-${index + 1}`,
|
|
327
|
+
input: item.input,
|
|
328
|
+
expected: item.expected ?? null,
|
|
329
|
+
metadata: item.metadata ?? {},
|
|
330
|
+
tags: item.tags ?? [],
|
|
331
|
+
}));
|
|
332
|
+
if (new Set(normalized.map((item) => item.id)).size !== cases.length) {
|
|
333
|
+
throw new Error("Eval case IDs must be unique");
|
|
334
|
+
}
|
|
335
|
+
const hash = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalJson(normalized)));
|
|
336
|
+
return "sha256:" + [...new Uint8Array(hash)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
337
|
+
}
|
|
314
338
|
async function datasetCases(resources, datasetId) {
|
|
315
339
|
const records = [];
|
|
316
340
|
let offset = 0;
|
|
@@ -394,11 +418,6 @@ async function experimentResults(resources, experimentId) {
|
|
|
394
418
|
}
|
|
395
419
|
return results;
|
|
396
420
|
}
|
|
397
|
-
async function existingCaseIds(resources, experimentId) {
|
|
398
|
-
return new Set((await experimentResults(resources, experimentId))
|
|
399
|
-
.map((item) => field(item, "caseId"))
|
|
400
|
-
.filter((caseId) => typeof caseId === "string" && caseId.length > 0));
|
|
401
|
-
}
|
|
402
421
|
async function findScorer(resources, name, workspaceId) {
|
|
403
422
|
const matches = [];
|
|
404
423
|
let offset = 0;
|
|
@@ -434,6 +453,39 @@ function scorerIdentity(value, workspaceId) {
|
|
|
434
453
|
}
|
|
435
454
|
return { id, version };
|
|
436
455
|
}
|
|
456
|
+
async function scorerDefinition(definition) {
|
|
457
|
+
const outputs = definition.outputs ?? { [definition.name]: { type: "number", minimum: 0, maximum: 1 } };
|
|
458
|
+
if (definition.spec !== undefined)
|
|
459
|
+
return { spec: definition.spec, outputs };
|
|
460
|
+
if (!globalThis.crypto?.subtle)
|
|
461
|
+
throw new Error("Web Crypto is required to identify a scorer");
|
|
462
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(String(definition.scorer)));
|
|
463
|
+
const codeDigest = "sha256:" +
|
|
464
|
+
[...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
465
|
+
return {
|
|
466
|
+
outputs,
|
|
467
|
+
spec: {
|
|
468
|
+
executor: "external",
|
|
469
|
+
sdk: "@atlanai/sdk",
|
|
470
|
+
entrypoint: definition.scorer.name || "anonymous",
|
|
471
|
+
code_digest: codeDigest,
|
|
472
|
+
},
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
async function reconcileScorer(resources, existing, workspaceId, definition) {
|
|
476
|
+
const identity = scorerIdentity(existing, workspaceId);
|
|
477
|
+
const scorerKind = definition.scorerKind ?? "code";
|
|
478
|
+
const scope = definition.scope ?? "result";
|
|
479
|
+
if (field(existing, "scorerKind") !== scorerKind || field(existing, "scope") !== scope) {
|
|
480
|
+
throw new Error(`scorer '${definition.name}' reuses a Registry name with a different identity`);
|
|
481
|
+
}
|
|
482
|
+
const desired = await scorerDefinition(definition);
|
|
483
|
+
if (canonicalJson(field(existing, "spec")) === canonicalJson(desired.spec) &&
|
|
484
|
+
canonicalJson(field(existing, "outputs")) === canonicalJson(desired.outputs)) {
|
|
485
|
+
return identity;
|
|
486
|
+
}
|
|
487
|
+
return scorerIdentity(await resources.scorers.update(identity.id, desired), workspaceId);
|
|
488
|
+
}
|
|
437
489
|
async function resolveScorer(resources, workspaceId, evalName, definition) {
|
|
438
490
|
if (!definition.name || /[\r\n]/.test(definition.name)) {
|
|
439
491
|
throw new Error("each scorer needs a non-empty single-line name");
|
|
@@ -448,7 +500,7 @@ async function resolveScorer(resources, workspaceId, evalName, definition) {
|
|
|
448
500
|
if (!Number.isInteger(definition.scorerVersion) || definition.scorerVersion < 1) {
|
|
449
501
|
throw new Error(`scorer '${definition.name}' has an invalid scorerVersion`);
|
|
450
502
|
}
|
|
451
|
-
return
|
|
503
|
+
return scorerIdentity(await resources.scorers.versions.get(identity.id, definition.scorerVersion), workspaceId);
|
|
452
504
|
}
|
|
453
505
|
return identity;
|
|
454
506
|
}
|
|
@@ -457,8 +509,10 @@ async function resolveScorer(resources, workspaceId, evalName, definition) {
|
|
|
457
509
|
}
|
|
458
510
|
const artifactName = definition.registryName ?? `${slug(evalName, "eval")}-${slug(definition.name, "score")}`;
|
|
459
511
|
const existing = await findScorer(resources, artifactName, workspaceId);
|
|
460
|
-
if (existing !== undefined)
|
|
461
|
-
return
|
|
512
|
+
if (existing !== undefined) {
|
|
513
|
+
return reconcileScorer(resources, existing, workspaceId, definition);
|
|
514
|
+
}
|
|
515
|
+
const desired = await scorerDefinition(definition);
|
|
462
516
|
try {
|
|
463
517
|
return scorerIdentity(await resources.scorers.create({
|
|
464
518
|
workspaceId,
|
|
@@ -468,12 +522,8 @@ async function resolveScorer(resources, workspaceId, evalName, definition) {
|
|
|
468
522
|
sourceId: `atlan-eval:${slug(evalName, "eval")}:${slug(definition.name, "score")}`,
|
|
469
523
|
scorerKind: definition.scorerKind ?? "code",
|
|
470
524
|
scope: definition.scope ?? "result",
|
|
471
|
-
outputs:
|
|
472
|
-
spec:
|
|
473
|
-
executor: "external",
|
|
474
|
-
sdk: "@atlanai/sdk",
|
|
475
|
-
entrypoint: definition.scorer.name || "anonymous",
|
|
476
|
-
},
|
|
525
|
+
outputs: desired.outputs,
|
|
526
|
+
spec: desired.spec,
|
|
477
527
|
}), workspaceId);
|
|
478
528
|
}
|
|
479
529
|
catch (error) {
|
|
@@ -482,7 +532,7 @@ async function resolveScorer(resources, workspaceId, evalName, definition) {
|
|
|
482
532
|
const raced = await findScorer(resources, artifactName, workspaceId);
|
|
483
533
|
if (raced === undefined)
|
|
484
534
|
throw error;
|
|
485
|
-
return
|
|
535
|
+
return reconcileScorer(resources, raced, workspaceId, definition);
|
|
486
536
|
}
|
|
487
537
|
}
|
|
488
538
|
function normalizedScores(value, defaultName) {
|
|
@@ -546,12 +596,41 @@ function localSummary(results, durationMs) {
|
|
|
546
596
|
},
|
|
547
597
|
};
|
|
548
598
|
}
|
|
599
|
+
async function traceSpans(resources, experimentId, traceId) {
|
|
600
|
+
const spans = [];
|
|
601
|
+
const seenCursors = new Set();
|
|
602
|
+
let cursor;
|
|
603
|
+
while (true) {
|
|
604
|
+
const page = await resources.experiments.traces.listSpans({
|
|
605
|
+
experimentId,
|
|
606
|
+
traceId,
|
|
607
|
+
limit: 500,
|
|
608
|
+
fields: "io",
|
|
609
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
610
|
+
});
|
|
611
|
+
const items = field(page, "items");
|
|
612
|
+
if (Array.isArray(items))
|
|
613
|
+
spans.push(...items);
|
|
614
|
+
const nextCursor = field(field(page, "page"), "nextCursor");
|
|
615
|
+
if (typeof nextCursor !== "string" || nextCursor.length === 0)
|
|
616
|
+
return spans;
|
|
617
|
+
if (seenCursors.has(nextCursor)) {
|
|
618
|
+
throw new Error("experiment trace span pagination repeated a cursor");
|
|
619
|
+
}
|
|
620
|
+
seenCursors.add(nextCursor);
|
|
621
|
+
cursor = nextCursor;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
549
624
|
async function listedTraceIds(resources, experimentId) {
|
|
550
625
|
const traceIds = new Set();
|
|
551
626
|
const seenCursors = new Set();
|
|
552
627
|
let cursor;
|
|
628
|
+
const windowEnd = Math.floor(Date.now() / 1_000);
|
|
553
629
|
while (true) {
|
|
554
|
-
const page = await resources.experiments.traces.list(
|
|
630
|
+
const page = await resources.experiments.traces.list({
|
|
631
|
+
experimentId,
|
|
632
|
+
startTimeUnixSeconds: windowEnd - 30 * 24 * 60 * 60,
|
|
633
|
+
endTimeUnixSeconds: windowEnd,
|
|
555
634
|
limit: 500,
|
|
556
635
|
...(cursor === undefined ? {} : { cursor }),
|
|
557
636
|
});
|
|
@@ -573,14 +652,73 @@ async function listedTraceIds(resources, experimentId) {
|
|
|
573
652
|
cursor = nextCursor;
|
|
574
653
|
}
|
|
575
654
|
}
|
|
576
|
-
|
|
655
|
+
function spanAttributes(span) {
|
|
656
|
+
const attributes = field(span, "attributes");
|
|
657
|
+
return isRecord(attributes) ? attributes : {};
|
|
658
|
+
}
|
|
659
|
+
function missingTraceEvidence(spans, expected) {
|
|
660
|
+
const missing = [];
|
|
661
|
+
const byId = new Map();
|
|
662
|
+
for (const span of spans) {
|
|
663
|
+
const spanId = field(span, "spanId");
|
|
664
|
+
if (typeof spanId === "string")
|
|
665
|
+
byId.set(spanId, span);
|
|
666
|
+
}
|
|
667
|
+
const root = expected.rootSpanId === undefined
|
|
668
|
+
? spans.find((span) => spanAttributes(span)["atlan.eval.case_id"] === expected.caseId &&
|
|
669
|
+
spanAttributes(span)["atlan.eval.evidence_complete"] === true)
|
|
670
|
+
: byId.get(expected.rootSpanId);
|
|
671
|
+
if (root === undefined) {
|
|
672
|
+
missing.push("case root");
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
const attributes = spanAttributes(root);
|
|
676
|
+
if (attributes["atlan.eval.evidence_complete"] !== true) {
|
|
677
|
+
missing.push("producer completion marker");
|
|
678
|
+
}
|
|
679
|
+
if (attributes["atlan.eval.case_id"] !== expected.caseId)
|
|
680
|
+
missing.push("case identity");
|
|
681
|
+
if (expected.datasetRecordId !== undefined &&
|
|
682
|
+
attributes["atlan.eval.dataset_record_id"] !== expected.datasetRecordId) {
|
|
683
|
+
missing.push("dataset record identity");
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const absentIds = [...expected.requiredSpanIds].filter((spanId) => !byId.has(spanId));
|
|
687
|
+
if (absentIds.length > 0)
|
|
688
|
+
missing.push(`${absentIds.length} SDK-owned spans`);
|
|
689
|
+
if (spans.some((span) => spanAttributes(span)["atlan.eval.evidence_truncated"] === true)) {
|
|
690
|
+
missing.push("untruncated payloads");
|
|
691
|
+
}
|
|
692
|
+
for (const [scoreName, scorer] of expected.scores) {
|
|
693
|
+
const present = spans.some((span) => field(span, "scoreName") === scoreName &&
|
|
694
|
+
field(span, "scorerId") === scorer.id &&
|
|
695
|
+
field(span, "scorerVersion") === scorer.version);
|
|
696
|
+
if (!present)
|
|
697
|
+
missing.push(`score '${scoreName}'`);
|
|
698
|
+
}
|
|
699
|
+
return missing;
|
|
700
|
+
}
|
|
701
|
+
async function verifyTraceEvidence(resources, experimentId, expected, timeoutMs) {
|
|
577
702
|
const deadline = Date.now() + timeoutMs;
|
|
578
|
-
let missing =
|
|
703
|
+
let missing = new Map();
|
|
579
704
|
let lastError;
|
|
580
705
|
while (true) {
|
|
581
706
|
try {
|
|
582
|
-
|
|
583
|
-
|
|
707
|
+
missing = new Map();
|
|
708
|
+
if (typeof resources.experiments.traces.listSpans === "function") {
|
|
709
|
+
for (const [traceId, expectation] of expected) {
|
|
710
|
+
const gaps = missingTraceEvidence(await traceSpans(resources, experimentId, traceId), expectation);
|
|
711
|
+
if (gaps.length > 0)
|
|
712
|
+
missing.set(traceId, gaps);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
const observed = await listedTraceIds(resources, experimentId);
|
|
717
|
+
for (const traceId of expected.keys()) {
|
|
718
|
+
if (!observed.has(traceId))
|
|
719
|
+
missing.set(traceId, ["trace read"]);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
584
722
|
lastError = undefined;
|
|
585
723
|
}
|
|
586
724
|
catch (error) {
|
|
@@ -588,15 +726,16 @@ async function verifyTraceJoins(resources, experimentId, expected, timeoutMs) {
|
|
|
588
726
|
![404, 429, 500, 502, 503, 504].includes(error.status)) {
|
|
589
727
|
throw error;
|
|
590
728
|
}
|
|
591
|
-
missing = expected;
|
|
729
|
+
missing = new Map([...expected.keys()].map((traceId) => [traceId, ["trace read"]]));
|
|
592
730
|
lastError = error;
|
|
593
731
|
}
|
|
594
732
|
if (missing.size === 0)
|
|
595
733
|
return;
|
|
596
734
|
const remaining = deadline - Date.now();
|
|
597
735
|
if (remaining <= 0) {
|
|
598
|
-
const failure = new Error(`Registry did not expose ${missing.size} of
|
|
599
|
-
|
|
736
|
+
const failure = new Error(`Registry did not expose complete trace evidence for ${missing.size} of ` +
|
|
737
|
+
`${expected.size} case traces before the verification deadline: ` +
|
|
738
|
+
[...missing].map(([traceId, gaps]) => `${traceId} (${gaps.join(", ")})`).join("; "));
|
|
600
739
|
if (lastError !== undefined)
|
|
601
740
|
failure.cause = lastError;
|
|
602
741
|
throw failure;
|
|
@@ -632,6 +771,11 @@ function defaultConnections(name, options) {
|
|
|
632
771
|
if (sampleRate !== undefined && sampleRate !== 1) {
|
|
633
772
|
throw new Error("Eval tracing requires sampleRate=1 so no case trace is sampled out");
|
|
634
773
|
}
|
|
774
|
+
const traceContent = traceLogger.client.config
|
|
775
|
+
?.traceContent;
|
|
776
|
+
if (traceContent === false) {
|
|
777
|
+
throw new Error("Eval tracing requires traceContent=true so case evidence is readable");
|
|
778
|
+
}
|
|
635
779
|
return { client, logger: traceLogger, workspaceId, tracing };
|
|
636
780
|
}
|
|
637
781
|
/**
|
|
@@ -689,11 +833,14 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
689
833
|
if (field(experiment, "experimentStatus") !== "running") {
|
|
690
834
|
throw new Error("resumeExperimentId must name a running experiment");
|
|
691
835
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
options.contextManifest.digest) {
|
|
836
|
+
const storedConfig = field(experiment, "config") ?? {};
|
|
837
|
+
if (field(storedConfig, "context_manifest_digest") !== options.contextManifest?.digest) {
|
|
695
838
|
throw new Error("resume experiment uses a different context manifest");
|
|
696
839
|
}
|
|
840
|
+
const storedUserConfig = Object.fromEntries(Object.entries(storedConfig).filter(([key]) => !["runner", "registry_dataset", "context_manifest", "context_manifest_digest", "eval_cases_digest"].includes(key)));
|
|
841
|
+
if (canonicalJson(storedUserConfig) !== canonicalJson(options.config ?? {})) {
|
|
842
|
+
throw new Error("resume experiment uses a different config");
|
|
843
|
+
}
|
|
697
844
|
let resolvedDataset;
|
|
698
845
|
if (evaluator.dataset !== undefined) {
|
|
699
846
|
resolvedDataset = await resolveDataset(client, evaluator.dataset, workspaceId);
|
|
@@ -706,6 +853,9 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
706
853
|
}
|
|
707
854
|
else {
|
|
708
855
|
cases = await collectData(evaluator.data);
|
|
856
|
+
if (field(storedConfig, "eval_cases_digest") !== await casesDigest(cases)) {
|
|
857
|
+
throw new Error("resume inline data differs or has no saved digest; start a new experiment");
|
|
858
|
+
}
|
|
709
859
|
}
|
|
710
860
|
run = new EvalRun(experiment, resolvedDataset, options.contextManifest);
|
|
711
861
|
}
|
|
@@ -717,6 +867,7 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
717
867
|
else {
|
|
718
868
|
cases = await collectData(evaluator.data);
|
|
719
869
|
const config = { ...experimentBody.config };
|
|
870
|
+
config.eval_cases_digest = await casesDigest(cases);
|
|
720
871
|
if (options.contextManifest !== undefined) {
|
|
721
872
|
config.context_manifest = options.contextManifest;
|
|
722
873
|
config.context_manifest_digest = options.contextManifest.digest;
|
|
@@ -729,23 +880,34 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
729
880
|
}
|
|
730
881
|
}
|
|
731
882
|
const scorerCache = new Map();
|
|
883
|
+
const traceEvidence = new Map();
|
|
732
884
|
const started = Date.now();
|
|
733
885
|
const results = [];
|
|
734
886
|
try {
|
|
735
887
|
options.onStart?.({ experimentId: run.id, experiment: run.experiment });
|
|
736
888
|
if (evaluator.dataset !== undefined) {
|
|
889
|
+
const snapshot = snapshotCases(run.experiment);
|
|
890
|
+
if (options.resumeExperimentId !== undefined && snapshot === undefined) {
|
|
891
|
+
throw new Error("resume requires the experiment's immutable dataset snapshot");
|
|
892
|
+
}
|
|
737
893
|
cases =
|
|
738
|
-
|
|
894
|
+
snapshot ??
|
|
739
895
|
(await datasetCases(resources, String(field(run.dataset, "id"))));
|
|
740
896
|
}
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
897
|
+
const caseIds = new Set(cases.map((item, index) => item.id ?? `case-${index + 1}`));
|
|
898
|
+
if (caseIds.size !== cases.length)
|
|
899
|
+
throw new Error("Eval case IDs must be unique");
|
|
900
|
+
const persisted = options.resumeExperimentId === undefined ? [] : await experimentResults(resources, run.id);
|
|
901
|
+
const completedCaseIds = new Set(persisted.map((item) => field(item, "caseId")));
|
|
902
|
+
if (completedCaseIds.size !== persisted.length || [...completedCaseIds].some((id) => typeof id !== "string" || !caseIds.has(id))) {
|
|
903
|
+
throw new Error("resume experiment contains unexpected or duplicate case IDs");
|
|
904
|
+
}
|
|
744
905
|
for (const definition of evaluator.scores) {
|
|
745
906
|
scorerCache.set(definition.name, await resolveScorer(resources, workspaceId, name, definition));
|
|
746
907
|
}
|
|
747
908
|
for (const [index, item] of cases.entries()) {
|
|
748
909
|
const caseId = item.id ?? `case-${index + 1}`;
|
|
910
|
+
const datasetRecordId = evaluator.dataset === undefined ? undefined : item.id;
|
|
749
911
|
if (completedCaseIds.has(caseId))
|
|
750
912
|
continue;
|
|
751
913
|
const caseStarted = Date.now();
|
|
@@ -753,8 +915,15 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
753
915
|
const failures = [];
|
|
754
916
|
let output;
|
|
755
917
|
let traceId = "";
|
|
918
|
+
let rootSpanId;
|
|
919
|
+
const requiredSpanIds = new Set();
|
|
920
|
+
const scoreEvidence = new Map();
|
|
756
921
|
const metadata = (item.metadata ?? {});
|
|
757
|
-
await tracing.propagateAttributes(
|
|
922
|
+
await tracing.propagateAttributes({
|
|
923
|
+
...run.traceOptions,
|
|
924
|
+
caseId,
|
|
925
|
+
...(datasetRecordId === undefined ? {} : { datasetRecordId }),
|
|
926
|
+
}, () => traceLogger.client.startAsCurrentSpan(`eval.case.${index + 1}`, {
|
|
758
927
|
asType: "task",
|
|
759
928
|
fields: {
|
|
760
929
|
input: item.input,
|
|
@@ -763,8 +932,13 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
763
932
|
},
|
|
764
933
|
}, async (rootSpan) => {
|
|
765
934
|
traceId = rootSpan.traceId;
|
|
935
|
+
rootSpanId = rootSpan.spanId;
|
|
936
|
+
if (rootSpan.spanId !== undefined)
|
|
937
|
+
requiredSpanIds.add(rootSpan.spanId);
|
|
766
938
|
try {
|
|
767
939
|
output = await traceLogger.client.startAsCurrentSpan("eval.task", { asType: "task", fields: { input: item.input } }, async (taskSpan) => {
|
|
940
|
+
if (taskSpan.spanId !== undefined)
|
|
941
|
+
requiredSpanIds.add(taskSpan.spanId);
|
|
768
942
|
const value = await evaluator.task(item.input, {
|
|
769
943
|
expected: item.expected,
|
|
770
944
|
metadata,
|
|
@@ -779,6 +953,7 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
779
953
|
catch (error) {
|
|
780
954
|
failures.push(safeError(error));
|
|
781
955
|
rootSpan.recordFailure(error);
|
|
956
|
+
rootSpan.markEvidenceComplete?.();
|
|
782
957
|
return;
|
|
783
958
|
}
|
|
784
959
|
for (const definition of evaluator.scores) {
|
|
@@ -793,6 +968,8 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
793
968
|
},
|
|
794
969
|
},
|
|
795
970
|
}, async (scoreSpan) => {
|
|
971
|
+
if (scoreSpan.spanId !== undefined)
|
|
972
|
+
requiredSpanIds.add(scoreSpan.spanId);
|
|
796
973
|
const value = await definition.scorer({ ...item, output: output });
|
|
797
974
|
scoreSpan.update({ output: value });
|
|
798
975
|
return value;
|
|
@@ -813,6 +990,7 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
813
990
|
}
|
|
814
991
|
const numeric = numericScore(scored.score);
|
|
815
992
|
scores[scoreName] = numeric;
|
|
993
|
+
scoreEvidence.set(scoreName, registered);
|
|
816
994
|
rootSpan.score(scoreName, numeric, {
|
|
817
995
|
scorerId: registered.id,
|
|
818
996
|
scorerVersion: registered.version,
|
|
@@ -828,6 +1006,7 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
828
1006
|
}
|
|
829
1007
|
}
|
|
830
1008
|
rootSpan.update({ output, metadata });
|
|
1009
|
+
rootSpan.markEvidenceComplete?.();
|
|
831
1010
|
}));
|
|
832
1011
|
if (!TRACE_ID.test(traceId) || /^0+$/.test(traceId)) {
|
|
833
1012
|
throw new Error(`case ${index + 1} did not produce a trace ID`);
|
|
@@ -841,41 +1020,84 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
841
1020
|
traceId,
|
|
842
1021
|
durationMs: Date.now() - caseStarted,
|
|
843
1022
|
});
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1023
|
+
traceEvidence.set(traceId, {
|
|
1024
|
+
caseId,
|
|
1025
|
+
...(datasetRecordId === undefined ? {} : { datasetRecordId }),
|
|
1026
|
+
...(rootSpanId === undefined ? {} : { rootSpanId }),
|
|
1027
|
+
requiredSpanIds,
|
|
1028
|
+
scores: scoreEvidence,
|
|
1029
|
+
});
|
|
1030
|
+
// Persist each completed case before starting another costly task.
|
|
1031
|
+
// Keep the source index so a resumed subset cannot reuse earlier names.
|
|
1032
|
+
const result = results[results.length - 1];
|
|
1033
|
+
await traceLogger.flush();
|
|
1034
|
+
if (options.verifyTraces !== false) {
|
|
1035
|
+
await verifyTraceEvidence(resources, run.id, new Map([[result.traceId, traceEvidence.get(result.traceId)]]), traceVerificationTimeoutMs);
|
|
1036
|
+
}
|
|
1037
|
+
const resultBody = {
|
|
1038
|
+
workspaceId,
|
|
1039
|
+
name: `${slug(run.id, "experiment")}-case-${index + 1}`,
|
|
1040
|
+
caseId: result.caseId,
|
|
1041
|
+
datasetRecordId,
|
|
1042
|
+
input: registryObject(result.input),
|
|
1043
|
+
expected: registryObject(result.expected),
|
|
1044
|
+
output: registryObject(result.output),
|
|
1045
|
+
error: result.error,
|
|
1046
|
+
scores: result.scores,
|
|
1047
|
+
traceId: result.traceId,
|
|
1048
|
+
durationMs: result.durationMs,
|
|
1049
|
+
sourceType: "local",
|
|
1050
|
+
};
|
|
865
1051
|
const response = await resources.experiments.results.createBulk(run.id, {
|
|
866
|
-
items:
|
|
1052
|
+
items: [resultBody],
|
|
867
1053
|
});
|
|
868
1054
|
const slots = field(response, "items");
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
return typeof status === "number" && status >= 400;
|
|
873
|
-
});
|
|
874
|
-
if (failed.length > 0)
|
|
875
|
-
throw new Error(`${failed.length} Eval results were rejected`);
|
|
1055
|
+
const status = Array.isArray(slots) && slots.length === 1 ? field(slots[0], "statusCode") : undefined;
|
|
1056
|
+
if (typeof status !== "number" || status < 200 || status >= 300) {
|
|
1057
|
+
throw new Error("1 Eval results were rejected or not acknowledged");
|
|
876
1058
|
}
|
|
877
1059
|
}
|
|
878
1060
|
const persistedResults = await experimentResults(resources, run.id);
|
|
1061
|
+
const persistedCaseIds = new Set(persistedResults.map((item) => field(item, "caseId")));
|
|
1062
|
+
if (persistedResults.length !== caseIds.size || persistedCaseIds.size !== caseIds.size || [...caseIds].some((id) => !persistedCaseIds.has(id))) {
|
|
1063
|
+
throw new Error("Registry result coverage does not match the evaluation cases");
|
|
1064
|
+
}
|
|
1065
|
+
if (options.verifyTraces !== false && persistedResults.length > 0) {
|
|
1066
|
+
const traceIds = new Set();
|
|
1067
|
+
for (const item of persistedResults) {
|
|
1068
|
+
const traceId = field(item, "traceId");
|
|
1069
|
+
if (typeof traceId !== "string" || !TRACE_ID.test(traceId) || /^0+$/.test(traceId)) {
|
|
1070
|
+
throw new Error("Registry result is missing a valid trace ID");
|
|
1071
|
+
}
|
|
1072
|
+
traceIds.add(traceId);
|
|
1073
|
+
}
|
|
1074
|
+
const expectations = new Map();
|
|
1075
|
+
for (const item of persistedResults) {
|
|
1076
|
+
const traceId = field(item, "traceId");
|
|
1077
|
+
const current = traceEvidence.get(traceId);
|
|
1078
|
+
if (current !== undefined) {
|
|
1079
|
+
expectations.set(traceId, current);
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
const persistedScores = field(item, "scores");
|
|
1083
|
+
const scorePins = new Map();
|
|
1084
|
+
if (isRecord(persistedScores)) {
|
|
1085
|
+
for (const [scoreName, value] of Object.entries(persistedScores)) {
|
|
1086
|
+
const scorer = scorerCache.get(scoreName);
|
|
1087
|
+
if (value !== null && scorer !== undefined)
|
|
1088
|
+
scorePins.set(scoreName, scorer);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
const datasetRecordId = field(item, "datasetRecordId");
|
|
1092
|
+
expectations.set(traceId, {
|
|
1093
|
+
caseId: String(field(item, "caseId")),
|
|
1094
|
+
...(typeof datasetRecordId === "string" ? { datasetRecordId } : {}),
|
|
1095
|
+
requiredSpanIds: new Set(),
|
|
1096
|
+
scores: scorePins,
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
await verifyTraceEvidence(resources, run.id, expectations, traceVerificationTimeoutMs);
|
|
1100
|
+
}
|
|
879
1101
|
const local = localSummary(persistedResults, Date.now() - started);
|
|
880
1102
|
const experiment = await resources.experiments.update(run.id, {
|
|
881
1103
|
experimentStatus: "completed",
|
|
@@ -902,15 +1124,8 @@ async function Eval(name, evaluator, options = {}) {
|
|
|
902
1124
|
catch {
|
|
903
1125
|
// Preserve the original runner/upload failure.
|
|
904
1126
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
experimentStatus: "failed",
|
|
908
|
-
summary: localSummary(results, Date.now() - started),
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
catch {
|
|
912
|
-
// Preserve the original runner/upload failure.
|
|
913
|
-
}
|
|
1127
|
+
// Leave interrupted infrastructure work running: terminal experiments
|
|
1128
|
+
// cannot accept more results. Resume reconciles Registry case IDs.
|
|
914
1129
|
throw error;
|
|
915
1130
|
}
|
|
916
1131
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { AtlanClient, ServiceNamespace, createService } from "./client";
|
|
|
2
2
|
export type { AtlanClientOptions, ForwardedOperation, Service } from "./client";
|
|
3
3
|
export { buildResources, resourceMap } from "./resources";
|
|
4
4
|
export type { Resource, ResourceOperation } from "./resources";
|
|
5
|
+
export type { ResourceMap, ResourceRoots } from "./resource-types";
|
|
5
6
|
export { AtlanAPIError } from "./errors";
|
|
6
7
|
export { operationManifest, operations } from "./operations";
|
|
7
8
|
export type { Operation, OperationManifest, ServiceContract } from "./operations";
|