diff --git a/config.py b/config.py index d0f285b..f5df2f5 100644 --- a/config.py +++ b/config.py @@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.121" +SYSTEM_VERSION = "V10.122" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/scripts/check_observability_ui.py b/scripts/check_observability_ui.py index 51de3c3..8cb0cfd 100644 --- a/scripts/check_observability_ui.py +++ b/scripts/check_observability_ui.py @@ -25,7 +25,6 @@ WEB_CSS_PATH = Path("web/static/css/observability-system.css") SHELL_PATH = Path("templates/components/_ewoooc_shell.html") BASE_PATH = Path("templates/ewoooc_base.html") ROUTE_PATH = Path("routes/admin_observability_routes.py") -OVERVIEW_PATH = Path("templates/admin/observability_overview.html") @dataclass(frozen=True) @@ -79,6 +78,7 @@ REQUIRED_CSS_SNIPPETS = [ "--obs-title-size", "--obs-value-size", "--obs-matrix-dot", + "v3.11 V2 workbench normalization", "v3.10 terminal dot-matrix layer", ".momo-observability-mode", ".obs-chart-frame", @@ -137,16 +137,55 @@ FORBIDDEN_BASE_PATTERNS = [ ), ] -FORBIDDEN_OVERVIEW_COPY = [ +FORBIDDEN_OBSERVABILITY_COPY = [ "AI Observability Command Room", + "Business Intelligence", + "Agent Command Matrix", + "AI Traffic Control", + "AI Cost Governance", + "RAG Recall Radar", + "Quality Diagnostics", + "PPT Visual QA Pipeline", + "RAG Promotion Gate", + "Infrastructure Lifeline", "Risk Signals", "AI Calls / 24h", "Host Cascade", "AI Runtime", "Learning Loop", - ">Command<", - ">Runtime<", - ">Quality<", + "Total Calls", + "Ollama Share", + "Paid Cost", + "Provider Split", + "Caller Orchestration", + "Model Economics", + "Recent Calls", + "Budget Ratio", + "Budget Lines", + "Provider Mix", + "Burn Rate", + "Saved Call", + "Query Stream", + "Caller Quality", + "Worst Avg", + "RAG Scores", + "Caller Feedback", + "RAG Feedback", + "Learning Pool", + "Root Cause", + "Action Outcomes", + "Audit History", + "Generated Files", + "30d Audit Mix", + "Failure Hotspots", + "Awaiting Review", + "Review Queue", + "Ollama Down", + "AIOps Open", + "Heal Rate", + "Cost Throttle", + "MCP Workload", + "Operation Ollama-First", "RAG hits", "Cache hits", "30d episodes", @@ -258,18 +297,20 @@ def scan_base_topbar() -> list[str]: return findings -def scan_overview_copy() -> list[str]: - path = ROOT / OVERVIEW_PATH - if not path.exists(): - return [f"{OVERVIEW_PATH}: missing required overview page"] - - text = path.read_text(encoding="utf-8") +def scan_observability_copy() -> list[str]: findings: list[str] = [] - for snippet in FORBIDDEN_OVERVIEW_COPY: - if snippet in text: - findings.append( - f"{OVERVIEW_PATH}: legacy English overview copy `{snippet}` must be localized to the V2 workbench language" - ) + for template_path in TEMPLATE_PATHS: + path = ROOT / Path(template_path) + if not path.exists(): + findings.append(f"{template_path}: missing required observability page") + continue + + text = path.read_text(encoding="utf-8") + for snippet in FORBIDDEN_OBSERVABILITY_COPY: + if snippet in text: + findings.append( + f"{template_path}: legacy English observability copy `{snippet}` must be localized to the V2 workbench language" + ) return findings @@ -357,7 +398,7 @@ def main() -> int: findings.extend(scan_css()) findings.extend(scan_shell()) findings.extend(scan_base_topbar()) - findings.extend(scan_overview_copy()) + findings.extend(scan_observability_copy()) findings.extend(scan_nav_contract()) if findings: @@ -371,7 +412,7 @@ def main() -> int: print(f"- css guardrails checked: {len(REQUIRED_CSS_SNIPPETS)}") print(f"- sidebar/nav guardrails checked: {len(REQUIRED_SHELL_SNIPPETS)}") print(f"- base/topbar guardrails checked: {len(REQUIRED_BASE_SNIPPETS) + len(FORBIDDEN_BASE_PATTERNS)}") - print(f"- overview copy guardrails checked: {len(FORBIDDEN_OVERVIEW_COPY)}") + print(f"- observability copy guardrails checked: {len(TEMPLATE_PATHS)} pages × {len(FORBIDDEN_OBSERVABILITY_COPY)} terms") print(f"- nav contract checked: {len(OBSERVABILITY_PAGES)} pages") return 0 diff --git a/scripts/observability_contract.py b/scripts/observability_contract.py index 9099f90..0abb514 100644 --- a/scripts/observability_contract.py +++ b/scripts/observability_contract.py @@ -65,7 +65,7 @@ OBSERVABILITY_PAGES = ( "/observability/ai_calls", "AI 呼叫", "AI 呼叫", - ("AI 呼叫", "Provider", "RAG"), + ("AI 呼叫", "供應商", "RAG"), ), ObservabilityPage( "templates/admin/budget.html", @@ -73,7 +73,7 @@ OBSERVABILITY_PAGES = ( "/observability/budget", "預算控管", "預算", - ("預算控管", "force", "throttle"), + ("預算控管", "預算線", "節流"), ), ObservabilityPage( "templates/admin/promotion_review.html", @@ -81,7 +81,7 @@ OBSERVABILITY_PAGES = ( "/observability/promotion_review", "RAG 晉升審核", "晉升", - ("RAG 晉升審核", "Promotion", "ai_insights"), + ("RAG 晉升審核", "晉升", "ai_insights"), ), ObservabilityPage( "templates/admin/rag_queries.html", @@ -89,7 +89,7 @@ OBSERVABILITY_PAGES = ( "/observability/rag_queries", "RAG 召回詳情", "RAG", - ("RAG 召回詳情", "最近 50", "hits"), + ("RAG 召回詳情", "最近 50", "命中"), ), ObservabilityPage( "templates/admin/quality_trend.html", @@ -97,7 +97,7 @@ OBSERVABILITY_PAGES = ( "/observability/quality_trend", "反饋趨勢", "品質", - ("反饋趨勢", "Caller", "蒸餾"), + ("反饋趨勢", "呼叫端", "蒸餾"), ), ObservabilityPage( "templates/admin/ppt_audit_history.html", @@ -105,7 +105,7 @@ OBSERVABILITY_PAGES = ( "/observability/ppt_audit_history", "PPT 視覺審核", "PPT", - ("PPT 視覺審核", "AiderHeal", "audit"), + ("PPT 視覺審核", "AiderHeal", "審核"), ), ) diff --git a/static/css/observability-system.css b/static/css/observability-system.css index 5eba739..fac4482 100644 --- a/static/css/observability-system.css +++ b/static/css/observability-system.css @@ -2379,6 +2379,136 @@ } } +/* v3.11 V2 workbench normalization: unify legacy observability page skins before the terminal dot layer. */ +.momo-observability-mode :is( + .obs-hero, + .agent-hero, + .biz-command, + .runtime-hero, + .calls-hero, + .gov-hero, + .gate-hero, + .rag-hero, + .qa-hero, + .quality-hero, + .ppt-hero +) { + border-color: var(--obs-line) !important; + border-radius: var(--momo-radius-lg, 8px) !important; + background-color: var(--momo-bg-surface, #faf7f0) !important; + background-image: var(--obs-matrix-dot) !important; + background-size: var(--obs-matrix-size) !important; + box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06)) !important; +} + +.momo-observability-mode :is( + .obs-panel, + .agent-panel, + .biz-panel, + .runtime-panel, + .calls-panel, + .gov-panel, + .gate-panel, + .rag-panel, + .qa-panel, + .quality-panel, + .ppt-panel, + .obs-signal, + .agent-signal, + .biz-signal, + .runtime-signal, + .calls-signal, + .gov-signal, + .gate-signal, + .rag-signal, + .qa-signal, + .quality-signal, + .ppt-signal, + .agent-card, + .rec-card, + .host-lane, + .runtime-mini, + .calls-mini, + .gov-mini, + .gate-mini, + .quality-mini, + .ppt-mini, + .strategy-card, + .episode-card, + .similar-box, + .fix-card, + .root-card, + .caller-card, + .biz-filter-card, + .biz-alert-strip, + .biz-chart-shell, + .biz-strategy-card, + .biz-mini-metric, + .biz-decision-card, + .obs-route-card +) { + border-color: var(--obs-line) !important; + border-radius: var(--momo-radius-lg, 8px) !important; + background-color: var(--momo-bg-elevated, #fdfaf3) !important; + background-image: var(--obs-matrix-dot-soft) !important; + background-size: var(--obs-matrix-size) !important; + box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06)) !important; +} + +.momo-observability-mode :is( + .obs-kicker, + .agent-kicker, + .biz-kicker, + .runtime-kicker, + .calls-kicker, + .gov-kicker, + .gate-kicker, + .rag-kicker, + .qa-kicker, + .quality-kicker, + .ppt-kicker, + .obs-signal-label, + .agent-label, + .biz-signal .label, + .runtime-label, + .calls-label, + .gov-label, + .gate-label, + .rag-label, + .qa-label, + .quality-label, + .ppt-label, + .obs-section-eyebrow +) { + font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace) !important; + font-size: var(--momo-text-label, 0.6875rem) !important; + letter-spacing: 0.06em !important; + text-transform: none !important; + color: color-mix(in srgb, var(--obs-accent) 76%, var(--obs-muted)) !important; +} + +.momo-observability-mode :is( + .btn, + .badge, + .obs-pill, + .biz-badge, + [class$="-pill"], + .model-chip +) { + border-radius: var(--momo-radius-lg, 8px) !important; +} + +.momo-observability-mode :is( + .agent-meter, + .caller-meter, + .progress, + .progress-bar, + .obs-progress-xs, + .obs-progress-sm +) { + border-radius: var(--momo-radius-sm, 3px) !important; +} + /* v3.10 terminal dot-matrix layer: this must stay at EOF to win the cascade. */ .momo-observability-mode { --obs-matrix-dot: radial-gradient(color-mix(in srgb, var(--obs-accent) 14%, transparent) 0.85px, transparent 0.95px); diff --git a/templates/admin/agent_orchestration.html b/templates/admin/agent_orchestration.html index a4fe229..127a2da 100644 --- a/templates/admin/agent_orchestration.html +++ b/templates/admin/agent_orchestration.html @@ -9,20 +9,20 @@
-
Agent Command Matrix · {{ hours }}h Window

