oqtane-general-description #62
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -14,6 +15,12 @@ import (
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
type Suggestion struct {
|
||||
LineNumber uint64 `json:"line_number"`
|
||||
Comment string `json:"comment"`
|
||||
Snippet string `json:"snippet,omitempty"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
apiKey := os.Getenv("GEMINI_API_KEY")
|
||||
token := os.Getenv("GITEA_TOKEN")
|
||||
@@ -55,7 +62,7 @@ func main() {
|
||||
}
|
||||
defer geminiClient.Close()
|
||||
|
||||
model := geminiClient.GenerativeModel("gemini-3-flash-preview")
|
||||
model := geminiClient.GenerativeModel("gemini-1.5-flash")
|
||||
|
||||
// Get PR files
|
||||
files, _, err := client.ListPullRequestFiles(owner, repo, prNumber, gitea.ListPullRequestFilesOptions{})
|
||||
@@ -63,7 +70,7 @@ func main() {
|
||||
log.Fatalf("Failed to get PR files: %v", err)
|
||||
}
|
||||
|
||||
var reviews []string
|
||||
var giteaComments []gitea.CreatePullReviewComment
|
||||
for _, file := range files {
|
||||
if !strings.HasSuffix(file.Filename, ".md") {
|
||||
continue
|
||||
@@ -76,25 +83,35 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
review, err := getGeminiReview(ctx, model, content)
|
||||
suggestions, err := getGeminiReview(ctx, model, content)
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting review for %s: %v\n", file.Filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
reviews = append(reviews, fmt.Sprintf("#### Review for `%s`\n\n%s", file.Filename, review))
|
||||
for _, s := range suggestions {
|
||||
body := s.Comment
|
||||
if s.Snippet != "" {
|
||||
body += fmt.Sprintf("\n\n```suggestion\n%s\n```", s.Snippet)
|
||||
}
|
||||
giteaComments = append(giteaComments, gitea.CreatePullReviewComment{
|
||||
Path: file.Filename,
|
||||
Body: body,
|
||||
NewLineNum: int64(s.LineNumber),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(reviews) > 0 {
|
||||
reviewBody := "### 🤖 Gemini Writing Review\n\n" + strings.Join(reviews, "\n\n---\n\n")
|
||||
if len(giteaComments) > 0 {
|
||||
_, _, err = client.CreatePullReview(owner, repo, prNumber, gitea.CreatePullReviewOptions{
|
||||
State: gitea.ReviewStateRequestChanges,
|
||||
Body: reviewBody,
|
||||
State: gitea.ReviewStateRequestChanges,
|
||||
Body: "### 🤖 Gemini Writing Review\n\nI've found some areas for improvement in the documentation. Please see the inline comments below.",
|
||||
Comments: giteaComments,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create PR review: %v", err)
|
||||
}
|
||||
fmt.Println("Successfully created PR change request review.")
|
||||
fmt.Printf("Successfully created PR review with %d inline comments.\n", len(giteaComments))
|
||||
} else {
|
||||
fmt.Println("No Markdown files to review or no suggestions found.")
|
||||
}
|
||||
@@ -114,35 +131,56 @@ func readFile(path string) (string, error) {
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func getGeminiReview(ctx context.Context, model *genai.GenerativeModel, content string) (string, error) {
|
||||
func getGeminiReview(ctx context.Context, model *genai.GenerativeModel, content string) ([]Suggestion, error) {
|
||||
prompt := fmt.Sprintf(`
|
||||
Review the following Markdown content for spelling errors, grammar mistakes, and style improvements.
|
||||
This review will be posted as a "Request Changes" on a Pull Request, so please be specific and actionable.
|
||||
Provide your feedback as a list of bullet points. For each point:
|
||||
1. Identify the issue.
|
||||
2. Provide the original text snippet.
|
||||
3. Suggest a clear alternative or fix.
|
||||
4. Briefly explain why the change is necessary if not obvious.
|
||||
5. Check the content for completeness and technical correctness.
|
||||
Analyze the text line by line.
|
||||
|
||||
Content:
|
||||
For each issue found, provide a suggestion in JSON format with:
|
||||
- "line_number": The 1-indexed line number where the issue occurs.
|
||||
- "comment": A brief explanation of the problem and the suggested fix.
|
||||
- "snippet": The corrected text for that line (optional, but highly recommended for spell/grammar fixes).
|
||||
|
||||
Return ONLY a JSON array of suggestions. If no issues are found, return "[]".
|
||||
|
||||
Content with line numbers for reference:
|
||||
%s
|
||||
`, content)
|
||||
`, addLineNumbers(content))
|
||||
|
||||
resp, err := model.GenerateContent(ctx, genai.Text(prompt))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(resp.Candidates) == 0 || len(resp.Candidates[0].Content.Parts) == 0 {
|
||||
return "No suggestions found.", nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var result strings.Builder
|
||||
var rawJS strings.Builder
|
||||
for _, part := range resp.Candidates[0].Content.Parts {
|
||||
if text, ok := part.(genai.Text); ok {
|
||||
result.WriteString(string(text))
|
||||
rawJS.WriteString(string(text))
|
||||
}
|
||||
}
|
||||
return result.String(), nil
|
||||
|
||||
// Clean up markdown code blocks if present
|
||||
js := strings.TrimSpace(rawJS.String())
|
||||
js = strings.TrimPrefix(js, "```json")
|
||||
js = strings.TrimSuffix(js, "```")
|
||||
js = strings.TrimSpace(js)
|
||||
|
||||
var suggestions []Suggestion
|
||||
if err := json.Unmarshal([]byte(js), &suggestions); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal suggestions: %v (raw response: %s)", err, js)
|
||||
}
|
||||
|
||||
return suggestions, nil
|
||||
}
|
||||
|
||||
func addLineNumbers(text string) string {
|
||||
lines := strings.Split(text, "\n")
|
||||
for i, line := range lines {
|
||||
lines[i] = fmt.Sprintf("%d: %s", i+1, line)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user