@baimingtao/lbs-agent 1.3.0 → 1.5.0

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.
@@ -1,13 +1,10 @@
1
- """
2
- Display — 格式化输出、颜色、标题生成。
3
-
4
- 对标 LBS display.py,精简实现:
5
- 1. Colors ANSI 颜色码(Windows 兼容)
6
- 2. format_title — 从任务文本生成简短标题
7
- 3. format_step 格式化迭代步骤输出
8
- 4. format_result — 格式化最终结果
9
- 5. Spinner — 简易加载动画
10
- 6. print_box — 框线输出
1
+ """LBS Display — Aurora Pink 现代终端输出。
2
+
3
+ 重写 display.py 为 Aurora 主题:
4
+ 旧: [工具] [结果] [思考] [迭代 N/M (ReAct)]
5
+ 新: iter N · 工具名(参数) · ✓ 结果 · ✦ 思考
6
+
7
+ 所有颜色统一到 cli_theme Aurora Pink 渐变体系。
11
8
  """
12
9
 
13
10
  from __future__ import annotations
@@ -19,27 +16,44 @@ import time
19
16
  from typing import Optional
20
17
 
21
18
 
22
- # ── ANSI 颜色 ──────────────────────────────────────
19
+ # ── ANSI True Color (Aurora Pink) ──────────────────────────────
23
20
 
24
21
  class Colors:
25
- RESET = "\033[0m"
26
- BOLD = "\033[1m"
27
- DIM = "\033[2m"
28
- RED = "\033[31m"
29
- GREEN = "\033[32m"
30
- YELLOW = "\033[33m"
31
- BLUE = "\033[34m"
32
- MAGENTA = "\033[35m"
33
- CYAN = "\033[36m"
34
- GRAY = "\033[90m"
35
-
36
- # Windows 兼容
22
+ RESET = "\033[0m"
23
+ BOLD = "\033[1m"
24
+ DIM = "\033[2m"
25
+ ITALIC = "\033[3m"
26
+
27
+ # Aurora 渐变
28
+ PINK = "\033[38;2;255;79;164m"
29
+ PINK_L = "\033[38;2;255;167;213m"
30
+ PURPLE = "\033[38;2;194;107;255m"
31
+ BLUE = "\033[38;2;110;139;255m"
32
+
33
+ # Mint
34
+ MINT = "\033[38;2;152;216;200m"
35
+ MINT_D = "\033[38;2;111;196;176m"
36
+
37
+ # 状态
38
+ GREEN = "\033[38;2;62;229;138m"
39
+ RED = "\033[38;2;255;107;129m"
40
+ YELLOW = "\033[38;2;255;179;71m"
41
+
42
+ # 文字
43
+ TEXT = "\033[38;2;235;235;245m"
44
+ SUBTLE = "\033[38;2;138;138;150m"
45
+ FAINT = "\033[38;2;90;90;102m"
46
+
47
+ # 兼容旧引用
48
+ CYAN = MINT_D
49
+ GRAY = SUBTLE
50
+ MAGENTA = PURPLE
51
+
37
52
  _enabled: Optional[bool] = None
38
53
 
39
54
  @classmethod
40
55
  def _check(cls) -> bool:
41
56
  if cls._enabled is None:
42
- # Windows 10+ 支持 ANSI,但需要启用
43
57
  if sys.platform == "win32":
44
58
  try:
45
59
  import ctypes
@@ -59,8 +73,9 @@ class Colors:
59
73
  return text
60
74
 
61
75
 
76
+ # ── 快捷颜色函数 ───────────────────────────────────────────────
77
+
62
78
  def color(text: str, c: str) -> str:
63
- """快捷函数:给文本加颜色。"""
64
79
  return Colors.wrap(text, c)
65
80
 
66
81
  def green(text: str) -> str:
@@ -73,78 +88,82 @@ def yellow(text: str) -> str:
73
88
  return Colors.wrap(text, Colors.YELLOW)
74
89
 
75
90
  def cyan(text: str) -> str:
76
- return Colors.wrap(text, Colors.CYAN)
91
+ return Colors.wrap(text, Colors.MINT_D)
77
92
 
78
93
  def gray(text: str) -> str:
79
- return Colors.wrap(text, Colors.GRAY)
94
+ return Colors.wrap(text, Colors.SUBTLE)
80
95
 
81
96
  def bold(text: str) -> str:
82
97
  return Colors.wrap(text, Colors.BOLD)
83
98
 
99
+ def pink(text: str) -> str:
100
+ return Colors.wrap(text, Colors.PINK)
84
101
 
85
- # ── 标题生成 ────────────────────────────────────────
102
+ def purple(text: str) -> str:
103
+ return Colors.wrap(text, Colors.PURPLE)
104
+
105
+ def mint(text: str) -> str:
106
+ return Colors.wrap(text, Colors.MINT_D)
107
+
108
+ def faint(text: str) -> str:
109
+ return Colors.wrap(text, Colors.FAINT)
86
110
 
87
- def format_title(task: str, max_len: int = 50) -> str:
88
- """从任务文本生成简短标题。
89
111
 