Agent 指揮矩陣

這頁回答 AI 中樞如何分工:誰在用 Ollama、誰還在吃付費 LLM、哪些 Agent 有 RAG 命中、哪些工作流已經接上 MCP。這不是列表,是指揮官視角。

{% if overall %}
Total Calls
{{ "{:,}".format(overall.total_calls) }}{{ "{:,}".format(overall.total_tokens) }} tokens
Ollama Share
{{ "%.0f"|format(overall.local_pct) }}%{{ "{:,}".format(overall.local_calls) }} local calls
Paid Cost
${{ "%.2f"|format(overall.total_cost) }}{{ "{:,}".format(overall.paid_calls) }} paid calls
RAG Rate
{{ "%.0f"|format(overall.rag_rate) }}%{{ "{:,}".format(overall.rag_hits) }} hits
{% endif %}
+
Agent 指揮矩陣 · {{ hours }} 小時視窗

Agent 指揮矩陣

這頁回答 AI 中樞如何分工:誰在用 Ollama、誰還在吃付費 LLM、哪些 Agent 有 RAG 命中、哪些工作流已經接上 MCP。這不是列表,是指揮官視角。

{% if overall %}
呼叫總量
{{ "{:,}".format(overall.total_calls) }}{{ "{:,}".format(overall.total_tokens) }} 權杖
Ollama 占比
{{ "%.0f"|format(overall.local_pct) }}%{{ "{:,}".format(overall.local_calls) }} 次本地呼叫
付費成本
${{ "%.2f"|format(overall.total_cost) }}{{ "{:,}".format(overall.paid_calls) }} 次付費呼叫
RAG 命中率
{{ "%.0f"|format(overall.rag_rate) }}%{{ "{:,}".format(overall.rag_hits) }} 次命中
{% endif %}
{% if error %}
{{ error }}
{% endif %}
-
4 Agent Matrix

