Skip to content

Conversation

@Pratham-Mishra04
Copy link
Collaborator

Summary

Added model pricing data support to the Bifrost core, enabling automatic pricing information in model listings and providing a mechanism to sync pricing data from external sources.

Changes

  • Added a pricingData field to the Bifrost struct to store pricing information for models
  • Created new methods to set and retrieve pricing data for models
  • Added a DataSheetPricingEntry struct to represent detailed pricing information
  • Enhanced BifrostListModelsResponse with an AddPricing method to include pricing in model listings
  • Integrated pricing data with the model catalog framework
  • Added a callback mechanism to update pricing data when synced from external sources
  • Added DeploymentName field to the Model struct

Type of change

  • Feature
  • Refactor

Affected areas

  • Core (Go)
  • Transports (HTTP)

How to test

# Core/Transports
go version
go test ./...

# Test model listing with pricing data
curl -X GET "http://localhost:8000/v1/models" -H "Authorization: Bearer $BIFROST_API_KEY"

Breaking changes

  • No

Related issues

Enhances model listing capabilities with pricing information

Security considerations

No security implications as this only adds informational data to model responses.

Checklist

  • I added/updated tests where appropriate
  • I verified builds succeed (Go and UI)

Copy link
Collaborator Author

Pratham-Mishra04 commented Nov 4, 2025

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 4, 2025

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Models now include comprehensive pricing information: per-token, per-image, per-video, per-audio, per-character costs, plus batch and cache pricing
    • Pagination support added for model listings with cursor-based navigation
    • Model information now displays deployment names
  • Refactor

    • Pricing data management system restructured for improved maintainability

Walkthrough

The changes introduce comprehensive pricing data management across the codebase. A new DataSheetPricingEntry type is added to core schemas with extensive pricing fields. The Bifrost client gains methods to store and retrieve pricing information keyed by provider and model. ModelCatalog transitions from legacy in-memory pricing structures to a callback-based mechanism. Pricing data flows from sync operations through callbacks to the Bifrost client, enriching model responses with pricing details.

Changes

Cohort / File(s) Summary
Core Pricing Schema
core/schemas/models.go
Added DataSheetPricingEntry struct with per-token, image, video, audio, character, and batch/cache pricing fields; added PricingFetcher function type; introduced ApplyPagination method for offset-based pagination with cursor validation; added AddPricing method to enrich models with pricing data; added DeploymentName field to Model; embedded DataSheetPricingEntry in Pricing struct.
Bifrost Client Pricing Integration
core/bifrost.go
Added pricingData sync.Map field for per-instance pricing storage; introduced SetPricingData to bulk-set pricing by key; added GetPricingDataForModel to fetch pricing for a specific model/provider; added GetConfiguredProviders method; integrated pricing attachment to ListModelsRequest and ListAllModels responses.
ModelCatalog Configuration & API
framework/modelcatalog/main.go
Added PricingSyncCallback to Config struct to receive pricing data after sync; added GetPricingData method returning map of pricing entries; introduced unexported pricingSyncCallback field; removed legacy PricingData and PricingEntry types; wired callback in Init.
ModelCatalog Sync & Data Loading
framework/modelcatalog/sync.go
Updated loadPricingFromURL return type from legacy PricingData to map[string]schemas.DataSheetPricingEntry; added optional callback invocation with pricing data after successful sync; updated pricing data processing loop to work with new map type; added debug logging for downloaded pricing.
ModelCatalog Conversion Utilities
framework/modelcatalog/utils.go
Added splitKey utility to decompose composite keys; updated convertPricingDataToTableModelPricing to accept schemas.DataSheetPricingEntry and populate expanded pricing fields (images, video, audio, characters, 128k-variants, batch/cache); introduced convertTableModelPricingToPricingData for reverse conversion.
HTTP Transport Configuration & Integration
transports/bifrost-http/lib/config.go
Added PricingSyncCallback assignments in LoadConfig to propagate pricing data to Bifrost client via config.client.SetPricingData.
HTTP Server Bootstrap
transports/bifrost-http/server/server.go
Added call to propagate pricing data from PricingManager to Bifrost client in Bootstrap after model data is added to pool.

Sequence Diagram

