forked from DFanso/commit-msg
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgroq.go
More file actions
124 lines (100 loc) · 3.24 KB
/
groq.go
File metadata and controls
124 lines (100 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package groq
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
internalHTTP "github.com/dfanso/commit-msg/internal/http"
"github.com/dfanso/commit-msg/pkg/types"
)
type chatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type chatRequest struct {
Model string `json:"model"`
Messages []chatMessage `json:"messages"`
Temperature float64 `json:"temperature"`
MaxTokens int `json:"max_tokens"`
}
type chatChoice struct {
Message chatMessage `json:"message"`
}
type chatResponse struct {
Choices []chatChoice `json:"choices"`
}
// defaultModel uses Groq's recommended general-purpose model as of Oct 2025.
// If Groq updates their defaults again, override via GROQ_MODEL.
const defaultModel = "llama-3.3-70b-versatile"
const (
groqTemperature = 0.2
groqMaxTokens = 200
groqSystemMessage = "You are an assistant that writes clear, concise git commit messages."
groqContentType = "application/json"
groqAuthorizationPrefix = "Bearer "
)
var (
// allow overrides in tests
baseURL = "https://api.groq.com/openai/v1/chat/completions"
// httpClient can be overridden in tests; defaults to the internal http client
httpClient *http.Client
)
func init() {
httpClient = internalHTTP.GetClient()
}
// GenerateCommitMessage calls Groq's OpenAI-compatible chat completions API.
func GenerateCommitMessage(_ *types.Config, changes string, apiKey string, opts *types.GenerationOptions) (string, error) {
if changes == "" {
return "", fmt.Errorf("no changes provided for commit message generation")
}
prompt := types.BuildCommitPrompt(changes, opts)
model := os.Getenv("GROQ_MODEL")
if model == "" {
model = defaultModel
}
payload := chatRequest{
Model: model,
Temperature: groqTemperature,
MaxTokens: groqMaxTokens,
Messages: []chatMessage{
{Role: "system", Content: groqSystemMessage},
{Role: "user", Content: prompt},
},
}
body, err := json.Marshal(payload)
if err != nil {
return "", fmt.Errorf("failed to marshal Groq request: %w", err)
}
endpoint := baseURL
if customEndpoint := os.Getenv("GROQ_API_URL"); customEndpoint != "" {
endpoint = customEndpoint
}
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(body))
if err != nil {
return "", fmt.Errorf("failed to create Groq request: %w", err)
}
req.Header.Set("Content-Type", groqContentType)
req.Header.Set("Authorization", fmt.Sprintf("%s%s", groqAuthorizationPrefix, apiKey))
resp, err := httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to call Groq API: %w", err)
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read Groq response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("groq API returned status %d: %s", resp.StatusCode, string(responseBody))
}
var completion chatResponse
if err := json.Unmarshal(responseBody, &completion); err != nil {
return "", fmt.Errorf("failed to decode Groq response: %w", err)
}
if len(completion.Choices) == 0 || completion.Choices[0].Message.Content == "" {
return "", fmt.Errorf("groq API returned empty response")
}
return completion.Choices[0].Message.Content, nil
}