From 2e1d052970aa01fa8966a7cbe977f5fd3fb39598 Mon Sep 17 00:00:00 2001 From: "datadog-official[bot]" <214633350+datadog-official[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:43:01 +0000 Subject: [PATCH] Add Sentry gen_ai span for OpenAI calls Co-authored-by: jboolean <406931+jboolean@users.noreply.github.com> --- backend/src/business/ai/AiService.ts | 36 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/backend/src/business/ai/AiService.ts b/backend/src/business/ai/AiService.ts index d2ee44c83..904c3cc1f 100644 --- a/backend/src/business/ai/AiService.ts +++ b/backend/src/business/ai/AiService.ts @@ -1,4 +1,5 @@ import OpenAI from 'openai'; +import * as Sentry from '@sentry/node'; import createStoryTitlePrompt from './createStoryTitlePrompt'; const openai = new OpenAI({ @@ -7,9 +8,36 @@ const openai = new OpenAI({ }); export async function suggestStoryTitle(storyContent: string): Promise { - const response = await openai.chat.completions.create( - createStoryTitlePrompt(storyContent) - ); + const prompt = createStoryTitlePrompt(storyContent); + const model = typeof prompt.model === 'string' ? prompt.model : 'unknown'; + const aiSpan = Sentry.getCurrentHub() + .getScope() + ?.getTransaction() + ?.startChild({ + op: 'gen_ai.request', + description: `OpenAI chat completion (${model})`, + }); + aiSpan?.setData('gen_ai.request.model', model); - return response.choices[0].message.content?.trim() ?? ''; + try { + const response = await openai.chat.completions.create(prompt); + + if (response.usage) { + aiSpan?.setData( + 'gen_ai.usage.input_tokens', + response.usage.prompt_tokens + ); + aiSpan?.setData( + 'gen_ai.usage.output_tokens', + response.usage.completion_tokens + ); + } + + return response.choices[0].message.content?.trim() ?? ''; + } catch (error) { + aiSpan?.setStatus('internal_error'); + throw error; + } finally { + aiSpan?.finish(); + } }