Skip to content

Commit ced8359

Browse files
authored
Improve the how command UI (#2559)
1 parent f490b15 commit ced8359

File tree

2 files changed

+56
-36
lines changed

2 files changed

+56
-36
lines changed

docs/general/ai/help.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ package ai
33
var Usage = []string{"how"}
44

55
func GetDescription() string {
6-
return "Ask questions about JFrog CLI commands and their usage."
6+
return "This AI-based interface converts your natural language inputs into fully functional JFrog CLI commands. This is an interactive command that accepts no arguments."
77
}

general/ai/cli.go

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,39 @@
11
package ai
22

33
import (
4+
"bufio"
45
"bytes"
56
"encoding/json"
67
"errors"
78
"fmt"
89
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
9-
"github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
1010
"github.com/jfrog/jfrog-cli/utils/cliutils"
11+
"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
1112
"github.com/jfrog/jfrog-client-go/http/httpclient"
1213
"github.com/jfrog/jfrog-client-go/utils/errorutils"
1314
"github.com/jfrog/jfrog-client-go/utils/log"
1415
"github.com/urfave/cli"
1516
"io"
1617
"net/http"
18+
"os"
1719
"strings"
1820
)
1921

2022
type ApiCommand string
2123

2224
const (
23-
cliAiApiPath = "https://cli-ai.jfrog.info/"
24-
questionApi ApiCommand = "ask"
25-
feedbackApi ApiCommand = "feedback"
25+
cliAiApiPath = "https://cli-ai-app.jfrog.info/"
26+
apiPrefix = "api/"
27+
questionApi ApiCommand = apiPrefix + "ask"
28+
feedbackApi ApiCommand = apiPrefix + "feedback"
2629
)
2730

28-
type questionBody struct {
31+
type QuestionBody struct {
2932
Question string `json:"question"`
3033
}
3134

32-
type feedbackBody struct {
33-
questionBody
35+
type FeedbackBody struct {
36+
QuestionBody
3437
LlmAnswer string `json:"llm_answer"`
3538
IsAccurate bool `json:"is_accurate"`
3639
ExpectedAnswer string `json:"expected_answer"`
@@ -40,47 +43,63 @@ func HowCmd(c *cli.Context) error {
4043
if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil {
4144
return err
4245
}
43-
if c.NArg() < 1 {
46+
if c.NArg() > 0 {
4447
return cliutils.WrongNumberOfArgumentsHandler(c)
4548
}
49+
log.Output(coreutils.PrintTitle("This AI-based interface converts your natural language inputs into fully functional JFrog CLI commands.\n" +
50+
"NOTE: This is a beta version and it supports mostly Artifactory and Xray commands.\n"))
4651

47-
args := cliutils.ExtractCommand(c)
48-
question := questionBody{Question: fmt.Sprintf("How %s", strings.Join(args, " "))}
49-
llmAnswer, err := askQuestion(question)
50-
if err != nil {
51-
return err
52-
}
53-
if strings.ToLower(llmAnswer) == "i dont know" {
54-
log.Output("The current version of the AI model does not support this type of command yet.")
55-
return nil
56-
}
57-
log.Output("AI generated JFrog CLI command:")
58-
err = coreutils.PrintTable("", "", coreutils.PrintTitle(llmAnswer), false)
59-
if err != nil {
60-
return err
61-
}
52+
for {
53+
var question string
54+
scanner := bufio.NewScanner(os.Stdin)
55+
fmt.Print("🐸 Your request: ")
56+
for {
57+
// Ask the user for a question
58+
scanner.Scan()
59+
question = strings.TrimSpace(scanner.Text())
60+
if question != "" {
61+
// If the user entered a question, break the loop
62+
break
63+
}
64+
}
65+
questionBody := QuestionBody{Question: question}
66+
llmAnswer, err := askQuestion(questionBody)
67+
if err != nil {
68+
return err
69+
}
6270

63-
feedback := feedbackBody{questionBody: question, LlmAnswer: llmAnswer}
64-
feedback.getUserFeedback()
65-
if err = sendFeedback(feedback); err != nil {
66-
return err
71+
log.Output("🤖 Generated command: " + coreutils.PrintLink(llmAnswer) + "\n")
72+
feedback := FeedbackBody{QuestionBody: questionBody, LlmAnswer: llmAnswer}
73+
feedback.getUserFeedback()
74+
if err = sendFeedback(feedback); err != nil {
75+
return err
76+
}
77+
log.Output("\n" + coreutils.PrintComment("-------------------") + "\n")
6778
}
68-
log.Output("Thank you for your feedback!")
69-
return nil
7079
}
7180

72-
func (fb *feedbackBody) getUserFeedback() {
73-
fb.IsAccurate = coreutils.AskYesNo(coreutils.PrintLink("Is the provided command accurate?"), true)
81+
func (fb *FeedbackBody) getUserFeedback() {
82+
fb.IsAccurate = coreutils.AskYesNo("Is the provided command accurate?", true)
7483
if !fb.IsAccurate {
75-
ioutils.ScanFromConsole("Please provide the exact command you expected (Example: 'jf rt u ...')", &fb.ExpectedAnswer, "")
84+
scanner := bufio.NewScanner(os.Stdin)
85+
fmt.Print("Please provide the exact command you expected (Example: 'jf rt u ...'): ")
86+
for {
87+
scanner.Scan()
88+
expectedAnswer := strings.TrimSpace(scanner.Text())
89+
if expectedAnswer != "" {
90+
// If the user entered an expected answer, break and return
91+
fb.ExpectedAnswer = expectedAnswer
92+
return
93+
}
94+
}
7695
}
7796
}
7897

79-
func askQuestion(question questionBody) (response string, err error) {
98+
func askQuestion(question QuestionBody) (response string, err error) {
8099
return sendRequestToCliAiServer(question, questionApi)
81100
}
82101

83-
func sendFeedback(feedback feedbackBody) (err error) {
102+
func sendFeedback(feedback FeedbackBody) (err error) {
84103
_, err = sendRequestToCliAiServer(feedback, feedbackApi)
85104
return
86105
}
@@ -123,7 +142,8 @@ func sendRequestToCliAiServer(content interface{}, apiCommand ApiCommand) (respo
123142
}
124143
}()
125144
var body []byte
126-
body, err = io.ReadAll(resp.Body)
145+
// Limit size of response body to 10MB
146+
body, err = io.ReadAll(io.LimitReader(resp.Body, 10*utils.SizeMiB))
127147
if errorutils.CheckError(err) != nil {
128148
return
129149
}

0 commit comments

Comments
 (0)