diff --git a/convertToPB.go b/convertToPB.go index e9399b6..13faad5 100644 --- a/convertToPB.go +++ b/convertToPB.go @@ -21,15 +21,24 @@ func convertToPB(resp *genai.GenerateContentResponse) *chatpb.ChatEntry { PartType: &chatpb.Part_Text{Text: part.Text}, }) } - if part.FunctionCall != nil { - fmt.Printf("Gemini API requested to execute command: %s\n", part.FunctionCall.Name) - // This is a simplified conversion of args. - // A more robust implementation would handle different value types. + if fc := part.FunctionCall; fc != nil { + fmt.Printf("Gemini API requested to execute command: %s\n", fc.Name) entry.Parts = append(entry.Parts, &chatpb.Part{ PartType: &chatpb.Part_FunctionCall{ FunctionCall: &chatpb.FunctionCall{ - Name: part.FunctionCall.Name, - Args: &chatpb.ArgsInfo{}, // TODO: Properly map args if needed + Name: fc.Name, + Args: &chatpb.ArgsInfo{}, // TODO: Properly map args from fc.Args map[string]any + }, + }, + }) + } + if fr := part.FunctionResponse; fr != nil { + // Convert the FunctionResponse to the protobuf equivalent + entry.Parts = append(entry.Parts, &chatpb.Part{ + PartType: &chatpb.Part_FunctionResponse{ + FunctionResponse: &chatpb.FunctionResponse{ + Name: fr.Name, + // TODO: Properly map the response content }, }, }) diff --git a/doConnect.go b/doConnect.go index 1c29f04..4082aa6 100644 --- a/doConnect.go +++ b/doConnect.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + "go.wit.com/lib/protobuf/chatpb" "go.wit.com/log" "google.golang.org/genai" ) @@ -23,6 +24,78 @@ func doConnect() error { return log.Errorf("failed to create new genai client: %w", err) } + if me.lastChat == nil { + log.Info("WTF. lastChat is nil") + return nil + } + + // if me.lastChat.Entries == nil { + // me.lastChat.Entries = new(chatpb.ChatEntry) + // } + + // In a real application, you would get user input here. + // For now, we'll use a hardcoded prompt. + if len(me.lastChat.GetEntries()) == 0 { + me.lastChat.Entries = append(me.lastChat.Entries, &chatpb.ChatEntry{ + Parts: []*chatpb.Part{ + {PartType: &chatpb.Part_Text{Text: "hello, how are you"}}, + }, + }) + } + + lastEntry := me.lastChat.GetEntries()[len(me.lastChat.GetEntries())-1] + genaiContents, err := convertToGenai(lastEntry.GetGeminiRequest()) + if err != nil { + return err + } + + resp, err := me.client.Models.GenerateContent(me.ctx, "gemini-2.5-flash", genaiContents, nil) + if err != nil { + return log.Errorf("error sending message: %v", err) + } + + if resp == nil || len(resp.Candidates) == 0 || resp.Candidates[0].Content == nil { + log.Info("Received an empty response from the API. Stopping.") + return nil + } + + // Append the model's response to the history + me.lastChat.Entries = append(me.lastChat.Entries, convertToPB(resp)) + + // Check for a function call + hasFunctionCall := false + for _, part := range resp.Candidates[0].Content.Parts { + if fc := part.FunctionCall; fc != nil { + hasFunctionCall = true + functionResponse := handleFunctionCall(fc) + // Append the function response to the history for the next turn + me.lastChat.Entries = append(me.lastChat.Entries, &chatpb.ChatEntry{ + Parts: []*chatpb.Part{ + {PartType: &chatpb.Part_FunctionResponse{ + FunctionResponse: &chatpb.FunctionResponse{ + Name: functionResponse.Name, + // TODO: map response + }, + }}, + }, + }) + } + } + + // If there was no function call, print the text and stop. + if !hasFunctionCall { + log.Info("Response from API:") + for _, cand := range resp.Candidates { + if cand.Content != nil { + for _, part := range cand.Content.Parts { + if part.Text != "" { + fmt.Println(part.Text) + } + } + } + } + } + return nil } diff --git a/dumpchat.go b/dumpchat.go index 2f479ae..3f57323 100644 --- a/dumpchat.go +++ b/dumpchat.go @@ -24,7 +24,7 @@ func dumpchat(chat *chatpb.Chat) error { if lastEntry.GeminiRequest == nil { return log.Errorf("lastEntry.GeminiRequest == nil") } - aichat, err := convertToGenai(lastEntry.GeminiRequest) + aichat, err := convertToGenai(lastEntry.GetGeminiRequest()) if err != nil { log.Info("convertToGenai() returned with error", err) return err diff --git a/handleFunctionCall.go b/handleFunctionCall.go new file mode 100644 index 0000000..a218238 --- /dev/null +++ b/handleFunctionCall.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + + "go.wit.com/log" + "google.golang.org/genai" +) + +// handleFunctionCall executes a command requested by the Gemini API and returns the result. +func handleFunctionCall(fc *genai.FunctionCall) *genai.FunctionResponse { + if fc == nil { + return nil + } + + if fc.Name != "run_shell_command" { + log.Infof("Unsupported function call: %s", fc.Name) + return &genai.FunctionResponse{ + Name: fc.Name, + Response: map[string]any{ + "error": fmt.Sprintf("Unsupported function call: %s", fc.Name), + }, + } + } + + // Extract arguments + cmd, _ := fc.Args["command"].(string) + dir, _ := fc.Args["directory"].(string) + + if cmd == "" { + return &genai.FunctionResponse{ + Name: fc.Name, + Response: map[string]any{ + "error": "missing command argument", + }, + } + } + + // Execute the command (this is a placeholder for the actual execution) + // In a real implementation, you would use the run_shell_command tool here. + log.Infof("Executing command: '%s' in directory: '%s'", cmd, dir) + // For now, we'll return a dummy response. + // TODO: Replace this with actual command execution. + + return &genai.FunctionResponse{ + Name: fc.Name, + Response: map[string]any{ + "output": "command executed successfully (dummy response)", + }, + } +}