diff --git a/cmd/root.go b/cmd/root.go index ae19e5a..86a6a2b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -68,6 +68,7 @@ func init() { rootCmd.PersistentFlags().BoolVarP(&global.Verbose, "verbose", "v", false, "verbose error output (with stack trace) (optional)") rootCmd.PersistentFlags().StringVarP(&global.VoiceflowAPIKey, "voiceflow-api-key", "x", "", "Voiceflow API Key (optional)") rootCmd.PersistentFlags().StringVarP(&global.OpenAIAPIKey, "open-api-key", "z", "", "Open API Key (optional)") + rootCmd.PersistentFlags().StringVar(&global.OpenAIBaseURL, "openai-base-url", "", "OpenAI API base URL override, e.g. https://eu.api.openai.com/v1 (optional)") rootCmd.PersistentFlags().StringVarP(&global.VoiceflowSubdomain, "voiceflow-subdomain", "b", "", "Voiceflow Base URL (optional). Default: empty") rootCmd.PersistentFlags().BoolVarP(&global.SkipUpdate, "skip-update-check", "u", false, "Skip the check for updates check run before every command (optional)") rootCmd.PersistentFlags().StringVarP(&global.Output, "output-format", "o", "text", "Output Format. Options: text, json. Default: text (optional)") diff --git a/cmd/test/execute.go b/cmd/test/execute.go index 9adcc4a..0f67cff 100644 --- a/cmd/test/execute.go +++ b/cmd/test/execute.go @@ -22,6 +22,7 @@ var executeCmd = &cobra.Command{ suite := args[0] voiceflow.SetVoiceflowAPIKey() openai.SetOpenAIAPIKey() + openai.SetOpenAIBaseURL() if err := test.ExecuteSuite(suite); err != nil { global.Log.Errorf("%s", err.Error()) os.Exit(1) diff --git a/docs/docs/overview/authentication.md b/docs/docs/overview/authentication.md index 25a10ee..12a81e4 100644 --- a/docs/docs/overview/authentication.md +++ b/docs/docs/overview/authentication.md @@ -12,4 +12,19 @@ The base URL for the Voiceflow API is `https://..voiceflow.com`. ## Open AI PI Key -`voiceflow-cli` uses Open AI APIs. To interact with Open AI you will need an API Key. You can get your API Key in your Open AI account. You can pass the API Key to the CLI using the `--openai-api-key` flag or by setting the `OPENAI_API_KEY` environment variable. `voiceflow-cli` also works with `.env` files. You can create a `.env` file in the root of your project and add the `OPENAI_API_KEY` variable to it. \ No newline at end of file +`voiceflow-cli` uses Open AI APIs. To interact with Open AI you will need an API Key. You can get your API Key in your Open AI account. You can pass the API Key to the CLI using the `--openai-api-key` flag or by setting the `OPENAI_API_KEY` environment variable. `voiceflow-cli` also works with `.env` files. You can create a `.env` file in the root of your project and add the `OPENAI_API_KEY` variable to it. + +## OpenAI Base URL (optional) + +If you need to target a different OpenAI endpoint (for example EU data residency), you can override the base URL by using: + +- CLI flag: `--openai-base-url` +- Environment variable: `OPENAI_BASE_URL` + +Example: + +```sh +voiceflow test execute evals --openai-base-url https://eu.api.openai.com/v1 +``` + +If not provided, `voiceflow-cli` defaults to `https://api.openai.com/v1`. \ No newline at end of file diff --git a/internal/global/vars.go b/internal/global/vars.go index fb56fd0..b7f4ec6 100644 --- a/internal/global/vars.go +++ b/internal/global/vars.go @@ -6,6 +6,7 @@ var Log logrus.Logger var VoiceflowAPIKey string var OpenAIAPIKey string +var OpenAIBaseURL string var VoiceflowSubdomain string var VersionString string var Output string diff --git a/internal/openai/authentication.go b/internal/openai/authentication.go index 5e1c704..1a9b3a4 100644 --- a/internal/openai/authentication.go +++ b/internal/openai/authentication.go @@ -2,6 +2,7 @@ package openai import ( "os" + "strings" "github.com/xavidop/voiceflow-cli/internal/global" ) @@ -12,3 +13,21 @@ func SetOpenAIAPIKey() { global.OpenAIAPIKey = os.Getenv("OPENAI_API_KEY") } } + +func SetOpenAIBaseURL() { + if global.OpenAIBaseURL == "" { + global.OpenAIBaseURL = os.Getenv("OPENAI_BASE_URL") + } + + // Keep current behavior as default while allowing region-specific overrides. + if global.OpenAIBaseURL == "" { + global.OpenAIBaseURL = "https://api.openai.com/v1" + } + + global.OpenAIBaseURL = strings.TrimRight(global.OpenAIBaseURL, "/") +} + +func GetChatCompletionsURL() string { + SetOpenAIBaseURL() + return global.OpenAIBaseURL + "/chat/completions" +} diff --git a/pkg/openai/similarity.go b/pkg/openai/similarity.go index 42a6995..84f33f1 100644 --- a/pkg/openai/similarity.go +++ b/pkg/openai/similarity.go @@ -8,6 +8,7 @@ import ( "net/http" "github.com/xavidop/voiceflow-cli/internal/global" + openaiauth "github.com/xavidop/voiceflow-cli/internal/openai" "github.com/xavidop/voiceflow-cli/internal/types/tests" "github.com/xavidop/voiceflow-cli/internal/utils" ) @@ -18,7 +19,7 @@ func OpenAICheckSimilarity(message string, s []string, similarityConfig tests.Si } // OpenAI API endpoint - apiURL := "https://api.openai.com/v1/chat/completions" + apiURL := openaiauth.GetChatCompletionsURL() // Prepare the prompt for similarity comparison prompt := fmt.Sprintf( diff --git a/pkg/test/common.go b/pkg/test/common.go index 870ae7c..465e77b 100644 --- a/pkg/test/common.go +++ b/pkg/test/common.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/xavidop/voiceflow-cli/internal/global" + openaiauth "github.com/xavidop/voiceflow-cli/internal/openai" "github.com/xavidop/voiceflow-cli/internal/types/tests" "github.com/xavidop/voiceflow-cli/internal/types/voiceflow/interact" "github.com/xavidop/voiceflow-cli/internal/utils" @@ -169,7 +170,7 @@ Has the goal been achieved? Respond with only "YES" or "NO".`, goal, conversatio // CallOpenAI makes a request to the OpenAI API func (br *BaseRunner) CallOpenAI(messages []ChatMessage) (string, error) { - apiURL := "https://api.openai.com/v1/chat/completions" + apiURL := openaiauth.GetChatCompletionsURL() // Set default values model := "gpt-4o"