-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmsg.py
More file actions
143 lines (131 loc) · 5.63 KB
/
Copy pathmsg.py
File metadata and controls
143 lines (131 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import re
import flet as ft
import asyncio
import time
from const import ClawConst
from agent import ReActAgent
def parse_text_with_links(text, page):
url_pattern = r'https?://[^\s]+'
spans = []
last_end = 0
for match in re.finditer(url_pattern, text):
start, end = match.span()
if start > last_end:
spans.append(ft.TextSpan(text[last_end:start]))
url = match.group()
def open_url(e, u=url):
asyncio.create_task(page.launch_url(u))
spans.append(ft.TextSpan(
text=url,
style=ft.TextStyle(color=ft.Colors.BLUE, decoration=ft.TextDecoration.UNDERLINE),
on_click=open_url
))
last_end = end
if last_end < len(text):
spans.append(ft.TextSpan(text[last_end:]))
return spans
def get_avatar(role):
if role == "assistant":
color = ft.Colors.BLUE
icon = ft.Icons.ANDROID
else:
color = ft.Colors.GREEN
icon = ft.Icons.PERSON
return ft.CircleAvatar(
bgcolor=color,
content=ft.Icon(icon, color=ft.Colors.WHITE, size=18),
radius=16,
)
def add_message(messages_control, role: str, content: str, page: ft.Page, member_info=None):
is_user = role == "user"
is_system = role == "system"
if re.search(r'https?://', content):
spans = parse_text_with_links(content, page)
text_widget = ft.Text(spans=spans, selectable=True, size=14)
else:
text_widget = ft.Text(content, selectable=True, size=14)
if is_system:
bubble = ft.Container(
content=text_widget,
bgcolor=ft.Colors.GREY_200,
border_radius=ft.BorderRadius.all(15),
padding=ft.Padding.symmetric(horizontal=15, vertical=8),
width=ClawConst.BUBBLE_STYLE['width'],
)
row = ft.Row([bubble], alignment=ft.MainAxisAlignment.CENTER)
else:
bubble = ft.Container(
content=text_widget,
bgcolor=ft.Colors.GREEN_100 if is_user else ft.Colors.WHITE,
**ClawConst.BUBBLE_STYLE,
)
if is_user:
avatar = get_avatar(role)
row = ft.Row([bubble, avatar], alignment=ft.MainAxisAlignment.END, vertical_alignment=ft.CrossAxisAlignment.START, spacing=10)
else:
avatar = get_avatar(role, member_info)
row = ft.Row([avatar, bubble], alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.START, spacing=10)
messages_control.controls.append(ft.Container(content=row))
page.update()
async def process_bot_message(messages_control: ft.ListView,
page: ft.Page,
agent: ReActAgent,
user_input: str,) -> tuple:
start_time = time.time()
message_content = ft.Text("正在思考...\n", **ClawConst.BUBBLE_BOT_THOUGHT_FONT)
model_info = ft.Text(f"模型: {agent.model}", size=11, color=ft.Colors.GREY_500, selectable=True)
bubble_column = ft.Column([message_content, model_info], spacing=4, horizontal_alignment=ft.CrossAxisAlignment.START)
assistant_bubble = ft.Container(content=bubble_column, bgcolor=ft.Colors.WHITE, **ClawConst.BUBBLE_STYLE)
robot_avatar = get_avatar("assistant")
message_row = ft.Container(content=ft.Row([robot_avatar, assistant_bubble], alignment=ft.MainAxisAlignment.START, spacing=10))
messages_control.controls.append(message_row)
page.update()
full_response = ""
isFinal=False
try:
async for chunk in agent.run_stream(user_input):
print(chunk, end='', flush=True)
if "\n💬 **最终回答**:\n" in chunk:
isFinal=True
chunk=chunk.replace("\n💬 **最终回答**:\n","")
if isFinal:
full_response += chunk
if full_response:
message_content.value=full_response
# 不知道为什么智谱可以用join
# message_content.value.join(chunk)
#执行过程也让用户看一下吧!
else:
message_content.value = chunk
page.update()
if re.search(r'https?://', full_response):
spans = parse_text_with_links(full_response, page)
new_content = ft.Text(spans=spans, **ClawConst.BUBBLE_BOT_THOUGHT_FONT)
bubble_column.controls[0] = new_content
message_content = new_content
if any(chunk.endswith(c) for c in ('.', '!', '?', '\n', '\t', ',')):
await messages_control.scroll_to(offset=-1, duration=ClawConst.MESSAGES_SCROLL_DURATION)
page.update()
except asyncio.CancelledError:
message_content.value = "❌已取消"
message_content.color = ft.Colors.GREY_500
model_info.value += " (已取消)"
page.update()
except Exception as ex:
message_content.value = f"Error: {str(ex)}"
message_content.color = ft.Colors.RED
page.update()
finally:
# breathing_task.cancel()
await messages_control.scroll_to(offset=-1, duration=ClawConst.MESSAGES_SCROLL_DURATION)
message_content.color = ft.Colors.BLACK
page.update()
input_tokens = agent.total_prompt_tokens
output_tokens = agent.total_completion_tokens
if input_tokens == 0 and output_tokens == 0:
input_tokens = max(1, len(user_input) // 2)
output_tokens = max(1, len(full_response) // 2)
model_info.value += f" · Token: {input_tokens+output_tokens}"
elapsed = time.time() - start_time
model_info.value += f" · 耗时: {elapsed:.2f}s"
return input_tokens, output_tokens