@@ -4,97 +4,53 @@ import (
44 "context"
55 "errors"
66 "fmt"
7+ "strings"
78 "time"
89
9- "github.com/google/generative-ai-go/genai"
10- "google.golang.org/api/option"
11-
1210 "github.com/securego/gosec/v2/issue"
1311)
1412
1513const (
16- GeminiModel = "gemini-1.5-flash"
17- AIPrompt = `Provide a brief explanation and a solution to fix this security issue
14+ AIProviderFlagHelp = `AI API provider to generate auto fixes to issues. Valid options are:
15+ - gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-2.0-flash, gemini-2.0-flash-lite (gemini, default);
16+ - claude-sonnet-4-0 (claude, default), claude-opus-4-0, claude-opus-4-1, claude-sonnet-3-7`
17+
18+ AIPrompt = `Provide a brief explanation and a solution to fix this security issue
1819 in Go programming language: %q.
1920 Answer in markdown format and keep the response limited to 200 words.`
20- GeminiProvider = "gemini"
2121
2222 timeout = 30 * time .Second
2323)
2424
25- // GenAIClient defines the interface for the GenAI client.
2625type GenAIClient interface {
27- // Close clean up and close the client.
28- Close () error
29- // GenerativeModel build the generative mode.
30- GenerativeModel (name string ) GenAIGenerativeModel
26+ GenerateSolution (ctx context.Context , prompt string ) (string , error )
3127}
3228
33- // GenAIGenerativeModel defines the interface for the Generative Model.
34- type GenAIGenerativeModel interface {
35- // GenerateContent generates an response for given prompt.
36- GenerateContent (ctx context.Context , prompt string ) (string , error )
37- }
38-
39- // genAIClientWrapper wraps the genai.Client to implement GenAIClient.
40- type genAIClientWrapper struct {
41- client * genai.Client
42- }
43-
44- // Close closes the gen AI client.
45- func (w * genAIClientWrapper ) Close () error {
46- return w .client .Close ()
47- }
48-
49- // GenerativeModel builds the generative Model.
50- func (w * genAIClientWrapper ) GenerativeModel (name string ) GenAIGenerativeModel {
51- return & genAIGenerativeModelWrapper {model : w .client .GenerativeModel (name )}
52- }
53-
54- // genAIGenerativeModelWrapper wraps the genai.GenerativeModel to implement GenAIGenerativeModel
55- type genAIGenerativeModelWrapper struct {
56- // model is the underlying generative model
57- model * genai.GenerativeModel
58- }
59-
60- // GenerateContent generates a response for the given prompt using gemini API.
61- func (w * genAIGenerativeModelWrapper ) GenerateContent (ctx context.Context , prompt string ) (string , error ) {
62- resp , err := w .model .GenerateContent (ctx , genai .Text (prompt ))
63- if err != nil {
64- return "" , fmt .Errorf ("generating autofix: %w" , err )
65- }
66- if len (resp .Candidates ) == 0 {
67- return "" , errors .New ("no autofix returned by gemini" )
68- }
69-
70- if len (resp .Candidates [0 ].Content .Parts ) == 0 {
71- return "" , errors .New ("nothing found in the first autofix returned by gemini" )
72- }
73-
74- // Return the first candidate
75- return fmt .Sprintf ("%+v" , resp .Candidates [0 ].Content .Parts [0 ]), nil
76- }
29+ // GenerateSolution generates a solution for the given issues using the specified AI provider
30+ func GenerateSolution (model , aiAPIKey string , issues []* issue.Issue ) (err error ) {
31+ var client GenAIClient
7732
78- // NewGenAIClient creates a new gemini API client.
79- func NewGenAIClient ( ctx context. Context , aiAPIKey , endpoint string ) ( GenAIClient , error ) {
80- clientOptions := []option. ClientOption { option . WithAPIKey ( aiAPIKey )}
81- if endpoint != "" {
82- clientOptions = append ( clientOptions , option . WithEndpoint ( endpoint ) )
33+ switch {
34+ case strings . HasPrefix ( model , "claude" ):
35+ client , err = NewClaudeClient ( model , aiAPIKey )
36+ case strings . HasPrefix ( model , "gemini" ):
37+ client , err = NewGeminiClient ( model , aiAPIKey )
8338 }
8439
85- client , err := genai .NewClient (ctx , clientOptions ... )
86- if err != nil {
87- return nil , fmt .Errorf ("calling gemini API: %w" , err )
40+ switch {
41+ case err != nil :
42+ return fmt .Errorf ("initializing AI client: %w" , err )
43+ case client == nil :
44+ return fmt .Errorf ("unsupported AI backend: %s" , model )
8845 }
8946
90- return & genAIClientWrapper { client : client }, nil
47+ return generateSolution ( client , issues )
9148}
9249
93- func generateSolutionByGemini (client GenAIClient , issues []* issue.Issue ) error {
50+ func generateSolution (client GenAIClient , issues []* issue.Issue ) error {
9451 ctx , cancel := context .WithTimeout (context .Background (), timeout )
9552 defer cancel ()
9653
97- model := client .GenerativeModel (GeminiModel )
9854 cachedAutofix := make (map [string ]string )
9955 for _ , issue := range issues {
10056 if val , ok := cachedAutofix [issue .What ]; ok {
@@ -103,7 +59,7 @@ func generateSolutionByGemini(client GenAIClient, issues []*issue.Issue) error {
10359 }
10460
10561 prompt := fmt .Sprintf (AIPrompt , issue .What )
106- resp , err := model . GenerateContent (ctx , prompt )
62+ resp , err := client . GenerateSolution (ctx , prompt )
10763 if err != nil {
10864 return fmt .Errorf ("generating autofix with gemini: %w" , err )
10965 }
@@ -117,26 +73,3 @@ func generateSolutionByGemini(client GenAIClient, issues []*issue.Issue) error {
11773 }
11874 return nil
11975}
120-
121- // GenerateSolution generates a solution for the given issues using the specified AI provider
122- func GenerateSolution (aiAPIProvider , aiAPIKey , endpoint string , issues []* issue.Issue ) error {
123- ctx , cancel := context .WithTimeout (context .Background (), timeout )
124- defer cancel ()
125-
126- var client GenAIClient
127-
128- switch aiAPIProvider {
129- case GeminiProvider :
130- var err error
131- client , err = NewGenAIClient (ctx , aiAPIKey , endpoint )
132- if err != nil {
133- return fmt .Errorf ("generating autofix: %w" , err )
134- }
135- default :
136- return errors .New ("ai provider not supported" )
137- }
138-
139- defer client .Close ()
140-
141- return generateSolutionByGemini (client , issues )
142- }
0 commit comments