2024-08-16 19:43:27 +00:00
|
|
|
package gemini
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2024-08-16 22:59:34 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2024-08-16 19:43:27 +00:00
|
|
|
|
|
|
|
"github.com/danielmiessler/fabric/common"
|
|
|
|
"github.com/google/generative-ai-go/genai"
|
|
|
|
"google.golang.org/api/iterator"
|
|
|
|
"google.golang.org/api/option"
|
|
|
|
)
|
|
|
|
|
2024-08-16 22:59:34 +00:00
|
|
|
const modelsNamePrefix = "models/"
|
|
|
|
|
2024-08-16 19:43:27 +00:00
|
|
|
func NewClient() (ret *Client) {
|
|
|
|
vendorName := "Gemini"
|
|
|
|
ret = &Client{}
|
|
|
|
|
|
|
|
ret.Configurable = &common.Configurable{
|
|
|
|
Label: vendorName,
|
|
|
|
EnvNamePrefix: common.BuildEnvVariablePrefix(vendorName),
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.ApiKey = ret.Configurable.AddSetupQuestion("API key", true)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
*common.Configurable
|
|
|
|
ApiKey *common.SetupQuestion
|
|
|
|
}
|
|
|
|
|
2024-08-16 22:59:34 +00:00
|
|
|
func (o *Client) ListModels() (ret []string, err error) {
|
2024-08-16 19:43:27 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
var client *genai.Client
|
2024-08-16 22:59:34 +00:00
|
|
|
if client, err = genai.NewClient(ctx, option.WithAPIKey(o.ApiKey.Value)); err != nil {
|
2024-08-16 19:43:27 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
iter := client.ListModels(ctx)
|
|
|
|
for {
|
|
|
|
var resp *genai.ModelInfo
|
|
|
|
if resp, err = iter.Next(); err != nil {
|
2024-08-16 22:01:55 +00:00
|
|
|
if errors.Is(err, iterator.Done) {
|
|
|
|
err = nil
|
|
|
|
}
|
2024-08-16 19:43:27 +00:00
|
|
|
break
|
|
|
|
}
|
2024-08-16 22:59:34 +00:00
|
|
|
|
|
|
|
name := o.buildModelNameSimple(resp.Name)
|
|
|
|
ret = append(ret, name)
|
2024-08-16 19:43:27 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-26 10:34:15 +00:00
|
|
|
func (o *Client) Send(ctx context.Context, msgs []*common.Message, opts *common.ChatOptions) (ret string, err error) {
|
2024-08-17 17:48:24 +00:00
|
|
|
systemInstruction, messages := toMessages(msgs)
|
2024-08-16 19:43:27 +00:00
|
|
|
|
|
|
|
var client *genai.Client
|
2024-08-16 22:59:34 +00:00
|
|
|
if client, err = genai.NewClient(ctx, option.WithAPIKey(o.ApiKey.Value)); err != nil {
|
2024-08-16 19:43:27 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer client.Close()
|
|
|
|
|
2024-08-16 22:59:34 +00:00
|
|
|
model := client.GenerativeModel(o.buildModelNameFull(opts.Model))
|
2024-08-16 19:43:27 +00:00
|
|
|
model.SetTemperature(float32(opts.Temperature))
|
|
|
|
model.SetTopP(float32(opts.TopP))
|
|
|
|
model.SystemInstruction = systemInstruction
|
|
|
|
|
|
|
|
var response *genai.GenerateContentResponse
|
2024-08-17 17:48:24 +00:00
|
|
|
if response, err = model.GenerateContent(ctx, messages...); err != nil {
|
2024-08-16 19:43:27 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-16 22:59:34 +00:00
|
|
|
ret = o.extractText(response)
|
2024-08-16 19:43:27 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-16 22:59:34 +00:00
|
|
|
func (o *Client) buildModelNameSimple(fullModelName string) string {
|
|
|
|
return strings.TrimPrefix(fullModelName, modelsNamePrefix)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Client) buildModelNameFull(modelName string) string {
|
|
|
|
return fmt.Sprintf("%v%v", modelsNamePrefix, modelName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Client) SendStream(msgs []*common.Message, opts *common.ChatOptions, channel chan string) (err error) {
|
2024-08-16 19:43:27 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
var client *genai.Client
|
2024-08-16 22:59:34 +00:00
|
|
|
if client, err = genai.NewClient(ctx, option.WithAPIKey(o.ApiKey.Value)); err != nil {
|
2024-08-16 19:43:27 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer client.Close()
|
|
|
|
|
2024-08-17 17:48:24 +00:00
|
|
|
systemInstruction, messages := toMessages(msgs)
|
2024-08-16 19:43:27 +00:00
|
|
|
|
2024-08-16 22:59:34 +00:00
|
|
|
model := client.GenerativeModel(o.buildModelNameFull(opts.Model))
|
2024-08-16 19:43:27 +00:00
|
|
|
model.SetTemperature(float32(opts.Temperature))
|
|
|
|
model.SetTopP(float32(opts.TopP))
|
|
|
|
model.SystemInstruction = systemInstruction
|
|
|
|
|
2024-08-17 17:48:24 +00:00
|
|
|
iter := model.GenerateContentStream(ctx, messages...)
|
2024-08-16 19:43:27 +00:00
|
|
|
for {
|
2024-08-17 17:48:24 +00:00
|
|
|
if resp, iterErr := iter.Next(); iterErr == nil {
|
2024-08-16 19:43:27 +00:00
|
|
|
for _, candidate := range resp.Candidates {
|
|
|
|
if candidate.Content != nil {
|
|
|
|
for _, part := range candidate.Content.Parts {
|
|
|
|
if text, ok := part.(genai.Text); ok {
|
|
|
|
channel <- string(text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-08-17 17:48:24 +00:00
|
|
|
} else {
|
|
|
|
if !errors.Is(iterErr, iterator.Done) {
|
|
|
|
channel <- fmt.Sprintf("%v\n", iterErr)
|
|
|
|
}
|
2024-08-16 19:43:27 +00:00
|
|
|
close(channel)
|
2024-08-17 17:48:24 +00:00
|
|
|
break
|
2024-08-16 19:43:27 +00:00
|
|
|
}
|
|
|
|
}
|
2024-08-17 17:48:24 +00:00
|
|
|
return
|
2024-08-16 19:43:27 +00:00
|
|
|
}
|
|
|
|
|
2024-08-16 22:59:34 +00:00
|
|
|
func (o *Client) extractText(response *genai.GenerateContentResponse) (ret string) {
|
2024-08-16 19:43:27 +00:00
|
|
|
for _, candidate := range response.Candidates {
|
|
|
|
if candidate.Content == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
for _, part := range candidate.Content.Parts {
|
|
|
|
if text, ok := part.(genai.Text); ok {
|
|
|
|
ret += string(text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-17 17:48:24 +00:00
|
|
|
func toMessages(msgs []*common.Message) (systemInstruction *genai.Content, messages []genai.Part) {
|
2024-08-16 22:01:55 +00:00
|
|
|
if len(msgs) >= 2 {
|
2024-08-17 17:48:24 +00:00
|
|
|
systemInstruction = &genai.Content{
|
2024-08-16 22:01:55 +00:00
|
|
|
Parts: []genai.Part{
|
2024-08-17 17:48:24 +00:00
|
|
|
genai.Text(msgs[0].Content),
|
2024-08-16 22:01:55 +00:00
|
|
|
},
|
|
|
|
}
|
2024-08-17 17:48:24 +00:00
|
|
|
for _, msg := range msgs[1:] {
|
|
|
|
messages = append(messages, genai.Text(msg.Content))
|
|
|
|
}
|
2024-08-16 22:01:55 +00:00
|
|
|
} else {
|
2024-08-17 17:48:24 +00:00
|
|
|
messages = append(messages, genai.Text(msgs[0].Content))
|
2024-08-16 19:43:27 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|