@@ -3,6 +3,7 @@ package schemas
33import (
44 "encoding/base64"
55 "fmt"
6+ "strings"
67
78 "github.com/bytedance/sonic"
89)
@@ -44,9 +45,95 @@ type BifrostListModelsResponse struct {
4445 HasMore * bool `json:"-"`
4546}
4647
48+ // ApplyPagination applies offset-based pagination to a BifrostListModelsResponse.
49+ // Uses opaque tokens with LastID validation to ensure cursor integrity.
50+ // Returns the paginated response with properly set NextPageToken.
51+ func (response * BifrostListModelsResponse ) ApplyPagination (pageSize int , pageToken string ) * BifrostListModelsResponse {
52+ if response == nil {
53+ return nil
54+ }
55+
56+ totalItems := len (response .Data )
57+
58+ if pageSize <= 0 {
59+ return response
60+ }
61+
62+ cursor := decodePaginationCursor (pageToken )
63+ offset := cursor .Offset
64+
65+ // Validate cursor integrity if LastID is present
66+ if cursor .LastID != "" && ! validatePaginationCursor (cursor , response .Data ) {
67+ // Invalid cursor: reset to beginning
68+ offset = 0
69+ }
70+
71+ if offset >= totalItems {
72+ // Return empty page, no next token
73+ return & BifrostListModelsResponse {
74+ Data : []Model {},
75+ ExtraFields : response .ExtraFields ,
76+ NextPageToken : "" ,
77+ }
78+ }
79+
80+ endIndex := offset + pageSize
81+ if endIndex > totalItems {
82+ endIndex = totalItems
83+ }
84+
85+ paginatedData := response .Data [offset :endIndex ]
86+
87+ paginatedResponse := & BifrostListModelsResponse {
88+ Data : paginatedData ,
89+ ExtraFields : response .ExtraFields ,
90+ }
91+
92+ if endIndex < totalItems {
93+ // Get the last item ID for cursor validation
94+ var lastID string
95+ if len (paginatedData ) > 0 {
96+ lastID = paginatedData [len (paginatedData )- 1 ].ID
97+ }
98+
99+ nextToken , err := encodePaginationCursor (endIndex , lastID )
100+ if err == nil {
101+ paginatedResponse .NextPageToken = nextToken
102+ }
103+ } else {
104+ paginatedResponse .NextPageToken = ""
105+ }
106+
107+ return paginatedResponse
108+ }
109+
110+ type PricingFetcher func (model string , provider ModelProvider ) * DataSheetPricingEntry
111+
112+ // AddPricing adds pricing data to the response.
113+ // This is used to add pricing data to the response.
114+ //
115+ // Parameters:
116+ // - fetcher: The pricing fetcher function
117+ //
118+ // Returns:
119+ // - response: The response with pricing data
120+ func (response * BifrostListModelsResponse ) AddPricing (fetcher PricingFetcher ) {
121+ for i , modelData := range response .Data {
122+ model := strings .TrimPrefix (modelData .ID , string (response .ExtraFields .Provider )+ "/" )
123+ pricing := fetcher (model , response .ExtraFields .Provider )
124+ if pricing != nil {
125+ if response .Data [i ].Pricing == nil {
126+ response .Data [i ].Pricing = & Pricing {}
127+ }
128+ response .Data [i ].Pricing .DataSheetPricingEntry = pricing
129+ }
130+ }
131+ }
132+
47133type Model struct {
48134 ID string `json:"id"`
49135 CanonicalSlug * string `json:"canonical_slug,omitempty"`
136+ DeploymentName * string `json:"deployment_name,omitempty"`
50137 Name * string `json:"name,omitempty"`
51138 Created * int64 `json:"created,omitempty"`
52139 ContextLength * int `json:"context_length,omitempty"`
@@ -82,6 +169,8 @@ type Pricing struct {
82169 InternalReasoning * string `json:"internal_reasoning,omitempty"`
83170 InputCacheRead * string `json:"input_cache_read,omitempty"`
84171 InputCacheWrite * string `json:"input_cache_write,omitempty"`
172+
173+ * DataSheetPricingEntry
85174}
86175
87176type TopProvider struct {
@@ -107,6 +196,38 @@ type paginationCursor struct {
107196 LastID string `json:"l,omitempty"`
108197}
109198
199+ // PricingEntry represents a single model's pricing information
200+ type DataSheetPricingEntry struct {
201+ // Basic pricing
202+ InputCostPerToken float64 `json:"input_cost_per_token"`
203+ OutputCostPerToken float64 `json:"output_cost_per_token"`
204+ Provider string `json:"provider"`
205+ Mode string `json:"mode"`
206+
207+ // Additional pricing for media
208+ InputCostPerImage * float64 `json:"input_cost_per_image,omitempty"`
209+ InputCostPerVideoPerSecond * float64 `json:"input_cost_per_video_per_second,omitempty"`
210+ InputCostPerAudioPerSecond * float64 `json:"input_cost_per_audio_per_second,omitempty"`
211+
212+ // Character-based pricing
213+ InputCostPerCharacter * float64 `json:"input_cost_per_character,omitempty"`
214+ OutputCostPerCharacter * float64 `json:"output_cost_per_character,omitempty"`
215+
216+ // Pricing above 128k tokens
217+ InputCostPerTokenAbove128kTokens * float64 `json:"input_cost_per_token_above_128k_tokens,omitempty"`
218+ InputCostPerCharacterAbove128kTokens * float64 `json:"input_cost_per_character_above_128k_tokens,omitempty"`
219+ InputCostPerImageAbove128kTokens * float64 `json:"input_cost_per_image_above_128k_tokens,omitempty"`
220+ InputCostPerVideoPerSecondAbove128kTokens * float64 `json:"input_cost_per_video_per_second_above_128k_tokens,omitempty"`
221+ InputCostPerAudioPerSecondAbove128kTokens * float64 `json:"input_cost_per_audio_per_second_above_128k_tokens,omitempty"`
222+ OutputCostPerTokenAbove128kTokens * float64 `json:"output_cost_per_token_above_128k_tokens,omitempty"`
223+ OutputCostPerCharacterAbove128kTokens * float64 `json:"output_cost_per_character_above_128k_tokens,omitempty"`
224+
225+ // Cache and batch pricing
226+ CacheReadInputTokenCost * float64 `json:"cache_read_input_token_cost,omitempty"`
227+ InputCostPerTokenBatches * float64 `json:"input_cost_per_token_batches,omitempty"`
228+ OutputCostPerTokenBatches * float64 `json:"output_cost_per_token_batches,omitempty"`
229+ }
230+
110231// encodePaginationCursor creates an opaque base64-encoded page token from cursor data.
111232// Returns empty string if offset is 0 or negative.
112233func encodePaginationCursor (offset int , lastID string ) (string , error ) {
@@ -172,65 +293,3 @@ func validatePaginationCursor(cursor paginationCursor, data []Model) bool {
172293
173294 return true
174295}
175-
176- // ApplyPagination applies offset-based pagination to a BifrostListModelsResponse.
177- // Uses opaque tokens with LastID validation to ensure cursor integrity.
178- // Returns the paginated response with properly set NextPageToken.
179- func (response * BifrostListModelsResponse ) ApplyPagination (pageSize int , pageToken string ) * BifrostListModelsResponse {
180- if response == nil {
181- return nil
182- }
183-
184- totalItems := len (response .Data )
185-
186- if pageSize <= 0 {
187- return response
188- }
189-
190- cursor := decodePaginationCursor (pageToken )
191- offset := cursor .Offset
192-
193- // Validate cursor integrity if LastID is present
194- if cursor .LastID != "" && ! validatePaginationCursor (cursor , response .Data ) {
195- // Invalid cursor: reset to beginning
196- offset = 0
197- }
198-
199- if offset >= totalItems {
200- // Return empty page, no next token
201- return & BifrostListModelsResponse {
202- Data : []Model {},
203- ExtraFields : response .ExtraFields ,
204- NextPageToken : "" ,
205- }
206- }
207-
208- endIndex := offset + pageSize
209- if endIndex > totalItems {
210- endIndex = totalItems
211- }
212-
213- paginatedData := response .Data [offset :endIndex ]
214-
215- paginatedResponse := & BifrostListModelsResponse {
216- Data : paginatedData ,
217- ExtraFields : response .ExtraFields ,
218- }
219-
220- if endIndex < totalItems {
221- // Get the last item ID for cursor validation
222- var lastID string
223- if len (paginatedData ) > 0 {
224- lastID = paginatedData [len (paginatedData )- 1 ].ID
225- }
226-
227- nextToken , err := encodePaginationCursor (endIndex , lastID )
228- if err == nil {
229- paginatedResponse .NextPageToken = nextToken
230- }
231- } else {
232- paginatedResponse .NextPageToken = ""
233- }
234-
235- return paginatedResponse
236- }
0 commit comments