Skip to content

Commit c065252

Browse files
committed
chore(ai): Clean up model costs v1
1 parent e17f09a commit c065252

File tree

2 files changed

+12
-158
lines changed

2 files changed

+12
-158
lines changed

relay-event-normalization/src/event.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2293,7 +2293,6 @@ mod tests {
22932293
&NormalizationConfig {
22942294
ai_model_costs: Some(&ModelCosts {
22952295
version: 2,
2296-
costs: vec![],
22972296
models: HashMap::from([
22982297
(
22992298
"claude-2.1".to_owned(),
@@ -2399,7 +2398,6 @@ mod tests {
23992398
&NormalizationConfig {
24002399
ai_model_costs: Some(&ModelCosts {
24012400
version: 2,
2402-
costs: vec![],
24032401
models: HashMap::from([
24042402
(
24052403
"claude-2.1".to_owned(),
@@ -2496,7 +2494,6 @@ mod tests {
24962494
&NormalizationConfig {
24972495
ai_model_costs: Some(&ModelCosts {
24982496
version: 2,
2499-
costs: vec![],
25002497
models: HashMap::from([(
25012498
"claude-2.1".to_owned(),
25022499
ModelCostV2 {
@@ -2580,7 +2577,6 @@ mod tests {
25802577
&NormalizationConfig {
25812578
ai_model_costs: Some(&ModelCosts {
25822579
version: 2,
2583-
costs: vec![],
25842580
models: HashMap::from([
25852581
(
25862582
"claude-2.1".to_owned(),
@@ -2660,7 +2656,6 @@ mod tests {
26602656
&NormalizationConfig {
26612657
ai_model_costs: Some(&ModelCosts {
26622658
version: 2,
2663-
costs: vec![],
26642659
models: HashMap::new(),
26652660
}),
26662661
..NormalizationConfig::default()
@@ -2704,7 +2699,6 @@ mod tests {
27042699
&NormalizationConfig {
27052700
ai_model_costs: Some(&ModelCosts {
27062701
version: 2,
2707-
costs: vec![],
27082702
models: HashMap::new(),
27092703
}),
27102704
..NormalizationConfig::default()

relay-event-normalization/src/normalize/mod.rs

Lines changed: 12 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::collections::HashMap;
22
use std::hash::Hash;
33

44
use relay_base_schema::metrics::MetricUnit;
5-
use relay_common::glob2::LazyGlob;
65
use relay_event_schema::protocol::{Event, VALID_PLATFORMS};
76
use relay_protocol::{FiniteF64, RuleCondition};
87
use serde::{Deserialize, Serialize};
@@ -219,25 +218,10 @@ pub struct PerformanceScoreConfig {
219218

220219
/// A mapping of AI model types (like GPT-4) to their respective costs.
221220
///
222-
/// This struct supports multiple versions with different cost structures:
223-
/// - Version 1: Array-based costs with glob pattern matching for model IDs (uses `costs` field)
224-
/// - Version 2: Dictionary-based costs with exact model ID keys and granular token pricing (uses `models` field)
221+
/// This struct uses a dictionary-based cost structure with exact model ID keys and granular
222+
/// token pricing.
225223
///
226-
/// Example V1 JSON:
227-
/// ```json
228-
/// {
229-
/// "version": 1,
230-
/// "costs": [
231-
/// {
232-
/// "modelId": "gpt-4*",
233-
/// "forCompletion": false,
234-
/// "costPer1kTokens": 0.03
235-
/// }
236-
/// ]
237-
/// }
238-
/// ```
239-
///
240-
/// Example V2 JSON:
224+
/// Example JSON:
241225
/// ```json
242226
/// {
243227
/// "version": 2,
@@ -257,72 +241,35 @@ pub struct ModelCosts {
257241
/// The version of the model cost struct
258242
pub version: u16,
259243

260-
/// The mappings of model ID => cost (used in version 1)
261-
#[serde(default, skip_serializing_if = "Vec::is_empty")]
262-
pub costs: Vec<ModelCost>,
263-
264-
/// The mappings of model ID => cost as a dictionary (version 2)
244+
/// The mappings of model ID => cost as a dictionary
265245
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
266246
pub models: HashMap<String, ModelCostV2>,
267247
}
268248

269249
impl ModelCosts {
270-
const MAX_SUPPORTED_VERSION: u16 = 2;
250+
const SUPPORTED_VERSION: u16 = 2;
271251

272252
/// `true` if the model costs are empty and the version is supported.
273253
pub fn is_empty(&self) -> bool {
274-
(self.costs.is_empty() && self.models.is_empty()) || !self.is_enabled()
254+
self.models.is_empty() || !self.is_enabled()
275255
}
276256

277257
/// `false` if measurement and metrics extraction should be skipped.
278258
pub fn is_enabled(&self) -> bool {
279-
self.version > 0 && self.version <= ModelCosts::MAX_SUPPORTED_VERSION
259+
self.version == 2
280260
}
281261

282262
/// Gets the cost per token, if defined for the given model.
283263
pub fn cost_per_token(&self, model_id: &str) -> Option<ModelCostV2> {
284-
match self.version {
285-
1 => {
286-
let input_cost = self.costs.iter().find(|cost| cost.matches(model_id, false));
287-
let output_cost = self.costs.iter().find(|cost| cost.matches(model_id, true));
288-
289-
// V1 costs were defined per 1k tokens, so we need to convert to per token.
290-
if input_cost.is_some() || output_cost.is_some() {
291-
Some(ModelCostV2 {
292-
input_per_token: input_cost.map_or(0.0, |c| c.cost_per_1k_tokens / 1000.0),
293-
output_per_token: output_cost
294-
.map_or(0.0, |c| c.cost_per_1k_tokens / 1000.0),
295-
output_reasoning_per_token: 0.0, // in v1 this info is not available
296-
input_cached_per_token: 0.0, // in v1 this info is not available
297-
})
298-
} else {
299-
None
300-
}
301-
}
302-
2 => self.models.get(model_id).copied(),
303-
_ => None,
264+
if self.version == Self::SUPPORTED_VERSION {
265+
self.models.get(model_id).copied()
266+
} else {
267+
None
304268
}
305269
}
306270
}
307271

308-
/// A mapping of AI model types (like GPT-4) to their respective costs.
309-
#[derive(Clone, Debug, Serialize, Deserialize)]
310-
#[serde(rename_all = "camelCase")]
311-
pub struct ModelCost {
312-
pub(crate) model_id: LazyGlob,
313-
pub(crate) for_completion: bool,
314-
pub(crate) cost_per_1k_tokens: f64,
315-
}
316-
317-
impl ModelCost {
318-
/// `true` if this cost definition matches the given model.
319-
pub fn matches(&self, model_id: &str, for_completion: bool) -> bool {
320-
self.for_completion == for_completion && self.model_id.compiled().is_match(model_id)
321-
}
322-
}
323-
324272
/// Version 2 of a mapping of AI model types (like GPT-4) to their respective costs.
325-
/// Version 1 had some limitations, so we're moving to a more flexible format.
326273
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
327274
#[serde(rename_all = "camelCase")]
328275
pub struct ModelCostV2 {
@@ -366,26 +313,6 @@ mod tests {
366313
/// Test that integer versions are handled correctly in the struct format
367314
#[test]
368315
fn test_model_cost_version_sent_as_number() {
369-
// Test integer version 1
370-
let original = r#"{"version":1,"costs":[{"modelId":"babbage-002.ft","forCompletion":false,"costPer1kTokens":0.0016}]}"#;
371-
let deserialized: ModelCosts = serde_json::from_str(original).unwrap();
372-
assert_debug_snapshot!(
373-
deserialized,
374-
@r#"
375-
ModelCosts {
376-
version: 1,
377-
costs: [
378-
ModelCost {
379-
model_id: LazyGlob("babbage-002.ft"),
380-
for_completion: false,
381-
cost_per_1k_tokens: 0.0016,
382-
},
383-
],
384-
models: {},
385-
}
386-
"#,
387-
);
388-
389316
// Test integer version 2
390317
let original_v2 = r#"{"version":2,"models":{"gpt-4":{"inputPerToken":0.03,"outputPerToken":0.06,"outputReasoningPerToken":0.12,"inputCachedPerToken":0.015}}}"#;
391318
let deserialized_v2: ModelCosts = serde_json::from_str(original_v2).unwrap();
@@ -394,7 +321,6 @@ mod tests {
394321
@r###"
395322
ModelCosts {
396323
version: 2,
397-
costs: [],
398324
models: {
399325
"gpt-4": ModelCostV2 {
400326
input_per_token: 0.03,
@@ -408,42 +334,19 @@ mod tests {
408334
);
409335

410336
// Test unknown integer version
411-
let original_unknown = r#"{"version":99,"costs":[]}"#;
337+
let original_unknown = r#"{"version":99,"models":{}}"#;
412338
let deserialized_unknown: ModelCosts = serde_json::from_str(original_unknown).unwrap();
413339
assert_eq!(deserialized_unknown.version, 99);
414340
assert!(!deserialized_unknown.is_enabled());
415341
}
416342

417-
#[test]
418-
fn test_model_cost_config_v1() {
419-
let original = r#"{"version":1,"costs":[{"modelId":"babbage-002.ft","forCompletion":false,"costPer1kTokens":0.0016}]}"#;
420-
let deserialized: ModelCosts = serde_json::from_str(original).unwrap();
421-
assert_debug_snapshot!(deserialized, @r###"
422-
ModelCosts {
423-
version: 1,
424-
costs: [
425-
ModelCost {
426-
model_id: LazyGlob("babbage-002.ft"),
427-
for_completion: false,
428-
cost_per_1k_tokens: 0.0016,
429-
},
430-
],
431-
models: {},
432-
}
433-
"###);
434-
435-
let serialized = serde_json::to_string(&deserialized).unwrap();
436-
assert_eq!(&serialized, original);
437-
}
438-
439343
#[test]
440344
fn test_model_cost_config_v2() {
441345
let original = r#"{"version":2,"models":{"gpt-4":{"inputPerToken":0.03,"outputPerToken":0.06,"outputReasoningPerToken":0.12,"inputCachedPerToken":0.015}}}"#;
442346
let deserialized: ModelCosts = serde_json::from_str(original).unwrap();
443347
assert_debug_snapshot!(deserialized, @r###"
444348
ModelCosts {
445349
version: 2,
446-
costs: [],
447350
models: {
448351
"gpt-4": ModelCostV2 {
449352
input_per_token: 0.03,
@@ -459,48 +362,6 @@ mod tests {
459362
assert_eq!(&serialized, original);
460363
}
461364

462-
#[test]
463-
fn test_model_cost_functionality_v1_only_input_tokens() {
464-
// Test V1 functionality
465-
let v1_config = ModelCosts {
466-
version: 1,
467-
costs: vec![ModelCost {
468-
model_id: LazyGlob::new("gpt-4*"),
469-
for_completion: false,
470-
cost_per_1k_tokens: 0.03,
471-
}],
472-
models: HashMap::new(),
473-
};
474-
assert!(v1_config.is_enabled());
475-
let costs = v1_config.cost_per_token("gpt-4-turbo").unwrap();
476-
assert_eq!(costs.input_per_token * 1000.0, 0.03); // multiplying by 1000 to avoid floating point errors
477-
assert_eq!(costs.output_per_token, 0.0); // output tokens are not defined
478-
}
479-
480-
#[test]
481-
fn test_model_cost_functionality_v1() {
482-
let v1_config = ModelCosts {
483-
version: 1,
484-
costs: vec![
485-
ModelCost {
486-
model_id: LazyGlob::new("gpt-4*"),
487-
for_completion: false,
488-
cost_per_1k_tokens: 0.03,
489-
},
490-
ModelCost {
491-
model_id: LazyGlob::new("gpt-4*"),
492-
for_completion: true,
493-
cost_per_1k_tokens: 0.06,
494-
},
495-
],
496-
models: HashMap::new(),
497-
};
498-
assert!(v1_config.is_enabled());
499-
let costs = v1_config.cost_per_token("gpt-4").unwrap();
500-
assert_eq!(costs.input_per_token * 1000.0, 0.03); // multiplying by 1000 to avoid floating point errors
501-
assert_eq!(costs.output_per_token * 1000.0, 0.06); // multiplying by 1000 to avoid floating point errors
502-
}
503-
504365
#[test]
505366
fn test_model_cost_functionality_v2() {
506367
// Test V2 functionality
@@ -516,7 +377,6 @@ mod tests {
516377
);
517378
let v2_config = ModelCosts {
518379
version: 2,
519-
costs: vec![],
520380
models: models_map,
521381
};
522382
assert!(v2_config.is_enabled());

0 commit comments

Comments
 (0)