Files
Blake Ridgway 04885022fd first commit
2026-04-11 13:58:06 -05:00

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
}