LLM × MCP × RAG 編排矩陣

{% for ag in agent_matrix %}{% endfor %}
Agent呼叫成本Ollama付費MCPRAG錯誤耗時
{{ ag.label }}{{ ag.desc }}{% if ag.calls > 0 %}{{ "{:,}".format(ag.calls) }}{{ "{:,}".format(ag.tokens) }} tk{% else %}{% endif %}{% if ag.calls > 0 %}${{ "%.2f"|format(ag.cost) }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.0f"|format(ag.ollama_pct) }}%A {{ ag.ollama_gcp_a }} · B {{ ag.ollama_gcp_b }} · 111 {{ ag.ollama_111 }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.0f"|format(ag.paid_pct) }}%Gemini {{ ag.gemini }}{% if ag.other_paid %} · 其他 {{ ag.other_paid }}{% endif %}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.1f"|format(ag.mcp_rate) }}%{{ ag.mcp_calls }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.1f"|format(ag.rag_rate) }}%{{ ag.rag_hits }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.1f"|format(ag.error_rate) }}%{{ ag.errors }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ ag.avg_ms }} ms{% else %}{% endif %}
+
四 Agent 矩陣

LLM × MCP × RAG 編排矩陣

{% for ag in agent_matrix %}{% endfor %}
Agent呼叫成本Ollama付費MCPRAG錯誤耗時
{{ ag.label }}{{ ag.desc }}{% if ag.calls > 0 %}{{ "{:,}".format(ag.calls) }}{{ "{:,}".format(ag.tokens) }} 權杖{% else %}{% endif %}{% if ag.calls > 0 %}${{ "%.2f"|format(ag.cost) }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.0f"|format(ag.ollama_pct) }}%A {{ ag.ollama_gcp_a }} · B {{ ag.ollama_gcp_b }} · 111 {{ ag.ollama_111 }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.0f"|format(ag.paid_pct) }}%Gemini {{ ag.gemini }}{% if ag.other_paid %} · 其他 {{ ag.other_paid }}{% endif %}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.1f"|format(ag.mcp_rate) }}%{{ ag.mcp_calls }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.1f"|format(ag.rag_rate) }}%{{ ag.rag_hits }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ "%.1f"|format(ag.error_rate) }}%{{ ag.errors }}{% else %}{% endif %}{% if ag.calls > 0 %}{{ ag.avg_ms }} ms{% else %}{% endif %}
- {% if recommendations %}
Rules

編排策略自動建議

{% for r in recommendations %}
{{ r.severity|upper }}{{ r.agent }}
發現:{{ r.finding }}
建議:{{ r.suggestion }}
{% endfor %}
{% endif %} - {% if mcp_matrix %}
MCP Detail

MCP server × caller 工作量

{% for m in mcp_matrix %}{% endfor %}
MCP ServerCallertool 呼叫cachecache 率成本
{{ m.server }}{{ m.caller }}{{ "{:,}".format(m.calls) }}{{ m.cache_hits }}{{ "%.0f"|format(m.cache_rate) }}%${{ "%.4f"|format(m.cost) }}
{% endif %} -

Operation Ollama-First v5.0 — Agent 指揮矩陣

+ {% if recommendations %}
策略規則

編排策略自動建議

{% for r in recommendations %}
{{ r.severity|upper }}{{ r.agent }}
發現:{{ r.finding }}
建議:{{ r.suggestion }}
{% endfor %}
{% endif %} + {% if mcp_matrix %}
MCP 明細

MCP 服務 × 呼叫端工作量

{% for m in mcp_matrix %}{% endfor %}
MCP 服務呼叫端tool 呼叫快取快取率成本
{{ m.server }}{{ m.caller }}{{ "{:,}".format(m.calls) }}{{ m.cache_hits }}{{ "%.0f"|format(m.cache_rate) }}%${{ "%.4f"|format(m.cost) }}
{% endif %} +

Ollama 優先策略 v5.0 — Agent 指揮矩陣

{% endblock %} diff --git a/templates/admin/ai_calls_dashboard.html b/templates/admin/ai_calls_dashboard.html index 10f6b88..9e2cf10 100644 --- a/templates/admin/ai_calls_dashboard.html +++ b/templates/admin/ai_calls_dashboard.html @@ -50,11 +50,11 @@
-
AI Traffic Control · {{ hours }}h Window
+
AI 流量管制 · {{ hours }} 小時視窗

AI 流量控制塔

-

這裡不是流水帳,而是 AI 中樞的飛航管制台:看呼叫量、Token、成本、錯誤率、RAG 命中與 MCP 編排,並在異常時一鍵派出 Code Review Pipeline。

+

這裡不是流水帳,而是 AI 中樞的飛航管制台:看呼叫量、權杖量、成本、錯誤率、RAG 命中與 MCP 編排,並在異常時一鍵派出程式碼審查管線。

- +
@@ -67,26 +67,26 @@ {% if error %}
{{ error }}
{% endif %}
-
Calls
{{ "{:,}".format(total) }}{% if hourly_trend %}{% endif %}
-
Tokens
{{ "{:,}".format(summary.total_tokens or 0) }}
{{ avg_tokens }} tk/call
-
Cost
${{ "%.2f"|format(summary.total_cost or 0) }}{% if hourly_trend %}{% endif %}
-
Latency
{{ summary.avg_duration or 0 }}ms
{{ summary.cache_hits or 0 }} cache hits
-
RAG Hit
{{ "%.1f"|format(rag_rate) }}%
{{ summary.rag_hits or 0 }} hits
-
Errors
{{ errors }}{% if hourly_trend %}{% endif %}
+
呼叫總量
{{ "{:,}".format(total) }}{% if hourly_trend %}{% endif %}
+
權杖量
{{ "{:,}".format(summary.total_tokens or 0) }}
{{ avg_tokens }} 權杖/次
+
成本
${{ "%.2f"|format(summary.total_cost or 0) }}{% if hourly_trend %}{% endif %}
+
延遲
{{ summary.avg_duration or 0 }}ms
{{ summary.cache_hits or 0 }} 次快取命中
+
RAG 命中
{{ "%.1f"|format(rag_rate) }}%
{{ summary.rag_hits or 0 }} 次命中
+
錯誤
{{ errors }}{% if hourly_trend %}{% endif %}
{% if hourly_trend %}
-
Hourly Traffic

