Coverage for aipyapp/plugins/p_style_minimal.py: 0%

1#!/usr/bin/env python

2# -*- coding: utf-8 -*-

3

4 from functools import wraps

5 import sys

6 import json

7

8 from rich.tree import Tree

9 from rich.text import Text

10 from rich.console import Console

11 from rich.status import Status

12 from rich.syntax import Syntax

13 from rich.progress import Progress, TimeElapsedColumn

14

15 from aipyapp.display import RichDisplayPlugin

16 from aipyapp import T

17

18 def restore_output(func):

19 @wraps(func)

20 def wrapper(self, *args, **kwargs):

21 old_stdout, old_stderr = sys.stdout, sys.stderr

22 sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__

23

24 try:

25 return func(self, *args, **kwargs)

26 finally:

27 sys.stdout, sys.stderr = old_stdout, old_stderr

28 return wrapper

29

30 class DisplayMinimal(RichDisplayPlugin):

31 """Minimal display style"""

32 name = "minimal"

33 version = "1.0.0"

34 description = "Minimal display style"

35 author = "AiPy Team"

36

37 def __init__(self, console: Console, quiet: bool = False):

38 super().__init__(console, quiet)

39 self.live_display = None

40 self.received_lines = 0 # 记录接收的行数

41 self.status = None # Status 对象

42 self.progress = None

43

44 def _get_title(self, title: str, *args, style: str = "info", prefix: str = "\n"):

45 text = Text(f"{prefix}● {title}".format(*args), style=style)

46 text.highlight_words(args, style="bold white")

47 return text

48

49 def on_exception(self, event):

50 """异常事件处理"""

51 msg = event.data.get('msg', '')

52 exception = event.data.get('exception')

53 title = self._get_title(T("Exception occurred"), msg, style="error")

54 tree = Tree(title)

55 tree.add(exception)

56 self.console.print(tree)

57

58 def on_task_start(self, event):

59 """任务开始事件处理"""

60 data = event.data

61 instruction = data.get('instruction')

62 title = data.get('title')

63 if not title:

64 title = instruction

65 tree = Tree(f"🚀 {T('Task processing started')}")

66 tree.add(title)

67 self.console.print(tree)

68

69 def on_task_end(self, event):

70 """任务结束事件处理"""

71 path = event.data.get('path', '')

72 self.console.print(f"[green]{T('Task completed')}: {path}")

73

74 def on_query_start(self, event):

75 """查询开始事件处理"""

76 data = event.data

77 llm = data.get('llm', '')

78 title = self._get_title(T("Sending message to {}"), llm)

79 self.console.print(title)

80

81 def on_round_start(self, event):

82 """回合开始事件处理"""

83 data = event.data

84 instruction = data.get('instruction')

85 title = data.get('title')

86 if not title:

87 title = instruction

88 prompt = self._get_title(T("Instruction processing started"))

89 tree = Tree(prompt)

90 tree.add(title)

91 self.console.print(tree)

92

93 def on_stream_start(self, event):

94 """流式开始事件处理"""

95 # 简约风格：重置行数计数器并启动 Status

96 self.received_lines = 0

97 title = self._get_title(T("Streaming started"), prefix="")

98 #self.status = Status(title, console=self.console)

99 #self.status.start()

100 self.progress = Progress(*Progress.get_default_columns(),TimeElapsedColumn(), transient=False)

101 self.progress.start()

102 self.progress.add_task(title, total=None)

103

104 def on_stream_end(self, event):

105 """流式结束事件处理"""

106 # 简约风格：停止 Status 并显示最终结果

107 if self.status:

108 self.status.stop()

109 if self.received_lines > 0:

110 title = self._get_title(T("Received {} lines total"), self.received_lines)

111 self.console.print(title)

112 self.status = None

113 if self.progress:

114 self.progress.stop()

115 self.progress = None

116

117 def on_stream(self, event):

118 """LLM 流式响应事件处理"""

119 response = event.data

120 lines = response.get('lines', [])

