diff --git a/packages/@n8n/agents/src/runtime/__tests__/model-request-tracing-wiring.test.ts b/packages/@n8n/agents/src/runtime/__tests__/model-request-tracing-wiring.test.ts index 393638d2c0d..4deb1e44420 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/model-request-tracing-wiring.test.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/model-request-tracing-wiring.test.ts @@ -103,6 +103,7 @@ describe('model request tracing wiring', () => { expect(requestSpans(spans)).toEqual([ expect.objectContaining({ attributes: expect.objectContaining({ + 'gen_ai.system': 'anthropic', 'gen_ai.provider.name': 'anthropic', 'gen_ai.request.model': 'claude-sonnet-4-5', }), diff --git a/packages/@n8n/agents/src/runtime/telemetry/__tests__/model-request-tracing.test.ts b/packages/@n8n/agents/src/runtime/telemetry/__tests__/model-request-tracing.test.ts index c1ba3ee7f26..87dbb1c250e 100644 --- a/packages/@n8n/agents/src/runtime/telemetry/__tests__/model-request-tracing.test.ts +++ b/packages/@n8n/agents/src/runtime/telemetry/__tests__/model-request-tracing.test.ts @@ -49,7 +49,7 @@ it('records each HTTP attempt, with no payload or duplicate usage', async () => .fn() .mockResolvedValueOnce(new Response('private error', { status: 500 })) .mockResolvedValueOnce(new Response('private output')); - const fetcher = traceModelRequests(delegate, telemetry, 'google', 'test-model'); + const fetcher = traceModelRequests(delegate, telemetry, 'custom', 'test-model'); const init = { method: 'POST', headers: { Authorization: 'private key' }, @@ -64,7 +64,7 @@ it('records each HTTP attempt, with no payload or duplicate usage', async () => 'langsmith.traceable': 'true', 'langsmith.metadata.thread_id': 'thread', 'langsmith.metadata.evalRole': 'helper', - 'gen_ai.provider.name': 'gcp.gemini', + 'gen_ai.system': 'custom', 'gen_ai.request.model': 'test-model', 'http.response.status_code': 500, }); @@ -95,6 +95,7 @@ it('keeps generic OpenTelemetry spans free of LangSmith attributes', async () => { name: 'model.provider.request', attributes: { + 'gen_ai.system': 'custom', 'gen_ai.request.model': 'model', 'http.response.status_code': 200, }, @@ -104,6 +105,16 @@ it('keeps generic OpenTelemetry spans free of LangSmith attributes', async () => ]); }); +it.each([ + ['google', 'gcp.gemini'], + ['custom', undefined], +] as const)('sends the spec provider name for the %s provider', async (provider, providerName) => { + const { spans, telemetry } = fixture(); + const delegate = vi.fn().mockResolvedValue(new Response('ok')); + await traceModelRequests(delegate, telemetry, provider, 'model')('url'); + expect(spans[0].attributes['gen_ai.provider.name']).toBe(providerName); +}); + it('records LangSmith request spans without optional metadata', async () => { const { spans, telemetry } = fixture(); const delegate = vi.fn().mockResolvedValue(new Response('ok')); @@ -116,6 +127,7 @@ it('records LangSmith request spans without optional metadata', async () => { expect(spans[0]).toEqual({ name: 'model.provider.request', attributes: { + 'gen_ai.system': 'custom', 'gen_ai.request.model': 'model', 'langsmith.traceable': 'true', 'langsmith.span.kind': 'chain', diff --git a/packages/@n8n/agents/src/runtime/telemetry/model-request-tracing.ts b/packages/@n8n/agents/src/runtime/telemetry/model-request-tracing.ts index 07cd1837c13..62ef9b78694 100644 --- a/packages/@n8n/agents/src/runtime/telemetry/model-request-tracing.ts +++ b/packages/@n8n/agents/src/runtime/telemetry/model-request-tracing.ts @@ -49,6 +49,7 @@ export function traceModelRequests( let span: Span | undefined; try { const attributes: Record = { + 'gen_ai.system': provider, ...(providerName ? { [GEN_AI_ATTR.PROVIDER_NAME]: providerName } : {}), [GEN_AI_ATTR.REQUEST_MODEL]: model, };