每小時呼叫趨勢

+
每小時流量

每小時呼叫趨勢

{% endif %} {% if caller_richness %}
-
Caller Orchestration

呼叫端 × RAG × MCP 編排矩陣

+
呼叫端編排

呼叫端 × RAG × MCP 編排矩陣

{% for c in caller_richness %}{% endfor %}
呼叫端總呼叫RAG 命中MCP 編排RAG 反饋筆數
{{ c.caller }}{{ "{:,}".format(c.total_calls) }}{{ "%.1f"|format(c.rag_hit_rate) }}% ({{ c.rag_hits }}){{ "%.1f"|format(c.mcp_rate) }}%{% if c.feedback_count > 0 %}{{ "%.2f"|format(c.avg_rag_feedback) }}/5{% else %}{% endif %}{{ c.feedback_count }}
{% endif %} @@ -94,19 +94,19 @@
@@ -143,6 +143,6 @@ const el = document.getElementById('hourlyTrendChart'); if (!el || !labels.length) return; new Chart(el, { data: { labels, datasets: [ { type: 'line', label: '呼叫數', data: calls, borderColor: '#c96442', backgroundColor: 'rgba(201,100,66,.12)', tension: .35, fill: true, yAxisID: 'y' }, { type: 'line', label: '錯誤', data: errors, borderColor: '#b94b45', backgroundColor: 'rgba(185,75,69,.1)', tension: .35, yAxisID: 'y' }, { type: 'bar', label: '成本 USD', data: costs, backgroundColor: 'rgba(184,121,47,.38)', borderColor: '#b8792f', yAxisID: 'y1' } ] }, options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false }, scales: { y: { beginAtZero: true, title: { display: true, text: '次數' } }, y1: { position: 'right', beginAtZero: true, grid: { drawOnChartArea: false }, title: { display: true, text: 'USD' } } } } }); })(); -async function triggerCodeReview() { if (!confirm('觸發 Code Review Pipeline?\n\n會對最新 commit 跑 5 step 審查,背景執行。')) return; try { const r = await fetch('/observability/ai_calls/trigger_code_review', {method: 'POST'}); const d = await r.json(); if (d.ok) { alert(`✅ ${d.message}\n\nPipeline ID: ${d.pipeline_id}\nCommit: ${d.commit_sha}\n變更檔案: ${d.changed_files_count} 個`); } else { alert('❌ ' + (d.error || '觸發失敗')); } } catch (e) { console.warn('code_review_trigger_failed', e); alert('操作暫時無法完成,請稍後再試或查看系統日誌。'); } } +async function triggerCodeReview() { if (!confirm('觸發程式碼審查管線?\n\n會對最新 commit 跑 5 步驟審查,背景執行。')) return; try { const r = await fetch('/observability/ai_calls/trigger_code_review', {method: 'POST'}); const d = await r.json(); if (d.ok) { alert(`✅ ${d.message}\n\n管線 ID: ${d.pipeline_id}\nCommit: ${d.commit_sha}\n變更檔案: ${d.changed_files_count} 個`); } else { alert('❌ ' + (d.error || '觸發失敗')); } } catch (e) { console.warn('code_review_trigger_failed', e); alert('操作暫時無法完成,請稍後再試或查看系統日誌。'); } } {% endblock %} diff --git a/templates/admin/budget.html b/templates/admin/budget.html index 893f3c2..08c014a 100644 --- a/templates/admin/budget.html +++ b/templates/admin/budget.html @@ -37,15 +37,15 @@
-
AI Cost Governance · Budget / Throttle / RAG Strategy
+
AI 成本治理 · 預算 / 節流 / RAG 策略

AI 成本治理艙

-

這頁回答一個問題:AI 中樞花錢是否仍在治理邊界內?預算、實際支出、月底推估、節流狀態與 RAG 策略建議集中在同一個 cockpit。

-
超過 110% 時不用等 cron,直接 evaluate provider throttle。
+

這頁回答一個問題:AI 中樞花錢是否仍在治理邊界內?預算、實際支出、月底推估、節流狀態與 RAG 策略建議集中在同一個工作台。

+
超過 110% 時不用等 排程,直接重算供應商節流。
-
Month Spend
${{ "%.2f"|format(total_spent.value) }}
預算 ${{ "%.2f"|format(total_budget.value) }}
-
Budget Ratio
{{ "%.0f"|format(total_ratio) }}%
全 provider 加總
-
Warnings
{{ warn_count.value }}
使用率 ≥ 80%
-
Throttled
{{ throttled_count.value }}
已啟動成本節流
+
當月花費
${{ "%.2f"|format(total_spent.value) }}
預算 ${{ "%.2f"|format(total_budget.value) }}
+
預算使用率
{{ "%.0f"|format(total_ratio) }}%
全供應商加總
+
預警
{{ warn_count.value }}
使用率 ≥ 80%
+
已節流
{{ throttled_count.value }}
已啟動成本節流
@@ -54,42 +54,42 @@
-
Budget Lines

預算線與節流狀態

+
預算線

預算線與節流狀態

{% for r in rows %}= 0.8 %}class="table-warning"{% endif %}>{% else %}{% endfor %}
週期供應商已花費預算閾值使用率狀態動作
{{ r.period }}{{ r.provider }}${{ "%.2f"|format(r.spent) }}{{ "%.0f"|format(r.ratio * 100) }}%{% if r.throttled %}已節流{% elif r.ratio >= 0.8 %}接近上限{% else %}正常{% endif %}
無預算資料(需先跑 migrations/025)
{% if cost_trend_30d %} -
30d Cost Trend

每日成本堆疊趨勢

+
30 日成本趨勢

每日成本堆疊趨勢

{% endif %}
{% if budget_strategies %} -
RAG Strategy

RAG 自動策略建議

{% for s in budget_strategies %}
{{ s.insight_type }}相似度 {{ "%.2f"|format(s.similarity) }}{{ s.content }}{% if s.content|length >= 240 %}…{% endif %}
{% endfor %}
+
RAG 策略

