diff --git a/cmd/kube-copilot/diagnose.go b/cmd/kube-copilot/diagnose.go index a7e8adf..5132c21 100644 --- a/cmd/kube-copilot/diagnose.go +++ b/cmd/kube-copilot/diagnose.go @@ -21,6 +21,7 @@ import ( "github.com/fatih/color" "github.com/feiskyer/kube-copilot/pkg/assistants" "github.com/feiskyer/kube-copilot/pkg/utils" + "github.com/feiskyer/kube-copilot/pkg/workflows" "github.com/sashabaranov/go-openai" "github.com/spf13/cobra" ) @@ -110,6 +111,15 @@ var diagnoseCmd = &cobra.Command{ color.Red(err.Error()) return } - utils.RenderMarkdown(response) + + instructions := fmt.Sprintf("Extract the final diagnose results and reformat in a concise Markdown response: %s", response) + result, err := workflows.AssistantFlow(model, instructions, verbose) + if err != nil { + color.Red(err.Error()) + fmt.Println(response) + return + } + + utils.RenderMarkdown(result) }, } diff --git a/cmd/kube-copilot/execute.go b/cmd/kube-copilot/execute.go index 2dbecbc..ef9495c 100644 --- a/cmd/kube-copilot/execute.go +++ b/cmd/kube-copilot/execute.go @@ -24,6 +24,7 @@ import ( "github.com/feiskyer/kube-copilot/pkg/tools" kubetools "github.com/feiskyer/kube-copilot/pkg/tools" "github.com/feiskyer/kube-copilot/pkg/utils" + "github.com/feiskyer/kube-copilot/pkg/workflows" "github.com/sashabaranov/go-openai" "github.com/spf13/cobra" ) @@ -95,6 +96,15 @@ var executeCmd = &cobra.Command{ color.Red(err.Error()) return } - utils.RenderMarkdown(response) + + instructions := fmt.Sprintf("Extract the execuation results for user instructions and reformat in a concise Markdown response: %s", response) + result, err := workflows.AssistantFlow(model, instructions, verbose) + if err != nil { + color.Red(err.Error()) + fmt.Println(response) + return + } + + utils.RenderMarkdown(result) }, } diff --git a/go.mod b/go.mod index 4cfc0d1..773fcff 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/pkoukk/tiktoken-go v0.1.7 github.com/sashabaranov/go-openai v1.36.0 github.com/spf13/cobra v1.8.1 + golang.org/x/term v0.27.0 google.golang.org/api v0.211.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/apimachinery v0.31.4 @@ -81,7 +82,6 @@ require ( golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 // indirect diff --git a/go.sum b/go.sum index a09933c..4c97e62 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,6 @@ github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtz github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/feiskyer/swarm-go v0.1.2 h1:0To2+wqIUH2V+5ci5f2va/dF/qw79nL3OtmdETBST6s= -github.com/feiskyer/swarm-go v0.1.2/go.mod h1:/8mxGrd86L4I7PUZmmVtreeDjMZAdSs0kETLHk0jLrE= github.com/feiskyer/swarm-go v0.1.3 h1:fnTP3RcE+ueyquwvs2V+3mDNeHjqf4+Mh7OWKAIRXSY= github.com/feiskyer/swarm-go v0.1.3/go.mod h1:/8mxGrd86L4I7PUZmmVtreeDjMZAdSs0kETLHk0jLrE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= diff --git a/pkg/utils/term.go b/pkg/utils/term.go index 4ae91e2..f9addfa 100644 --- a/pkg/utils/term.go +++ b/pkg/utils/term.go @@ -4,12 +4,15 @@ import ( "fmt" "github.com/charmbracelet/glamour" + "golang.org/x/term" ) // RenderMarkdown renders markdown to the terminal. func RenderMarkdown(md string) error { + width, _, _ := term.GetSize(0) styler, err := glamour.NewTermRenderer( glamour.WithAutoStyle(), + glamour.WithWordWrap(width), ) if err != nil { fmt.Println(md) diff --git a/pkg/workflows/assistant.go b/pkg/workflows/assistant.go new file mode 100644 index 0000000..61b45d3 --- /dev/null +++ b/pkg/workflows/assistant.go @@ -0,0 +1,67 @@ +package workflows + +import ( + "context" + "fmt" + "os" + + "github.com/feiskyer/swarm-go" +) + +const assistantPrompt = `As a Kubernetes expert, guide the user according to the given instructions to solve their problem or achieve their objective. + +Understand the nature of their request, clarify any complex concepts, and provide step-by-step guidance tailored to their specific needs. Ensure that your explanations are comprehensive, using precise Kubernetes terminology and concepts. + +# Steps + +1. **Interpret User Intent**: Carefully analyze the user's instructions or questions to understand their goal. +2. **Concepts Explanation**: If necessary, break down complex Kubernetes concepts into simpler terms. +3. **Step-by-Step Solution**: Provide a detailed, clear step-by-step process to achieve the desired outcome. +4. **Troubleshooting**: Suggest potential solutions for common issues and pitfalls when working with Kubernetes. +5. **Best Practices**: Mention any relevant Kubernetes best practices that should be followed. + +# Output Format + +Provide a concise Markdown response in a clear, logical order. Each step should be concise, using bullet points or numbered lists if necessary. Include code snippets in markdown code blocks where relevant. + +# Notes + +- Assume the user has basic knowledge of Kubernetes. +- Use precise terminology and include explanations only as needed based on the complexity of the task. +- Ensure instructions are applicable across major cloud providers (GKE, EKS, AKS) unless specified otherwise.` + +// AssistantFlow runs a simple workflow by following the given instructions. +func AssistantFlow(model string, instructions string, verbose bool) (string, error) { + assistantFlow := &swarm.Workflow{ + Name: "assistant-workflow", + Model: model, + MaxTurns: 30, + Verbose: verbose, + System: "You are an expert on Kubernetes helping user for the given instructions.", + Steps: []swarm.WorkflowStep{ + { + Name: "assistant", + Instructions: assistantPrompt, + Inputs: map[string]interface{}{ + "instructions": instructions, + }, + }, + }, + } + + // Create OpenAI client + client, err := NewSwarm() + if err != nil { + fmt.Printf("Failed to create client: %v\n", err) + os.Exit(1) + } + + // Initialize and run workflow + assistantFlow.Initialize() + result, _, err := assistantFlow.Run(context.Background(), client) + if err != nil { + return "", err + } + + return result, nil +}