@@ -2,11 +2,14 @@ package commands
22
33import (
44 "bufio"
5+ "context"
56 "errors"
67 "fmt"
78 "io"
89 "os"
10+ "os/signal"
911 "strings"
12+ "syscall"
1013
1114 "github.com/charmbracelet/glamour"
1215 "github.com/docker/model-runner/cmd/cli/commands/completion"
@@ -201,8 +204,32 @@ func generateInteractiveWithReadline(cmd *cobra.Command, desktopClient *desktop.
201204 if sb .Len () > 0 && ! multiline {
202205 userInput := sb .String ()
203206
204- if err := chatWithMarkdown (cmd , desktopClient , backend , model , userInput , apiKey ); err != nil {
205- cmd .PrintErr (handleClientError (err , "Failed to generate a response" ))
207+ // Create a cancellable context for the chat request
208+ // This allows us to cancel the request if the user presses Ctrl+C during response generation
209+ chatCtx , cancelChat := context .WithCancel (context .Background ())
210+
211+ // Set up signal handler to cancel the context on Ctrl+C
212+ sigChan := make (chan os.Signal , 1 )
213+ signal .Notify (sigChan , syscall .SIGINT )
214+ go func () {
215+ <- sigChan
216+ cancelChat ()
217+ }()
218+
219+ err := chatWithMarkdownContext (chatCtx , cmd , desktopClient , backend , model , userInput , apiKey )
220+
221+ // Clean up signal handler
222+ signal .Stop (sigChan )
223+ close (sigChan )
224+ cancelChat ()
225+
226+ if err != nil {
227+ // Check if the error is due to context cancellation (Ctrl+C during response)
228+ if errors .Is (err , context .Canceled ) {
229+ cmd .Println ()
230+ } else {
231+ cmd .PrintErr (handleClientError (err , "Failed to generate a response" ))
232+ }
206233 sb .Reset ()
207234 continue
208235 }
@@ -233,8 +260,32 @@ func generateInteractiveBasic(cmd *cobra.Command, desktopClient *desktop.Client,
233260 continue
234261 }
235262
236- if err := chatWithMarkdown (cmd , desktopClient , backend , model , userInput , apiKey ); err != nil {
237- cmd .PrintErr (handleClientError (err , "Failed to generate a response" ))
263+ // Create a cancellable context for the chat request
264+ // This allows us to cancel the request if the user presses Ctrl+C during response generation
265+ chatCtx , cancelChat := context .WithCancel (context .Background ())
266+
267+ // Set up signal handler to cancel the context on Ctrl+C
268+ sigChan := make (chan os.Signal , 1 )
269+ signal .Notify (sigChan , syscall .SIGINT )
270+ go func () {
271+ <- sigChan
272+ cancelChat ()
273+ }()
274+
275+ err = chatWithMarkdownContext (chatCtx , cmd , desktopClient , backend , model , userInput , apiKey )
276+
277+ // Clean up signal handler
278+ signal .Stop (sigChan )
279+ close (sigChan )
280+ cancelChat ()
281+
282+ if err != nil {
283+ // Check if the error is due to context cancellation (Ctrl+C during response)
284+ if errors .Is (err , context .Canceled ) {
285+ fmt .Println ("\n Use Ctrl + d or /bye to exit." )
286+ } else {
287+ cmd .PrintErr (handleClientError (err , "Failed to generate a response" ))
288+ }
238289 continue
239290 }
240291
@@ -425,21 +476,26 @@ func renderMarkdown(content string) (string, error) {
425476
426477// chatWithMarkdown performs chat and streams the response with selective markdown rendering.
427478func chatWithMarkdown (cmd * cobra.Command , client * desktop.Client , backend , model , prompt , apiKey string ) error {
479+ return chatWithMarkdownContext (cmd .Context (), cmd , client , backend , model , prompt , apiKey )
480+ }
481+
482+ // chatWithMarkdownContext performs chat with context support and streams the response with selective markdown rendering.
483+ func chatWithMarkdownContext (ctx context.Context , cmd * cobra.Command , client * desktop.Client , backend , model , prompt , apiKey string ) error {
428484 colorMode , _ := cmd .Flags ().GetString ("color" )
429485 useMarkdown := shouldUseMarkdown (colorMode )
430486 debug , _ := cmd .Flags ().GetBool ("debug" )
431487
432488 if ! useMarkdown {
433489 // Simple case: just stream as plain text
434- return client .Chat ( backend , model , prompt , apiKey , func (content string ) {
490+ return client .ChatWithContext ( ctx , backend , model , prompt , apiKey , func (content string ) {
435491 cmd .Print (content )
436492 }, false )
437493 }
438494
439495 // For markdown: use streaming buffer to render code blocks as they complete
440496 markdownBuffer := NewStreamingMarkdownBuffer ()
441497
442- err := client .Chat ( backend , model , prompt , apiKey , func (content string ) {
498+ err := client .ChatWithContext ( ctx , backend , model , prompt , apiKey , func (content string ) {
443499 // Use the streaming markdown buffer to intelligently render content
444500 rendered , err := markdownBuffer .AddContent (content , true )
445501 if err != nil {
0 commit comments