100 lines
2.5 KiB
Go
100 lines
2.5 KiB
Go
// Package deepseek provides a minimal client for the DeepSeek API,
|
|
// which is compatible with the OpenAI chat completions format.
|
|
package deepseek
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
apiURL = "https://api.deepseek.com/chat/completions"
|
|
model = "deepseek-coder"
|
|
|
|
systemPrompt = `You are an expert code reviewer. When given a pull request diff, you:
|
|
- Identify bugs, logic errors, and edge cases
|
|
- Flag security vulnerabilities (injections, auth issues, data exposure, etc.)
|
|
- Note performance concerns
|
|
- Point out style or maintainability issues
|
|
- Highlight anything that looks unfinished or unclear
|
|
|
|
Be specific: reference file names and line numbers where relevant.
|
|
Be concise: skip praise for obvious or trivial things.
|
|
Format your response in Markdown with clear sections.
|
|
If the diff is clean, say so briefly.`
|
|
)
|
|
|
|
// Client is a DeepSeek API client.
|
|
type Client struct {
|
|
APIKey string
|
|
http *http.Client
|
|
}
|
|
|
|
// New creates a new DeepSeek client.
|
|
func New(apiKey string) *Client {
|
|
return &Client{
|
|
APIKey: apiKey,
|
|
http: &http.Client{Timeout: 120 * time.Second},
|
|
}
|
|
}
|
|
|
|
type chatMessage struct {
|
|
Role string `json:"role"`
|
|
Content string `json:"content"`
|
|
}
|
|
|
|
type chatRequest struct {
|
|
Model string `json:"model"`
|
|
Messages []chatMessage `json:"messages"`
|
|
}
|
|
|
|
type chatResponse struct {
|
|
Choices []struct {
|
|
Message chatMessage `json:"message"`
|
|
} `json:"choices"`
|
|
Error *struct {
|
|
Message string `json:"message"`
|
|
} `json:"error,omitempty"`
|
|
}
|
|
|
|
// Review sends a PR diff to deepseek-coder and returns the review text.
|
|
func (c *Client) Review(title, diff string) (string, error) {
|
|
userContent := fmt.Sprintf("Please review this pull request.\n\n**Title:** %s\n\n```diff\n%s\n```", title, diff)
|
|
|
|
body, _ := json.Marshal(chatRequest{
|
|
Model: model,
|
|
Messages: []chatMessage{
|
|
{Role: "system", Content: systemPrompt},
|
|
{Role: "user", Content: userContent},
|
|
},
|
|
})
|
|
|
|
req, err := http.NewRequest("POST", apiURL, bytes.NewReader(body))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+c.APIKey)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := c.http.Do(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var r chatResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
|
return "", fmt.Errorf("deepseek: decode response: %w", err)
|
|
}
|
|
if r.Error != nil {
|
|
return "", fmt.Errorf("deepseek: %s", r.Error.Message)
|
|
}
|
|
if len(r.Choices) == 0 {
|
|
return "", fmt.Errorf("deepseek: empty response")
|
|
}
|
|
return r.Choices[0].Message.Content, nil
|
|
}
|