RAG 自動策略建議

{% for s in budget_strategies %}
{{ s.insight_type }}相似度 {{ "%.2f"|format(s.similarity) }}{{ s.content }}{% if s.content|length >= 240 %}…{% endif %}
{% endfor %}
{% endif %} {% if price_rec_7d %} -
Business Output

AI 價格決策 7 日

{% for p in price_rec_7d %}
{{ p.strategy }}{{ p.count }}信心 {{ "%.2f"|format(p.avg_confidence) }}
{% endfor %}
+
商業產出

AI 價格決策 7 日

{% for p in price_rec_7d %}
{{ p.strategy }}{{ p.count }}信心 {{ "%.2f"|format(p.avg_confidence) }}
{% endfor %}
{% endif %} -

Operation Ollama-First v5.0 — AI 成本治理艙

+

Ollama 優先策略 v5.0 — AI 成本治理艙

{% endblock %} diff --git a/templates/admin/business_intel.html b/templates/admin/business_intel.html index df13982..d4104a2 100644 --- a/templates/admin/business_intel.html +++ b/templates/admin/business_intel.html @@ -439,7 +439,7 @@
- Business Intelligence + 商業情報

商業 AI 戰果室

這一頁不再只是資料列表,而是把價格建議、未跟進警示、閉環學習與競品監測收成一個商業決策控制台。 @@ -476,7 +476,7 @@

高信心未跟進
{{ unfollowed_count }}
-
confidence >= 0.8 且仍未轉 action_plan
+
信心分 >= 0.8 且仍未轉行動計畫
平均信心分
@@ -486,7 +486,7 @@
有效率
{{ '%.0f'|format(effective_rate) }}%
-
effective / 已回收 verdict
+
有效 / 已回收結論
競品監測
@@ -559,7 +559,7 @@
{{ r.momo_price or '-' }} / {{ r.pchome_price or '-' }} - gap {{ '%.1f'|format(r.gap_pct or 0) }}% + 差距 {{ '%.1f'|format(r.gap_pct or 0) }}%
{{ r.reason or '尚無原因摘要' }}
@@ -575,20 +575,20 @@

閉環學習紀錄

-

追蹤 action_plan 到 outcome 的真實效果,這是 AI 能不能變聰明的核心證據。

+

追蹤行動計畫到實際結果的真實效果,這是 AI 能不能變聰明的核心證據。

- {{ loop_records|length }} records + {{ loop_records|length }} 筆紀錄
{% if loop_records %} - + - + @@ -608,7 +608,7 @@
Plan計畫 SKU 狀態 建立 / 執行Verdict結論 指標 變化
{% else %} -
尚未形成 action_plan → outcome 閉環紀錄。
+
尚未形成行動計畫到實際結果的閉環紀錄。
{% endif %}
@@ -618,7 +618,7 @@
-

Verdict 戰果分布

+

結論戰果分布

用結果反校正 AI 建議,不讓漂亮信心分掩蓋真實成效。

@@ -626,7 +626,7 @@
{% if verdict_stats %} - + {% for v in verdict_stats %} @@ -634,7 +634,7 @@
VerdictCountAvg Δ
結論數量平均變化
{{ v.verdict or '未分類' }}{{ v.count }}{{ '%.1f'|format(v.avg_delta or 0) }}%
{% else %} -
尚無 verdict 統計。
+
尚無結論統計。
{% endif %}
@@ -649,7 +649,7 @@
{% if match_stats %} - + {% for m in match_stats %} @@ -683,8 +683,8 @@ - - + + {% endfor %} @@ -697,7 +697,7 @@ -
資料來源:ai_price_recommendations / action_plans / action_outcomes / competitor_match_attempts / competitor_price_history
+
資料來源:AI 價格建議、行動計畫、實際結果、競品比對與競品價格歷史。
{% endblock %} diff --git a/templates/admin/host_health.html b/templates/admin/host_health.html index b649c41..b8a001c 100644 --- a/templates/admin/host_health.html +++ b/templates/admin/host_health.html @@ -59,14 +59,14 @@
-
Infrastructure Lifeline · Ollama / MCP / AIOps
+
基礎設施生命線 · Ollama / MCP / AIOps

基礎設施生命線

這頁是 AI 中樞的底盤監控:三主機 Ollama 級聯、MCP 工具層、成本節流與 ADR-013 AutoHeal 閉環。先看能不能活,再看要不要修。

-
Ollama Down
{{ down.count }}{{ ollama_hosts|length }} 台即時 probe
-
AIOps Open
{{ aiops_summary.incidents_open if aiops_summary else '—' }}7 日 incident 未解決
-
Heal Rate
{{ "%.0f"|format(aiops_summary.heal_success_rate) if aiops_summary else '—' }}{% if aiops_summary %}%{% endif %}ADR-013 自癒成功率
-
Throttled
{{ throttled.count }}成本節流供應商
+
Ollama 離線
{{ down.count }}{{ ollama_hosts|length }} 台即時探測
+
AIOps 未解
{{ aiops_summary.incidents_open if aiops_summary else '—' }}7 日事件未解決
+
自癒成功率
{{ "%.0f"|format(aiops_summary.heal_success_rate) if aiops_summary else '—' }}{% if aiops_summary %}%{% endif %}ADR-013 自癒成功率
+
節流供應商
{{ throttled.count }}成本節流供應商
@@ -74,7 +74,7 @@
-
Host Cascade

Ollama 三主機

+
主機級聯

Ollama 三主機

{{ '需要處理' if down.count > 0 else '全部在線' }}
@@ -104,7 +104,7 @@ {% if health_history %}
-
24h Probe History

健康趨勢摘要

+
24 小時探測歷史

健康趨勢摘要

