Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions relay-event-normalization/src/eap/ai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,21 @@ fn normalize_ai_costs(attributes: &mut Attributes, model_metadata: Option<&Model

// Overwrite all values, the attributes should reflect the values we used to calculate the total.
attributes.insert(GEN_AI__COST__INPUT_TOKENS, costs.input);
attributes.insert(
GEN_AI__COST__CACHE_READ__INPUT_TOKENS,
costs.cache_read_input,
);
attributes.insert(
GEN_AI__COST__CACHE_CREATION__INPUT_TOKENS,
costs.cache_creation_input,
);

attributes.insert(GEN_AI__COST__OUTPUT_TOKENS, costs.output);
attributes.insert(
GEN_AI__COST__REASONING__OUTPUT_TOKENS,
costs.reasoning_output,
);
Comment thread
vgrozdanic marked this conversation as resolved.

attributes.insert(GEN_AI__COST__TOTAL_TOKENS, costs.total());
}

Expand Down Expand Up @@ -305,6 +319,14 @@ mod tests {

assert_annotated_snapshot!(attributes, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.cache_read.input_tokens": {
"type": "double",
"value": 20.0
},
"gen_ai.cost.input_tokens": {
"type": "double",
"value": 25.0
Expand All @@ -313,6 +335,10 @@ mod tests {
"type": "double",
"value": 50.0
},
"gen_ai.cost.reasoning.output_tokens": {
"type": "double",
"value": 30.0
},
"gen_ai.cost.total_tokens": {
"type": "double",
"value": 75.0
Expand Down Expand Up @@ -374,6 +400,14 @@ mod tests {

assert_annotated_snapshot!(attributes, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.cache_read.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.input_tokens": {
"type": "double",
"value": 90.0
Expand All @@ -382,6 +416,10 @@ mod tests {
"type": "double",
"value": 100.0
},
"gen_ai.cost.reasoning.output_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.total_tokens": {
"type": "double",
"value": 190.0
Expand Down Expand Up @@ -483,6 +521,14 @@ mod tests {

assert_annotated_snapshot!(attributes, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.cache_read.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.input_tokens": {
"type": "double",
"value": 90.0
Expand All @@ -491,6 +537,10 @@ mod tests {
"type": "double",
"value": 100.0
},
"gen_ai.cost.reasoning.output_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.total_tokens": {
"type": "double",
"value": 190.0
Expand Down Expand Up @@ -552,6 +602,14 @@ mod tests {

assert_annotated_snapshot!(attributes, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.cache_read.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.input_tokens": {
"type": "double",
"value": 90.0
Expand All @@ -560,6 +618,10 @@ mod tests {
"type": "double",
"value": 100.0
},
"gen_ai.cost.reasoning.output_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.total_tokens": {
"type": "double",
"value": 190.0
Expand Down Expand Up @@ -683,6 +745,14 @@ mod tests {
"type": "integer",
"value": 100000
},
"gen_ai.cost.cache_creation.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.cache_read.input_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.input_tokens": {
"type": "double",
"value": 300.0
Expand All @@ -691,6 +761,10 @@ mod tests {
"type": "double",
"value": 240.0
},
"gen_ai.cost.reasoning.output_tokens": {
"type": "double",
"value": 0.0
},
"gen_ai.cost.total_tokens": {
"type": "double",
"value": 540.0
Expand Down
21 changes: 21 additions & 0 deletions relay-event-normalization/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2566,8 +2566,11 @@ mod tests {

assert_annotated_snapshot!(span1, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": 0.0,
"gen_ai.cost.cache_read.input_tokens": 0.0,
"gen_ai.cost.input_tokens": 10.0,
"gen_ai.cost.output_tokens": 40.0,
"gen_ai.cost.reasoning.output_tokens": 0.0,
"gen_ai.cost.total_tokens": 50.0,
"gen_ai.operation.type": "ai_client",
"gen_ai.request.model": "claude-2.1",
Expand All @@ -2580,8 +2583,11 @@ mod tests {
"#);
assert_annotated_snapshot!(span2, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": 0.0,
"gen_ai.cost.cache_read.input_tokens": 0.0,
"gen_ai.cost.input_tokens": 20.0,
"gen_ai.cost.output_tokens": 60.0,
"gen_ai.cost.reasoning.output_tokens": 0.0,
"gen_ai.cost.total_tokens": 80.0,
"gen_ai.operation.type": "ai_client",
"gen_ai.request.model": "gpt4-21-04",
Expand Down Expand Up @@ -2691,8 +2697,11 @@ mod tests {

assert_annotated_snapshot!(span1, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": 0.0,
"gen_ai.cost.cache_read.input_tokens": 20.0,
"gen_ai.cost.input_tokens": 25.0,
"gen_ai.cost.output_tokens": 50.0,
"gen_ai.cost.reasoning.output_tokens": 30.0,
"gen_ai.cost.total_tokens": 75.0,
"gen_ai.operation.type": "ai_client",
"gen_ai.request.model": "claude-2.1",
Expand All @@ -2707,8 +2716,11 @@ mod tests {
"#);
assert_annotated_snapshot!(span2, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": 0.0,
"gen_ai.cost.cache_read.input_tokens": 0.0,
"gen_ai.cost.input_tokens": 90.0,
"gen_ai.cost.output_tokens": 100.0,
"gen_ai.cost.reasoning.output_tokens": 0.0,
"gen_ai.cost.total_tokens": 190.0,
"gen_ai.operation.type": "ai_client",
"gen_ai.request.model": "gpt4-21-04",
Expand All @@ -2721,8 +2733,11 @@ mod tests {
"#);
assert_annotated_snapshot!(span3, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": 0.0,
"gen_ai.cost.cache_read.input_tokens": 0.0,
"gen_ai.cost.input_tokens": 90.0,
"gen_ai.cost.output_tokens": 100.0,
"gen_ai.cost.reasoning.output_tokens": 0.0,
"gen_ai.cost.total_tokens": 190.0,
"gen_ai.operation.type": "ai_client",
"gen_ai.response.model": "gpt4-21-04",
Expand Down Expand Up @@ -2874,8 +2889,11 @@ mod tests {

assert_annotated_snapshot!(span1, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": 0.0,
"gen_ai.cost.cache_read.input_tokens": 20.0,
"gen_ai.cost.input_tokens": 25.0,
"gen_ai.cost.output_tokens": 40.0,
"gen_ai.cost.reasoning.output_tokens": 20.0,
"gen_ai.cost.total_tokens": 65.0,
"gen_ai.operation.type": "ai_client",
"gen_ai.request.model": "claude-2.1",
Expand All @@ -2890,8 +2908,11 @@ mod tests {
"#);
assert_annotated_snapshot!(span2, @r#"
{
"gen_ai.cost.cache_creation.input_tokens": 0.0,
"gen_ai.cost.cache_read.input_tokens": 0.0,
"gen_ai.cost.input_tokens": 90.0,
"gen_ai.cost.output_tokens": 100.0,
"gen_ai.cost.reasoning.output_tokens": 0.0,
"gen_ai.cost.total_tokens": 190.0,
"gen_ai.operation.type": "ai_client",
"gen_ai.request.model": "gpt4-21-04",
Expand Down
63 changes: 53 additions & 10 deletions relay-event-normalization/src/normalize/span/ai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,16 @@ impl UsedTokens {
/// Calculated model call costs.
#[derive(Debug, Copy, Clone)]
pub struct CalculatedCost {
/// The cost of input tokens used.
/// The total cost of all input tokens (raw + cached + cache_write).
pub input: f64,
/// The cost of output tokens used.
/// The total cost of all output tokens (raw + reasoning).
pub output: f64,
/// The cost of cached input tokens only (subset of `input`).
pub cache_read_input: f64,
/// The cost of cache-write input tokens only (subset of `input`).
pub cache_creation_input: f64,
/// The cost of reasoning output tokens only (subset of `output`).
pub reasoning_output: f64,
}

impl CalculatedCost {
Expand Down Expand Up @@ -105,19 +111,21 @@ pub fn calculate_costs(
return None;
}

let cache_read_input = tokens.input_cached_tokens * model_cost.input_cached_per_token;
let cache_creation_input =
tokens.input_cache_write_tokens * model_cost.input_cache_write_per_token;
let input = (tokens.raw_input_tokens() * model_cost.input_per_token)
+ (tokens.input_cached_tokens * model_cost.input_cached_per_token)
+ (tokens.input_cache_write_tokens * model_cost.input_cache_write_per_token);
+ cache_read_input
+ cache_creation_input;

// For now most of the models do not differentiate between reasoning and output token cost,
// it costs the same.
let reasoning_cost = match model_cost.output_reasoning_per_token {
reasoning_cost if reasoning_cost > 0.0 => reasoning_cost,
let reasoning_per_token = match model_cost.output_reasoning_per_token {
r if r > 0.0 => r,
_ => model_cost.output_per_token,
};

let output = (tokens.raw_output_tokens() * model_cost.output_per_token)
+ (tokens.output_reasoning_tokens * reasoning_cost);
let reasoning_output = tokens.output_reasoning_tokens * reasoning_per_token;
let output = (tokens.raw_output_tokens() * model_cost.output_per_token) + reasoning_output;

let metric_label = match (input, output) {
(x, y) if x < 0.0 || y < 0.0 => "calculation_negative",
Expand All @@ -132,7 +140,13 @@ pub fn calculate_costs(
platform = platform,
);

Some(CalculatedCost { input, output })
Some(CalculatedCost {
input,
output,
cache_read_input,
cache_creation_input,
reasoning_output,
})
}

/// Default AI operation stored in [`GEN_AI__OPERATION__TYPE`]
Expand Down Expand Up @@ -221,10 +235,24 @@ fn extract_ai_model_cost_data(
.entry(GEN_AI__COST__INPUT_TOKENS.to_owned())
.or_default()
.set_value(Value::F64(costs.input).into());
data.other
.entry(GEN_AI__COST__CACHE_READ__INPUT_TOKENS.to_owned())
.or_default()
.set_value(Value::F64(costs.cache_read_input).into());
data.other
.entry(GEN_AI__COST__CACHE_CREATION__INPUT_TOKENS.to_owned())
.or_default()
.set_value(Value::F64(costs.cache_creation_input).into());

data.other
.entry(GEN_AI__COST__OUTPUT_TOKENS.to_owned())
.or_default()
.set_value(Value::F64(costs.output).into());

data.other
.entry(GEN_AI__COST__REASONING__OUTPUT_TOKENS.to_owned())
.or_default()
.set_value(Value::F64(costs.reasoning_output).into());
}

/// Maps AI-related measurements (legacy) to span data.
Expand Down Expand Up @@ -556,6 +584,9 @@ mod tests {
CalculatedCost {
input: 5.5,
output: 39.0,
cache_read_input: 2.5,
cache_creation_input: 0.0,
reasoning_output: 27.0,
}
");
}
Expand Down Expand Up @@ -587,6 +618,9 @@ mod tests {
CalculatedCost {
input: 5.5,
output: 30.0,
cache_read_input: 2.5,
cache_creation_input: 0.0,
reasoning_output: 18.0,
}
");
}
Expand Down Expand Up @@ -620,6 +654,9 @@ mod tests {
CalculatedCost {
input: -9.0,
output: -7.0,
cache_read_input: 11.0,
cache_creation_input: 0.0,
reasoning_output: 9.0,
}
");
}
Expand Down Expand Up @@ -653,6 +690,9 @@ mod tests {
CalculatedCost {
input: 82.5,
output: 110.0,
cache_read_input: 10.0,
cache_creation_input: 22.5,
reasoning_output: 30.0,
}
");
}
Expand Down Expand Up @@ -701,6 +741,9 @@ mod tests {
CalculatedCost {
input: 90.0,
output: 100.0,
cache_read_input: 10.0,
cache_creation_input: 0.0,
reasoning_output: 0.0,
}
");
}
Expand Down
Loading
Loading