mirror of
https://github.com/kardolus/chatgpt-cli.git
synced 2024-09-08 23:15:00 +03:00
Add debug mode
This commit is contained in:
@@ -122,7 +122,17 @@ func (c *Client) Query(input string) (string, int, error) {
|
|||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
raw, err := c.caller.Post(c.getEndpoint(c.Config.CompletionsPath), body, false)
|
endpoint := c.getEndpoint(c.Config.CompletionsPath)
|
||||||
|
|
||||||
|
if c.Config.Debug {
|
||||||
|
c.printRequestDebugInfo(endpoint, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := c.caller.Post(endpoint, body, false)
|
||||||
|
if c.Config.Debug {
|
||||||
|
c.printResponseDebugInfo(raw)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
@@ -154,7 +164,13 @@ func (c *Client) Stream(input string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := c.caller.Post(c.getEndpoint(c.Config.CompletionsPath), body, true)
|
endpoint := c.getEndpoint(c.Config.CompletionsPath)
|
||||||
|
|
||||||
|
if c.Config.Debug {
|
||||||
|
c.printRequestDebugInfo(endpoint, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := c.caller.Post(endpoint, body, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -317,3 +333,18 @@ func generateUniqueSlug() string {
|
|||||||
guid := uuid.New()
|
guid := uuid.New()
|
||||||
return InteractiveThreadPrefix + guid.String()[:4]
|
return InteractiveThreadPrefix + guid.String()[:4]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) printRequestDebugInfo(endpoint string, body []byte) {
|
||||||
|
bodyString := strings.ReplaceAll(string(body), "'", "'\"'\"'") // Escape single quotes
|
||||||
|
|
||||||
|
fmt.Printf("\nGenerated cURL command:\n\n")
|
||||||
|
fmt.Printf("curl --location --insecure --request POST '%s' \\\n", endpoint)
|
||||||
|
fmt.Printf(" --header \"Authorization: Bearer ${OPENAI_API_KEY}\" \\\n")
|
||||||
|
fmt.Printf(" --header 'Content-Type: application/json' \\\n")
|
||||||
|
fmt.Printf(" --data-raw '%s'\n\n", bodyString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) printResponseDebugInfo(raw []byte) {
|
||||||
|
fmt.Printf("\nResponse\n\n")
|
||||||
|
fmt.Printf("%s\n\n", raw)
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
defaultOmitHistory = false
|
defaultOmitHistory = false
|
||||||
defaultAutoCreateNewThread = false
|
defaultAutoCreateNewThread = false
|
||||||
defaultTrackTokenUsage = false
|
defaultTrackTokenUsage = false
|
||||||
|
defaultDebug = false
|
||||||
defaultTemperature = 1.1
|
defaultTemperature = 1.1
|
||||||
defaultTopP = 2.2
|
defaultTopP = 2.2
|
||||||
defaultFrequencyPenalty = 3.3
|
defaultFrequencyPenalty = 3.3
|
||||||
@@ -78,6 +79,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
CommandPrompt: defaultCommandPrompt,
|
CommandPrompt: defaultCommandPrompt,
|
||||||
AutoCreateNewThread: defaultAutoCreateNewThread,
|
AutoCreateNewThread: defaultAutoCreateNewThread,
|
||||||
TrackTokenUsage: defaultTrackTokenUsage,
|
TrackTokenUsage: defaultTrackTokenUsage,
|
||||||
|
Debug: defaultDebug,
|
||||||
}
|
}
|
||||||
|
|
||||||
envPrefix = strings.ToUpper(defaultConfig.Name) + "_"
|
envPrefix = strings.ToUpper(defaultConfig.Name) + "_"
|
||||||
@@ -114,6 +116,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
Expect(subject.Config.CommandPrompt).To(Equal(defaultCommandPrompt))
|
Expect(subject.Config.CommandPrompt).To(Equal(defaultCommandPrompt))
|
||||||
Expect(subject.Config.AutoCreateNewThread).To(Equal(defaultAutoCreateNewThread))
|
Expect(subject.Config.AutoCreateNewThread).To(Equal(defaultAutoCreateNewThread))
|
||||||
Expect(subject.Config.TrackTokenUsage).To(Equal(defaultTrackTokenUsage))
|
Expect(subject.Config.TrackTokenUsage).To(Equal(defaultTrackTokenUsage))
|
||||||
|
Expect(subject.Config.Debug).To(Equal(defaultDebug))
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should prioritize user-provided config over defaults", func() {
|
it("should prioritize user-provided config over defaults", func() {
|
||||||
@@ -136,6 +139,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
CommandPrompt: "user-command-prompt",
|
CommandPrompt: "user-command-prompt",
|
||||||
AutoCreateNewThread: true,
|
AutoCreateNewThread: true,
|
||||||
TrackTokenUsage: true,
|
TrackTokenUsage: true,
|
||||||
|
Debug: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
|
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
|
||||||
@@ -154,6 +158,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
Expect(subject.Config.OmitHistory).To(BeTrue())
|
Expect(subject.Config.OmitHistory).To(BeTrue())
|
||||||
Expect(subject.Config.AutoCreateNewThread).To(BeTrue())
|
Expect(subject.Config.AutoCreateNewThread).To(BeTrue())
|
||||||
Expect(subject.Config.TrackTokenUsage).To(BeTrue())
|
Expect(subject.Config.TrackTokenUsage).To(BeTrue())
|
||||||
|
Expect(subject.Config.Debug).To(BeTrue())
|
||||||
Expect(subject.Config.Role).To(Equal("user-role"))
|
Expect(subject.Config.Role).To(Equal("user-role"))
|
||||||
Expect(subject.Config.Thread).To(Equal("user-thread"))
|
Expect(subject.Config.Thread).To(Equal("user-thread"))
|
||||||
Expect(subject.Config.Temperature).To(Equal(2.5))
|
Expect(subject.Config.Temperature).To(Equal(2.5))
|
||||||
@@ -176,6 +181,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
os.Setenv(envPrefix+"OMIT_HISTORY", "true")
|
os.Setenv(envPrefix+"OMIT_HISTORY", "true")
|
||||||
os.Setenv(envPrefix+"AUTO_CREATE_NEW_THREAD", "true")
|
os.Setenv(envPrefix+"AUTO_CREATE_NEW_THREAD", "true")
|
||||||
os.Setenv(envPrefix+"TRACK_TOKEN_USAGE", "true")
|
os.Setenv(envPrefix+"TRACK_TOKEN_USAGE", "true")
|
||||||
|
os.Setenv(envPrefix+"DEBUG", "true")
|
||||||
os.Setenv(envPrefix+"ROLE", "env-role")
|
os.Setenv(envPrefix+"ROLE", "env-role")
|
||||||
os.Setenv(envPrefix+"THREAD", "env-thread")
|
os.Setenv(envPrefix+"THREAD", "env-thread")
|
||||||
os.Setenv(envPrefix+"TEMPERATURE", "2.2")
|
os.Setenv(envPrefix+"TEMPERATURE", "2.2")
|
||||||
@@ -201,6 +207,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
Expect(subject.Config.OmitHistory).To(BeTrue())
|
Expect(subject.Config.OmitHistory).To(BeTrue())
|
||||||
Expect(subject.Config.AutoCreateNewThread).To(BeTrue())
|
Expect(subject.Config.AutoCreateNewThread).To(BeTrue())
|
||||||
Expect(subject.Config.TrackTokenUsage).To(BeTrue())
|
Expect(subject.Config.TrackTokenUsage).To(BeTrue())
|
||||||
|
Expect(subject.Config.Debug).To(BeTrue())
|
||||||
Expect(subject.Config.Role).To(Equal("env-role"))
|
Expect(subject.Config.Role).To(Equal("env-role"))
|
||||||
Expect(subject.Config.Thread).To(Equal("env-thread"))
|
Expect(subject.Config.Thread).To(Equal("env-thread"))
|
||||||
Expect(subject.Config.Temperature).To(Equal(2.2))
|
Expect(subject.Config.Temperature).To(Equal(2.2))
|
||||||
@@ -223,6 +230,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
os.Setenv(envPrefix+"OMIT_HISTORY", "true")
|
os.Setenv(envPrefix+"OMIT_HISTORY", "true")
|
||||||
os.Setenv(envPrefix+"AUTO_CREATE_NEW_THREAD", "true")
|
os.Setenv(envPrefix+"AUTO_CREATE_NEW_THREAD", "true")
|
||||||
os.Setenv(envPrefix+"TRACK_TOKEN_USAGE", "true")
|
os.Setenv(envPrefix+"TRACK_TOKEN_USAGE", "true")
|
||||||
|
os.Setenv(envPrefix+"DEBUG", "false")
|
||||||
os.Setenv(envPrefix+"ROLE", "env-role")
|
os.Setenv(envPrefix+"ROLE", "env-role")
|
||||||
os.Setenv(envPrefix+"THREAD", "env-thread")
|
os.Setenv(envPrefix+"THREAD", "env-thread")
|
||||||
os.Setenv(envPrefix+"TEMPERATURE", "2.2")
|
os.Setenv(envPrefix+"TEMPERATURE", "2.2")
|
||||||
@@ -244,6 +252,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
OmitHistory: false,
|
OmitHistory: false,
|
||||||
AutoCreateNewThread: false,
|
AutoCreateNewThread: false,
|
||||||
TrackTokenUsage: false,
|
TrackTokenUsage: false,
|
||||||
|
Debug: true,
|
||||||
Role: "user-role",
|
Role: "user-role",
|
||||||
Thread: "user-thread",
|
Thread: "user-thread",
|
||||||
Temperature: 1.5,
|
Temperature: 1.5,
|
||||||
@@ -270,6 +279,7 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
|
|||||||
Expect(subject.Config.OmitHistory).To(BeTrue())
|
Expect(subject.Config.OmitHistory).To(BeTrue())
|
||||||
Expect(subject.Config.AutoCreateNewThread).To(BeTrue())
|
Expect(subject.Config.AutoCreateNewThread).To(BeTrue())
|
||||||
Expect(subject.Config.TrackTokenUsage).To(BeTrue())
|
Expect(subject.Config.TrackTokenUsage).To(BeTrue())
|
||||||
|
Expect(subject.Config.Debug).To(BeFalse())
|
||||||
Expect(subject.Config.Role).To(Equal("env-role"))
|
Expect(subject.Config.Role).To(Equal("env-role"))
|
||||||
Expect(subject.Config.Thread).To(Equal("env-thread"))
|
Expect(subject.Config.Thread).To(Equal("env-thread"))
|
||||||
Expect(subject.Config.Temperature).To(Equal(2.2))
|
Expect(subject.Config.Temperature).To(Equal(2.2))
|
||||||
@@ -428,6 +438,7 @@ func unsetEnvironmentVariables(envPrefix string) {
|
|||||||
"COMMAND_PROMPT",
|
"COMMAND_PROMPT",
|
||||||
"AUTO_CREATE_NEW_THREAD",
|
"AUTO_CREATE_NEW_THREAD",
|
||||||
"TRACK_TOKEN_USAGE",
|
"TRACK_TOKEN_USAGE",
|
||||||
|
"DEBUG",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, variable := range variables {
|
for _, variable := range variables {
|
||||||
|
|||||||
22
http/http.go
22
http/http.go
@@ -56,12 +56,22 @@ func (r *RestCaller) Post(url string, body []byte, stream bool) ([]byte, error)
|
|||||||
return r.doRequest(http.MethodPost, url, body, stream)
|
return r.doRequest(http.MethodPost, url, body, stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessResponse(r io.Reader, w io.Writer) []byte {
|
func (r *RestCaller) ProcessResponse(reader io.Reader, writer io.Writer) []byte {
|
||||||
var result []byte
|
var result []byte
|
||||||
|
|
||||||
scanner := bufio.NewScanner(r)
|
if r.config.Debug {
|
||||||
|
fmt.Printf("\nResponse\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
|
|
||||||
|
if r.config.Debug {
|
||||||
|
fmt.Println(line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(line, "data:") {
|
if strings.HasPrefix(line, "data:") {
|
||||||
line = line[6:] // Skip the "data: " prefix
|
line = line[6:] // Skip the "data: " prefix
|
||||||
if len(line) < 6 {
|
if len(line) < 6 {
|
||||||
@@ -69,7 +79,7 @@ func ProcessResponse(r io.Reader, w io.Writer) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if line == "[DONE]" {
|
if line == "[DONE]" {
|
||||||
_, _ = w.Write([]byte("\n"))
|
_, _ = writer.Write([]byte("\n"))
|
||||||
result = append(result, []byte("\n")...)
|
result = append(result, []byte("\n")...)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -77,13 +87,13 @@ func ProcessResponse(r io.Reader, w io.Writer) []byte {
|
|||||||
var data types.Data
|
var data types.Data
|
||||||
err := json.Unmarshal([]byte(line), &data)
|
err := json.Unmarshal([]byte(line), &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(w, "Error: %s\n", err.Error())
|
_, _ = fmt.Fprintf(writer, "Error: %s\n", err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, choice := range data.Choices {
|
for _, choice := range data.Choices {
|
||||||
if content, ok := choice.Delta["content"]; ok {
|
if content, ok := choice.Delta["content"]; ok {
|
||||||
_, _ = w.Write([]byte(content))
|
_, _ = writer.Write([]byte(content))
|
||||||
result = append(result, []byte(content)...)
|
result = append(result, []byte(content)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +129,7 @@ func (r *RestCaller) doRequest(method, url string, body []byte, stream bool) ([]
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stream {
|
if stream {
|
||||||
return ProcessResponse(response.Body, os.Stdout), nil
|
return r.ProcessResponse(response.Body, os.Stdout), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := io.ReadAll(response.Body)
|
result, err := io.ReadAll(response.Body)
|
||||||
|
|||||||
@@ -16,14 +16,17 @@ func TestUnitHTTP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testHTTP(t *testing.T, when spec.G, it spec.S) {
|
func testHTTP(t *testing.T, when spec.G, it spec.S) {
|
||||||
|
var subject http.RestCaller
|
||||||
|
|
||||||
it.Before(func() {
|
it.Before(func() {
|
||||||
RegisterTestingT(t)
|
RegisterTestingT(t)
|
||||||
|
subject = http.RestCaller{}
|
||||||
})
|
})
|
||||||
|
|
||||||
when("ProcessResponse()", func() {
|
when("ProcessResponse()", func() {
|
||||||
it("parses a stream as expected", func() {
|
it("parses a stream as expected", func() {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
http.ProcessResponse(strings.NewReader(stream), buf)
|
subject.ProcessResponse(strings.NewReader(stream), buf)
|
||||||
output := buf.String()
|
output := buf.String()
|
||||||
Expect(output).To(Equal("a b c\n"))
|
Expect(output).To(Equal("a b c\n"))
|
||||||
})
|
})
|
||||||
@@ -32,7 +35,7 @@ func testHTTP(t *testing.T, when spec.G, it spec.S) {
|
|||||||
expectedOutput := "Error: unexpected end of JSON input\n"
|
expectedOutput := "Error: unexpected end of JSON input\n"
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
http.ProcessResponse(strings.NewReader(input), &buf)
|
subject.ProcessResponse(strings.NewReader(input), &buf)
|
||||||
output := buf.String()
|
output := buf.String()
|
||||||
|
|
||||||
Expect(output).To(Equal(expectedOutput))
|
Expect(output).To(Equal(expectedOutput))
|
||||||
|
|||||||
@@ -487,6 +487,22 @@ max_tokens: 100
|
|||||||
Expect(output).To(ContainSubstring("Token Usage:"))
|
Expect(output).To(ContainSubstring("Token Usage:"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("prints debug information with the --debug flag", func() {
|
||||||
|
Expect(os.Setenv("OPENAI_DEBUG", "true")).To(Succeed())
|
||||||
|
|
||||||
|
output := runCommand("--query", "tell me a joke")
|
||||||
|
|
||||||
|
Expect(output).To(ContainSubstring("Generated cURL command"))
|
||||||
|
Expect(output).To(ContainSubstring("/v1/chat/completions"))
|
||||||
|
Expect(output).To(ContainSubstring("--header \"Authorization: Bearer ${OPENAI_API_KEY}\""))
|
||||||
|
Expect(output).To(ContainSubstring("--header 'Content-Type: application/json'"))
|
||||||
|
Expect(output).To(ContainSubstring("\"model\":\"gpt-3.5-turbo\""))
|
||||||
|
Expect(output).To(ContainSubstring("\"messages\":"))
|
||||||
|
Expect(output).To(ContainSubstring("Response"))
|
||||||
|
|
||||||
|
Expect(os.Unsetenv("OPENAI_DEBUG")).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
it("should assemble http errors as expected", func() {
|
it("should assemble http errors as expected", func() {
|
||||||
Expect(os.Setenv(apiKeyEnvVar, "wrong-token")).To(Succeed())
|
Expect(os.Setenv(apiKeyEnvVar, "wrong-token")).To(Succeed())
|
||||||
|
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ type Config struct {
|
|||||||
CommandPrompt string `yaml:"command_prompt"`
|
CommandPrompt string `yaml:"command_prompt"`
|
||||||
AutoCreateNewThread bool `yaml:"auto_create_new_thread"`
|
AutoCreateNewThread bool `yaml:"auto_create_new_thread"`
|
||||||
TrackTokenUsage bool `yaml:"track_token_usage"`
|
TrackTokenUsage bool `yaml:"track_token_usage"`
|
||||||
|
Debug bool `yaml:"debug"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user