Leon4gr45's picture
Upload folder using huggingface_hub (part 6)
d6f631f verified
Raw
History Blame Contribute Delete
7.43 kB
package productcatalogdriver
import (
"context"
"fmt"
"github.com/alpacahq/alpacadecimal"
"github.com/samber/lo"
"github.com/openmeterio/openmeter/api"
"github.com/openmeterio/openmeter/openmeter/apiconverter"
"github.com/openmeterio/openmeter/openmeter/llmcost"
"github.com/openmeterio/openmeter/openmeter/productcatalog/feature"
"github.com/openmeterio/openmeter/pkg/convert"
"github.com/openmeterio/openmeter/pkg/filter"
)
func MapFeatureToResponse(f feature.Feature) (api.Feature, error) {
meterGroupByFilters := feature.ConvertMeterGroupByFiltersToMapString(f.MeterGroupByFilters)
resp := api.Feature{
CreatedAt: f.CreatedAt,
DeletedAt: nil,
UpdatedAt: f.UpdatedAt,
Id: f.ID,
Key: f.Key,
Metadata: convert.MapToPointer(f.Metadata),
Name: f.Name,
ArchivedAt: f.ArchivedAt,
MeterGroupByFilters: convert.MapToPointer(meterGroupByFilters),
AdvancedMeterGroupByFilters: convert.MapToPointer(apiconverter.ConvertStringMapToAPIPtr(f.MeterGroupByFilters)),
MeterSlug: f.MeterSlug,
}
if f.UnitCost != nil {
apiUnitCost, err := domainUnitCostToAPI(f.UnitCost)
if err != nil {
return api.Feature{}, fmt.Errorf("failed to convert unit cost: %w", err)
}
resp.UnitCost = &apiUnitCost
}
return resp, nil
}
func MapFeatureCreateInputsRequest(namespace string, f api.FeatureCreateInputs, meterID *string) (feature.CreateFeatureInputs, error) {
// if advancedMeterGroupByFilters is set, use it
// otherwise, use legacy meterGroupByFilters
meterGroupByFilters := lo.FromPtrOr(apiconverter.ConvertStringMapPtr(f.AdvancedMeterGroupByFilters), map[string]filter.FilterString{})
if len(meterGroupByFilters) == 0 {
meterGroupByFilters = feature.ConvertMapStringToMeterGroupByFilters(lo.FromPtrOr(f.MeterGroupByFilters, map[string]string{}))
}
inputs := feature.CreateFeatureInputs{
Namespace: namespace,
Name: f.Name,
Key: f.Key,
MeterID: meterID,
MeterGroupByFilters: meterGroupByFilters,
Metadata: convert.DerefHeaderPtr[string](f.Metadata),
}
if f.UnitCost != nil {
unitCost, err := apiUnitCostToDomain(f.UnitCost)
if err != nil {
return feature.CreateFeatureInputs{}, fmt.Errorf("invalid unit cost: %w", err)
}
inputs.UnitCost = unitCost
}
return inputs, nil
}
func domainUnitCostToAPI(u *feature.UnitCost) (api.FeatureUnitCost, error) {
var out api.FeatureUnitCost
switch u.Type {
case feature.UnitCostTypeManual:
if err := out.FromFeatureManualUnitCost(api.FeatureManualUnitCost{
Amount: u.Manual.Amount.String(),
}); err != nil {
return out, fmt.Errorf("failed to convert manual unit cost: %w", err)
}
case feature.UnitCostTypeLLM:
llmCost := api.FeatureLLMUnitCost{}
if u.LLM.ProviderProperty != "" {
llmCost.ProviderProperty = lo.ToPtr(u.LLM.ProviderProperty)
}
if u.LLM.Provider != "" {
llmCost.Provider = lo.ToPtr(u.LLM.Provider)
}
if u.LLM.ModelProperty != "" {
llmCost.ModelProperty = lo.ToPtr(u.LLM.ModelProperty)
}
if u.LLM.Model != "" {
llmCost.Model = lo.ToPtr(u.LLM.Model)
}
if u.LLM.TokenTypeProperty != "" {
llmCost.TokenTypeProperty = lo.ToPtr(u.LLM.TokenTypeProperty)
}
if u.LLM.TokenType != "" {
llmCost.TokenType = lo.ToPtr(u.LLM.TokenType)
}
if err := out.FromFeatureLLMUnitCost(llmCost); err != nil {
return out, fmt.Errorf("failed to convert LLM unit cost: %w", err)
}
default:
return out, fmt.Errorf("unknown unit cost type: %s", u.Type)
}
return out, nil
}
func apiUnitCostToDomain(u *api.FeatureUnitCost) (*feature.UnitCost, error) {
discriminator, err := u.Discriminator()
if err != nil {
return nil, fmt.Errorf("failed to determine unit cost type: %w", err)
}
switch discriminator {
case "manual":
manual, err := u.AsFeatureManualUnitCost()
if err != nil {
return nil, fmt.Errorf("failed to parse manual unit cost: %w", err)
}
amount, err := alpacadecimal.NewFromString(manual.Amount)
if err != nil {
return nil, fmt.Errorf("invalid manual unit cost amount %q: %w", manual.Amount, err)
}
return &feature.UnitCost{
Type: feature.UnitCostTypeManual,
Manual: &feature.ManualUnitCost{
Amount: amount,
},
}, nil
case "llm":
llm, err := u.AsFeatureLLMUnitCost()
if err != nil {
return nil, fmt.Errorf("failed to parse LLM unit cost: %w", err)
}
return &feature.UnitCost{
Type: feature.UnitCostTypeLLM,
LLM: &feature.LLMUnitCost{
ProviderProperty: lo.FromPtrOr(llm.ProviderProperty, ""),
Provider: lo.FromPtrOr(llm.Provider, ""),
ModelProperty: lo.FromPtrOr(llm.ModelProperty, ""),
Model: lo.FromPtrOr(llm.Model, ""),
TokenTypeProperty: lo.FromPtrOr(llm.TokenTypeProperty, ""),
TokenType: lo.FromPtrOr(llm.TokenType, ""),
},
}, nil
default:
return nil, fmt.Errorf("unknown unit cost type: %s", discriminator)
}
}
// resolveLLMPricing extracts provider and model from the feature's meterGroupByFilters
// and resolves the current pricing from the LLM cost database.
// Returns nil if provider/model can't be determined or pricing can't be resolved.
func resolveLLMPricing(ctx context.Context, svc llmcost.Service, feat *feature.Feature) *llmcost.ModelPricing {
if feat.UnitCost == nil || feat.UnitCost.LLM == nil {
return nil
}
llmConf := feat.UnitCost.LLM
// Resolve provider: static value or from meterGroupByFilters
provider := llmConf.Provider
if provider == "" {
provider = extractEqFilterValue(feat.MeterGroupByFilters, llmConf.ProviderProperty)
}
if provider == "" {
return nil
}
// Resolve model: static value or from meterGroupByFilters
model := llmConf.Model
if model == "" {
model = extractEqFilterValue(feat.MeterGroupByFilters, llmConf.ModelProperty)
}
if model == "" {
return nil
}
price, err := svc.ResolvePrice(ctx, llmcost.ResolvePriceInput{
Namespace: feat.Namespace,
Provider: llmcost.Provider(provider),
ModelID: model,
})
if err != nil {
return nil
}
return &price.Pricing
}
// extractEqFilterValue extracts a simple $eq value from a MeterGroupByFilters map for the given key.
func extractEqFilterValue(filters feature.MeterGroupByFilters, key string) string {
if filters == nil {
return ""
}
f, ok := filters[key]
if !ok || f.Eq == nil {
return ""
}
return *f.Eq
}
// enrichFeatureResponseWithPricing adds resolved LLM pricing to the feature API response.
func enrichFeatureResponseWithPricing(resp *api.Feature, pricing *llmcost.ModelPricing) {
if resp.UnitCost == nil || pricing == nil {
return
}
llmCost, err := resp.UnitCost.AsFeatureLLMUnitCost()
if err != nil {
return
}
apiPricing := api.FeatureLLMUnitCostPricing{
InputPerToken: pricing.InputPerToken.String(),
OutputPerToken: pricing.OutputPerToken.String(),
}
if pricing.CacheReadPerToken != nil {
v := pricing.CacheReadPerToken.String()
apiPricing.CacheReadPerToken = &v
}
if pricing.CacheWritePerToken != nil {
v := pricing.CacheWritePerToken.String()
apiPricing.CacheWritePerToken = &v
}
if pricing.ReasoningPerToken != nil {
v := pricing.ReasoningPerToken.String()
apiPricing.ReasoningPerToken = &v
}
llmCost.Pricing = &apiPricing
_ = resp.UnitCost.FromFeatureLLMUnitCost(llmCost)
}