lite-agent 0.13.0__py3-none-any.whl → 0.14.1__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.
Potentially problematic release.
This version of lite-agent might be problematic. Click here for more details.
- lite_agent/__init__.py +2 -2
- lite_agent/chat_display.py +210 -35
- lite_agent/context.py +8 -2
- lite_agent/runner.py +22 -16
- {lite_agent-0.13.0.dist-info → lite_agent-0.14.1.dist-info}/METADATA +1 -1
- {lite_agent-0.13.0.dist-info → lite_agent-0.14.1.dist-info}/RECORD +7 -7
- {lite_agent-0.13.0.dist-info → lite_agent-0.14.1.dist-info}/WHEEL +0 -0
lite_agent/__init__.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Lite Agent - A lightweight AI agent framework."""
|
|
2
2
|
|
|
3
3
|
from .agent import Agent
|
|
4
|
-
from .chat_display import display_chat_summary, display_messages
|
|
4
|
+
from .chat_display import chat_summary_to_string, display_chat_summary, display_messages, messages_to_string
|
|
5
5
|
from .message_transfers import consolidate_history_transfer
|
|
6
6
|
from .runner import Runner
|
|
7
7
|
|
|
8
|
-
__all__ = ["Agent", "Runner", "consolidate_history_transfer", "display_chat_summary", "display_messages"]
|
|
8
|
+
__all__ = ["Agent", "Runner", "chat_summary_to_string", "consolidate_history_transfer", "display_chat_summary", "display_messages", "messages_to_string"]
|
lite_agent/chat_display.py
CHANGED
|
@@ -10,6 +10,7 @@ import json
|
|
|
10
10
|
import time
|
|
11
11
|
from collections.abc import Callable
|
|
12
12
|
from datetime import datetime, timedelta, timezone
|
|
13
|
+
from io import StringIO
|
|
13
14
|
|
|
14
15
|
try:
|
|
15
16
|
from zoneinfo import ZoneInfo
|
|
@@ -46,6 +47,7 @@ class DisplayConfig:
|
|
|
46
47
|
console: Console | None = None
|
|
47
48
|
show_indices: bool = True
|
|
48
49
|
show_timestamps: bool = True
|
|
50
|
+
show_metadata: bool = True
|
|
49
51
|
max_content_length: int = 1000
|
|
50
52
|
local_timezone: timezone | str | None = None
|
|
51
53
|
|
|
@@ -57,6 +59,7 @@ class MessageContext:
|
|
|
57
59
|
console: Console
|
|
58
60
|
index_str: str
|
|
59
61
|
timestamp_str: str
|
|
62
|
+
show_metadata: bool
|
|
60
63
|
max_content_length: int
|
|
61
64
|
truncate_content: Callable[[str, int], str]
|
|
62
65
|
|
|
@@ -441,6 +444,7 @@ def display_messages(
|
|
|
441
444
|
console=console,
|
|
442
445
|
max_content_length=config.max_content_length,
|
|
443
446
|
show_timestamp=config.show_timestamps,
|
|
447
|
+
show_metadata=config.show_metadata,
|
|
444
448
|
local_timezone=local_timezone,
|
|
445
449
|
)
|
|
446
450
|
|
|
@@ -452,6 +456,7 @@ def _display_single_message_compact(
|
|
|
452
456
|
console: Console,
|
|
453
457
|
max_content_length: int = 100,
|
|
454
458
|
show_timestamp: bool = False,
|
|
459
|
+
show_metadata: bool = True,
|
|
455
460
|
local_timezone: timezone | None = None,
|
|
456
461
|
) -> None:
|
|
457
462
|
"""以列式格式打印单个消息,类似 rich log。"""
|
|
@@ -469,7 +474,7 @@ def _display_single_message_compact(
|
|
|
469
474
|
timestamp = _format_timestamp(message_time, local_timezone=local_timezone)
|
|
470
475
|
|
|
471
476
|
# 创建列式显示
|
|
472
|
-
_display_message_in_columns(message, console, index, timestamp, max_content_length, truncate_content)
|
|
477
|
+
_display_message_in_columns(message, console, index, timestamp, show_metadata=show_metadata, max_content_length=max_content_length, truncate_content=truncate_content)
|
|
473
478
|
|
|
474
479
|
|
|
475
480
|
def _display_message_in_columns(
|
|
@@ -477,6 +482,8 @@ def _display_message_in_columns(
|
|
|
477
482
|
console: Console,
|
|
478
483
|
index: int | None,
|
|
479
484
|
timestamp: str | None,
|
|
485
|
+
*,
|
|
486
|
+
show_metadata: bool,
|
|
480
487
|
max_content_length: int,
|
|
481
488
|
truncate_content: Callable[[str, int], str],
|
|
482
489
|
) -> None:
|
|
@@ -490,12 +497,9 @@ def _display_message_in_columns(
|
|
|
490
497
|
if isinstance(message, NewUserMessage):
|
|
491
498
|
_display_user_message_with_columns(message, console, time_str, index_str, max_content_length, truncate_content)
|
|
492
499
|
elif isinstance(message, NewAssistantMessage):
|
|
493
|
-
_display_assistant_message_with_columns(message, console, time_str, index_str, max_content_length, truncate_content)
|
|
500
|
+
_display_assistant_message_with_columns(message, console, time_str, index_str, show_metadata=show_metadata, max_content_length=max_content_length, truncate_content=truncate_content)
|
|
494
501
|
elif isinstance(message, NewSystemMessage):
|
|
495
502
|
_display_system_message_with_columns(message, console, time_str, index_str, max_content_length, truncate_content)
|
|
496
|
-
else:
|
|
497
|
-
# 处理旧格式消息
|
|
498
|
-
_display_legacy_message_with_columns(message, console, time_str, index_str, max_content_length, truncate_content)
|
|
499
503
|
|
|
500
504
|
|
|
501
505
|
def _display_user_message_with_columns(
|
|
@@ -523,27 +527,48 @@ def _display_user_message_with_columns(
|
|
|
523
527
|
content = " ".join(content_parts)
|
|
524
528
|
content = truncate_content(content, max_content_length)
|
|
525
529
|
|
|
526
|
-
#
|
|
530
|
+
# 创建表格来确保对齐,根据配置动态调整列宽
|
|
527
531
|
table = Table.grid(padding=0)
|
|
528
|
-
|
|
529
|
-
|
|
532
|
+
|
|
533
|
+
# 只有在显示时间戳时才添加时间列
|
|
534
|
+
time_width = 8 if time_str.strip() else 0
|
|
535
|
+
if time_width > 0:
|
|
536
|
+
table.add_column(width=time_width, justify="left") # 时间列
|
|
537
|
+
|
|
538
|
+
# 只有在显示序号时才添加序号列
|
|
539
|
+
index_width = 4 if index_str.strip() else 0
|
|
540
|
+
if index_width > 0:
|
|
541
|
+
table.add_column(width=index_width, justify="left") # 序号列
|
|
542
|
+
|
|
530
543
|
table.add_column(min_width=0) # 内容列
|
|
531
544
|
|
|
545
|
+
# 辅助函数:根据列数构建行
|
|
546
|
+
def build_table_row(*content_parts: str) -> tuple[str, ...]:
|
|
547
|
+
row_parts = []
|
|
548
|
+
if time_width > 0:
|
|
549
|
+
row_parts.append(content_parts[0] if len(content_parts) > 0 else "")
|
|
550
|
+
if index_width > 0:
|
|
551
|
+
row_parts.append(content_parts[1] if len(content_parts) > 1 else "")
|
|
552
|
+
row_parts.append(content_parts[-1] if content_parts else "") # 内容列总是最后一个
|
|
553
|
+
return tuple(row_parts)
|
|
554
|
+
|
|
532
555
|
lines = content.split("\n")
|
|
533
556
|
for i, line in enumerate(lines):
|
|
534
557
|
if i == 0:
|
|
535
558
|
# 第一行显示 User: 标签
|
|
536
559
|
table.add_row(
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
560
|
+
*build_table_row(
|
|
561
|
+
f"[dim]{time_str:8}[/dim]",
|
|
562
|
+
f"[dim]{index_str:4}[/dim]",
|
|
563
|
+
"[blue]User:[/blue]",
|
|
564
|
+
),
|
|
540
565
|
)
|
|
541
566
|
# 如果有内容,添加内容行
|
|
542
567
|
if line:
|
|
543
|
-
table.add_row("", "", line)
|
|
568
|
+
table.add_row(*build_table_row("", "", line))
|
|
544
569
|
else:
|
|
545
570
|
# 续行只在内容列显示
|
|
546
|
-
table.add_row("", "", line)
|
|
571
|
+
table.add_row(*build_table_row("", "", line))
|
|
547
572
|
|
|
548
573
|
console.print(table)
|
|
549
574
|
|
|
@@ -559,24 +584,45 @@ def _display_system_message_with_columns(
|
|
|
559
584
|
"""使用列布局显示系统消息。"""
|
|
560
585
|
content = truncate_content(message.content, max_content_length)
|
|
561
586
|
|
|
562
|
-
#
|
|
587
|
+
# 创建表格来确保对齐,根据配置动态调整列宽
|
|
563
588
|
table = Table.grid(padding=0)
|
|
564
|
-
|
|
565
|
-
|
|
589
|
+
|
|
590
|
+
# 只有在显示时间戳时才添加时间列
|
|
591
|
+
time_width = 8 if time_str.strip() else 0
|
|
592
|
+
if time_width > 0:
|
|
593
|
+
table.add_column(width=time_width, justify="left") # 时间列
|
|
594
|
+
|
|
595
|
+
# 只有在显示序号时才添加序号列
|
|
596
|
+
index_width = 4 if index_str.strip() else 0
|
|
597
|
+
if index_width > 0:
|
|
598
|
+
table.add_column(width=index_width, justify="left") # 序号列
|
|
599
|
+
|
|
566
600
|
table.add_column(min_width=0) # 内容列
|
|
567
601
|
|
|
602
|
+
# 辅助函数:根据列数构建行
|
|
603
|
+
def build_table_row(*content_parts: str) -> tuple[str, ...]:
|
|
604
|
+
row_parts = []
|
|
605
|
+
if time_width > 0:
|
|
606
|
+
row_parts.append(content_parts[0] if len(content_parts) > 0 else "")
|
|
607
|
+
if index_width > 0:
|
|
608
|
+
row_parts.append(content_parts[1] if len(content_parts) > 1 else "")
|
|
609
|
+
row_parts.append(content_parts[-1] if content_parts else "") # 内容列总是最后一个
|
|
610
|
+
return tuple(row_parts)
|
|
611
|
+
|
|
568
612
|
lines = content.split("\n")
|
|
569
613
|
for i, line in enumerate(lines):
|
|
570
614
|
if i == 0:
|
|
571
615
|
# 第一行显示完整信息
|
|
572
616
|
table.add_row(
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
617
|
+
*build_table_row(
|
|
618
|
+
f"[dim]{time_str:8}[/dim]",
|
|
619
|
+
f"[dim]{index_str:4}[/dim]",
|
|
620
|
+
f"[yellow]System:[/yellow] {line}",
|
|
621
|
+
),
|
|
576
622
|
)
|
|
577
623
|
else:
|
|
578
624
|
# 续行只在内容列显示
|
|
579
|
-
table.add_row("", "", line)
|
|
625
|
+
table.add_row(*build_table_row("", "", line))
|
|
580
626
|
|
|
581
627
|
console.print(table)
|
|
582
628
|
|
|
@@ -586,6 +632,8 @@ def _display_assistant_message_with_columns(
|
|
|
586
632
|
console: Console,
|
|
587
633
|
time_str: str,
|
|
588
634
|
index_str: str,
|
|
635
|
+
*,
|
|
636
|
+
show_metadata: bool,
|
|
589
637
|
max_content_length: int,
|
|
590
638
|
truncate_content: Callable[[str, int], str],
|
|
591
639
|
) -> None:
|
|
@@ -605,7 +653,7 @@ def _display_assistant_message_with_columns(
|
|
|
605
653
|
|
|
606
654
|
# 构建元信息
|
|
607
655
|
meta_info = ""
|
|
608
|
-
if message.meta:
|
|
656
|
+
if show_metadata and message.meta:
|
|
609
657
|
meta_parts = []
|
|
610
658
|
if message.meta.model is not None:
|
|
611
659
|
meta_parts.append(f"Model:{message.meta.model}")
|
|
@@ -620,12 +668,31 @@ def _display_assistant_message_with_columns(
|
|
|
620
668
|
if meta_parts:
|
|
621
669
|
meta_info = f" [dim]({' | '.join(meta_parts)})[/dim]"
|
|
622
670
|
|
|
623
|
-
#
|
|
671
|
+
# 创建表格来确保对齐,根据配置动态调整列宽
|
|
624
672
|
table = Table.grid(padding=0)
|
|
625
|
-
|
|
626
|
-
|
|
673
|
+
|
|
674
|
+
# 只有在显示时间戳时才添加时间列
|
|
675
|
+
time_width = 8 if time_str.strip() else 0
|
|
676
|
+
if time_width > 0:
|
|
677
|
+
table.add_column(width=time_width, justify="left") # 时间列
|
|
678
|
+
|
|
679
|
+
# 只有在显示序号时才添加序号列
|
|
680
|
+
index_width = 4 if index_str.strip() else 0
|
|
681
|
+
if index_width > 0:
|
|
682
|
+
table.add_column(width=index_width, justify="left") # 序号列
|
|
683
|
+
|
|
627
684
|
table.add_column(min_width=0) # 内容列
|
|
628
685
|
|
|
686
|
+
# 辅助函数:根据列数构建行
|
|
687
|
+
def build_table_row(*content_parts: str) -> tuple[str, ...]:
|
|
688
|
+
row_parts = []
|
|
689
|
+
if time_width > 0:
|
|
690
|
+
row_parts.append(content_parts[0] if len(content_parts) > 0 else "")
|
|
691
|
+
if index_width > 0:
|
|
692
|
+
row_parts.append(content_parts[1] if len(content_parts) > 1 else "")
|
|
693
|
+
row_parts.append(content_parts[-1] if content_parts else "") # 内容列总是最后一个
|
|
694
|
+
return tuple(row_parts)
|
|
695
|
+
|
|
629
696
|
# 处理文本内容
|
|
630
697
|
first_row_added = False
|
|
631
698
|
if text_parts:
|
|
@@ -636,24 +703,28 @@ def _display_assistant_message_with_columns(
|
|
|
636
703
|
if i == 0:
|
|
637
704
|
# 第一行显示 Assistant: 标签
|
|
638
705
|
table.add_row(
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
706
|
+
*build_table_row(
|
|
707
|
+
f"[dim]{time_str:8}[/dim]",
|
|
708
|
+
f"[dim]{index_str:4}[/dim]",
|
|
709
|
+
f"[green]Assistant:[/green]{meta_info}",
|
|
710
|
+
),
|
|
642
711
|
)
|
|
643
712
|
# 如果有内容,添加内容行
|
|
644
713
|
if line:
|
|
645
|
-
table.add_row("", "", line)
|
|
714
|
+
table.add_row(*build_table_row("", "", line))
|
|
646
715
|
first_row_added = True
|
|
647
716
|
else:
|
|
648
717
|
# 续行只在内容列显示
|
|
649
|
-
table.add_row("", "", line)
|
|
718
|
+
table.add_row(*build_table_row("", "", line))
|
|
650
719
|
|
|
651
720
|
# 如果没有文本内容,只显示助手消息头
|
|
652
721
|
if not first_row_added:
|
|
653
722
|
table.add_row(
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
723
|
+
*build_table_row(
|
|
724
|
+
f"[dim]{time_str:8}[/dim]",
|
|
725
|
+
f"[dim]{index_str:4}[/dim]",
|
|
726
|
+
f"[green]Assistant:[/green]{meta_info}",
|
|
727
|
+
),
|
|
657
728
|
)
|
|
658
729
|
|
|
659
730
|
# 添加工具调用
|
|
@@ -667,7 +738,7 @@ def _display_assistant_message_with_columns(
|
|
|
667
738
|
args_str = f" {tool_call.arguments}"
|
|
668
739
|
|
|
669
740
|
args_display = truncate_content(args_str, max_content_length - len(tool_call.name) - 10)
|
|
670
|
-
table.add_row("", "", f"[magenta]Call:[/magenta] {tool_call.name}{args_display}")
|
|
741
|
+
table.add_row(*build_table_row("", "", f"[magenta]Call:[/magenta] {tool_call.name}{args_display}"))
|
|
671
742
|
|
|
672
743
|
# 添加工具结果
|
|
673
744
|
for tool_result in tool_results:
|
|
@@ -676,10 +747,10 @@ def _display_assistant_message_with_columns(
|
|
|
676
747
|
if tool_result.execution_time_ms is not None:
|
|
677
748
|
time_info = f" [dim]({tool_result.execution_time_ms}ms)[/dim]"
|
|
678
749
|
|
|
679
|
-
table.add_row("", "", f"[cyan]Output:[/cyan]{time_info}")
|
|
750
|
+
table.add_row(*build_table_row("", "", f"[cyan]Output:[/cyan]{time_info}"))
|
|
680
751
|
lines = output.split("\n")
|
|
681
752
|
for line in lines:
|
|
682
|
-
table.add_row("", "", line)
|
|
753
|
+
table.add_row(*build_table_row("", "", line))
|
|
683
754
|
|
|
684
755
|
console.print(table)
|
|
685
756
|
|
|
@@ -689,6 +760,8 @@ def _display_legacy_message_with_columns(
|
|
|
689
760
|
console: Console,
|
|
690
761
|
time_str: str,
|
|
691
762
|
index_str: str,
|
|
763
|
+
*,
|
|
764
|
+
show_metadata: bool, # noqa: ARG001
|
|
692
765
|
max_content_length: int,
|
|
693
766
|
truncate_content: Callable[[str, int], str],
|
|
694
767
|
) -> None:
|
|
@@ -735,6 +808,7 @@ def _create_message_context(context_config: dict[str, FlexibleRunnerMessage | Co
|
|
|
735
808
|
max_content_length = max_content_length_val
|
|
736
809
|
truncate_content = context_config["truncate_content"]
|
|
737
810
|
show_timestamp = context_config.get("show_timestamp", False)
|
|
811
|
+
show_metadata = bool(context_config.get("show_metadata", True))
|
|
738
812
|
local_timezone = context_config.get("local_timezone")
|
|
739
813
|
|
|
740
814
|
# 类型检查
|
|
@@ -773,6 +847,7 @@ def _create_message_context(context_config: dict[str, FlexibleRunnerMessage | Co
|
|
|
773
847
|
console=console,
|
|
774
848
|
index_str=index_str,
|
|
775
849
|
timestamp_str=timestamp_str,
|
|
850
|
+
show_metadata=show_metadata,
|
|
776
851
|
max_content_length=max_content_length,
|
|
777
852
|
truncate_content=truncate_content, # type: ignore[arg-type]
|
|
778
853
|
)
|
|
@@ -1048,3 +1123,103 @@ def _display_new_assistant_message_compact(message: NewAssistantMessage, context
|
|
|
1048
1123
|
# Always use indented format for better hierarchy
|
|
1049
1124
|
context.console.print(f" [cyan]Output:[/cyan]{time_info}")
|
|
1050
1125
|
context.console.print(f" {output}")
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
def messages_to_string(
|
|
1129
|
+
messages: RunnerMessages,
|
|
1130
|
+
*,
|
|
1131
|
+
show_indices: bool = False,
|
|
1132
|
+
show_timestamps: bool = False,
|
|
1133
|
+
show_metadata: bool = False,
|
|
1134
|
+
max_content_length: int = 1000,
|
|
1135
|
+
local_timezone: timezone | str | None = None,
|
|
1136
|
+
) -> str:
|
|
1137
|
+
"""
|
|
1138
|
+
将消息列表转换为纯文本字符串,默认简洁格式(不显示时间、序号、元数据)。
|
|
1139
|
+
|
|
1140
|
+
Args:
|
|
1141
|
+
messages: 要转换的消息列表
|
|
1142
|
+
show_indices: 是否显示消息序号(默认False)
|
|
1143
|
+
show_timestamps: 是否显示时间戳(默认False)
|
|
1144
|
+
show_metadata: 是否显示元数据(如模型、延迟、token使用等,默认False)
|
|
1145
|
+
max_content_length: 内容最大长度限制(默认1000)
|
|
1146
|
+
local_timezone: 本地时区设置(可选)
|
|
1147
|
+
|
|
1148
|
+
Returns:
|
|
1149
|
+
包含所有消息的纯文本字符串
|
|
1150
|
+
"""
|
|
1151
|
+
# 创建一个没有颜色的 Console 来捕获输出
|
|
1152
|
+
string_buffer = StringIO()
|
|
1153
|
+
plain_console = Console(file=string_buffer, force_terminal=False, no_color=True, width=120)
|
|
1154
|
+
|
|
1155
|
+
# 使用配置
|
|
1156
|
+
config = DisplayConfig(
|
|
1157
|
+
console=plain_console,
|
|
1158
|
+
show_indices=show_indices,
|
|
1159
|
+
show_timestamps=show_timestamps,
|
|
1160
|
+
show_metadata=show_metadata,
|
|
1161
|
+
max_content_length=max_content_length,
|
|
1162
|
+
local_timezone=local_timezone,
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1165
|
+
# 调用现有的 display_messages 函数,但输出到字符串缓冲区
|
|
1166
|
+
display_messages(messages, config=config)
|
|
1167
|
+
|
|
1168
|
+
# 获取结果并清理尾随空格
|
|
1169
|
+
result = string_buffer.getvalue()
|
|
1170
|
+
string_buffer.close()
|
|
1171
|
+
|
|
1172
|
+
# 清理每行的尾随空格
|
|
1173
|
+
lines = result.split("\n")
|
|
1174
|
+
cleaned_lines = [line.rstrip() for line in lines]
|
|
1175
|
+
return "\n".join(cleaned_lines)
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
def chat_summary_to_string(messages: RunnerMessages, *, include_performance: bool = False) -> str:
|
|
1179
|
+
"""
|
|
1180
|
+
将聊天摘要转换为纯文本字符串,默认只显示基本统计信息。
|
|
1181
|
+
|
|
1182
|
+
Args:
|
|
1183
|
+
messages: 要分析的消息列表
|
|
1184
|
+
include_performance: 是否包含性能统计信息(默认False)
|
|
1185
|
+
|
|
1186
|
+
Returns:
|
|
1187
|
+
包含聊天摘要的纯文本字符串
|
|
1188
|
+
"""
|
|
1189
|
+
string_buffer = StringIO()
|
|
1190
|
+
plain_console = Console(file=string_buffer, force_terminal=False, no_color=True, width=120)
|
|
1191
|
+
|
|
1192
|
+
if include_performance:
|
|
1193
|
+
# 调用现有的 display_chat_summary 函数,包含所有信息
|
|
1194
|
+
display_chat_summary(messages, console=plain_console)
|
|
1195
|
+
else:
|
|
1196
|
+
# 只显示基本的消息统计信息
|
|
1197
|
+
_display_basic_message_stats(messages, plain_console)
|
|
1198
|
+
|
|
1199
|
+
# 获取结果并清理
|
|
1200
|
+
result = string_buffer.getvalue()
|
|
1201
|
+
string_buffer.close()
|
|
1202
|
+
|
|
1203
|
+
return result
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
def _display_basic_message_stats(messages: RunnerMessages, console: Console) -> None:
|
|
1207
|
+
"""显示基本的消息统计信息,不包含性能数据。"""
|
|
1208
|
+
message_counts, _ = _analyze_messages(messages)
|
|
1209
|
+
|
|
1210
|
+
# 创建简化的统计表格
|
|
1211
|
+
table = Table(title="Message Summary", show_header=True, header_style="bold blue")
|
|
1212
|
+
table.add_column("Message Type", justify="left")
|
|
1213
|
+
table.add_column("Count", justify="right")
|
|
1214
|
+
|
|
1215
|
+
# 添加消息类型统计
|
|
1216
|
+
for msg_type, count in message_counts.items():
|
|
1217
|
+
if msg_type != "Total": # 跳过总计,单独处理
|
|
1218
|
+
table.add_row(msg_type, str(count))
|
|
1219
|
+
|
|
1220
|
+
# 添加总计行
|
|
1221
|
+
if "Total" in message_counts:
|
|
1222
|
+
table.add_row("", "") # 空行分隔
|
|
1223
|
+
table.add_row("[bold]Total[/bold]", f"[bold]{message_counts['Total']}[/bold]")
|
|
1224
|
+
|
|
1225
|
+
console.print(table)
|
lite_agent/context.py
CHANGED
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Any, Generic, TypeVar
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from lite_agent.types import NewMessage
|
|
11
|
+
else:
|
|
12
|
+
# Runtime: use Any to avoid circular import issues
|
|
13
|
+
NewMessage = Any
|
|
14
|
+
|
|
9
15
|
T = TypeVar("T")
|
|
10
16
|
|
|
11
17
|
|
|
@@ -33,5 +39,5 @@ class HistoryContext(BaseModel, Generic[T]):
|
|
|
33
39
|
... return f"用户 {user_id} 有 {len(messages)} 条消息"
|
|
34
40
|
"""
|
|
35
41
|
|
|
36
|
-
history_messages: list[
|
|
42
|
+
history_messages: list[NewMessage]
|
|
37
43
|
data: T | None = None
|
lite_agent/runner.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import inspect
|
|
2
|
+
import json
|
|
3
3
|
from collections.abc import AsyncGenerator, Sequence
|
|
4
4
|
from datetime import datetime, timedelta, timezone
|
|
5
5
|
from os import PathLike
|
|
@@ -115,54 +115,54 @@ class Runner:
|
|
|
115
115
|
|
|
116
116
|
def _tool_expects_history_context(self, tool_calls: Sequence["ToolCall"]) -> bool:
|
|
117
117
|
"""Check if any of the tool calls expect HistoryContext in their signatures.
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
Returns True if any tool function has a Context[HistoryContext[...]] parameter,
|
|
120
120
|
False if they expect Context[...] without HistoryContext wrapper.
|
|
121
121
|
"""
|
|
122
122
|
if not tool_calls:
|
|
123
123
|
return False
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
for tool_call in tool_calls:
|
|
126
126
|
tool_func = self.agent.fc.function_registry.get(tool_call.function.name)
|
|
127
127
|
if not tool_func:
|
|
128
128
|
continue
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
# Get function signature
|
|
131
131
|
sig = inspect.signature(tool_func)
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
# Check each parameter for Context annotation
|
|
134
134
|
for param in sig.parameters.values():
|
|
135
135
|
if param.annotation == inspect.Parameter.empty:
|
|
136
136
|
continue
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
# Check if parameter is Context[...]
|
|
139
139
|
origin = get_origin(param.annotation)
|
|
140
140
|
if origin is not None:
|
|
141
141
|
# Check if it's Context type (compare by string name to handle import differences)
|
|
142
|
-
origin_name = getattr(origin,
|
|
143
|
-
if
|
|
142
|
+
origin_name = getattr(origin, "__name__", str(origin))
|
|
143
|
+
if "Context" in origin_name:
|
|
144
144
|
args = get_args(param.annotation)
|
|
145
145
|
if args:
|
|
146
146
|
# Check if the Context contains HistoryContext
|
|
147
147
|
inner_type = args[0]
|
|
148
148
|
inner_origin = get_origin(inner_type)
|
|
149
149
|
if inner_origin is not None:
|
|
150
|
-
inner_name = getattr(inner_origin,
|
|
151
|
-
if
|
|
150
|
+
inner_name = getattr(inner_origin, "__name__", str(inner_origin))
|
|
151
|
+
if "HistoryContext" in inner_name:
|
|
152
152
|
logger.debug(f"Tool {tool_call.function.name} expects HistoryContext")
|
|
153
153
|
return True
|
|
154
154
|
# Also check for direct HistoryContext class
|
|
155
|
-
elif hasattr(inner_type,
|
|
155
|
+
elif hasattr(inner_type, "__name__") and "HistoryContext" in inner_type.__name__:
|
|
156
156
|
logger.debug(f"Tool {tool_call.function.name} expects HistoryContext")
|
|
157
157
|
return True
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
# Also handle direct annotation checking
|
|
160
|
-
if hasattr(param.annotation,
|
|
160
|
+
if hasattr(param.annotation, "__name__"):
|
|
161
161
|
annotation_str = str(param.annotation)
|
|
162
|
-
if
|
|
162
|
+
if "HistoryContext" in annotation_str:
|
|
163
163
|
logger.debug(f"Tool {tool_call.function.name} expects HistoryContext (direct)")
|
|
164
164
|
return True
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
logger.debug("No tools expect HistoryContext")
|
|
167
167
|
return False
|
|
168
168
|
|
|
@@ -240,7 +240,7 @@ class Runner:
|
|
|
240
240
|
|
|
241
241
|
# Check if tools expect HistoryContext wrapper
|
|
242
242
|
expects_history = self._tool_expects_history_context(tool_calls)
|
|
243
|
-
|
|
243
|
+
|
|
244
244
|
if expects_history:
|
|
245
245
|
# Auto-inject history messages into context for tools that expect HistoryContext
|
|
246
246
|
if context is not None and not isinstance(context, Context):
|
|
@@ -353,6 +353,12 @@ class Runner:
|
|
|
353
353
|
# Determine completion condition based on agent configuration
|
|
354
354
|
completion_condition = getattr(self.agent, "completion_condition", CompletionMode.STOP)
|
|
355
355
|
|
|
356
|
+
# If termination_tools are set but completion_condition is still STOP,
|
|
357
|
+
# automatically switch to CALL mode to enable custom termination
|
|
358
|
+
if completion_condition == CompletionMode.STOP and hasattr(self.agent, "termination_tools") and self.agent.termination_tools:
|
|
359
|
+
completion_condition = CompletionMode.CALL
|
|
360
|
+
logger.debug(f"Auto-switching to CALL mode due to termination_tools: {self.agent.termination_tools}")
|
|
361
|
+
|
|
356
362
|
def is_finish() -> bool:
|
|
357
363
|
if completion_condition == CompletionMode.CALL:
|
|
358
364
|
# Check if any termination tool was called in the last assistant message
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
lite_agent/__init__.py,sha256=
|
|
1
|
+
lite_agent/__init__.py,sha256=uN7fMz4MIO8sc8mMV-MLFZNXsW3PgzzWJVHZSlJ2jT4,430
|
|
2
2
|
lite_agent/agent.py,sha256=gMn-NeQxl0298uKmlJph_c9WP3QN8WeKMEEDuv5RdVw,18681
|
|
3
|
-
lite_agent/chat_display.py,sha256=
|
|
3
|
+
lite_agent/chat_display.py,sha256=bSJvYmrhc6xdSTyF8Tg3wiydnl4aL9R30QfkijDx8O8,47390
|
|
4
4
|
lite_agent/client.py,sha256=-9BXLhAp3bGJsdKJ02lLpPJeHQKyHKQwhebZ6WCYh_k,9988
|
|
5
5
|
lite_agent/constants.py,sha256=_xIDdQwaJrWk8N_62o-KYEo3jj1waPJ0ZOd3hHybKNo,718
|
|
6
|
-
lite_agent/context.py,sha256=
|
|
6
|
+
lite_agent/context.py,sha256=Igh6SCtcynzg95jWRUp2ljQp_TmirRwTSRneMpNzzpo,1392
|
|
7
7
|
lite_agent/loggers.py,sha256=XkNkdqwD_nQGfhQJ-bBWT7koci_mMkNw3aBpyMhOICw,57
|
|
8
8
|
lite_agent/message_transfers.py,sha256=N9ViK7Gxqqa1sd3V_hkNuQ9fUipg7M95l-sVBBG2Id4,5357
|
|
9
9
|
lite_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
lite_agent/runner.py,sha256=
|
|
10
|
+
lite_agent/runner.py,sha256=XbQO5vD1LUfqq_XPNBOxZEYp3s_98sfDQXnnMWyFIsI,48908
|
|
11
11
|
lite_agent/processors/__init__.py,sha256=ybpAzpMBIE9v5I24wIBZRXeaOaPNTmoKH13aofgNI6Q,234
|
|
12
12
|
lite_agent/processors/completion_event_processor.py,sha256=zoWvs8dfrIkCSITGtS-4Hpve3WFCA0UUsMvYifL2fw0,13010
|
|
13
13
|
lite_agent/processors/response_event_processor.py,sha256=Jr3cj1ItJ8aq9UBhEEjDwWDnPNOZ2ZXjWJ3-g4ghkhM,8514
|
|
@@ -30,6 +30,6 @@ lite_agent/utils/message_builder.py,sha256=J-yycL9pXSO9MbgC5NEGqvoP1LC2Nxe9r2YRW
|
|
|
30
30
|
lite_agent/utils/message_converter.py,sha256=5HmNncTl71TD2M_6Ezz1Tnfavzna8DQYb4-D47Du7mA,9165
|
|
31
31
|
lite_agent/utils/message_state_manager.py,sha256=rFUyqyd_7NdJRtyqsAWGcfwrDIlD6gK2dBDSDx1eGBs,5766
|
|
32
32
|
lite_agent/utils/metrics.py,sha256=RzOEhCWxbLmmIEkzaxOJ6tAdthI8dv2Foc98Lq8afOQ,1915
|
|
33
|
-
lite_agent-0.
|
|
34
|
-
lite_agent-0.
|
|
35
|
-
lite_agent-0.
|
|
33
|
+
lite_agent-0.14.1.dist-info/METADATA,sha256=qrYXCtrk3T5Q6er8RVUmBe33RHFULkTKMj8dBndMDD8,3538
|
|
34
|
+
lite_agent-0.14.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
35
|
+
lite_agent-0.14.1.dist-info/RECORD,,
|
|
File without changes
|