diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..7223b342a2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +insert_final_newline = false +end_of_line = lf +charset = utf-8 + +[*.{js,jsx,ts,tsx,mjs,json,md,css,scss,html}] +insert_final_newline = false diff --git a/.github/workflows/release-pipeline.yml b/.github/workflows/release-pipeline.yml index 27dc4599ca..99fa0919dc 100644 --- a/.github/workflows/release-pipeline.yml +++ b/.github/workflows/release-pipeline.yml @@ -3,12 +3,12 @@ name: Release Pipeline # Triggers automatically on push to main when any version file changes on: push: - branches: ["main"] + branches: ["main"] # Prevent concurrent runs concurrency: group: release-pipeline - cancel-in-progress: false + cancel-in-progress: false jobs: # Check if pipeline should be skipped based on first line of commit message @@ -57,7 +57,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y jq - + - name: Detect what needs release id: detect run: ./.github/workflows/scripts/detect-all-changes.sh "auto" @@ -140,7 +140,7 @@ jobs: - name: Release framework id: release env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} run: ./.github/workflows/scripts/release-framework.sh "${{ needs.detect-changes.outputs.framework-version }}" plugins-release: @@ -268,7 +268,7 @@ jobs: IMAGE_NAME: bifrost steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -291,6 +291,8 @@ jobs: uses: docker/build-push-action@v5 with: context: . + build-args: | + VERSION=${{ needs.detect-changes.outputs.transport-version }} file: ./transports/Dockerfile push: true tags: ${{ steps.tags.outputs.tags }} @@ -311,7 +313,7 @@ jobs: IMAGE_NAME: bifrost steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -336,12 +338,14 @@ jobs: context: . file: ./transports/Dockerfile push: true + build-args: | + VERSION=${{ needs.detect-changes.outputs.transport-version }} tags: ${{ steps.tags.outputs.tags }} platforms: linux/arm64 cache-from: type=gha cache-to: type=gha,mode=max - # Docker manifest + # Docker manifest docker-manifest: needs: [check-skip, detect-changes, docker-build-amd64, docker-build-arm64] if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.docker-build-amd64.result == 'success' && needs.docker-build-arm64.result == 'success'" @@ -352,8 +356,8 @@ jobs: IMAGE_NAME: bifrost steps: - name: Checkout repository - uses: actions/checkout@v4 - + uses: actions/checkout@v4 + - name: Log in to Docker Hub uses: docker/login-action@v3 with: @@ -362,8 +366,8 @@ jobs: - name: Create and push multi-arch manifest run: | - ./.github/workflows/scripts/create-docker-manifest.sh "${{ needs.detect-changes.outputs.transport-version }}" - + ./.github/workflows/scripts/create-docker-manifest.sh "${{ needs.detect-changes.outputs.transport-version }}" + # Notification notify: needs: [check-skip, detect-changes, core-release, framework-release, plugins-release, bifrost-http-release, docker-manifest] @@ -375,7 +379,7 @@ jobs: sudo apt-get update sudo apt-get install -y jq - - name: Discord Notification + - name: Discord Notification env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} run: | diff --git a/.github/workflows/scripts/build-executables.sh b/.github/workflows/scripts/build-executables.sh index dd8c3158da..763306c5ec 100755 --- a/.github/workflows/scripts/build-executables.sh +++ b/.github/workflows/scripts/build-executables.sh @@ -2,9 +2,16 @@ set -euo pipefail # Cross-compile Go binaries for multiple platforms -# Usage: ./build-executables.sh +# Usage: ./build-executables.sh -echo "šŸ”Ø Building Go executables..." +# Require version argument (matches usage) +if [[ -z "${1:-}" ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi +VERSION="$1" + +echo "šŸ”Ø Building Go executables with version: $VERSION" # Get the script directory and project root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -14,6 +21,7 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" rm -rf "$PROJECT_ROOT/dist" mkdir -p "$PROJECT_ROOT/dist" + # Define platforms platforms=( "darwin/amd64" @@ -55,8 +63,8 @@ for platform in "${platforms[@]}"; do fi env GOWORK=off CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" CC="$CC_COMPILER" CXX="$CXX_COMPILER" \ - go build -trimpath -tags "netgo,osusergo" \ - -ldflags "-s -w -buildid=" \ + go build -trimpath -tags "netgo,osusergo,sqlite_static" \ + -ldflags "-s -w -buildid= -extldflags '-static' -X main.Version=${VERSION}" \ -o "$PROJECT_ROOT/dist/$PLATFORM_DIR/$GOARCH/$output_name" . elif [[ "$GOOS" = "windows" ]]; then @@ -66,7 +74,7 @@ for platform in "${platforms[@]}"; do fi env GOWORK=off CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" CC="$CC_COMPILER" CXX="$CXX_COMPILER" \ - go build -trimpath -ldflags "-s -w -buildid=" \ + go build -trimpath -ldflags "-s -w -buildid= -X main.Version=${VERSION}" \ -o "$PROJECT_ROOT/dist/$PLATFORM_DIR/$GOARCH/$output_name" . else # Darwin (macOS) @@ -79,7 +87,7 @@ for platform in "${platforms[@]}"; do fi env GOWORK=off CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" CC="$CC_COMPILER" CXX="$CXX_COMPILER" \ - go build -trimpath -ldflags "-s -w -buildid=" \ + go build -trimpath -ldflags "-s -w -buildid= -X main.Version=${VERSION}" \ -o "$PROJECT_ROOT/dist/$PLATFORM_DIR/$GOARCH/$output_name" . fi diff --git a/.github/workflows/scripts/release-bifrost-http.sh b/.github/workflows/scripts/release-bifrost-http.sh index 07fccb0091..a661189204 100755 --- a/.github/workflows/scripts/release-bifrost-http.sh +++ b/.github/workflows/scripts/release-bifrost-http.sh @@ -49,14 +49,14 @@ declare -A PLUGIN_VERSIONS for plugin_dir in plugins/*/; do if [ -d "$plugin_dir" ]; then plugin_name=$(basename "$plugin_dir") - + # Check if VERSION parameter contains prerelease suffix if [[ "$VERSION" == *"-"* ]]; then # VERSION has prerelease, so include all versions but prefer stable ALL_TAGS=$(git tag -l "plugins/${plugin_name}/v*" | sort -V) STABLE_TAGS=$(echo "$ALL_TAGS" | grep -v '\-') PRERELEASE_TAGS=$(echo "$ALL_TAGS" | grep '\-') - + if [ -n "$STABLE_TAGS" ]; then # Get the highest stable version LATEST_PLUGIN_TAG=$(echo "$STABLE_TAGS" | tail -1) @@ -71,7 +71,7 @@ for plugin_dir in plugins/*/; do LATEST_PLUGIN_TAG=$(git tag -l "plugins/${plugin_name}/v*" | grep -v '\-' | sort -V | tail -1) echo "latest plugin tag (stable only): $LATEST_PLUGIN_TAG" fi - + if [ -z "$LATEST_PLUGIN_TAG" ]; then # No matching release found, use version from file PLUGIN_VERSION="v$(tr -d '\n\r' < "${plugin_dir}version")" @@ -80,7 +80,7 @@ for plugin_dir in plugins/*/; do PLUGIN_VERSION=${LATEST_PLUGIN_TAG#plugins/${plugin_name}/} echo " šŸ“¦ $plugin_name: $PLUGIN_VERSION (latest release)" fi - + PLUGIN_VERSIONS["$plugin_name"]="$PLUGIN_VERSION" fi done @@ -92,7 +92,7 @@ echo "šŸ” Checking for additional plugins in transport go.mod..." while IFS= read -r plugin_line; do plugin_name=$(echo "$plugin_line" | awk -F'/' '{print $NF}' | awk '{print $1}') current_version=$(echo "$plugin_line" | awk '{print $NF}') - + # Only add if we don't already have this plugin if [[ -z "${PLUGIN_VERSIONS[$plugin_name]:-}" ]]; then echo " šŸ“¦ $plugin_name: $current_version (from transport go.mod)" @@ -117,7 +117,7 @@ PLUGINS_USED=() cd transports for plugin_name in "${!PLUGIN_VERSIONS[@]}"; do plugin_version="${PLUGIN_VERSIONS[$plugin_name]}" - + # Check if transport depends on this plugin if grep -q "github.com/maximhq/bifrost/plugins/$plugin_name" go.mod; then echo " šŸ“¦ Using $plugin_name plugin $plugin_version" @@ -131,7 +131,7 @@ done echo " šŸ”§ Updating core to $CORE_VERSION" go_get_with_backoff "github.com/maximhq/bifrost/core@$CORE_VERSION" -echo " šŸ“¦ Updating framework to $FRAMEWORK_VERSION" +echo " šŸ“¦ Updating framework to $FRAMEWORK_VERSION" go_get_with_backoff "github.com/maximhq/bifrost/framework@$FRAMEWORK_VERSION" go mod tidy @@ -157,7 +157,7 @@ if ! git diff --cached --quiet; then git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" echo "šŸ”§ Committing and pushing changes..." - git commit -m "transports: update dependencies --skip-pipeline" + git commit -m "transports: update dependencies --skip-pipeline" git push -u origin HEAD else echo "ā„¹ļø No staged changes to commit" @@ -169,7 +169,7 @@ bash ./.github/workflows/scripts/install-cross-compilers.sh # Build Go executables echo "šŸ”Ø Building executables..." -bash ./.github/workflows/scripts/build-executables.sh +bash ./.github/workflows/scripts/build-executables.sh $VERSION # Configure and upload to R2 echo "šŸ“¤ Uploading binaries..." diff --git a/.prettierrc b/.prettierrc index 11d2b912ce..4da40ee345 100644 --- a/.prettierrc +++ b/.prettierrc @@ -20,5 +20,6 @@ "tailwindFunctions": [ "cn", "classNames" - ] + ], + "endOfLine": "lf" } \ No newline at end of file diff --git a/Makefile b/Makefile index 356cde8f8b..79ce1b7e8e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # Variables HOST ?= localhost PORT ?= 8080 -APP_DIR ?= +APP_DIR ?= PROMETHEUS_LABELS ?= LOG_STYLE ?= json LOG_LEVEL ?= info @@ -16,7 +16,7 @@ BLUE=\033[0;34m CYAN=\033[0;36m NC=\033[0m # No Color -.PHONY: all help dev build run install-air clean test install-ui setup-workspace work-init work-clean docs docker-build +.PHONY: all help dev build-ui build run install-air clean test install-ui setup-workspace work-init work-clean docs docker-build all: help @@ -77,13 +77,13 @@ build: build-ui ## Build bifrost-http binary @echo "$(GREEN)Built: tmp/bifrost-http$(NC)" docker-build: build-ui ## Build Docker image - @echo "$(GREEN)Building Docker image...$(NC)" + @echo "$(GREEN)Building Docker image...$(NC)" @docker build -f transports/Dockerfile -t bifrost . @echo "$(GREEN)Docker image built: bifrost$(NC)" docker-run: ## Run Docker container @echo "$(GREEN)Running Docker container...$(NC)" - @docker run -e APP_PORT=$(PORT) -e APP_HOST=0.0.0.0 -p $(PORT):$(PORT) -e LOG_LEVEL=$(LOG_LEVEL) -e LOG_STYLE=$(LOG_STYLE) -v $(shell pwd):/app/data bifrost + @docker run -e APP_PORT=$(PORT) -e APP_HOST=0.0.0.0 -p $(PORT):$(PORT) -e LOG_LEVEL=$(LOG_LEVEL) -e LOG_STYLE=$(LOG_STYLE) -v $(shell pwd):/app/data bifrost docs: ## Prepare local docs @echo "$(GREEN)Preparing local docs...$(NC)" diff --git a/transports/Dockerfile b/transports/Dockerfile index dd9b351286..9e6660270e 100644 --- a/transports/Dockerfile +++ b/transports/Dockerfile @@ -1,5 +1,5 @@ # --- UI Build Stage: Build the Next.js frontend --- -FROM node:24-alpine AS ui-builder +FROM node:24-alpine3.22 AS ui-builder WORKDIR /app # Copy UI package files and install dependencies @@ -15,7 +15,7 @@ RUN node scripts/fix-paths.js # Skip the copy-build step since we'll copy the files in the Go build stage # --- Go Build Stage: Compile the Go binary --- -FROM golang:1.24-alpine AS builder +FROM golang:1.24-alpine3.22 AS builder WORKDIR /app # Install dependencies including gcc for CGO and sqlite @@ -36,12 +36,13 @@ COPY --from=ui-builder /app/out ./bifrost-http/ui # Build the binary with CGO enabled and static SQLite linking ENV GOWORK=off +ARG VERSION=unknown RUN go build \ - -ldflags="-w -s -extldflags '-static'" \ - -a -trimpath \ - -tags "sqlite_static" \ - -o /app/main \ - ./bifrost-http + -ldflags="-w -s -extldflags '-static' -X main.Version=${VERSION}" \ + -a -trimpath \ + -tags "sqlite_static" \ + -o /app/main \ + ./bifrost-http # Compress binary with upx RUN upx --best --lzma /app/main @@ -50,7 +51,7 @@ RUN upx --best --lzma /app/main RUN test -f /app/main || (echo "Build failed" && exit 1) # --- Runtime Stage: Minimal runtime image --- -FROM alpine:3.19 +FROM alpine:3.22 WORKDIR /app # Create data directory and set up user diff --git a/transports/bifrost-http/handlers/cache.go b/transports/bifrost-http/handlers/cache.go index d4e2d6fa9c..931c6b0688 100644 --- a/transports/bifrost-http/handlers/cache.go +++ b/transports/bifrost-http/handlers/cache.go @@ -25,12 +25,12 @@ func NewCacheHandler(plugin schemas.Plugin, logger schemas.Logger) *CacheHandler } func (h *CacheHandler) RegisterRoutes(r *router.Router) { - r.DELETE("/api/cache/clear/{request-id}", h.ClearCache) - r.DELETE("/api/cache/clear-by-key/{cache-key}", h.ClearCacheByKey) + r.DELETE("/api/cache/clear/{requestId}", h.clearCache) + r.DELETE("/api/cache/clear-by-key/{cacheKey}", h.clearCacheByKey) } -func (h *CacheHandler) ClearCache(ctx *fasthttp.RequestCtx) { - requestID, ok := ctx.UserValue("request-id").(string) +func (h *CacheHandler) clearCache(ctx *fasthttp.RequestCtx) { + requestID, ok := ctx.UserValue("requestId").(string) if !ok { SendError(ctx, fasthttp.StatusBadRequest, "Invalid request ID", h.logger) return @@ -40,13 +40,13 @@ func (h *CacheHandler) ClearCache(ctx *fasthttp.RequestCtx) { return } - SendJSON(ctx, map[string]interface{}{ + SendJSON(ctx, map[string]any{ "message": "Cache cleared successfully", }, h.logger) } -func (h *CacheHandler) ClearCacheByKey(ctx *fasthttp.RequestCtx) { - cacheKey, ok := ctx.UserValue("cache-key").(string) +func (h *CacheHandler) clearCacheByKey(ctx *fasthttp.RequestCtx) { + cacheKey, ok := ctx.UserValue("cacheKey").(string) if !ok { SendError(ctx, fasthttp.StatusBadRequest, "Invalid cache key", h.logger) return @@ -56,7 +56,7 @@ func (h *CacheHandler) ClearCacheByKey(ctx *fasthttp.RequestCtx) { return } - SendJSON(ctx, map[string]interface{}{ + SendJSON(ctx, map[string]any{ "message": "Cache cleared successfully", }, h.logger) } diff --git a/transports/bifrost-http/handlers/completions.go b/transports/bifrost-http/handlers/completions.go index 1311fb8b43..160f7bb169 100644 --- a/transports/bifrost-http/handlers/completions.go +++ b/transports/bifrost-http/handlers/completions.go @@ -304,35 +304,35 @@ func (h *CompletionHandler) validateAudioFile(fileHeader *multipart.FileHeader) // RegisterRoutes registers all completion-related routes func (h *CompletionHandler) RegisterRoutes(r *router.Router) { // Completion endpoints - r.POST("/v1/text/completions", h.TextCompletion) - r.POST("/v1/chat/completions", h.ChatCompletion) - r.POST("/v1/embeddings", h.Embeddings) - r.POST("/v1/audio/speech", h.SpeechCompletion) - r.POST("/v1/audio/transcriptions", h.TranscriptionCompletion) + r.POST("/v1/text/completions", h.textCompletion) + r.POST("/v1/chat/completions", h.chatCompletion) + r.POST("/v1/embeddings", h.embeddings) + r.POST("/v1/audio/speech", h.speechCompletion) + r.POST("/v1/audio/transcriptions", h.transcriptionCompletion) } -// TextCompletion handles POST /v1/text/completions - Process text completion requests -func (h *CompletionHandler) TextCompletion(ctx *fasthttp.RequestCtx) { +// textCompletion handles POST /v1/text/completions - Process text completion requests +func (h *CompletionHandler) textCompletion(ctx *fasthttp.RequestCtx) { h.handleRequest(ctx, CompletionTypeText) } -// ChatCompletion handles POST /v1/chat/completions - Process chat completion requests -func (h *CompletionHandler) ChatCompletion(ctx *fasthttp.RequestCtx) { +// chatCompletion handles POST /v1/chat/completions - Process chat completion requests +func (h *CompletionHandler) chatCompletion(ctx *fasthttp.RequestCtx) { h.handleRequest(ctx, CompletionTypeChat) } -// Embeddings handles POST /v1/embeddings - Process embeddings requests -func (h *CompletionHandler) Embeddings(ctx *fasthttp.RequestCtx) { +// embeddings handles POST /v1/embeddings - Process embeddings requests +func (h *CompletionHandler) embeddings(ctx *fasthttp.RequestCtx) { h.handleRequest(ctx, CompletionTypeEmbeddings) } -// SpeechCompletion handles POST /v1/audio/speech - Process speech completion requests -func (h *CompletionHandler) SpeechCompletion(ctx *fasthttp.RequestCtx) { +// speechCompletion handles POST /v1/audio/speech - Process speech completion requests +func (h *CompletionHandler) speechCompletion(ctx *fasthttp.RequestCtx) { h.handleRequest(ctx, CompletionTypeSpeech) } -// TranscriptionCompletion handles POST /v1/audio/transcriptions - Process transcription requests -func (h *CompletionHandler) TranscriptionCompletion(ctx *fasthttp.RequestCtx) { +// transcriptionCompletion handles POST /v1/audio/transcriptions - Process transcription requests +func (h *CompletionHandler) transcriptionCompletion(ctx *fasthttp.RequestCtx) { // Parse multipart form form, err := ctx.MultipartForm() if err != nil { diff --git a/transports/bifrost-http/handlers/config.go b/transports/bifrost-http/handlers/config.go index 570bc8909c..c79e7f4e6c 100644 --- a/transports/bifrost-http/handlers/config.go +++ b/transports/bifrost-http/handlers/config.go @@ -34,12 +34,18 @@ func NewConfigHandler(client *bifrost.Bifrost, logger schemas.Logger, store *lib // RegisterRoutes registers the configuration-related routes. // It adds the `PUT /api/config` endpoint. func (h *ConfigHandler) RegisterRoutes(r *router.Router) { - r.GET("/api/config", h.GetConfig) - r.PUT("/api/config", h.handleUpdateConfig) + r.GET("/api/config", h.getConfig) + r.PUT("/api/config", h.updateConfig) + r.GET("/api/version", h.getVersion) } -// GetConfig handles GET /config - Get the current configuration -func (h *ConfigHandler) GetConfig(ctx *fasthttp.RequestCtx) { +// getVersion handles GET /api/version - Get the current version +func (h *ConfigHandler) getVersion(ctx *fasthttp.RequestCtx) { + SendJSON(ctx, version, h.logger) +} + +// getConfig handles GET /config - Get the current configuration +func (h *ConfigHandler) getConfig(ctx *fasthttp.RequestCtx) { var mapConfig = make(map[string]any) @@ -68,10 +74,10 @@ func (h *ConfigHandler) GetConfig(ctx *fasthttp.RequestCtx) { SendJSON(ctx, mapConfig, h.logger) } -// handleUpdateConfig updates the core configuration settings. +// updateConfig updates the core configuration settings. // Currently, it supports hot-reloading of the `drop_excess_requests` setting. // Note that settings like `prometheus_labels` cannot be changed at runtime. -func (h *ConfigHandler) handleUpdateConfig(ctx *fasthttp.RequestCtx) { +func (h *ConfigHandler) updateConfig(ctx *fasthttp.RequestCtx) { if h.store.ConfigStore == nil { SendError(ctx, fasthttp.StatusInternalServerError, "Config store not initialized", h.logger) return diff --git a/transports/bifrost-http/handlers/governance.go b/transports/bifrost-http/handlers/governance.go index 3b65bd8dfb..b641dc7b17 100644 --- a/transports/bifrost-http/handlers/governance.go +++ b/transports/bifrost-http/handlers/governance.go @@ -118,31 +118,31 @@ type UpdateCustomerRequest struct { // RegisterRoutes registers all governance-related routes for the new hierarchical system func (h *GovernanceHandler) RegisterRoutes(r *router.Router) { // Virtual Key CRUD operations - r.GET("/api/governance/virtual-keys", h.GetVirtualKeys) - r.POST("/api/governance/virtual-keys", h.CreateVirtualKey) - r.GET("/api/governance/virtual-keys/{vk_id}", h.GetVirtualKey) - r.PUT("/api/governance/virtual-keys/{vk_id}", h.UpdateVirtualKey) - r.DELETE("/api/governance/virtual-keys/{vk_id}", h.DeleteVirtualKey) + r.GET("/api/governance/virtual-keys", h.getVirtualKeys) + r.POST("/api/governance/virtual-keys", h.createVirtualKey) + r.GET("/api/governance/virtual-keys/{vk_id}", h.getVirtualKey) + r.PUT("/api/governance/virtual-keys/{vk_id}", h.updateVirtualKey) + r.DELETE("/api/governance/virtual-keys/{vk_id}", h.deleteVirtualKey) // Team CRUD operations - r.GET("/api/governance/teams", h.GetTeams) - r.POST("/api/governance/teams", h.CreateTeam) - r.GET("/api/governance/teams/{team_id}", h.GetTeam) - r.PUT("/api/governance/teams/{team_id}", h.UpdateTeam) - r.DELETE("/api/governance/teams/{team_id}", h.DeleteTeam) + r.GET("/api/governance/teams", h.getTeams) + r.POST("/api/governance/teams", h.createTeam) + r.GET("/api/governance/teams/{team_id}", h.getTeam) + r.PUT("/api/governance/teams/{team_id}", h.updateTeam) + r.DELETE("/api/governance/teams/{team_id}", h.deleteTeam) // Customer CRUD operations - r.GET("/api/governance/customers", h.GetCustomers) - r.POST("/api/governance/customers", h.CreateCustomer) - r.GET("/api/governance/customers/{customer_id}", h.GetCustomer) - r.PUT("/api/governance/customers/{customer_id}", h.UpdateCustomer) - r.DELETE("/api/governance/customers/{customer_id}", h.DeleteCustomer) + r.GET("/api/governance/customers", h.getCustomers) + r.POST("/api/governance/customers", h.createCustomer) + r.GET("/api/governance/customers/{customer_id}", h.getCustomer) + r.PUT("/api/governance/customers/{customer_id}", h.updateCustomer) + r.DELETE("/api/governance/customers/{customer_id}", h.deleteCustomer) } // Virtual Key CRUD Operations -// GetVirtualKeys handles GET /api/governance/virtual-keys - Get all virtual keys with relationships -func (h *GovernanceHandler) GetVirtualKeys(ctx *fasthttp.RequestCtx) { +// getVirtualKeys handles GET /api/governance/virtual-keys - Get all virtual keys with relationships +func (h *GovernanceHandler) getVirtualKeys(ctx *fasthttp.RequestCtx) { // Preload all relationships for complete information virtualKeys, err := h.configStore.GetVirtualKeys() if err != nil { @@ -157,8 +157,8 @@ func (h *GovernanceHandler) GetVirtualKeys(ctx *fasthttp.RequestCtx) { }, h.logger) } -// CreateVirtualKey handles POST /api/governance/virtual-keys - Create a new virtual key -func (h *GovernanceHandler) CreateVirtualKey(ctx *fasthttp.RequestCtx) { +// createVirtualKey handles POST /api/governance/virtual-keys - Create a new virtual key +func (h *GovernanceHandler) createVirtualKey(ctx *fasthttp.RequestCtx) { var req CreateVirtualKeyRequest if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { SendError(ctx, 400, "Invalid JSON", h.logger) @@ -286,8 +286,8 @@ func (h *GovernanceHandler) CreateVirtualKey(ctx *fasthttp.RequestCtx) { }, h.logger) } -// GetVirtualKey handles GET /api/governance/virtual-keys/{vk_id} - Get a specific virtual key -func (h *GovernanceHandler) GetVirtualKey(ctx *fasthttp.RequestCtx) { +// getVirtualKey handles GET /api/governance/virtual-keys/{vk_id} - Get a specific virtual key +func (h *GovernanceHandler) getVirtualKey(ctx *fasthttp.RequestCtx) { vkID := ctx.UserValue("vk_id").(string) vk, err := h.configStore.GetVirtualKey(vkID) @@ -305,8 +305,8 @@ func (h *GovernanceHandler) GetVirtualKey(ctx *fasthttp.RequestCtx) { }, h.logger) } -// UpdateVirtualKey handles PUT /api/governance/virtual-keys/{vk_id} - Update a virtual key -func (h *GovernanceHandler) UpdateVirtualKey(ctx *fasthttp.RequestCtx) { +// updateVirtualKey handles PUT /api/governance/virtual-keys/{vk_id} - Update a virtual key +func (h *GovernanceHandler) updateVirtualKey(ctx *fasthttp.RequestCtx) { vkID := ctx.UserValue("vk_id").(string) var req UpdateVirtualKeyRequest @@ -497,8 +497,8 @@ func (h *GovernanceHandler) UpdateVirtualKey(ctx *fasthttp.RequestCtx) { }, h.logger) } -// DeleteVirtualKey handles DELETE /api/governance/virtual-keys/{vk_id} - Delete a virtual key -func (h *GovernanceHandler) DeleteVirtualKey(ctx *fasthttp.RequestCtx) { +// deleteVirtualKey handles DELETE /api/governance/virtual-keys/{vk_id} - Delete a virtual key +func (h *GovernanceHandler) deleteVirtualKey(ctx *fasthttp.RequestCtx) { vkID := ctx.UserValue("vk_id").(string) // Fetch the virtual key from the database to get the budget and rate limit @@ -538,8 +538,8 @@ func (h *GovernanceHandler) DeleteVirtualKey(ctx *fasthttp.RequestCtx) { // Team CRUD Operations -// GetTeams handles GET /api/governance/teams - Get all teams -func (h *GovernanceHandler) GetTeams(ctx *fasthttp.RequestCtx) { +// getTeams handles GET /api/governance/teams - Get all teams +func (h *GovernanceHandler) getTeams(ctx *fasthttp.RequestCtx) { customerID := string(ctx.QueryArgs().Peek("customer_id")) // Preload relationships for complete information @@ -556,8 +556,8 @@ func (h *GovernanceHandler) GetTeams(ctx *fasthttp.RequestCtx) { }, h.logger) } -// CreateTeam handles POST /api/governance/teams - Create a new team -func (h *GovernanceHandler) CreateTeam(ctx *fasthttp.RequestCtx) { +// createTeam handles POST /api/governance/teams - Create a new team +func (h *GovernanceHandler) createTeam(ctx *fasthttp.RequestCtx) { var req CreateTeamRequest if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { SendError(ctx, 400, "Invalid JSON", h.logger) @@ -636,8 +636,8 @@ func (h *GovernanceHandler) CreateTeam(ctx *fasthttp.RequestCtx) { }, h.logger) } -// GetTeam handles GET /api/governance/teams/{team_id} - Get a specific team -func (h *GovernanceHandler) GetTeam(ctx *fasthttp.RequestCtx) { +// getTeam handles GET /api/governance/teams/{team_id} - Get a specific team +func (h *GovernanceHandler) getTeam(ctx *fasthttp.RequestCtx) { teamID := ctx.UserValue("team_id").(string) team, err := h.configStore.GetTeam(teamID) @@ -655,8 +655,8 @@ func (h *GovernanceHandler) GetTeam(ctx *fasthttp.RequestCtx) { }, h.logger) } -// UpdateTeam handles PUT /api/governance/teams/{team_id} - Update a team -func (h *GovernanceHandler) UpdateTeam(ctx *fasthttp.RequestCtx) { +// updateTeam handles PUT /api/governance/teams/{team_id} - Update a team +func (h *GovernanceHandler) updateTeam(ctx *fasthttp.RequestCtx) { teamID := ctx.UserValue("team_id").(string) var req UpdateTeamRequest @@ -754,8 +754,8 @@ func (h *GovernanceHandler) UpdateTeam(ctx *fasthttp.RequestCtx) { }, h.logger) } -// DeleteTeam handles DELETE /api/governance/teams/{team_id} - Delete a team -func (h *GovernanceHandler) DeleteTeam(ctx *fasthttp.RequestCtx) { +// deleteTeam handles DELETE /api/governance/teams/{team_id} - Delete a team +func (h *GovernanceHandler) deleteTeam(ctx *fasthttp.RequestCtx) { teamID := ctx.UserValue("team_id").(string) team, err := h.configStore.GetTeam(teamID) @@ -794,8 +794,8 @@ func (h *GovernanceHandler) DeleteTeam(ctx *fasthttp.RequestCtx) { // Customer CRUD Operations -// GetCustomers handles GET /api/governance/customers - Get all customers -func (h *GovernanceHandler) GetCustomers(ctx *fasthttp.RequestCtx) { +// getCustomers handles GET /api/governance/customers - Get all customers +func (h *GovernanceHandler) getCustomers(ctx *fasthttp.RequestCtx) { customers, err := h.configStore.GetCustomers() if err != nil { h.logger.Error("failed to retrieve customers: %v", err) @@ -809,8 +809,8 @@ func (h *GovernanceHandler) GetCustomers(ctx *fasthttp.RequestCtx) { }, h.logger) } -// CreateCustomer handles POST /api/governance/customers - Create a new customer -func (h *GovernanceHandler) CreateCustomer(ctx *fasthttp.RequestCtx) { +// createCustomer handles POST /api/governance/customers - Create a new customer +func (h *GovernanceHandler) createCustomer(ctx *fasthttp.RequestCtx) { var req CreateCustomerRequest if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { SendError(ctx, 400, "Invalid JSON", h.logger) @@ -887,8 +887,8 @@ func (h *GovernanceHandler) CreateCustomer(ctx *fasthttp.RequestCtx) { }, h.logger) } -// GetCustomer handles GET /api/governance/customers/{customer_id} - Get a specific customer -func (h *GovernanceHandler) GetCustomer(ctx *fasthttp.RequestCtx) { +// getCustomer handles GET /api/governance/customers/{customer_id} - Get a specific customer +func (h *GovernanceHandler) getCustomer(ctx *fasthttp.RequestCtx) { customerID := ctx.UserValue("customer_id").(string) customer, err := h.configStore.GetCustomer(customerID) @@ -906,8 +906,8 @@ func (h *GovernanceHandler) GetCustomer(ctx *fasthttp.RequestCtx) { }, h.logger) } -// UpdateCustomer handles PUT /api/governance/customers/{customer_id} - Update a customer -func (h *GovernanceHandler) UpdateCustomer(ctx *fasthttp.RequestCtx) { +// updateCustomer handles PUT /api/governance/customers/{customer_id} - Update a customer +func (h *GovernanceHandler) updateCustomer(ctx *fasthttp.RequestCtx) { customerID := ctx.UserValue("customer_id").(string) var req UpdateCustomerRequest @@ -1002,8 +1002,8 @@ func (h *GovernanceHandler) UpdateCustomer(ctx *fasthttp.RequestCtx) { }, h.logger) } -// DeleteCustomer handles DELETE /api/governance/customers/{customer_id} - Delete a customer -func (h *GovernanceHandler) DeleteCustomer(ctx *fasthttp.RequestCtx) { +// deleteCustomer handles DELETE /api/governance/customers/{customer_id} - Delete a customer +func (h *GovernanceHandler) deleteCustomer(ctx *fasthttp.RequestCtx) { customerID := ctx.UserValue("customer_id").(string) customer, err := h.configStore.GetCustomer(customerID) diff --git a/transports/bifrost-http/handlers/handlers.go b/transports/bifrost-http/handlers/handlers.go new file mode 100644 index 0000000000..7e2baffc76 --- /dev/null +++ b/transports/bifrost-http/handlers/handlers.go @@ -0,0 +1,8 @@ +package handlers + +var version string + +// SetVersion sets the version of the application. +func SetVersion(v string) { + version = v +} diff --git a/transports/bifrost-http/handlers/logging.go b/transports/bifrost-http/handlers/logging.go index fbc2c616b0..ed810aaa9f 100644 --- a/transports/bifrost-http/handlers/logging.go +++ b/transports/bifrost-http/handlers/logging.go @@ -32,13 +32,13 @@ func NewLoggingHandler(logManager logging.LogManager, logger schemas.Logger) *Lo // RegisterRoutes registers all logging-related routes func (h *LoggingHandler) RegisterRoutes(r *router.Router) { // Log retrieval with filtering, search, and pagination - r.GET("/api/logs", h.GetLogs) - r.GET("/api/logs/dropped", h.GetDroppedRequests) - r.GET("/api/logs/models", h.GetAvailableModels) + r.GET("/api/logs", h.getLogs) + r.GET("/api/logs/dropped", h.getDroppedRequests) + r.GET("/api/logs/models", h.getAvailableModels) } -// GetLogs handles GET /api/logs - Get logs with filtering, search, and pagination via query parameters -func (h *LoggingHandler) GetLogs(ctx *fasthttp.RequestCtx) { +// getLogs handles GET /api/logs - Get logs with filtering, search, and pagination via query parameters +func (h *LoggingHandler) getLogs(ctx *fasthttp.RequestCtx) { // Parse query parameters into filters filters := &logstore.SearchFilters{} pagination := &logstore.PaginationOptions{} @@ -147,14 +147,14 @@ func (h *LoggingHandler) GetLogs(ctx *fasthttp.RequestCtx) { SendJSON(ctx, result, h.logger) } -// GetDroppedRequests handles GET /api/logs/dropped - Get the number of dropped requests -func (h *LoggingHandler) GetDroppedRequests(ctx *fasthttp.RequestCtx) { +// getDroppedRequests handles GET /api/logs/dropped - Get the number of dropped requests +func (h *LoggingHandler) getDroppedRequests(ctx *fasthttp.RequestCtx) { droppedRequests := h.logManager.GetDroppedRequests() SendJSON(ctx, map[string]int64{"dropped_requests": droppedRequests}, h.logger) } -// GetAvailableModels handles GET /api/logs/models - Get all unique models from logs -func (h *LoggingHandler) GetAvailableModels(ctx *fasthttp.RequestCtx) { +// getAvailableModels handles GET /api/logs/models - Get all unique models from logs +func (h *LoggingHandler) getAvailableModels(ctx *fasthttp.RequestCtx) { models := h.logManager.GetAvailableModels() SendJSON(ctx, map[string]interface{}{"models": models}, h.logger) } diff --git a/transports/bifrost-http/handlers/mcp.go b/transports/bifrost-http/handlers/mcp.go index a0ae28a3c6..37fedf8fce 100644 --- a/transports/bifrost-http/handlers/mcp.go +++ b/transports/bifrost-http/handlers/mcp.go @@ -32,16 +32,16 @@ func NewMCPHandler(client *bifrost.Bifrost, logger schemas.Logger, store *lib.Co // RegisterRoutes registers all MCP-related routes func (h *MCPHandler) RegisterRoutes(r *router.Router) { // MCP tool execution endpoint - r.POST("/v1/mcp/tool/execute", h.ExecuteTool) - r.GET("/api/mcp/clients", h.GetMCPClients) - r.POST("/api/mcp/client", h.AddMCPClient) - r.PUT("/api/mcp/client/{name}", h.EditMCPClientTools) - r.DELETE("/api/mcp/client/{name}", h.RemoveMCPClient) - r.POST("/api/mcp/client/{name}/reconnect", h.ReconnectMCPClient) + r.POST("/v1/mcp/tool/execute", h.executeTool) + r.GET("/api/mcp/clients", h.getMCPClients) + r.POST("/api/mcp/client", h.addMCPClient) + r.PUT("/api/mcp/client/{name}", h.editMCPClientTools) + r.DELETE("/api/mcp/client/{name}", h.removeMCPClient) + r.POST("/api/mcp/client/{name}/reconnect", h.reconnectMCPClient) } -// ExecuteTool handles POST /v1/mcp/tool/execute - Execute MCP tool -func (h *MCPHandler) ExecuteTool(ctx *fasthttp.RequestCtx) { +// executeTool handles POST /v1/mcp/tool/execute - Execute MCP tool +func (h *MCPHandler) executeTool(ctx *fasthttp.RequestCtx) { var req schemas.ToolCall if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid request format: %v", err), h.logger) @@ -72,8 +72,8 @@ func (h *MCPHandler) ExecuteTool(ctx *fasthttp.RequestCtx) { SendJSON(ctx, resp, h.logger) } -// GetMCPClients handles GET /api/mcp/clients - Get all MCP clients -func (h *MCPHandler) GetMCPClients(ctx *fasthttp.RequestCtx) { +// getMCPClients handles GET /api/mcp/clients - Get all MCP clients +func (h *MCPHandler) getMCPClients(ctx *fasthttp.RequestCtx) { // Get clients from store config configsInStore := h.store.MCPConfig if configsInStore == nil { @@ -120,8 +120,8 @@ func (h *MCPHandler) GetMCPClients(ctx *fasthttp.RequestCtx) { SendJSON(ctx, clients, h.logger) } -// ReconnectMCPClient handles POST /api/mcp/client/{name}/reconnect - Reconnect an MCP client -func (h *MCPHandler) ReconnectMCPClient(ctx *fasthttp.RequestCtx) { +// reconnectMCPClient handles POST /api/mcp/client/{name}/reconnect - Reconnect an MCP client +func (h *MCPHandler) reconnectMCPClient(ctx *fasthttp.RequestCtx) { name, err := getNameFromCtx(ctx) if err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid name: %v", err), h.logger) @@ -139,8 +139,8 @@ func (h *MCPHandler) ReconnectMCPClient(ctx *fasthttp.RequestCtx) { }, h.logger) } -// AddMCPClient handles POST /api/mcp/client - Add a new MCP client -func (h *MCPHandler) AddMCPClient(ctx *fasthttp.RequestCtx) { +// addMCPClient handles POST /api/mcp/client - Add a new MCP client +func (h *MCPHandler) addMCPClient(ctx *fasthttp.RequestCtx) { var req schemas.MCPClientConfig if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid request format: %v", err), h.logger) @@ -158,8 +158,8 @@ func (h *MCPHandler) AddMCPClient(ctx *fasthttp.RequestCtx) { }, h.logger) } -// EditMCPClientTools handles PUT /api/mcp/client/{name} - Edit MCP client tools -func (h *MCPHandler) EditMCPClientTools(ctx *fasthttp.RequestCtx) { +// editMCPClientTools handles PUT /api/mcp/client/{name} - Edit MCP client tools +func (h *MCPHandler) editMCPClientTools(ctx *fasthttp.RequestCtx) { name, err := getNameFromCtx(ctx) if err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid name: %v", err), h.logger) @@ -186,8 +186,8 @@ func (h *MCPHandler) EditMCPClientTools(ctx *fasthttp.RequestCtx) { }, h.logger) } -// RemoveMCPClient handles DELETE /api/mcp/client/{name} - Remove an MCP client -func (h *MCPHandler) RemoveMCPClient(ctx *fasthttp.RequestCtx) { +// removeMCPClient handles DELETE /api/mcp/client/{name} - Remove an MCP client +func (h *MCPHandler) removeMCPClient(ctx *fasthttp.RequestCtx) { name, err := getNameFromCtx(ctx) if err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid name: %v", err), h.logger) diff --git a/transports/bifrost-http/handlers/plugins.go b/transports/bifrost-http/handlers/plugins.go index e23615a8bb..3842ada91d 100644 --- a/transports/bifrost-http/handlers/plugins.go +++ b/transports/bifrost-http/handlers/plugins.go @@ -35,14 +35,14 @@ type UpdatePluginRequest struct { } func (h *PluginsHandler) RegisterRoutes(r *router.Router) { - r.GET("/api/plugins", h.GetPlugins) - r.GET("/api/plugins/{name}", h.GetPlugin) - r.POST("/api/plugins", h.CreatePlugin) - r.PUT("/api/plugins/{name}", h.UpdatePlugin) - r.DELETE("/api/plugins/{name}", h.DeletePlugin) + r.GET("/api/plugins", h.getPlugins) + r.GET("/api/plugins/{name}", h.getPlugin) + r.POST("/api/plugins", h.createPlugin) + r.PUT("/api/plugins/{name}", h.updatePlugin) + r.DELETE("/api/plugins/{name}", h.deletePlugin) } -func (h *PluginsHandler) GetPlugins(ctx *fasthttp.RequestCtx) { +func (h *PluginsHandler) getPlugins(ctx *fasthttp.RequestCtx) { plugins, err := h.configStore.GetPlugins() if err != nil { h.logger.Error("failed to get plugins: %v", err) @@ -56,7 +56,7 @@ func (h *PluginsHandler) GetPlugins(ctx *fasthttp.RequestCtx) { }, h.logger) } -func (h *PluginsHandler) GetPlugin(ctx *fasthttp.RequestCtx) { +func (h *PluginsHandler) getPlugin(ctx *fasthttp.RequestCtx) { // Safely validate the "name" parameter nameValue := ctx.UserValue("name") if nameValue == nil { @@ -91,7 +91,7 @@ func (h *PluginsHandler) GetPlugin(ctx *fasthttp.RequestCtx) { SendJSON(ctx, plugin, h.logger) } -func (h *PluginsHandler) CreatePlugin(ctx *fasthttp.RequestCtx) { +func (h *PluginsHandler) createPlugin(ctx *fasthttp.RequestCtx) { var request CreatePluginRequest if err := json.Unmarshal(ctx.PostBody(), &request); err != nil { h.logger.Error("failed to unmarshal create plugin request: %v", err) @@ -136,7 +136,7 @@ func (h *PluginsHandler) CreatePlugin(ctx *fasthttp.RequestCtx) { }, h.logger) } -func (h *PluginsHandler) UpdatePlugin(ctx *fasthttp.RequestCtx) { +func (h *PluginsHandler) updatePlugin(ctx *fasthttp.RequestCtx) { // Safely validate the "name" parameter nameValue := ctx.UserValue("name") if nameValue == nil { @@ -199,7 +199,7 @@ func (h *PluginsHandler) UpdatePlugin(ctx *fasthttp.RequestCtx) { }, h.logger) } -func (h *PluginsHandler) DeletePlugin(ctx *fasthttp.RequestCtx) { +func (h *PluginsHandler) deletePlugin(ctx *fasthttp.RequestCtx) { // Safely validate the "name" parameter nameValue := ctx.UserValue("name") if nameValue == nil { diff --git a/transports/bifrost-http/handlers/providers.go b/transports/bifrost-http/handlers/providers.go index acc27c5410..a10303d385 100644 --- a/transports/bifrost-http/handlers/providers.go +++ b/transports/bifrost-http/handlers/providers.go @@ -33,27 +33,6 @@ func NewProviderHandler(store *lib.Config, client *bifrost.Bifrost, logger schem } } -// AddProviderRequest represents the request body for adding a new provider -type AddProviderRequest struct { - Provider schemas.ModelProvider `json:"provider"` - Keys []schemas.Key `json:"keys"` // API keys for the provider - NetworkConfig *schemas.NetworkConfig `json:"network_config,omitempty"` // Network-related settings - ConcurrencyAndBufferSize *schemas.ConcurrencyAndBufferSize `json:"concurrency_and_buffer_size,omitempty"` // Concurrency settings - ProxyConfig *schemas.ProxyConfig `json:"proxy_config,omitempty"` // Proxy configuration - SendBackRawResponse *bool `json:"send_back_raw_response,omitempty"` // Include raw response in BifrostResponse - CustomProviderConfig *schemas.CustomProviderConfig `json:"custom_provider_config,omitempty"` // Custom provider configuration -} - -// UpdateProviderRequest represents the request body for updating a provider -type UpdateProviderRequest struct { - Keys []schemas.Key `json:"keys"` // API keys for the provider - NetworkConfig schemas.NetworkConfig `json:"network_config"` // Network-related settings - ConcurrencyAndBufferSize schemas.ConcurrencyAndBufferSize `json:"concurrency_and_buffer_size"` // Concurrency settings - ProxyConfig *schemas.ProxyConfig `json:"proxy_config,omitempty"` // Proxy configuration - SendBackRawResponse *bool `json:"send_back_raw_response,omitempty"` // Include raw response in BifrostResponse - CustomProviderConfig *schemas.CustomProviderConfig `json:"custom_provider_config,omitempty"` // Custom provider configuration -} - // ProviderResponse represents the response for provider operations type ProviderResponse struct { Name schemas.ModelProvider `json:"name"` @@ -80,16 +59,16 @@ type ErrorResponse struct { // RegisterRoutes registers all provider management routes func (h *ProviderHandler) RegisterRoutes(r *router.Router) { // Provider CRUD operations - r.GET("/api/providers", h.ListProviders) - r.GET("/api/providers/{provider}", h.GetProvider) - r.POST("/api/providers", h.AddProvider) - r.PUT("/api/providers/{provider}", h.UpdateProvider) - r.DELETE("/api/providers/{provider}", h.DeleteProvider) - r.GET("/api/keys", h.ListKeys) + r.GET("/api/providers", h.listProviders) + r.GET("/api/providers/{provider}", h.getProvider) + r.POST("/api/providers", h.addProvider) + r.PUT("/api/providers/{provider}", h.updateProvider) + r.DELETE("/api/providers/{provider}", h.deleteProvider) + r.GET("/api/keys", h.listKeys) } -// ListProviders handles GET /api/providers - List all providers -func (h *ProviderHandler) ListProviders(ctx *fasthttp.RequestCtx) { +// listProviders handles GET /api/providers - List all providers +func (h *ProviderHandler) listProviders(ctx *fasthttp.RequestCtx) { providers, err := h.store.GetAllProviders() if err != nil { SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to get providers: %v", err), h.logger) @@ -125,8 +104,8 @@ func (h *ProviderHandler) ListProviders(ctx *fasthttp.RequestCtx) { SendJSON(ctx, response, h.logger) } -// GetProvider handles GET /api/providers/{provider} - Get specific provider -func (h *ProviderHandler) GetProvider(ctx *fasthttp.RequestCtx) { +// getProvider handles GET /api/providers/{provider} - Get specific provider +func (h *ProviderHandler) getProvider(ctx *fasthttp.RequestCtx) { provider, err := getProviderFromCtx(ctx) if err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid provider: %v", err), h.logger) @@ -144,96 +123,101 @@ func (h *ProviderHandler) GetProvider(ctx *fasthttp.RequestCtx) { SendJSON(ctx, response, h.logger) } -// AddProvider handles POST /api/providers - Add a new provider -func (h *ProviderHandler) AddProvider(ctx *fasthttp.RequestCtx) { - var req AddProviderRequest - if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { +// addProvider handles POST /api/providers - Add a new provider +func (h *ProviderHandler) addProvider(ctx *fasthttp.RequestCtx) { + // Payload structure + var payload = struct { + Provider schemas.ModelProvider `json:"provider"` + Keys []schemas.Key `json:"keys"` // API keys for the provider + NetworkConfig *schemas.NetworkConfig `json:"network_config,omitempty"` // Network-related settings + ConcurrencyAndBufferSize *schemas.ConcurrencyAndBufferSize `json:"concurrency_and_buffer_size,omitempty"` // Concurrency settings + ProxyConfig *schemas.ProxyConfig `json:"proxy_config,omitempty"` // Proxy configuration + SendBackRawResponse *bool `json:"send_back_raw_response,omitempty"` // Include raw response in BifrostResponse + CustomProviderConfig *schemas.CustomProviderConfig `json:"custom_provider_config,omitempty"` // Custom provider configuration + }{} + + if err := json.Unmarshal(ctx.PostBody(), &payload); err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid JSON: %v", err), h.logger) return } // Validate provider - if req.Provider == "" { + if payload.Provider == "" { SendError(ctx, fasthttp.StatusBadRequest, "Missing provider", h.logger) return } // baseProvider tracks the effective base provider type for validations/keys - baseProvider := req.Provider - if req.CustomProviderConfig != nil { + baseProvider := payload.Provider + if payload.CustomProviderConfig != nil { // custom provider key should not be same as standard provider names if bifrost.IsStandardProvider(baseProvider) { SendError(ctx, fasthttp.StatusBadRequest, "Custom provider cannot be same as a standard provider", h.logger) return } - if req.CustomProviderConfig.BaseProviderType == "" { + if payload.CustomProviderConfig.BaseProviderType == "" { SendError(ctx, fasthttp.StatusBadRequest, "BaseProviderType is required when CustomProviderConfig is provided", h.logger) return } // check if base provider is a supported base provider - if !bifrost.IsSupportedBaseProvider(req.CustomProviderConfig.BaseProviderType) { + if !bifrost.IsSupportedBaseProvider(payload.CustomProviderConfig.BaseProviderType) { SendError(ctx, fasthttp.StatusBadRequest, "BaseProviderType must be a standard provider", h.logger) return } // CustomProviderKey is internally set by Bifrost, no validation needed - baseProvider = req.CustomProviderConfig.BaseProviderType - } - - // Validate required keys - if len(req.Keys) == 0 && baseProvider != schemas.Ollama && baseProvider != schemas.SGL { - SendError(ctx, fasthttp.StatusBadRequest, "At least one API key is required", h.logger) - return + baseProvider = payload.CustomProviderConfig.BaseProviderType } - if req.ConcurrencyAndBufferSize != nil { - if req.ConcurrencyAndBufferSize.Concurrency == 0 { + if payload.ConcurrencyAndBufferSize != nil { + if payload.ConcurrencyAndBufferSize.Concurrency == 0 { SendError(ctx, fasthttp.StatusBadRequest, "Concurrency must be greater than 0", h.logger) return } - if req.ConcurrencyAndBufferSize.BufferSize == 0 { + if payload.ConcurrencyAndBufferSize.BufferSize == 0 { SendError(ctx, fasthttp.StatusBadRequest, "Buffer size must be greater than 0", h.logger) return } - if req.ConcurrencyAndBufferSize.Concurrency > req.ConcurrencyAndBufferSize.BufferSize { + if payload.ConcurrencyAndBufferSize.Concurrency > payload.ConcurrencyAndBufferSize.BufferSize { SendError(ctx, fasthttp.StatusBadRequest, "Concurrency must be less than or equal to buffer size", h.logger) return } } // Check if provider already exists - if _, err := h.store.GetProviderConfigRedacted(req.Provider); err == nil { - SendError(ctx, fasthttp.StatusConflict, fmt.Sprintf("Provider %s already exists", req.Provider), h.logger) + if _, err := h.store.GetProviderConfigRedacted(payload.Provider); err == nil { + SendError(ctx, fasthttp.StatusConflict, fmt.Sprintf("Provider %s already exists", payload.Provider), h.logger) return } // Construct ProviderConfig from individual fields config := configstore.ProviderConfig{ - Keys: req.Keys, - NetworkConfig: req.NetworkConfig, - ConcurrencyAndBufferSize: req.ConcurrencyAndBufferSize, - SendBackRawResponse: req.SendBackRawResponse != nil && *req.SendBackRawResponse, - CustomProviderConfig: req.CustomProviderConfig, + Keys: payload.Keys, + NetworkConfig: payload.NetworkConfig, + ProxyConfig: payload.ProxyConfig, + ConcurrencyAndBufferSize: payload.ConcurrencyAndBufferSize, + SendBackRawResponse: payload.SendBackRawResponse != nil && *payload.SendBackRawResponse, + CustomProviderConfig: payload.CustomProviderConfig, } // Add provider to store (env vars will be processed by store) - if err := h.store.AddProvider(req.Provider, config); err != nil { - h.logger.Warn(fmt.Sprintf("Failed to add provider %s: %v", req.Provider, err)) + if err := h.store.AddProvider(payload.Provider, config); err != nil { + h.logger.Warn(fmt.Sprintf("Failed to add provider %s: %v", payload.Provider, err)) SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to add provider: %v", err), h.logger) return } - h.logger.Info(fmt.Sprintf("Provider %s added successfully", req.Provider)) + h.logger.Info(fmt.Sprintf("Provider %s added successfully", payload.Provider)) // Get redacted config for response - redactedConfig, err := h.store.GetProviderConfigRedacted(req.Provider) + redactedConfig, err := h.store.GetProviderConfigRedacted(payload.Provider) if err != nil { - h.logger.Warn(fmt.Sprintf("Failed to get redacted config for provider %s: %v", req.Provider, err)) + h.logger.Warn(fmt.Sprintf("Failed to get redacted config for provider %s: %v", payload.Provider, err)) // Fall back to the raw config (no keys) - response := h.getProviderResponseFromConfig(req.Provider, configstore.ProviderConfig{ + response := h.getProviderResponseFromConfig(payload.Provider, configstore.ProviderConfig{ NetworkConfig: config.NetworkConfig, ConcurrencyAndBufferSize: config.ConcurrencyAndBufferSize, ProxyConfig: config.ProxyConfig, @@ -244,24 +228,33 @@ func (h *ProviderHandler) AddProvider(ctx *fasthttp.RequestCtx) { return } - response := h.getProviderResponseFromConfig(req.Provider, *redactedConfig) + response := h.getProviderResponseFromConfig(payload.Provider, *redactedConfig) SendJSON(ctx, response, h.logger) } -// UpdateProvider handles PUT /api/providers/{provider} - Update provider config +// updateProvider handles PUT /api/providers/{provider} - Update provider config // NOTE: This endpoint expects ALL fields to be provided in the request body, // including both edited and non-edited fields. Partial updates are not supported. // The frontend should send the complete provider configuration. -func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { +// This flow upserts the config +func (h *ProviderHandler) updateProvider(ctx *fasthttp.RequestCtx) { provider, err := getProviderFromCtx(ctx) if err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid provider: %v", err), h.logger) return } - var req UpdateProviderRequest - if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { + var payload = struct { + Keys []schemas.Key `json:"keys"` // API keys for the provider + NetworkConfig schemas.NetworkConfig `json:"network_config"` // Network-related settings + ConcurrencyAndBufferSize schemas.ConcurrencyAndBufferSize `json:"concurrency_and_buffer_size"` // Concurrency settings + ProxyConfig *schemas.ProxyConfig `json:"proxy_config,omitempty"` // Proxy configuration + SendBackRawResponse *bool `json:"send_back_raw_response,omitempty"` // Include raw response in BifrostResponse + CustomProviderConfig *schemas.CustomProviderConfig `json:"custom_provider_config,omitempty"` // Custom provider configuration + }{} + + if err := json.Unmarshal(ctx.PostBody(), &payload); err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid JSON: %v", err), h.logger) return } @@ -269,16 +262,24 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { // Get the raw config to access actual values for merging with redacted request values oldConfigRaw, err := h.store.GetProviderConfigRaw(provider) if err != nil { - SendError(ctx, fasthttp.StatusNotFound, fmt.Sprintf("Provider not found: %v", err), h.logger) + SendError(ctx, fasthttp.StatusNotFound, err.Error(), h.logger) return } + if oldConfigRaw == nil { + oldConfigRaw = &configstore.ProviderConfig{} + } + oldConfigRedacted, err := h.store.GetProviderConfigRedacted(provider) if err != nil { - SendError(ctx, fasthttp.StatusNotFound, fmt.Sprintf("Provider not found: %v", err), h.logger) + SendError(ctx, fasthttp.StatusNotFound, err.Error(), h.logger) return } + if oldConfigRedacted == nil { + oldConfigRedacted = &configstore.ProviderConfig{} + } + // Construct ProviderConfig from individual fields config := configstore.ProviderConfig{ Keys: oldConfigRaw.Keys, @@ -293,7 +294,7 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { var keysToAdd []schemas.Key var keysToUpdate []schemas.Key - for _, key := range req.Keys { + for _, key := range payload.Keys { if !slices.ContainsFunc(oldConfigRaw.Keys, func(k schemas.Key) bool { return k.ID == key.ID }) { @@ -305,7 +306,7 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { var keysToDelete []schemas.Key for _, key := range oldConfigRaw.Keys { - if !slices.ContainsFunc(req.Keys, func(k schemas.Key) bool { + if !slices.ContainsFunc(payload.Keys, func(k schemas.Key) bool { return k.ID == key.ID }) { keysToDelete = append(keysToDelete, key) @@ -319,34 +320,34 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { } config.Keys = keys - if req.ConcurrencyAndBufferSize.Concurrency == 0 { + if payload.ConcurrencyAndBufferSize.Concurrency == 0 { SendError(ctx, fasthttp.StatusBadRequest, "Concurrency must be greater than 0", h.logger) return } - if req.ConcurrencyAndBufferSize.BufferSize == 0 { + if payload.ConcurrencyAndBufferSize.BufferSize == 0 { SendError(ctx, fasthttp.StatusBadRequest, "Buffer size must be greater than 0", h.logger) return } - if req.ConcurrencyAndBufferSize.Concurrency > req.ConcurrencyAndBufferSize.BufferSize { + if payload.ConcurrencyAndBufferSize.Concurrency > payload.ConcurrencyAndBufferSize.BufferSize { SendError(ctx, fasthttp.StatusBadRequest, "Concurrency must be less than or equal to buffer size", h.logger) return } // Build a prospective config with the requested CustomProviderConfig (including nil) prospective := config - prospective.CustomProviderConfig = req.CustomProviderConfig + prospective.CustomProviderConfig = payload.CustomProviderConfig if err := lib.ValidateCustomProviderUpdate(prospective, *oldConfigRaw, provider); err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid custom provider config: %v", err), h.logger) return } - config.ConcurrencyAndBufferSize = &req.ConcurrencyAndBufferSize - config.NetworkConfig = &req.NetworkConfig - config.ProxyConfig = req.ProxyConfig - config.CustomProviderConfig = req.CustomProviderConfig - if req.SendBackRawResponse != nil { - config.SendBackRawResponse = *req.SendBackRawResponse + config.ConcurrencyAndBufferSize = &payload.ConcurrencyAndBufferSize + config.NetworkConfig = &payload.NetworkConfig + config.ProxyConfig = payload.ProxyConfig + config.CustomProviderConfig = payload.CustomProviderConfig + if payload.SendBackRawResponse != nil { + config.SendBackRawResponse = *payload.SendBackRawResponse } // Update provider config in store (env vars will be processed by store) @@ -391,8 +392,8 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { SendJSON(ctx, response, h.logger) } -// DeleteProvider handles DELETE /api/providers/{provider} - Remove provider -func (h *ProviderHandler) DeleteProvider(ctx *fasthttp.RequestCtx) { +// deleteProvider handles DELETE /api/providers/{provider} - Remove provider +func (h *ProviderHandler) deleteProvider(ctx *fasthttp.RequestCtx) { provider, err := getProviderFromCtx(ctx) if err != nil { SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid provider: %v", err), h.logger) @@ -421,8 +422,8 @@ func (h *ProviderHandler) DeleteProvider(ctx *fasthttp.RequestCtx) { SendJSON(ctx, response, h.logger) } -// ListKeys handles GET /api/keys - List all keys -func (h *ProviderHandler) ListKeys(ctx *fasthttp.RequestCtx) { +// listKeys handles GET /api/keys - List all keys +func (h *ProviderHandler) listKeys(ctx *fasthttp.RequestCtx) { keys, err := h.store.GetAllKeys() if err != nil { SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to get keys: %v", err), h.logger) diff --git a/transports/bifrost-http/handlers/websocket.go b/transports/bifrost-http/handlers/websocket.go index 15ee3adbdf..4e640574f6 100644 --- a/transports/bifrost-http/handlers/websocket.go +++ b/transports/bifrost-http/handlers/websocket.go @@ -24,30 +24,30 @@ type WebSocketClient struct { // WebSocketHandler manages WebSocket connections for real-time updates type WebSocketHandler struct { - logManager logging.LogManager - logger schemas.Logger + logManager logging.LogManager + logger schemas.Logger allowedOrigins []string - clients map[*websocket.Conn]*WebSocketClient - mu sync.RWMutex - stopChan chan struct{} // Channel to signal heartbeat goroutine to stop - done chan struct{} // Channel to signal when heartbeat goroutine has stopped + clients map[*websocket.Conn]*WebSocketClient + mu sync.RWMutex + stopChan chan struct{} // Channel to signal heartbeat goroutine to stop + done chan struct{} // Channel to signal when heartbeat goroutine has stopped } // NewWebSocketHandler creates a new WebSocket handler instance func NewWebSocketHandler(logManager logging.LogManager, logger schemas.Logger, allowedOrigins []string) *WebSocketHandler { return &WebSocketHandler{ - logManager: logManager, - logger: logger, + logManager: logManager, + logger: logger, allowedOrigins: allowedOrigins, - clients: make(map[*websocket.Conn]*WebSocketClient), - stopChan: make(chan struct{}), - done: make(chan struct{}), + clients: make(map[*websocket.Conn]*WebSocketClient), + stopChan: make(chan struct{}), + done: make(chan struct{}), } } // RegisterRoutes registers all WebSocket-related routes func (h *WebSocketHandler) RegisterRoutes(r *router.Router) { - r.GET("/ws/logs", h.HandleLogStream) + r.GET("/ws/logs", h.connectLogStream) } // getUpgrader returns a WebSocket upgrader configured with the current allowed origins @@ -82,10 +82,17 @@ func isLocalhost(host string) bool { host == "" } -// HandleLogStream handles WebSocket connections for real-time log streaming -func (h *WebSocketHandler) HandleLogStream(ctx *fasthttp.RequestCtx) { +// connectLogStream handles WebSocket connections for real-time log streaming +func (h *WebSocketHandler) connectLogStream(ctx *fasthttp.RequestCtx) { upgrader := h.getUpgrader() err := upgrader.Upgrade(ctx, func(ws *websocket.Conn) { + // Read safety & liveness + ws.SetReadLimit(50 << 20) // 50 MiB + ws.SetReadDeadline(time.Now().Add(60 * time.Second)) + ws.SetPongHandler(func(string) error { + ws.SetReadDeadline(time.Now().Add(60 * time.Second)) + return nil + }) // Create a new client with its own mutex client := &WebSocketClient{ conn: ws, diff --git a/transports/bifrost-http/lib/config.go b/transports/bifrost-http/lib/config.go index e365ca394f..b8a5ebb244 100644 --- a/transports/bifrost-http/lib/config.go +++ b/transports/bifrost-http/lib/config.go @@ -682,7 +682,7 @@ func (s *Config) GetProviderConfigRaw(provider schemas.ModelProvider) (*configst config, exists := s.Providers[provider] if !exists { - return nil, fmt.Errorf("provider %s not found", provider) + return nil, ErrNotFound } // Return direct reference for maximum performance - this is used by Bifrost core @@ -714,7 +714,7 @@ func (s *Config) GetProviderConfigRedacted(provider schemas.ModelProvider) (*con config, exists := s.Providers[provider] if !exists { - return nil, fmt.Errorf("provider %s not found", provider) + return nil, ErrNotFound } // Create a map for quick lookup of env vars for this provider diff --git a/transports/bifrost-http/lib/errors.go b/transports/bifrost-http/lib/errors.go new file mode 100644 index 0000000000..e2e37d0b3c --- /dev/null +++ b/transports/bifrost-http/lib/errors.go @@ -0,0 +1,5 @@ +package lib + +import "errors" + +var ErrNotFound = errors.New("not found") diff --git a/transports/bifrost-http/main.go b/transports/bifrost-http/main.go index bab4ec31b3..88127f73f4 100644 --- a/transports/bifrost-http/main.go +++ b/transports/bifrost-http/main.go @@ -89,6 +89,8 @@ import ( //go:embed all:ui var uiContent embed.FS +var Version string + var logger = bifrost.NewDefaultLogger(schemas.LogLevelInfo) // Command line flags @@ -110,8 +112,12 @@ var ( // - log-style: Logger output type (json or pretty). Default is JSON. func init() { + if Version == "" { + Version = "v1.0.0" + } + versionLine := fmt.Sprintf("ā•‘%s%s%sā•‘", strings.Repeat(" ", (61-2-len(Version))/2), Version, strings.Repeat(" ", (61-2-len(Version)+1)/2)) // Welcome to bifrost! - fmt.Println(` + fmt.Printf(` ╔═══════════════════════════════════════════════════════════╗ ā•‘ ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘ @@ -122,11 +128,15 @@ func init() { ā•‘ ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā• ā•‘ ā•‘ ā•‘ ║═══════════════════════════════════════════════════════════║ -ā•‘ The Fastest LLM Gateway ā•‘ +%s ║═══════════════════════════════════════════════════════════║ -ā•‘ https://github.com/maximhq/bifrost ā•‘ -ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•`) +ā•‘ The Fastest LLM Gateway ā•‘ +║═══════════════════════════════════════════════════════════║ +ā•‘ https://github.com/maximhq/bifrost ā•‘ +ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā• +`, versionLine) + handlers.SetVersion(Version) // Set default host from environment variable or use localhost defaultHost := os.Getenv("BIFROST_HOST") if defaultHost == "" { @@ -477,7 +487,6 @@ func main() { // Set up WebSocket callback for real-time log updates if wsHandler != nil && loggingPlugin != nil { loggingPlugin.SetLogCallback(wsHandler.BroadcastLogUpdate) - // Start WebSocket heartbeat wsHandler.StartHeartbeat() } diff --git a/ui/.prettierrc b/ui/.prettierrc index 8cbfaffa44..f73138e64f 100644 --- a/ui/.prettierrc +++ b/ui/.prettierrc @@ -3,11 +3,19 @@ "singleQuote": false, "bracketSpacing": true, "semi": true, - "jsxBracketSameLine": false, + "bracketSameLine": false, "useTabs": true, "tabWidth": 2, "trailingComma": "all", - "plugins": ["prettier-plugin-tailwindcss"], - "tailwindAttributes": ["buttonClassname"], - "tailwindFunctions": ["cn", "classNames"] -} + "plugins": [ + "prettier-plugin-tailwindcss" + ], + "tailwindAttributes": [ + "buttonClassname" + ], + "tailwindFunctions": [ + "cn", + "classNames" + ], + "endOfLine": "lf" +} \ No newline at end of file diff --git a/ui/app/config/page.tsx b/ui/app/config/page.tsx index ada88266f4..5fcd58436c 100644 --- a/ui/app/config/page.tsx +++ b/ui/app/config/page.tsx @@ -1,7 +1,7 @@ "use client"; -import PluginsForm from "@/app/config/views/plugins-form"; -import FullPageLoader from "@/components/full-page-loader"; +import PluginsForm from "@/app/config/views/pluginsForm"; +import FullPageLoader from "@/components/fullPageLoader"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; diff --git a/ui/app/config/views/mcp-client-form.tsx b/ui/app/config/views/mcpClientForm.tsx similarity index 100% rename from ui/app/config/views/mcp-client-form.tsx rename to ui/app/config/views/mcpClientForm.tsx diff --git a/ui/app/config/views/mcp-clients-lists.tsx b/ui/app/config/views/mcpClientsLists.tsx similarity index 98% rename from ui/app/config/views/mcp-clients-lists.tsx rename to ui/app/config/views/mcpClientsLists.tsx index 0bf6e37660..fd659268c1 100644 --- a/ui/app/config/views/mcp-clients-lists.tsx +++ b/ui/app/config/views/mcpClientsLists.tsx @@ -1,6 +1,6 @@ "use client"; -import ClientForm from "@/app/config/views/mcp-client-form"; +import ClientForm from "@/app/config/views/mcpClientForm"; import { AlertDialog, AlertDialogAction, @@ -11,7 +11,7 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; +} from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; diff --git a/ui/app/config/views/plugins-form.tsx b/ui/app/config/views/pluginsForm.tsx similarity index 95% rename from ui/app/config/views/plugins-form.tsx rename to ui/app/config/views/pluginsForm.tsx index 47f59339b9..49e3c002cc 100644 --- a/ui/app/config/views/plugins-form.tsx +++ b/ui/app/config/views/pluginsForm.tsx @@ -3,19 +3,19 @@ import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; -import { Separator } from "@/components/ui/separator"; -import { getErrorMessage, useGetPluginsQuery, useUpdatePluginMutation, useCreatePluginMutation, useGetProvidersQuery } from "@/lib/store"; -import { CacheConfig, ModelProvider } from "@/lib/types/config"; -import { useEffect, useState, useMemo, useCallback, useRef } from "react"; -import { toast } from "sonner"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { Switch } from "@/components/ui/switch"; import { getProviderLabel } from "@/lib/constants/logs"; -import { Loader2 } from "lucide-react"; +import { getErrorMessage, useCreatePluginMutation, useGetPluginsQuery, useGetProvidersQuery, useUpdatePluginMutation } from "@/lib/store"; +import { CacheConfig, ModelProviderName } from "@/lib/types/config"; import { SEMANTIC_CACHE_PLUGIN } from "@/lib/types/plugins"; +import { Loader2 } from "lucide-react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { toast } from "sonner"; const defaultCacheConfig: CacheConfig = { - provider: "openai" as ModelProvider, + provider: "openai" as ModelProviderName, keys: [], embedding_model: "text-embedding-3-small", ttl_seconds: 300, @@ -35,7 +35,7 @@ export default function PluginsForm({ isVectorStoreEnabled }: PluginsFormProps) const { data: providersData, error: providersError, isLoading: providersLoading } = useGetProvidersQuery(); - const providers = useMemo(() => providersData?.providers || [], [providersData]); + const providers = useMemo(() => providersData || [], [providersData]); useEffect(() => { if (providersError) { @@ -65,7 +65,7 @@ export default function PluginsForm({ isVectorStoreEnabled }: PluginsFormProps) if (providers.length > 0 && !semanticCachePlugin?.config) { setCacheConfig((prev) => ({ ...prev, - provider: providers[0].name as ModelProvider, + provider: providers[0].name as ModelProviderName, })); } }, [providers, semanticCachePlugin?.config]); @@ -224,7 +224,10 @@ export default function PluginsForm({ isVectorStoreEnabled }: PluginsFormProps)
- updateCacheConfig({ provider: value })} + > diff --git a/ui/app/docs/page.tsx b/ui/app/docs/page.tsx index 2fc32e1249..0482b9d06e 100644 --- a/ui/app/docs/page.tsx +++ b/ui/app/docs/page.tsx @@ -1,7 +1,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import GradientHeader from "@/components/ui/gradient-header"; +import GradientHeader from "@/components/ui/gradientHeader"; import { BookOpen, Code, ExternalLink, FileText, GitBranch, Play, Shield, Users, Zap } from "lucide-react"; import Link from "next/link"; diff --git a/ui/app/governance/page.tsx b/ui/app/governance/page.tsx index b21467aaca..b4dafb4ef8 100644 --- a/ui/app/governance/page.tsx +++ b/ui/app/governance/page.tsx @@ -1,6 +1,6 @@ "use client"; -import FullPageLoader from "@/components/full-page-loader"; +import FullPageLoader from "@/components/fullPageLoader"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { getErrorMessage, useGetCoreConfigQuery, useGetCustomersQuery, useGetTeamsQuery, useGetVirtualKeysQuery } from "@/lib/store"; import { useEffect, useState } from "react"; diff --git a/ui/app/governance/views/customer-dialog.tsx b/ui/app/governance/views/customer-dialog.tsx index 1f4627a9b8..d9b37bf4bd 100644 --- a/ui/app/governance/views/customer-dialog.tsx +++ b/ui/app/governance/views/customer-dialog.tsx @@ -1,12 +1,12 @@ "use client"; -import FormFooter from "@/components/form-footer"; +import FormFooter from "@/components/formFooter"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import NumberAndSelect from "@/components/ui/number-and-select"; +import NumberAndSelect from "@/components/ui/numberAndSelect"; import { resetDurationOptions } from "@/lib/constants/governance"; import { getErrorMessage, useCreateCustomerMutation, useUpdateCustomerMutation } from "@/lib/store"; import { CreateCustomerRequest, Customer, UpdateCustomerRequest } from "@/lib/types/governance"; diff --git a/ui/app/governance/views/customers-table.tsx b/ui/app/governance/views/customers-table.tsx index 616b1069fc..0a166c6e63 100644 --- a/ui/app/governance/views/customers-table.tsx +++ b/ui/app/governance/views/customers-table.tsx @@ -10,7 +10,7 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; +} from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; diff --git a/ui/app/governance/views/team-dialog.tsx b/ui/app/governance/views/team-dialog.tsx index 1d8341d99b..9b695c64fd 100644 --- a/ui/app/governance/views/team-dialog.tsx +++ b/ui/app/governance/views/team-dialog.tsx @@ -1,11 +1,11 @@ "use client"; -import FormFooter from "@/components/form-footer"; +import FormFooter from "@/components/formFooter"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import NumberAndSelect from "@/components/ui/number-and-select"; +import NumberAndSelect from "@/components/ui/numberAndSelect"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { resetDurationOptions } from "@/lib/constants/governance"; import { getErrorMessage, useCreateTeamMutation, useUpdateTeamMutation } from "@/lib/store"; diff --git a/ui/app/governance/views/teams-table.tsx b/ui/app/governance/views/teams-table.tsx index 6c417fe832..b0855e9c41 100644 --- a/ui/app/governance/views/teams-table.tsx +++ b/ui/app/governance/views/teams-table.tsx @@ -10,7 +10,7 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; +} from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; diff --git a/ui/app/governance/views/virtual-key-dialog.tsx b/ui/app/governance/views/virtual-key-dialog.tsx index 3dd1a28020..7a59dd3bbf 100644 --- a/ui/app/governance/views/virtual-key-dialog.tsx +++ b/ui/app/governance/views/virtual-key-dialog.tsx @@ -1,14 +1,14 @@ "use client"; -import FormFooter from "@/components/form-footer"; +import FormFooter from "@/components/formFooter"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { MultiSelect } from "@/components/ui/multi-select"; -import NumberAndSelect from "@/components/ui/number-and-select"; +import { MultiSelect } from "@/components/ui/multiSelect"; +import NumberAndSelect from "@/components/ui/numberAndSelect"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { DottedSeparator } from "@/components/ui/separator"; -import { TagInput } from "@/components/ui/tag-input"; +import { TagInput } from "@/components/ui/tagInput"; import { Textarea } from "@/components/ui/textarea"; import Toggle from "@/components/ui/toggle"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; diff --git a/ui/app/governance/views/virtual-keys-table.tsx b/ui/app/governance/views/virtual-keys-table.tsx index ea7687745a..58181e8cb0 100644 --- a/ui/app/governance/views/virtual-keys-table.tsx +++ b/ui/app/governance/views/virtual-keys-table.tsx @@ -10,7 +10,7 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; +} from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx index 35ee226035..8d5bf1a117 100644 --- a/ui/app/layout.tsx +++ b/ui/app/layout.tsx @@ -1,10 +1,10 @@ "use client"; -import FullPageLoader from "@/components/full-page-loader"; -import NotAvailableBanner from "@/components/not-available-banner"; -import ProgressProvider from "@/components/progress-bar"; +import FullPageLoader from "@/components/fullPageLoader"; +import NotAvailableBanner from "@/components/notAvailableBanner"; +import ProgressProvider from "@/components/progressBar"; import Sidebar from "@/components/sidebar"; -import { ThemeProvider } from "@/components/theme-provider"; +import { ThemeProvider } from "@/components/themeProvider"; import { SidebarProvider } from "@/components/ui/sidebar"; import { WebSocketProvider } from "@/hooks/useWebSocket"; import { getErrorMessage, ReduxProvider, useGetCoreConfigQuery } from "@/lib/store"; @@ -12,6 +12,7 @@ import { Geist, Geist_Mono } from "next/font/google"; import { useEffect } from "react"; import { toast, Toaster } from "sonner"; import "./globals.css"; +import { NuqsAdapter } from "nuqs/adapters/next/app"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -54,7 +55,9 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - {children} + + {children} + diff --git a/ui/app/logs/page.tsx b/ui/app/logs/page.tsx index bad11095d0..653e5eba86 100644 --- a/ui/app/logs/page.tsx +++ b/ui/app/logs/page.tsx @@ -1,10 +1,10 @@ "use client"; import { createColumns } from "@/app/logs/views/columns"; -import { EmptyState } from "@/app/logs/views/empty-state"; -import { LogDetailSheet } from "@/app/logs/views/log-detail-sheet"; -import { LogsDataTable } from "@/app/logs/views/logs-table"; -import FullPageLoader from "@/components/full-page-loader"; +import { EmptyState } from "@/app/logs/views/emptyState"; +import { LogDetailSheet } from "@/app/logs/views/logDetailsSheet"; +import { LogsDataTable } from "@/app/logs/views/logsTable"; +import FullPageLoader from "@/components/fullPageLoader"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Card, CardContent } from "@/components/ui/card"; import { useWebSocket } from "@/hooks/useWebSocket"; diff --git a/ui/app/logs/views/audio-player.tsx b/ui/app/logs/views/audioPlayer.tsx similarity index 100% rename from ui/app/logs/views/audio-player.tsx rename to ui/app/logs/views/audioPlayer.tsx diff --git a/ui/app/logs/views/code-editor.css b/ui/app/logs/views/codeEditor.css similarity index 100% rename from ui/app/logs/views/code-editor.css rename to ui/app/logs/views/codeEditor.css diff --git a/ui/app/logs/views/code-editor.tsx b/ui/app/logs/views/codeEditor.tsx similarity index 100% rename from ui/app/logs/views/code-editor.tsx rename to ui/app/logs/views/codeEditor.tsx diff --git a/ui/app/logs/views/columns.tsx b/ui/app/logs/views/columns.tsx index 90dee20c80..88cc6dab24 100644 --- a/ui/app/logs/views/columns.tsx +++ b/ui/app/logs/views/columns.tsx @@ -3,7 +3,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { ProviderIconType, RenderProviderIcon } from "@/lib/constants/icons"; -import { Provider, REQUEST_TYPE_COLORS, REQUEST_TYPE_LABELS, Status, STATUS_COLORS } from "@/lib/constants/logs"; +import { ProviderName, RequestTypeColors, RequestTypeLabels, Status, StatusColors } from "@/lib/constants/logs"; import { LogEntry } from "@/lib/types/logs"; import { ColumnDef } from "@tanstack/react-table"; import { ArrowUpDown } from "lucide-react"; @@ -19,7 +19,7 @@ export const createColumns = (): ColumnDef[] => [ ), cell: ({ row }) => { const timestamp = row.original.timestamp; - return
{new Date(timestamp).toLocaleString()}
; + return
{new Date(timestamp).toISOString()}
; }, }, { @@ -27,8 +27,8 @@ export const createColumns = (): ColumnDef[] => [ header: "Type", cell: ({ row }) => { return ( - - {REQUEST_TYPE_LABELS[row.original.object as keyof typeof REQUEST_TYPE_LABELS]} + + {RequestTypeLabels[row.original.object as keyof typeof RequestTypeLabels]} ); }, @@ -37,9 +37,9 @@ export const createColumns = (): ColumnDef[] => [ accessorKey: "provider", header: "Provider", cell: ({ row }) => { - const provider = row.original.provider as Provider; + const provider = row.original.provider as ProviderName; return ( - + {provider} @@ -49,7 +49,7 @@ export const createColumns = (): ColumnDef[] => [ { accessorKey: "model", header: "Model", - cell: ({ row }) =>
{row.original.model}
, + cell: ({ row }) =>
{row.original.model}
, }, { accessorKey: "latency", @@ -61,7 +61,9 @@ export const createColumns = (): ColumnDef[] => [ ), cell: ({ row }) => { const latency = row.original.latency; - return
{latency ? `${latency.toLocaleString()}ms` : "N/A"}
; + return ( +
{latency === undefined || latency === null ? "N/A" : `${latency.toLocaleString()}ms`}
+ ); }, }, { @@ -75,11 +77,11 @@ export const createColumns = (): ColumnDef[] => [ cell: ({ row }) => { const tokenUsage = row.original.token_usage; if (!tokenUsage) { - return
N/A
; + return
N/A
; } return ( -
+
{tokenUsage.total_tokens.toLocaleString()} ({tokenUsage.prompt_tokens}+{tokenUsage.completion_tokens})
@@ -113,7 +115,7 @@ export const createColumns = (): ColumnDef[] => [ cell: ({ row }) => { const status = row.original.status as Status; return ( - + {status} ); diff --git a/ui/app/logs/views/empty-state.tsx b/ui/app/logs/views/emptyState.tsx similarity index 99% rename from ui/app/logs/views/empty-state.tsx rename to ui/app/logs/views/emptyState.tsx index c50b72dfaf..e0d8c6eada 100644 --- a/ui/app/logs/views/empty-state.tsx +++ b/ui/app/logs/views/emptyState.tsx @@ -8,7 +8,7 @@ import { getExampleBaseUrl } from "@/lib/utils/port"; import { AlertTriangle, Copy } from "lucide-react"; import { useMemo, useState } from "react"; import { toast } from "sonner"; -import { CodeEditor } from "./code-editor"; +import { CodeEditor } from "./codeEditor"; type Provider = "openai" | "anthropic" | "genai" | "litellm" | "langchain"; type Language = "python" | "typescript"; diff --git a/ui/app/logs/views/filters.tsx b/ui/app/logs/views/filters.tsx index 7133b2e7ae..4638b6b784 100644 --- a/ui/app/logs/views/filters.tsx +++ b/ui/app/logs/views/filters.tsx @@ -2,11 +2,11 @@ import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { PROVIDERS, REQUEST_TYPE_LABELS, REQUEST_TYPES, STATUSES } from "@/lib/constants/logs"; +import { ProviderNames, RequestTypeLabels, RequestTypes, Statuses } from "@/lib/constants/logs"; import { useGetAvailableModelsQuery } from "@/lib/store"; import type { LogFilters } from "@/lib/types/logs"; import { cn } from "@/lib/utils"; -import { Check, Search } from "lucide-react"; +import { Check, FilterIcon, Search } from "lucide-react"; import { useCallback, useEffect, useRef, useState } from "react"; interface LogFiltersProps { @@ -90,9 +90,9 @@ export function LogFilters({ filters, onFiltersChange }: LogFiltersProps) { }; const FILTER_OPTIONS = { - Status: STATUSES, - Providers: PROVIDERS, - Type: REQUEST_TYPES, + Status: Statuses, + Providers: ProviderNames, + Type: RequestTypes, Models: modelsLoading ? ["Loading models..."] : availableModels, } as const; @@ -112,6 +112,7 @@ export function LogFilters({ filters, onFiltersChange }: LogFiltersProps) {
- {category === "Type" ? REQUEST_TYPE_LABELS[value as keyof typeof REQUEST_TYPE_LABELS] : value} + {category === "Type" ? RequestTypeLabels[value as keyof typeof RequestTypeLabels] : value} ); diff --git a/ui/app/logs/views/log-detail-sheet.tsx b/ui/app/logs/views/logDetailsSheet.tsx similarity index 89% rename from ui/app/logs/views/log-detail-sheet.tsx rename to ui/app/logs/views/logDetailsSheet.tsx index de6504fa2b..45477d305f 100644 --- a/ui/app/logs/views/log-detail-sheet.tsx +++ b/ui/app/logs/views/logDetailsSheet.tsx @@ -3,17 +3,16 @@ import { Badge } from "@/components/ui/badge"; import { DottedSeparator } from "@/components/ui/separator"; import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { ProviderIconType, RenderProviderIcon } from "@/lib/constants/icons"; -import { REQUEST_TYPE_COLORS, REQUEST_TYPE_LABELS, Status, STATUS_COLORS } from "@/lib/constants/logs"; +import { RequestTypeColors, RequestTypeLabels, Status, StatusColors } from "@/lib/constants/logs"; import { LogEntry } from "@/lib/types/logs"; -import { DollarSign, FileText, Info, Timer } from "lucide-react"; +import { DollarSign, FileText, Timer } from "lucide-react"; import moment from "moment"; -import { CodeEditor } from "./code-editor"; -import LogEntryDetailsView from "./log-entry-details-view"; -import LogMessageView from "./log-message-view"; -import SpeechView from "./speech-view"; -import TranscriptionView from "./transcription-view"; +import { CodeEditor } from "./codeEditor"; +import LogEntryDetailsView from "./logEntryDetailsView"; +import LogMessageView from "./logMessageView"; +import SpeechView from "./speechView"; +import TranscriptionView from "./transcriptionView"; interface LogDetailSheetProps { log: LogEntry | null; @@ -45,7 +44,7 @@ export function LogDetailSheet({ log, open, onOpenChange }: LogDetailSheetProps) {log.status === "success" &&

Request ID: {log.id}

} - + {log.status}
@@ -94,10 +93,10 @@ export function LogDetailSheet({ log, open, onOpenChange }: LogDetailSheetProps) value={
- {REQUEST_TYPE_LABELS[log.object as keyof typeof REQUEST_TYPE_LABELS] ?? log.object ?? "unknown"} + {RequestTypeLabels[log.object as keyof typeof RequestTypeLabels] ?? log.object ?? "unknown"}
} /> @@ -279,19 +278,6 @@ export function LogDetailSheet({ log, open, onOpenChange }: LogDetailSheetProps) <>
Response
- {log.stream && ( - - - - - - - The response shown may appear incomplete or out of order due to the way streamed data is accumulated for real-time - display. - - - - )}
diff --git a/ui/app/logs/views/log-entry-details-view.tsx b/ui/app/logs/views/logEntryDetailsView.tsx similarity index 100% rename from ui/app/logs/views/log-entry-details-view.tsx rename to ui/app/logs/views/logEntryDetailsView.tsx diff --git a/ui/app/logs/views/log-message-view.tsx b/ui/app/logs/views/logMessageView.tsx similarity index 79% rename from ui/app/logs/views/log-message-view.tsx rename to ui/app/logs/views/logMessageView.tsx index a72ccbf2b7..0b5cbcece9 100644 --- a/ui/app/logs/views/log-message-view.tsx +++ b/ui/app/logs/views/logMessageView.tsx @@ -1,5 +1,5 @@ -import { CodeEditor } from "./code-editor"; import { BifrostMessage } from "@/lib/types/logs"; +import { CodeEditor } from "./codeEditor"; interface LogMessageViewProps { message: BifrostMessage; @@ -14,9 +14,13 @@ const isJson = (text: string) => { } }; -const cleanJson = (text: any) => { +const cleanJson = (text: unknown) => { try { - return JSON.parse(text); + if (typeof text === "string") return JSON.parse(text); // parse JSON strings + if (Array.isArray(text)) return text; // keep arrays as-is + if (text !== null && typeof text === "object") return text; // keep objects as-is + if (typeof text === "number" || typeof text === "boolean") return text; + return "Invalid payload"; } catch { return text; } diff --git a/ui/app/logs/views/logs-table.tsx b/ui/app/logs/views/logsTable.tsx similarity index 94% rename from ui/app/logs/views/logs-table.tsx rename to ui/app/logs/views/logsTable.tsx index 8b27cc5442..fe3d90f488 100644 --- a/ui/app/logs/views/logs-table.tsx +++ b/ui/app/logs/views/logsTable.tsx @@ -148,7 +148,7 @@ export function LogsDataTable({
- @@ -158,7 +158,12 @@ export function LogsDataTable({ of {totalPages}
-
diff --git a/ui/app/logs/views/speech-view.tsx b/ui/app/logs/views/speechView.tsx similarity index 98% rename from ui/app/logs/views/speech-view.tsx rename to ui/app/logs/views/speechView.tsx index 3c482cf100..3200cad60f 100644 --- a/ui/app/logs/views/speech-view.tsx +++ b/ui/app/logs/views/speechView.tsx @@ -1,7 +1,7 @@ import { BifrostSpeech, SpeechInput } from "@/lib/types/logs"; import { AlertCircle, Play, Volume2 } from "lucide-react"; import React, { Component, useMemo } from "react"; -import AudioPlayer from "./audio-player"; +import AudioPlayer from "./audioPlayer"; interface SpeechViewProps { speechInput?: SpeechInput; diff --git a/ui/app/logs/views/transcription-view.tsx b/ui/app/logs/views/transcriptionView.tsx similarity index 97% rename from ui/app/logs/views/transcription-view.tsx rename to ui/app/logs/views/transcriptionView.tsx index d5d9cbbe46..dd30539fbb 100644 --- a/ui/app/logs/views/transcription-view.tsx +++ b/ui/app/logs/views/transcriptionView.tsx @@ -1,9 +1,8 @@ -import React from "react"; -import { BifrostTranscribe, TranscriptionInput } from "@/lib/types/logs"; -import { FileAudio, Clock, Mic } from "lucide-react"; -import { CodeEditor } from "./code-editor"; import { Badge } from "@/components/ui/badge"; -import AudioPlayer from "./audio-player"; +import { BifrostTranscribe, TranscriptionInput } from "@/lib/types/logs"; +import { Clock, FileAudio, Mic } from "lucide-react"; +import AudioPlayer from "./audioPlayer"; +import { CodeEditor } from "./codeEditor"; interface TranscriptionViewProps { transcriptionInput?: TranscriptionInput; diff --git a/ui/app/mcp-clients/page.tsx b/ui/app/mcp-clients/page.tsx index b0660df526..fa2341ea67 100644 --- a/ui/app/mcp-clients/page.tsx +++ b/ui/app/mcp-clients/page.tsx @@ -1,7 +1,7 @@ "use client"; -import MCPClientsList from "@/app/config/views/mcp-clients-lists"; -import FullPageLoader from "@/components/full-page-loader"; +import MCPClientsList from "@/app/config/views/mcpClientsLists"; +import FullPageLoader from "@/components/fullPageLoader"; import { useToast } from "@/hooks/use-toast"; import { getErrorMessage, useGetMCPClientsQuery } from "@/lib/store"; import { useEffect } from "react"; diff --git a/ui/app/plugins/page.tsx b/ui/app/plugins/page.tsx index d3fed7e32f..7a7d975f5a 100644 --- a/ui/app/plugins/page.tsx +++ b/ui/app/plugins/page.tsx @@ -4,10 +4,10 @@ import { Alert, AlertDescription } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import GradientHeader from "@/components/ui/gradient-header"; +import GradientHeader from "@/components/ui/gradientHeader"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { GithubLogoIcon } from "@phosphor-icons/react"; -import { AlertTriangle, ChevronRight, Code, Container, Database, Info, Monitor, Puzzle, Rocket, Shield, Terminal, Zap } from "lucide-react"; +import { AlertTriangle, ChevronRight, Code, Container, Database, Info, Monitor, Puzzle, Shield, Terminal, Zap } from "lucide-react"; import { useTheme } from "next-themes"; import Image from "next/image"; import Link from "next/link"; @@ -120,12 +120,6 @@ export default function PluginsPage() { Browse All Plugins -
@@ -373,13 +367,7 @@ export default function PluginsPage() { - - - - - - -
- - - {tabs.map((tab) => ( - - {tab.label} - - ))} - - - {/* Container for Tab Content */} -
-
- {/* Custom Settings Tab */} - {isCustomProvider && selectedTab === "api-structure" && ( -
-
-
- - updateField("customProviderName", e.target.value)} - className={cn(isUpdating && "bg-muted")} - /> -

A unique name for your custom provider

-
-
- - -

The underlying provider this custom provider will use

-
-
- - {/* Allowed Requests Configuration */} -
-
Allowed Request Types
-

Select which request types this custom provider can handle

- -
-
-
- - updateAllowedRequest("text_completion", checked)} - /> -
-
- - updateAllowedRequest("chat_completion", checked)} - /> -
-
- - updateAllowedRequest("chat_completion_stream", checked)} - /> -
-
- - updateAllowedRequest("embedding", checked)} - /> -
-
-
-
- - updateAllowedRequest("speech", checked)} - /> -
-
- - updateAllowedRequest("speech_stream", checked)} - /> -
-
- - updateAllowedRequest("transcription", checked)} - /> -
-
- - updateAllowedRequest("transcription_stream", checked)} - /> -
-
-
-
-
- )} - - {/* API Keys Tab */} - {keysRequired && selectedTab === "api-keys" && ( -
-
- -
- {effectiveProviderType === "bedrock" && ( - - - IAM Role Authentication - - Leave both Access Key and Secret Key empty to use IAM roles attached to your environment (EC2, Lambda, ECS, EKS). - This is the recommended approach for production deployments. - - - )} -
- {keys.map((key, index) => ( -
-
- {effectiveProviderType !== "vertex" && effectiveProviderType !== "bedrock" && ( -
-
API Key
- updateKey(index, "value", e.target.value)} - type="text" - className="flex-1" - /> -
- )} - -
-
- - - - - - - - - -

Determines traffic distribution between keys. Higher weights receive more requests.

-
-
-
-
- updateKey(index, "weight", e.target.value)} - type="number" - step="0.01" - min="0" - className={cn("w-20", keysRequired && (key.weight < 0 || key.weight > 1) && "border-destructive")} - /> -
-
-
-
- - - - - - - - - -

Comma-separated list of models this key applies to. Leave blank for all models.

-
-
-
-
- updateKey(index, "models", newModels)} - /> -
- - {/* Azure Key Configuration */} - {effectiveProviderType === "azure" && ( -
-
- - updateKeyAzureConfig(index, "endpoint", e.target.value)} - className="" - /> -
-
- - updateKeyAzureConfig(index, "api_version", e.target.value)} - /> -
-
- -
- JSON object mapping model names to deployment names -
-