From 669c24e83761dee7a93665aff5c31bcb1b43509c Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:21:33 +0300 Subject: [PATCH 01/10] added to the agent span --- .../src/SemanticAttributes.ts | 1 + .../sample-app/src/sample_vercel_ai_tools.ts | 2 +- .../src/lib/tracing/decorators.ts | 11 ++++++++++ .../traceloop-sdk/src/lib/tracing/manual.ts | 20 ++++++++++++++++--- .../traceloop-sdk/src/lib/tracing/tracing.ts | 1 + 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/ai-semantic-conventions/src/SemanticAttributes.ts b/packages/ai-semantic-conventions/src/SemanticAttributes.ts index 884f9fce..6c047cb0 100644 --- a/packages/ai-semantic-conventions/src/SemanticAttributes.ts +++ b/packages/ai-semantic-conventions/src/SemanticAttributes.ts @@ -27,6 +27,7 @@ export const SpanAttributes = { LLM_RESPONSE_MODEL: "gen_ai.response.model", LLM_USAGE_PROMPT_TOKENS: "gen_ai.usage.prompt_tokens", LLM_USAGE_COMPLETION_TOKENS: "gen_ai.usage.completion_tokens", + LLM_AGENT_NAME: "gen_ai.agent.name", // LLM LLM_REQUEST_TYPE: "llm.request.type", diff --git a/packages/sample-app/src/sample_vercel_ai_tools.ts b/packages/sample-app/src/sample_vercel_ai_tools.ts index be760f2f..b91842f5 100644 --- a/packages/sample-app/src/sample_vercel_ai_tools.ts +++ b/packages/sample-app/src/sample_vercel_ai_tools.ts @@ -114,7 +114,7 @@ const searchRestaurants = tool({ }); async function planTrip(destination: string) { - return await traceloop.withWorkflow( + return await traceloop.withAgent( { name: "plan_trip" }, async () => { console.log(`\n🌟 Planning a trip to ${destination}...\n`); diff --git a/packages/traceloop-sdk/src/lib/tracing/decorators.ts b/packages/traceloop-sdk/src/lib/tracing/decorators.ts index cdbd91bf..ae39af52 100644 --- a/packages/traceloop-sdk/src/lib/tracing/decorators.ts +++ b/packages/traceloop-sdk/src/lib/tracing/decorators.ts @@ -1,6 +1,7 @@ import { Span, context } from "@opentelemetry/api"; import { suppressTracing } from "@opentelemetry/core"; import { + AGENT_NAME_KEY, ASSOCATION_PROPERTIES_KEY, ENTITY_NAME_KEY, getEntityPath, @@ -49,6 +50,10 @@ function withEntity< entityContext = entityContext.setValue(WORKFLOW_NAME_KEY, name); } + if (type === TraceloopSpanKindValues.AGENT) { + entityContext = entityContext.setValue(AGENT_NAME_KEY, name); + } + const entityPath = getEntityPath(entityContext); if ( type === TraceloopSpanKindValues.TOOL || @@ -94,6 +99,12 @@ function withEntity< ); span.setAttribute(SpanAttributes.TRACELOOP_SPAN_KIND, type); + // Set agent name on all spans when there's an active agent context + const agentName = entityContext.getValue(AGENT_NAME_KEY); + if (agentName) { + span.setAttribute("gen_ai.agent.name", agentName as string); + } + if (version) { span.setAttribute(SpanAttributes.TRACELOOP_ENTITY_VERSION, version); } diff --git a/packages/traceloop-sdk/src/lib/tracing/manual.ts b/packages/traceloop-sdk/src/lib/tracing/manual.ts index 9a00fe06..75569db2 100644 --- a/packages/traceloop-sdk/src/lib/tracing/manual.ts +++ b/packages/traceloop-sdk/src/lib/tracing/manual.ts @@ -1,5 +1,5 @@ import { Span, context, trace } from "@opentelemetry/api"; -import { getTracer } from "./tracing"; +import { getTracer, AGENT_NAME_KEY } from "./tracing"; import { Events, EventAttributes, @@ -145,6 +145,12 @@ export function withVectorDBCall< { [SpanAttributes.LLM_REQUEST_TYPE]: type }, entityContext, (span: Span) => { + // Set agent name if there's an active agent context + const agentName = entityContext.getValue(AGENT_NAME_KEY); + if (agentName) { + span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); + } + const res = fn.apply(thisArg, [{ span: new VectorSpan(span) }]); if (res instanceof Promise) { return res.then((resolvedRes) => { @@ -162,9 +168,17 @@ export function withVectorDBCall< export function withLLMCall< F extends ({ span }: { span: LLMSpan }) => ReturnType, >({ vendor, type }: LLMCallConfig, fn: F, thisArg?: ThisParameterType) { - const span = getTracer().startSpan(`${vendor}.${type}`, {}, context.active()); + const currentContext = context.active(); + const span = getTracer().startSpan(`${vendor}.${type}`, {}, currentContext); span.setAttribute(SpanAttributes.LLM_REQUEST_TYPE, type); - trace.setSpan(context.active(), span); + + // Set agent name if there's an active agent context + const agentName = currentContext.getValue(AGENT_NAME_KEY); + if (agentName) { + span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); + } + + trace.setSpan(currentContext, span); const res = fn.apply(thisArg, [{ span: new LLMSpan(span) }]); if (res instanceof Promise) { diff --git a/packages/traceloop-sdk/src/lib/tracing/tracing.ts b/packages/traceloop-sdk/src/lib/tracing/tracing.ts index 92275668..15c2179c 100644 --- a/packages/traceloop-sdk/src/lib/tracing/tracing.ts +++ b/packages/traceloop-sdk/src/lib/tracing/tracing.ts @@ -4,6 +4,7 @@ import { Context } from "@opentelemetry/api/build/src/context/types"; const TRACER_NAME = "@traceloop/node-server-sdk"; export const WORKFLOW_NAME_KEY = createContextKey("workflow_name"); export const ENTITY_NAME_KEY = createContextKey("entity_name"); +export const AGENT_NAME_KEY = createContextKey("agent_name"); export const ASSOCATION_PROPERTIES_KEY = createContextKey( "association_properties", ); From 263f0c7bfc2e9f56a7907b3c89134e9af69c5499 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:32:48 +0300 Subject: [PATCH 02/10] on span start --- packages/traceloop-sdk/src/lib/tracing/span-processor.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts index 3215d019..41c78138 100644 --- a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts +++ b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts @@ -11,6 +11,7 @@ import { ASSOCATION_PROPERTIES_KEY, ENTITY_NAME_KEY, WORKFLOW_NAME_KEY, + AGENT_NAME_KEY, } from "./tracing"; import { SpanAttributes } from "@traceloop/ai-semantic-conventions"; import { transformAiSdkSpan } from "./ai-sdk-transformations"; @@ -144,6 +145,14 @@ const onSpanStart = (span: Span): void => { ); } + const agentName = context.active().getValue(AGENT_NAME_KEY); + console.log("On span start agentName: ", agentName, "span_id: ", span.spanContext().spanId); + if (agentName) { + span.setAttribute(SpanAttributes.LLM_AGENT_NAME, + agentName as string + ); + } + const associationProperties = context .active() .getValue(ASSOCATION_PROPERTIES_KEY); From acdc019b12971b8365e904de2d74d5f55faee4ee Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:40:28 +0300 Subject: [PATCH 03/10] works --- packages/traceloop-sdk/src/lib/tracing/span-processor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts index 41c78138..c8796ace 100644 --- a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts +++ b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts @@ -146,7 +146,7 @@ const onSpanStart = (span: Span): void => { } const agentName = context.active().getValue(AGENT_NAME_KEY); - console.log("On span start agentName: ", agentName, "span_id: ", span.spanContext().spanId); + console.log("On span start agentName: ", agentName, ": span_id: ", span.spanContext().spanId); if (agentName) { span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string From 2fec953d37ce7174172a0a584f040e509de8e743 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:40:50 +0300 Subject: [PATCH 04/10] add test --- .../recording.har | 207 +++++++++++++ .../recording.har | 207 +++++++++++++ .../test/agent_decorator.test.ts | 280 ++++++++++++++++++ 3 files changed, 694 insertions(+) create mode 100644 packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har create mode 100644 packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har create mode 100644 packages/traceloop-sdk/test/agent_decorator.test.ts diff --git a/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har b/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har new file mode 100644 index 00000000..ace2d3c7 --- /dev/null +++ b/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har @@ -0,0 +1,207 @@ +{ + "log": { + "_recordingName": "Test SDK Decorators/should create spans for agents using decoration syntax", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "3f88a6f174d2ccbdd95ead7906e706d1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 125, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "content-length", + "value": "125" + }, + { + "_fromType": "array", + "name": "accept", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "OpenAI/JS 4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-lang", + "value": "js" + }, + { + "_fromType": "array", + "name": "x-stainless-package-version", + "value": "4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-os", + "value": "MacOS" + }, + { + "_fromType": "array", + "name": "x-stainless-arch", + "value": "arm64" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime", + "value": "node" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime-version", + "value": "v22.19.0" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "name": "host", + "value": "api.openai.com" + } + ], + "headersSize": 419, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Plan a trip to Paris\"\n }\n ],\n \"model\": \"gpt-3.5-turbo\"\n}" + }, + "queryString": [], + "url": "https://api.openai.com/v1/chat/completions" + }, + "response": { + "bodySize": 496, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 496, + "text": "{\n \"error\": {\n \"message\": \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\",\n \"type\": \"invalid_request_error\",\n \"param\": null,\n \"code\": null\n }\n}\n" + }, + "cookies": [ + { + "domain": ".api.openai.com", + "expires": "2025-09-21T11:03:14.000Z", + "httpOnly": true, + "name": "__cf_bm", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "AwoNELi9c8_4ayE.7s2SWAV3d2Zy5E0mVZtoypvEba8-1758450794-1.0.1.1-WzHichxOXDFavHgKTto8L.wfMRjmjKtffD9Zbis.nVkV2CPdZqswsMqZ96VHhGIeFpAQtf9nsnrDrwpNWE_FLceBoch0qGjVPy_mIgIJFwA" + }, + { + "domain": ".api.openai.com", + "httpOnly": true, + "name": "_cfuvid", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "L2pYF1Umq8aMqZfNqGQ906M01t3Jw6GnQcAdwusWqfc-1758450794631-0.0.1.1-604800000" + } + ], + "headers": [ + { + "name": "date", + "value": "Sun, 21 Sep 2025 10:33:14 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-length", + "value": "496" + }, + { + "name": "connection", + "value": "keep-alive" + }, + { + "name": "vary", + "value": "Origin" + }, + { + "name": "x-request-id", + "value": "req_7be59bb340344cbc970a909b2299bb80" + }, + { + "name": "x-envoy-upstream-service-time", + "value": "0" + }, + { + "name": "x-openai-proxy-wasm", + "value": "v0.1" + }, + { + "name": "cf-cache-status", + "value": "DYNAMIC" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "__cf_bm=AwoNELi9c8_4ayE.7s2SWAV3d2Zy5E0mVZtoypvEba8-1758450794-1.0.1.1-WzHichxOXDFavHgKTto8L.wfMRjmjKtffD9Zbis.nVkV2CPdZqswsMqZ96VHhGIeFpAQtf9nsnrDrwpNWE_FLceBoch0qGjVPy_mIgIJFwA; path=/; expires=Sun, 21-Sep-25 11:03:14 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "_cfuvid=L2pYF1Umq8aMqZfNqGQ906M01t3Jw6GnQcAdwusWqfc-1758450794631-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "server", + "value": "cloudflare" + }, + { + "name": "cf-ray", + "value": "9828e739fceabff2-ATL" + }, + { + "name": "alt-svc", + "value": "h3=\":443\"; ma=86400" + } + ], + "headersSize": 926, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 401, + "statusText": "Unauthorized" + }, + "startedDateTime": "2025-09-21T10:33:14.161Z", + "time": 238, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 238 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har b/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har new file mode 100644 index 00000000..e0d7a0a1 --- /dev/null +++ b/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har @@ -0,0 +1,207 @@ +{ + "log": { + "_recordingName": "Test SDK Decorators/should create spans for agents using withAgent syntax", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "80b48c70cae76a3c38b9af59d5273e33", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 139, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "content-length", + "value": "139" + }, + { + "_fromType": "array", + "name": "accept", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "OpenAI/JS 4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-lang", + "value": "js" + }, + { + "_fromType": "array", + "name": "x-stainless-package-version", + "value": "4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-os", + "value": "MacOS" + }, + { + "_fromType": "array", + "name": "x-stainless-arch", + "value": "arm64" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime", + "value": "node" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime-version", + "value": "v22.19.0" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "name": "host", + "value": "api.openai.com" + } + ], + "headersSize": 419, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Tell me a joke about OpenTelemetry\"\n }\n ],\n \"model\": \"gpt-3.5-turbo\"\n}" + }, + "queryString": [], + "url": "https://api.openai.com/v1/chat/completions" + }, + "response": { + "bodySize": 496, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 496, + "text": "{\n \"error\": {\n \"message\": \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\",\n \"type\": \"invalid_request_error\",\n \"param\": null,\n \"code\": null\n }\n}\n" + }, + "cookies": [ + { + "domain": ".api.openai.com", + "expires": "2025-09-21T11:03:14.000Z", + "httpOnly": true, + "name": "__cf_bm", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "w2dCODYd6xOIHy75Rgx_pXtBUzGvUiXg3B1ESwhhpdk-1758450794-1.0.1.1-NZKEU3wV0RKKHJQgegLm8KhCW6ACC26PXJ.W2ffNVgJGqetM0pSTBNCyE7pwG1zybZvGFBhAv.JX2MlfSXS2vaghltcrRa5OQfysVxg6BhQ" + }, + { + "domain": ".api.openai.com", + "httpOnly": true, + "name": "_cfuvid", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "7n0lyMKe63Fb94MgHuoxVNqgBIiM.ZgwlwoSm1M5ixc-1758450794388-0.0.1.1-604800000" + } + ], + "headers": [ + { + "name": "date", + "value": "Sun, 21 Sep 2025 10:33:14 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-length", + "value": "496" + }, + { + "name": "connection", + "value": "keep-alive" + }, + { + "name": "vary", + "value": "Origin" + }, + { + "name": "x-request-id", + "value": "req_c57f38ce78cf416082d6eef4735c8384" + }, + { + "name": "x-envoy-upstream-service-time", + "value": "0" + }, + { + "name": "x-openai-proxy-wasm", + "value": "v0.1" + }, + { + "name": "cf-cache-status", + "value": "DYNAMIC" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "__cf_bm=w2dCODYd6xOIHy75Rgx_pXtBUzGvUiXg3B1ESwhhpdk-1758450794-1.0.1.1-NZKEU3wV0RKKHJQgegLm8KhCW6ACC26PXJ.W2ffNVgJGqetM0pSTBNCyE7pwG1zybZvGFBhAv.JX2MlfSXS2vaghltcrRa5OQfysVxg6BhQ; path=/; expires=Sun, 21-Sep-25 11:03:14 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "_cfuvid=7n0lyMKe63Fb94MgHuoxVNqgBIiM.ZgwlwoSm1M5ixc-1758450794388-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "server", + "value": "cloudflare" + }, + { + "name": "cf-ray", + "value": "9828e7386bddbff2-ATL" + }, + { + "name": "alt-svc", + "value": "h3=\":443\"; ma=86400" + } + ], + "headersSize": 926, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 401, + "statusText": "Unauthorized" + }, + "startedDateTime": "2025-09-21T10:33:13.920Z", + "time": 235, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 235 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/packages/traceloop-sdk/test/agent_decorator.test.ts b/packages/traceloop-sdk/test/agent_decorator.test.ts new file mode 100644 index 00000000..6ed4042c --- /dev/null +++ b/packages/traceloop-sdk/test/agent_decorator.test.ts @@ -0,0 +1,280 @@ +/* + * Copyright Traceloop + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from "assert"; + +import type * as OpenAIModule from "openai"; + +import * as traceloop from "../src"; + +import { Polly, setupMocha as setupPolly } from "@pollyjs/core"; +import NodeHttpAdapter from "@pollyjs/adapter-node-http"; +import FetchAdapter from "@pollyjs/adapter-fetch"; +import FSPersister from "@pollyjs/persister-fs"; +import { SpanAttributes } from "@traceloop/ai-semantic-conventions"; +import { ChatCompletionMessageParam } from "openai/resources/index.mjs"; +import { initializeSharedTraceloop, getSharedExporter } from "./test-setup"; + +const memoryExporter = getSharedExporter(); + +Polly.register(NodeHttpAdapter); +Polly.register(FetchAdapter); +Polly.register(FSPersister); + +describe("Test Agent Decorator", () => { + let openai: OpenAIModule.OpenAI; + + setupPolly({ + adapters: ["node-http", "fetch"], + persister: "fs", + recordIfMissing: process.env.RECORD_MODE === "NEW", + recordFailedRequests: true, + mode: process.env.RECORD_MODE === "NEW" ? "record" : "replay", + matchRequestsBy: { + headers: false, + url: { + protocol: true, + hostname: true, + pathname: true, + query: false, + }, + }, + logging: true, + }); + + before(async function () { + if (process.env.RECORD_MODE !== "NEW") { + process.env.OPENAI_API_KEY = "test"; + } + + // Use shared initialization to avoid conflicts with other test suites + initializeSharedTraceloop(); + + // Initialize OpenAI after Polly is set up + const openAIModule: typeof OpenAIModule = await import("openai"); + openai = new openAIModule.OpenAI(); + }); + + beforeEach(function () { + const { server } = this.polly as Polly; + server.any().on("beforePersist", (_req, recording) => { + recording.request.headers = recording.request.headers.filter( + ({ name }: { name: string }) => name !== "authorization", + ); + }); + }); + + afterEach(async () => { + memoryExporter.reset(); + }); + + it("should create spans for agents using withAgent syntax", async () => { + const jokeSubject = "OpenTelemetry"; + const result = await traceloop.withAgent( + { name: "plan_trip", associationProperties: { userId: "123" } }, + async () => { + const chatCompletion = await openai.chat.completions.create({ + messages: [ + { role: "user", content: `Tell me a joke about ${jokeSubject}` }, + ], + model: "gpt-3.5-turbo", + }); + + return chatCompletion.choices[0].message.content; + }, + { jokeSubject }, + ); + + await traceloop.forceFlush(); + const spans = memoryExporter.getFinishedSpans(); + + const agentSpan = spans.find((span) => span.name === "plan_trip.agent"); + const chatSpan = spans.find((span) => span.name === "openai.chat"); + + assert.ok(result); + assert.ok(agentSpan); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_WORKFLOW_NAME}`], + "plan_trip", + ); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_SPAN_KIND}`], + "agent", + ); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_ENTITY_NAME}`], + "plan_trip", + ); + assert.strictEqual( + agentSpan.attributes[ + `${SpanAttributes.TRACELOOP_ASSOCIATION_PROPERTIES}.userId` + ], + "123", + ); + assert.ok(chatSpan); + assert.strictEqual( + chatSpan.attributes[ + `${SpanAttributes.TRACELOOP_ASSOCIATION_PROPERTIES}.userId` + ], + "123", + ); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.TRACELOOP_WORKFLOW_NAME}`], + "plan_trip", + ); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.LLM_AGENT_NAME}`], + "plan_trip", + ); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.role`], + "user", + ); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.content`], + "Tell me a joke about OpenTelemetry", + ); + }); + + it("should create spans for agents using decoration syntax", async () => { + class TestAgent { + @traceloop.agent({ name: "travel_planner", version: 2 }) + async planTrip(destination: string) { + const chatCompletion = await openai.chat.completions.create({ + messages: [ + { role: "user", content: `Plan a trip to ${destination}` }, + ], + model: "gpt-3.5-turbo", + }); + + return chatCompletion.choices[0].message.content; + } + } + + const testAgent = new TestAgent(); + const result = await testAgent.planTrip("Paris"); + + const spans = memoryExporter.getFinishedSpans(); + const agentSpan = spans.find((span) => span.name === "travel_planner.agent"); + const chatSpan = spans.find((span) => span.name === "openai.chat"); + + assert.ok(result); + assert.ok(agentSpan); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_WORKFLOW_NAME}`], + "travel_planner", + ); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_SPAN_KIND}`], + "agent", + ); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_ENTITY_NAME}`], + "travel_planner", + ); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_ENTITY_VERSION}`], + 2, + ); + assert.ok(chatSpan); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.TRACELOOP_WORKFLOW_NAME}`], + "travel_planner", + ); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.LLM_AGENT_NAME}`], + "travel_planner", + ); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.role`], + "user", + ); + assert.strictEqual( + chatSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.content`], + "Plan a trip to Paris", + ); + }); + + it("should propagate agent name to manual LLM instrumentation", async () => { + const result = await traceloop.withAgent( + { name: "assistant", associationProperties: { userId: "123" } }, + () => + traceloop.withLLMCall( + { vendor: "openai", type: "chat" }, + async ({ span }) => { + const messages: ChatCompletionMessageParam[] = [ + { role: "user", content: "Tell me a joke about agents" }, + ]; + const model = "gpt-3.5-turbo"; + + span.reportRequest({ model, messages }); + + const response = await openai.chat.completions.create({ + messages, + model, + }); + + span.reportResponse(response); + + return response; + }, + ), + ); + + await traceloop.forceFlush(); + const spans = memoryExporter.getFinishedSpans(); + const agentSpan = spans.find((span) => span.name === "assistant.agent"); + const completionSpan = spans.find((span) => span.name === "openai.chat"); + + assert.ok(result); + assert.ok(completionSpan); + assert.ok(agentSpan); + assert.strictEqual( + agentSpan.attributes[`${SpanAttributes.TRACELOOP_WORKFLOW_NAME}`], + "assistant", + ); + assert.strictEqual( + completionSpan.parentSpanContext?.spanId, + agentSpan.spanContext().spanId, + ); + assert.strictEqual( + completionSpan.attributes[ + `${SpanAttributes.TRACELOOP_ASSOCIATION_PROPERTIES}.userId` + ], + "123", + ); + assert.strictEqual( + completionSpan.attributes[`${SpanAttributes.LLM_AGENT_NAME}`], + "assistant", + ); + assert.strictEqual( + completionSpan.attributes[`${SpanAttributes.LLM_REQUEST_TYPE}`], + "chat", + ); + assert.strictEqual( + completionSpan.attributes[`${SpanAttributes.LLM_REQUEST_MODEL}`], + "gpt-3.5-turbo", + ); + assert.strictEqual( + completionSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.role`], + "user", + ); + assert.strictEqual( + completionSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.content`], + "Tell me a joke about agents", + ); + }); +}); \ No newline at end of file From a8d41efe6e6c7cc514dca1a95854339a5c3080f2 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:44:27 +0300 Subject: [PATCH 05/10] pretty --- packages/traceloop-sdk/src/lib/tracing/decorators.ts | 2 +- packages/traceloop-sdk/src/lib/tracing/span-processor.ts | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/traceloop-sdk/src/lib/tracing/decorators.ts b/packages/traceloop-sdk/src/lib/tracing/decorators.ts index ae39af52..50d34afe 100644 --- a/packages/traceloop-sdk/src/lib/tracing/decorators.ts +++ b/packages/traceloop-sdk/src/lib/tracing/decorators.ts @@ -102,7 +102,7 @@ function withEntity< // Set agent name on all spans when there's an active agent context const agentName = entityContext.getValue(AGENT_NAME_KEY); if (agentName) { - span.setAttribute("gen_ai.agent.name", agentName as string); + span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); } if (version) { diff --git a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts index c8796ace..aa710c40 100644 --- a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts +++ b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts @@ -146,11 +146,8 @@ const onSpanStart = (span: Span): void => { } const agentName = context.active().getValue(AGENT_NAME_KEY); - console.log("On span start agentName: ", agentName, ": span_id: ", span.spanContext().spanId); if (agentName) { - span.setAttribute(SpanAttributes.LLM_AGENT_NAME, - agentName as string - ); + span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); } const associationProperties = context From de3a40af65615336904d788f15575044df77483f Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:02:10 +0300 Subject: [PATCH 06/10] fix test --- .../recording.har | 248 ++++++++++++++++++ .../recording.har | 248 ++++++++++++++++++ .../recording.har | 248 ++++++++++++++++++ .../test/agent_decorator.test.ts | 8 +- 4 files changed, 748 insertions(+), 4 deletions(-) create mode 100644 packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har create mode 100644 packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har create mode 100644 packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-propagate-agent-name-to-manual-LLM-instrumentation_2332462647/recording.har diff --git a/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har b/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har new file mode 100644 index 00000000..e636d631 --- /dev/null +++ b/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har @@ -0,0 +1,248 @@ +{ + "log": { + "_recordingName": "Test Agent Decorator/should create spans for agents using decoration syntax", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "80b48c70cae76a3c38b9af59d5273e33", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 139, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "content-length", + "value": "139" + }, + { + "_fromType": "array", + "name": "accept", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "OpenAI/JS 4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-lang", + "value": "js" + }, + { + "_fromType": "array", + "name": "x-stainless-package-version", + "value": "4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-os", + "value": "MacOS" + }, + { + "_fromType": "array", + "name": "x-stainless-arch", + "value": "arm64" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime", + "value": "node" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime-version", + "value": "v20.11.1" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "name": "host", + "value": "api.openai.com" + } + ], + "headersSize": 583, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Tell me a joke about OpenTelemetry\"\n }\n ],\n \"model\": \"gpt-3.5-turbo\"\n}" + }, + "queryString": [], + "url": "https://api.openai.com/v1/chat/completions" + }, + "response": { + "bodySize": 651, + "content": { + "encoding": "base64", + "mimeType": "application/json", + "size": 651, + "text": "[\"H4sIAAAAAAAAAwAAAP//\",\"jFLLjhMxELzPVzS+cElWSdjsIxcE7AkOgIS0CLQa9dg9M816bK/dwxJW+XfkyWMSHhIXH7q6yt1V/VQAKDZqBUq3KLoLdvrm6ub8nf18k17Rdf1gq2jslw8/w+Pb2cPHWzXJDF99Iy171pn2XbAk7N0W1pFQKKvOL5cXs8vF1fXFAHTekM20Jsj0xdlyKn2s/HQ2Xyx3zNazpqRW8LUAAHga3jyjM/RDrWA22Vc6SgkbUqtDE4CK3uaKwpQ4CTpRkxHU3gm5Yezbdg2GDUhL8D6Q+0SWOpK4BstVxLiGKhLeQx/gkaUFlgQNR1tHJmdewmvS2CcCFtC+t8Y9F2jRGUuAEMliNiO1vKOjtSAtClTYNNjQs+OxItV9wmyL6609AtA5L1ulbMjdDtkcLLC+CdFX6TeqqtlxastImLzL6ybxQQ3opgC4G6zuT9xTIfouSCn+nobv5sutnBrDHcHFHhQvaMf6+S6eU7XSkCDbdBSV0qhbMiNzzBV7w/4IKI52/nOYv2lv92bX/I/8CGhNQciUIZJhfbrw2BYpn/6/2g4eDwOrRPE7ayqFKeYcDNXY2+1RqrROQl1Zs2sohsjDZeYci03xCwAA//8DAEaTkYeYAwAA\"]" + }, + "cookies": [ + { + "domain": ".api.openai.com", + "expires": "2025-08-24T22:31:37.000Z", + "httpOnly": true, + "name": "__cf_bm", + "path": "/", + "sameSite": "None", + "secure": true, + "value": ".o0a7QV1g5p2PGVThh2GmiCx5a8.gEUKTV5wQkJcV2o-1756072897-1.0.1.1-p53oAolAr8UafMy5McpEJ9rhTr5Z82LsQPQVeQ5Wca5wKQEq9kaa9EnemekZ6_KBaRWxhl5F.Kf4qkjUGsFC5a3LGZVtl9qnhZompi2UvS0" + }, + { + "domain": ".api.openai.com", + "httpOnly": true, + "name": "_cfuvid", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "YH._SGFPHKE16IZLGfcgZL0Vqk..kuHrGz0kglvAD94-1756072897170-0.0.1.1-604800000" + } + ], + "headers": [ + { + "name": "date", + "value": "Sun, 24 Aug 2025 22:01:37 GMT" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "keep-alive" + }, + { + "name": "access-control-expose-headers", + "value": "X-Request-ID" + }, + { + "name": "openai-organization", + "value": "traceloop" + }, + { + "name": "openai-processing-ms", + "value": "334" + }, + { + "name": "openai-project", + "value": "proj_tzz1TbPPOXaf6j9tEkVUBIAa" + }, + { + "name": "openai-version", + "value": "2020-10-01" + }, + { + "name": "x-envoy-upstream-service-time", + "value": "414" + }, + { + "name": "x-ratelimit-limit-requests", + "value": "10000" + }, + { + "name": "x-ratelimit-limit-tokens", + "value": "50000000" + }, + { + "name": "x-ratelimit-remaining-requests", + "value": "9999" + }, + { + "name": "x-ratelimit-remaining-tokens", + "value": "49999989" + }, + { + "name": "x-ratelimit-reset-requests", + "value": "6ms" + }, + { + "name": "x-ratelimit-reset-tokens", + "value": "0s" + }, + { + "name": "x-request-id", + "value": "req_fc8ecede554a4503985a45721f1b8aa9" + }, + { + "name": "cf-cache-status", + "value": "DYNAMIC" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "__cf_bm=.o0a7QV1g5p2PGVThh2GmiCx5a8.gEUKTV5wQkJcV2o-1756072897-1.0.1.1-p53oAolAr8UafMy5McpEJ9rhTr5Z82LsQPQVeQ5Wca5wKQEq9kaa9EnemekZ6_KBaRWxhl5F.Kf4qkjUGsFC5a3LGZVtl9qnhZompi2UvS0; path=/; expires=Sun, 24-Aug-25 22:31:37 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "_cfuvid=YH._SGFPHKE16IZLGfcgZL0Vqk..kuHrGz0kglvAD94-1756072897170-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "server", + "value": "cloudflare" + }, + { + "name": "cf-ray", + "value": "97462113aa47c224-TLV" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "alt-svc", + "value": "h3=\":443\"; ma=86400" + } + ], + "headersSize": 1294, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-08-24T22:01:36.384Z", + "time": 612, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 612 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har b/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har new file mode 100644 index 00000000..0b00b65b --- /dev/null +++ b/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har @@ -0,0 +1,248 @@ +{ + "log": { + "_recordingName": "Test Agent Decorator/should create spans for agents using withAgent syntax", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "80b48c70cae76a3c38b9af59d5273e33", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 139, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "content-length", + "value": "139" + }, + { + "_fromType": "array", + "name": "accept", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "OpenAI/JS 4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-lang", + "value": "js" + }, + { + "_fromType": "array", + "name": "x-stainless-package-version", + "value": "4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-os", + "value": "MacOS" + }, + { + "_fromType": "array", + "name": "x-stainless-arch", + "value": "arm64" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime", + "value": "node" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime-version", + "value": "v20.11.1" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "name": "host", + "value": "api.openai.com" + } + ], + "headersSize": 583, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Tell me a joke about OpenTelemetry\"\n }\n ],\n \"model\": \"gpt-3.5-turbo\"\n}" + }, + "queryString": [], + "url": "https://api.openai.com/v1/chat/completions" + }, + "response": { + "bodySize": 651, + "content": { + "encoding": "base64", + "mimeType": "application/json", + "size": 651, + "text": "[\"H4sIAAAAAAAAAwAAAP//\",\"jFLLjhMxELzPVzS+cElWSdjsIxcE7AkOgIS0CLQa9dg9M816bK/dwxJW+XfkyWMSHhIXH7q6yt1V/VQAKDZqBUq3KLoLdvrm6ub8nf18k17Rdf1gq2jslw8/w+Pb2cPHWzXJDF99Iy171pn2XbAk7N0W1pFQKKvOL5cXs8vF1fXFAHTekM20Jsj0xdlyKn2s/HQ2Xyx3zNazpqRW8LUAAHga3jyjM/RDrWA22Vc6SgkbUqtDE4CK3uaKwpQ4CTpRkxHU3gm5Yezbdg2GDUhL8D6Q+0SWOpK4BstVxLiGKhLeQx/gkaUFlgQNR1tHJmdewmvS2CcCFtC+t8Y9F2jRGUuAEMliNiO1vKOjtSAtClTYNNjQs+OxItV9wmyL6609AtA5L1ulbMjdDtkcLLC+CdFX6TeqqtlxastImLzL6ybxQQ3opgC4G6zuT9xTIfouSCn+nobv5sutnBrDHcHFHhQvaMf6+S6eU7XSkCDbdBSV0qhbMiNzzBV7w/4IKI52/nOYv2lv92bX/I/8CGhNQciUIZJhfbrw2BYpn/6/2g4eDwOrRPE7ayqFKeYcDNXY2+1RqrROQl1Zs2sohsjDZeYci03xCwAA//8DAEaTkYeYAwAA\"]" + }, + "cookies": [ + { + "domain": ".api.openai.com", + "expires": "2025-08-24T22:31:37.000Z", + "httpOnly": true, + "name": "__cf_bm", + "path": "/", + "sameSite": "None", + "secure": true, + "value": ".o0a7QV1g5p2PGVThh2GmiCx5a8.gEUKTV5wQkJcV2o-1756072897-1.0.1.1-p53oAolAr8UafMy5McpEJ9rhTr5Z82LsQPQVeQ5Wca5wKQEq9kaa9EnemekZ6_KBaRWxhl5F.Kf4qkjUGsFC5a3LGZVtl9qnhZompi2UvS0" + }, + { + "domain": ".api.openai.com", + "httpOnly": true, + "name": "_cfuvid", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "YH._SGFPHKE16IZLGfcgZL0Vqk..kuHrGz0kglvAD94-1756072897170-0.0.1.1-604800000" + } + ], + "headers": [ + { + "name": "date", + "value": "Sun, 24 Aug 2025 22:01:37 GMT" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "keep-alive" + }, + { + "name": "access-control-expose-headers", + "value": "X-Request-ID" + }, + { + "name": "openai-organization", + "value": "traceloop" + }, + { + "name": "openai-processing-ms", + "value": "334" + }, + { + "name": "openai-project", + "value": "proj_tzz1TbPPOXaf6j9tEkVUBIAa" + }, + { + "name": "openai-version", + "value": "2020-10-01" + }, + { + "name": "x-envoy-upstream-service-time", + "value": "414" + }, + { + "name": "x-ratelimit-limit-requests", + "value": "10000" + }, + { + "name": "x-ratelimit-limit-tokens", + "value": "50000000" + }, + { + "name": "x-ratelimit-remaining-requests", + "value": "9999" + }, + { + "name": "x-ratelimit-remaining-tokens", + "value": "49999989" + }, + { + "name": "x-ratelimit-reset-requests", + "value": "6ms" + }, + { + "name": "x-ratelimit-reset-tokens", + "value": "0s" + }, + { + "name": "x-request-id", + "value": "req_fc8ecede554a4503985a45721f1b8aa9" + }, + { + "name": "cf-cache-status", + "value": "DYNAMIC" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "__cf_bm=.o0a7QV1g5p2PGVThh2GmiCx5a8.gEUKTV5wQkJcV2o-1756072897-1.0.1.1-p53oAolAr8UafMy5McpEJ9rhTr5Z82LsQPQVeQ5Wca5wKQEq9kaa9EnemekZ6_KBaRWxhl5F.Kf4qkjUGsFC5a3LGZVtl9qnhZompi2UvS0; path=/; expires=Sun, 24-Aug-25 22:31:37 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "_cfuvid=YH._SGFPHKE16IZLGfcgZL0Vqk..kuHrGz0kglvAD94-1756072897170-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "server", + "value": "cloudflare" + }, + { + "name": "cf-ray", + "value": "97462113aa47c224-TLV" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "alt-svc", + "value": "h3=\":443\"; ma=86400" + } + ], + "headersSize": 1294, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-08-24T22:01:36.384Z", + "time": 612, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 612 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-propagate-agent-name-to-manual-LLM-instrumentation_2332462647/recording.har b/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-propagate-agent-name-to-manual-LLM-instrumentation_2332462647/recording.har new file mode 100644 index 00000000..95d708ec --- /dev/null +++ b/packages/traceloop-sdk/recordings/Test-Agent-Decorator_2969879889/should-propagate-agent-name-to-manual-LLM-instrumentation_2332462647/recording.har @@ -0,0 +1,248 @@ +{ + "log": { + "_recordingName": "Test Agent Decorator/should propagate agent name to manual LLM instrumentation", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "80b48c70cae76a3c38b9af59d5273e33", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 139, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "content-length", + "value": "139" + }, + { + "_fromType": "array", + "name": "accept", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "OpenAI/JS 4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-lang", + "value": "js" + }, + { + "_fromType": "array", + "name": "x-stainless-package-version", + "value": "4.38.3" + }, + { + "_fromType": "array", + "name": "x-stainless-os", + "value": "MacOS" + }, + { + "_fromType": "array", + "name": "x-stainless-arch", + "value": "arm64" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime", + "value": "node" + }, + { + "_fromType": "array", + "name": "x-stainless-runtime-version", + "value": "v20.11.1" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "name": "host", + "value": "api.openai.com" + } + ], + "headersSize": 583, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Tell me a joke about OpenTelemetry\"\n }\n ],\n \"model\": \"gpt-3.5-turbo\"\n}" + }, + "queryString": [], + "url": "https://api.openai.com/v1/chat/completions" + }, + "response": { + "bodySize": 623, + "content": { + "encoding": "base64", + "mimeType": "application/json", + "size": 623, + "text": "[\"H4sIAAAAAAAAAwAAAP//\",\"jFJNb9swDL37V3C69JIU+ZjXLJcBa1H0lq0Y1hVFYSgSY2uVRUGigwZF/vsgJYvdrQN20YGP74mPjy8FgDBaLEGoRrJqvR1fLq7er6bX7erH/a35/nXR3erLZlbSzfbuy0yMEoPWP1Hxb9a5otZbZEPuAKuAkjGpTi/KD5OL2cfJJAMtabSJVnsez8/LMXdhTePJdFYemQ0ZhVEs4aEAAHjJb5rRaXwWS8g6udJijLJGsTw1AYhANlWEjNFElo7FqAcVOUaXx75rdqCNBm4QVh7dN7TYIocdaNyiJY8BaoJ1oCf8BJ9RyS5i6t6Bos5qd8bAQapcMwHw2aOLGN8N/wu46aJMfl1n7QCQzhHLtK/s9PGI7E/eLNU+0Dr+QRUb40xsqoAykks+IpMXGd0XAI95h92rtQgfqPVcMT1h/m5aHuREn9oAXBxBJpa2r8/nozfUKo0sjY2DDISSqkHdM/vAZKcNDYBi4PnvYd7SPvg2rv4f+R5QCj2jrnxAbdRrw31bwHTT/2o77TgPLCKGrVFYscGQctC4kZ09XJuIu8jYVhvjagw+mHxyKcdiX/wCAAD//wMABQIPSXEDAAA=\"]" + }, + "cookies": [ + { + "domain": ".api.openai.com", + "expires": "2025-08-24T22:31:41.000Z", + "httpOnly": true, + "name": "__cf_bm", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "yA3yaxTVq_lRzeGvndltDF4hGkZn9kZFrWae1e49_LM-1756072901-1.0.1.1-a6dJxx8ZUNkw16gR0W5ZhZJTb0EuHUyPyPxESerXEko9thjRpPGdnqRhWWeQHfWQq3JmiqoZ_CG9ka0u0CAGDjBoDkBsiPdYOd1NTixzAGA" + }, + { + "domain": ".api.openai.com", + "httpOnly": true, + "name": "_cfuvid", + "path": "/", + "sameSite": "None", + "secure": true, + "value": "iQtUVjYqEy.DU6WhyJf3Muh3Lu7OOiBtEvvkWgQ9KN8-1756072901348-0.0.1.1-604800000" + } + ], + "headers": [ + { + "name": "date", + "value": "Sun, 24 Aug 2025 22:01:41 GMT" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "keep-alive" + }, + { + "name": "access-control-expose-headers", + "value": "X-Request-ID" + }, + { + "name": "openai-organization", + "value": "traceloop" + }, + { + "name": "openai-processing-ms", + "value": "359" + }, + { + "name": "openai-project", + "value": "proj_tzz1TbPPOXaf6j9tEkVUBIAa" + }, + { + "name": "openai-version", + "value": "2020-10-01" + }, + { + "name": "x-envoy-upstream-service-time", + "value": "463" + }, + { + "name": "x-ratelimit-limit-requests", + "value": "10000" + }, + { + "name": "x-ratelimit-limit-tokens", + "value": "50000000" + }, + { + "name": "x-ratelimit-remaining-requests", + "value": "9999" + }, + { + "name": "x-ratelimit-remaining-tokens", + "value": "49999989" + }, + { + "name": "x-ratelimit-reset-requests", + "value": "6ms" + }, + { + "name": "x-ratelimit-reset-tokens", + "value": "0s" + }, + { + "name": "x-request-id", + "value": "req_e724b47ef08549ee913f345a8fed7bae" + }, + { + "name": "cf-cache-status", + "value": "DYNAMIC" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "__cf_bm=yA3yaxTVq_lRzeGvndltDF4hGkZn9kZFrWae1e49_LM-1756072901-1.0.1.1-a6dJxx8ZUNkw16gR0W5ZhZJTb0EuHUyPyPxESerXEko9thjRpPGdnqRhWWeQHfWQq3JmiqoZ_CG9ka0u0CAGDjBoDkBsiPdYOd1NTixzAGA; path=/; expires=Sun, 24-Aug-25 22:31:41 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "_cfuvid=iQtUVjYqEy.DU6WhyJf3Muh3Lu7OOiBtEvvkWgQ9KN8-1756072901348-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "server", + "value": "cloudflare" + }, + { + "name": "cf-ray", + "value": "9746212d6fb1c224-TLV" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "alt-svc", + "value": "h3=\":443\"; ma=86400" + } + ], + "headersSize": 1294, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-08-24T22:01:40.520Z", + "time": 649, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 649 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/packages/traceloop-sdk/test/agent_decorator.test.ts b/packages/traceloop-sdk/test/agent_decorator.test.ts index 6ed4042c..c6cc3088 100644 --- a/packages/traceloop-sdk/test/agent_decorator.test.ts +++ b/packages/traceloop-sdk/test/agent_decorator.test.ts @@ -155,7 +155,7 @@ describe("Test Agent Decorator", () => { async planTrip(destination: string) { const chatCompletion = await openai.chat.completions.create({ messages: [ - { role: "user", content: `Plan a trip to ${destination}` }, + { role: "user", content: `Tell me a joke about OpenTelemetry` }, ], model: "gpt-3.5-turbo", }); @@ -204,7 +204,7 @@ describe("Test Agent Decorator", () => { ); assert.strictEqual( chatSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.content`], - "Plan a trip to Paris", + "Tell me a joke about OpenTelemetry", ); }); @@ -216,7 +216,7 @@ describe("Test Agent Decorator", () => { { vendor: "openai", type: "chat" }, async ({ span }) => { const messages: ChatCompletionMessageParam[] = [ - { role: "user", content: "Tell me a joke about agents" }, + { role: "user", content: "Tell me a joke about OpenTelemetry" }, ]; const model = "gpt-3.5-turbo"; @@ -274,7 +274,7 @@ describe("Test Agent Decorator", () => { ); assert.strictEqual( completionSpan.attributes[`${SpanAttributes.LLM_PROMPTS}.0.content`], - "Tell me a joke about agents", + "Tell me a joke about OpenTelemetry", ); }); }); \ No newline at end of file From a11c838bd02f660253a6d761a3930e26125221f2 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:04:38 +0300 Subject: [PATCH 07/10] pretty --- packages/traceloop-sdk/test/agent_decorator.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/traceloop-sdk/test/agent_decorator.test.ts b/packages/traceloop-sdk/test/agent_decorator.test.ts index c6cc3088..11c526c7 100644 --- a/packages/traceloop-sdk/test/agent_decorator.test.ts +++ b/packages/traceloop-sdk/test/agent_decorator.test.ts @@ -168,7 +168,9 @@ describe("Test Agent Decorator", () => { const result = await testAgent.planTrip("Paris"); const spans = memoryExporter.getFinishedSpans(); - const agentSpan = spans.find((span) => span.name === "travel_planner.agent"); + const agentSpan = spans.find( + (span) => span.name === "travel_planner.agent", + ); const chatSpan = spans.find((span) => span.name === "openai.chat"); assert.ok(result); @@ -277,4 +279,4 @@ describe("Test Agent Decorator", () => { "Tell me a joke about OpenTelemetry", ); }); -}); \ No newline at end of file +}); From 014b1a6794a49c43bd039fabcf2f8567dc4f9a1a Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:07:05 +0300 Subject: [PATCH 08/10] delete recording --- .../recording.har | 207 ------------------ .../recording.har | 207 ------------------ 2 files changed, 414 deletions(-) delete mode 100644 packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har delete mode 100644 packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har diff --git a/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har b/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har deleted file mode 100644 index ace2d3c7..00000000 --- a/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-decoration-syntax_1932039671/recording.har +++ /dev/null @@ -1,207 +0,0 @@ -{ - "log": { - "_recordingName": "Test SDK Decorators/should create spans for agents using decoration syntax", - "creator": { - "comment": "persister:fs", - "name": "Polly.JS", - "version": "6.0.6" - }, - "entries": [ - { - "_id": "3f88a6f174d2ccbdd95ead7906e706d1", - "_order": 0, - "cache": {}, - "request": { - "bodySize": 125, - "cookies": [], - "headers": [ - { - "_fromType": "array", - "name": "content-length", - "value": "125" - }, - { - "_fromType": "array", - "name": "accept", - "value": "application/json" - }, - { - "_fromType": "array", - "name": "content-type", - "value": "application/json" - }, - { - "_fromType": "array", - "name": "user-agent", - "value": "OpenAI/JS 4.38.3" - }, - { - "_fromType": "array", - "name": "x-stainless-lang", - "value": "js" - }, - { - "_fromType": "array", - "name": "x-stainless-package-version", - "value": "4.38.3" - }, - { - "_fromType": "array", - "name": "x-stainless-os", - "value": "MacOS" - }, - { - "_fromType": "array", - "name": "x-stainless-arch", - "value": "arm64" - }, - { - "_fromType": "array", - "name": "x-stainless-runtime", - "value": "node" - }, - { - "_fromType": "array", - "name": "x-stainless-runtime-version", - "value": "v22.19.0" - }, - { - "_fromType": "array", - "name": "accept-encoding", - "value": "gzip,deflate" - }, - { - "name": "host", - "value": "api.openai.com" - } - ], - "headersSize": 419, - "httpVersion": "HTTP/1.1", - "method": "POST", - "postData": { - "mimeType": "application/json", - "params": [], - "text": "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Plan a trip to Paris\"\n }\n ],\n \"model\": \"gpt-3.5-turbo\"\n}" - }, - "queryString": [], - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "bodySize": 496, - "content": { - "mimeType": "application/json; charset=utf-8", - "size": 496, - "text": "{\n \"error\": {\n \"message\": \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\",\n \"type\": \"invalid_request_error\",\n \"param\": null,\n \"code\": null\n }\n}\n" - }, - "cookies": [ - { - "domain": ".api.openai.com", - "expires": "2025-09-21T11:03:14.000Z", - "httpOnly": true, - "name": "__cf_bm", - "path": "/", - "sameSite": "None", - "secure": true, - "value": "AwoNELi9c8_4ayE.7s2SWAV3d2Zy5E0mVZtoypvEba8-1758450794-1.0.1.1-WzHichxOXDFavHgKTto8L.wfMRjmjKtffD9Zbis.nVkV2CPdZqswsMqZ96VHhGIeFpAQtf9nsnrDrwpNWE_FLceBoch0qGjVPy_mIgIJFwA" - }, - { - "domain": ".api.openai.com", - "httpOnly": true, - "name": "_cfuvid", - "path": "/", - "sameSite": "None", - "secure": true, - "value": "L2pYF1Umq8aMqZfNqGQ906M01t3Jw6GnQcAdwusWqfc-1758450794631-0.0.1.1-604800000" - } - ], - "headers": [ - { - "name": "date", - "value": "Sun, 21 Sep 2025 10:33:14 GMT" - }, - { - "name": "content-type", - "value": "application/json; charset=utf-8" - }, - { - "name": "content-length", - "value": "496" - }, - { - "name": "connection", - "value": "keep-alive" - }, - { - "name": "vary", - "value": "Origin" - }, - { - "name": "x-request-id", - "value": "req_7be59bb340344cbc970a909b2299bb80" - }, - { - "name": "x-envoy-upstream-service-time", - "value": "0" - }, - { - "name": "x-openai-proxy-wasm", - "value": "v0.1" - }, - { - "name": "cf-cache-status", - "value": "DYNAMIC" - }, - { - "_fromType": "array", - "name": "set-cookie", - "value": "__cf_bm=AwoNELi9c8_4ayE.7s2SWAV3d2Zy5E0mVZtoypvEba8-1758450794-1.0.1.1-WzHichxOXDFavHgKTto8L.wfMRjmjKtffD9Zbis.nVkV2CPdZqswsMqZ96VHhGIeFpAQtf9nsnrDrwpNWE_FLceBoch0qGjVPy_mIgIJFwA; path=/; expires=Sun, 21-Sep-25 11:03:14 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" - }, - { - "_fromType": "array", - "name": "set-cookie", - "value": "_cfuvid=L2pYF1Umq8aMqZfNqGQ906M01t3Jw6GnQcAdwusWqfc-1758450794631-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" - }, - { - "name": "strict-transport-security", - "value": "max-age=31536000; includeSubDomains; preload" - }, - { - "name": "x-content-type-options", - "value": "nosniff" - }, - { - "name": "server", - "value": "cloudflare" - }, - { - "name": "cf-ray", - "value": "9828e739fceabff2-ATL" - }, - { - "name": "alt-svc", - "value": "h3=\":443\"; ma=86400" - } - ], - "headersSize": 926, - "httpVersion": "HTTP/1.1", - "redirectURL": "", - "status": 401, - "statusText": "Unauthorized" - }, - "startedDateTime": "2025-09-21T10:33:14.161Z", - "time": 238, - "timings": { - "blocked": -1, - "connect": -1, - "dns": -1, - "receive": 0, - "send": 0, - "ssl": -1, - "wait": 238 - } - } - ], - "pages": [], - "version": "1.2" - } -} diff --git a/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har b/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har deleted file mode 100644 index e0d7a0a1..00000000 --- a/packages/traceloop-sdk/recordings/Test-SDK-Decorators_847855269/should-create-spans-for-agents-using-withAgent-syntax_3895564654/recording.har +++ /dev/null @@ -1,207 +0,0 @@ -{ - "log": { - "_recordingName": "Test SDK Decorators/should create spans for agents using withAgent syntax", - "creator": { - "comment": "persister:fs", - "name": "Polly.JS", - "version": "6.0.6" - }, - "entries": [ - { - "_id": "80b48c70cae76a3c38b9af59d5273e33", - "_order": 0, - "cache": {}, - "request": { - "bodySize": 139, - "cookies": [], - "headers": [ - { - "_fromType": "array", - "name": "content-length", - "value": "139" - }, - { - "_fromType": "array", - "name": "accept", - "value": "application/json" - }, - { - "_fromType": "array", - "name": "content-type", - "value": "application/json" - }, - { - "_fromType": "array", - "name": "user-agent", - "value": "OpenAI/JS 4.38.3" - }, - { - "_fromType": "array", - "name": "x-stainless-lang", - "value": "js" - }, - { - "_fromType": "array", - "name": "x-stainless-package-version", - "value": "4.38.3" - }, - { - "_fromType": "array", - "name": "x-stainless-os", - "value": "MacOS" - }, - { - "_fromType": "array", - "name": "x-stainless-arch", - "value": "arm64" - }, - { - "_fromType": "array", - "name": "x-stainless-runtime", - "value": "node" - }, - { - "_fromType": "array", - "name": "x-stainless-runtime-version", - "value": "v22.19.0" - }, - { - "_fromType": "array", - "name": "accept-encoding", - "value": "gzip,deflate" - }, - { - "name": "host", - "value": "api.openai.com" - } - ], - "headersSize": 419, - "httpVersion": "HTTP/1.1", - "method": "POST", - "postData": { - "mimeType": "application/json", - "params": [], - "text": "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Tell me a joke about OpenTelemetry\"\n }\n ],\n \"model\": \"gpt-3.5-turbo\"\n}" - }, - "queryString": [], - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "bodySize": 496, - "content": { - "mimeType": "application/json; charset=utf-8", - "size": 496, - "text": "{\n \"error\": {\n \"message\": \"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\",\n \"type\": \"invalid_request_error\",\n \"param\": null,\n \"code\": null\n }\n}\n" - }, - "cookies": [ - { - "domain": ".api.openai.com", - "expires": "2025-09-21T11:03:14.000Z", - "httpOnly": true, - "name": "__cf_bm", - "path": "/", - "sameSite": "None", - "secure": true, - "value": "w2dCODYd6xOIHy75Rgx_pXtBUzGvUiXg3B1ESwhhpdk-1758450794-1.0.1.1-NZKEU3wV0RKKHJQgegLm8KhCW6ACC26PXJ.W2ffNVgJGqetM0pSTBNCyE7pwG1zybZvGFBhAv.JX2MlfSXS2vaghltcrRa5OQfysVxg6BhQ" - }, - { - "domain": ".api.openai.com", - "httpOnly": true, - "name": "_cfuvid", - "path": "/", - "sameSite": "None", - "secure": true, - "value": "7n0lyMKe63Fb94MgHuoxVNqgBIiM.ZgwlwoSm1M5ixc-1758450794388-0.0.1.1-604800000" - } - ], - "headers": [ - { - "name": "date", - "value": "Sun, 21 Sep 2025 10:33:14 GMT" - }, - { - "name": "content-type", - "value": "application/json; charset=utf-8" - }, - { - "name": "content-length", - "value": "496" - }, - { - "name": "connection", - "value": "keep-alive" - }, - { - "name": "vary", - "value": "Origin" - }, - { - "name": "x-request-id", - "value": "req_c57f38ce78cf416082d6eef4735c8384" - }, - { - "name": "x-envoy-upstream-service-time", - "value": "0" - }, - { - "name": "x-openai-proxy-wasm", - "value": "v0.1" - }, - { - "name": "cf-cache-status", - "value": "DYNAMIC" - }, - { - "_fromType": "array", - "name": "set-cookie", - "value": "__cf_bm=w2dCODYd6xOIHy75Rgx_pXtBUzGvUiXg3B1ESwhhpdk-1758450794-1.0.1.1-NZKEU3wV0RKKHJQgegLm8KhCW6ACC26PXJ.W2ffNVgJGqetM0pSTBNCyE7pwG1zybZvGFBhAv.JX2MlfSXS2vaghltcrRa5OQfysVxg6BhQ; path=/; expires=Sun, 21-Sep-25 11:03:14 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" - }, - { - "_fromType": "array", - "name": "set-cookie", - "value": "_cfuvid=7n0lyMKe63Fb94MgHuoxVNqgBIiM.ZgwlwoSm1M5ixc-1758450794388-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" - }, - { - "name": "strict-transport-security", - "value": "max-age=31536000; includeSubDomains; preload" - }, - { - "name": "x-content-type-options", - "value": "nosniff" - }, - { - "name": "server", - "value": "cloudflare" - }, - { - "name": "cf-ray", - "value": "9828e7386bddbff2-ATL" - }, - { - "name": "alt-svc", - "value": "h3=\":443\"; ma=86400" - } - ], - "headersSize": 926, - "httpVersion": "HTTP/1.1", - "redirectURL": "", - "status": 401, - "statusText": "Unauthorized" - }, - "startedDateTime": "2025-09-21T10:33:13.920Z", - "time": 235, - "timings": { - "blocked": -1, - "connect": -1, - "dns": -1, - "receive": 0, - "send": 0, - "ssl": -1, - "wait": 235 - } - } - ], - "pages": [], - "version": "1.2" - } -} From b22a9a6ed7a4f050cf18d5fc4fbc27eecf5e1de1 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:43:27 +0300 Subject: [PATCH 09/10] rename --- packages/ai-semantic-conventions/src/SemanticAttributes.ts | 4 +++- packages/traceloop-sdk/src/lib/tracing/decorators.ts | 2 +- packages/traceloop-sdk/src/lib/tracing/manual.ts | 4 ++-- packages/traceloop-sdk/src/lib/tracing/span-processor.ts | 2 +- packages/traceloop-sdk/test/agent_decorator.test.ts | 6 +++--- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/ai-semantic-conventions/src/SemanticAttributes.ts b/packages/ai-semantic-conventions/src/SemanticAttributes.ts index 6c047cb0..b103a21b 100644 --- a/packages/ai-semantic-conventions/src/SemanticAttributes.ts +++ b/packages/ai-semantic-conventions/src/SemanticAttributes.ts @@ -14,6 +14,7 @@ * limitations under the License. */ + export const SpanAttributes = { LLM_SYSTEM: "gen_ai.system", LLM_REQUEST_MODEL: "gen_ai.request.model", @@ -27,7 +28,8 @@ export const SpanAttributes = { LLM_RESPONSE_MODEL: "gen_ai.response.model", LLM_USAGE_PROMPT_TOKENS: "gen_ai.usage.prompt_tokens", LLM_USAGE_COMPLETION_TOKENS: "gen_ai.usage.completion_tokens", - LLM_AGENT_NAME: "gen_ai.agent.name", + + GEN_AI_AGENT_NAME: "gen_ai.agent.name", // LLM LLM_REQUEST_TYPE: "llm.request.type", diff --git a/packages/traceloop-sdk/src/lib/tracing/decorators.ts b/packages/traceloop-sdk/src/lib/tracing/decorators.ts index 50d34afe..42b4796a 100644 --- a/packages/traceloop-sdk/src/lib/tracing/decorators.ts +++ b/packages/traceloop-sdk/src/lib/tracing/decorators.ts @@ -102,7 +102,7 @@ function withEntity< // Set agent name on all spans when there's an active agent context const agentName = entityContext.getValue(AGENT_NAME_KEY); if (agentName) { - span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); + span.setAttribute(SpanAttributes.GEN_AI_AGENT_NAME, agentName as string); } if (version) { diff --git a/packages/traceloop-sdk/src/lib/tracing/manual.ts b/packages/traceloop-sdk/src/lib/tracing/manual.ts index 75569db2..8eebc0e7 100644 --- a/packages/traceloop-sdk/src/lib/tracing/manual.ts +++ b/packages/traceloop-sdk/src/lib/tracing/manual.ts @@ -148,7 +148,7 @@ export function withVectorDBCall< // Set agent name if there's an active agent context const agentName = entityContext.getValue(AGENT_NAME_KEY); if (agentName) { - span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); + span.setAttribute(SpanAttributes.GEN_AI_AGENT_NAME, agentName as string); } const res = fn.apply(thisArg, [{ span: new VectorSpan(span) }]); @@ -175,7 +175,7 @@ export function withLLMCall< // Set agent name if there's an active agent context const agentName = currentContext.getValue(AGENT_NAME_KEY); if (agentName) { - span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); + span.setAttribute(SpanAttributes.GEN_AI_AGENT_NAME, agentName as string); } trace.setSpan(currentContext, span); diff --git a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts index 6a3a8840..548e55db 100644 --- a/packages/traceloop-sdk/src/lib/tracing/span-processor.ts +++ b/packages/traceloop-sdk/src/lib/tracing/span-processor.ts @@ -151,7 +151,7 @@ const onSpanStart = (span: Span): void => { const agentName = context.active().getValue(AGENT_NAME_KEY); if (agentName) { - span.setAttribute(SpanAttributes.LLM_AGENT_NAME, agentName as string); + span.setAttribute(SpanAttributes.GEN_AI_AGENT_NAME, agentName as string); } const associationProperties = context diff --git a/packages/traceloop-sdk/test/agent_decorator.test.ts b/packages/traceloop-sdk/test/agent_decorator.test.ts index 11c526c7..3a806aff 100644 --- a/packages/traceloop-sdk/test/agent_decorator.test.ts +++ b/packages/traceloop-sdk/test/agent_decorator.test.ts @@ -136,7 +136,7 @@ describe("Test Agent Decorator", () => { "plan_trip", ); assert.strictEqual( - chatSpan.attributes[`${SpanAttributes.LLM_AGENT_NAME}`], + chatSpan.attributes[`${SpanAttributes.GEN_AI_AGENT_NAME}`], "plan_trip", ); assert.strictEqual( @@ -197,7 +197,7 @@ describe("Test Agent Decorator", () => { "travel_planner", ); assert.strictEqual( - chatSpan.attributes[`${SpanAttributes.LLM_AGENT_NAME}`], + chatSpan.attributes[`${SpanAttributes.GEN_AI_AGENT_NAME}`], "travel_planner", ); assert.strictEqual( @@ -259,7 +259,7 @@ describe("Test Agent Decorator", () => { "123", ); assert.strictEqual( - completionSpan.attributes[`${SpanAttributes.LLM_AGENT_NAME}`], + completionSpan.attributes[`${SpanAttributes.GEN_AI_AGENT_NAME}`], "assistant", ); assert.strictEqual( From 732c0b2b6bd35f16f48556ea73c740b23df50140 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:52:58 +0300 Subject: [PATCH 10/10] lint --- packages/ai-semantic-conventions/src/SemanticAttributes.ts | 3 +-- packages/traceloop-sdk/src/lib/tracing/decorators.ts | 5 ++++- packages/traceloop-sdk/src/lib/tracing/manual.ts | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/ai-semantic-conventions/src/SemanticAttributes.ts b/packages/ai-semantic-conventions/src/SemanticAttributes.ts index b103a21b..b73b961f 100644 --- a/packages/ai-semantic-conventions/src/SemanticAttributes.ts +++ b/packages/ai-semantic-conventions/src/SemanticAttributes.ts @@ -14,7 +14,6 @@ * limitations under the License. */ - export const SpanAttributes = { LLM_SYSTEM: "gen_ai.system", LLM_REQUEST_MODEL: "gen_ai.request.model", @@ -28,7 +27,7 @@ export const SpanAttributes = { LLM_RESPONSE_MODEL: "gen_ai.response.model", LLM_USAGE_PROMPT_TOKENS: "gen_ai.usage.prompt_tokens", LLM_USAGE_COMPLETION_TOKENS: "gen_ai.usage.completion_tokens", - + GEN_AI_AGENT_NAME: "gen_ai.agent.name", // LLM diff --git a/packages/traceloop-sdk/src/lib/tracing/decorators.ts b/packages/traceloop-sdk/src/lib/tracing/decorators.ts index 42b4796a..e1c480d5 100644 --- a/packages/traceloop-sdk/src/lib/tracing/decorators.ts +++ b/packages/traceloop-sdk/src/lib/tracing/decorators.ts @@ -102,7 +102,10 @@ function withEntity< // Set agent name on all spans when there's an active agent context const agentName = entityContext.getValue(AGENT_NAME_KEY); if (agentName) { - span.setAttribute(SpanAttributes.GEN_AI_AGENT_NAME, agentName as string); + span.setAttribute( + SpanAttributes.GEN_AI_AGENT_NAME, + agentName as string, + ); } if (version) { diff --git a/packages/traceloop-sdk/src/lib/tracing/manual.ts b/packages/traceloop-sdk/src/lib/tracing/manual.ts index 8eebc0e7..bd7fff93 100644 --- a/packages/traceloop-sdk/src/lib/tracing/manual.ts +++ b/packages/traceloop-sdk/src/lib/tracing/manual.ts @@ -148,7 +148,10 @@ export function withVectorDBCall< // Set agent name if there's an active agent context const agentName = entityContext.getValue(AGENT_NAME_KEY); if (agentName) { - span.setAttribute(SpanAttributes.GEN_AI_AGENT_NAME, agentName as string); + span.setAttribute( + SpanAttributes.GEN_AI_AGENT_NAME, + agentName as string, + ); } const res = fn.apply(thisArg, [{ span: new VectorSpan(span) }]);