refactor: remove all emoji from agentkit
Deploy to Production / deploy (push) Waiting to run
Details
Test / backend-test (push) Waiting to run
Details
Test / frontend-unit (push) Waiting to run
Details
Test / api-e2e (push) Waiting to run
Details
Test / frontend-e2e (push) Waiting to run
Details
Test / backend-test (pull_request) Has been cancelled
Details
Test / frontend-unit (pull_request) Has been cancelled
Details
Test / api-e2e (pull_request) Has been cancelled
Details
Test / frontend-e2e (pull_request) Has been cancelled
Details
Deploy to Production / deploy (push) Waiting to run
Details
Test / backend-test (push) Waiting to run
Details
Test / frontend-unit (push) Waiting to run
Details
Test / api-e2e (push) Waiting to run
Details
Test / frontend-e2e (push) Waiting to run
Details
Test / backend-test (pull_request) Has been cancelled
Details
Test / frontend-unit (pull_request) Has been cancelled
Details
Test / api-e2e (pull_request) Has been cancelled
Details
Test / frontend-e2e (pull_request) Has been cancelled
Details
Replace emoji across codebase: YAML avatars -> first char, frontend banners -> Ant Design Vue components, CLI status -> OK/FAIL/WARN labels, terminal -> [WARN]/[OK]/[PENDING], Bitable DB default -> table, App.vue font cleanup, test fixtures -> first char letters. shell.avatar type upgraded to string | Component.
This commit is contained in:
parent
36b0296730
commit
78a7faa17b
|
|
@ -73,7 +73,7 @@ jobs:
|
|||
echo "尝试 $i/30: 服务未就绪,等待 5 秒..."
|
||||
sleep 5
|
||||
done
|
||||
echo "❌ 健康检查失败"
|
||||
echo "[FAIL] 健康检查失败"
|
||||
docker compose -f "$REPO_DIR/$COMPOSE_FILE" logs --tail=100
|
||||
exit 1
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ config:
|
|||
decision_framework: "用户价值优先 — 问'这会让用户觉得简单吗'和'它在 5 年后还有意义吗'"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "💬"
|
||||
avatar: "A"
|
||||
color: "#07C160"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ config:
|
|||
decision_framework: "系统设计 — 评估性能、安全性、可扩展性,遵循 SOLID 原则"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "⚙️"
|
||||
avatar: "B"
|
||||
color: "#fa8c16"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ config:
|
|||
decision_framework: "逆向思考 — 问'怎样做会必然失败',然后避免它"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🧠"
|
||||
avatar: "C"
|
||||
color: "#2C3E50"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ config:
|
|||
decision_framework: "代码质量 — 规范性、安全性、可维护性、性能四维评估"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🛡️"
|
||||
avatar: "C"
|
||||
color: "#722ed1"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ config:
|
|||
- backend_engineer
|
||||
- qa_engineer
|
||||
- code_reviewer
|
||||
avatar: "👥"
|
||||
avatar: "D"
|
||||
color: "#1890ff"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ config:
|
|||
decision_framework: "第一性原理 — 问'这件事的物理学本质是什么',再推导可行性"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🚀"
|
||||
avatar: "E"
|
||||
color: "#E31937"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ config:
|
|||
decision_framework: "用户体验优先 — 在技术实现和用户需求之间找到最佳平衡"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🎨"
|
||||
avatar: "F"
|
||||
color: "#52c41a"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ config:
|
|||
decision_framework: "客户至上 + 长期主义 — 问'什么对客户最好'和'这个决策 10 年后是否仍正确'"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "📦"
|
||||
avatar: "J"
|
||||
color: "#FF9900"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ config:
|
|||
decision_framework: "用户价值 + 不寻常路 — 问'用户真的想要这个吗'和'这看起来像坏想法吗'"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "📝"
|
||||
avatar: "P"
|
||||
color: "#FF6600"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ config:
|
|||
- allenzhang
|
||||
- charlie_munger
|
||||
- paul_graham
|
||||
avatar: "🏛️"
|
||||
avatar: "P"
|
||||
color: "#8E44AD"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ config:
|
|||
decision_framework: "质量保障 — 测试覆盖率、边界条件、回归测试三重保障"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🔍"
|
||||
avatar: "Q"
|
||||
color: "#eb2f96"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ config:
|
|||
decision_framework: "原则驱动 — 问'这符合哪条原则'和'最可信的人怎么看'"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "⚖️"
|
||||
avatar: "R"
|
||||
color: "#1A5276"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ config:
|
|||
decision_framework: "用户体验 + 专注 — 问'这足够简单吗'和'这是我能做的最好的吗'"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🍎"
|
||||
avatar: "S"
|
||||
color: "#555555"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ config:
|
|||
decision_framework: "架构决策 — 评估可行性、可维护性、扩展性,权衡短期与长期"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "🏗️"
|
||||
avatar: "T"
|
||||
color: "#1890ff"
|
||||
is_lead: true
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ config:
|
|||
decision_framework: "能力圈 + 内在价值 — 问'我理解这个业务吗'和'它的内在价值是多少'"
|
||||
collaboration_strategy: "cooperative"
|
||||
bound_skills: []
|
||||
avatar: "💰"
|
||||
avatar: "W"
|
||||
color: "#1E8449"
|
||||
is_lead: false
|
||||
task_mode: llm_generate
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class FileModel(BitableBase):
|
|||
|
||||
id = Column(String, primary_key=True, default=_uuid_str)
|
||||
name = Column(String, nullable=False)
|
||||
icon = Column(String, default="📋")
|
||||
icon = Column(String, default="table")
|
||||
description = Column(Text, default="")
|
||||
owner_user_id = Column(String, nullable=True)
|
||||
created_at = Column(DateTime(timezone=True), default=_utcnow)
|
||||
|
|
@ -213,7 +213,7 @@ async def _apply_v2_migration(conn: object) -> None:
|
|||
"CREATE TABLE IF NOT EXISTS bitable.bitable_files ("
|
||||
" id VARCHAR PRIMARY KEY,"
|
||||
" name VARCHAR NOT NULL,"
|
||||
" icon VARCHAR DEFAULT '📋',"
|
||||
" icon VARCHAR DEFAULT 'table',"
|
||||
" description TEXT DEFAULT '',"
|
||||
" owner_user_id VARCHAR,"
|
||||
" created_at TIMESTAMPTZ DEFAULT NOW(),"
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ def dept_list(
|
|||
str(d.get("id", "")),
|
||||
str(d.get("name", "")),
|
||||
str(d.get("description", "")),
|
||||
"✓" if d.get("is_active") else "✗",
|
||||
"OK" if d.get("is_active") else "--",
|
||||
str(d.get("created_at", "")),
|
||||
)
|
||||
console.print(table)
|
||||
|
|
@ -598,7 +598,7 @@ def user_list(
|
|||
str(u.get("username", "")),
|
||||
str(u.get("email", "")),
|
||||
str(u.get("role", "")),
|
||||
"✓" if u.get("is_active") else "✗",
|
||||
"OK" if u.get("is_active") else "--",
|
||||
)
|
||||
console.print(table)
|
||||
|
||||
|
|
|
|||
|
|
@ -892,7 +892,7 @@ async def _run_llm_reasoning(
|
|||
)
|
||||
cases.append(case)
|
||||
if verbose:
|
||||
status = "[green]✓[/green]" if case.passed else "[red]✗[/red]"
|
||||
status = "[green]OK[/green]" if case.passed else "[red]FAIL[/red]"
|
||||
console.print(
|
||||
f" {status} {task.task_id}: {result.actual} ({result.duration_ms:.2f}ms)"
|
||||
)
|
||||
|
|
@ -986,7 +986,7 @@ async def _run_gui_integration(
|
|||
|
||||
def _log(tid: str, passed: bool, label: str) -> None:
|
||||
if verbose:
|
||||
status = "[green]✓[/green]" if passed else "[red]✗[/red]"
|
||||
status = "[green]OK[/green]" if passed else "[red]FAIL[/red]"
|
||||
console.print(f" {status} {tid}: {label}")
|
||||
|
||||
all_runs_cases: list[list[CaseResult]] = []
|
||||
|
|
@ -2029,7 +2029,7 @@ async def _run_dimension(
|
|||
cases.append(case)
|
||||
|
||||
if verbose:
|
||||
status = "[green]✓[/green]" if case.passed else "[red]✗[/red]"
|
||||
status = "[green]OK[/green]" if case.passed else "[red]FAIL[/red]"
|
||||
console.print(
|
||||
f" {status} {task.task_id}: {result.actual} ({result.duration_ms:.2f}ms)"
|
||||
)
|
||||
|
|
@ -2730,7 +2730,7 @@ def benchmark(
|
|||
components = _build_real_components()
|
||||
if components is None:
|
||||
console.print(
|
||||
"[yellow]⚠ LLM mode skipped — no valid agentkit.yaml or API key.[/yellow]"
|
||||
"[yellow]WARN LLM mode skipped — no valid agentkit.yaml or API key.[/yellow]"
|
||||
)
|
||||
else:
|
||||
preprocessor, _skill_registry, llm_gateway = components
|
||||
|
|
@ -2764,7 +2764,7 @@ def benchmark(
|
|||
progress.update(task, completed=True, total=1)
|
||||
|
||||
if not results:
|
||||
console.print("[yellow]⚠ No dimensions were run.[/yellow]")
|
||||
console.print("[yellow]WARN No dimensions were run.[/yellow]")
|
||||
return
|
||||
|
||||
# Display summary table
|
||||
|
|
@ -2781,13 +2781,13 @@ def benchmark(
|
|||
|
||||
if fail_all == 0:
|
||||
summary = f"All {pass_all} tests passed across {len(results)} dimensions."
|
||||
console.print(f"[bold green]✓ {summary}[/bold green]")
|
||||
console.print(f"[bold green]OK {summary}[/bold green]")
|
||||
else:
|
||||
summary = (
|
||||
f"{pass_all}/{total_all} tests passed ({fail_all} failed) "
|
||||
f"across {len(results)} dimensions."
|
||||
)
|
||||
console.print(f"[bold yellow]⚠ {summary}[/bold yellow]")
|
||||
console.print(f"[bold yellow]WARN {summary}[/bold yellow]")
|
||||
|
||||
console.print()
|
||||
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ def _render_pm_collaboration_event(message: dict) -> bool:
|
|||
rprint(
|
||||
Panel(
|
||||
"\n".join(lines),
|
||||
title=f"[bold]{'✓' if passed else '✗'} 验收结果[/bold]",
|
||||
title=f"[bold]{'OK' if passed else 'FAIL'} 验收结果[/bold]",
|
||||
border_style=color,
|
||||
)
|
||||
)
|
||||
|
|
@ -600,7 +600,7 @@ def _render_pm_collaboration_event(message: dict) -> bool:
|
|||
f"[bold]专家:[/bold] {expert}\n"
|
||||
f"[bold]阶段:[/bold] {phase_name}\n"
|
||||
f"[bold]风险:[/bold] {risk_desc}",
|
||||
title="[bold]⚠ 风险标记[/bold]",
|
||||
title="[bold]WARN 风险标记[/bold]",
|
||||
border_style="yellow",
|
||||
)
|
||||
)
|
||||
|
|
@ -689,9 +689,9 @@ async def _execute_team_cli(
|
|||
elif etype == "plan_update":
|
||||
phases = message.get("plan_phases", [])
|
||||
icon_map = {
|
||||
"completed": ("✓", "green"),
|
||||
"completed": ("OK", "green"),
|
||||
"in_progress": ("▶", "blue"),
|
||||
"failed": ("✗", "red"),
|
||||
"failed": ("FAIL", "red"),
|
||||
}
|
||||
lines = []
|
||||
for ph in phases:
|
||||
|
|
@ -724,10 +724,10 @@ async def _execute_team_cli(
|
|||
)
|
||||
elif etype == "phase_completed":
|
||||
summary = message.get("result_summary", "")
|
||||
rprint(f" [green]✓ {message.get('phase_name', '?')}[/green]: {summary[:120]}")
|
||||
rprint(f" [green]OK {message.get('phase_name', '?')}[/green]: {summary[:120]}")
|
||||
elif etype == "phase_failed":
|
||||
rprint(
|
||||
f" [red]✗ {message.get('phase_name', '?')}[/red]: {message.get('error', '')}"
|
||||
f" [red]FAIL {message.get('phase_name', '?')}[/red]: {message.get('error', '')}"
|
||||
)
|
||||
elif etype == "debate_started":
|
||||
rprint(
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ def _render_risk_guard_suggestions(suggestions: list) -> None:
|
|||
return
|
||||
|
||||
rprint(
|
||||
"[bold yellow]⚠ 以下为自动生成的风险守卫建议,"
|
||||
"[bold yellow]WARN 以下为自动生成的风险守卫建议,"
|
||||
"必须人工审查后手动编辑 YAML 应用,不会自动生效。[/bold yellow]\n"
|
||||
)
|
||||
table = Table(title="Risk Guard Suggestions (待人工审查)")
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class ExecutionPlan:
|
|||
|
||||
def to_readable(self) -> str:
|
||||
"""序列化为可读格式,用于人工确认"""
|
||||
lines = [f"📋 执行计划 [{self.plan_id}]", f"目标: {self.goal}", ""]
|
||||
lines = [f"[Plan] 执行计划 [{self.plan_id}]", f"目标: {self.goal}", ""]
|
||||
|
||||
for group_idx, group in enumerate(self.parallel_groups):
|
||||
lines.append(f"── 并行组 {group_idx + 1} ──")
|
||||
|
|
@ -119,7 +119,7 @@ class ExecutionPlan:
|
|||
lines.append("")
|
||||
|
||||
if self.skill_gaps:
|
||||
lines.append("⚠️ 能力缺口:")
|
||||
lines.append("[WARN] 能力缺口:")
|
||||
for gap in self.skill_gaps:
|
||||
lines.append(f" - {gap.step_name}: 缺少 '{gap.required_skill}' ({gap.level.value})")
|
||||
if gap.suggestion:
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class ExpertTemplateRegistry:
|
|||
bound_skills:
|
||||
- data_query
|
||||
- chart_gen
|
||||
avatar: "📊"
|
||||
avatar: "首字符"
|
||||
color: "#52c41a"
|
||||
is_lead: false
|
||||
|
||||
|
|
|
|||
|
|
@ -106,8 +106,7 @@ html, body, #app {
|
|||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Sans Emoji';
|
||||
'Noto Sans', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: var(--text-primary, #1a1a1a);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* History
|
||||
* -------
|
||||
* Originally, the Bitable file ``icon`` field stored emoji glyphs (📋 📊 📝
|
||||
* Originally, the Bitable file ``icon`` field stored emoji glyphs (table, chart, note
|
||||
* etc.). Those look fine in isolation but clash with the project's line-icon
|
||||
* aesthetic everywhere else (Ant Design Vue ``Outlined`` family — Tabs,
|
||||
* Sidebar, Tool call cards all use stroke-based 1.5px icons). The mixed
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { computed, type Component } from 'vue'
|
||||
import { AuditOutlined } from '@ant-design/icons-vue'
|
||||
import type { IChatMessage } from '@/api/types'
|
||||
import UserBubble from '@/components/chat/messages/UserBubble.vue'
|
||||
import AssistantText from '@/components/chat/messages/AssistantText.vue'
|
||||
|
|
@ -36,7 +37,7 @@ export type MessageViewType =
|
|||
export interface MessageShellMeta {
|
||||
name: string
|
||||
meta?: string
|
||||
avatar?: string
|
||||
avatar?: string | Component
|
||||
color?: string
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +210,7 @@ export function useMessageRenderer(message: IChatMessage) {
|
|||
type,
|
||||
shell: {
|
||||
name: '辩论',
|
||||
avatar: '⚖',
|
||||
avatar: AuditOutlined,
|
||||
color: '#722ed1',
|
||||
meta: message.debate_topic || '',
|
||||
},
|
||||
|
|
@ -268,7 +269,7 @@ export function useMessageRenderer(message: IChatMessage) {
|
|||
type,
|
||||
shell: {
|
||||
name: '辩论裁决',
|
||||
avatar: '⚖',
|
||||
avatar: AuditOutlined,
|
||||
color: '#fa8c16',
|
||||
meta: decisionLabels[decision] || decision,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<div class="board-banner-card__bar" />
|
||||
<div class="board-banner-card__body">
|
||||
<div class="board-banner-card__title">
|
||||
<span class="board-banner-card__icon">🏛️</span>
|
||||
<BankOutlined class="board-banner-card__icon" />
|
||||
<span>私董会 — {{ topic }}</span>
|
||||
</div>
|
||||
<div class="board-banner-card__experts">
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { BankOutlined } from '@ant-design/icons-vue'
|
||||
import type { IBoardExpert } from '@/api/types'
|
||||
|
||||
interface Props {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="debate-banner">
|
||||
<div class="debate-banner__icon">⚖</div>
|
||||
<div class="debate-banner__icon"><AuditOutlined /></div>
|
||||
<div class="debate-banner__body">
|
||||
<div class="debate-banner__title">辩论开始</div>
|
||||
<div class="debate-banner__topic">{{ topic }}</div>
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { AuditOutlined } from '@ant-design/icons-vue'
|
||||
import AssistantText from './AssistantText.vue'
|
||||
import type { IChatMessage } from '@/api/types'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="debate-conclusion" :class="`debate-conclusion--${decision}`">
|
||||
<div class="debate-conclusion__header">
|
||||
<span class="debate-conclusion__icon">{{ decisionIcon }}</span>
|
||||
<span class="debate-conclusion__icon"><component :is="decisionIcon" /></span>
|
||||
<span class="debate-conclusion__decision">{{ decisionLabel }}</span>
|
||||
</div>
|
||||
<div class="debate-conclusion__body">
|
||||
|
|
@ -15,7 +15,8 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, type Component } from 'vue'
|
||||
import { CheckOutlined, SwapOutlined, MinusOutlined, QuestionOutlined } from '@ant-design/icons-vue'
|
||||
import AssistantText from './AssistantText.vue'
|
||||
import type { IChatMessage } from '@/api/types'
|
||||
|
||||
|
|
@ -31,15 +32,15 @@ const decisionLabels: Record<string, string> = {
|
|||
shelve: '搁置',
|
||||
inconclusive: '未决',
|
||||
}
|
||||
const decisionIcons: Record<string, string> = {
|
||||
adopt: '✓',
|
||||
compromise: '⇄',
|
||||
shelve: '○',
|
||||
inconclusive: '?',
|
||||
const decisionIcons: Record<string, Component> = {
|
||||
adopt: CheckOutlined,
|
||||
compromise: SwapOutlined,
|
||||
shelve: MinusOutlined,
|
||||
inconclusive: QuestionOutlined,
|
||||
}
|
||||
|
||||
const decisionLabel = computed(() => decisionLabels[props.decision] || props.decision)
|
||||
const decisionIcon = computed(() => decisionIcons[props.decision] || '?')
|
||||
const decisionIcon = computed(() => decisionIcons[props.decision] || QuestionOutlined)
|
||||
|
||||
const textMessage = computed<IChatMessage>(() => ({
|
||||
id: 'debate-conclusion',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
class="message-shell__custom-avatar"
|
||||
:style="{ backgroundColor: color || '#1a1a1a' }"
|
||||
>
|
||||
{{ avatar || (name ? name.charAt(0).toUpperCase() : '?') }}
|
||||
<component :is="avatar" v-if="typeof avatar !== 'string' && avatar" />
|
||||
<template v-else>{{ avatar || (name ? name.charAt(0).toUpperCase() : '?') }}</template>
|
||||
</div>
|
||||
<a-avatar v-else-if="role === 'assistant'" :size="28" class="message-shell__avatar--assistant">
|
||||
<template #icon><RobotOutlined /></template>
|
||||
|
|
@ -43,12 +44,13 @@
|
|||
<script setup lang="ts">
|
||||
import { Avatar as AAvatar } from 'ant-design-vue'
|
||||
import { RobotOutlined, UserOutlined } from '@ant-design/icons-vue'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
interface Props {
|
||||
role: 'user' | 'assistant'
|
||||
name?: string
|
||||
meta?: string
|
||||
avatar?: string
|
||||
avatar?: string | Component
|
||||
color?: string
|
||||
/** U4 R10: 专家身份 badge 名称 — 存在时渲染为彩色 badge 替代普通 name 文本 */
|
||||
expertName?: string
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
* Avatar glyph shown inside the 22x22 pill in the card header.
|
||||
*
|
||||
* 2026-07-01: switched the default from the business-person emoji
|
||||
* (``🧑💼``) to a clean initials-based fallback to keep the
|
||||
* (business-person glyph) to a clean initials-based fallback to keep the
|
||||
* visual language consistent with the rest of the app (Ant Design
|
||||
* Vue outlined icons + monospace text). Backend payloads that supply
|
||||
* a non-empty ``leadAvatar`` string (e.g. expert avatars streamed over
|
||||
|
|
@ -124,10 +124,10 @@ function statusLabel(status: string): string {
|
|||
|
||||
/**
|
||||
* Phase status dot icon. 2026-07-01: replaced the ad-hoc Unicode
|
||||
* glyphs (``○ ● ✓ ✕``) with the matching Ant Design Vue components
|
||||
* glyphs (hollow dot, solid dot, check, cross) with the matching Ant Design Vue components
|
||||
* so the dot matches the line-icon family used in the rest of the
|
||||
* app. The dotted ``MinusOutlined`` for pending is the closest
|
||||
* outlined equivalent of the old hollow ``○``; the spinning /
|
||||
* outlined equivalent of the old hollow dot; the spinning /
|
||||
* pulse feel of ``in_progress`` is left to the existing colour
|
||||
* treatment in the scoped CSS below.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
/>
|
||||
<div v-else-if="commandBubble" class="user-bubble__command" :class="`user-bubble__command--${commandBubble.kind}`">
|
||||
<div class="user-bubble__command-header">
|
||||
<span class="user-bubble__command-icon">{{ commandBubble.icon }}</span>
|
||||
<span class="user-bubble__command-icon"><component :is="commandBubble.icon" /></span>
|
||||
<span class="user-bubble__command-label">{{ commandBubble.label }}</span>
|
||||
<span class="user-bubble__command-sep">·</span>
|
||||
<span class="user-bubble__command-topic">{{ commandBubble.topic }}</span>
|
||||
|
|
@ -92,8 +92,8 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons-vue'
|
||||
import { ref, computed, onMounted, onUnmounted, type Component } from 'vue'
|
||||
import { CopyOutlined, DeleteOutlined, EditOutlined, BankOutlined, TeamOutlined } from '@ant-design/icons-vue'
|
||||
import FileAttachment from './FileAttachment.vue'
|
||||
import { useChatStore, nextMessageIsAssistant } from '@/stores/chatStore'
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ const COMMAND_RE = /^@(board|team)(?::([^\s]+))?\s+([\s\S]+)$/
|
|||
|
||||
interface CommandBubble {
|
||||
kind: 'board' | 'team'
|
||||
icon: string
|
||||
icon: Component
|
||||
label: string
|
||||
topic: string
|
||||
experts: string[]
|
||||
|
|
@ -140,7 +140,7 @@ const commandBubble = computed<CommandBubble | null>(() => {
|
|||
: []
|
||||
return {
|
||||
kind,
|
||||
icon: kind === 'board' ? '🏛️' : '👥',
|
||||
icon: kind === 'board' ? BankOutlined : TeamOutlined,
|
||||
label: kind === 'board' ? '私董会' : '专家团',
|
||||
topic: topic || rest,
|
||||
experts,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<span
|
||||
:class="['command-history__exit-code', record.exit_code === 0 ? 'command-history__exit-code--success' : 'command-history__exit-code--error']"
|
||||
>
|
||||
{{ record.exit_code === 0 ? '✓' : '✗' }}
|
||||
{{ record.exit_code === 0 ? 'OK' : 'FAIL' }}
|
||||
</span>
|
||||
<span class="command-history__command">{{ record.command }}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1146,7 +1146,7 @@ export function dispatchWsEvent(
|
|||
const startMsg: IChatMessage = {
|
||||
id: generateId(),
|
||||
role: "assistant",
|
||||
content: `🏛️ 私董会开始:${boardData.topic}`,
|
||||
content: `私董会开始:${boardData.topic}`,
|
||||
timestamp: new Date().toISOString(),
|
||||
status: "completed",
|
||||
message_type: "board_started",
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ export const useTerminalStore = defineStore('terminal', () => {
|
|||
command: data.command as string,
|
||||
reason: (data.reason as string) || '需要用户确认',
|
||||
}
|
||||
appendOutput(`\x1b[33m⚠ 需要确认: ${data.command}\x1b[0m`)
|
||||
appendOutput(`\x1b[33m[WARN] 需要确认: ${data.command}\x1b[0m`)
|
||||
appendOutput(`\x1b[33m 原因: ${data.reason}\x1b[0m`)
|
||||
break
|
||||
// ── Server terminal: admin approval ────────────────────────
|
||||
|
|
@ -259,20 +259,20 @@ export const useTerminalStore = defineStore('terminal', () => {
|
|||
reason: (data.reason as string) || '需要管理员审批',
|
||||
expires_in: (data.expires_in as number) || 300,
|
||||
}
|
||||
appendOutput(`\x1b[33m⏳ 等待管理员审批: ${data.command}\x1b[0m`)
|
||||
appendOutput(`\x1b[33m[PENDING] 等待管理员审批: ${data.command}\x1b[0m`)
|
||||
appendOutput(`\x1b[33m 原因: ${data.reason}\x1b[0m`)
|
||||
break
|
||||
case 'approval_approved':
|
||||
appendOutput(`\x1b[32m✓ 审批已通过,正在执行...\x1b[0m`)
|
||||
appendOutput(`\x1b[32m[OK] 审批已通过,正在执行...\x1b[0m`)
|
||||
pendingApproval.value = null
|
||||
break
|
||||
case 'approval_rejected':
|
||||
appendOutput(`\x1b[31m✗ 审批被拒绝: ${data.reason || ''}\x1b[0m`)
|
||||
appendOutput(`\x1b[31m[REJECTED] 审批被拒绝: ${data.reason || ''}\x1b[0m`)
|
||||
pendingApproval.value = null
|
||||
isExecuting.value = false
|
||||
break
|
||||
case 'approval_expired':
|
||||
appendOutput(`\x1b[31m✗ 审批超时未响应\x1b[0m`)
|
||||
appendOutput(`\x1b[31m[TIMEOUT] 审批超时未响应\x1b[0m`)
|
||||
pendingApproval.value = null
|
||||
isExecuting.value = false
|
||||
break
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ const renaming = ref(false)
|
|||
|
||||
/**
|
||||
* ``icon`` accepts any string here because legacy DB rows may still
|
||||
* contain the old emoji values (``📋`` etc.). The select options only
|
||||
* contain the old emoji values (clipboard-emoji etc.). The select options only
|
||||
* show the new Bitable icon keys; the resolver maps unknown values to
|
||||
* the default ``table`` icon at render time.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ function makeExpert(overrides: Partial<IExpertInfo> = {}): IExpertInfo {
|
|||
id: 'e1',
|
||||
name: '专家A',
|
||||
persona: '资深架构师',
|
||||
avatar: '🤖',
|
||||
avatar: 'A',
|
||||
color: '#3b82f6',
|
||||
is_lead: false,
|
||||
bound_skills: ['react'],
|
||||
|
|
@ -120,7 +120,7 @@ function makeBoardState(overrides: Partial<BoardState> = {}): BoardState {
|
|||
experts: [
|
||||
{
|
||||
name: '主持人',
|
||||
avatar: '🎯',
|
||||
avatar: 'T',
|
||||
color: '#a855f7',
|
||||
is_moderator: true,
|
||||
persona: '引导讨论',
|
||||
|
|
@ -177,8 +177,8 @@ describe('StickyModeHeader (U2)', () => {
|
|||
teamState.value = makeTeamState({
|
||||
task_description: '实现用户登录功能',
|
||||
experts: [
|
||||
makeExpert({ id: 'e1', name: '专家A', avatar: '🤖', is_lead: true }),
|
||||
makeExpert({ id: 'e2', name: '专家B', avatar: '🐱' }),
|
||||
makeExpert({ id: 'e1', name: '专家A', avatar: 'A', is_lead: true }),
|
||||
makeExpert({ id: 'e2', name: '专家B', avatar: 'C' }),
|
||||
],
|
||||
})
|
||||
const { container, unmount } = mountStickyHeader()
|
||||
|
|
@ -196,7 +196,7 @@ describe('StickyModeHeader (U2)', () => {
|
|||
|
||||
const avatars = container.querySelectorAll('.sticky-mode-header__avatar')
|
||||
expect(avatars.length).toBe(2)
|
||||
expect(avatars[0].textContent).toBe('🤖')
|
||||
expect(avatars[0].textContent).toBe('A')
|
||||
|
||||
unmount()
|
||||
})
|
||||
|
|
@ -229,14 +229,14 @@ describe('StickyModeHeader (U2)', () => {
|
|||
experts: [
|
||||
{
|
||||
name: '主持人',
|
||||
avatar: '🎯',
|
||||
avatar: 'T',
|
||||
color: '#a855f7',
|
||||
is_moderator: true,
|
||||
persona: '引导讨论',
|
||||
},
|
||||
{
|
||||
name: '专家1',
|
||||
avatar: '💡',
|
||||
avatar: 'I',
|
||||
color: '#3b82f6',
|
||||
is_moderator: false,
|
||||
persona: '市场分析',
|
||||
|
|
@ -285,7 +285,7 @@ describe('StickyModeHeader (U2)', () => {
|
|||
makeExpert({
|
||||
id: 'e1',
|
||||
name: '架构师',
|
||||
avatar: '🤖',
|
||||
avatar: 'A',
|
||||
persona: '专注系统设计',
|
||||
bound_skills: ['react', 'vue'],
|
||||
is_lead: true,
|
||||
|
|
@ -435,7 +435,7 @@ describe('StickyModeHeader (U2)', () => {
|
|||
experts: [
|
||||
{
|
||||
name: '主持人',
|
||||
avatar: '🎯',
|
||||
avatar: 'T',
|
||||
color: '#a855f7',
|
||||
is_moderator: true,
|
||||
persona: '引导者',
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function boardStartedMsg(round: number = 0): IChatMessage {
|
|||
return {
|
||||
id: 'msg-start',
|
||||
role: 'assistant',
|
||||
content: '🏛️ 私董会开始:AI 未来',
|
||||
content: '私董会开始:AI 未来',
|
||||
timestamp: '2026-07-01T10:00:00Z',
|
||||
status: 'completed',
|
||||
message_type: 'board_started',
|
||||
|
|
@ -30,14 +30,14 @@ function boardStartedMsg(round: number = 0): IChatMessage {
|
|||
experts: [
|
||||
{
|
||||
name: 'Alice',
|
||||
avatar: '🦊',
|
||||
avatar: 'F',
|
||||
color: '#7a5af8',
|
||||
is_moderator: true,
|
||||
persona: '主持人',
|
||||
},
|
||||
{
|
||||
name: 'Bob',
|
||||
avatar: '🐼',
|
||||
avatar: 'P',
|
||||
color: '#22c55e',
|
||||
is_moderator: false,
|
||||
persona: '工程师',
|
||||
|
|
|
|||
|
|
@ -728,7 +728,7 @@ describe('dispatchWsEvent', () => {
|
|||
experts: [
|
||||
{
|
||||
name: 'Mod',
|
||||
avatar: '🦊',
|
||||
avatar: 'F',
|
||||
color: '#f00',
|
||||
is_moderator: true,
|
||||
persona: 'moderator',
|
||||
|
|
|
|||
|
|
@ -301,9 +301,9 @@ async def _execute_board_meeting(
|
|||
# proper board_speech card instead of a plain assistant bubble.
|
||||
experts_data = event_data.get("experts")
|
||||
board_started_text = (
|
||||
f"🏛️ 私董会开始:{event_data.get('topic', '')}"
|
||||
f"私董会开始:{event_data.get('topic', '')}"
|
||||
if event_data.get("topic")
|
||||
else "🏛️ 私董会开始"
|
||||
else "私董会开始"
|
||||
)
|
||||
persistable: dict[str, tuple[str, str, dict[str, object] | None]] = {
|
||||
"board_started": (
|
||||
|
|
|
|||
|
|
@ -1331,8 +1331,8 @@ class MetricsReporter:
|
|||
if report.overfitting_results:
|
||||
lines.append("── 过拟合分析 ────────────────────────────────────────────")
|
||||
for r in report.overfitting_results:
|
||||
status = "⚠ 过拟合" if r.is_overfitted else "✓ 正常"
|
||||
orig_label = "✓" if r.original_correct else "✗"
|
||||
status = "WARN 过拟合" if r.is_overfitted else "OK 正常"
|
||||
orig_label = "OK" if r.original_correct else "FAIL"
|
||||
lines.append(
|
||||
f" [{status}] {r.benchmark_id}: "
|
||||
f"原始输入={orig_label}, "
|
||||
|
|
@ -1386,8 +1386,8 @@ class MetricsReporter:
|
|||
if report.weaknesses:
|
||||
lines.append("── 智能化短板识别 ────────────────────────────────────────")
|
||||
for w in report.weaknesses:
|
||||
icon = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "🟢"}.get(
|
||||
w.severity, "⚪"
|
||||
icon = {"critical": "[C]", "high": "[H]", "medium": "[M]", "low": "[L]"}.get(
|
||||
w.severity, "[?]"
|
||||
)
|
||||
severity_label = {
|
||||
"critical": "严重",
|
||||
|
|
@ -1438,8 +1438,8 @@ class MetricsReporter:
|
|||
lines.append(f" │ 总体策略: {plan.overall_strategy}")
|
||||
lines.append(" │")
|
||||
for action in plan.actions:
|
||||
priority_icon = {"P0": "🔴", "P1": "🟠", "P2": "🟡", "P3": "🟢"}.get(
|
||||
action.priority, "⚪"
|
||||
priority_icon = {"P0": "[P0]", "P1": "[P1]", "P2": "[P2]", "P3": "[P3]"}.get(
|
||||
action.priority, "[?]"
|
||||
)
|
||||
effort_label = {"small": "小", "medium": "中", "large": "大"}.get(
|
||||
action.effort, action.effort
|
||||
|
|
|
|||
|
|
@ -1434,7 +1434,7 @@ def _generate_text_report(json_report: dict[str, Any]) -> str:
|
|||
for dim, score in json_report["dimension_scores"].items():
|
||||
name = dim_names.get(dim, dim)
|
||||
detail = json_report["dimension_details"][dim]
|
||||
status = "✓" if score == 100 else "✗"
|
||||
status = "OK" if score == 100 else "FAIL"
|
||||
lines.append(f" {status} {name}: {score:.1f}% ({detail['passed']}/{detail['total']})")
|
||||
lines.append("")
|
||||
|
||||
|
|
@ -1446,7 +1446,7 @@ def _generate_text_report(json_report: dict[str, Any]) -> str:
|
|||
name = dim_names.get(dim, dim)
|
||||
lines.append(f"\n[{name}]")
|
||||
for case in details["cases"]:
|
||||
status = "✓" if case["passed"] else "✗"
|
||||
status = "OK" if case["passed"] else "FAIL"
|
||||
lines.append(f" {status} {case['case_id']}")
|
||||
lines.append("")
|
||||
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ class TestRequestPreprocessorMetrics:
|
|||
print(f"Total: {total}, Correct: {correct}, Accuracy: {accuracy:.1f}%")
|
||||
print(f"{'-'*60}")
|
||||
for r in results:
|
||||
status = "✓" if r["correct"] else "✗"
|
||||
status = "OK" if r["correct"] else "FAIL"
|
||||
print(f" {status} {r['id']}: '{r['input']}' → {r['actual']} (expected {r['expected']})")
|
||||
print(f"{'='*60}")
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ async def test_create_file_returns_with_defaults(bitable_service) -> None:
|
|||
file = await bitable_service.create_file(name="销售管线", owner_user_id="u1")
|
||||
assert file.id
|
||||
assert file.name == "销售管线"
|
||||
assert file.icon == "📋"
|
||||
assert file.icon == "table"
|
||||
assert file.description == ""
|
||||
assert file.owner_user_id == "u1"
|
||||
assert file.created_at is not None
|
||||
|
|
@ -75,11 +75,11 @@ async def test_update_file_changes_name_and_icon(bitable_service) -> None:
|
|||
"""update_file patches name/icon/description."""
|
||||
file = await bitable_service.create_file(name="Old", owner_user_id="u1")
|
||||
updated = await bitable_service.update_file(
|
||||
file.id, name="New", icon="🚀", description="updated desc"
|
||||
file.id, name="New", icon="rocket", description="updated desc"
|
||||
)
|
||||
assert updated is not None
|
||||
assert updated.name == "New"
|
||||
assert updated.icon == "🚀"
|
||||
assert updated.icon == "rocket"
|
||||
assert updated.description == "updated desc"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ def _make_expert_config(
|
|||
is_lead=is_lead,
|
||||
task_mode="llm_generate",
|
||||
prompt={"identity": name},
|
||||
avatar="🎭",
|
||||
avatar="T",
|
||||
color="#FF0000",
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ def _make_expert_configs(count: int = 3) -> list[ExpertConfig]:
|
|||
is_lead=(i == 0),
|
||||
task_mode="llm_generate",
|
||||
prompt={"identity": f"Expert {i}"},
|
||||
avatar="🎭",
|
||||
avatar="T",
|
||||
color=f"#FF{i:02d}000",
|
||||
))
|
||||
return configs
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class TestExpertConfig:
|
|||
thinking_style="逻辑推理",
|
||||
collaboration_strategy="cooperative",
|
||||
bound_skills=["data_query", "chart_gen"],
|
||||
avatar="📊",
|
||||
avatar="C",
|
||||
color="#52c41a",
|
||||
is_lead=True,
|
||||
task_mode="llm_generate",
|
||||
|
|
@ -67,7 +67,7 @@ class TestExpertConfig:
|
|||
assert config.thinking_style == "逻辑推理"
|
||||
assert config.collaboration_strategy == "cooperative"
|
||||
assert config.bound_skills == ["data_query", "chart_gen"]
|
||||
assert config.avatar == "📊"
|
||||
assert config.avatar == "C"
|
||||
assert config.color == "#52c41a"
|
||||
assert config.is_lead is True
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ class TestExpertConfig:
|
|||
thinking_style="创造性思维",
|
||||
collaboration_strategy="lead",
|
||||
bound_skills=["skill_a", "skill_b"],
|
||||
avatar="🧠",
|
||||
avatar="B",
|
||||
color="#ff4d4f",
|
||||
is_lead=True,
|
||||
task_mode="llm_generate",
|
||||
|
|
@ -193,7 +193,7 @@ class TestExpertTemplate:
|
|||
thinking_style="分析型",
|
||||
collaboration_strategy="cooperative",
|
||||
bound_skills=["skill_x"],
|
||||
avatar="🔬",
|
||||
avatar="M",
|
||||
color="#722ed1",
|
||||
is_lead=False,
|
||||
task_mode="llm_generate",
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ class TestExpertGetCapabilitiesSummary:
|
|||
bound_skills=["data_query", "chart_gen"],
|
||||
is_lead=True,
|
||||
color="#52c41a",
|
||||
avatar="📊",
|
||||
avatar="C",
|
||||
)
|
||||
agent = _make_mock_agent()
|
||||
|
||||
|
|
@ -373,7 +373,7 @@ class TestExpertGetCapabilitiesSummary:
|
|||
"bound_skills": ["data_query", "chart_gen"],
|
||||
"is_lead": True,
|
||||
"color": "#52c41a",
|
||||
"avatar": "📊",
|
||||
"avatar": "C",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ class TestExpertTemplateRegistry:
|
|||
"thinking_style": "结构化思维",
|
||||
"collaboration_strategy": "cooperative",
|
||||
"bound_skills": ["skill_a", "skill_b"],
|
||||
"avatar": "🤖",
|
||||
"avatar": "A",
|
||||
"color": "#fa8c16",
|
||||
"is_lead": False,
|
||||
"task_mode": "llm_generate",
|
||||
|
|
@ -167,7 +167,7 @@ class TestExpertTemplateRegistry:
|
|||
assert template.config.persona == "YAML 专家"
|
||||
assert template.config.thinking_style == "结构化思维"
|
||||
assert template.config.bound_skills == ["skill_a", "skill_b"]
|
||||
assert template.config.avatar == "🤖"
|
||||
assert template.config.avatar == "A"
|
||||
assert template.config.color == "#fa8c16"
|
||||
# 同时注册到 registry
|
||||
assert registry.get("yaml_expert") is template
|
||||
|
|
|
|||
Loading…
Reference in New Issue