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
4 changes: 2 additions & 2 deletions .github/workflows/snyk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
run: snyk test --all-projects --detection-depth=4 --sarif-file-output=snyk.sarif || true

- name: Upload SARIF
if: always()
if: always() && hashFiles('snyk.sarif') != ''
uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
with:
sarif_file: snyk.sarif
Expand Down Expand Up @@ -135,7 +135,7 @@ jobs:
run: snyk code test --sarif-file-output=snyk-code.sarif || true

- name: Upload SARIF
if: always()
if: always() && hashFiles('snyk-code.sarif') != ''
uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
with:
sarif_file: snyk-code.sarif
1 change: 0 additions & 1 deletion .next/trace

This file was deleted.

2 changes: 1 addition & 1 deletion docs/security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ to an existing tag.
| Repository | Pinning Strategy | Actions Pinned |
| --- | --- | --- |
| `bifrost` (OSS) | Full SHA with version comment | 103 / 103 (100 %) |
| `bifrost-enterprise` | Version tags (`@v4`) | Upgrade planned |
| `bifrost-enterprise` | Full SHA with version comment | 103 / 103 (100 %) |

### NPM Provenance

Expand Down
3 changes: 3 additions & 0 deletions framework/modelcatalog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ type PricingEntry struct {
OutputCostPerVideoPerSecond *float64 `json:"output_cost_per_video_per_second,omitempty"`
OutputCostPerSecond *float64 `json:"output_cost_per_second,omitempty"`

// Model parameters
MaxOutputTokens *int `json:"max_output_tokens,omitempty"`

// Costs - Other
//
// SearchContextCostPerQuery is stored as a single float64, but the pricing datasheet
Expand Down
23 changes: 23 additions & 0 deletions framework/modelcatalog/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,30 @@ func (mc *ModelCatalog) syncPricing(ctx context.Context) error {
return fmt.Errorf("failed to reload pricing cache: %w", err)
}

// Populate model params cache from pricing datasheet max_output_tokens
mc.populateModelParamsFromPricing(pricingData)

mc.logger.Info("successfully synced %d pricing records", len(pricingData))
return nil
}

// populateModelParamsFromPricing extracts max_output_tokens from pricing entries
// and populates the model params cache so that providers can look up max output
// tokens without a separate model-parameters sync.
func (mc *ModelCatalog) populateModelParamsFromPricing(pricingData map[string]PricingEntry) {
modelParamsEntries := make(map[string]providerUtils.ModelParams)
for modelKey, entry := range pricingData {
if entry.MaxOutputTokens != nil {
modelName := extractModelName(modelKey)
modelParamsEntries[modelName] = providerUtils.ModelParams{MaxOutputTokens: entry.MaxOutputTokens}
}
}
if len(modelParamsEntries) > 0 {
providerUtils.BulkSetModelParams(modelParamsEntries)
mc.logger.Debug("populated %d model params entries from pricing datasheet", len(modelParamsEntries))
}
}
Comment on lines +137 to +149
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Non-deterministic overwrite when multiple providers share a base model name

extractModelName strips the provider prefix ("openai/gpt-4o""gpt-4o"), so when the pricing datasheet contains entries from multiple providers for the same underlying model (e.g. "openai/gpt-4o" and "azure/gpt-4o"), the last key visited in the Go map iteration — which is randomised — silently wins and overwrites the earlier value.

This matches the pattern already present in syncModelParameters (lines 368-375), so the current behaviour may be intentional. However, in that path the source is a dedicated model-parameters API that is expected to be provider-neutral, whereas the pricing datasheet routinely has per-provider entries for the same model. If providers legitimately report different max_output_tokens, the cache value will be unpredictable.

If provider identity matters here, consider incorporating it into the cache key, or at least only storing the value when it is absent (i.e. first-write wins) to make the behaviour deterministic.


// loadPricingFromURL loads pricing data from the remote URL
func (mc *ModelCatalog) loadPricingFromURL(ctx context.Context) (map[string]PricingEntry, error) {
// Create HTTP client with timeout
Expand Down Expand Up @@ -183,6 +203,9 @@ func (mc *ModelCatalog) loadPricingIntoMemory(ctx context.Context) error {
mc.pricingData[key] = pricing
}

// Populate model params cache from pricing datasheet max_output_tokens
mc.populateModelParamsFromPricing(pricingData)

return nil
}

Expand Down
17 changes: 10 additions & 7 deletions framework/modelcatalog/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,21 @@ func normalizeStreamRequestType(rt schemas.RequestType) schemas.RequestType {
}
}

// convertPricingDataToTableModelPricing converts the pricing data to a TableModelPricing struct
func convertPricingDataToTableModelPricing(modelKey string, entry PricingEntry) configstoreTables.TableModelPricing {
provider := normalizeProvider(entry.Provider)

// Handle provider/model format - extract just the model name
modelName := modelKey
// extractModelName extracts the model name from a model key that may be in provider/model format
func extractModelName(modelKey string) string {
if strings.Contains(modelKey, "/") {
parts := strings.Split(modelKey, "/")
if len(parts) > 1 {
modelName = strings.Join(parts[1:], "/")
return strings.Join(parts[1:], "/")
}
}
return modelKey
}

// convertPricingDataToTableModelPricing converts the pricing data to a TableModelPricing struct
func convertPricingDataToTableModelPricing(modelKey string, entry PricingEntry) configstoreTables.TableModelPricing {
provider := normalizeProvider(entry.Provider)
modelName := extractModelName(modelKey)

return configstoreTables.TableModelPricing{
Model: modelName,
Expand Down
Loading