@@ -2,9 +2,11 @@ use std::sync::Arc;
2
2
3
3
use anyhow:: Result ;
4
4
use assistant_tool:: ToolWorkingSet ;
5
+ use client:: zed_urls;
5
6
use gpui:: {
6
- prelude:: * , px, Action , AppContext , AsyncWindowContext , EventEmitter , FocusHandle ,
7
- FocusableView , Model , Pixels , Subscription , Task , View , ViewContext , WeakView , WindowContext ,
7
+ prelude:: * , px, Action , AnyElement , AppContext , AsyncWindowContext , EventEmitter , FocusHandle ,
8
+ FocusableView , FontWeight , Model , Pixels , Subscription , Task , View , ViewContext , WeakView ,
9
+ WindowContext ,
8
10
} ;
9
11
use language_model:: { LanguageModelRegistry , Role } ;
10
12
use language_model_selector:: LanguageModelSelector ;
@@ -13,7 +15,7 @@ use workspace::dock::{DockPosition, Panel, PanelEvent};
13
15
use workspace:: Workspace ;
14
16
15
17
use crate :: message_editor:: MessageEditor ;
16
- use crate :: thread:: { Message , Thread , ThreadEvent } ;
18
+ use crate :: thread:: { Message , Thread , ThreadError , ThreadEvent } ;
17
19
use crate :: thread_store:: ThreadStore ;
18
20
use crate :: { NewThread , ToggleFocus , ToggleModelSelector } ;
19
21
@@ -35,6 +37,7 @@ pub struct AssistantPanel {
35
37
thread : Model < Thread > ,
36
38
message_editor : View < MessageEditor > ,
37
39
tools : Arc < ToolWorkingSet > ,
40
+ last_error : Option < ThreadError > ,
38
41
_subscriptions : Vec < Subscription > ,
39
42
}
40
43
@@ -76,6 +79,7 @@ impl AssistantPanel {
76
79
thread : thread. clone ( ) ,
77
80
message_editor : cx. new_view ( |cx| MessageEditor :: new ( thread, cx) ) ,
78
81
tools,
82
+ last_error : None ,
79
83
_subscriptions : subscriptions,
80
84
}
81
85
}
@@ -102,6 +106,9 @@ impl AssistantPanel {
102
106
cx : & mut ViewContext < Self > ,
103
107
) {
104
108
match event {
109
+ ThreadEvent :: ShowError ( error) => {
110
+ self . last_error = Some ( error. clone ( ) ) ;
111
+ }
105
112
ThreadEvent :: StreamedCompletion => { }
106
113
ThreadEvent :: UsePendingTools => {
107
114
let pending_tool_uses = self
@@ -320,6 +327,152 @@ impl AssistantPanel {
320
327
)
321
328
. child ( v_flex ( ) . p_1p5 ( ) . child ( Label :: new ( message. text . clone ( ) ) ) )
322
329
}
330
+
331
+ fn render_last_error ( & self , cx : & mut ViewContext < Self > ) -> Option < AnyElement > {
332
+ let last_error = self . last_error . as_ref ( ) ?;
333
+
334
+ Some (
335
+ div ( )
336
+ . absolute ( )
337
+ . right_3 ( )
338
+ . bottom_12 ( )
339
+ . max_w_96 ( )
340
+ . py_2 ( )
341
+ . px_3 ( )
342
+ . elevation_2 ( cx)
343
+ . occlude ( )
344
+ . child ( match last_error {
345
+ ThreadError :: PaymentRequired => self . render_payment_required_error ( cx) ,
346
+ ThreadError :: MaxMonthlySpendReached => {
347
+ self . render_max_monthly_spend_reached_error ( cx)
348
+ }
349
+ ThreadError :: Message ( error_message) => {
350
+ self . render_error_message ( error_message, cx)
351
+ }
352
+ } )
353
+ . into_any ( ) ,
354
+ )
355
+ }
356
+
357
+ fn render_payment_required_error ( & self , cx : & mut ViewContext < Self > ) -> AnyElement {
358
+ const ERROR_MESSAGE : & str = "Free tier exceeded. Subscribe and add payment to continue using Zed LLMs. You'll be billed at cost for tokens used." ;
359
+
360
+ v_flex ( )
361
+ . gap_0p5 ( )
362
+ . child (
363
+ h_flex ( )
364
+ . gap_1p5 ( )
365
+ . items_center ( )
366
+ . child ( Icon :: new ( IconName :: XCircle ) . color ( Color :: Error ) )
367
+ . child ( Label :: new ( "Free Usage Exceeded" ) . weight ( FontWeight :: MEDIUM ) ) ,
368
+ )
369
+ . child (
370
+ div ( )
371
+ . id ( "error-message" )
372
+ . max_h_24 ( )
373
+ . overflow_y_scroll ( )
374
+ . child ( Label :: new ( ERROR_MESSAGE ) ) ,
375
+ )
376
+ . child (
377
+ h_flex ( )
378
+ . justify_end ( )
379
+ . mt_1 ( )
380
+ . child ( Button :: new ( "subscribe" , "Subscribe" ) . on_click ( cx. listener (
381
+ |this, _, cx| {
382
+ this. last_error = None ;
383
+ cx. open_url ( & zed_urls:: account_url ( cx) ) ;
384
+ cx. notify ( ) ;
385
+ } ,
386
+ ) ) )
387
+ . child ( Button :: new ( "dismiss" , "Dismiss" ) . on_click ( cx. listener (
388
+ |this, _, cx| {
389
+ this. last_error = None ;
390
+ cx. notify ( ) ;
391
+ } ,
392
+ ) ) ) ,
393
+ )
394
+ . into_any ( )
395
+ }
396
+
397
+ fn render_max_monthly_spend_reached_error ( & self , cx : & mut ViewContext < Self > ) -> AnyElement {
398
+ const ERROR_MESSAGE : & str = "You have reached your maximum monthly spend. Increase your spend limit to continue using Zed LLMs." ;
399
+
400
+ v_flex ( )
401
+ . gap_0p5 ( )
402
+ . child (
403
+ h_flex ( )
404
+ . gap_1p5 ( )
405
+ . items_center ( )
406
+ . child ( Icon :: new ( IconName :: XCircle ) . color ( Color :: Error ) )
407
+ . child ( Label :: new ( "Max Monthly Spend Reached" ) . weight ( FontWeight :: MEDIUM ) ) ,
408
+ )
409
+ . child (
410
+ div ( )
411
+ . id ( "error-message" )
412
+ . max_h_24 ( )
413
+ . overflow_y_scroll ( )
414
+ . child ( Label :: new ( ERROR_MESSAGE ) ) ,
415
+ )
416
+ . child (
417
+ h_flex ( )
418
+ . justify_end ( )
419
+ . mt_1 ( )
420
+ . child (
421
+ Button :: new ( "subscribe" , "Update Monthly Spend Limit" ) . on_click (
422
+ cx. listener ( |this, _, cx| {
423
+ this. last_error = None ;
424
+ cx. open_url ( & zed_urls:: account_url ( cx) ) ;
425
+ cx. notify ( ) ;
426
+ } ) ,
427
+ ) ,
428
+ )
429
+ . child ( Button :: new ( "dismiss" , "Dismiss" ) . on_click ( cx. listener (
430
+ |this, _, cx| {
431
+ this. last_error = None ;
432
+ cx. notify ( ) ;
433
+ } ,
434
+ ) ) ) ,
435
+ )
436
+ . into_any ( )
437
+ }
438
+
439
+ fn render_error_message (
440
+ & self ,
441
+ error_message : & SharedString ,
442
+ cx : & mut ViewContext < Self > ,
443
+ ) -> AnyElement {
444
+ v_flex ( )
445
+ . gap_0p5 ( )
446
+ . child (
447
+ h_flex ( )
448
+ . gap_1p5 ( )
449
+ . items_center ( )
450
+ . child ( Icon :: new ( IconName :: XCircle ) . color ( Color :: Error ) )
451
+ . child (
452
+ Label :: new ( "Error interacting with language model" )
453
+ . weight ( FontWeight :: MEDIUM ) ,
454
+ ) ,
455
+ )
456
+ . child (
457
+ div ( )
458
+ . id ( "error-message" )
459
+ . max_h_32 ( )
460
+ . overflow_y_scroll ( )
461
+ . child ( Label :: new ( error_message. clone ( ) ) ) ,
462
+ )
463
+ . child (
464
+ h_flex ( )
465
+ . justify_end ( )
466
+ . mt_1 ( )
467
+ . child ( Button :: new ( "dismiss" , "Dismiss" ) . on_click ( cx. listener (
468
+ |this, _, cx| {
469
+ this. last_error = None ;
470
+ cx. notify ( ) ;
471
+ } ,
472
+ ) ) ) ,
473
+ )
474
+ . into_any ( )
475
+ }
323
476
}
324
477
325
478
impl Render for AssistantPanel {
@@ -354,5 +507,6 @@ impl Render for AssistantPanel {
354
507
. border_color ( cx. theme ( ) . colors ( ) . border_variant )
355
508
. child ( self . message_editor . clone ( ) ) ,
356
509
)
510
+ . children ( self . render_last_error ( cx) )
357
511
}
358
512
}
0 commit comments