From 4a800a7cfd6e8d59b021ff1b5a0ea9d42b89b7b3 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Mon, 1 Sep 2025 00:29:48 -0500 Subject: [PATCH] attempts to submit data to the Gemini API --- Makefile | 2 +- argv.go | 8 +++++--- argvAutoshell.go | 2 +- convertToGenai.go | 28 ++++++++++++++++++++++++++++ doConnect.go | 35 ++++++++++++++++++++++++++--------- doJSON.go | 43 +++++++++++++++++++++++++++++++++++++++++++ dumpchat.go | 25 +++++++++++++++++++++++-- json.go | 23 ----------------------- main.go | 33 +++------------------------------ structs.go | 11 ++++++++--- 10 files changed, 138 insertions(+), 72 deletions(-) create mode 100644 convertToGenai.go create mode 100644 doJSON.go diff --git a/Makefile b/Makefile index 7813410..29d5ed2 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = $(shell git describe --tags) BUILDTIME = $(shell date +%Y.%m.%d_%H%M) default: install - regex --json tmp/regex.bf203df1-ce24-42ea-93e9-10e0d8b5e8b0.gemini-api-request.64.json + regex playback vet: @GO111MODULE=off go vet diff --git a/argv.go b/argv.go index 314adf4..54b4900 100644 --- a/argv.go +++ b/argv.go @@ -27,9 +27,11 @@ type EmptyCmd struct { } type PlaybackCmd struct { - List *EmptyCmd `arg:"subcommand:list" help:"list memories"` - Long *EmptyCmd `arg:"subcommand:long" help:"show info on each chat"` - Purge *EmptyCmd `arg:"subcommand:purge" help:"verify chat uuids & purge empty chats"` + Last *EmptyCmd `arg:"subcommand:last" help:"dump the last one"` + List *EmptyCmd `arg:"subcommand:list" help:"list memories"` + Long *EmptyCmd `arg:"subcommand:long" help:"show info on each chat"` + Purge *EmptyCmd `arg:"subcommand:purge" help:"verify chat uuids & purge empty chats"` + Submit *EmptyCmd `arg:"subcommand:submit" help:"convert and submit the last entry"` } func (args) Version() string { diff --git a/argvAutoshell.go b/argvAutoshell.go index 14a21f1..f6fcee7 100644 --- a/argvAutoshell.go +++ b/argvAutoshell.go @@ -24,7 +24,7 @@ func (args) doBashAuto() { // argv.doBashHelp() switch argv.BashAuto[0] { case "playback": - fmt.Println("long --uuid purge") + fmt.Println("long --uuid purge last submit") case "clean": fmt.Println("user devel master") default: diff --git a/convertToGenai.go b/convertToGenai.go new file mode 100644 index 0000000..a9dd1b4 --- /dev/null +++ b/convertToGenai.go @@ -0,0 +1,28 @@ +package main + +import ( + "go.wit.com/lib/protobuf/chatpb" + "google.golang.org/genai" +) + +// convertToGenai transforms the parsed JSON request into the genai.Content format. +func convertToGenai(req *chatpb.GeminiRequest) ([]*genai.Content, error) { + var contents []*genai.Content + for _, c := range req.Contents { + genaiParts := []*genai.Part{} // Create a slice of the interface type + for _, p := range c.Parts { + if p.Text != "" { + // genai.Text returns a Part interface, which is what we need + var tmp *genai.Part + tmp = new(genai.Part) + tmp.Text = p.Text + genaiParts = append(genaiParts, tmp) + } + } + contents = append(contents, &genai.Content{ + Role: c.Role, + Parts: genaiParts, + }) + } + return contents, nil +} diff --git a/doConnect.go b/doConnect.go index b7abd40..8eb2674 100644 --- a/doConnect.go +++ b/doConnect.go @@ -10,25 +10,25 @@ import ( ) // doConnect initializes the Gemini client and handles the request flow. -func doConnect() (*genai.Client, error) { +func doConnect() error { apiKey := os.Getenv("GEMINI_API_KEY") if apiKey == "" { - return nil, log.Errorf("GEMINI_API_KEY environment variable not set") + return log.Errorf("GEMINI_API_KEY environment variable not set") } - ctx := context.Background() - client, err := genai.NewClient(ctx, &genai.ClientConfig{APIKey: apiKey}) + me.ctx = context.Background() + var err error + me.client, err = genai.NewClient(me.ctx, &genai.ClientConfig{APIKey: apiKey}) if err != nil { - return nil, log.Errorf("failed to create new genai client: %w", err) + return log.Errorf("failed to create new genai client: %w", err) } - return client, err + return nil } // sampleHello sends a hardcoded prompt to the model and prints the response. -func simpleHello(client *genai.Client) error { +func simpleHello() error { log.Info("Sending 'hello, how are you' to the Gemini API...") - ctx := context.Background() // Create the parts slice parts := []*genai.Part{ @@ -37,7 +37,24 @@ func simpleHello(client *genai.Client) error { content := []*genai.Content{{Parts: parts}} - resp, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash", content, nil) + resp, err := me.client.Models.GenerateContent(me.ctx, "gemini-2.5-flash", content, nil) + if err != nil { + return log.Errorf("error sending message: %v", err) + } + + log.Info("Response from API:") + for _, cand := range resp.Candidates { + if cand.Content != nil { + for _, part := range cand.Content.Parts { + fmt.Println(part) + } + } + } + return nil +} + +func submitChat(content []*genai.Content) error { + resp, err := me.client.Models.GenerateContent(me.ctx, "gemini-2.5-flash", content, nil) if err != nil { return log.Errorf("error sending message: %v", err) } diff --git a/doJSON.go b/doJSON.go new file mode 100644 index 0000000..3853783 --- /dev/null +++ b/doJSON.go @@ -0,0 +1,43 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package main + +// An app to submit patches for the 30 GO GUI repos + +import ( + "path/filepath" + "strconv" + "strings" + + "go.wit.com/lib/protobuf/chatpb" + "go.wit.com/log" +) + +func doJSON() { + // now try to Marshal() into a protobuf + pb, err := parsePB(argv.JsonFile) + if err != nil { + badExit(err) + } + log.Info("GeminiContent pb.Marshal() worked pb.Contents len =", len(pb.Contents)) + _, filename := filepath.Split(argv.JsonFile) + parts := strings.Split(filename, ".") + + if len(parts) == 5 { + uuid := parts[1] + num, _ := strconv.Atoi(parts[3]) + log.Info(uuid, parts) + if chat := me.chats.FindByUuid(uuid); chat != nil { + log.Info("FOUND CHAT", uuid, num) + newEntry := new(chatpb.ChatEntry) + newEntry.GeminiRequest = pb + newEntry.ContentFile = filename + newEntry.RequestCounter = int32(num) + chat.AppendEntry(newEntry) + me.chats.ConfigSave() + } + } else { + } + okExit("") +} diff --git a/dumpchat.go b/dumpchat.go index dbaa3ee..2f479ae 100644 --- a/dumpchat.go +++ b/dumpchat.go @@ -9,10 +9,31 @@ func dumpEntry(entry *chatpb.ChatEntry) { log.Printf(entry.FormatTEXT()) } -func dumpchat(chat *chatpb.Chat) { +func dumpchat(chat *chatpb.Chat) error { entries := chat.GetEntries() + var lastEntry *chatpb.ChatEntry if len(entries) > 0 { - lastEntry := entries[len(entries)-1] + lastEntry = entries[len(entries)-1] + } else { + return log.Errorf("no entries") + } + if argv.Playback.Last != nil { dumpEntry(lastEntry) } + if argv.Playback.Submit != nil { + if lastEntry.GeminiRequest == nil { + return log.Errorf("lastEntry.GeminiRequest == nil") + } + aichat, err := convertToGenai(lastEntry.GeminiRequest) + if err != nil { + log.Info("convertToGenai() returned with error", err) + return err + } + err = submitChat(aichat) + if err != nil { + log.Info("submitChat() returned with error", err) + return err + } + } + return nil } diff --git a/json.go b/json.go index e55521d..818bbf9 100644 --- a/json.go +++ b/json.go @@ -6,7 +6,6 @@ import ( "go.wit.com/lib/protobuf/chatpb" "go.wit.com/log" - "google.golang.org/genai" ) // GeminiRequest matches the overall structure of the gemini-cli JSON output. @@ -124,25 +123,3 @@ func dumpFullJSON(req *GeminiRequest) { } } } - -// convertToGenai transforms the parsed JSON request into the genai.Content format. -func convertToGenai(req *GeminiRequest) ([]*genai.Content, error) { - var contents []*genai.Content - for _, c := range req.Contents { - genaiParts := []*genai.Part{} // Create a slice of the interface type - for _, p := range c.Parts { - if p.Text != "" { - // genai.Text returns a Part interface, which is what we need - var tmp *genai.Part - tmp = new(genai.Part) - tmp.Text = p.Text - genaiParts = append(genaiParts, tmp) - } - } - contents = append(contents, &genai.Content{ - Role: c.Role, - Parts: genaiParts, - }) - } - return contents, nil -} diff --git a/main.go b/main.go index 9826fdc..b2b58bb 100644 --- a/main.go +++ b/main.go @@ -8,9 +8,6 @@ package main import ( "embed" "os" - "path/filepath" - "strconv" - "strings" "github.com/google/uuid" "go.wit.com/dev/alexflint/arg" @@ -61,43 +58,19 @@ func main() { me.chats.ConfigSave() } - aiClient, err := doConnect() + err := doConnect() if err != nil { badExit(err) } - _ = aiClient if argv.JsonFile != "" { - // now try to Marshal() into a protobuf - pb, err := parsePB(argv.JsonFile) - if err != nil { - badExit(err) - } - log.Info("GeminiContent pb.Marshal() worked pb.Contents len =", len(pb.Contents)) - _, filename := filepath.Split(argv.JsonFile) - parts := strings.Split(filename, ".") - - if len(parts) == 5 { - uuid := parts[1] - num, _ := strconv.Atoi(parts[3]) - log.Info(uuid, parts) - if chat := me.chats.FindByUuid(uuid); chat != nil { - log.Info("FOUND CHAT", uuid, num) - newEntry := new(chatpb.ChatEntry) - newEntry.GeminiRequest = pb - newEntry.ContentFile = filename - newEntry.RequestCounter = int32(num) - chat.AppendEntry(newEntry) - me.chats.ConfigSave() - } - } else { - } + doJSON() okExit("") } if argv.Interact != nil { log.Info("testing AI client with simpleHello()") - err = simpleHello(aiClient) + err = simpleHello() if err != nil { badExit(err) } diff --git a/structs.go b/structs.go index a66f1fd..16c63e9 100644 --- a/structs.go +++ b/structs.go @@ -4,16 +4,21 @@ package main import ( + "context" + "go.wit.com/dev/alexflint/arg" "go.wit.com/gui" "go.wit.com/lib/protobuf/chatpb" + "google.golang.org/genai" ) var me *mainType // this app's variables type mainType struct { - pp *arg.Parser // for parsing the command line args. Yay to alexf lint! - chats *chatpb.Chats // all our prior conversations with regex - myGui *gui.Node // the gui toolkit handle + pp *arg.Parser // for parsing the command line args. Yay to alexf lint! + chats *chatpb.Chats // all our prior conversations with regex + myGui *gui.Node // the gui toolkit handle + client *genai.Client // the Google Gemini AI client variable + ctx context.Context }