← Back to Files
ToolLogs.cshtml
@page "/tool-logs"
@model ToolLogsPageModel
@{
ViewData["Title"] = "Tool Logs - KaiChat";
}
<div class="page-header">
<a href="/" class="back-link">← Back to Chat</a>
<h1>Tool Call Log</h1>
</div>
<div class="tool-logs-page">
<div class="tool-logs-toolbar">
<button class="btn" onclick="loadToolLogs()">Refresh</button>
<button class="btn" onclick="clearToolLogs()">Clear</button>
<span class="tool-logs-counter" id="toolLogsCounter"></span>
</div>
<div id="toolLogsContent" class="tool-logs-content">
<div class="conv-loading">Loading...</div>
</div>
</div>
<script>
function escapeHtml(text) {
if (!text) return '';
return String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
function loadToolLogs() {
fetch('/api/tool-logs')
.then(function(r) { return r.json(); })
.then(function(data) {
var logs = data.logs;
var container = document.getElementById('toolLogsContent');
var counter = document.getElementById('toolLogsCounter');
counter.textContent = (logs ? logs.length : 0) + ' / ' + (data.total || 0) + ' (max 500)';
if (!logs || logs.length === 0) {
container.innerHTML = '<div class="tool-logs-empty">No tool calls logged yet.</div>';
return;
}
var html = '';
logs.forEach(function(entry) {
var cls = entry.status === 'error' ? 'tool-log-entry error' : 'tool-log-entry';
var ts = new Date(entry.timestamp).toLocaleString();
var entryId = 'tlr' + (entry.id || Math.random().toString(36).substring(2, 10));
var resultText = entry.result || entry.summary || '(no output)';
var canRerun = entry.path === '/api/instructions/rewrite';
var rerunBadge = entry.rerunOf ? '<span class="tool-log-rerun-badge">rerun of ' + escapeHtml(entry.rerunOf) + '</span>' : '';
html += '<div class="' + cls + '">'
+ '<div class="tool-log-header">'
+ '<span class="tool-log-method">' + escapeHtml(entry.method) + '</span> '
+ '<span class="tool-log-path">' + escapeHtml(entry.path) + '</span>'
+ '<span class="tool-log-time">' + escapeHtml(ts) + '</span>'
+ (canRerun && !entry.rerunOf ? '<button class="tool-log-rerun" onclick="rerunToolCall(\'' + escapeHtml(entry.id) + '\')">Rerun</button>' : '')
+ '</div>'
+ rerunBadge
+ '<div class="tool-log-summary tool-log-summary-clickable" onclick="toggleToolResult(\'' + entryId + '\')">' + escapeHtml(entry.summary) + '</div>'
+ '<span class="tool-log-body-label">Parameters</span><pre class="tool-log-body">' + escapeHtml(entry.body) + '</pre>'
+ '<div class="tool-log-result" id="' + entryId + '" style="display:none"><pre class="tool-log-result-pre">' + escapeHtml(resultText) + '</pre></div>'
+ '</div>';
});
container.innerHTML = html;
});
}
function rerunToolCall(logId) {
fetch('/api/tool-logs')
.then(function(r) { return r.json(); })
.then(function(data) {
var logs = data.logs || [];
var entry = logs.find(function(l) { return l.id === logId; });
if (!entry) { alert('Log entry not found.'); return; }
// Check if a rerun was already done for this log ID
var prevRerun = logs.find(function(l) { return l.rerunOf === logId; });
if (prevRerun) {
var wasSuccess = prevRerun.status === 'success';
var msg = 'This tool call was already rerun on ' + new Date(prevRerun.timestamp).toLocaleString()
+ ' with status: ' + prevRerun.status.toUpperCase()
+ '. Rerun again?';
if (!confirm(msg)) return;
} else if (entry.status === 'success') {
if (!confirm('This tool call originally succeeded. Rerun it anyway?')) return;
}
var btn = document.querySelector('button[onclick*="' + logId + '"]');
if (btn) { btn.textContent = 'Running...'; btn.disabled = true; }
var params;
try { params = JSON.parse(entry.body); } catch (e) { alert('Invalid JSON body'); return; }
fetch('/api/instructions/rewrite', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params)
})
.then(function(r) { return r.json(); })
.then(function(result) {
// Log the rerun result as a new tool call log entry
var newEntry = {
id: '',
method: entry.method,
path: entry.path,
body: entry.body,
status: result.rewritten ? 'success' : 'error',
summary: result.rewritten ? 'Rewrote: ' + result.path : 'Failed: ' + (result.error || 'unknown error'),
result: result.rewritten ? 'Rewrote ' + result.path + ' successfully.' : 'Error: ' + (result.error || 'unknown'),
timestamp: new Date().toISOString(),
rerunOf: logId
};
fetch('/api/tool-logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newEntry)
}).then(function() {
if (result.rewritten) {
alert('Rewrote: ' + result.path);
} else {
alert('Failed: ' + (result.error || 'unknown error'));
}
loadToolLogs();
});
})
.catch(function(err) {
alert('Error: ' + err.message);
loadToolLogs();
});
});
}
function clearToolLogs() {
fetch('/api/tool-logs', { method: 'DELETE' })
.then(function() {
document.getElementById('toolLogsContent').innerHTML = '<div class="tool-logs-empty">No tool calls logged yet.</div>';
document.getElementById('toolLogsCounter').textContent = '0 / 0 (max 500)';
});
}
function toggleToolResult(id) {
var el = document.getElementById(id);
if (el) el.style.display = el.style.display === 'none' ? '' : 'none';
}
loadToolLogs();
</script>