fix(core): Keep gen_ai.system next to gen_ai.provider.name for LangSmith

This commit is contained in:
Theo Nastos
2026-10-23 19:21:40 +03:00
parent f916debd92
commit 7b491a0163
3 changed files with 16 additions and 2 deletions
@@ -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',
}),
@@ -49,7 +49,7 @@ it('records each HTTP attempt, with no payload or duplicate usage', async () =>
.fn<typeof fetch>()
.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<typeof fetch>().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<typeof fetch>().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',
@@ -49,6 +49,7 @@ export function traceModelRequests(
let span: Span | undefined;
try {
const attributes: Record<string, AttributeValue> = {
'gen_ai.system': provider,
...(providerName ? { [GEN_AI_ATTR.PROVIDER_NAME]: providerName } : {}),
[GEN_AI_ATTR.REQUEST_MODEL]: model,
};