X-Git-Url: http://git.giacomolli.uk/onechat/blobdiff_plain/6d1d9d9a7cf90b25a2e10af1c5bc56faee7e494c:/chat.go..153f3f9b4b09d521412eacca30eddeed395c3f41:/terminal.go?ds=sidebyside diff --git a/chat.go b/terminal.go similarity index 57% rename from chat.go rename to terminal.go index 1e96b24..2906204 100644 --- a/chat.go +++ b/terminal.go @@ -12,19 +12,17 @@ import ( "strings" ) -const ( - // stopCommand is the input line that interrupts the running turn. - stopCommand = "/stop" +// terminalResultLines is the most lines of a tool result that the client shows. +const terminalResultLines = 10 - // resultLines is the most lines of a tool result that the client shows. - resultLines = 10 -) +// errTerminalDaemonClosed tells that the daemon closed the connection. +var errTerminalDaemonClosed = errors.New("the daemon closed the connection") -// chat runs "onechat chat": the terminal client of the daemon. Each line of +// terminal runs "onechat chat": the terminal client of the daemon. Each line of // input is a user message, and the line /stop interrupts the running turn. The // client shows the replies, the tool calls, and their results as they happen. // It ends at the end of the input or when the daemon closes the connection. -func chat(ctx context.Context, in io.Reader, out io.Writer) error { +func terminal(ctx context.Context, in io.Reader, out io.Writer) error { dir, err := dataDir() if err != nil { return err @@ -42,24 +40,21 @@ func chat(ctx context.Context, in io.Reader, out io.Writer) error { return fmt.Errorf(`connect to the daemon (start it with "onechat daemon"): %w`, err) } - return chatSession(ctx, conn, in, out) + return terminalSession(ctx, conn, in, out) } -// errDaemonClosed tells that the daemon closed the connection. -var errDaemonClosed = errors.New("the daemon closed the connection") - -// chatSession runs the client on conn until the input ends, the daemon closes -// the connection, or ctx ends, and closes conn. A read of the input that waits -// for a line can stay behind when the daemon closes the connection. -func chatSession(ctx context.Context, conn net.Conn, in io.Reader, out io.Writer) error { +// terminalSession runs the client on conn until the input ends, the daemon +// closes the connection, or ctx ends, and closes conn. A read of the input that +// waits for a line can stay behind when the daemon closes the connection. +func terminalSession(ctx context.Context, conn net.Conn, in io.Reader, out io.Writer) error { sent := make(chan error, 1) go func() { - sent <- sendLines(ctx, conn, in) + sent <- terminalSendLines(ctx, conn, in) }() shown := make(chan error, 1) go func() { - shown <- showEvents(conn, out) + shown <- terminalShowEvents(conn, out) }() var err error @@ -68,7 +63,7 @@ func chatSession(ctx context.Context, conn net.Conn, in io.Reader, out io.Writer err = errors.Join(err, conn.Close()) <-shown case err = <-shown: - err = errors.Join(cmpError(err, errDaemonClosed), conn.Close()) + err = errors.Join(terminalCmpError(err, errTerminalDaemonClosed), conn.Close()) case <-ctx.Done(): err = conn.Close() <-shown @@ -77,17 +72,8 @@ func chatSession(ctx context.Context, conn net.Conn, in io.Reader, out io.Writer return err } -// cmpError returns err, or fallback when err is nil. -func cmpError(err, fallback error) error { - if err == nil { - return fallback - } - - return err -} - -// sendLines sends each line of in as a request until the input ends. -func sendLines(ctx context.Context, conn net.Conn, in io.Reader) error { +// terminalSendLines sends each line of in as a request until the input ends. +func terminalSendLines(ctx context.Context, conn net.Conn, in io.Reader) error { encoder := json.NewEncoder(conn) scanner := bufio.NewScanner(in) scanner.Buffer(make([]byte, 64*1024), requestBytes) @@ -116,9 +102,9 @@ func sendLines(ctx context.Context, conn net.Conn, in io.Reader) error { return scanner.Err() } -// showEvents writes each event from the daemon to out until the connection -// closes. -func showEvents(conn net.Conn, out io.Writer) error { +// terminalShowEvents writes each event from the daemon to out until the +// connection closes. +func terminalShowEvents(conn net.Conn, out io.Writer) error { scanner := bufio.NewScanner(conn) scanner.Buffer(make([]byte, 64*1024), requestBytes) for scanner.Scan() { @@ -128,7 +114,7 @@ func showEvents(conn net.Conn, out io.Writer) error { return fmt.Errorf("decode event: %w", err) } - text := eventText(event) + text := terminalEventText(event) if text == "" { continue } @@ -142,11 +128,20 @@ func showEvents(conn net.Conn, out io.Writer) error { return scanner.Err() } -// eventText returns the text that the client shows for event, or "" for none. -// The client shows a user message only from another source, such as telegram, -// because the user typed the others. A tool result shows its first resultLines -// lines. -func eventText(event Event) string { +// terminalCmpError returns err, or fallback when err is nil. +func terminalCmpError(err, fallback error) error { + if err == nil { + return fallback + } + + return err +} + +// terminalEventText returns the text that the client shows for event, or "" for +// none. The client shows a user message only from another source, such as +// telegram, because the user typed the others. A tool result shows its first +// terminalResultLines lines. +func terminalEventText(event Event) string { switch event.Kind { case "ready": return "[connected to " + event.Text + "]" @@ -161,7 +156,7 @@ func eventText(event Event) string { case "tool": return "[tool] " + event.Text case "echo": - return "[result] " + firstLines(event.Text, resultLines) + return "[result] " + terminalFirstLines(event.Text, terminalResultLines) case "busy": return "[working]" case "idle": @@ -173,9 +168,9 @@ func eventText(event Event) string { } } -// firstLines returns the first count lines of text, with the number of the -// lines left out. -func firstLines(text string, count int) string { +// terminalFirstLines returns the first count lines of text, with the number of +// the lines left out. +func terminalFirstLines(text string, count int) string { text = strings.TrimRight(text, "\n") lines := strings.Split(text, "\n") if len(lines) <= count {