From 1ddb40ae4d90723001c92a842dd7b73fb7d6af85 Mon Sep 17 00:00:00 2001 From: Mawen Salignat-Moandal Date: Tue, 8 Jul 2025 18:40:33 +0200 Subject: [PATCH 1/3] feat(cmd): add API validation tool for quick configuration testing --- cmd/validate-api/main.go | 120 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 cmd/validate-api/main.go diff --git a/cmd/validate-api/main.go b/cmd/validate-api/main.go new file mode 100644 index 00000000..3786be6c --- /dev/null +++ b/cmd/validate-api/main.go @@ -0,0 +1,120 @@ +// cmd/validate-api/main.go +package main + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "time" + + "github.com/openai/openai-go" + "github.com/openai/openai-go/internal/apierror" + "github.com/openai/openai-go/option" +) + +func main() { + // Command line options + var ( + apiKey = flag.String("api-key", "", "OpenAI API key (overrides OPENAI_API_KEY)") + timeout = flag.Duration("timeout", 10*time.Second, "Request timeout") + verbose = flag.Bool("verbose", false, "Show request details") + ) + flag.Parse() + + // Get API key + key := *apiKey + if key == "" { + key = os.Getenv("OPENAI_API_KEY") + } + if key == "" { + fmt.Fprintln(os.Stderr, "No API key provided") + fmt.Fprintln(os.Stderr, " Use:") + fmt.Fprintln(os.Stderr, " • --api-key 'sk-...'") + fmt.Fprintln(os.Stderr, " • or export OPENAI_API_KEY='sk-...'") + os.Exit(1) + } + + // Create client with timeout + ctx, cancel := context.WithTimeout(context.Background(), *timeout) + defer cancel() + + client := openai.NewClient( + option.WithAPIKey(key), + option.WithRequestTimeout(*timeout), + ) + + // Verbose mode + if *verbose { + fmt.Printf("Testing OpenAI API connection...\n") + fmt.Printf("Timeout: %v\n", *timeout) + displayKey := key + if len(key) > 7 { + displayKey = key[:7] + } + fmt.Printf("šŸ”‘ API Key: %s...\n", displayKey) + fmt.Println() + } + + // Test connection + if err := validateAPI(ctx, client); err != nil { + handleError(err) + os.Exit(1) + } + + fmt.Println("āœ… API connection successful") + if *verbose { + fmt.Println("šŸŽ‰ Your OpenAI configuration is working correctly!") + } +} + +func validateAPI(ctx context.Context, client openai.Client) error { + // Simple test: list models + _, err := client.Models.List(ctx) + return err +} + +func handleError(err error) { + fmt.Fprintf(os.Stderr, "āŒ API Error: %v\n\n", err) + + if apiErr, ok := err.(*apierror.Error); ok { + switch apiErr.StatusCode { + case 401: + fmt.Fprintln(os.Stderr, "šŸ” Authentication issue:") + fmt.Fprintln(os.Stderr, " • Verify your API key is correct") + fmt.Fprintln(os.Stderr, " • Check at https://platform.openai.com/api-keys") + fmt.Fprintln(os.Stderr, " • Ensure your account has available credits") + case 403: + fmt.Fprintln(os.Stderr, "🚫 Access denied:") + fmt.Fprintln(os.Stderr, " • Check your API key permissions") + fmt.Fprintln(os.Stderr, " • Verify your subscription plan") + case 429: + fmt.Fprintln(os.Stderr, "ā±ļø Rate limit exceeded:") + fmt.Fprintln(os.Stderr, " • Wait a few minutes") + fmt.Fprintln(os.Stderr, " • Check your quota at https://platform.openai.com/usage") + case 500, 502, 503, 504: + fmt.Fprintln(os.Stderr, "🌐 OpenAI server issue:") + fmt.Fprintln(os.Stderr, " • OpenAI service is temporarily unavailable") + fmt.Fprintln(os.Stderr, " • Try again in a few minutes") + default: + fmt.Fprintf(os.Stderr, "�� Error code: %d\n", apiErr.StatusCode) + fmt.Fprintf(os.Stderr, "šŸ“ Message: %s\n", apiErr.Message) + } + } else { + errStr := err.Error() + if strings.Contains(errStr, "connection refused") { + fmt.Fprintln(os.Stderr, "🌐 Connection issue:") + fmt.Fprintln(os.Stderr, " • Check your internet connection") + fmt.Fprintln(os.Stderr, " • Verify your proxy/firewall settings") + } else if strings.Contains(errStr, "timeout") { + fmt.Fprintln(os.Stderr, "ā° Connection timeout:") + fmt.Fprintln(os.Stderr, " • Check your internet connection") + fmt.Fprintln(os.Stderr, " • Increase timeout with --timeout 30s") + } else { + fmt.Fprintf(os.Stderr, "ā“ Unknown error: %s\n", errStr) + } + } + + fmt.Fprintln(os.Stderr, "\nšŸ’” For more help: https://platform.openai.com/docs/guides/error-codes") +} From 2471af85eca24a697a0a134c796da7190cdaea86 Mon Sep 17 00:00:00 2001 From: Mawen Salignat-Moandal Date: Tue, 8 Jul 2025 18:46:48 +0200 Subject: [PATCH 2/3] feat(cmd): add API validation tool for quick configuration testing --- cmd/validate-api/main.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd/validate-api/main.go b/cmd/validate-api/main.go index 3786be6c..0fbc4b5a 100644 --- a/cmd/validate-api/main.go +++ b/cmd/validate-api/main.go @@ -53,7 +53,7 @@ func main() { if len(key) > 7 { displayKey = key[:7] } - fmt.Printf("šŸ”‘ API Key: %s...\n", displayKey) + fmt.Printf("API Key: %s...\n", displayKey) fmt.Println() } @@ -63,9 +63,9 @@ func main() { os.Exit(1) } - fmt.Println("āœ… API connection successful") + fmt.Println("Your OpenAI configuration is working correctly!") if *verbose { - fmt.Println("šŸŽ‰ Your OpenAI configuration is working correctly!") + fmt.Println("šŸŽ‰") } } @@ -76,45 +76,45 @@ func validateAPI(ctx context.Context, client openai.Client) error { } func handleError(err error) { - fmt.Fprintf(os.Stderr, "āŒ API Error: %v\n\n", err) + fmt.Fprintf(os.Stderr, "API Error: %v\n\n", err) if apiErr, ok := err.(*apierror.Error); ok { switch apiErr.StatusCode { case 401: - fmt.Fprintln(os.Stderr, "šŸ” Authentication issue:") + fmt.Fprintln(os.Stderr, " Authentication issue:") fmt.Fprintln(os.Stderr, " • Verify your API key is correct") fmt.Fprintln(os.Stderr, " • Check at https://platform.openai.com/api-keys") fmt.Fprintln(os.Stderr, " • Ensure your account has available credits") case 403: - fmt.Fprintln(os.Stderr, "🚫 Access denied:") + fmt.Fprintln(os.Stderr, " Access denied:") fmt.Fprintln(os.Stderr, " • Check your API key permissions") fmt.Fprintln(os.Stderr, " • Verify your subscription plan") case 429: - fmt.Fprintln(os.Stderr, "ā±ļø Rate limit exceeded:") + fmt.Fprintln(os.Stderr, " Rate limit exceeded:") fmt.Fprintln(os.Stderr, " • Wait a few minutes") fmt.Fprintln(os.Stderr, " • Check your quota at https://platform.openai.com/usage") case 500, 502, 503, 504: - fmt.Fprintln(os.Stderr, "🌐 OpenAI server issue:") + fmt.Fprintln(os.Stderr, " OpenAI server issue:") fmt.Fprintln(os.Stderr, " • OpenAI service is temporarily unavailable") fmt.Fprintln(os.Stderr, " • Try again in a few minutes") default: - fmt.Fprintf(os.Stderr, "�� Error code: %d\n", apiErr.StatusCode) - fmt.Fprintf(os.Stderr, "šŸ“ Message: %s\n", apiErr.Message) + fmt.Fprintf(os.Stderr, " Error code: %d\n", apiErr.StatusCode) + fmt.Fprintf(os.Stderr, " Message: %s\n", apiErr.Message) } } else { errStr := err.Error() - if strings.Contains(errStr, "connection refused") { - fmt.Fprintln(os.Stderr, "🌐 Connection issue:") + if strings.Contains(errStr, "Connection refused") { + fmt.Fprintln(os.Stderr, " Connection issue:") fmt.Fprintln(os.Stderr, " • Check your internet connection") fmt.Fprintln(os.Stderr, " • Verify your proxy/firewall settings") } else if strings.Contains(errStr, "timeout") { - fmt.Fprintln(os.Stderr, "ā° Connection timeout:") + fmt.Fprintln(os.Stderr, " Connection timeout:") fmt.Fprintln(os.Stderr, " • Check your internet connection") fmt.Fprintln(os.Stderr, " • Increase timeout with --timeout 30s") } else { - fmt.Fprintf(os.Stderr, "ā“ Unknown error: %s\n", errStr) + fmt.Fprintf(os.Stderr, "Unknown error: %s\n", errStr) } } - fmt.Fprintln(os.Stderr, "\nšŸ’” For more help: https://platform.openai.com/docs/guides/error-codes") + fmt.Fprintln(os.Stderr, "\n For more help: https://platform.openai.com/docs/guides/error-codes") } From 7211d1b6f6003e7a3a2d93ce3877946ce952e260 Mon Sep 17 00:00:00 2001 From: Mawen Salignat-Moandal Date: Tue, 8 Jul 2025 18:48:40 +0200 Subject: [PATCH 3/3] feat(cmd): add API validation tool for quick configuration testing --- cmd/validate-api/main.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/cmd/validate-api/main.go b/cmd/validate-api/main.go index 0fbc4b5a..03580221 100644 --- a/cmd/validate-api/main.go +++ b/cmd/validate-api/main.go @@ -15,7 +15,8 @@ import ( ) func main() { - // Command line options + // --- Command line flags --- + // These allow the user to override the API key, set a custom timeout, and enable verbose output. var ( apiKey = flag.String("api-key", "", "OpenAI API key (overrides OPENAI_API_KEY)") timeout = flag.Duration("timeout", 10*time.Second, "Request timeout") @@ -23,12 +24,14 @@ func main() { ) flag.Parse() - // Get API key + // --- API Key Resolution --- + // Priority: --api-key flag > OPENAI_API_KEY env var. key := *apiKey if key == "" { key = os.Getenv("OPENAI_API_KEY") } if key == "" { + // Fail fast if no API key is provided. fmt.Fprintln(os.Stderr, "No API key provided") fmt.Fprintln(os.Stderr, " Use:") fmt.Fprintln(os.Stderr, " • --api-key 'sk-...'") @@ -36,16 +39,20 @@ func main() { os.Exit(1) } - // Create client with timeout + // --- Context with Timeout --- + // Ensures the request does not hang indefinitely. ctx, cancel := context.WithTimeout(context.Background(), *timeout) defer cancel() + // --- Client Initialization --- + // The client is configured with the resolved API key and timeout. client := openai.NewClient( option.WithAPIKey(key), option.WithRequestTimeout(*timeout), ) - // Verbose mode + // --- Verbose Logging --- + // Show diagnostic info if requested. if *verbose { fmt.Printf("Testing OpenAI API connection...\n") fmt.Printf("Timeout: %v\n", *timeout) @@ -57,7 +64,8 @@ func main() { fmt.Println() } - // Test connection + // --- API Validation --- + // The core check: attempt to list models. This is a lightweight endpoint and a good proxy for API health. if err := validateAPI(ctx, client); err != nil { handleError(err) os.Exit(1) @@ -69,16 +77,21 @@ func main() { } } +// validateAPI attempts a simple API call to verify connectivity and authentication. +// Returns an error if the call fails for any reason. func validateAPI(ctx context.Context, client openai.Client) error { - // Simple test: list models + // Simple test: list models (minimal permissions required, fast response) _, err := client.Models.List(ctx) return err } +// handleError provides structured, actionable error messages for common API issues. +// This function distinguishes between API errors (with status codes) and generic/network errors. func handleError(err error) { fmt.Fprintf(os.Stderr, "API Error: %v\n\n", err) if apiErr, ok := err.(*apierror.Error); ok { + // Handle known API error codes with specific guidance. switch apiErr.StatusCode { case 401: fmt.Fprintln(os.Stderr, " Authentication issue:") @@ -98,10 +111,12 @@ func handleError(err error) { fmt.Fprintln(os.Stderr, " • OpenAI service is temporarily unavailable") fmt.Fprintln(os.Stderr, " • Try again in a few minutes") default: + // For unhandled status codes, print the code and message for debugging. fmt.Fprintf(os.Stderr, " Error code: %d\n", apiErr.StatusCode) fmt.Fprintf(os.Stderr, " Message: %s\n", apiErr.Message) } } else { + // Handle network and unknown errors. errStr := err.Error() if strings.Contains(errStr, "Connection refused") { fmt.Fprintln(os.Stderr, " Connection issue:") @@ -112,9 +127,11 @@ func handleError(err error) { fmt.Fprintln(os.Stderr, " • Check your internet connection") fmt.Fprintln(os.Stderr, " • Increase timeout with --timeout 30s") } else { + // Catch-all for unexpected errors. fmt.Fprintf(os.Stderr, "Unknown error: %s\n", errStr) } } + // Always provide a pointer to the official error documentation for further troubleshooting. fmt.Fprintln(os.Stderr, "\n For more help: https://platform.openai.com/docs/guides/error-codes") }