Size: 6.7 KB Modified: 25/06/2026 11:29 AM
@page "/tool-logs"
@model ToolLogsPageModel
@{
    ViewData["Title"] = "Tool Logs - KaiChat";
}

<div class="page-header">
    <a href="/" class="back-link">&larr; 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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

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>
Offline