kader 0.1.5__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cli/app.tcss CHANGED
@@ -132,6 +132,26 @@ ConversationView {
132
132
  scrollbar-size: 1 1;
133
133
  }
134
134
 
135
+ .message-footer {
136
+ height: auto;
137
+ margin-top: 0;
138
+ padding: 0 1;
139
+ border-top: none;
140
+ }
141
+
142
+ .footer-left {
143
+ color: $secondary;
144
+ text-style: italic;
145
+ width: 1fr;
146
+ }
147
+
148
+ .footer-right {
149
+ color: $success;
150
+ text-style: bold;
151
+ text-align: right;
152
+ width: auto;
153
+ }
154
+
135
155
  /* ===== Welcome Message ===== */
136
156
 
137
157
  #welcome {
@@ -274,391 +294,16 @@ ScrollbarSlider:hover {
274
294
  color: $secondary;
275
295
  }
276
296
 
277
- /* ===== Theme Variants ===== */
278
-
279
- /* Ocean Theme */
280
- .theme-ocean Screen {
281
- background: #0f172a;
282
- }
283
-
284
- .theme-ocean Header {
285
- background: #3b82f6;
286
- color: #f1f5f9;
287
- }
288
-
289
- .theme-ocean Footer {
290
- background: #1e293b;
291
- color: #94a3b8;
292
- }
293
-
294
- .theme-ocean FooterKey > .footer-key--key {
295
- background: #3b82f6;
296
- color: #f1f5f9;
297
- }
298
-
299
- .theme-ocean #sidebar {
300
- background: #1e293b;
301
- border-right: thick #3b82f6;
302
- }
303
-
304
- .theme-ocean #sidebar-title {
305
- background: #3b82f6 20%;
306
- color: #f1f5f9;
307
- }
308
-
309
- .theme-ocean DirectoryTree > .directory-tree--folder {
310
- color: #06b6d4;
311
- }
312
-
313
- .theme-ocean DirectoryTree > .directory-tree--file {
314
- color: #f1f5f9;
315
- }
316
-
317
- .theme-ocean DirectoryTree > .directory-tree--extension {
318
- color: #94a3b8;
319
- }
320
-
321
- .theme-ocean DirectoryTree:focus > .directory-tree--cursor {
322
- background: #3b82f6 40%;
323
- }
324
-
325
- .theme-ocean #conversation {
326
- background: #0f172a;
327
- border-bottom: thick #1e293b;
328
- }
329
-
330
- .theme-ocean #input-container {
331
- background: #1e293b;
332
- border-top: thick #3b82f6;
333
- }
334
-
335
- .theme-ocean #prompt-input {
336
- background: #0f172a;
337
- border: round #3b82f6;
338
- }
339
-
340
- .theme-ocean #prompt-input:focus {
341
- border: round #06b6d4;
342
- }
343
-
344
- .theme-ocean Input.-valid {
345
- border: round #10b981;
346
- }
347
-
348
- .theme-ocean Input > .input--placeholder {
349
- color: #94a3b8;
350
- text-style: italic;
351
- }
352
-
353
- .theme-ocean LoadingSpinner {
354
- background: #1e293b;
355
- border-left: thick #f59e0b;
356
- }
357
-
358
- .theme-ocean #command-hints {
359
- background: #1e293b;
360
- }
361
-
362
- .theme-ocean .command-hint {
363
- color: #94a3b8;
364
- }
365
-
366
- .theme-ocean .command-key {
367
- color: #3b82f6;
368
- }
369
-
370
- .theme-ocean MarkdownH1 {
371
- color: #3b82f6;
372
- }
373
-
374
- .theme-ocean MarkdownH2 {
375
- color: #06b6d4;
376
- }
377
-
378
- .theme-ocean MarkdownH3 {
379
- color: #10b981;
380
- }
381
-
382
- .theme-ocean MarkdownFence {
383
- background: #1e293b;
384
- border: round #94a3b8;
385
- }
386
-
387
- .theme-ocean MarkdownBlockQuote {
388
- background: #3b82f6 10%;
389
- border-left: thick #3b82f6;
390
- }
391
-
392
- .theme-ocean MarkdownTH {
393
- background: #3b82f6 20%;
394
- }
395
-
396
- .theme-ocean Scrollbar {
397
- background: #1e293b;
398
- }
399
-
400
- .theme-ocean ScrollbarSlider {
401
- color: #3b82f6;
402
- }
403
-
404
- .theme-ocean ScrollbarSlider:hover {
405
- color: #06b6d4;
406
- }
407
-
408
- /* Forest Theme */
409
- .theme-forest Screen {
410
- background: #0d1f0d;
411
- }
412
-
413
- .theme-forest Header {
414
- background: #16a34a;
415
- color: #dcfce7;
416
- }
417
-
418
- .theme-forest Footer {
419
- background: #0f2f0f;
420
- color: #6ee7b7;
421
- }
422
-
423
- .theme-forest FooterKey > .footer-key--key {
424
- background: #16a34a;
425
- color: #dcfce7;
426
- }
427
-
428
- .theme-forest #sidebar {
429
- background: #0f2f0f;
430
- border-right: thick #16a34a;
431
- }
432
-
433
- .theme-forest #sidebar-title {
434
- background: #16a34a 20%;
435
- color: #dcfce7;
436
- }
437
-
438
- .theme-forest DirectoryTree > .directory-tree--folder {
439
- color: #0891b2;
440
- }
441
-
442
- .theme-forest DirectoryTree > .directory-tree--file {
443
- color: #dcfce7;
444
- }
445
-
446
- .theme-forest DirectoryTree > .directory-tree--extension {
447
- color: #6ee7b7;
448
- }
449
-
450
- .theme-forest DirectoryTree:focus > .directory-tree--cursor {
451
- background: #16a34a 40%;
452
- }
453
-
454
- .theme-forest #conversation {
455
- background: #0d1f0d;
456
- border-bottom: thick #0f2f0f;
457
- }
458
-
459
- .theme-forest #input-container {
460
- background: #0f2f0f;
461
- border-top: thick #16a34a;
462
- }
463
-
464
- .theme-forest #prompt-input {
465
- background: #0d1f0d;
466
- border: round #16a34a;
467
- }
468
-
469
- .theme-forest #prompt-input:focus {
470
- border: round #0891b2;
471
- }
472
-
473
- .theme-forest Input.-valid {
474
- border: round #22c55e;
475
- }
476
-
477
- .theme-forest Input > .input--placeholder {
478
- color: #6ee7b7;
479
- text-style: italic;
480
- }
481
-
482
- .theme-forest LoadingSpinner {
483
- background: #0f2f0f;
484
- border-left: thick #eab308;
485
- }
486
-
487
- .theme-forest #command-hints {
488
- background: #0f2f0f;
489
- }
490
-
491
- .theme-forest .command-hint {
492
- color: #6ee7b7;
493
- }
494
-
495
- .theme-forest .command-key {
496
- color: #16a34a;
497
- }
498
-
499
- .theme-forest MarkdownH1 {
500
- color: #16a34a;
501
- }
502
-
503
- .theme-forest MarkdownH2 {
504
- color: #0891b2;
505
- }
506
-
507
- .theme-forest MarkdownH3 {
508
- color: #22c55e;
509
- }
510
-
511
- .theme-forest MarkdownFence {
512
- background: #0f2f0f;
513
- border: round #6ee7b7;
514
- }
515
-
516
- .theme-forest MarkdownBlockQuote {
517
- background: #16a34a 10%;
518
- border-left: thick #16a34a;
519
- }
520
-
521
- .theme-forest MarkdownTH {
522
- background: #16a34a 20%;
523
- }
524
-
525
- .theme-forest Scrollbar {
526
- background: #0f2f0f;
527
- }
528
-
529
- .theme-forest ScrollbarSlider {
530
- color: #16a34a;
531
- }
532
-
533
- .theme-forest ScrollbarSlider:hover {
534
- color: #0891b2;
535
- }
536
-
537
- /* Sunset Theme */
538
- .theme-sunset Screen {
539
- background: #1f1315;
540
- }
541
-
542
- .theme-sunset Header {
543
- background: #dc2626;
544
- color: #fef2f2;
545
- }
546
-
547
- .theme-sunset Footer {
548
- background: #2f171a;
549
- color: #fecaca;
550
- }
551
-
552
- .theme-sunset FooterKey > .footer-key--key {
553
- background: #dc2626;
554
- color: #fef2f2;
555
- }
556
-
557
- .theme-sunset #sidebar {
558
- background: #2f171a;
559
- border-right: thick #dc2626;
560
- }
561
-
562
- .theme-sunset #sidebar-title {
563
- background: #dc2626 20%;
564
- color: #fef2f2;
565
- }
566
-
567
- .theme-sunset DirectoryTree > .directory-tree--folder {
568
- color: #ea580c;
569
- }
570
-
571
- .theme-sunset DirectoryTree > .directory-tree--file {
572
- color: #fef2f2;
573
- }
574
-
575
- .theme-sunset DirectoryTree > .directory-tree--extension {
576
- color: #fecaca;
577
- }
578
-
579
- .theme-sunset DirectoryTree:focus > .directory-tree--cursor {
580
- background: #dc2626 40%;
581
- }
582
-
583
- .theme-sunset #conversation {
584
- background: #1f1315;
585
- border-bottom: thick #2f171a;
586
- }
587
-
588
- .theme-sunset #input-container {
589
- background: #2f171a;
590
- border-top: thick #dc2626;
591
- }
592
-
593
- .theme-sunset #prompt-input {
594
- background: #1f1315;
595
- border: round #dc2626;
596
- }
597
-
598
- .theme-sunset #prompt-input:focus {
599
- border: round #ea580c;
600
- }
601
-
602
- .theme-sunset Input.-valid {
603
- border: round #16a34a;
604
- }
605
-
606
- .theme-sunset Input > .input--placeholder {
607
- color: #fecaca;
608
- text-style: italic;
609
- }
610
-
611
- .theme-sunset LoadingSpinner {
612
- background: #2f171a;
613
- border-left: thick #eab308;
614
- }
615
-
616
- .theme-sunset #command-hints {
617
- background: #2f171a;
618
- }
619
-
620
- .theme-sunset .command-hint {
621
- color: #fecaca;
622
- }
623
-
624
- .theme-sunset .command-key {
625
- color: #dc2626;
626
- }
627
-
628
- .theme-sunset MarkdownH1 {
629
- color: #dc2626;
630
- }
631
-
632
- .theme-sunset MarkdownH2 {
633
- color: #ea580c;
634
- }
635
-
636
- .theme-sunset MarkdownH3 {
637
- color: #16a34a;
638
- }
639
-
640
- .theme-sunset MarkdownFence {
641
- background: #2f171a;
642
- border: round #fecaca;
643
- }
644
-
645
- .theme-sunset MarkdownBlockQuote {
646
- background: #dc2626 10%;
647
- border-left: thick #dc2626;
648
- }
297
+ /* ===== Scrollbars ===== */
649
298
 