StatusCountCandidatesScore
狀態數量候選數分數
{{ r.crawled_at or '-' }} {{ r.sku }}
{{ r.product_name or '-' }}
{{ r.pchome_price or '-' }} / {{ r.momo_price or '-' }}
gap {{ r.gap or '-' }}
{{ '%.1f'|format(r.discount_pct or 0) }}%
score {{ '%.2f'|format(r.match_score or 0) }}
{{ r.pchome_price or '-' }} / {{ r.momo_price or '-' }}
差距 {{ r.gap or '-' }}
{{ '%.1f'|format(r.discount_pct or 0) }}%
分數 {{ '%.2f'|format(r.match_score or 0) }}
{% for h in health_history %}{% endfor %}
角色總探針正常離線在線率平均 ms
{{ h.host_label }}{{ h.total }}{{ h.up_count }}{{ h.down_count }}{{ "%.1f"|format(h.uptime_pct) }}%{{ h.avg_ms }}
{% endif %} @@ -113,7 +113,7 @@
diff --git a/templates/admin/observability_overview.html b/templates/admin/observability_overview.html index 8b31607..7c337f0 100644 --- a/templates/admin/observability_overview.html +++ b/templates/admin/observability_overview.html @@ -608,9 +608,9 @@
RAG 品質

RAG 與品質

- RAG 晉升審核Promotion Gate 與人工審核。07 + RAG 晉升審核晉升守門與人工審核。07 RAG 召回詳情Query hits、節省呼叫、反饋追蹤。08 - 反饋趨勢Caller 品質、蒸餾池、根因建議。09 + 反饋趨勢呼叫端品質、蒸餾池、根因建議。09 PPT 視覺審核PPT audit、RAG 修法、AiderHeal。10
diff --git a/templates/admin/ppt_audit_history.html b/templates/admin/ppt_audit_history.html index 7b7316f..a587e03 100644 --- a/templates/admin/ppt_audit_history.html +++ b/templates/admin/ppt_audit_history.html @@ -9,23 +9,23 @@
-
PPT Visual QA Pipeline · minicpm-v / AiderHeal / RAG Fixes

PPT 視覺 QA 產線

這頁追蹤每份自動簡報是否通過視覺審核:檔案產出、minicpm-v audit、Telegram 推送、RAG 修法建議與 AiderHeal 自動修 generator。

Vision
{{ 'ON' if vision_enabled else 'OFF' }}PPT_VISION_ENABLED
30d Total
{{ audit_30d_stats.total if audit_30d_stats else 0 }}audit records
Pass Rate
{{ "%.0f"|format(audit_30d_stats.pass_rate) if audit_30d_stats else '—' }}{% if audit_30d_stats %}%{% endif %}過去 30 日
Issues
{{ audit_30d_stats.total_issues if audit_30d_stats else 0 }}視覺問題數
+
PPT 視覺 QA 產線 · minicpm-v / AiderHeal / RAG 修法

PPT 視覺 QA 產線

這頁追蹤每份自動簡報是否通過視覺審核:檔案產出、minicpm-v 審核、Telegram 推送、RAG 修法建議與 AiderHeal 自動修 generator。

視覺模型
{{ '啟用' if vision_enabled else '停用' }}PPT_VISION_ENABLED
30 日總量
{{ audit_30d_stats.total if audit_30d_stats else 0 }}審核紀錄
通過率
{{ "%.0f"|format(audit_30d_stats.pass_rate) if audit_30d_stats else '—' }}{% if audit_30d_stats %}%{% endif %}過去 30 日
問題數
{{ audit_30d_stats.total_issues if audit_30d_stats else 0 }}視覺問題數
{% if error %}
{{ error }}
{% endif %}
-
Audit History

視覺審核歷史 100 筆

{% for r in audit_records %}{% else %}{% endfor %}
時間檔名結果問題信心耗時錯誤動作
{{ r.audited_at }}{{ r.pptx_filename }}{% if r.audit_status == 'passed' %}通過{% elif r.audit_status == 'failed' %}有問題{% elif r.audit_status == 'error' %}錯誤{% elif r.audit_status == 'skipped' %}跳過{% else %}{{ r.audit_status }}{% endif %}{{ r.issues_count }}{{ "%.2f"|format(r.confidence) }}{{ r.duration_ms }}{{ (r.error_msg or '')[:80] }}{% if r.audit_status in ('failed','error') %}{% endif %}
尚無審核紀錄
-
Generated Files

過去 7 日 PPT 檔案

{% for f in files %}{% else %}{% endfor %}
檔名KB修改時間狀態
{{ f.name }}{{ f.size_kb }}{{ f.mtime }}22:00 cron 自動審核
過去 7 日無 PPT 生成
+
審核歷史

視覺審核歷史 100 筆

{% for r in audit_records %}{% else %}{% endfor %}
時間檔名結果問題信心耗時錯誤動作
{{ r.audited_at }}{{ r.pptx_filename }}{% if r.audit_status == 'passed' %}通過{% elif r.audit_status == 'failed' %}有問題{% elif r.audit_status == 'error' %}錯誤{% elif r.audit_status == 'skipped' %}跳過{% else %}{{ r.audit_status }}{% endif %}{{ r.issues_count }}{{ "%.2f"|format(r.confidence) }}{{ r.duration_ms }}{{ (r.error_msg or '')[:80] }}{% if r.audit_status in ('failed','error') %}{% endif %}
尚無審核紀錄
+
已產檔案

過去 7 日 PPT 檔案

{% for f in files %}{% else %}{% endfor %}
檔名KB修改時間狀態
{{ f.name }}{{ f.size_kb }}{{ f.mtime }}22:00 cron 自動審核
過去 7 日無 PPT 生成
- {% if rag_fixes %}
RAG Fix Suggestions

RAG 自動修法建議

{% for fix in rag_fixes %}
{{ fix.pptx_filename }}{{ fix.audited_at }}
{{ fix.error_msg }}
    {% for h in fix.hits %}
  • {{ h.insight_type }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} + {% if rag_fixes %}
RAG 修法建議

RAG 自動修法建議

{% for fix in rag_fixes %}
{{ fix.pptx_filename }}{{ fix.audited_at }}
{{ fix.error_msg }}
    {% for h in fix.hits %}
  • {{ h.insight_type }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} {% if (not audit_30d_stats or audit_30d_stats.total == 0) and not vision_enabled %}
為什麼這頁空?
{% endif %} -

Operation Ollama-First v5.0 — PPT 視覺 QA 產線

+

Ollama 優先策略 v5.0 — PPT 視覺 QA 產線

