@@ -188,6 +188,10 @@ class GenericMetricUnit(BaseMetricUnit):
188188 RATIO = _unit ("ratio" )
189189 USER = _unit ("user" )
190190 PERCENT = _unit ("%" )
191+ IMAGE = _unit ("image" )
192+ IMAGES = _unit ("images" )
193+ VIDEO = _unit ("video" )
194+ VIDEOS = _unit ("videos" )
191195
192196
193197class PowerMetricUnitInfo (BaseMetricUnitInfo ):
@@ -289,7 +293,11 @@ class MetricOverTimeUnitInfo(BaseMetricUnitInfo):
289293 @model_validator (mode = "after" )
290294 def _set_tag (self : Self ) -> Self :
291295 """Set the tag based on the existing units. ie. requests/sec, tokens/sec, etc."""
292- self .tag = f"{ self .primary_unit } /{ self .time_unit } "
296+ self .tag = (
297+ f"{ self .primary_unit } /{ self .time_unit } "
298+ if not self .inverted
299+ else f"{ self .time_unit } /{ self .primary_unit } "
300+ )
293301 if self .third_unit :
294302 # If there is a third unit, add it to the tag. ie. tokens/sec/user
295303 self .tag += f"/{ self .third_unit } "
@@ -302,6 +310,7 @@ def _set_tag(self: Self) -> Self:
302310 primary_unit : "MetricUnitT"
303311 time_unit : MetricTimeUnit | MetricTimeUnitInfo
304312 third_unit : "MetricUnitT | None" = None
313+ inverted : bool = False
305314
306315 def convert_to (self , other_unit : "MetricUnitT" , value : int | float ) -> float :
307316 """Convert a value from this unit to another unit."""
@@ -342,6 +351,24 @@ class MetricOverTimeUnit(BaseMetricUnit):
342351 time_unit = MetricTimeUnit .SECONDS ,
343352 third_unit = GenericMetricUnit .USER ,
344353 )
354+ IMAGES_PER_SECOND = MetricOverTimeUnitInfo (
355+ primary_unit = GenericMetricUnit .IMAGES ,
356+ time_unit = MetricTimeUnit .SECONDS ,
357+ )
358+ MS_PER_IMAGE = MetricOverTimeUnitInfo (
359+ time_unit = MetricTimeUnit .MILLISECONDS ,
360+ primary_unit = GenericMetricUnit .IMAGE ,
361+ inverted = True ,
362+ )
363+ VIDEOS_PER_SECOND = MetricOverTimeUnitInfo (
364+ primary_unit = GenericMetricUnit .VIDEOS ,
365+ time_unit = MetricTimeUnit .SECONDS ,
366+ )
367+ MS_PER_VIDEO = MetricOverTimeUnitInfo (
368+ time_unit = MetricTimeUnit .MILLISECONDS ,
369+ primary_unit = GenericMetricUnit .VIDEO ,
370+ inverted = True ,
371+ )
345372
346373 @cached_property
347374 def info (self ) -> MetricOverTimeUnitInfo :
@@ -363,6 +390,11 @@ def third_unit(self) -> "MetricUnitT | None":
363390 """Get the third unit (if applicable)."""
364391 return self .info .third_unit
365392
393+ @cached_property
394+ def inverted (self ) -> bool :
395+ """Whether the metric is inverted (e.g. time / metric)."""
396+ return self .info .inverted
397+
366398
367399class MetricType (CaseInsensitiveStrEnum ):
368400 """Defines the possible types of metrics."""
@@ -643,6 +675,9 @@ class MetricFlags(Flag):
643675 TOKENIZES_INPUT_ONLY = 1 << 12
644676 """Metrics that are only applicable when the endpoint tokenizes input text."""
645677
678+ SUPPORTS_VIDEO_ONLY = 1 << 13
679+ """Metrics that are only applicable to video-based endpoints."""
680+
646681 def has_flags (self , flags : "MetricFlags" ) -> bool :
647682 """Return True if the metric has ALL of the given flag(s) (regardless of other flags)."""
648683 # Bitwise AND will return the input flags only if all of the given flags are present.
0 commit comments