90
- 规则:
91
- 1. 取第一行
92
- 2. 截断到 max_len
93
- 3. 去掉标点和特殊字符
94
- """
112
+ # ── 标题生成 ───────────────────────────────────────────────────
113
+
114
+ def format_title(task: str, max_len: int = 50) -> str:
95
115
  if not task:
96
116
  return "(no task)"
97
117
  first_line = task.split("\n")[0].strip()
98
- # 去掉 markdown 标记
99
118
  first_line = re.sub(r"[*_`#>]", "", first_line)
100
119
  if len(first_line) > max_len:
101
120
  first_line = first_line[:max_len - 3] + "..."
102
121
  return first_line
103
122
 
104
123
 
105
- # ── 格式化输出 ──────────────────────────────────────
124
+ # ── 格式化输出 (Aurora 主题) ───────────────────────────────────
106
125
 
107
126
  def print_box(title: str, content: str = "", color_str: str = ""):
108
- """框线输出。"""
109
- width = 60
127
+ """框线输出 — Aurora 极简风格"""
110
128
  if color_str:
111
129
  title = color(title, color_str)
112
- print(f"\n{'=' * width}")
130
+ w = 52
131
+ sep = color("─" * w, Colors.FAINT)
132
+ print(f"\n{sep}")
113
133
  if title:
114
134
  print(f" {title}")
115
135
  if content:
116
136
  print()
117
137
  for line in content.split("\n"):
118
138
  print(f" {line}")
119
- print(f"{'=' * width}\n")
139
+ print(f"{sep}\n")
120
140
 
121
141
 
122
142
  def format_step_header(iteration: int, max_iterations: int, phase: str = "") -> str:
123
- """格式化迭代步骤标题。"""
124
- phase_str = f" [{phase}]" if phase else ""
125
- return f"--- 迭代 {iteration}/{max_iterations}{phase_str} ---"
143
+ """迭代标题 — 极简"""
144
+ return f"\n {pink('⟡')} {bold(f'iter {iteration}')}{faint(f'/{max_iterations}')}"
126
145
 
127
146
 
128
147
  def format_tool_call(name: str, args: dict, max_preview: int = 100) -> str:
129
- """格式化工具调用显示。"""
148
+ """工具调用 — Aurora 风格"""
130
149
  import json
131
150
  args_str = json.dumps(args, ensure_ascii=False)[:max_preview]
132
- return f" {cyan('[工具]')} {bold(name)}({args_str})"
151
+ return f" {purple('')} {cyan(name)}{faint('(')}{faint(args_str)}{faint(')')}"
133
152
 
134
153
 
135
154
  def format_tool_result(result: str, success: bool, max_preview: int = 200) -> str:
136
- """格式化工具结果显示。"""
155
+ """工具结果"""
137
156
  preview = result[:max_preview]
138
157
  if success:
139
- return f" {green('[结果]')} {preview}"
158
+ return f" {green('')} {faint(preview)}"
140
159
  else:
141
- return f" {red('[错误]')} {preview}"
160
+ return f" {red('')} {faint(preview)}"
142
161
 
143
162
 
144
163
  def format_thought(content: str, max_preview: int = 300) -> str:
145
- """格式化 LLM 思考输出。"""
164
+ """思考过程 薄荷绿斜体"""
146
165
  preview = content[:max_preview]
147
- return f" {gray('[思考]')} {preview}"
166
+ return f" {pink('')} {Colors.wrap(preview, Colors.ITALIC + Colors.MINT_D)}"
148
167
 
149
168
 
150
169
  def format_result_summary(
@@ -155,21 +174,27 @@ def format_result_summary(
155
174
  model: str = "",
156
175
  error: str = "",
157
176
  ) -> str:
158
- """格式化最终结果摘要。"""
159
- status = green("✓ 成功") if success else red("✗ 失败")
160
- lines = [f" 结果: {status}"]
161
- lines.append(f" 迭代: {iterations} 工具调用: {tool_calls} 耗时: {duration:.1f}s")
177
+ """最终摘要 — 一行式"""
178
+ icon = green("✓") if success else red("")
179
+ parts = [
180
+ f"{icon}",
181
+ faint(f"iter {iterations}"),
182
+ faint(f"tools {tool_calls}"),
183
+ color(f"{duration:.1f}s", Colors.MINT_D),
184
+ ]
162
185
  if model:
163
- lines.append(f" 模型: {model}")
186
+ parts.append(faint(model))
187
+ line1 = " " + faint(" · ").join(parts)
188
+ lines = [line1]
164
189
  if error:
165
- lines.append(f" {red('错误')}: {error[:200]}")
190
+ lines.append(f" {red(error[:200])}")
166
191
  return "\n".join(lines)
167
192
 
168
193
 
169
- # ── Spinner ────────────────────────────────────────
194
+ # ── Spinner — Aurora 风格 ──────────────────────────────────────
170
195
 
171
196
  class Spinner:
172
- """简易加载动画(后台线程)。"""
197
+ """加载动画(后台线程)"""
173
198
 
174
199
  _FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
175
200
 
@@ -180,7 +205,7 @@ class Spinner:
180
205
 
181
206
  def start(self):
182
207
  if not Colors._check():
183
- return # 不支持 ANSI 就不显示 spinner
208
+ return
184
209
  self._running = True
185
210
  self._thread = threading.Thread(target=self._spin, daemon=True)
186
211
  self._thread.start()
@@ -189,7 +214,6 @@ class Spinner:
189
214
  self._running = False
190
215
  if self._thread:
191
216
  self._thread.join(timeout=1)
192
- # 清除 spinner 行
193
217
  if Colors._check():
194
218
  sys.stdout.write("\r" + " " * (len(self._message) + 10) + "\r")
195
219
  sys.stdout.flush()
@@ -200,7 +224,7 @@ class Spinner:
200
224
  i = 0
201
225
  while self._running:
202
226
  frame = self._FRAMES[i % len(self._FRAMES)]
203
- sys.stdout.write(f"\r{cyan(frame)} {self._message}")
227
+ sys.stdout.write(f"\r{pink(frame)} {faint(self._message)}")
204
228
  sys.stdout.flush()
205
229
  time.sleep(0.1)
206
230
  i += 1
@@ -252,16 +252,19 @@ class AgentLoop:
252
252
 
253
253
  logger.info("[%s] loop started (%s): %s", self.config.name, mode, task[:100])
254
254
  if self.config.report_progress:
255
- label = f"{self.config.display_name or self.config.name}"
256
- if mode == "react":
257
- label += " [ReAct]"
258
- self._print(f"\n{'='*60}")
259
- self._print(f"Agent: {label}")
255
+ from runtime.orchestration.display import pink, purple, mint, faint, bold, color, Colors
256
+ sep = color("" * 52, Colors.FAINT)
257
+ self._print(f"\n{sep}")
258
+ agent_label = self.config.display_name or self.config.name
259
+ self._print(f" {purple('◐')} {bold(agent_label)}")
260
260
  chain = [self.config.model] + self.config.fallback_models
261
- self._print(f"Model: {' -> '.join(chain)}")
262
- self._print(f"Tools: {self.config.tools or '(none)'}")
263
- self._print(f"Task: {task[:200]}")
264
- self._print(f"{'='*60}\n")
261
+ model_str = faint(" ").join(pink(m) for m in chain)
262
+ self._print(f" {model_str}")
263
+ tools_list = self.config.tools or []
264
+ if tools_list:
265
+ tools_str = faint(", ").join(mint(t) for t in tools_list)
266
+ self._print(f" {faint('tools')} {tools_str}")
267
+ self._print(f"{sep}")
265
268
 
266
269
  return tools_schema
267
270
 
@@ -1997,14 +2000,20 @@ class AgentLoop:
1997
2000
  compression_results=list(self.compression_results),
1998
2001
  )
1999
2002
 
2000
- self._print(f"\n{'='*60}")
2001
- self._print(f"Agent: {self.config.name}")
2002
- self._print(f"结果: {'✓ 成功' if success else '✗ 失败'}")
2003
- self._print(f"迭代: {self.current_iteration} 工具调用: {self._tool_call_count}")
2004
- self._print(f"耗时: {duration:.1f}s")
2003
+ from runtime.orchestration.display import green, red, faint, pink, purple, mint, color, Colors, bold
2004
+ sep = color("─" * 52, Colors.FAINT)
2005
+ self._print(f"\n{sep}")
2006
+ icon = green("✓") if success else red("✕")
2007
+ self._print(f" {icon} {bold(self.config.name)}")
2008
+ parts = [
2009
+ faint(f"iter {self.current_iteration}"),
2010
+ faint(f"tools {self._tool_call_count}"),
2011
+ color(f"{duration:.1f}s", Colors.MINT_D),
2012
+ ]
2013
+ self._print(" " + faint(" · ").join(parts))
2005
2014
  if error:
2006
- self._print(f"错误: {error}")
2007
- self._print(f"{'='*60}\n")
2015
+ self._print(f" {red(error[:200])}")
2016
+ self._print(f"{sep}\n")
2008
2017
 
2009
2018
  logger.info(
2010
2019
  "[%s] loop finished: success=%s iterations=%d tools=%d duration=%.1fs",
package/scripts/init.js CHANGED
@@ -63,7 +63,7 @@ function ensureDir(dir) {
63
63
  // ── Step 1: 检测系统 Python ────────────────────────────────────
64
64
 
65
65
  function findSystemPython() {
66
- console.log(paint(' 🔍 ', C.sakura) + '检测系统 Python...');
66
+ console.log(paint(' 🔍 ', C.pink) + '检测系统 Python...');
67
67
 
68
68
  const candidates = isWin
69
69
  ? ['python', 'python3', 'py -3']
@@ -73,7 +73,7 @@ function findSystemPython() {
73
73
  const result = run(cmd, ['--version'], { silent: true, shell: isWin });
74
74
  if (result.status === 0) {
75
75
  const version = (result.stdout || result.stderr || '').trim();
76
- console.log(paint(' ✓ ', C.green) + `${cmd}: ${version}`);
76
+ console.log(paint(' ✓ ', C.ok) + `${cmd}: ${version}`);
77
77
 
78
78
  const match = version.match(/(\d+)\.(\d+)/);
79
79
  if (match) {
@@ -81,11 +81,11 @@ function findSystemPython() {
81
81
  const minor = parseInt(match[2]);
82
82
  if (major >= 3 && minor >= 10) return cmd;
83
83
  }
84
- console.log(paint(' ⚠ ', C.honey) + '版本低于 3.10,继续寻找...');
84
+ console.log(paint(' ⚠ ', C.warn) + '版本低于 3.10,继续寻找...');
85
85
  }
86
86
  }
87
87
 
88
- console.error(paint('\n ✖ ', C.red) + '未找到 Python 3.10+');
88
+ console.error(paint('\n ✖ ', C.err) + '未找到 Python 3.10+');
89
89
  console.error(paint(' ', C.dim) + 'Windows: https://www.python.org/downloads/');
90
90
  console.error(paint(' ', C.dim) + 'macOS: brew install python@3.12');
91
91
  console.error(paint(' ', C.dim) + 'Linux: sudo apt install python3 python3-venv');
@@ -96,20 +96,20 @@ function findSystemPython() {
96
96
 
97
97
  function createVenv(sysPython) {
98
98
  if (fs.existsSync(PYTHON)) {
99
- console.log(paint(' ✓ ', C.green) + '虚拟环境已存在,跳过创建');
99
+ console.log(paint(' ✓ ', C.ok) + '虚拟环境已存在,跳过创建');
100
100
  return true;
101
101
  }
102
102
 
103
103
  // 确保 state root 存在
104
104
  ensureDir(STATE_ROOT);
105
- console.log(paint(' 📦 ', C.sakura) + `创建 Python 虚拟环境 → ${paint(STATE_ROOT + '/venv', C.mint)}`);
105
+ console.log(paint(' 📦 ', C.pink) + `创建 Python 虚拟环境 → ${paint(STATE_ROOT + '/venv', C.mint)}`);
106
106
 
107
107
  const result = run(sysPython, ['-m', 'venv', VENV_DIR]);
108
108
  if (result.status !== 0 || !fs.existsSync(PYTHON)) {
109
- console.error(paint(' ✖ ', C.red) + '创建虚拟环境失败');
109
+ console.error(paint(' ✖ ', C.err) + '创建虚拟环境失败');
110
110
  return false;
111
111
  }
112
- console.log(paint(' ✓ ', C.green) + 'venv 已创建');
112
+ console.log(paint(' ✓ ', C.ok) + 'venv 已创建');
113
113
  return true;
114
114
  }
115
115
 
@@ -117,17 +117,17 @@ function createVenv(sysPython) {
117
117
 
118
118
  function installDeps() {
119
119
  if (!fs.existsSync(REQUIREMENTS)) {
120
- console.error(paint(' ✖ ', C.red) + 'requirements.txt 不存在: ' + REQUIREMENTS);
120
+ console.error(paint(' ✖ ', C.err) + 'requirements.txt 不存在: ' + REQUIREMENTS);
121
121
  return false;
122
122
  }
123
123
 
124
- console.log(paint(' 📥 ', C.sakura) + '安装 Python 依赖 (可能需要 1-2 分钟)...');
124
+ console.log(paint(' 📥 ', C.pink) + '安装 Python 依赖 (可能需要 1-2 分钟)...');
125
125
  const result = run(PIP, ['install', '-r', REQUIREMENTS, '--disable-pip-version-check']);
126
126
  if (result.status !== 0) {
127
- console.error(paint(' ✖ ', C.red) + '依赖安装失败');
127
+ console.error(paint(' ✖ ', C.err) + '依赖安装失败');
128
128
  return false;
129
129
  }
130
- console.log(paint(' ✓ ', C.green) + '依赖安装完成');
130
+ console.log(paint(' ✓ ', C.ok) + '依赖安装完成');
131
131
  return true;
132
132
  }
133
133
 
@@ -161,22 +161,22 @@ function createDefaultDirs() {
161
161
  fs.writeFileSync(configPath, defaultConfig, 'utf-8');
162
162
  }
163
163
 
164
- console.log(paint(' ✓ ', C.green) + '目录结构已创建');
164
+ console.log(paint(' ✓ ', C.ok) + '目录结构已创建');
165
165
  }
166
166
 
167
167
  // ── Step 5: 验证 ───────────────────────────────────────────────
168
168
 
169
169
  function verifyInstall() {
170
- console.log(paint(' 🔍 ', C.sakura) + '验证安装...');
170
+ console.log(paint(' 🔍 ', C.pink) + '验证安装...');
171
171
  const result = run(PYTHON, [path.join(PKG_ROOT, 'lbs.py'), 'status'], {
172
172
  silent: true,
173
173
  cwd: PKG_ROOT,
174
174
  env: { ...process.env, LBS_HOME: STATE_ROOT },
175
175
  });
176
176
  if (result.status === 0) {
177
- console.log(paint(' ✓ ', C.green) + 'lbs status 运行正常');
177
+ console.log(paint(' ✓ ', C.ok) + 'lbs status 运行正常');
178
178
  } else {
179
- console.log(paint(' ⚠ ', C.honey) + 'lbs status 运行异常(可能是配置问题,不影响初始化)');
179
+ console.log(paint(' ⚠ ', C.warn) + 'lbs status 运行异常(可能是配置问题,不影响初始化)');
180
180
  }
181
181
  return true;
182
182
  }
@@ -191,16 +191,16 @@ function writeFlag() {
191
191
  platform: os.platform(),
192
192
  node_version: process.version,
193
193
  }, null, 2));
194
- console.log(paint(' ✓ ', C.green) + '.lbs-initialized 已创建');
194
+ console.log(paint(' ✓ ', C.ok) + '.lbs-initialized 已创建');
195
195
  }
196
196
 
197
197
  // ── 主流程 ─────────────────────────────────────────────────────
198
198
 
199
199
  function main() {
200
200
  console.log();
201
- console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.sakura));
202
- console.log(paint(' │ ', C.sakura) + paint('⚙ LBS Agent — 初始化', C.bold, C.sakura) + paint(' │', C.sakura));
203
- console.log(paint(' └─────────────────────────────────────────────────────────┘', C.sakura));
201
+ console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.pink));
202
+ console.log(paint(' │ ', C.pink) + paint('⚙ LBS Agent — 初始化', C.bold, C.pink) + paint(' │', C.pink));
203
+ console.log(paint(' └─────────────────────────────────────────────────────────┘', C.pink));
204
204
  console.log();
205
205
  console.log(paint(' 包目录 (只读): ', C.dim) + paint(PKG_ROOT, C.mint));
206
206
  console.log(paint(' 状态目录 (可写): ', C.dim) + paint(STATE_ROOT, C.mint));
@@ -226,17 +226,17 @@ function main() {
226
226
  writeFlag();
227
227
 
228
228
  console.log();
229
- console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.green));
230
- console.log(paint(' │ ', C.green) + paint('✅ 初始化完成!', C.bold, C.green) + paint(' │', C.green));
231
- console.log(paint(' │ │', C.green));
232
- console.log(paint(' │ ', C.green) + paint('下一步:', C.bold) + paint(' │', C.green));
233
- console.log(paint(' │ ', C.green) + paint(' lbs setup 配置模型/供应商/Agent', C.mint) + paint(' │', C.green));
234
- console.log(paint(' │ ', C.green) + paint(' lbs status 查看系统状态', C.mint) + paint(' │', C.green));
235
- console.log(paint(' │ ', C.green) + paint(' lbs help 查看所有命令', C.mint) + paint(' │', C.green));
236
- console.log(paint(' │ ', C.green) + paint(' lbs web 启动 Web 界面', C.mint) + paint(' │', C.green));
237
- console.log(paint(' │ │', C.green));
238
- console.log(paint(' │ ', C.green) + paint('状态目录: ', C.dim) + paint(STATE_ROOT, C.mint) + paint(' │', C.green));
239
- console.log(paint(' └─────────────────────────────────────────────────────────┘', C.green));
229
+ console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.ok));
230
+ console.log(paint(' │ ', C.ok) + paint('✅ 初始化完成!', C.bold, C.ok) + paint(' │', C.ok));
231
+ console.log(paint(' │ │', C.ok));
232
+ console.log(paint(' │ ', C.ok) + paint('下一步:', C.bold) + paint(' │', C.ok));
233
+ console.log(paint(' │ ', C.ok) + paint(' lbs setup 配置模型/供应商/Agent', C.mint) + paint(' │', C.ok));
234
+ console.log(paint(' │ ', C.ok) + paint(' lbs status 查看系统状态', C.mint) + paint(' │', C.ok));
235
+ console.log(paint(' │ ', C.ok) + paint(' lbs help 查看所有命令', C.mint) + paint(' │', C.ok));
236
+ console.log(paint(' │ ', C.ok) + paint(' lbs web 启动 Web 界面', C.mint) + paint(' │', C.ok));
237
+ console.log(paint(' │ │', C.ok));
238
+ console.log(paint(' │ ', C.ok) + paint('状态目录: ', C.dim) + paint(STATE_ROOT, C.mint) + paint(' │', C.ok));
239
+ console.log(paint(' └─────────────────────────────────────────────────────────┘', C.ok));
240
240
  console.log();
241
241
  }
242
242