-
Notifications
You must be signed in to change notification settings - Fork 30
feat(ai-assistants): align telemetry with OTel GenAI semantic conventions #1625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,12 @@ | ||
| import base64ToBlob from "../utils/blobConversion.ts"; | ||
| import { AssistantIds } from "../types.ts"; | ||
| import { AppContext } from "../mod.ts"; | ||
| import { logger, meter, ValueType } from "@deco/deco/o11y"; | ||
| const stats = { | ||
| awsUploadImageError: meter.createCounter("assistant_aws_upload_error", { | ||
| unit: "1", | ||
| valueType: ValueType.INT, | ||
| }), | ||
| }; | ||
| import { logger } from "@deco/deco/o11y"; | ||
| import { | ||
| ATTR_ASSISTANT_ID, | ||
| ATTR_ASSISTANT_OPERATION, | ||
| stats, | ||
| } from "../observability.ts"; | ||
| export interface AWSUploadImageProps { | ||
| file: string | ArrayBuffer | null; | ||
| assistantIds?: AssistantIds; | ||
|
|
@@ -49,8 +48,9 @@ export default async function awsUploadImage( | |
| const uploadURL = await getSignedUrl(blobData.type, ctx); | ||
| const uploadResponse = await uploadFileToS3(uploadURL, blobData); | ||
| if (!uploadResponse.ok) { | ||
| stats.awsUploadImageError.add(1, { | ||
| assistantId, | ||
| stats.errors.add(1, { | ||
| [ATTR_ASSISTANT_OPERATION]: "aws_upload", | ||
| [ATTR_ASSISTANT_ID]: assistantId, | ||
| }); | ||
|
Comment on lines
+51
to
54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
🤖 Prompt for AI Agents |
||
| throw new Error(`Failed to upload file: ${uploadResponse.statusText}`); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,12 @@ | ||
| import base64ToBlob from "../utils/blobConversion.ts"; | ||
| import { AssistantIds } from "../types.ts"; | ||
| import { AppContext } from "../mod.ts"; | ||
| import { logger, meter, ValueType } from "@deco/deco/o11y"; | ||
| const stats = { | ||
| audioSize: meter.createHistogram("assistant_transcribe_audio_size", { | ||
| description: | ||
| "Audio size used in Sales Assistant Transcribe Image Input - OpenAI", | ||
| unit: "s", | ||
| valueType: ValueType.DOUBLE, | ||
| }), | ||
| transcribeAudioError: meter.createCounter( | ||
| "assistant_transcribe_audio_error", | ||
| { | ||
| unit: "1", | ||
| valueType: ValueType.INT, | ||
| }, | ||
| ), | ||
| }; | ||
| import { logger } from "@deco/deco/o11y"; | ||
| import { | ||
| ATTR_ASSISTANT_ID, | ||
| ATTR_ASSISTANT_OPERATION, | ||
| stats, | ||
| } from "../observability.ts"; | ||
| export interface TranscribeAudioProps { | ||
| file: string | ArrayBuffer | null; | ||
| assistantIds?: AssistantIds; | ||
|
|
@@ -31,8 +21,9 @@ export default async function transcribeAudio( | |
| const assistantId = transcribeAudioProps.assistantIds?.assistantId; | ||
| const threadId = transcribeAudioProps.assistantIds?.threadId; | ||
| if (!transcribeAudioProps.file) { | ||
| stats.transcribeAudioError.add(1, { | ||
| assistantId, | ||
| stats.errors.add(1, { | ||
| [ATTR_ASSISTANT_OPERATION]: "transcribe_audio", | ||
| [ATTR_ASSISTANT_ID]: assistantId, | ||
| }); | ||
| throw new Error("Audio file is empty"); | ||
| } | ||
|
|
@@ -42,8 +33,8 @@ export default async function transcribeAudio( | |
| transcribeAudioProps.assistantIds, | ||
| ); | ||
| const file = new File([blobData], "input.wav", { type: "audio/wav" }); | ||
| stats.audioSize.record(transcribeAudioProps.audioDuration, { | ||
| assistant_id: assistantId, | ||
| stats.audioDuration.record(transcribeAudioProps.audioDuration, { | ||
| [ATTR_ASSISTANT_ID]: assistantId, | ||
| }); | ||
|
Comment on lines
+36
to
38
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find callers that set audioDuration on transcribeAudio props
rg -nP --type=ts -C3 '\baudioDuration\b'Repository: deco-cx/apps Length of output: 150 🏁 Script executed: #!/bin/bash
# Search for audioDuration across all files to find usage and declaration
rg -n "audioDuration"Repository: deco-cx/apps Length of output: 150 🏁 Script executed: #!/bin/bash
# Find the function definition in transcribeAudio.ts to check the metric unit
cat -n ai-assistants/actions/transcribeAudio.tsRepository: deco-cx/apps Length of output: 2046 🏁 Script executed: #!/bin/bash
# Find where transcribeAudio is called to inspect the passed argument
rg -n "transcribeAudio\("Repository: deco-cx/apps Length of output: 150 🏁 Script executed: #!/bin/bash
# Find usages of transcribeAudio function call across all files
rg "transcribeAudio"Repository: deco-cx/apps Length of output: 150 🏁 Script executed: #!/bin/bash
# Find imports of the transcribeAudio module
rg -n "from ['\"].*transcribeAudio['\"]"Repository: deco-cx/apps Length of output: 150 🏁 Script executed: #!/bin/bash
# List directory structure to ensure correct path and find potential callers
find . -name "*transcribe*" -o -path "*/ai-assistants/*" -type f | head -n 20Repository: deco-cx/apps Length of output: 747 🏁 Script executed: #!/bin/bash
# Use grep instead of rg to find the text "transcribeAudio"
grep -rn "transcribeAudio" --include="*.ts" --include="*.tsx" .Repository: deco-cx/apps Length of output: 1903 🏁 Script executed: #!/bin/bash
# Check the callers to verify how audioDuration is passed
cat ./ai-assistants/hooks/useFileUpload.tsRepository: deco-cx/apps Length of output: 451 🏁 Script executed: #!/bin/bash
# Inspect the observability definition to confirm the unit
cat -n ./ai-assistants/observability.tsRepository: deco-cx/apps Length of output: 2687 🏁 Script executed: #!/bin/bash
# Search for where audioDuration is calculated or assigned before being passed
grep -rn "audioDuration:" --include="*.ts" --include="*.tsx" . | head -n 20Repository: deco-cx/apps Length of output: 297 🏁 Script executed: #!/bin/bash
# Double check if there is any client-side calculation for audio duration that might use milliseconds
grep -rn "audio.*duration" --include="*.ts" --include="*.tsx" . | grep -v "transcribeAudio.ts" | grep -v "observability.ts"Repository: deco-cx/apps Length of output: 298 🏁 Script executed: #!/bin/bash
# Check the type definition to see if there's any JSDoc or comment about units
grep -A5 -B5 "audioDuration" ./ai-assistants/actions/transcribeAudio.tsRepository: deco-cx/apps Length of output: 848 Ensure The 🤖 Prompt for AI Agents |
||
| const response = await ctx.openAI.audio.transcriptions.create({ | ||
| model: "whisper-1", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // Shared OTel instruments for the AI assistants app. Standard GenAI telemetry | ||
| // uses the official @opentelemetry/semantic-conventions (gen_ai.*) constants; | ||
| // deco-proprietary dimensions use the deco.assistant.* namespace. The meter is | ||
| // reused from the deco framework. | ||
| import { meter, ValueType } from "@deco/deco/o11y"; | ||
| import { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Uses deprecated Prompt for AI agents |
||
| ATTR_GEN_AI_SYSTEM, | ||
| ATTR_GEN_AI_TOKEN_TYPE, | ||
| METRIC_GEN_AI_CLIENT_OPERATION_DURATION, | ||
| METRIC_GEN_AI_CLIENT_TOKEN_USAGE, | ||
| } from "npm:@opentelemetry/semantic-conventions@1.37.0/incubating"; | ||
|
|
||
| // semconv attribute keys + values | ||
| export const GEN_AI_SYSTEM = ATTR_GEN_AI_SYSTEM; | ||
| export const GEN_AI_SYSTEM_OPENAI = "openai"; | ||
| export const GEN_AI_TOKEN_TYPE = ATTR_GEN_AI_TOKEN_TYPE; | ||
| export const GEN_AI_TOKEN_TYPE_INPUT = "input"; | ||
| export const GEN_AI_TOKEN_TYPE_OUTPUT = "output"; | ||
|
|
||
| // deco-proprietary attributes (no semconv equivalent) | ||
| export const ATTR_ASSISTANT_ID = "assistant_id"; | ||
| export const ATTR_ASSISTANT_PHASE = "deco.assistant.phase"; | ||
| export const ATTR_ASSISTANT_OPERATION = "deco.assistant.operation"; | ||
|
|
||
| export const stats = { | ||
| // gen_ai.client.operation.duration — seconds (semconv) | ||
| operationDuration: meter.createHistogram( | ||
| METRIC_GEN_AI_CLIENT_OPERATION_DURATION, | ||
| { | ||
| description: "GenAI assistant operation duration.", | ||
| unit: "s", | ||
| valueType: ValueType.DOUBLE, | ||
| }, | ||
| ), | ||
| // gen_ai.client.token.usage — split by gen_ai.token.type (input/output) | ||
| tokenUsage: meter.createHistogram(METRIC_GEN_AI_CLIENT_TOKEN_USAGE, { | ||
| description: "Number of tokens used in GenAI assistant requests.", | ||
| unit: "{token}", | ||
| valueType: ValueType.INT, | ||
| }), | ||
| // deco-proprietary: transcribed audio duration (seconds) | ||
| audioDuration: meter.createHistogram( | ||
| "deco.assistant.transcribe.audio_duration", | ||
| { | ||
| description: "Duration of audio transcribed by the assistant.", | ||
| unit: "s", | ||
| valueType: ValueType.DOUBLE, | ||
| }, | ||
| ), | ||
| // deco-proprietary: assistant operation errors, dimensioned by operation | ||
| errors: meter.createCounter("deco.assistant.errors", { | ||
| description: "Assistant operation errors.", | ||
| unit: "1", | ||
| valueType: ValueType.INT, | ||
| }), | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2:
assistantIdcan beundefined(from optionalassistantIds?.assistantId), and the OTel SDK will silently drop the attribute when the value isundefined. This leaves error metrics undimensioned byassistant_id. Consider defaulting to a sentinel like"unknown"to ensure the dimension is always present:The same pattern applies in
describeImage.tsandtranscribeAudio.ts.Prompt for AI agents
The same pattern applies in
@@ -49,8 +48,9 @@ export default async function awsUploadImage( - assistantId, + stats.errors.add(1, { + [ATTR_ASSISTANT_OPERATION]: "aws_upload", + [ATTR_ASSISTANT_ID]: assistantId, }); throw new Error(`Failed to upload file: ${uploadResponse.statusText}`); ```describeImage.tsandtranscribeAudio.ts.