121 reason = response.get('reason', False)

122

123 if not reason: # 只统计非思考内容

124 self.received_lines += len(lines)

125 # 使用 Status 在同一行更新进度

126 if self.status:

127 title = self._get_title(T("Receiving response... ({})"), self.received_lines)

128 self.status.update(title)

129

130 def on_response_complete(self, event):

131 """LLM 响应完成事件处理"""

132 data = event.data

133 llm = data.get('llm', '')

134 msg = data.get('msg')

135 if not msg:

136 title = self._get_title(T("LLM response is empty"), style="error")

137 self.console.print(title)

138 return

139

140 if msg.role == 'error':

141 title = self._get_title(T("Failed to receive message"), style="error")

142 tree = Tree(title)

143 tree.add(msg.content)

144 self.console.print(tree)

145 return

146

147 if msg.reason:

148 content = f"{msg.reason}\n\n-----\n\n{msg.content}"

149 else:

150 content = msg.content

151 title = self._get_title(f"{T('Completed receiving message')} ({llm})", style="success")

152 tree = Tree(title)

153 self.console.print(tree)

154

155 def on_task_status(self, event):

156 """任务状态事件处理"""

157 status = event.data.get('status')

158 completed = status.get('completed', False)

159 style = "success" if completed else "error"

160 title = self._get_title(T("Task status"), style=style)

161 tree = Tree(title, guide_style=style)

162 if completed:

163 tree.add(T("Completed"))

164 tree.add(T("Confidence level: {}", status.get('confidence', 0)))

165 else:

166 tree.add(T("Failed"))

167 tree.add(T("Reason: {}", status.get('reason', '')))

168 tree.add(T("Suggestion: {}", status.get('suggestion', '')))

169 self.console.print(tree)

170

171 def on_parse_reply(self, event):

172 """消息解析结果事件处理"""

173 ret = event.data.get('result')

174 if not ret:

175 return

176

177 title = self._get_title(T("Message parse result"))

178 tree = Tree(title)

179

180 if 'blocks' in ret and ret['blocks']:

181 block_count = len(ret['blocks'])

182 tree.add(f"{block_count} {T('code blocks')}")

183

184 if 'commands' in ret and ret['commands']:

185 commands = ret['commands']

186 exec_count = sum(1 for cmd in commands if cmd['type'] == 'exec')

187 edit_count = sum(1 for cmd in commands if cmd['type'] == 'edit')

188

189 if exec_count > 0:

190 tree.add(f"{T('Execution')}: {exec_count}")

191 if edit_count > 0:

192 tree.add(f"{T('Edit')}: {edit_count}")

193

194 if 'call_tool' in ret:

195 tree.add(T("MCP tool call"))

196

197 if 'errors' in ret and ret['errors']:

198 error_count = len(ret['errors'])

199 tree.add(f"{error_count} {T('errors')}")

200

201 self.console.print(tree)

202

203 def on_exec(self, event):

204 """代码执行开始事件处理"""

205 block = event.data.get('block')

206 title = self._get_title(T("Start executing code block {}"), block.name)

207 self.console.print(title)

208

209 def on_edit_start(self, event):

210 """代码编辑开始事件处理"""

211 instruction = event.data.get('instruction', {})

212 block_name = instruction.get('name', 'Unknown')

213 title = self._get_title(T("Start editing {}"), block_name, style="warning")

214 self.console.print(title)

215

216 def on_edit_result(self, event):

217 """代码编辑结果事件处理"""

218 data = event.data

219 result = data.get('result', {})

220

221 success = result.get('success', False)

222 block_name = result.get('block_name', 'Unknown')

223 new_version = result.get('new_version')

224

225 if success:

226 style = "success"

227 version_info = f" (v{new_version})" if new_version else ""

228 title = self._get_title(T("Edit completed {}{}"), block_name, version_info, style=style)

229 else:

230 style = "error"

231 title = self._get_title(T("Edit failed {}"), block_name, style=style)

232

