agent-inspect 6.31.6 → 6.31.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +1 -1
- package/docs/AI-SDK-ADOPTION.md +62 -34
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-YXDNWHNJ.mjs → chunk-6FE47XSF.mjs} +119 -34
- package/packages/cli/dist/chunk-6FE47XSF.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +569 -102
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +454 -74
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-H22KNWAZ.mjs → src-NXAYHG34.mjs} +3 -3
- package/packages/cli/dist/{src-H22KNWAZ.mjs.map → src-NXAYHG34.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +116 -31
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.mjs +7 -7
- package/packages/core/dist/checks.mjs +2 -2
- package/packages/core/dist/{chunk-LXYPM2ZO.mjs → chunk-2CCRUI42.mjs} +19 -318
- package/packages/core/dist/chunk-2CCRUI42.mjs.map +1 -0
- package/packages/core/dist/{chunk-Q5SLKV2Y.mjs → chunk-2IRRUNIN.mjs} +28 -5
- package/packages/core/dist/chunk-2IRRUNIN.mjs.map +1 -0
- package/packages/core/dist/{chunk-EXGQLMVB.mjs → chunk-7YYS742B.mjs} +49 -12
- package/packages/core/dist/chunk-7YYS742B.mjs.map +1 -0
- package/packages/core/dist/{chunk-OSGNGRHF.mjs → chunk-IJJSPJXV.mjs} +51 -25
- package/packages/core/dist/chunk-IJJSPJXV.mjs.map +1 -0
- package/packages/core/dist/chunk-L5WLVBW4.mjs +355 -0
- package/packages/core/dist/chunk-L5WLVBW4.mjs.map +1 -0
- package/packages/core/dist/exporters.cjs +327 -58
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +281 -49
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/persisted.cjs +361 -9
- package/packages/core/dist/persisted.cjs.map +1 -1
- package/packages/core/dist/persisted.d.cts +8 -1
- package/packages/core/dist/persisted.d.ts +8 -1
- package/packages/core/dist/persisted.mjs +3 -2
- package/packages/core/dist/persisted.mjs.map +1 -1
- package/packages/core/dist/readers.cjs +425 -100
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.mjs +3 -3
- package/packages/cli/dist/chunk-YXDNWHNJ.mjs.map +0 -1
- package/packages/core/dist/chunk-EXGQLMVB.mjs.map +0 -1
- package/packages/core/dist/chunk-FWK3Q3OJ.mjs +0 -56
- package/packages/core/dist/chunk-FWK3Q3OJ.mjs.map +0 -1
- package/packages/core/dist/chunk-LXYPM2ZO.mjs.map +0 -1
- package/packages/core/dist/chunk-OSGNGRHF.mjs.map +0 -1
- package/packages/core/dist/chunk-Q5SLKV2Y.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildRunWhatSummary, renderRunWhat, renderTimeline, buildRunTimeline } from './chunk-
|
|
1
|
+
import { buildRunWhatSummary, renderRunWhat, renderTimeline, buildRunTimeline } from './chunk-7YYS742B.mjs';
|
|
2
2
|
import { summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml } from './chunk-N5PZAI6Q.mjs';
|
|
3
3
|
import { escapeHtml, flattenTree, safeString, compactAttributes, stableJson, escapeMarkdown, zeroKinds } from './chunk-CAWPF22J.mjs';
|
|
4
4
|
export { compactAttributes, escapeHtml, escapeMarkdown, flattenTree, safeString, stableJson, summarizeTree } from './chunk-CAWPF22J.mjs';
|
|
@@ -740,11 +740,13 @@ function exportOpenInference(tree, options) {
|
|
|
740
740
|
endNs = startNs + unixNano(ev.durationMs);
|
|
741
741
|
}
|
|
742
742
|
const { openInferenceKind } = mapInspectKindToOI(ev.kind, warnings);
|
|
743
|
+
const meta = ev.attributes;
|
|
744
|
+
const originalSource = typeof meta?.originalSourceType === "string" && meta.originalSourceType.trim() !== "" ? meta.originalSourceType.trim() : ev.source.type;
|
|
743
745
|
const attrs = {
|
|
744
746
|
"openinference.span.kind": openInferenceKind,
|
|
745
747
|
"agent_inspect.kind": ev.kind,
|
|
746
748
|
"agent_inspect.confidence": ev.confidence,
|
|
747
|
-
"agent_inspect.source.type":
|
|
749
|
+
"agent_inspect.source.type": originalSource,
|
|
748
750
|
"agent_inspect.run_id": tree.runId,
|
|
749
751
|
"agent_inspect.event_id": ev.eventId,
|
|
750
752
|
"agent_inspect.status": ev.status ?? "unset"
|
|
@@ -752,9 +754,9 @@ function exportOpenInference(tree, options) {
|
|
|
752
754
|
if (ev.durationMs !== void 0) {
|
|
753
755
|
attrs["agent_inspect.duration_ms"] = ev.durationMs;
|
|
754
756
|
}
|
|
755
|
-
const
|
|
756
|
-
if (
|
|
757
|
-
attrs["llm.model_name"] =
|
|
757
|
+
const requestModel = (typeof meta?.model === "string" && meta.model.trim() !== "" ? meta.model : void 0) ?? (typeof meta?.modelId === "string" && meta.modelId.trim() !== "" ? meta.modelId : void 0);
|
|
758
|
+
if (requestModel !== void 0) {
|
|
759
|
+
attrs["llm.model_name"] = requestModel;
|
|
758
760
|
}
|
|
759
761
|
const tokens = meta?.tokens;
|
|
760
762
|
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
@@ -764,8 +766,9 @@ function exportOpenInference(tree, options) {
|
|
|
764
766
|
if (typeof outp === "number") attrs["llm.token_count.completion"] = outp;
|
|
765
767
|
}
|
|
766
768
|
if (includeAttributes && meta && typeof meta === "object") {
|
|
769
|
+
const skipKeys = /* @__PURE__ */ new Set(["tokens", "model", "modelId", "originalSourceType"]);
|
|
767
770
|
for (const [k, v] of Object.entries(meta)) {
|
|
768
|
-
if (k
|
|
771
|
+
if (skipKeys.has(k)) continue;
|
|
769
772
|
if (v !== void 0 && v !== null && typeof v !== "object") {
|
|
770
773
|
attrs[`agent_inspect.preview.${k}`] = typeof v === "string" ? v.slice(0, maxLen) : v;
|
|
771
774
|
}
|
|
@@ -808,6 +811,10 @@ function exportOpenInference(tree, options) {
|
|
|
808
811
|
warnings
|
|
809
812
|
};
|
|
810
813
|
}
|
|
814
|
+
var OTLP_STATUS_CODE_UNSET = 0;
|
|
815
|
+
var OTLP_STATUS_CODE_OK = 1;
|
|
816
|
+
var OTLP_STATUS_CODE_ERROR = 2;
|
|
817
|
+
var OTLP_SPAN_KIND_INTERNAL = 1;
|
|
811
818
|
function hexFrom2(seed, byteLen) {
|
|
812
819
|
return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
813
820
|
}
|
|
@@ -820,7 +827,19 @@ function stringAttr(key, value) {
|
|
|
820
827
|
return { key, value: { stringValue: value } };
|
|
821
828
|
}
|
|
822
829
|
function intAttr(key, value) {
|
|
823
|
-
return { key, value: { intValue: String(value) } };
|
|
830
|
+
return { key, value: { intValue: String(Math.trunc(value)) } };
|
|
831
|
+
}
|
|
832
|
+
function doubleAttr(key, value) {
|
|
833
|
+
return { key, value: { doubleValue: value } };
|
|
834
|
+
}
|
|
835
|
+
function boolAttr(key, value) {
|
|
836
|
+
return { key, value: { boolValue: value } };
|
|
837
|
+
}
|
|
838
|
+
function numberAttr(key, value) {
|
|
839
|
+
if (Number.isFinite(value) && !Number.isInteger(value)) {
|
|
840
|
+
return doubleAttr(key, value);
|
|
841
|
+
}
|
|
842
|
+
return intAttr(key, value);
|
|
824
843
|
}
|
|
825
844
|
function genAiOperationName(kind) {
|
|
826
845
|
switch (kind) {
|
|
@@ -834,10 +853,27 @@ function genAiOperationName(kind) {
|
|
|
834
853
|
return void 0;
|
|
835
854
|
}
|
|
836
855
|
}
|
|
856
|
+
function resolveSpanTimes(ev) {
|
|
857
|
+
const startedAtMs = typeof ev.attributes?.startedAtMs === "number" && Number.isFinite(ev.attributes.startedAtMs) ? ev.attributes.startedAtMs : void 0;
|
|
858
|
+
const endedAtMs = typeof ev.attributes?.endedAtMs === "number" && Number.isFinite(ev.attributes.endedAtMs) ? ev.attributes.endedAtMs : void 0;
|
|
859
|
+
const startMs = startedAtMs ?? ev.timestamp;
|
|
860
|
+
const startNs = String(unixNano2(startMs));
|
|
861
|
+
if (endedAtMs !== void 0) {
|
|
862
|
+
return { startNs, endNs: String(unixNano2(endedAtMs)) };
|
|
863
|
+
}
|
|
864
|
+
if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
|
|
865
|
+
return {
|
|
866
|
+
startNs,
|
|
867
|
+
endNs: String(unixNano2(startMs) + unixNano2(ev.durationMs))
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
return { startNs };
|
|
871
|
+
}
|
|
837
872
|
function exportOtlpJson(tree, options) {
|
|
838
873
|
const warnings = [
|
|
839
874
|
"OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
|
|
840
|
-
"Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
|
|
875
|
+
"Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload.",
|
|
876
|
+
"Status and span kind use OTLP numeric enums (StatusCode 0/1/2, SpanKind INTERNAL=1)."
|
|
841
877
|
];
|
|
842
878
|
const traceId = hexFrom2(`trace:${tree.runId}`, 16);
|
|
843
879
|
const includeAttributes = options?.includeAttributes ?? false;
|
|
@@ -849,29 +885,34 @@ function exportOtlpJson(tree, options) {
|
|
|
849
885
|
const ev = n.event;
|
|
850
886
|
const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
|
|
851
887
|
const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
|
|
852
|
-
const startNs =
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
|
|
856
|
-
}
|
|
888
|
+
const { startNs, endNs } = resolveSpanTimes(ev);
|
|
889
|
+
const meta = ev.attributes;
|
|
890
|
+
const originalSource = typeof meta?.originalSourceType === "string" && meta.originalSourceType.trim() !== "" ? meta.originalSourceType.trim() : ev.source.type;
|
|
857
891
|
const attrs = [
|
|
858
892
|
stringAttr("agent_inspect.kind", ev.kind),
|
|
859
893
|
stringAttr("agent_inspect.confidence", ev.confidence),
|
|
860
|
-
stringAttr("agent_inspect.source.type",
|
|
894
|
+
stringAttr("agent_inspect.source.type", originalSource),
|
|
861
895
|
stringAttr("agent_inspect.run_id", tree.runId),
|
|
862
896
|
stringAttr("agent_inspect.event_id", ev.eventId),
|
|
863
897
|
stringAttr("agent_inspect.status", ev.status ?? "unset")
|
|
864
898
|
];
|
|
865
899
|
if (ev.durationMs !== void 0) {
|
|
866
|
-
attrs.push(
|
|
900
|
+
attrs.push(numberAttr("agent_inspect.duration_ms", ev.durationMs));
|
|
867
901
|
}
|
|
868
902
|
const op = genAiOperationName(ev.kind);
|
|
869
903
|
if (op !== void 0) {
|
|
870
904
|
attrs.push(stringAttr("gen_ai.operation.name", op));
|
|
871
905
|
}
|
|
872
|
-
const
|
|
873
|
-
if (
|
|
874
|
-
attrs.push(stringAttr("gen_ai.request.model",
|
|
906
|
+
const requestModel = (typeof meta?.model === "string" && meta.model.trim() !== "" ? meta.model : void 0) ?? (typeof meta?.modelId === "string" && meta.modelId.trim() !== "" ? meta.modelId : void 0);
|
|
907
|
+
if (requestModel !== void 0) {
|
|
908
|
+
attrs.push(stringAttr("gen_ai.request.model", requestModel.slice(0, maxLen)));
|
|
909
|
+
}
|
|
910
|
+
const responseModel = typeof meta?.responseModelId === "string" && meta.responseModelId.trim() !== "" ? meta.responseModelId : typeof meta?.responseModel === "string" && meta.responseModel.trim() !== "" ? meta.responseModel : void 0;
|
|
911
|
+
if (responseModel !== void 0) {
|
|
912
|
+
attrs.push(stringAttr("gen_ai.response.model", responseModel.slice(0, maxLen)));
|
|
913
|
+
}
|
|
914
|
+
if (typeof meta?.provider === "string" && meta.provider.trim() !== "") {
|
|
915
|
+
attrs.push(stringAttr("gen_ai.provider.name", meta.provider.slice(0, maxLen)));
|
|
875
916
|
}
|
|
876
917
|
const tokens = meta?.tokens;
|
|
877
918
|
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
@@ -881,31 +922,39 @@ function exportOtlpJson(tree, options) {
|
|
|
881
922
|
if (typeof outp === "number") attrs.push(intAttr("gen_ai.usage.output_tokens", outp));
|
|
882
923
|
}
|
|
883
924
|
if (includeAttributes && meta && typeof meta === "object") {
|
|
925
|
+
const skipKeys = /* @__PURE__ */ new Set([
|
|
926
|
+
"tokens",
|
|
927
|
+
"model",
|
|
928
|
+
"modelId",
|
|
929
|
+
"responseModel",
|
|
930
|
+
"responseModelId",
|
|
931
|
+
"provider",
|
|
932
|
+
"originalSourceType"
|
|
933
|
+
]);
|
|
884
934
|
for (const [k, v] of Object.entries(meta)) {
|
|
885
|
-
if (k
|
|
886
|
-
if (typeof v === "string"
|
|
887
|
-
attrs.push(
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
);
|
|
935
|
+
if (skipKeys.has(k)) continue;
|
|
936
|
+
if (typeof v === "string") {
|
|
937
|
+
attrs.push(stringAttr(`agent_inspect.preview.${k}`, v.slice(0, maxLen)));
|
|
938
|
+
} else if (typeof v === "number" && Number.isFinite(v)) {
|
|
939
|
+
attrs.push(numberAttr(`agent_inspect.preview.${k}`, v));
|
|
940
|
+
} else if (typeof v === "boolean") {
|
|
941
|
+
attrs.push(boolAttr(`agent_inspect.preview.${k}`, v));
|
|
893
942
|
}
|
|
894
943
|
}
|
|
895
944
|
}
|
|
896
|
-
let statusCode =
|
|
945
|
+
let statusCode = OTLP_STATUS_CODE_UNSET;
|
|
897
946
|
let statusMessage;
|
|
898
947
|
if (ev.status === "error") {
|
|
899
|
-
statusCode =
|
|
948
|
+
statusCode = OTLP_STATUS_CODE_ERROR;
|
|
900
949
|
statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
|
|
901
950
|
} else if (ev.status === "ok") {
|
|
902
|
-
statusCode =
|
|
951
|
+
statusCode = OTLP_STATUS_CODE_OK;
|
|
903
952
|
}
|
|
904
953
|
const spanJson = {
|
|
905
954
|
traceId,
|
|
906
955
|
spanId,
|
|
907
956
|
name: ev.name,
|
|
908
|
-
kind:
|
|
957
|
+
kind: OTLP_SPAN_KIND_INTERNAL,
|
|
909
958
|
startTimeUnixNano: startNs,
|
|
910
959
|
attributes: attrs,
|
|
911
960
|
status: {
|
|
@@ -947,6 +996,207 @@ function exportOtlpJson(tree, options) {
|
|
|
947
996
|
|
|
948
997
|
// packages/core/src/exporters/validation.ts
|
|
949
998
|
var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
|
|
999
|
+
function pushPath(errors, path, message) {
|
|
1000
|
+
errors.push(`${path}: ${message}`);
|
|
1001
|
+
}
|
|
1002
|
+
function isRecord2(value) {
|
|
1003
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1004
|
+
}
|
|
1005
|
+
function isHexId(value, byteLen) {
|
|
1006
|
+
return typeof value === "string" && new RegExp(`^[0-9a-fA-F]{${byteLen * 2}}$`).test(value);
|
|
1007
|
+
}
|
|
1008
|
+
function validateAnyValue(errors, path, value) {
|
|
1009
|
+
if (!isRecord2(value)) {
|
|
1010
|
+
pushPath(errors, path, "must be an AnyValue object");
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
const variants = [
|
|
1014
|
+
"stringValue",
|
|
1015
|
+
"boolValue",
|
|
1016
|
+
"intValue",
|
|
1017
|
+
"doubleValue",
|
|
1018
|
+
"bytesValue",
|
|
1019
|
+
"arrayValue",
|
|
1020
|
+
"kvlistValue"
|
|
1021
|
+
].filter((key) => value[key] !== void 0);
|
|
1022
|
+
if (variants.length !== 1) {
|
|
1023
|
+
pushPath(
|
|
1024
|
+
errors,
|
|
1025
|
+
path,
|
|
1026
|
+
`AnyValue must set exactly one variant (found ${variants.length}: ${variants.join(",")})`
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
if (value.intValue !== void 0) {
|
|
1030
|
+
if (typeof value.intValue !== "string" || !/^-?\d+$/.test(value.intValue)) {
|
|
1031
|
+
pushPath(errors, `${path}.intValue`, "must be a decimal integer string");
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
function validateOtlpJson(content) {
|
|
1036
|
+
const errors = [];
|
|
1037
|
+
const warnings = [
|
|
1038
|
+
EXPERIMENTAL,
|
|
1039
|
+
"OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation.",
|
|
1040
|
+
"producer-profile checks IDs, timing, and AnyValue shape; historical string enums are reader-compat warnings only."
|
|
1041
|
+
];
|
|
1042
|
+
let parsed;
|
|
1043
|
+
try {
|
|
1044
|
+
parsed = JSON.parse(content);
|
|
1045
|
+
} catch {
|
|
1046
|
+
pushPath(errors, "$", "OTLP JSON export is not valid JSON");
|
|
1047
|
+
return { ok: false, format: "otlp-json", errors, warnings };
|
|
1048
|
+
}
|
|
1049
|
+
if (!isRecord2(parsed)) {
|
|
1050
|
+
pushPath(errors, "$", "OTLP JSON export must be an object");
|
|
1051
|
+
return { ok: false, format: "otlp-json", errors, warnings };
|
|
1052
|
+
}
|
|
1053
|
+
if (!Array.isArray(parsed.resourceSpans)) {
|
|
1054
|
+
pushPath(errors, "$.resourceSpans", "must be an array");
|
|
1055
|
+
return { ok: false, format: "otlp-json", errors, warnings };
|
|
1056
|
+
}
|
|
1057
|
+
parsed.resourceSpans.forEach((resourceSpan, rsi) => {
|
|
1058
|
+
const rsPath = `$.resourceSpans[${rsi}]`;
|
|
1059
|
+
if (!isRecord2(resourceSpan)) {
|
|
1060
|
+
pushPath(errors, rsPath, "must be an object");
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
if (!Array.isArray(resourceSpan.scopeSpans)) {
|
|
1064
|
+
pushPath(errors, `${rsPath}.scopeSpans`, "must be an array");
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
resourceSpan.scopeSpans.forEach((scopeSpan, ssi) => {
|
|
1068
|
+
const ssPath = `${rsPath}.scopeSpans[${ssi}]`;
|
|
1069
|
+
if (!isRecord2(scopeSpan)) {
|
|
1070
|
+
pushPath(errors, ssPath, "must be an object");
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
if (!Array.isArray(scopeSpan.spans)) {
|
|
1074
|
+
pushPath(errors, `${ssPath}.spans`, "must be an array");
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
scopeSpan.spans.forEach((span, spi) => {
|
|
1078
|
+
const spanPath = `${ssPath}.spans[${spi}]`;
|
|
1079
|
+
if (!isRecord2(span)) {
|
|
1080
|
+
pushPath(errors, spanPath, "must be an object");
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
if (!isHexId(span.traceId, 16)) {
|
|
1084
|
+
pushPath(
|
|
1085
|
+
errors,
|
|
1086
|
+
`${spanPath}.traceId`,
|
|
1087
|
+
"must be a 32-char hex trace id (16 bytes)"
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
if (!isHexId(span.spanId, 8)) {
|
|
1091
|
+
pushPath(
|
|
1092
|
+
errors,
|
|
1093
|
+
`${spanPath}.spanId`,
|
|
1094
|
+
"must be a 16-char hex span id (8 bytes)"
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
if (span.parentSpanId !== void 0 && !isHexId(span.parentSpanId, 8)) {
|
|
1098
|
+
pushPath(
|
|
1099
|
+
errors,
|
|
1100
|
+
`${spanPath}.parentSpanId`,
|
|
1101
|
+
"must be a 16-char hex span id when present"
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
if (typeof span.name !== "string") {
|
|
1105
|
+
pushPath(errors, `${spanPath}.name`, "must be a string");
|
|
1106
|
+
}
|
|
1107
|
+
if (typeof span.startTimeUnixNano !== "string" || !/^\d+$/.test(span.startTimeUnixNano)) {
|
|
1108
|
+
pushPath(
|
|
1109
|
+
errors,
|
|
1110
|
+
`${spanPath}.startTimeUnixNano`,
|
|
1111
|
+
"must be a decimal-string 64-bit integer"
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
if (span.endTimeUnixNano !== void 0 && (typeof span.endTimeUnixNano !== "string" || !/^\d+$/.test(span.endTimeUnixNano))) {
|
|
1115
|
+
pushPath(
|
|
1116
|
+
errors,
|
|
1117
|
+
`${spanPath}.endTimeUnixNano`,
|
|
1118
|
+
"must be a decimal-string 64-bit integer when present"
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
if (typeof span.startTimeUnixNano === "string" && /^\d+$/.test(span.startTimeUnixNano) && typeof span.endTimeUnixNano === "string" && /^\d+$/.test(span.endTimeUnixNano)) {
|
|
1122
|
+
try {
|
|
1123
|
+
if (BigInt(span.endTimeUnixNano) < BigInt(span.startTimeUnixNano)) {
|
|
1124
|
+
pushPath(
|
|
1125
|
+
errors,
|
|
1126
|
+
`${spanPath}.endTimeUnixNano`,
|
|
1127
|
+
"must be >= startTimeUnixNano"
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
} catch {
|
|
1131
|
+
pushPath(errors, `${spanPath}.endTimeUnixNano`, "out of integer range");
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
if (Array.isArray(span.attributes)) {
|
|
1135
|
+
span.attributes.forEach((attr, ai) => {
|
|
1136
|
+
const attrPath = `${spanPath}.attributes[${ai}]`;
|
|
1137
|
+
if (!isRecord2(attr)) {
|
|
1138
|
+
pushPath(errors, attrPath, "must be an object");
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
if (typeof attr.key !== "string") {
|
|
1142
|
+
pushPath(errors, `${attrPath}.key`, "must be a string");
|
|
1143
|
+
}
|
|
1144
|
+
validateAnyValue(errors, `${attrPath}.value`, attr.value);
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
if (typeof span.kind === "number") {
|
|
1148
|
+
if (!Number.isInteger(span.kind)) {
|
|
1149
|
+
pushPath(errors, `${spanPath}.kind`, "must be an integer SpanKind enum");
|
|
1150
|
+
}
|
|
1151
|
+
} else if (typeof span.kind === "string") {
|
|
1152
|
+
warnings.push(
|
|
1153
|
+
`${spanPath}.kind: string SpanKind is historical reader-compat only; producer wire format uses numeric enums`
|
|
1154
|
+
);
|
|
1155
|
+
} else if (span.kind !== void 0) {
|
|
1156
|
+
pushPath(
|
|
1157
|
+
errors,
|
|
1158
|
+
`${spanPath}.kind`,
|
|
1159
|
+
"must be numeric SpanKind or historical string enum"
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
if (!isRecord2(span.status)) {
|
|
1163
|
+
pushPath(errors, `${spanPath}.status`, "must be an object");
|
|
1164
|
+
} else {
|
|
1165
|
+
const code = span.status.code;
|
|
1166
|
+
if (typeof code === "number") {
|
|
1167
|
+
if (![0, 1, 2].includes(code)) {
|
|
1168
|
+
pushPath(
|
|
1169
|
+
errors,
|
|
1170
|
+
`${spanPath}.status.code`,
|
|
1171
|
+
"must be numeric StatusCode 0 (UNSET), 1 (OK), or 2 (ERROR)"
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
} else if (typeof code === "string") {
|
|
1175
|
+
const upper = code.toUpperCase();
|
|
1176
|
+
if (upper !== "STATUS_CODE_UNSET" && upper !== "STATUS_CODE_OK" && upper !== "STATUS_CODE_ERROR" && upper !== "UNSET" && upper !== "OK" && upper !== "ERROR" && !/^[012]$/.test(code.trim())) {
|
|
1177
|
+
pushPath(
|
|
1178
|
+
errors,
|
|
1179
|
+
`${spanPath}.status.code`,
|
|
1180
|
+
"unrecognized StatusCode string"
|
|
1181
|
+
);
|
|
1182
|
+
} else {
|
|
1183
|
+
warnings.push(
|
|
1184
|
+
`${spanPath}.status.code: string StatusCode is historical reader-compat only; producer wire format uses numeric 0/1/2`
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
} else {
|
|
1188
|
+
pushPath(
|
|
1189
|
+
errors,
|
|
1190
|
+
`${spanPath}.status.code`,
|
|
1191
|
+
"must be numeric StatusCode 0/1/2 or historical string enum"
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1198
|
+
return { ok: errors.length === 0, format: "otlp-json", errors, warnings };
|
|
1199
|
+
}
|
|
950
1200
|
function validateExportContent(format, content) {
|
|
951
1201
|
const errors = [];
|
|
952
1202
|
const warnings = [EXPERIMENTAL];
|
|
@@ -992,25 +1242,7 @@ function validateExportContent(format, content) {
|
|
|
992
1242
|
return { ok: errors.length === 0, format, errors, warnings };
|
|
993
1243
|
}
|
|
994
1244
|
if (format === "otlp-json") {
|
|
995
|
-
|
|
996
|
-
try {
|
|
997
|
-
parsed = JSON.parse(content);
|
|
998
|
-
} catch {
|
|
999
|
-
errors.push("OTLP JSON export is not valid JSON");
|
|
1000
|
-
return { ok: false, format, errors, warnings };
|
|
1001
|
-
}
|
|
1002
|
-
if (!parsed || typeof parsed !== "object") {
|
|
1003
|
-
errors.push("OTLP JSON export must be an object");
|
|
1004
|
-
return { ok: false, format, errors, warnings };
|
|
1005
|
-
}
|
|
1006
|
-
const o = parsed;
|
|
1007
|
-
if (!Array.isArray(o.resourceSpans)) {
|
|
1008
|
-
errors.push("OTLP JSON export must include resourceSpans array");
|
|
1009
|
-
}
|
|
1010
|
-
warnings.push(
|
|
1011
|
-
"OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
|
|
1012
|
-
);
|
|
1013
|
-
return { ok: errors.length === 0, format, errors, warnings };
|
|
1245
|
+
return validateOtlpJson(content);
|
|
1014
1246
|
}
|
|
1015
1247
|
errors.push(`Unsupported export format`);
|
|
1016
1248
|
return { ok: false, format, errors, warnings };
|