diff --git a/templates/admin/quality_trend.html b/templates/admin/quality_trend.html index 82391d5..07186cc 100644 --- a/templates/admin/quality_trend.html +++ b/templates/admin/quality_trend.html @@ -17,24 +17,24 @@ {% set rag_total = (rag_overall_dist | sum(attribute='count')) if rag_overall_dist else 0 %}
-
Quality Diagnostics · {{ days }}d Window

AI 品質診斷台

這裡看 AI 的回答到底有沒有變好:caller 反饋、RAG 分數、learning episode 流量、action plan 與 outcome 閉環全部聚合到同一張品質雷達。

Feedback
{{ total_feedback.value }}caller feedback 總量
Worst Avg
{{ "%.2f"|format(worst_avg.value) }}最差 caller 平均分
Episodes
{{ episode_total }}蒸餾池 {{ days }} 日
RAG Scores
{{ rag_total }}已回饋 RAG query
+
品質診斷 · {{ days }} 日視窗

AI 品質診斷台

這裡看 AI 的回答到底有沒有變好:呼叫端反饋、RAG 分數、學習片段流量、行動計畫與結果閉環全部聚合到同一張品質雷達。

反饋總量
{{ total_feedback.value }}呼叫端反饋總量
最差均分
{{ "%.2f"|format(worst_avg.value) }}最差呼叫端平均分
蒸餾樣本
{{ episode_total }}蒸餾池 {{ days }} 日
RAG 評分
{{ rag_total }}已回饋 RAG 查詢
{% if error %}
{{ error }}
{% endif %}
-
Caller Feedback

呼叫端 × 反饋分佈

{% for caller, info in trends %}{% else %}{% endfor %}
呼叫端平均倒讚總數趨勢分布
{{ caller }}{{ "%.2f"|format(info.avg_score) }}/5{{ info.thumbs_up }}{{ info.thumbs_down }}{{ info.total_feedback }}{% if info.trend == 'positive' %}正向{% elif info.trend == 'negative' %}負向{% elif info.trend == 'neutral' %}中性{% else %}無資料{% endif %}
無反饋資料
- {% if action_plans_status %}
Action Plans

Action Plans 狀態分布

{% for a in action_plans_status %}{% endfor %}
狀態計畫類型數量
{{ a.status }}{{ a.plan_type }}{{ a.count }}
{% endif %} +
呼叫端反饋

呼叫端 × 反饋分佈

{% for caller, info in trends %}{% else %}{% endfor %}
呼叫端平均倒讚總數趨勢分布
{{ caller }}{{ "%.2f"|format(info.avg_score) }}/5{{ info.thumbs_up }}{{ info.thumbs_down }}{{ info.total_feedback }}{% if info.trend == 'positive' %}正向{% elif info.trend == 'negative' %}負向{% elif info.trend == 'neutral' %}中性{% else %}無資料{% endif %}
無反饋資料
+ {% if action_plans_status %}
行動計畫

行動計畫 狀態分布

{% for a in action_plans_status %}{% endfor %}
狀態計畫類型數量
{{ a.status }}{{ a.plan_type }}{{ a.count }}
{% endif %}
- {% if rag_root_causes %}
Root Cause

RAG 自動根因建議

{% for rc in rag_root_causes %}
{{ rc.caller }}{{ "%.2f"|format(rc.avg_score) }}/5{{ rc.feedback_n }} 筆
    {% for h in rc.hits %}
  • {{ h.insight_type }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} - {% if recommendations %}
Recommendations

智能建議

    {% for rec in recommendations %}
  • {% if rec.action == 'review' %}{% else %}{% endif %}{{ rec.caller }}:{{ rec.reason }}
  • {% endfor %}
{% endif %} - {% if action_outcomes_stats %}
Action Outcomes

實際動作成效

{% set total_ao = (action_outcomes_stats | sum(attribute='count')) or 1 %}{% for r in action_outcomes_stats %}
{{ r.verdict }}{{ r.count }}{{ "%.1f"|format(r.count / total_ao * 100) }}%
{% endfor %}
{% endif %} -

Operation Ollama-First v5.0 — AI 品質診斷台

+ {% if rag_root_causes %}
根因分析

RAG 自動根因建議

{% for rc in rag_root_causes %}
{{ rc.caller }}{{ "%.2f"|format(rc.avg_score) }}/5{{ rc.feedback_n }} 筆
    {% for h in rc.hits %}
  • {{ h.insight_type }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} + {% if recommendations %}
智能建議

智能建議

    {% for rec in recommendations %}
  • {% if rec.action == 'review' %}{% else %}{% endif %}{{ rec.caller }}:{{ rec.reason }}
  • {% endfor %}
{% endif %} + {% if action_outcomes_stats %}
動作成效

實際動作成效

{% set total_ao = (action_outcomes_stats | sum(attribute='count')) or 1 %}{% for r in action_outcomes_stats %}
{{ r.verdict }}{{ r.count }}{{ "%.1f"|format(r.count / total_ao * 100) }}%
{% endfor %}
{% endif %} +

Ollama 優先策略 v5.0 — AI 品質診斷台

{% if rag_overall_dist %}{% endif %} diff --git a/templates/admin/rag_queries.html b/templates/admin/rag_queries.html index 18909ff..fe25f29 100644 --- a/templates/admin/rag_queries.html +++ b/templates/admin/rag_queries.html @@ -18,29 +18,29 @@
-
RAG Recall Radar · {{ hours }}h Window
+
RAG 召回雷達 · {{ hours }} 小時視窗

RAG 召回雷達

-

這裡追蹤每次 RAG 查詢是否真的命中、是否省下 LLM call、哪些 caller 用得好、哪些 query 沒找到知識。RAG 如果要成為武器,這裡就是雷達螢幕。

-
- {% if summary and summary.total > 0 %}
Queries
{{ "{:,}".format(summary.total) }}
{{ summary.distinct_callers }} callers
Hit Rate
{{ "%.1f"|format(summary.hit_rate) }}%
{{ summary.with_hits }} hit · {{ summary.no_hits }} miss
Saved Call
{{ "%.1f"|format(summary.saved_rate) }}%
{{ summary.saved }} 次省下 LLM
Feedback
{{ "%.2f"|format(summary.avg_score) }}
{{ summary.feedback_count }} 筆 · 平均 {{ summary.avg_hits }} hits
{% endif %} +

