@@ -37,11 +37,18 @@ pub fn normalize_ai(
3737 normalize_ai_type ( attributes) ;
3838 let span_origin = attributes
3939 . get_value ( ORIGIN )
40+ let platform = attributes
41+ . get_value ( PLATFORM )
4042 . and_then ( |v| v. as_str ( ) )
4143 . map ( String :: from) ;
4244 normalize_total_tokens ( attributes) ;
4345 normalize_tokens_per_second ( attributes, duration) ;
44- normalize_ai_costs ( attributes, costs, span_origin. as_deref ( ) ) ;
46+ normalize_ai_costs (
47+ attributes,
48+ costs,
49+ span_origin. as_deref ( ) ,
50+ platform. as_deref ( ) ,
51+ ) ;
4552}
4653
4754/// Returns whether the item is should have AI normalizations applied.
@@ -113,11 +120,60 @@ fn normalize_tokens_per_second(attributes: &mut Attributes, duration: Option<Dur
113120 }
114121}
115122
123+ /// Maps a span origin to a well-known AI integration name for metrics.
124+ ///
125+ /// Origins follow the pattern `auto.<integration>.<source>` or `auto.<category>.<protocol>.<source>`.
126+ /// This function extracts recognized AI integrations for cleaner metric tagging.
127+ fn map_origin_to_integration ( origin : Option < & str > ) -> & ' static str {
128+ match origin {
129+ Some ( o) if o. starts_with ( "auto.ai.openai" ) => "openai" ,
130+ Some ( o) if o. starts_with ( "auto.ai.openai_agents" ) => "openai_agents" ,
131+ Some ( o) if o. starts_with ( "auto.ai.anthropic" ) => "anthropic" ,
132+ Some ( o) if o. starts_with ( "auto.ai.cohere" ) => "cohere" ,
133+ Some ( o) if o. starts_with ( "auto.vercelai." ) => "vercelai" ,
134+ Some ( o) if o. starts_with ( "auto.ai.langchain" ) => "langchain" ,
135+ Some ( o) if o. starts_with ( "auto.ai.langgraph" ) => "langgraph" ,
136+ Some ( o) if o. starts_with ( "auto.ai.google_genai" ) => "google_genai" ,
137+ Some ( o) if o. starts_with ( "auto.ai.pydantic_ai" ) => "pydantic_ai" ,
138+ Some ( o) if o. starts_with ( "auto.ai.huggingface_hub" ) => "huggingface_hub" ,
139+ Some ( o) if o. starts_with ( "auto.ai.litellm" ) => "litellm" ,
140+ Some ( o) if o. starts_with ( "auto.ai.mcp" ) => "mcp" ,
141+ Some ( o) if o. starts_with ( "auto.ai.mcp_server" ) => "mcp_server" ,
142+ Some ( o) if o. starts_with ( "auto.ai.claude_agent_sdk" ) => "claude_agent_sdk" ,
143+ Some ( o) if o. starts_with ( "auto.ai." ) => "other" ,
144+ Some ( _) => "other" ,
145+ None => "unknown" ,
146+ }
147+ }
148+
149+ fn platform_tag ( platform : Option < & str > ) -> & ' static str {
150+ match platform {
151+ Some ( "cocoa" ) => "cocoa" ,
152+ Some ( "csharp" ) => "csharp" ,
153+ Some ( "edge" ) => "edge" ,
154+ Some ( "go" ) => "go" ,
155+ Some ( "java" ) => "java" ,
156+ Some ( "javascript" ) => "javascript" ,
157+ Some ( "julia" ) => "julia" ,
158+ Some ( "native" ) => "native" ,
159+ Some ( "node" ) => "node" ,
160+ Some ( "objc" ) => "objc" ,
161+ Some ( "perl" ) => "perl" ,
162+ Some ( "php" ) => "php" ,
163+ Some ( "python" ) => "python" ,
164+ Some ( "ruby" ) => "ruby" ,
165+ Some ( "swift" ) => "swift" ,
166+ Some ( _) => "other" ,
167+ None => "unknown" ,
168+ }
169+ }
170+
116171/// Calculates model costs and serializes them into attributes.
117172fn normalize_ai_costs (
118173 attributes : & mut Attributes ,
119174 model_costs : Option < & ModelCosts > ,
120175 origin : Option < & str > ,
176+ platform : Option < & str > ,
121177) {
122178 if attributes. contains_key ( GEN_AI_COST_TOTAL_TOKENS ) {
123179 return ;
@@ -146,11 +202,15 @@ fn normalize_ai_costs(
146202 output_reasoning_tokens : get_tokens ( GEN_AI_USAGE_OUTPUT_REASONING_TOKENS ) ,
147203 } ;
148204
205+ let integration = map_origin_to_integration ( origin) ;
206+ let platform = platform_tag ( platform) ;
207+
149208 let Some ( costs) = ai:: calculate_costs ( model_cost, tokens) else {
150209 relay_statsd:: metric!(
151210 counter( Counters :: GenAiCostCalculationResult ) += 1 ,
152211 result = "calculation_none" ,
153- origin = origin. unwrap_or( "unknown" ) ,
212+ integration = integration,
213+ platform = platform,
154214 ) ;
155215
156216 return ;
@@ -166,7 +226,8 @@ fn normalize_ai_costs(
166226 relay_statsd:: metric!(
167227 counter( Counters :: GenAiCostCalculationResult ) += 1 ,
168228 result = metric_label,
169- origin = origin. unwrap_or( "unknown" ) ,
229+ integration = integration,
230+ platform = platform,
170231 ) ;
171232
172233 attributes. insert ( GEN_AI_COST_INPUT_TOKENS , costs. input ) ;
0 commit comments