-
Notifications
You must be signed in to change notification settings - Fork 71
Add management endpoints support #493
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?
Conversation
@sahil913121 thanks for the PR. A few high priority things first
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds management API support across integrations (OpenAI, Anthropic, GenAI) with new request/response types, a forwarding client, and router pre-callbacks that short-circuit normal flows. Introduces API-only mode flag to disable UI routes. Updates schemas to include ManagementInput/Response and expose management results in BifrostResponse. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant C as Client
participant R as Router (PreCallback)
participant U as ManagementAPIClient
participant P as Provider API
participant H as HTTP Responder
C->>R: HTTP GET /{provider}/... (management path)
R->>R: Extract API key & query params
R->>U: ForwardRequest(provider, endpoint, apiKey, query)
U->>P: HTTPS GET {base}/{endpoint}?query (Auth: apiKey)
P-->>U: Status, headers, body
U-->>R: ManagementResponse
R->>H: SendManagementResponse(status, body, headers)
Note over R,H: Mark context management_handled=true<br/>Skip normal model flow
H-->>C: HTTP response
sequenceDiagram
autonumber
participant S as Server Init
participant F as Flags
participant RT as Router
S->>F: Parse -api-only
alt api-only = true
S->>RT: Register API routes only
else
S->>RT: Register API + UI routes
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
transports/bifrost-http/integrations/openai/router.go (1)
3-16
: Fix compile error (context mismatch) and reduce noisy logs.ForwardRequest expects context.Context but a fasthttp.RequestCtx is passed. Also remove verbose logs in prod paths.
Apply this diff:
@@ -import ( +import ( + "context" "errors" "fmt" "log" "strconv" "strings" @@ -func handleOpenAIManagementRequest(ctx *fasthttp.RequestCtx, req interface{}) error { - log.Printf("handleOpenAIManagementRequest called with path: %s", string(ctx.Path())) - log.Printf("Request method: %s", string(ctx.Method())) +func handleOpenAIManagementRequest(ctx *fasthttp.RequestCtx, req interface{}) error { @@ - log.Println("endpoint", endpoint) // Validate that it's a known management endpoint @@ - log.Println("endpoint", endpoint) @@ - client := integrations.NewManagementAPIClient() - response, err := client.ForwardRequest(ctx, schemas.OpenAI, endpoint, apiKey, queryParams) + client := integrations.NewManagementAPIClient() + response, err := client.ForwardRequest(context.Background(), schemas.OpenAI, endpoint, apiKey, queryParams) if err != nil { integrations.SendManagementError(ctx, err, 500) ctx.SetUserValue("management_handled", true) return nil }Also applies to: 405-470
transports/bifrost-http/integrations/genai/router.go (1)
3-14
: Fix compile error and prevent double responses on error.
- Pass a std context to ForwardRequest.
- When PreCallback writes an error, mark management_handled and return nil (don’t bubble the error).
Apply this diff:
@@ -import ( +import ( + "context" "errors" "fmt" "log" "strings" @@ func handleGenAIManagementRequest(ctx *fasthttp.RequestCtx, req interface{}) error { // Extract API key from request apiKey, err := integrations.ExtractAPIKeyFromContext(ctx) if err != nil { integrations.SendManagementError(ctx, err, 401) - return err + ctx.SetUserValue("management_handled", true) + return nil } @@ } else if strings.Contains(path, "/v1beta/models/") { // Model details endpoint - extract model from path model := ctx.UserValue("model") if model == nil { - integrations.SendManagementError(ctx, fmt.Errorf("model parameter is required"), 400) - return fmt.Errorf("model parameter is required") + err := fmt.Errorf("model parameter is required") + integrations.SendManagementError(ctx, err, 400) + ctx.SetUserValue("management_handled", true) + return nil } modelStr := model.(string) endpoint = "/v1beta/models/" + modelStr } else { - integrations.SendManagementError(ctx, fmt.Errorf("unknown management endpoint"), 404) - return fmt.Errorf("unknown management endpoint") + integrations.SendManagementError(ctx, fmt.Errorf("unknown management endpoint"), 404) + ctx.SetUserValue("management_handled", true) + return nil } @@ // Create management client and forward the request client := integrations.NewManagementAPIClient() - response, err := client.ForwardRequest(ctx, schemas.Gemini, endpoint, apiKey, queryParams) + response, err := client.ForwardRequest(context.Background(), schemas.Gemini, endpoint, apiKey, queryParams) if err != nil { integrations.SendManagementError(ctx, err, 500) ctx.SetUserValue("management_handled", true) return nil }Also applies to: 169-215
🧹 Nitpick comments (9)
core/schemas/bifrost.go (1)
202-208
: Clarify constraints on ManagementInput.Endpoint.Document that Endpoint is a provider‑relative path (not a full URL) to avoid SSRF risks and keep forwarding logic consistent. Consider renaming to Path for clarity.
transports/bifrost-http/integrations/utils.go (2)
1217-1236
: YAGNI: ManagementRequest.ConvertToBifrostRequest seems unused.GET routes don’t parse bodies and PreCallback fully handles forwarding. Consider removing ConvertToBifrostRequest to reduce surface area.
1368-1374
: Propagate useful upstream headers.Consider forwarding safe headers (e.g., Content-Type, X-Request-Id, rate‑limit headers) from response.Headers to client for better UX.
transports/bifrost-http/main.go (1)
528-535
: Misleading log: UI routes not actually registered.Logs say “Registering UI routes” but routes are commented. Either register or drop the log.
Apply this diff to register existing uiHandler when not api-only:
if !apiOnly { - logger.Info("Registering UI routes") - // r.GET("/", uiHandler) - // r.GET("/{filepath:*}", uiHandler) + logger.Info("Registering UI routes") + r.GET("/", uiHandler) + r.GET("/{filepath:*}", uiHandler) } else { logger.Info("API-only mode: UI routes disabled") }transports/bifrost-http/integrations/genai/router.go (1)
169-215
: GenAI auth header format check.Google GenAI typically requires X-Goog-Api-Key (or key= query) rather than Authorization: Bearer for simple API keys. Ensure ForwardRequest sets the correct auth (see utils.go suggestion).
transports/bifrost-http/integrations/anthropic/router.go (4)
85-93
: Management RequestConverter: consider setting ManagementInput to align with schema.
Not required for PreCallback short‑circuit, but more semantically accurate for future handling.
94-105
: Placeholder converters may mislead if PreCallback isn’t triggered.
If the request ever falls through, returning an empty “list” for both models and usage is incorrect. Either:
- Make these return types clearly generic (e.g., passthrough based on resp.Management), or
- Ensure PreCallback always short‑circuits (see next comment).
138-141
: Reuse a persistent ManagementAPIClient to retain HTTP connection pooling.
Creating a new client per request forfeits pooling and adds TLS/connect overhead.Apply this diff in the handler:
- client := integrations.NewManagementAPIClient() - response, err := client.ForwardRequest(ctx, schemas.Anthropic, endpoint, apiKey, queryParams) + client := managementClient + response, err := client.ForwardRequest(ctx, schemas.Anthropic, endpoint, apiKey, queryParams)Add this package‑level variable (outside the function, after imports):
var managementClient = integrations.NewManagementAPIClient()
147-151
: Trim success logging or make it more structured.
The status log on every successful request can be noisy. Either remove it or include endpoint and keep it minimal.Apply this diff to improve the line:
- log.Printf("Response status code: %v", response.StatusCode) + log.Printf("Anthropic management %s -> status=%d", endpoint, response.StatusCode)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
core/schemas/bifrost.go
(4 hunks)transports/bifrost-http/integrations/anthropic/router.go
(3 hunks)transports/bifrost-http/integrations/genai/router.go
(3 hunks)transports/bifrost-http/integrations/openai/router.go
(3 hunks)transports/bifrost-http/integrations/utils.go
(3 hunks)transports/bifrost-http/main.go
(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
transports/bifrost-http/integrations/genai/router.go (2)
transports/bifrost-http/integrations/utils.go (10)
RouteConfig
(145-156)ManagementRequest
(1218-1223)RequestConverter
(84-84)ResponseConverter
(88-88)ErrorConverter
(96-96)ExtractAPIKeyFromContext
(1338-1357)SendManagementError
(1376-1388)ExtractQueryParams
(1360-1366)NewManagementAPIClient
(1251-1257)SendManagementResponse
(1369-1373)core/schemas/bifrost.go (5)
BifrostRequest
(212-222)Gemini
(53-53)RequestInput
(117-124)BifrostResponse
(453-467)BifrostError
(784-793)
transports/bifrost-http/integrations/openai/router.go (2)
transports/bifrost-http/integrations/utils.go (10)
RouteConfig
(145-156)ManagementRequest
(1218-1223)RequestConverter
(84-84)ResponseConverter
(88-88)ErrorConverter
(96-96)ExtractAPIKeyFromContext
(1338-1357)SendManagementError
(1376-1388)ExtractQueryParams
(1360-1366)NewManagementAPIClient
(1251-1257)SendManagementResponse
(1369-1373)core/schemas/bifrost.go (5)
BifrostRequest
(212-222)OpenAI
(41-41)RequestInput
(117-124)BifrostResponse
(453-467)BifrostError
(784-793)
core/schemas/bifrost.go (2)
ui/lib/types/logs.ts (1)
TranscriptionInput
(16-22)transports/bifrost-http/integrations/utils.go (1)
ManagementResponse
(1239-1243)
transports/bifrost-http/integrations/utils.go (1)
core/schemas/bifrost.go (7)
ModelProvider
(38-38)BifrostRequest
(212-222)RequestInput
(117-124)OpenAI
(41-41)Anthropic
(43-43)Gemini
(53-53)ManagementResponse
(679-683)
transports/bifrost-http/integrations/anthropic/router.go (2)
transports/bifrost-http/integrations/utils.go (11)
RouteConfig
(145-156)RequestConverter
(84-84)ResponseConverter
(88-88)ErrorConverter
(96-96)StreamConfig
(138-141)ManagementRequest
(1218-1223)ExtractAPIKeyFromContext
(1338-1357)SendManagementError
(1376-1388)ExtractQueryParams
(1360-1366)NewManagementAPIClient
(1251-1257)SendManagementResponse
(1369-1373)core/schemas/bifrost.go (5)
BifrostRequest
(212-222)BifrostResponse
(453-467)BifrostError
(784-793)Anthropic
(43-43)RequestInput
(117-124)
🔇 Additional comments (11)
core/schemas/bifrost.go (2)
122-124
: LGTM: request surface extended safely.Adding ManagementInput behind omitempty is non-breaking.
460-460
: LGTM: response surface extended.Management added to BifrostResponse with omitempty—safe addition.
transports/bifrost-http/integrations/utils.go (1)
609-613
: Good short‑circuit hook for management requests.This prevents double handling after PreCallback. Ensure all management PreCallbacks set management_handled on both success and error. GenAI handler currently misses this in some error paths (see comments there).
transports/bifrost-http/integrations/openai/router.go (1)
239-245
: LGTM: management routes gated to the primary prefix.Scope limited to /openai avoids duplicates in aliased paths.
transports/bifrost-http/integrations/genai/router.go (1)
55-58
: LGTM: management routes gated to primary GenAI prefix.transports/bifrost-http/integrations/anthropic/router.go (6)
5-7
: LGTM: imports align with management routing needs.Also applies to: 13-13
25-52
: LGTM: messages route wiring and stream converters remain correct.
54-57
: Good gate: only register management routes for the primary /anthropic prefix.
73-82
: Route construction for management endpoints looks correct (GET, paths, factory).
106-107
: LGTM: PreCallback hook is correctly attached to management routes.
81-87
: Verified — GET routes skip body parsing; no-op RequestParser not required.
GenericRouter only parses the body when method != GET && method != DELETE and still invokes PreCallback before the management_handled check (transports/bifrost-http/integrations/utils.go:575–613).
// ManagementResponse represents the response from a management API call | ||
type ManagementResponse struct { | ||
Data []byte `json:"data"` | ||
StatusCode int `json:"status_code"` | ||
Headers map[string]string `json:"headers,omitempty"` | ||
} | ||
|
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
Deduplicate ManagementResponse type (reuse schemas across packages).
An identical ManagementResponse exists in integrations/utils.go. Keep a single source of truth here and remove the duplicate from integrations to prevent drift.
Apply this diff in transports/bifrost-http/integrations/utils.go to reuse the schema type:
-// ManagementResponse represents the response from a management API call
-type ManagementResponse struct {
- Data []byte `json:"data"`
- StatusCode int `json:"status_code"`
- Headers map[string]string `json:"headers,omitempty"`
-}
And update ForwardRequest’s return type:
-func (c *ManagementAPIClient) ForwardRequest(
+func (c *ManagementAPIClient) ForwardRequest(
ctx context.Context,
provider schemas.ModelProvider,
endpoint string,
apiKey string,
queryParams map[string]string,
-) (*ManagementResponse, error) {
+) (*schemas.ManagementResponse, error) {
Also update the return statement:
- return &ManagementResponse{
+ return &schemas.ManagementResponse{
Data: body,
StatusCode: resp.StatusCode,
Headers: headers,
}, nil
🤖 Prompt for AI Agents
In core/schemas/bifrost.go around lines 678-684 the ManagementResponse struct is
defined; remove the duplicate ManagementResponse definition from
transports/bifrost-http/integrations/utils.go and instead import the
core/schemas package there. Update the ForwardRequest function signature to
return (schemas.ManagementResponse, error) (or *schemas.ManagementResponse if
pointers are used across the codebase) and adjust its return statement to
construct and return a schemas.ManagementResponse (mapping data, status code and
headers into the core schema) before returning, ensuring any header/map types
are converted to match the core type.
apiKey, err := integrations.ExtractAPIKeyFromContext(ctx) | ||
if err != nil { | ||
integrations.SendManagementError(ctx, err, 401) | ||
return err | ||
} | ||
|
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.
Short‑circuit after writing errors to avoid double handling.
PreCallback currently writes an error response then returns a non‑nil error without marking the request as handled for API‑key missing and unknown‑endpoint branches. This risks duplicate handling by the GenericRouter.
Apply this diff to consistently mark as handled and return nil after writing the response:
@@
- if err != nil {
- integrations.SendManagementError(ctx, err, 401)
- return err
- }
+ if err != nil {
+ integrations.SendManagementError(ctx, err, 401)
+ ctx.SetUserValue("management_handled", true)
+ return nil
+ }
@@
- default:
- integrations.SendManagementError(ctx, fmt.Errorf("unknown management endpoint"), 404)
- return fmt.Errorf("unknown management endpoint")
+ default:
+ err = fmt.Errorf("unknown management endpoint")
+ integrations.SendManagementError(ctx, err, 404)
+ ctx.SetUserValue("management_handled", true)
+ return nil
Also applies to: 133-136
🤖 Prompt for AI Agents
transports/bifrost-http/integrations/anthropic/router.go lines 116-121 (and
similarly 133-136): after writing an error response with
integrations.SendManagementError(ctx, err, ...) you must mark the request as
handled and return nil to avoid double handling by GenericRouter; update both
branches so that immediately after SendManagementError you call the codepath
that marks the context/request as handled (e.g.,
integrations.MarkRequestHandled(ctx) or set the handled flag on ctx) and then
return nil instead of returning the original error.
type ManagementResponse struct { | ||
Data []byte `json:"data"` | ||
StatusCode int `json:"status_code"` | ||
Headers map[string]string `json:"headers,omitempty"` | ||
} | ||
|
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
Remove duplicate ManagementResponse (use schemas.ManagementResponse).
See schemas comment; keep the single definition in core.
Apply this diff:
-// ManagementResponse represents the response from a management API call
-type ManagementResponse struct {
- Data []byte `json:"data"`
- StatusCode int `json:"status_code"`
- Headers map[string]string `json:"headers,omitempty"`
-}
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In transports/bifrost-http/integrations/utils.go around lines 1239 to 1244,
remove the duplicate ManagementResponse struct definition and replace its usage
with the canonical type from the core schemas package
(schemas.ManagementResponse); delete the type block, update any local references
to use schemas.ManagementResponse, and add or adjust the import for the schemas
package if it isn't already imported.
func (c *ManagementAPIClient) ForwardRequest( | ||
ctx context.Context, | ||
provider schemas.ModelProvider, | ||
endpoint string, | ||
apiKey string, | ||
queryParams map[string]string, | ||
) (*ManagementResponse, error) { | ||
baseURL, exists := ProviderEndpoints[provider] | ||
if !exists { | ||
return nil, fmt.Errorf("unsupported provider: %s", provider) | ||
} | ||
log.Println("baseURL", baseURL) | ||
|
||
// Build the full URL | ||
fullURL := baseURL + endpoint | ||
|
||
// Add query parameters if any | ||
if len(queryParams) > 0 { | ||
fullURL += "?" | ||
first := true | ||
for key, value := range queryParams { | ||
if !first { | ||
fullURL += "&" | ||
} | ||
fullURL += fmt.Sprintf("%s=%s", key, value) | ||
first = false | ||
} | ||
} | ||
|
||
// Create the HTTP request | ||
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create request: %w", err) | ||
} | ||
|
||
// Set the authorization header | ||
req.Header.Set("Authorization", "Bearer "+apiKey) | ||
req.Header.Set("Content-Type", "application/json") | ||
req.Header.Set("User-Agent", "Bifrost-Management-Client/1.0") | ||
|
||
// Make the request | ||
resp, err := c.httpClient.Do(req) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to make request: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
// Read the response body | ||
body, err := io.ReadAll(resp.Body) | ||
log.Printf("Response body: %s", string(body)) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to read response body: %w", err) | ||
} | ||
|
||
// Extract headers | ||
headers := make(map[string]string) | ||
for key, values := range resp.Header { | ||
if len(values) > 0 { | ||
headers[key] = values[0] | ||
} | ||
} | ||
log.Printf("Response status code: %d", resp.StatusCode) | ||
log.Printf("Response headers: %v", resp.Header) | ||
return &ManagementResponse{ | ||
Data: body, | ||
StatusCode: resp.StatusCode, | ||
Headers: headers, | ||
}, nil | ||
} |
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 URL building, sensitive logging, and provider auth headers.
- Don’t string‑concatenate query params; use net/url to encode safely.
- Avoid logging full response bodies/headers (privacy/noise).
- Gemini typically expects API keys via X-Goog-Api-Key (or key= query), not Authorization: Bearer.
Apply these diffs:
@@
-func (c *ManagementAPIClient) ForwardRequest(
+func (c *ManagementAPIClient) ForwardRequest(
ctx context.Context,
provider schemas.ModelProvider,
endpoint string,
apiKey string,
queryParams map[string]string,
-) (*ManagementResponse, error) {
+) (*schemas.ManagementResponse, error) {
baseURL, exists := ProviderEndpoints[provider]
if !exists {
return nil, fmt.Errorf("unsupported provider: %s", provider)
}
- log.Println("baseURL", baseURL)
- // Build the full URL
- fullURL := baseURL + endpoint
-
- // Add query parameters if any
- if len(queryParams) > 0 {
- fullURL += "?"
- first := true
- for key, value := range queryParams {
- if !first {
- fullURL += "&"
- }
- fullURL += fmt.Sprintf("%s=%s", key, value)
- first = false
- }
- }
+ // Build the full URL with proper query encoding
+ u, err := url.Parse(baseURL + endpoint)
+ if err != nil {
+ return nil, fmt.Errorf("invalid base URL or endpoint: %w", err)
+ }
+ q := u.Query()
+ for key, value := range queryParams {
+ q.Set(key, value)
+ }
+ u.RawQuery = q.Encode()
+ fullURL := u.String()
@@
- // Set the authorization header
- req.Header.Set("Authorization", "Bearer "+apiKey)
+ // Set auth headers per provider
+ switch provider {
+ case schemas.Gemini:
+ // Public GenAI APIs accept API key via header; OAuth Bearer also works but is different.
+ req.Header.Set("X-Goog-Api-Key", apiKey)
+ default:
+ req.Header.Set("Authorization", "Bearer "+apiKey)
+ }
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Bifrost-Management-Client/1.0")
@@
- body, err := io.ReadAll(resp.Body)
- log.Printf("Response body: %s", string(body))
+ body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
@@
- log.Printf("Response status code: %d", resp.StatusCode)
- log.Printf("Response headers: %v", resp.Header)
- return &ManagementResponse{
+ return &schemas.ManagementResponse{
Data: body,
StatusCode: resp.StatusCode,
Headers: headers,
}, nil
And add missing import:
@@
- "net/http"
+ "net/http"
+ "net/url"
📝 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.
func (c *ManagementAPIClient) ForwardRequest( | |
ctx context.Context, | |
provider schemas.ModelProvider, | |
endpoint string, | |
apiKey string, | |
queryParams map[string]string, | |
) (*ManagementResponse, error) { | |
baseURL, exists := ProviderEndpoints[provider] | |
if !exists { | |
return nil, fmt.Errorf("unsupported provider: %s", provider) | |
} | |
log.Println("baseURL", baseURL) | |
// Build the full URL | |
fullURL := baseURL + endpoint | |
// Add query parameters if any | |
if len(queryParams) > 0 { | |
fullURL += "?" | |
first := true | |
for key, value := range queryParams { | |
if !first { | |
fullURL += "&" | |
} | |
fullURL += fmt.Sprintf("%s=%s", key, value) | |
first = false | |
} | |
} | |
// Create the HTTP request | |
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil) | |
if err != nil { | |
return nil, fmt.Errorf("failed to create request: %w", err) | |
} | |
// Set the authorization header | |
req.Header.Set("Authorization", "Bearer "+apiKey) | |
req.Header.Set("Content-Type", "application/json") | |
req.Header.Set("User-Agent", "Bifrost-Management-Client/1.0") | |
// Make the request | |
resp, err := c.httpClient.Do(req) | |
if err != nil { | |
return nil, fmt.Errorf("failed to make request: %w", err) | |
} | |
defer resp.Body.Close() | |
// Read the response body | |
body, err := io.ReadAll(resp.Body) | |
log.Printf("Response body: %s", string(body)) | |
if err != nil { | |
return nil, fmt.Errorf("failed to read response body: %w", err) | |
} | |
// Extract headers | |
headers := make(map[string]string) | |
for key, values := range resp.Header { | |
if len(values) > 0 { | |
headers[key] = values[0] | |
} | |
} | |
log.Printf("Response status code: %d", resp.StatusCode) | |
log.Printf("Response headers: %v", resp.Header) | |
return &ManagementResponse{ | |
Data: body, | |
StatusCode: resp.StatusCode, | |
Headers: headers, | |
}, nil | |
} | |
func (c *ManagementAPIClient) ForwardRequest( | |
ctx context.Context, | |
provider schemas.ModelProvider, | |
endpoint string, | |
apiKey string, | |
queryParams map[string]string, | |
) (*schemas.ManagementResponse, error) { | |
baseURL, exists := ProviderEndpoints[provider] | |
if !exists { | |
return nil, fmt.Errorf("unsupported provider: %s", provider) | |
} | |
// Build the full URL with proper query encoding | |
u, err := url.Parse(baseURL + endpoint) | |
if err != nil { | |
return nil, fmt.Errorf("invalid base URL or endpoint: %w", err) | |
} | |
q := u.Query() | |
for key, value := range queryParams { | |
q.Set(key, value) | |
} | |
u.RawQuery = q.Encode() | |
fullURL := u.String() | |
// Create the HTTP request | |
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil) | |
if err != nil { | |
return nil, fmt.Errorf("failed to create request: %w", err) | |
} | |
// Set auth headers per provider | |
switch provider { | |
case schemas.Gemini: | |
// Public GenAI APIs accept API key via header; OAuth Bearer also works but is different. | |
req.Header.Set("X-Goog-Api-Key", apiKey) | |
default: | |
req.Header.Set("Authorization", "Bearer "+apiKey) | |
} | |
req.Header.Set("Content-Type", "application/json") | |
req.Header.Set("User-Agent", "Bifrost-Management-Client/1.0") | |
// Make the request | |
resp, err := c.httpClient.Do(req) | |
if err != nil { | |
return nil, fmt.Errorf("failed to make request: %w", err) | |
} | |
defer resp.Body.Close() | |
// Read the response body | |
body, err := io.ReadAll(resp.Body) | |
if err != nil { | |
return nil, fmt.Errorf("failed to read response body: %w", err) | |
} | |
// Extract headers | |
headers := make(map[string]string) | |
for key, values := range resp.Header { | |
if len(values) > 0 { | |
headers[key] = values[0] | |
} | |
} | |
return &schemas.ManagementResponse{ | |
Data: body, | |
StatusCode: resp.StatusCode, | |
Headers: headers, | |
}, nil | |
} |
(1) I will update the description as per the default format. Could you please let me know where I can find the default description format? (2) I will add the test cases as requested. |
This PR adds new endpoints to fetch model lists and usage statistics from multiple providers.
Newly Added Endpoints
OpenAI
GET /openai/v1/models → List available OpenAI models
GET /openai/v1/models/{model} → Get details for a specific OpenAI model
GET /openai/v1/organizations → Fetch organization info
GET /openai/v1/usage → Fetch usage statistics
Anthropic (Claude)
GET /anthropic/v1/models → List Claude models
GET /anthropic/v1/usage → Fetch usage statistics
Google Gemini
GET /genai/v1beta/models → List Gemini models
GET /genai/v1beta/models/{model} → Get details for a specific Gemini model