diff --git a/README.md b/README.md index aab6d34d77..9c172deb2c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Bifrost is an open-source middleware that serves as a unified gateway to various ### A. Using Bifrost as an HTTP Server 1. **Create `config.json`**: This file should contain your provider settings and API keys. + ```json [ "openai": { @@ -24,10 +25,16 @@ Bifrost is an open-source middleware that serves as a unified gateway to various }, ] ``` -2. **Create `.env`**: Set up your environment variables here. - ```env - OPENAI_API_KEY=your_openai_api_key; + +2. **Setup your Environment**: Add your environment variable to the session. + + ```bash + export OPENAI_API_KEY=your_openai_api_key + export ANTHROPIC_API_KEY=your_anthropic_api_key ``` + + Note: Make sure to add all the variables stated in your config.json file. + 3. **Start the Bifrost HTTP Server**: You have two options to run the server, either using Go Binary or a Docker setup if go is not installed. @@ -43,13 +50,13 @@ Bifrost is an open-source middleware that serves as a unified gateway to various - If it's in your PATH: ```bash - bifrost-http -config config.json -env .env -port 8080 -pool-size 300 + bifrost-http -config config.json -port 8080 -pool-size 300 ``` - Otherwise: ```bash - ./bifrost-http -config config.json -env .env -port 8080 -pool-size 300 + ./bifrost-http -config config.json -port 8080 -pool-size 300 ``` #### ii) OR Using Docker @@ -65,17 +72,19 @@ Bifrost is an open-source middleware that serves as a unified gateway to various ```bash docker build \ --build-arg CONFIG_PATH=./config.example.json \ - --build-arg ENV_PATH=./.env.sample \ --build-arg PORT=8080 \ --build-arg POOL_SIZE=300 \ -t bifrost-transports . ``` - Run the Docker container: + ```bash - docker run -p 8080:8080 bifrost-transports + docker run -p 8080:8080 bifrost-transports -e OPENAI_API_KEY -e ANTHROPIC_API_KEY ``` + Note: Make sure to add all the variables stated in your config.json file. + 4. **Using the API**: Once the server is running, you can send requests to the HTTP endpoints. ```bash diff --git a/transports/.env.sample b/transports/.env.sample deleted file mode 100644 index 30e582a355..0000000000 --- a/transports/.env.sample +++ /dev/null @@ -1,10 +0,0 @@ -OPENAI_API_KEY = YOUR_OPENAI_API_KEY -ANTHROPIC_API_KEY = YOUR_ANTHROPIC_API_KEY -BEDROCK_API_KEY = YOUR_BEDROCK_API_KEY -BEDROCK_ACCESS_KEY = YOUR_BEDROCK_ACCESS_KEY -COHERE_API_KEY = YOUR_COHERE_API_KEY -AZURE_API_KEY = YOUR_AZURE_API_KEY -AZURE_ENDPOINT = YOUR_AZURE_ENDPOINT - -MAXIM_API_KEY = YOUR_MAXIM_API_KEY -MAXIM_LOGGER_ID = YOUR_MAXIM_LOGGER_ID \ No newline at end of file diff --git a/transports/Dockerfile b/transports/Dockerfile index d7c2e35660..2b17560f74 100644 --- a/transports/Dockerfile +++ b/transports/Dockerfile @@ -15,7 +15,7 @@ RUN go mod init bifrost-transports && \ go get github.com/maximhq/bifrost/transports/bifrost-${TRANSPORT_TYPE}@latest # Build the binary from the fetched package with static linking -RUN go build -ldflags="-w -s" -o /app/main github.com/maximhq/bifrost/transports/${TRANSPORT_TYPE} && \ +RUN go build -ldflags="-w -s" -o /app/main github.com/maximhq/bifrost/transports/bifrost-${TRANSPORT_TYPE} && \ test -f /app/main || (echo "Build failed: /app/main not found" && exit 1) && \ ls -lh /app/main @@ -32,7 +32,6 @@ RUN mkdir -p /app/config # Define build-time variables for config file paths ARG CONFIG_PATH -ARG ENV_PATH ARG PORT ARG POOL_SIZE ARG DROP_EXCESS_REQUESTS @@ -48,16 +47,14 @@ ENV APP_PLUGINS=${PLUGINS:-""} ENV APP_MAXIM_LOG_REPO_ID=${MAXIM_LOG_REPO_ID:-""} ENV APP_PROMETHEUS_LABELS=${PROMETHEUS_LABELS:-""} -# Copy the config and environment files into the image +# Copy the config file into the image COPY ${CONFIG_PATH} /app/config/config.json -COPY ${ENV_PATH} /app/config/.env # Write a small script to validate config presence and run the app RUN echo '#!/bin/sh' > /app/entrypoint.sh && \ echo 'if [ ! -f /app/config/config.json ]; then echo "Missing config.json"; exit 1; fi' >> /app/entrypoint.sh && \ - echo 'if [ ! -f /app/config/.env ]; then echo "Missing .env"; exit 1; fi' >> /app/entrypoint.sh && \ echo 'if [ ! -f /app/main ]; then echo "Missing main binary"; exit 1; fi' >> /app/entrypoint.sh && \ - echo 'exec /app/main -config /app/config/config.json -env /app/config/.env -port "$APP_PORT" -pool-size "$APP_POOL_SIZE" -drop-excess-requests "$APP_DROP_EXCESS_REQUESTS" -plugins "$APP_PLUGINS" -maxim-log-repo-id "$APP_MAXIM_LOG_REPO_ID" -prometheus-labels "$APP_PROMETHEUS_LABELS"' >> /app/entrypoint.sh && \ + echo 'exec /app/main -config /app/config/config.json -port "$APP_PORT" -pool-size "$APP_POOL_SIZE" -drop-excess-requests "$APP_DROP_EXCESS_REQUESTS" -plugins "$APP_PLUGINS" -maxim-log-repo-id "$APP_MAXIM_LOG_REPO_ID" -prometheus-labels "$APP_PROMETHEUS_LABELS"' >> /app/entrypoint.sh && \ chmod +x /app/entrypoint.sh # Expose the port defined by argument diff --git a/transports/README.md b/transports/README.md index eec2234304..f486c4e6ab 100644 --- a/transports/README.md +++ b/transports/README.md @@ -34,7 +34,7 @@ This package contains clients for various transports that can be used to spin up Bifrost uses a combination of a JSON configuration file and environment variables: 1. **JSON Configuration File**: Bifrost requires a configuration file to set up the gateway. This includes all your provider-level settings, keys, and meta configs for each of your providers. -2. **Environment Variables**: If you don't want to include your keys in your config file, you can provide a `.env` file and add a prefix of `env.` followed by its key in your `.env` file. +2. **Environment Variables**: If you don't want to include your keys in your config file, you can add a prefix of `env.` followed by its key in your environment. ```json { @@ -48,7 +48,7 @@ Bifrost uses a combination of a JSON configuration file and environment variable } ``` -In this example, `OPENAI_API_KEY` refers to a key in the `.env` file. At runtime, its value will be used to replace the placeholder. +In this example, `OPENAI_API_KEY` refers to a key set in your environment. At runtime, its value will be used to replace the placeholder. The same setup applies to keys in meta configs of all providers: @@ -61,9 +61,9 @@ The same setup applies to keys in meta configs of all providers: } ``` -In this example, `BEDROCK_ACCESS_KEY` and `BEDROCK_REGION` refer to keys in the `.env` file. +In this example, `BEDROCK_ACCESS_KEY` and `BEDROCK_REGION` refer to keys in the environment. -Please refer to `config.example.json` and `.env.sample` for examples. +Please refer to `config.example.json` for examples. ### Docker Setup @@ -78,7 +78,6 @@ curl -L -o Dockerfile https://raw.githubusercontent.com/maximhq/bifrost/main/tra ```bash docker build \ --build-arg CONFIG_PATH=./config.example.json \ - --build-arg ENV_PATH=./.env.sample \ --build-arg PORT=8080 \ --build-arg POOL_SIZE=300 \ -t bifrost-transports . @@ -87,7 +86,20 @@ docker build \ 3. Run the Docker container: ```bash -docker run --name bifrost-api -p 8080:8080 bifrost-transports +docker run -p 8080:8080 bifrost-transports -e OPENAI_API_KEY -e ANTHROPIC_API_KEY +``` + +Note: In the command above, `OPENAI_API_KEY` and `ANTHROPIC_API_KEY` are just example environment variables. +You need to pass all environment variables referenced in your `config.json` file that start with the prefix `env.` to the docker run command using the -e flag. This ensures Docker sets them correctly inside the container. + +example usage: Suppose your config.json only contains one environment variable placeholder, `env.COHERE_API_KEY`. Here’s how you would run it: + +```bash +export COHERE_API_KEY=your_cohere_api_key + +docker build --build-arg CONFIG_PATH=./config.example.json -t bifrost-transports . + +docker run -p 8080:8080 bifrost-transports -e COHERE_API_KEY ``` You can also add a flag for `DROP_EXCESS_REQUESTS=false` in your Docker build command to drop excess requests when the buffer is full. Read more about `DROP_EXCESS_REQUESTS` and `POOL_SIZE` [here](https://github.com/maximhq/bifrost/tree/main?tab=README-ov-file#additional-configurations). @@ -109,13 +121,13 @@ go install github.com/maximhq/bifrost/transports/bifrost-http@latest - If it's in your PATH: ```bash -bifrost-http -config config.json -env .env -port 8080 -pool-size 300 +bifrost-http -config config.json -port 8080 -pool-size 300 ``` - Otherwise: ```bash -./bifrost-http -config config.json -env .env -port 8080 -pool-size 300 +./bifrost-http -config config.json -port 8080 -pool-size 300 ``` You can also add a flag for `-drop-excess-requests=false` in your command to drop excess requests when the buffer is full. Read more about `DROP_EXCESS_REQUESTS` and `POOL_SIZE` [here](https://github.com/maximhq/bifrost/tree/main?tab=README-ov-file#additional-configurations). diff --git a/transports/bifrost-http/lib/account.go b/transports/bifrost-http/lib/account.go index 156f92603c..143a7da33e 100644 --- a/transports/bifrost-http/lib/account.go +++ b/transports/bifrost-http/lib/account.go @@ -5,11 +5,11 @@ package lib import ( "errors" "fmt" + "os" "reflect" "strings" "sync" - "github.com/joho/godotenv" "github.com/maximhq/bifrost/core/schemas" ) @@ -70,23 +70,18 @@ func (baseAccount *BaseAccount) GetConfigForProvider(providerKey schemas.ModelPr return providerConfig, nil } -// readKeys reads environment variables from a .env file and updates the provider configurations. +// ReadKeys reads environment variables from the environment and updates the provider configurations. // It replaces values starting with "env." in the config with actual values from the environment. // Returns an error if any required environment variable is missing. -func (baseAccount *BaseAccount) ReadKeys(envLocation string) error { - envVars, err := godotenv.Read(envLocation) - if err != nil { - return fmt.Errorf("failed to read .env file: %w", err) - } - +func (baseAccount *BaseAccount) ReadKeys() error { // Helper function to check and replace env values replaceEnvValue := func(value string) (string, error) { if strings.HasPrefix(value, "env.") { envKey := strings.TrimPrefix(value, "env.") - if envValue, exists := envVars[envKey]; exists { + if envValue := os.Getenv(envKey); envValue != "" { return envValue, nil } - return "", fmt.Errorf("environment variable %s not found in .env file", envKey) + return "", fmt.Errorf("environment variable %s not found in the environment", envKey) } return value, nil } diff --git a/transports/bifrost-http/lib/config.go b/transports/bifrost-http/lib/config.go index 41614f875e..8c25a35c06 100644 --- a/transports/bifrost-http/lib/config.go +++ b/transports/bifrost-http/lib/config.go @@ -30,8 +30,7 @@ type ConfigMap map[schemas.ModelProvider]ProviderConfig // Panics if the config file cannot be read or parsed. // // In the config file, use placeholder keys (e.g., env.OPENAI_API_KEY) instead of hardcoding actual values. -// These placeholders will be replaced with the corresponding values from the .env file. -// Location of the .env file is specified by the -env flag. It +// These placeholders will be replaced with the corresponding values from the environment variables. // Example: // // "keys":[{ @@ -40,7 +39,7 @@ type ConfigMap map[schemas.ModelProvider]ProviderConfig // "weight": 1.0 // }] // -// In this example, OPENAI_API_KEY refers to a key in the .env file. At runtime, its value will be used to replace the placeholder. +// In this example, OPENAI_API_KEY refers to a key in the environment variables. At runtime, its value will be used to replace the placeholder. // Same setup applies to keys in meta configs of all the providers. // Example: // @@ -49,7 +48,7 @@ type ConfigMap map[schemas.ModelProvider]ProviderConfig // "region": "env.BEDROCK_REGION" // } // -// In this example, BEDROCK_ACCESS_KEY and BEDROCK_REGION refer to keys in the .env file. +// In this example, BEDROCK_ACCESS_KEY and BEDROCK_REGION refer to keys in environment variables. func ReadConfig(configLocation string) ConfigMap { data, err := os.ReadFile(configLocation) if err != nil { diff --git a/transports/bifrost-http/main.go b/transports/bifrost-http/main.go index aec07a8a70..341af0398a 100644 --- a/transports/bifrost-http/main.go +++ b/transports/bifrost-http/main.go @@ -7,13 +7,12 @@ // // Configuration is handled through a JSON config file and environment variables: // - Use -config flag to specify the config file location -// - Use -env flag to specify the .env file location // - Use -port flag to specify the server port (default: 8080) // - Use -pool-size flag to specify the initial connection pool size (default: 300) // // Example usage: -// go run http.go -config config.example.json -env .env -port 8080 -pool-size 300 -// after setting the environment variables present in config.example.json in your .env file. +// go run http.go -config config.example.json -port 8080 -pool-size 300 +// after setting the environment variables present in config.example.json in the environment. package main @@ -46,7 +45,6 @@ var ( dropExcessRequests bool // Drop excess requests port string // Port to run the server on configPath string // Path to the config file - envPath string // Path to the .env file pluginsToLoad []string // Path to the plugins maximLogRepoId string // ID of the Maxim log repo prometheusLabels []string // Labels to add to Prometheus metrics (optional) @@ -57,7 +55,6 @@ var ( // - pool-size: Initial connection pool size (default: 300) // - port: Server port (default: 8080) // - config: Path to config file (required) -// - env: Path to .env file (required) // - drop-excess-requests: Whether to drop excess requests func init() { pluginString := "" @@ -66,7 +63,6 @@ func init() { flag.IntVar(&initialPoolSize, "pool-size", 300, "Initial pool size for Bifrost") flag.StringVar(&port, "port", "8080", "Port to run the server on") flag.StringVar(&configPath, "config", "", "Path to the config file") - flag.StringVar(&envPath, "env", "", "Path to the .env file") flag.BoolVar(&dropExcessRequests, "drop-excess-requests", false, "Drop excess requests") flag.StringVar(&pluginString, "plugins", "", "Comma separated list of plugins to load") flag.StringVar(&maximLogRepoId, "maxim-log-repo-id", "", "ID of the Maxim log repo") @@ -79,10 +75,6 @@ func init() { log.Fatalf("config path is required") } - if envPath == "" { - log.Fatalf("env path is required") - } - if prometheusLabelsString != "" { // Split and filter out empty strings rawLabels := strings.Split(prometheusLabelsString, ",") @@ -141,7 +133,7 @@ func main() { config := lib.ReadConfig(configPath) account := &lib.BaseAccount{Config: config} - if err := account.ReadKeys(envPath); err != nil { + if err := account.ReadKeys(); err != nil { log.Printf("warning: failed to read environment variables: %v", err) } diff --git a/transports/go.mod b/transports/go.mod index a6f0faa532..7194c69305 100644 --- a/transports/go.mod +++ b/transports/go.mod @@ -4,8 +4,8 @@ go 1.24.1 require ( github.com/fasthttp/router v1.5.4 - github.com/joho/godotenv v1.5.1 github.com/maximhq/bifrost/core v1.0.5 + github.com/maximhq/bifrost/plugins v1.0.0 github.com/prometheus/client_golang v1.22.0 github.com/valyala/fasthttp v1.60.0 google.golang.org/genai v1.4.0 diff --git a/transports/go.sum b/transports/go.sum index 9703909539..481a295344 100644 --- a/transports/go.sum +++ b/transports/go.sum @@ -63,16 +63,14 @@ github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrk github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/maximhq/bifrost/core v1.0.5 h1:JopRRuV2zylwisj+cBonIpB/Dw4p8gEQoW3hrKsPyI8= github.com/maximhq/bifrost/core v1.0.5/go.mod h1:nL2tc1SVJ7EAKxUb1G1yS4nO46ZsCdm1qC3Oc78oYIU= -github.com/maximhq/bifrost/plugins v1.0.1 h1:wu+kEwdEFvnhEiNLsrFHpxVdd/dJ2Uvkk3el3DkFoWc= -github.com/maximhq/bifrost/plugins v1.0.1/go.mod h1:c5L95RCOHGAKGAl9up03/00DHqjdxVG+3MdIUlAnzYM= +github.com/maximhq/bifrost/plugins v1.0.0 h1:ul4tMMQHOdhyFQueyZwmQB3uX+s2buYSKzq1FW0m090= +github.com/maximhq/bifrost/plugins v1.0.0/go.mod h1:IUDZ2NMgCjIn1SVCvYbWZd/Lsk96MNytOvEKpinjvHo= github.com/maximhq/maxim-go v0.1.1 h1:69uUQjjDPmUGcKg/M4/3AO0fbD+70Agt66pH/UCsI5M= github.com/maximhq/maxim-go v0.1.1/go.mod h1:0+UTWM7UZwNNE5VnljLtr/vpRGtYP8r/2q9WDwlLWFw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=