Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand Down
10 changes: 0 additions & 10 deletions transports/.env.sample

This file was deleted.

9 changes: 3 additions & 6 deletions transports/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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
Expand Down
28 changes: 20 additions & 8 deletions transports/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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:

Expand All @@ -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

Expand All @@ -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 .
Expand All @@ -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).
Expand All @@ -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).
Expand Down
15 changes: 5 additions & 10 deletions transports/bifrost-http/lib/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ package lib
import (
"errors"
"fmt"
"os"
"reflect"
"strings"
"sync"

"github.com/joho/godotenv"
"github.com/maximhq/bifrost/core/schemas"
)

Expand Down Expand Up @@ -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
}
Expand Down
7 changes: 3 additions & 4 deletions transports/bifrost-http/lib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":[{
Expand All @@ -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:
//
Expand All @@ -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 {
Expand Down
14 changes: 3 additions & 11 deletions transports/bifrost-http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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 := ""
Expand All @@ -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")
Expand All @@ -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, ",")
Expand Down Expand Up @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion transports/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions transports/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down