Skip to content

Conversation

sahil913121
Copy link

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

@akshaydeo
Copy link
Contributor

@sahil913121 thanks for the PR. A few high priority things first

  1. Can you please add description in the default format and not custom format?
  2. Please add test cases and attach the results.

Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added management endpoints for OpenAI, Anthropic, and Google GenAI (models list/details, usage, organizations).
    • Direct forwarding of management requests with authentication and query parameter support.
    • Introduced API-only mode to run without the UI (enable with --api-only).
  • Improvements

    • Unified response handling and clearer error messages for management requests.
    • Enhanced logging around management routes for easier troubleshooting.

Walkthrough

Adds 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

Cohort / File(s) Summary
Schemas: management I/O additions
core/schemas/bifrost.go
Adds ManagementInput to RequestInput; adds ManagementResponse and exposes it via BifrostResponse. Note: ManagementResponse defined twice in the file.
Anthropic integration: management routes
transports/bifrost-http/integrations/anthropic/router.go
Refactors route config; adds management GET routes (/v1/models, /v1/usage); implements handleAnthropicManagementRequest using ManagementAPIClient; updates streaming converter reference.
GenAI integration: management routes
transports/bifrost-http/integrations/genai/router.go
Registers management GET routes (/v1beta/models, /v1beta/models/{model:*}) for “/genai”; adds handleGenAIManagementRequest to forward via ManagementAPIClient.
OpenAI integration: management routes
transports/bifrost-http/integrations/openai/router.go
Adds management GET routes (/v1/models, /v1/organizations, /v1/usage, /v1/models/{model}); implements handleOpenAIManagementRequest with path normalization and forwarding.
Integrations utilities: management client and helpers
transports/bifrost-http/integrations/utils.go
Introduces ManagementRequest/Response, ManagementAPIClient with ForwardRequest, ProviderEndpoints, context helpers (API key, query params), and response helpers; router handler short-circuits when management_handled is set.
HTTP server: API-only mode
transports/bifrost-http/main.go
Adds -api-only flag and apiOnly var; conditionally skips UI route registration when enabled; logs mode selection.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I thump my paws at routes anew,
Models listed, usage too—how cool!
I forward keys with whiskered grace,
Hop to providers, back apace.
API-only moonlight glow,
No UI fields, just data flow.
Carrot-coded, off we go! 🥕🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description lists the new endpoints but does not follow the repository's required template: it omits a Summary section with purpose and rationale, a detailed Changes section, Type of change and Affected areas checkboxes, How to test steps (commands and expected outcomes) with test results, Breaking changes and Security considerations, and the completion Checklist, so the description is incomplete for review. Please reformat the PR description using the repository template: add a concise Summary and detailed Changes with rationale, mark Type of change and Affected areas, provide explicit How to test steps and attach test outputs, state any breaking or security considerations, and complete the Checklist; also call out notable code changes for reviewers (for example the new exported Management types and the duplicate ManagementResponse definition).
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Add management endpoints support" is concise and accurately summarizes the primary change — adding management endpoints and associated plumbing for provider integrations — so it clearly communicates the main intent of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

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.

❤️ Share

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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 237d4a4 and e33118c.

📒 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).

Comment on lines +678 to +684
// 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"`
}

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

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.

Comment on lines +116 to +121
apiKey, err := integrations.ExtractAPIKeyFromContext(ctx)
if err != nil {
integrations.SendManagementError(ctx, err, 401)
return err
}

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

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.

Comment on lines +1239 to +1244
type ManagementResponse struct {
Data []byte `json:"data"`
StatusCode int `json:"status_code"`
Headers map[string]string `json:"headers,omitempty"`
}

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

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.

Comment on lines +1267 to +1335
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
}
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

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.

Suggested change
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
}

@sahil913121
Copy link
Author

(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.
For clarification, should I create a test case file in this same repo, or can I attach the API responses from Postman in the description?

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.

2 participants