






















@@ -89,7 +89,6 @@ const OTLP_SIGNAL_PATHS = new Map<string, OtlpSignal>([
8989const REQUIRED_SPAN_NAMES = [
9090"openclaw.run",
9191"openclaw.harness.run",
92-"openclaw.model.call",
9392"openclaw.context.assembled",
9493"openclaw.message.delivery",
9594] as const;
@@ -911,6 +910,19 @@ function capturedValueKind(value: string | number | boolean | string[]): string
911910return Array.isArray(value) ? "array" : typeof value;
912911}
913912913+function isLatestGenAiModelCallSpan(span: CapturedSpan): boolean {
914+const operationName = span.attributes["gen_ai.operation.name"];
915+const modelName = span.attributes["gen_ai.request.model"];
916+if (typeof operationName !== "string" || typeof modelName !== "string") {
917+ return false;
918+}
919+return (
920+span.name === `${operationName} ${modelName}` &&
921+typeof span.attributes["openclaw.provider"] === "string" &&
922+typeof span.attributes["openclaw.model"] === "string"
923+);
924+}
925+914926function assertSmoke(params: {
915927childExitCode: number;
916928disallowedBodyNeedles: string[];
@@ -951,6 +963,13 @@ function assertSmoke(params: {
951963failures.push(`missing required span ${name}`);
952964}
953965}
966+const modelSpans = params.spans.filter(isLatestGenAiModelCallSpan);
967+if (modelSpans.length === 0) {
968+failures.push("missing required GenAI model-call span");
969+}
970+if (spanNames.has("openclaw.model.call")) {
971+failures.push("legacy openclaw.model.call span exported with GenAI semconv opt-in");
972+}
954973const metricNames = new Set(params.metrics.map((metric) => metric.name));
955974for (const name of REQUIRED_METRIC_NAMES) {
956975if (!metricNames.has(name)) {
@@ -975,8 +994,10 @@ function assertSmoke(params: {
975994if (contentKeys.length > 0) {
976995failures.push(`content attributes exported with capture disabled: ${contentKeys.join(", ")}`);
977996}
997+if (modelSpans.some((span) => Object.hasOwn(span.attributes, "gen_ai.system"))) {
998+failures.push("legacy gen_ai.system attribute exported on GenAI model-call span");
999+}
9781000979-const modelSpans = params.spans.filter((span) => span.name === "openclaw.model.call");
9801001const modelErrorSpans = modelSpans.filter((span) => {
9811002const serialized = JSON.stringify(span.attributes);
9821003return (
@@ -985,9 +1006,6 @@ function assertSmoke(params: {
9851006serialized.includes("StreamAbandoned")
9861007);
9871008});
988-if (modelSpans.length === 0) {
989-failures.push("no openclaw.model.call span was exported");
990-}
9911009if (modelErrorSpans.length > 0) {
9921010failures.push("successful QA run exported model-call error attributes");
9931011}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。