diff --git a/internal/claude/claude.go b/internal/claude/claude.go index 5a0a02b..c4aefbc 100644 --- a/internal/claude/claude.go +++ b/internal/claude/claude.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" + httpClient "github.com/dfanso/commit-msg/internal/http" "github.com/dfanso/commit-msg/pkg/types" ) @@ -58,7 +59,7 @@ func GenerateCommitMessage(config *types.Config, changes string, apiKey string, req.Header.Set("x-api-key", apiKey) req.Header.Set("anthropic-version", "2023-06-01") - client := &http.Client{} + client := httpClient.GetClient() resp, err := client.Do(req) if err != nil { return "", err diff --git a/internal/grok/grok.go b/internal/grok/grok.go index 65ebd4e..7a42960 100644 --- a/internal/grok/grok.go +++ b/internal/grok/grok.go @@ -2,41 +2,15 @@ package grok import ( "bytes" - "crypto/tls" "encoding/json" "fmt" "io" "net/http" - "sync" - "time" + httpClient "github.com/dfanso/commit-msg/internal/http" "github.com/dfanso/commit-msg/pkg/types" ) -var ( - grokClientOnce sync.Once - grokClient *http.Client -) - -func getHTTPClient() *http.Client { - grokClientOnce.Do(func() { - transport := &http.Transport{ - TLSHandshakeTimeout: 10 * time.Second, - MaxIdleConns: 10, - IdleConnTimeout: 30 * time.Second, - DisableCompression: true, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: false, - }, - } - grokClient = &http.Client{ - Timeout: 30 * time.Second, - Transport: transport, - } - }) - return grokClient -} - // GenerateCommitMessage calls X.AI's Grok API to create a commit message from // the provided Git diff and generation options. func GenerateCommitMessage(config *types.Config, changes string, apiKey string, opts *types.GenerationOptions) (string, error) { @@ -71,7 +45,7 @@ func GenerateCommitMessage(config *types.Config, changes string, apiKey string, req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey)) - client := getHTTPClient() + client := httpClient.GetClient() resp, err := client.Do(req) if err != nil { return "", err diff --git a/internal/groq/groq.go b/internal/groq/groq.go index b7a1be4..a8dc1be 100644 --- a/internal/groq/groq.go +++ b/internal/groq/groq.go @@ -7,8 +7,8 @@ import ( "io" "net/http" "os" - "time" + httpClient "github.com/dfanso/commit-msg/internal/http" "github.com/dfanso/commit-msg/pkg/types" ) @@ -38,8 +38,7 @@ const defaultModel = "llama-3.3-70b-versatile" var ( // allow overrides in tests - baseURL = "https://api.groq.com/openai/v1/chat/completions" - httpClient = &http.Client{Timeout: 30 * time.Second} + baseURL = "https://api.groq.com/openai/v1/chat/completions" ) // GenerateCommitMessage calls Groq's OpenAI-compatible chat completions API. @@ -83,7 +82,7 @@ func GenerateCommitMessage(_ *types.Config, changes string, apiKey string, opts req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey)) - resp, err := httpClient.Do(req) + resp, err := httpClient.GetClient().Do(req) if err != nil { return "", fmt.Errorf("failed to call Groq API: %w", err) } diff --git a/internal/http/client.go b/internal/http/client.go new file mode 100644 index 0000000..a0ef723 --- /dev/null +++ b/internal/http/client.go @@ -0,0 +1,51 @@ +package http + +import ( + "crypto/tls" + "net/http" + "sync" + "time" +) + +var ( + clientOnce sync.Once + sharedClient *http.Client + + ollamaClientOnce sync.Once + ollamaClient *http.Client +) + +// createTransport creates a shared HTTP transport with optimized settings +func createTransport() *http.Transport { + return &http.Transport{ + TLSHandshakeTimeout: 10 * time.Second, + MaxIdleConns: 10, + IdleConnTimeout: 30 * time.Second, + DisableCompression: true, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: false, + }, + } +} + +// GetClient returns a shared HTTP client with optimized settings for cloud APIs +func GetClient() *http.Client { + clientOnce.Do(func() { + sharedClient = &http.Client{ + Timeout: 30 * time.Second, + Transport: createTransport(), + } + }) + return sharedClient +} + +// GetOllamaClient returns an HTTP client with extended timeout for local Ollama inference +func GetOllamaClient() *http.Client { + ollamaClientOnce.Do(func() { + ollamaClient = &http.Client{ + Timeout: 10 * time.Minute, // 10 minutes for local inference + Transport: createTransport(), + } + }) + return ollamaClient +} \ No newline at end of file diff --git a/internal/ollama/ollama.go b/internal/ollama/ollama.go index cb4f26e..98f354b 100644 --- a/internal/ollama/ollama.go +++ b/internal/ollama/ollama.go @@ -7,6 +7,7 @@ import ( "io" "net/http" + httpClient "github.com/dfanso/commit-msg/internal/http" "github.com/dfanso/commit-msg/pkg/types" ) @@ -46,7 +47,13 @@ func GenerateCommitMessage(_ *types.Config, changes string, url string, model st return "", fmt.Errorf("failed to marshal request: %v", err) } - resp, err := http.Post(url, "application/json", bytes.NewBuffer(body)) + req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) + if err != nil { + return "", fmt.Errorf("failed to create request: %v", err) + } + req.Header.Set("Content-Type", "application/json") + + resp, err := httpClient.GetOllamaClient().Do(req) if err != nil { return "", fmt.Errorf("failed to send request to Ollama: %v", err) }