這裡追蹤每次 RAG 查詢是否真的命中、是否省下 LLM 呼叫、哪些呼叫端用得好、哪些查詢沒找到知識。RAG 如果要成為武器,這裡就是雷達螢幕。

+
+ {% if summary and summary.total > 0 %}
查詢數
{{ "{:,}".format(summary.total) }}
{{ summary.distinct_callers }} 個呼叫端
命中率
{{ "%.1f"|format(summary.hit_rate) }}%
{{ summary.with_hits }} 次命中 · {{ summary.no_hits }} 次未命中
省下呼叫
{{ "%.1f"|format(summary.saved_rate) }}%
{{ summary.saved }} 次省下 LLM
反饋分
{{ "%.2f"|format(summary.avg_score) }}
{{ summary.feedback_count }} 筆 · 平均 {{ summary.avg_hits }} 次命中
{% endif %}
{% if error %}
{{ error }}
{% endif %}
-
Query Stream

最近 50 筆查詢詳情

{% if queries %}{% for q in queries %}{% endfor %}
時間呼叫端查詢top_k門檻命中saved反饋動作
{{ q.queried_at }}{{ q.caller }}{{ q.query_text }}{% if q.query_text|length >= 200 %}…{% endif %}{{ q.top_k }}{{ q.threshold }}{% if q.hit_count > 0 %}{{ q.hit_count }}{% else %}0{% endif %}{% if q.saved_call %}saved{% else %}{% endif %}{% if q.feedback_score is not none %}{{ q.feedback_score }}/5{% else %}{% endif %}{% if q.hit_count > 0 %}{% endif %}
{% else %}
過去 {{ hours }} 小時無符合條件的 RAG 查詢紀錄。
{% endif %}
+
查詢串流

最近 50 筆查詢詳情

{% if queries %}{% for q in queries %}{% endfor %}
時間呼叫端查詢top_k門檻命中已省下反饋動作
{{ q.queried_at }}{{ q.caller }}{{ q.query_text }}{% if q.query_text|length >= 200 %}…{% endif %}{{ q.top_k }}{{ q.threshold }}{% if q.hit_count > 0 %}{{ q.hit_count }}{% else %}0{% endif %}{% if q.saved_call %}已省下{% else %}{% endif %}{% if q.feedback_score is not none %}{{ q.feedback_score }}/5{% else %}{% endif %}{% if q.hit_count > 0 %}{% endif %}
{% else %}
過去 {{ hours }} 小時無符合條件的 RAG 查詢紀錄。
{% endif %}
-

Operation Ollama-First v5.0 — RAG 召回雷達

+

Ollama 優先策略 v5.0 — RAG 召回雷達

{% endblock %} diff --git a/web/static/css/observability-system.css b/web/static/css/observability-system.css index 5eba739..fac4482 100644 --- a/web/static/css/observability-system.css +++ b/web/static/css/observability-system.css @@ -2379,6 +2379,136 @@ } } +/* v3.11 V2 workbench normalization: unify legacy observability page skins before the terminal dot layer. */ +.momo-observability-mode :is( + .obs-hero, + .agent-hero, + .biz-command, + .runtime-hero, + .calls-hero, + .gov-hero, + .gate-hero, + .rag-hero, + .qa-hero, + .quality-hero, + .ppt-hero +) { + border-color: var(--obs-line) !important; + border-radius: var(--momo-radius-lg, 8px) !important; + background-color: var(--momo-bg-surface, #faf7f0) !important; + background-image: var(--obs-matrix-dot) !important; + background-size: var(--obs-matrix-size) !important; + box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06)) !important; +} + +.momo-observability-mode :is( + .obs-panel, + .agent-panel, + .biz-panel, + .runtime-panel, + .calls-panel, + .gov-panel, + .gate-panel, + .rag-panel, + .qa-panel, + .quality-panel, + .ppt-panel, + .obs-signal, + .agent-signal, + .biz-signal, + .runtime-signal, + .calls-signal, + .gov-signal, + .gate-signal, + .rag-signal, + .qa-signal, + .quality-signal, + .ppt-signal, + .agent-card, + .rec-card, + .host-lane, + .runtime-mini, + .calls-mini, + .gov-mini, + .gate-mini, + .quality-mini, + .ppt-mini, + .strategy-card, + .episode-card, + .similar-box, + .fix-card, + .root-card, + .caller-card, + .biz-filter-card, + .biz-alert-strip, + .biz-chart-shell, + .biz-strategy-card, + .biz-mini-metric, + .biz-decision-card, + .obs-route-card +) { + border-color: var(--obs-line) !important; + border-radius: var(--momo-radius-lg, 8px) !important; + background-color: var(--momo-bg-elevated, #fdfaf3) !important; + background-image: var(--obs-matrix-dot-soft) !important; + background-size: var(--obs-matrix-size) !important; + box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06)) !important; +} + +.momo-observability-mode :is( + .obs-kicker, + .agent-kicker, + .biz-kicker, + .runtime-kicker, + .calls-kicker, + .gov-kicker, + .gate-kicker, + .rag-kicker, + .qa-kicker, + .quality-kicker, + .ppt-kicker, + .obs-signal-label, + .agent-label, + .biz-signal .label, + .runtime-label, + .calls-label, + .gov-label, + .gate-label, + .rag-label, + .qa-label, + .quality-label, + .ppt-label, + .obs-section-eyebrow +) { + font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace) !important; + font-size: var(--momo-text-label, 0.6875rem) !important; + letter-spacing: 0.06em !important; + text-transform: none !important; + color: color-mix(in srgb, var(--obs-accent) 76%, var(--obs-muted)) !important; +} + +.momo-observability-mode :is( + .btn, + .badge, + .obs-pill, + .biz-badge, + [class$="-pill"], + .model-chip +) { + border-radius: var(--momo-radius-lg, 8px) !important; +} + +.momo-observability-mode :is( + .agent-meter, + .caller-meter, + .progress, + .progress-bar, + .obs-progress-xs, + .obs-progress-sm +) { + border-radius: var(--momo-radius-sm, 3px) !important; +} + /* v3.10 terminal dot-matrix layer: this must stay at EOF to win the cascade. */ .momo-observability-mode { --obs-matrix-dot: radial-gradient(color-mix(in srgb, var(--obs-accent) 14%, transparent) 0.85px, transparent 0.95px);