apitypes.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package inferencecost
  2. import (
  3. "github.com/opencost/opencost/core/pkg/opencost"
  4. )
  5. // InferenceCostResponse is the flat, per-cost-basis API representation of
  6. // inference costs for a single model/namespace in a time window. It is
  7. // projected from the internal InferenceCost struct (which stores costs keyed
  8. // by CostBasis) so that the JSON output matches the design doc shape exactly.
  9. type InferenceCostResponse struct {
  10. Properties InferenceCostAPIProperties `json:"properties"`
  11. Window opencost.Window `json:"window"`
  12. // CostBasis identifies whether these costs are usage-based or
  13. // allocation-based. Set from the QueryRequest.
  14. CostBasis CostBasis `json:"costBasis"`
  15. // Total infrastructure cost for the window under the chosen cost basis.
  16. TotalCost float64 `json:"totalCost"`
  17. // Token counts from vLLM metrics.
  18. PromptTokens float64 `json:"promptTokens"`
  19. GenerationTokens float64 `json:"generationTokens"`
  20. TotalTokens float64 `json:"totalTokens"`
  21. // Blended cost per 1M delivered tokens (input + output together).
  22. CostPerMillionTokens float64 `json:"costPerMillionTokens"`
  23. // Input/output cost split. InputCost and OutputCost sum to TotalCost if the cost basis is allocation.
  24. InputCost float64 `json:"inputCost"`
  25. OutputCost float64 `json:"outputCost"`
  26. // Per-million cost metrics for differentiated pricing.
  27. // InputCostPerMillionTokens uses PromptTokens as the denominator (all delivered
  28. // input tokens, including those served from KV cache).
  29. InputCostPerMillionTokens float64 `json:"inputCostPerMillionTokens"`
  30. OutputCostPerMillionTokens float64 `json:"outputCostPerMillionTokens"`
  31. // CacheSavingsFraction is the fraction of prompt tokens served from the KV
  32. // cache (CachedTokens / PromptTokens, clamped to [0, 1]). Zero when prefix
  33. // caching is disabled (see allocationMethod) or when no cache hits occurred
  34. // in the window.
  35. //
  36. // Note: in workloads with heavy prefix reuse (e.g. benchmarks with long
  37. // shared system prompts), the raw ratio can exceed 1.0 because
  38. // vllm:prefix_cache_hits_total counts tokens retrieved from cache per
  39. // request — including prefixes established by earlier requests outside the
  40. // current window — while vllm:prompt_tokens_total only counts new input
  41. // tokens delivered in this window. The value is clamped to 1.0 in that case.
  42. CacheSavingsFraction float64 `json:"cacheSavingsFraction"`
  43. // cachedTokens is carried for aggregation recomputation of CacheSavingsFraction
  44. // and is not included in the JSON output.
  45. cachedTokens float64
  46. // AllocationMethod records which input/output cost-split path was used.
  47. // Informational; omitted when empty.
  48. AllocationMethod AllocationMethod `json:"allocationMethod,omitempty"`
  49. }
  50. // InferenceCostAPIProperties is the JSON-facing properties struct for API
  51. // responses. It mirrors InferenceCostProperties but with explicit JSON tags
  52. // matching the design doc field names.
  53. type InferenceCostAPIProperties struct {
  54. ModelName string `json:"modelName"`
  55. ModelVersion string `json:"modelVersion,omitempty"`
  56. Namespace string `json:"namespace"`
  57. Cluster string `json:"cluster,omitempty"`
  58. Pod string `json:"pod,omitempty"`
  59. Controller string `json:"controller,omitempty"`
  60. ControllerKind string `json:"controllerKind,omitempty"`
  61. Container string `json:"container,omitempty"`
  62. WorkloadType string `json:"workloadType"` // currently always "inference"
  63. }
  64. // InferenceCostSet holds a collection of InferenceCostResponses for a single
  65. // time window, keyed by aggregation key.
  66. type InferenceCostSet struct {
  67. InferenceCosts map[string]*InferenceCostResponse `json:"inferenceCosts"`
  68. Window opencost.Window `json:"window"`
  69. }
  70. // InferenceCostSetRange holds multiple InferenceCostSets covering a broader
  71. // time range. Used for the /timeseries endpoint.
  72. type InferenceCostSetRange struct {
  73. InferenceCostSets []*InferenceCostSet `json:"inferenceCostSets"`
  74. Window opencost.Window `json:"window"`
  75. }
  76. // newInferenceCostResponse projects a single InferenceCost into the flat
  77. // per-basis API response type for the given window.
  78. func newInferenceCostResponse(ic *InferenceCost, basis CostBasis, win opencost.Window) *InferenceCostResponse {
  79. var totalCost float64
  80. if basis == CostBasisUsage {
  81. totalCost = ic.UsageTotalCost
  82. } else {
  83. totalCost = ic.AllocationTotalCost
  84. }
  85. cpmt := ic.CostPerMillionTokens[basis]
  86. icpmt := ic.InputCostPerMillionTokens[basis]
  87. ocpmt := ic.OutputCostPerMillionTokens[basis]
  88. inputCost := ic.InputCost[basis]
  89. outputCost := ic.OutputCost[basis]
  90. return &InferenceCostResponse{
  91. Properties: InferenceCostAPIProperties{
  92. ModelName: ic.Properties.ModelName,
  93. ModelVersion: ic.Properties.ModelVersion,
  94. Namespace: ic.Properties.Namespace,
  95. Cluster: ic.Properties.Cluster,
  96. Pod: ic.Properties.Pod,
  97. Controller: ic.Properties.Controller,
  98. ControllerKind: ic.Properties.ControllerKind,
  99. Container: ic.Properties.Container,
  100. WorkloadType: ic.Properties.WorkloadType,
  101. },
  102. Window: win,
  103. CostBasis: basis,
  104. TotalCost: totalCost,
  105. PromptTokens: ic.PromptTokens,
  106. GenerationTokens: ic.GenerationTokens,
  107. TotalTokens: ic.TotalTokens,
  108. CostPerMillionTokens: cpmt,
  109. InputCost: inputCost,
  110. OutputCost: outputCost,
  111. InputCostPerMillionTokens: icpmt,
  112. OutputCostPerMillionTokens: ocpmt,
  113. CacheSavingsFraction: ic.CacheSavingsFraction,
  114. cachedTokens: ic.CachedTokens,
  115. AllocationMethod: ic.AllocationMethod,
  116. }
  117. }
  118. // newInferenceCostSet creates an empty InferenceCostSet for the given window.
  119. func newInferenceCostSet(win opencost.Window) *InferenceCostSet {
  120. return &InferenceCostSet{
  121. InferenceCosts: make(map[string]*InferenceCostResponse),
  122. Window: win,
  123. }
  124. }