diff --git a/channel/web/chat.html b/channel/web/chat.html
index 01e1d0e4..0b15e21c 100644
--- a/channel/web/chat.html
+++ b/channel/web/chat.html
@@ -907,6 +907,28 @@
run.log
+
+
+
+
+
+
+
实时
diff --git a/channel/web/static/css/console.css b/channel/web/static/css/console.css
index 83693c32..78fcea1a 100644
--- a/channel/web/static/css/console.css
+++ b/channel/web/static/css/console.css
@@ -606,6 +606,14 @@
}
.tool-error-text { color: #f87171; }
+/* Log level highlighting */
+.log-line { display: block; }
+.log-line-debug { color: #94a3b8; }
+.log-line-info { background-color: rgba(59, 130, 246, 0.08); }
+.log-line-warning { background-color: rgba(234, 179, 8, 0.15); color: #fde68a; }
+.log-line-error { background-color: rgba(239, 68, 68, 0.15); color: #fca5a5; }
+.log-line-critical { background-color: rgba(239, 68, 68, 0.35); color: #ffffff; font-weight: bold; }
+
/* Tool failed state */
.agent-tool-step.tool-failed .tool-name { color: #f87171; }
diff --git a/channel/web/static/js/console.js b/channel/web/static/js/console.js
index 44ec1467..80817c4c 100644
--- a/channel/web/static/js/console.js
+++ b/channel/web/static/js/console.js
@@ -4022,6 +4022,51 @@ function loadTasksView() {
// =====================================================================
let logEventSource = null;
+function logLevelClass(line) {
+ if (/\[CRITICAL\]/.test(line)) return 'log-line-critical';
+ if (/\[ERROR\]/.test(line)) return 'log-line-error';
+ if (/\[WARNING\]/.test(line)) return 'log-line-warning';
+ if (/\[INFO\]/.test(line)) return 'log-line-info';
+ if (/\[DEBUG\]/.test(line)) return 'log-line-debug';
+ return '';
+}
+
+function getHiddenLevels() {
+ const hidden = new Set();
+ document.querySelectorAll('.log-filter-cb').forEach(function(cb) {
+ if (!cb.checked) hidden.add('log-line-' + cb.dataset.level);
+ });
+ return hidden;
+}
+
+function applyLogFilter() {
+ const hidden = getHiddenLevels();
+ document.querySelectorAll('#log-output .log-line').forEach(function(span) {
+ const level = span.classList[1] || '';
+ span.style.display = hidden.has(level) ? 'none' : '';
+ });
+}
+
+function appendLogLines(output, text) {
+ const hidden = getHiddenLevels();
+ let lastLevelClass = '';
+ const lines = text.split('\n');
+ lines.forEach(function(line, i) {
+ if (i === lines.length - 1 && line === '') return;
+ const span = document.createElement('span');
+ const levelClass = logLevelClass(line) || lastLevelClass;
+ if (logLevelClass(line)) lastLevelClass = levelClass;
+ span.className = 'log-line ' + levelClass;
+ span.textContent = line + '\n';
+ if (hidden.has(levelClass)) span.style.display = 'none';
+ output.appendChild(span);
+ });
+}
+
+document.addEventListener('change', function(e) {
+ if (e.target.classList.contains('log-filter-cb')) applyLogFilter();
+});
+
function startLogStream() {
if (logEventSource) return;
const output = document.getElementById('log-output');
@@ -4033,10 +4078,11 @@ function startLogStream() {
try { item = JSON.parse(e.data); } catch (_) { return; }
if (item.type === 'init') {
- output.textContent = item.content || '';
+ output.innerHTML = '';
+ appendLogLines(output, item.content || '');
output.scrollTop = output.scrollHeight;
} else if (item.type === 'line') {
- output.textContent += item.content;
+ appendLogLines(output, item.content);
output.scrollTop = output.scrollHeight;
} else if (item.type === 'error') {
output.textContent = item.message || 'Error loading logs';