sequenceDiagram
    participant MC as ModelCatalog
    participant BIF as Bifrost Client
    participant MODEL as Model Response
    
    Note over MC,MODEL: Pricing Data Initialization
    MC->>MC: loadPricingFromURL()
    MC->>MC: Invoke PricingSyncCallback<br/>with pricingData map
    MC->>BIF: SetPricingData(pricingData)
    BIF->>BIF: Store in pricingData<br/>sync.Map
    
    Note over BIF,MODEL: Model List Request with Pricing
    BIF->>BIF: ListModelsRequest()
    BIF->>BIF: AddPricing(GetPricingDataForModel)
    BIF->>BIF: Enriches each Model<br/>with pricing entry
    BIF->>MODEL: Return response with<br/>pricing attached
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Multiple affected files across different layers (7 files total) with varying purposes
  • Introduction of new DataSheetPricingEntry struct with extensive fields requires understanding of pricing taxonomy
  • Callback-based pricing propagation flow requires tracing data movement across components
  • Conversion logic between internal (TableModelPricing) and schema (DataSheetPricingEntry) types needs verification
  • Legacy pricing structure removal should be cross-checked for completeness

Poem

🐰 Pricing whispers through the air,
Callbacks carry data with care,
From sync to Bifrost, models shine bright,
With tokens and images priced just right!

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding model pricing data support to Bifrost and model responses. It is specific, concise, and directly related to the primary objective of the PR.
Description check ✅ Passed The description covers all required template sections: Summary, Changes, Type of change, Affected areas, How to test, Breaking changes, Related issues, Security considerations, and Checklist. All required information is present and complete.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 11-04-feat_model_pricing_added_to_list_models_endpoint

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49db44b and 7e6b09b.

📒 Files selected for processing (7)
  • core/bifrost.go (5 hunks)
  • core/schemas/models.go (4 hunks)
  • framework/modelcatalog/main.go (4 hunks)
  • framework/modelcatalog/sync.go (3 hunks)
  • framework/modelcatalog/utils.go (3 hunks)
  • transports/bifrost-http/lib/config.go (2 hunks)
  • transports/bifrost-http/server/server.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
core/bifrost.go (3)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/schemas/provider.go (1)
  • Provider (199-226)
core/schemas/bifrost.go (1)
  • ModelProvider (32-32)
framework/modelcatalog/sync.go (2)
framework/modelcatalog/main.go (1)
  • ModelCatalog (32-56)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
framework/modelcatalog/utils.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
transports/bifrost-http/server/server.go (2)
framework/modelcatalog/main.go (1)
  • Config (26-30)
transports/bifrost-http/lib/config.go (1)
  • Config (139-169)
transports/bifrost-http/lib/config.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/schemas/models.go (2)
core/schemas/bifrost.go (1)
  • ModelProvider (32-32)
core/schemas/provider.go (1)
  • Provider (199-226)
framework/modelcatalog/main.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
core/bifrost.go (1)

376-378: Avoid duplicate AddPricing calls.

AddPricing already runs in ListModelsRequest; calling it again in ListAllModels is redundant.

Apply this diff:

- // Add pricing data to the response
- response.AddPricing(bifrost.GetPricingDataForModel)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba5078f and 951ed2f.

📒 Files selected for processing (7)
  • core/bifrost.go (5 hunks)
  • core/schemas/models.go (4 hunks)
  • framework/modelcatalog/main.go (4 hunks)
  • framework/modelcatalog/sync.go (3 hunks)
  • framework/modelcatalog/utils.go (3 hunks)
  • transports/bifrost-http/lib/config.go (2 hunks)
  • transports/bifrost-http/server.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
transports/bifrost-http/server.go (2)
framework/modelcatalog/main.go (1)
  • Config (26-30)
transports/bifrost-http/lib/config.go (1)
  • Config (139-169)
transports/bifrost-http/lib/config.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/bifrost.go (2)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/schemas/bifrost.go (1)
  • ModelProvider (32-32)
framework/modelcatalog/utils.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/schemas/models.go (2)
core/schemas/bifrost.go (1)
  • ModelProvider (32-32)
core/schemas/provider.go (1)
  • Provider (199-226)
framework/modelcatalog/sync.go (2)
framework/modelcatalog/main.go (1)
  • ModelCatalog (32-56)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
framework/modelcatalog/main.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check
🔇 Additional comments (3)
transports/bifrost-http/server.go (1)