650
- .theme-sunset MarkdownTH {
651
- background: #dc2626 20%;
299
+ Scrollbar {
300
+ background: $surface;
652
301
  }
653
302
 
654
- .theme-sunset Scrollbar {
655
- background: #2f171a;
303
+ ScrollbarSlider {
304
+ color: $primary;
656
305
  }
657
306
 
658
- .theme-sunset ScrollbarSlider {
659
- color: #dc2626;
307
+ ScrollbarSlider:hover {
308
+ color: $secondary;
660
309
  }
661
-
662
- .theme-sunset ScrollbarSlider:hover {
663
- color: #ea580c;
664
- }
cli/utils.py CHANGED
@@ -2,18 +2,14 @@
2
2
 
3
3
  from kader.providers import OllamaProvider
4
4
 
5
- # Theme names for cycling
6
- THEME_NAMES = ["dark", "ocean", "forest", "sunset"]
7
-
8
5
  # Default model
9
- DEFAULT_MODEL = "qwen3-coder:480b-cloud"
6
+ DEFAULT_MODEL = "kimi-k2.5:cloud"
10
7
 
11
8
  HELP_TEXT = """## Kader CLI Commands
12
9
 
13
10
  | Command | Description |
14
11
  |---------|-------------|
15
12
  | `/models` | Show available LLM models |
16
- | `/theme` | Cycle through color themes |
17
13
  | `/help` | Show this help message |
18
14
  | `/clear` | Clear the conversation |
19
15
  | `/save` | Save current session |
@@ -28,7 +24,6 @@ HELP_TEXT = """## Kader CLI Commands
28
24
  | Shortcut | Action |
29
25
  |----------|--------|
30
26
  | `Ctrl+L` | Clear conversation |
31
- | `Ctrl+T` | Cycle theme |
32
27
  | `Ctrl+S` | Save session |
33
28
  | `Ctrl+R` | Refresh file tree |
34
29
  | `Ctrl+Q` | Quit |
@@ -1,23 +1,43 @@
1
1
  """Conversation display widget for Kader CLI."""
2
2
 
3
3
  from textual.app import ComposeResult
4
- from textual.containers import VerticalScroll
4
+ from textual.containers import Horizontal, VerticalScroll
5
5
  from textual.widgets import Markdown, Static
6
6
 
7
7
 
8
8
  class Message(Static):
9
9
  """A single message in the conversation."""
10
10
 
11
- def __init__(self, content: str, role: str = "user") -> None:
11
+ def __init__(
12
+ self,
13
+ content: str,
14
+ role: str = "user",
15
+ model_name: str | None = None,
16
+ usage_cost: float | None = None,
17
+ ) -> None:
12
18
  super().__init__()
13
19
  self.content = content
14
20
  self.role = role
21
+ self.model_name = model_name
22
+ self.usage_cost = usage_cost
15
23
  self.add_class(f"message-{role}")
16
24
 
17
25
  def compose(self) -> ComposeResult:
18
26
  prefix = "(**) **You:**" if self.role == "user" else "(^^) **Kader:**"
19
27
  yield Markdown(f"{prefix}\n\n{self.content}")
20
28
 
29
+ if self.role == "assistant" and (
30
+ self.model_name or self.usage_cost is not None
31
+ ):
32
+ with Horizontal(classes="message-footer"):
33
+ model_label = f"[*] {self.model_name}" if self.model_name else ""
34
+ yield Static(model_label, classes="footer-left")
35
+
36
+ usage_label = (
37
+ f"($) {self.usage_cost:.6f}" if self.usage_cost is not None else ""
38
+ )
39
+ yield Static(usage_label, classes="footer-right")
40
+
21
41
 
22
42
  class ConversationView(VerticalScroll):
23
43
  """Scrollable conversation history with markdown rendering."""
@@ -41,11 +61,37 @@ class ConversationView(VerticalScroll):
41
61
  background: $surface-darken-1;
42
62
  border-left: thick $success;
43
63
  }
64
+
65
+ .message-footer {
66
+ height: auto;
67
+ margin-top: 0;
68
+ padding: 0 1;
69
+ border-top: none;
70
+ }
71
+
72
+ .footer-left {
73
+ color: $secondary;
74
+ text-style: italic;
75
+ width: 1fr;
76
+ }
77
+
78
+ .footer-right {
79
+ color: $success;
80
+ text-style: bold;
81
+ text-align: right;
82
+ width: auto;
83
+ }
44
84
  """
45
85
 
46
- def add_message(self, content: str, role: str = "user") -> None:
86
+ def add_message(
87
+ self,
88
+ content: str,
89
+ role: str = "user",
90
+ model_name: str | None = None,
91
+ usage_cost: float | None = None,
92
+ ) -> None:
47
93
  """Add a message to the conversation."""
48
- message = Message(content, role)
94
+ message = Message(content, role, model_name, usage_cost)
49
95
  self.mount(message)
50
96
  self.scroll_end(animate=True)
51
97
 
kader/__init__.py CHANGED
@@ -8,6 +8,7 @@ creating the .kader directory in the user's home directory.
8
8
  from .config import ENV_FILE_PATH, KADER_DIR, initialize_kader_config
9
9
  from .providers import * # noqa: F401, F403
10
10
  from .tools import * # noqa: F401, F403
11
+ from .utils import Checkpointer
11
12
 
12
13
  # Initialize the configuration when the module is imported
13
14
  initialize_kader_config()
@@ -18,5 +19,6 @@ __all__ = [
18
19
  "KADER_DIR",
19
20
  "ENV_FILE_PATH",
20
21
  "initialize_kader_config",
22
+ "Checkpointer",
21
23
  # Export everything from providers and tools
22
24
  ]
kader/agent/agents.py CHANGED
@@ -31,6 +31,8 @@ class ReActAgent(BaseAgent):
31
31
  use_persistence: bool = False,
32
32
  interrupt_before_tool: bool = True,
33
33
  tool_confirmation_callback: Optional[callable] = None,
34
+ direct_execution_callback: Optional[callable] = None,
35
+ tool_execution_result_callback: Optional[callable] = None,
34
36
  ) -> None:
35
37
  # Resolve tools for prompt context if necessary
36
38
  # The base agent handles tool registration, but for the prompt template
@@ -67,6 +69,8 @@ class ReActAgent(BaseAgent):
67
69
  use_persistence=use_persistence,
68
70
  interrupt_before_tool=interrupt_before_tool,
69
71
  tool_confirmation_callback=tool_confirmation_callback,
72
+ direct_execution_callback=direct_execution_callback,
73
+ tool_execution_result_callback=tool_execution_result_callback,
70
74
  )
71
75
 
72
76
 
@@ -90,6 +94,8 @@ class PlanningAgent(BaseAgent):
90
94
  use_persistence: bool = False,
91
95
  interrupt_before_tool: bool = True,
92
96
  tool_confirmation_callback: Optional[callable] = None,
97
+ direct_execution_callback: Optional[callable] = None,
98
+ tool_execution_result_callback: Optional[callable] = None,
93
99
  ) -> None:
94
100
  # Ensure TodoTool is available
95
101
  _todo_tool = TodoTool()
@@ -123,4 +129,6 @@ class PlanningAgent(BaseAgent):
123
129
  use_persistence=use_persistence,
124
130
  interrupt_before_tool=interrupt_before_tool,
125
131
  tool_confirmation_callback=tool_confirmation_callback,
132
+ direct_execution_callback=direct_execution_callback,
133
+ tool_execution_result_callback=tool_execution_result_callback,
126
134
  )