-
Notifications
You must be signed in to change notification settings - Fork 98
DO NOT MERGE feat: add model pricing data to Bifrost and model responses #745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
DO NOT MERGE feat: add model pricing data to Bifrost and model responses #745
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThe changes introduce comprehensive pricing data management across the codebase. A new Changes
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (7)
🧰 Additional context used🧬 Code graph analysis (7)core/bifrost.go (3)
framework/modelcatalog/sync.go (2)
framework/modelcatalog/utils.go (1)
transports/bifrost-http/server/server.go (2)
transports/bifrost-http/lib/config.go (1)
core/schemas/models.go (2)
framework/modelcatalog/main.go (1)
Comment |
There was a problem hiding this 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
📒 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 methodGetBifrostClient()The data race concern is valid—both callbacks at lines 467–468 and 921–922 use unsynchronized direct reads of
config.clientwhileSetBifrostClient(line 1523) writes withmuMCPlock protection. However, the suggested diff cannot be applied as written becauseGetBifrostClient()does not exist in the codebase.To properly fix the data race, either:
- Create a thread-safe
GetBifrostClient()getter method that acquires themuMCPlock before returning the client, then apply the suggested diff, OR- Modify both callbacks to acquire the mutex directly, similar to how
AddMCPClientusesc.muMCP.Lock()for client field access.
| for model, pricing := range pricingData { | ||
| bifrost.pricingData.Store(pricing.Provider+"/"+model, pricing) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| if mc.pricingSyncCallback != nil { | ||
| mc.pricingSyncCallback(pricingData) | ||
| mc.logger.Debug("pricing sync callback executed") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| // 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] | ||
| } |
There was a problem hiding this comment.
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.
| // 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.
| pricingConfig.PricingSyncCallback = func(pricingData map[string]schemas.DataSheetPricingEntry) { | ||
| if config.client != nil { | ||
| config.client.SetPricingData(pricingData) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
951ed2f to
49db44b
Compare
ba5078f to
9782009
Compare
49db44b to
b31416f
Compare
9782009 to
f34e7ee
Compare
b31416f to
8fc37ed
Compare
8fc37ed to
7e6b09b
Compare
There was a problem hiding this 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
modelalone 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.
splitKeypanics 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
📒 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
DeploymentNamefield 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
DataSheetPricingEntrystruct 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.
| // Returns: | ||
| // - response: The response with pricing data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 dataCommittable 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.
| 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 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
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 -iLength 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 -5Length 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.

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
pricingDatafield to the Bifrost struct to store pricing information for modelsDataSheetPricingEntrystruct to represent detailed pricing informationBifrostListModelsResponsewith anAddPricingmethod to include pricing in model listingsDeploymentNamefield to the Model structType of change
Affected areas
How to test
Breaking changes
Related issues
Enhances model listing capabilities with pricing information
Security considerations
No security implications as this only adds informational data to model responses.
Checklist