From f62d2198f9c766b1ff16bb2f6bab0228186edde7 Mon Sep 17 00:00:00 2001 From: Kayvan Sylvan Date: Wed, 25 Jun 2025 22:08:26 -0700 Subject: [PATCH] refactor: extract message and option formatting logic into reusable methods ## CHANGES - Extract multi-content message formatting to dedicated method - Create formatMessages method for all message types - Add formatOptions method for chat options display - Replace inline formatting with strings.Builder usage - Reduce code duplication between Send and SendStream - Improve code organization and maintainability --- plugins/ai/dryrun/dryrun.go | 116 ++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 63 deletions(-) diff --git a/plugins/ai/dryrun/dryrun.go b/plugins/ai/dryrun/dryrun.go index fd868d82..f1522fcf 100644 --- a/plugins/ai/dryrun/dryrun.go +++ b/plugins/ai/dryrun/dryrun.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "strings" goopenai "github.com/sashabaranov/go-openai" @@ -23,88 +24,77 @@ func (c *Client) ListModels() ([]string, error) { return []string{"dry-run-model"}, nil } -func (c *Client) SendStream(msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string) error { - output := "Dry run: Would send the following request:\n\n" +func (c *Client) formatMultiContentMessage(msg *goopenai.ChatCompletionMessage) string { + var builder strings.Builder + + if len(msg.MultiContent) > 0 { + builder.WriteString(fmt.Sprintf("%s:\n", msg.Role)) + for _, part := range msg.MultiContent { + builder.WriteString(fmt.Sprintf(" - Type: %s\n", part.Type)) + if part.Type == goopenai.ChatMessagePartTypeImageURL { + builder.WriteString(fmt.Sprintf(" Image URL: %s\n", part.ImageURL.URL)) + } else { + builder.WriteString(fmt.Sprintf(" Text: %s\n", part.Text)) + } + } + builder.WriteString("\n") + } else { + builder.WriteString(fmt.Sprintf("%s:\n%s\n\n", msg.Role, msg.Content)) + } + + return builder.String() +} + +func (c *Client) formatMessages(msgs []*goopenai.ChatCompletionMessage) string { + var builder strings.Builder for _, msg := range msgs { switch msg.Role { case goopenai.ChatMessageRoleSystem: - output += fmt.Sprintf("System:\n%s\n\n", msg.Content) + builder.WriteString(fmt.Sprintf("System:\n%s\n\n", msg.Content)) case goopenai.ChatMessageRoleAssistant: - output += fmt.Sprintf("Assistant:\n%s\n\n", msg.Content) + builder.WriteString(fmt.Sprintf("Assistant:\n%s\n\n", msg.Content)) case goopenai.ChatMessageRoleUser: - if msg.MultiContent != nil { - output += "User:\n" - for _, part := range msg.MultiContent { - output += fmt.Sprintf(" - Type: %s\n", part.Type) - if part.Type == goopenai.ChatMessagePartTypeImageURL { - output += fmt.Sprintf(" Image URL: %s\n", part.ImageURL.URL) - } else { - output += fmt.Sprintf(" Text: %s\n", part.Text) - } - } - output += "\n" - } else { - output += fmt.Sprintf("User:\n%s\n\n", msg.Content) - } + builder.WriteString(c.formatMultiContentMessage(msg)) default: - output += fmt.Sprintf("%s:\n%s\n\n", msg.Role, msg.Content) + builder.WriteString(fmt.Sprintf("%s:\n%s\n\n", msg.Role, msg.Content)) } } - output += "Options:\n" - output += fmt.Sprintf("Model: %s\n", opts.Model) - output += fmt.Sprintf("Temperature: %f\n", opts.Temperature) - output += fmt.Sprintf("TopP: %f\n", opts.TopP) - output += fmt.Sprintf("PresencePenalty: %f\n", opts.PresencePenalty) - output += fmt.Sprintf("FrequencyPenalty: %f\n", opts.FrequencyPenalty) + return builder.String() +} + +func (c *Client) formatOptions(opts *common.ChatOptions) string { + var builder strings.Builder + + builder.WriteString("Options:\n") + builder.WriteString(fmt.Sprintf("Model: %s\n", opts.Model)) + builder.WriteString(fmt.Sprintf("Temperature: %f\n", opts.Temperature)) + builder.WriteString(fmt.Sprintf("TopP: %f\n", opts.TopP)) + builder.WriteString(fmt.Sprintf("PresencePenalty: %f\n", opts.PresencePenalty)) + builder.WriteString(fmt.Sprintf("FrequencyPenalty: %f\n", opts.FrequencyPenalty)) if opts.ModelContextLength != 0 { - output += fmt.Sprintf("ModelContextLength: %d\n", opts.ModelContextLength) + builder.WriteString(fmt.Sprintf("ModelContextLength: %d\n", opts.ModelContextLength)) } - channel <- output + return builder.String() +} + +func (c *Client) SendStream(msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string) error { + var builder strings.Builder + builder.WriteString("Dry run: Would send the following request:\n\n") + builder.WriteString(c.formatMessages(msgs)) + builder.WriteString(c.formatOptions(opts)) + + channel <- builder.String() close(channel) return nil } func (c *Client) Send(_ context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (string, error) { fmt.Println("Dry run: Would send the following request:") - - for _, msg := range msgs { - switch msg.Role { - case goopenai.ChatMessageRoleSystem: - fmt.Printf("System:\n%s\n\n", msg.Content) - case goopenai.ChatMessageRoleAssistant: - fmt.Printf("Assistant:\n%s\n\n", msg.Content) - case goopenai.ChatMessageRoleUser: - if msg.MultiContent != nil { - fmt.Println("User:") - for _, part := range msg.MultiContent { - fmt.Printf(" - Type: %s\n", part.Type) - if part.Type == goopenai.ChatMessagePartTypeImageURL { - fmt.Printf(" Image URL: %s\n", part.ImageURL.URL) - } else { - fmt.Printf(" Text: %s\n", part.Text) - } - } - fmt.Println() - } else { - fmt.Printf("User:\n%s\n\n", msg.Content) - } - default: - fmt.Printf("%s:\n%s\n\n", msg.Role, msg.Content) - } - } - - fmt.Println("Options:") - fmt.Printf("Model: %s\n", opts.Model) - fmt.Printf("Temperature: %f\n", opts.Temperature) - fmt.Printf("TopP: %f\n", opts.TopP) - fmt.Printf("PresencePenalty: %f\n", opts.PresencePenalty) - fmt.Printf("FrequencyPenalty: %f\n", opts.FrequencyPenalty) - if opts.ModelContextLength != 0 { - fmt.Printf("ModelContextLength: %d\n", opts.ModelContextLength) - } + fmt.Print(c.formatMessages(msgs)) + fmt.Print(c.formatOptions(opts)) return "", nil }