233 self.console.print(title)

234

235 @restore_output

236 def on_call_function(self, event):

237 """函数调用事件处理"""

238 data = event.data

239 funcname = data.get('funcname')

240 title = self._get_title(T("Start calling function {}"), funcname)

241 self.console.print(title)

242

243 @restore_output

244 def on_call_function_result(self, event):

245 """函数调用结果事件处理"""

246 data = event.data

247 funcname = data.get('funcname')

248 success = data.get('success', False)

249 result = data.get('result')

250 error = data.get('error')

251

252 if success:

253 style = "success"

254 title = self._get_title(T("Function call result {}"), funcname, style=style)

255 tree = Tree(title)

256 # 简约风格：只显示结果存在性，不显示详细内容

257 if result is not None:

258 tree.add(T("Result returned"))

259 else:

260 tree.add(T("No return value"))

261 self.console.print(tree)

262 else:

263 style = "error"

264 title = self._get_title(T("Function call failed {}"), funcname, style=style)

265 tree = Tree(title)

266 tree.add(error if error else T("Unknown error"))

267 self.console.print(tree)

268

269 def on_exec_result(self, event):

270 """代码执行结果事件处理"""

271 data = event.data

272 result = data.get('result')

273 block = data.get('block')

274

275 try:

276 success = result['__state__']['success']

277 style = "success" if success else "error"

278 except:

279 style = "warning"

280

281 # 显示说明信息

282 block_name = getattr(block, 'name', 'Unknown') if block else 'Unknown'

283 title = self._get_title(T("Execution result {}"), block_name, style=style)

284 tree = Tree(title)

285

286 # JSON格式化和高亮显示结果

287 #json_result = json.dumps(result, ensure_ascii=False, indent=2, default=str)

288 #tree.add(Syntax(json_result, "json", word_wrap=True))

289 self.console.print(tree)

290

291 def on_mcp_call(self, event):

292 """工具调用事件处理"""

293 title = self._get_title(T("Start calling MCP tool"))

294 self.console.print(title)

295

296 def on_mcp_result(self, event):

297 """MCP 工具调用结果事件处理"""

298 data = event.data

299 result = data.get('result')

300 block = data.get('block')

301 title = self._get_title(T("MCP tool call result {}"), block.name)

302 self.console.print(title)

303 #json_result = json.dumps(result, ensure_ascii=False, indent=2, default=str)

304 #self.console.print_json(json_result, style="dim")

305

306 def on_round_end(self, event):

307 """任务总结事件处理"""

308 data = event.data

309 summary = data.get('summary', {})

310 response = data.get('response', '')

311 # 简约显示：只显示总结信息

312 title = self._get_title(T("End processing instruction"))

313 tree = Tree(title)

314 if response:

315 tree.add(Syntax(response, "markdown", word_wrap=True))

316 tree.add(f"{T('Summary')}: {summary.get('summary')}")

317 self.console.print(tree)

318

319 def on_upload_result(self, event):

320 """云端上传结果事件处理"""

321 data = event.data

322 status_code = data.get('status_code', 0)

323 url = data.get('url', '')

324 if url:

325 self.console.print(f"🟢 {T('Article uploaded successfully, {}', url)}", style="success")

326 else:

327 self.console.print(f"🔴 {T('Upload failed (status code: {})', status_code)}", style="error")

328

329 def on_task_end(self, event):

330 """任务结束事件处理"""

331 path = event.data.get('path', '')

332 title = self._get_title(T("Task completed"))

333 tree = Tree(title)

334 tree.add(path)

335 self.console.print(tree)

336

337 def on_runtime_message(self, event):

338 """Runtime消息事件处理"""

339 data = event.data

340 message = data.get('message', '')

341 status = data.get('status', 'info')

342 title = self._get_title(message, style=status)

343 self.console.print(title)

344

345 def on_runtime_input(self, event):

346 """Runtime输入事件处理"""

347 # 输入事件通常不需要特殊处理，因为input_prompt已经处理了

348 pass