732-734: Bootstrap pricing propagation looks good.

This correctly seeds the client with pricing after catalog load.

core/schemas/models.go (1)

118-131: Pricing attachment logic looks solid.

Sets DataSheetPricingEntry irrespective of preexisting Pricing object.

transports/bifrost-http/lib/config.go (1)

920-924: Review comment references non-existent method GetBifrostClient()

The data race concern is valid—both callbacks at lines 467–468 and 921–922 use unsynchronized direct reads of config.client while SetBifrostClient (line 1523) writes with muMCP lock protection. However, the suggested diff cannot be applied as written because GetBifrostClient() does not exist in the codebase.

To properly fix the data race, either:

  1. Create a thread-safe GetBifrostClient() getter method that acquires the muMCP lock before returning the client, then apply the suggested diff, OR
  2. Modify both callbacks to acquire the mutex directly, similar to how AddMCPClient uses c.muMCP.Lock() for client field access.

Comment on lines +838 to +841
for model, pricing := range pricingData {
bifrost.pricingData.Store(pricing.Provider+"/"+model, pricing)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Normalize model keys to prevent double provider prefixes.

If incoming map keys already include "/", we end up storing "provider/provider/model". Strip the prefix before storing.

Apply this diff:

-func (bifrost *Bifrost) SetPricingData(pricingData map[string]schemas.DataSheetPricingEntry) {
-  for model, pricing := range pricingData {
-    bifrost.pricingData.Store(pricing.Provider+"/"+model, pricing)
-  }
-}
+func (bifrost *Bifrost) SetPricingData(pricingData map[string]schemas.DataSheetPricingEntry) {
+  for model, pricing := range pricingData {
+    // Normalize model: strip "<provider>/" if present
+    if strings.HasPrefix(model, pricing.Provider+"/") {
+      model = strings.TrimPrefix(model, pricing.Provider+"/")
+    }
+    bifrost.pricingData.Store(pricing.Provider+"/"+model, pricing)
+  }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for model, pricing := range pricingData {
bifrost.pricingData.Store(pricing.Provider+"/"+model, pricing)
}
}
func (bifrost *Bifrost) SetPricingData(pricingData map[string]schemas.DataSheetPricingEntry) {
for model, pricing := range pricingData {
// Normalize model: strip "<provider>/" if present
if strings.HasPrefix(model, pricing.Provider+"/") {
model = strings.TrimPrefix(model, pricing.Provider+"/")
}
bifrost.pricingData.Store(pricing.Provider+"/"+model, pricing)
}
}
🤖 Prompt for AI Agents
In core/bifrost.go around lines 838 to 841, the loop stores pricing entries
using pricing.Provider+"/"+model which can duplicate the provider prefix if
model already starts with "provider/"; update the loop to normalize keys by
checking if model begins with pricing.Provider+"/" (or any "<provider>/") and
strip that leading "<provider>/" before constructing the stored key so you
always store a single "provider/model" entry.

Comment on lines +131 to +134
if mc.pricingSyncCallback != nil {
mc.pricingSyncCallback(pricingData)
mc.logger.Debug("pricing sync callback executed")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Pass normalized pricing data to the callback to avoid key mismatches.

Use mc.GetPricingData() instead of the raw URL map to keep keys consistent for SetPricingData.

Apply this diff:

- if mc.pricingSyncCallback != nil {
-   mc.pricingSyncCallback(pricingData)
-   mc.logger.Debug("pricing sync callback executed")
- }
+ if mc.pricingSyncCallback != nil {
+   mc.pricingSyncCallback(mc.GetPricingData())
+   mc.logger.Debug("pricing sync callback executed")
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if mc.pricingSyncCallback != nil {
mc.pricingSyncCallback(pricingData)
mc.logger.Debug("pricing sync callback executed")
}
if mc.pricingSyncCallback != nil {
mc.pricingSyncCallback(mc.GetPricingData())
mc.logger.Debug("pricing sync callback executed")
}
🤖 Prompt for AI Agents
In framework/modelcatalog/sync.go around lines 131 to 134, the pricing sync
callback is being invoked with the raw URL map (pricingData) which can cause key
mismatches; call mc.GetPricingData() and pass that normalized result to
mc.pricingSyncCallback so the callback receives the same key format used by
SetPricingData, then keep the existing debug log.

Comment on lines +13 to +17
// splitKey splits a key into model, provider, and mode
func splitKey(key string) (string, string, string) {
parts := strings.Split(key, "|")
return parts[0], parts[1], parts[2]
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Harden splitKey against malformed keys.

Avoid possible index-out-of-range by validating parts length.

Apply this diff:

-func splitKey(key string) (string, string, string) {
-  parts := strings.Split(key, "|")
-  return parts[0], parts[1], parts[2]
-}
+func splitKey(key string) (string, string, string) {
+  parts := strings.Split(key, "|")
+  if len(parts) < 3 {
+    // best-effort fallback
+    return key, "", ""
+  }
+  return parts[0], parts[1], parts[2]
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// splitKey splits a key into model, provider, and mode
func splitKey(key string) (string, string, string) {
parts := strings.Split(key, "|")
return parts[0], parts[1], parts[2]
}
// splitKey splits a key into model, provider, and mode
func splitKey(key string) (string, string, string) {
parts := strings.Split(key, "|")
if len(parts) < 3 {
// best-effort fallback
return key, "", ""
}
return parts[0], parts[1], parts[2]
}
🤖 Prompt for AI Agents
In framework/modelcatalog/utils.go around lines 13 to 17, splitKey currently
assumes the input contains two '|' separators and directly indexes parts, which
can panic on malformed keys; update the function to validate parts length
(ensure len(parts) >= 3) before indexing and handle invalid input by returning
safe zero-values (e.g., "", "", "") or an explicit error indicator; implement
the length check and conditional return so callers won't experience
index-out-of-range panics.

Comment on lines +466 to +470
pricingConfig.PricingSyncCallback = func(pricingData map[string]schemas.DataSheetPricingEntry) {
if config.client != nil {
config.client.SetPricingData(pricingData)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Data race: unsynchronized read of config.client in callback.

SetBifrostClient writes under muMCP; this callback reads without a lock. Use a getter with RLock.

Apply this diff here:

- pricingConfig.PricingSyncCallback = func(pricingData map[string]schemas.DataSheetPricingEntry) {
-   if config.client != nil {
-     config.client.SetPricingData(pricingData)
-   }
- }
+ pricingConfig.PricingSyncCallback = func(pricingData map[string]schemas.DataSheetPricingEntry) {
+   if c := config.GetBifrostClient(); c != nil {
+     c.SetPricingData(pricingData)
+   }
+ }

Add this accessor (near SetBifrostClient):

func (c *Config) GetBifrostClient() *bifrost.Bifrost {
  c.muMCP.RLock()
  defer c.muMCP.RUnlock()
  return c.client
}
🤖 Prompt for AI Agents
In transports/bifrost-http/lib/config.go around lines 466 to 470, the callback
reads config.client without synchronization causing a data race; add a
thread-safe getter method near SetBifrostClient that acquires
c.muMCP.RLock()/RUnlock() and returns c.client, then update the
pricingConfig.PricingSyncCallback to call config.GetBifrostClient() and check
the returned value before calling SetPricingData so the read is protected by the
RLock.

@Pratham-Mishra04 Pratham-Mishra04 changed the title feat: add model pricing data to Bifrost and model responses DO NOT MERGE feat: add model pricing data to Bifrost and model responses Nov 4, 2025
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 11-04-feat_model_pricing_added_to_list_models_endpoint branch from 951ed2f to 49db44b Compare November 4, 2025 10:01
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 10-31-feat_modelcatelog_enhancements branch from ba5078f to 9782009 Compare November 4, 2025 10:01
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 11-04-feat_model_pricing_added_to_list_models_endpoint branch from 49db44b to b31416f Compare November 4, 2025 10:35
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 10-31-feat_modelcatelog_enhancements branch from 9782009 to f34e7ee Compare November 4, 2025 10:35
@akshaydeo akshaydeo changed the base branch from 10-31-feat_modelcatelog_enhancements to graphite-base/745 November 4, 2025 10:36
@akshaydeo akshaydeo force-pushed the 11-04-feat_model_pricing_added_to_list_models_endpoint branch from b31416f to 8fc37ed Compare November 4, 2025 10:36
@graphite-app graphite-app bot changed the base branch from graphite-base/745 to main November 4, 2025 10:37
@akshaydeo akshaydeo force-pushed the 11-04-feat_model_pricing_added_to_list_models_endpoint branch from 8fc37ed to 7e6b09b Compare November 4, 2025 10:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
framework/modelcatalog/sync.go (1)

131-134: Invoke the pricing callback with normalized data.

Passing the raw map from the download keeps provider-prefixed keys, so downstream consumers (e.g. Bifrost.SetPricingData) see inconsistent identifiers and produce incorrect keys. Call the accessor that already returns our canonicalized map instead.

Apply this diff:

-	if mc.pricingSyncCallback != nil {
-		mc.pricingSyncCallback(pricingData)
+	if mc.pricingSyncCallback != nil {
+		mc.pricingSyncCallback(mc.GetPricingData())
core/bifrost.go (1)

838-840: Normalize incoming pricing keys before storing.

Once the catalog starts returning provider-qualified keys, this loop will emit provider/provider/model, corrupting lookups. Strip the provider prefix from the incoming key before composing the final storage key.

Apply this diff:

 func (bifrost *Bifrost) SetPricingData(pricingData map[string]schemas.DataSheetPricingEntry) {
-	for model, pricing := range pricingData {
-		bifrost.pricingData.Store(pricing.Provider+"/"+model, pricing)
+	for model, pricing := range pricingData {
+		normalized := model
+		prefix := pricing.Provider + "/"
+		if strings.HasPrefix(normalized, prefix) {
+			normalized = strings.TrimPrefix(normalized, prefix)
+		}
+		bifrost.pricingData.Store(pricing.Provider+"/"+normalized, pricing)
 	}
 }
framework/modelcatalog/main.go (1)

169-171: Fix pricing map key collisions across providers.

Keying the export map by model alone overwrites entries whenever two providers expose the same model name (e.g. OpenAI vs Azure wrappers), so callers receive whichever entry happened to be processed last. Use a provider-qualified key (matching what the client expects) to preserve every variant.

Apply this diff:

-		model, _, _ := splitKey(key)
-		pricingData[model] = convertTableModelPricingToPricingData(pricing)
+		model, provider, _ := splitKey(key)
+		composite := provider + "/" + model
+		pricingData[composite] = convertTableModelPricingToPricingData(pricing)
🧹 Nitpick comments (1)
framework/modelcatalog/utils.go (1)

14-17: Defend against malformed pricing keys.

splitKey panics on any key that lacks two delimiters (e.g. legacy rows, manual config edits). Add a length check and fall back safely to avoid taking down the pricing sync.

Apply this diff:

 func splitKey(key string) (string, string, string) {
 	parts := strings.Split(key, "|")
-	return parts[0], parts[1], parts[2]
+	if len(parts) < 3 {
+		return key, "", ""
+	}
+	return parts[0], parts[1], parts[2]
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49db44b and 7e6b09b.

📒 Files selected for processing (7)
  • core/bifrost.go (5 hunks)
  • core/schemas/models.go (4 hunks)
  • framework/modelcatalog/main.go (4 hunks)
  • framework/modelcatalog/sync.go (3 hunks)
  • framework/modelcatalog/utils.go (3 hunks)
  • transports/bifrost-http/lib/config.go (2 hunks)
  • transports/bifrost-http/server/server.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • transports/bifrost-http/lib/config.go
🧰 Additional context used
🧬 Code graph analysis (6)
transports/bifrost-http/server/server.go (2)
framework/modelcatalog/main.go (1)
  • Config (26-30)
transports/bifrost-http/lib/config.go (1)
  • Config (139-169)
framework/modelcatalog/sync.go (2)
framework/modelcatalog/main.go (1)
  • ModelCatalog (32-56)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
framework/modelcatalog/utils.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/schemas/models.go (2)
core/schemas/bifrost.go (1)
  • ModelProvider (32-32)
core/schemas/provider.go (1)
  • Provider (199-226)
framework/modelcatalog/main.go (1)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/bifrost.go (2)
core/schemas/models.go (1)
  • DataSheetPricingEntry (200-229)
core/schemas/bifrost.go (1)
  • ModelProvider (32-32)
🔇 Additional comments (4)
core/schemas/models.go (4)

48-108: LGTM! Well-implemented pagination with cursor validation.

The pagination logic correctly handles edge cases including nil responses, invalid cursors, empty pages, and boundary conditions. The use of opaque tokens with LastID validation ensures cursor integrity.


110-110: LGTM! Clean function type definition.

The type signature appropriately uses a pointer return to allow for optional pricing data.


136-136: LGTM! Field addition follows established patterns.

The new DeploymentName field is appropriately optional with a pointer type and correct JSON tags, consistent with other fields in the struct.


199-229: LGTM! Comprehensive and well-structured pricing data model.

The DataSheetPricingEntry struct appropriately distinguishes required fields (basic token pricing, provider, mode) from optional fields (media, character-based, 128k+, cache/batch pricing). The naming is consistent and descriptive, and the use of pointers for optional fields is correct.

Comment on lines +118 to +119
// Returns:
// - response: The response with pricing data
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix documentation: method doesn't return a value.

The documentation indicates the method returns the response, but the method signature on line 120 shows func (response *BifrostListModelsResponse) AddPricing(fetcher PricingFetcher) with no return value. The method modifies the response in-place.

Apply this diff to fix the documentation:

 // AddPricing adds pricing data to the response.
-// This is used to add pricing data to the response.
+// This method modifies the response in-place by enriching each model with pricing information.
 //
 // Parameters:
 //   - fetcher: The pricing fetcher function
-//
-// Returns:
-//   - response: The response with pricing data

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In core/schemas/models.go around lines 118 to 119, the method comment
incorrectly states the method returns the response while the signature func
(response *BifrostListModelsResponse) AddPricing(fetcher PricingFetcher) has no
return; update the doc comment to reflect that AddPricing mutates the response
in-place (for example change "Returns: - response: The response with pricing
data" to "Modifies the response in-place to include pricing data" or remove the
Returns section entirely), keeping wording concise and accurate.

Comment on lines +120 to +131
func (response *BifrostListModelsResponse) AddPricing(fetcher PricingFetcher) {
for i, modelData := range response.Data {
model := strings.TrimPrefix(modelData.ID, string(response.ExtraFields.Provider)+"/")
pricing := fetcher(model, response.ExtraFields.Provider)
if pricing != nil {
if response.Data[i].Pricing == nil {
response.Data[i].Pricing = &Pricing{}
}
response.Data[i].Pricing.DataSheetPricingEntry = pricing
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add nil check for fetcher parameter to prevent panic.

If fetcher is nil, line 123 will cause a nil pointer dereference panic. Add a defensive check at the start of the method.

Apply this diff to add the nil check:

 func (response *BifrostListModelsResponse) AddPricing(fetcher PricingFetcher) {
+	if fetcher == nil {
+		return
+	}
 	for i, modelData := range response.Data {
🤖 Prompt for AI Agents
In core/schemas/models.go around lines 120 to 131, the method AddPricing can
panic when the PricingFetcher parameter is nil because fetcher(...) is called
unguarded; add a defensive nil-check at the top of the method (e.g., if fetcher
== nil { return }) to return early when no fetcher is provided, keeping the rest
of the logic unchanged so you avoid a nil pointer dereference.

InputCacheRead *string `json:"input_cache_read,omitempty"`
InputCacheWrite *string `json:"input_cache_write,omitempty"`

*DataSheetPricingEntry
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify the intended API surface with dual pricing representations.

Embedding *DataSheetPricingEntry promotes all its fields to the Pricing JSON output, creating two sets of pricing information:

  • Existing string-based fields: Prompt, Completion, Request, etc.
  • New detailed float64-based fields: InputCostPerToken, OutputCostPerToken, etc.

This dual representation might be intentional for backward compatibility, but could confuse API consumers about which pricing data to use.

Run the following script to check if there's documentation or tests explaining the relationship between these two pricing representations:


🏁 Script executed:

#!/bin/bash
# Search for documentation or usage patterns explaining the dual pricing format

# Look for comments or docs explaining Pricing struct usage
rg -nP -C5 'type Pricing struct|Pricing.*\{' --type=go

# Look for tests that populate both old and new pricing fields
rg -nP -C3 'Pricing\s*\{' --type=go -g '*_test.go'

# Search for any migration notes or comments about pricing changes
rg -nP -C3 'pricing.*backward|backward.*pricing|pricing.*compat|legacy.*pricing' --type=go -i

Length of output: 40613


Now let me search the codebase directly to inspect the struct definition and any custom marshaling:

I need to inspect the actual codebase directly. Let me extract the specific struct definitions and check for custom marshaling:

#!/bin/bash
# Get the exact Pricing struct definition including all fields and tags
cat core/schemas/models.go | sed -n '163,180p'

# Check if Pricing or DataSheetPricingEntry have MarshalJSON methods
echo "=== Checking for MarshalJSON on Pricing ==="
rg -n "func.*Pricing.*MarshalJSON" --type=go

echo "=== Checking for MarshalJSON on DataSheetPricingEntry ==="
rg -n "func.*DataSheetPricingEntry.*MarshalJSON" --type=go

# Search for any JSON tag patterns on DataSheetPricingEntry
echo "=== DataSheetPricingEntry struct definition ==="
rg -n -A20 "type DataSheetPricingEntry struct" --type=go

#!/bin/bash
# Extract Pricing struct complete definition
echo "=== Pricing struct definition (lines 163-180) ==="
sed -n '163,180p' core/schemas/models.go

# Extract DataSheetPricingEntry struct complete definition
echo -e "\n=== DataSheetPricingEntry struct definition ==="
rg -A30 "type DataSheetPricingEntry struct" core/schemas/models.go

# Check for MarshalJSON methods
echo -e "\n=== Checking MarshalJSON implementations ==="
rg -n "func.*MarshalJSON" core/schemas/models.go

🏁 Script executed:

#!/bin/bash
# Extract Pricing struct complete definition around line 163
echo "=== Pricing struct definition ==="
sed -n '163,195p' core/schemas/models.go

# Extract DataSheetPricingEntry struct complete definition
echo -e "\n=== DataSheetPricingEntry struct definition ==="
sed -n '200,240p' core/schemas/models.go

# Check for MarshalJSON methods in the file
echo -e "\n=== MarshalJSON implementations ==="
grep -n "func.*MarshalJSON" core/schemas/models.go || echo "No MarshalJSON found"

Length of output: 3529


🏁 Script executed:

#!/bin/bash
# Search for tests that create Pricing structs and show JSON output expectations
echo "=== Tests with Pricing struct ==="
rg -n "Pricing\s*{" core/schemas/models_test.go 2>/dev/null || rg -n "Pricing" --type=go -g '*_test.go' -C2 | head -50

# Search for comments explaining the dual pricing design
echo -e "\n=== Comments near Pricing struct ==="
sed -n '155,165p' core/schemas/models.go

# Search for JSON marshaling tests
echo -e "\n=== JSON marshaling or response format documentation ==="
rg -n "json\." --type=go -C2 core/schemas/models.go | head -40

# Check if there are actual API response examples or integration tests
echo -e "\n=== Integration tests or response examples ==="
find . -name "*.go" -type f -exec grep -l "BifrostListModelsResponse.*json" {} \; 2>/dev/null | head -5

Length of output: 637


Add documentation explaining the dual pricing representation in the Pricing struct.

The embedded *DataSheetPricingEntry (line 173) promotes its fields to the Pricing JSON output due to Go's embedding behavior, creating dual pricing information: legacy string-based fields (Prompt, Completion, etc.) alongside detailed float64-based fields (InputCostPerToken, OutputCostPerToken, etc.). While this design is intentional for backward compatibility, it lacks documentation explaining the relationship between the two field sets, which could confuse API consumers about which pricing data to use.

Add inline comments to the Pricing struct explaining:

  • Why both field sets coexist
  • Which represents the current/preferred format
  • When/if legacy fields are deprecated
🤖 Prompt for AI Agents
In core/schemas/models.go around line 173, the Pricing struct embeds
*DataSheetPricingEntry which promotes legacy string-based fields into the JSON
output alongside the newer float64-based fields, causing dual pricing
representations; update the struct by adding concise inline comments that
explain: why both sets coexist (backwards compatibility), that the float64
fields (InputCostPerToken/OutputCostPerToken/etc.) are the current/preferred
format, which legacy string fields map to those values (e.g., Prompt/Completion
-> input/output) and when the legacy fields will be deprecated or removed
(include expected deprecation timeline or “TBD” if not decided), so API
consumers know which to prefer and the migration path.

@Pratham-Mishra04 Pratham-Mishra04 linked an issue Nov 4, 2025 that may be closed by this pull request
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: /v1/models endpoint not returning pricing

2 participants