mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
add llm status panel
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||||
<title>Multi Roomba Rover</title>
|
<title>Multi Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-Bn5Th84i.js"></script>
|
<script type="module" crossorigin src="/assets/index-DiXI9XEi.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BBp-2M2r.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BBp-2M2r.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const { Ollama } = require('ollama');
|
|||||||
const io = require('../globals/io');
|
const io = require('../globals/io');
|
||||||
const logger = require('../globals/logger').child('llmCommentary');
|
const logger = require('../globals/logger').child('llmCommentary');
|
||||||
const { loadConfig } = require('../helpers/configLoader');
|
const { loadConfig } = require('../helpers/configLoader');
|
||||||
|
const { getRole, roleEvents } = require('./roleService');
|
||||||
const roverManager = require('./roverManager');
|
const roverManager = require('./roverManager');
|
||||||
const { getActiveDrivers } = require('./turnService');
|
const { getActiveDrivers } = require('./turnService');
|
||||||
const { getNickname } = require('./nicknameService');
|
const { getNickname } = require('./nicknameService');
|
||||||
@@ -31,6 +32,7 @@ const ollamaClient = ollamaUrl ? new Ollama({ host: ollamaUrl }) : null;
|
|||||||
|
|
||||||
let timer = null;
|
let timer = null;
|
||||||
let inFlight = false;
|
let inFlight = false;
|
||||||
|
let tickCount = 0;
|
||||||
|
|
||||||
function normalizeFrequencyMs(value) {
|
function normalizeFrequencyMs(value) {
|
||||||
if (!Number.isFinite(value)) return DEFAULT_FREQUENCY_MS;
|
if (!Number.isFinite(value)) return DEFAULT_FREQUENCY_MS;
|
||||||
@@ -40,6 +42,60 @@ function normalizeFrequencyMs(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const frequencyMs = normalizeFrequencyMs(Number(commentaryConfig.frequency ?? commentaryConfig.frequencyMs));
|
const frequencyMs = normalizeFrequencyMs(Number(commentaryConfig.frequency ?? commentaryConfig.frequencyMs));
|
||||||
|
let status = {
|
||||||
|
enabled,
|
||||||
|
model,
|
||||||
|
ollamaUrl,
|
||||||
|
frequencyMs,
|
||||||
|
jitterMs: JITTER_MS,
|
||||||
|
promptPath: PROMPT_PATH,
|
||||||
|
running: false,
|
||||||
|
inFlight: false,
|
||||||
|
tickCount: 0,
|
||||||
|
nextRunAt: null,
|
||||||
|
lastTickAt: null,
|
||||||
|
lastOutcome: null,
|
||||||
|
lastReason: null,
|
||||||
|
lastError: null,
|
||||||
|
lastPromptReadAt: null,
|
||||||
|
lastPromptChars: 0,
|
||||||
|
lastSnapshotSummary: null,
|
||||||
|
lastGeneratedText: null,
|
||||||
|
lastPostedText: null,
|
||||||
|
lastPostedAt: null,
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
function isAdminSocket(socket) {
|
||||||
|
if (!socket) return false;
|
||||||
|
const role = getRole(socket);
|
||||||
|
return role === 'admin' || role === 'lockdown' || role === 'lockdown-admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitStatusToSocket(socket) {
|
||||||
|
if (!socket || !isAdminSocket(socket)) return;
|
||||||
|
socket.emit('llmCommentary:status', status);
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitStatusToAdmins() {
|
||||||
|
io.sockets.sockets.forEach((socket) => {
|
||||||
|
if (!isAdminSocket(socket)) return;
|
||||||
|
socket.emit('llmCommentary:status', status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStatus(patch = {}) {
|
||||||
|
const next = {
|
||||||
|
...status,
|
||||||
|
...patch,
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
};
|
||||||
|
if (JSON.stringify(next) === JSON.stringify(status)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
status = next;
|
||||||
|
emitStatusToAdmins();
|
||||||
|
}
|
||||||
|
|
||||||
function localTimeString(date, tz) {
|
function localTimeString(date, tz) {
|
||||||
try {
|
try {
|
||||||
@@ -73,10 +129,25 @@ function resolveDriverNickname(socketId) {
|
|||||||
return getNickname(socket) || socket?.data?.user?.username || socketId.slice(0, 6);
|
return getNickname(socket) || socket?.data?.user?.username || socketId.slice(0, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectActiveDriverEntries() {
|
||||||
|
const fromTurns = Object.entries(getActiveDrivers()).filter(([, socketId]) => Boolean(socketId));
|
||||||
|
if (fromTurns.length > 0) {
|
||||||
|
return fromTurns;
|
||||||
|
}
|
||||||
|
const fallback = [];
|
||||||
|
roverManager.rovers.forEach((record, roverId) => {
|
||||||
|
const socketId = record?.drivers?.values?.().next?.().value || null;
|
||||||
|
if (socketId) {
|
||||||
|
fallback.push([String(roverId), socketId]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
function buildSnapshot() {
|
function buildSnapshot() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const activeDrivers = getActiveDrivers();
|
const activeDrivers = getActiveDrivers();
|
||||||
const driverEntries = Object.entries(activeDrivers).filter(([, socketId]) => Boolean(socketId));
|
const driverEntries = collectActiveDriverEntries();
|
||||||
if (driverEntries.length === 0) {
|
if (driverEntries.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -167,6 +238,10 @@ async function readSystemPrompt() {
|
|||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
throw new Error(`Prompt file empty: ${PROMPT_PATH}`);
|
throw new Error(`Prompt file empty: ${PROMPT_PATH}`);
|
||||||
}
|
}
|
||||||
|
updateStatus({
|
||||||
|
lastPromptReadAt: Date.now(),
|
||||||
|
lastPromptChars: trimmed.length,
|
||||||
|
});
|
||||||
return trimmed;
|
return trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,31 +268,107 @@ async function generateCommentary(systemPrompt, snapshot) {
|
|||||||
|
|
||||||
function scheduleNextTick() {
|
function scheduleNextTick() {
|
||||||
const delay = frequencyMs + Math.floor(Math.random() * (JITTER_MS + 1));
|
const delay = frequencyMs + Math.floor(Math.random() * (JITTER_MS + 1));
|
||||||
|
const nextRunAt = Date.now() + delay;
|
||||||
|
updateStatus({ nextRunAt });
|
||||||
timer = setTimeout(runTick, delay);
|
timer = setTimeout(runTick, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runTick() {
|
async function runTick() {
|
||||||
|
tickCount += 1;
|
||||||
|
const tickId = tickCount;
|
||||||
|
updateStatus({
|
||||||
|
tickCount,
|
||||||
|
inFlight: true,
|
||||||
|
lastTickAt: Date.now(),
|
||||||
|
lastError: null,
|
||||||
|
});
|
||||||
if (inFlight) {
|
if (inFlight) {
|
||||||
|
logger.info('Commentary tick skipped; previous tick still running', { tickId });
|
||||||
|
updateStatus({
|
||||||
|
inFlight: false,
|
||||||
|
lastOutcome: 'skipped',
|
||||||
|
lastReason: 'previous tick still running',
|
||||||
|
});
|
||||||
scheduleNextTick();
|
scheduleNextTick();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
inFlight = true;
|
inFlight = true;
|
||||||
try {
|
try {
|
||||||
const snapshot = buildSnapshot();
|
const snapshot = buildSnapshot();
|
||||||
if (!snapshot) return;
|
if (!snapshot) {
|
||||||
|
logger.info('Commentary tick skipped; no active drivers', { tickId });
|
||||||
|
updateStatus({
|
||||||
|
lastOutcome: 'skipped',
|
||||||
|
lastReason: 'no active drivers',
|
||||||
|
inFlight: false,
|
||||||
|
lastSnapshotSummary: {
|
||||||
|
activeDrivers: 0,
|
||||||
|
rovers: roverManager.getRoster().length,
|
||||||
|
chatMessages: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const snapshotSummary = {
|
||||||
|
activeDrivers: snapshot.activity.active_driver_count,
|
||||||
|
rovers: snapshot.rovers.length,
|
||||||
|
chatMessages: snapshot.chat_recent.length,
|
||||||
|
drivingRovers: snapshot.activity.driving_rovers,
|
||||||
|
drivers: snapshot.drivers,
|
||||||
|
};
|
||||||
|
logger.info('Commentary tick started', {
|
||||||
|
tickId,
|
||||||
|
...snapshotSummary,
|
||||||
|
});
|
||||||
|
updateStatus({
|
||||||
|
lastSnapshotSummary: snapshotSummary,
|
||||||
|
});
|
||||||
const systemPrompt = await readSystemPrompt();
|
const systemPrompt = await readSystemPrompt();
|
||||||
const text = await generateCommentary(systemPrompt, snapshot);
|
const text = await generateCommentary(systemPrompt, snapshot);
|
||||||
if (!text) return;
|
if (!text) {
|
||||||
|
logger.info('Commentary tick produced SKIP/empty output', { tickId });
|
||||||
|
updateStatus({
|
||||||
|
lastOutcome: 'skipped',
|
||||||
|
lastReason: 'model returned SKIP/empty',
|
||||||
|
lastGeneratedText: null,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateStatus({
|
||||||
|
lastGeneratedText: text,
|
||||||
|
});
|
||||||
const recentBotMessages = snapshot.bot_recent_messages || [];
|
const recentBotMessages = snapshot.bot_recent_messages || [];
|
||||||
const duplicate = recentBotMessages.some(
|
const duplicate = recentBotMessages.some(
|
||||||
(entry) => String(entry?.text || '').trim().toLowerCase() === text.toLowerCase(),
|
(entry) => String(entry?.text || '').trim().toLowerCase() === text.toLowerCase(),
|
||||||
);
|
);
|
||||||
if (duplicate) return;
|
if (duplicate) {
|
||||||
|
logger.info('Commentary tick skipped duplicate output', { tickId, text });
|
||||||
|
updateStatus({
|
||||||
|
lastOutcome: 'skipped',
|
||||||
|
lastReason: 'duplicate text',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
sendSystemMessage(text);
|
sendSystemMessage(text);
|
||||||
|
logger.info('Commentary message posted', { tickId, text });
|
||||||
|
updateStatus({
|
||||||
|
lastOutcome: 'posted',
|
||||||
|
lastReason: null,
|
||||||
|
lastPostedText: text,
|
||||||
|
lastPostedAt: Date.now(),
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn('Commentary tick failed', err.message);
|
logger.warn('Commentary tick failed', { tickId, error: err.message });
|
||||||
|
updateStatus({
|
||||||
|
lastOutcome: 'failed',
|
||||||
|
lastReason: 'exception',
|
||||||
|
lastError: err.message,
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
inFlight = false;
|
inFlight = false;
|
||||||
|
updateStatus({
|
||||||
|
inFlight: false,
|
||||||
|
});
|
||||||
scheduleNextTick();
|
scheduleNextTick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,14 +376,37 @@ async function runTick() {
|
|||||||
function start() {
|
function start() {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
logger.info('LLM commentary disabled');
|
logger.info('LLM commentary disabled');
|
||||||
|
updateStatus({
|
||||||
|
running: false,
|
||||||
|
lastOutcome: 'disabled',
|
||||||
|
lastReason: 'llmCommentary.enabled is false',
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!model || !ollamaUrl) {
|
if (!model || !ollamaUrl) {
|
||||||
logger.warn('LLM commentary disabled; model or ollamaUrl missing');
|
logger.warn('LLM commentary disabled; model or ollamaUrl missing');
|
||||||
|
updateStatus({
|
||||||
|
running: false,
|
||||||
|
lastOutcome: 'disabled',
|
||||||
|
lastReason: 'model or ollama server missing',
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.info('LLM commentary enabled', { model, ollamaUrl, frequencyMs, promptPath: PROMPT_PATH });
|
logger.info('LLM commentary enabled', { model, ollamaUrl, frequencyMs, promptPath: PROMPT_PATH });
|
||||||
|
updateStatus({
|
||||||
|
running: true,
|
||||||
|
lastOutcome: 'running',
|
||||||
|
lastReason: null,
|
||||||
|
});
|
||||||
runTick();
|
runTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
emitStatusToSocket(socket);
|
||||||
|
});
|
||||||
|
|
||||||
|
roleEvents.on('change', ({ socket }) => {
|
||||||
|
emitStatusToSocket(socket);
|
||||||
|
});
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default function AdminPanel() {
|
|||||||
rebootRover,
|
rebootRover,
|
||||||
rebootServer,
|
rebootServer,
|
||||||
adminLogs,
|
adminLogs,
|
||||||
|
llmCommentaryStatus,
|
||||||
} = useSession();
|
} = useSession();
|
||||||
const roster = useMemo(() => session?.roster ?? [], [session?.roster]);
|
const roster = useMemo(() => session?.roster ?? [], [session?.roster]);
|
||||||
const [lockStates, setLockStates] = useState({});
|
const [lockStates, setLockStates] = useState({});
|
||||||
@@ -236,11 +237,123 @@ export default function AdminPanel() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<ReplaySnapshotHealth health={health} />
|
<ReplaySnapshotHealth health={health} />
|
||||||
|
<LlmCommentaryPanel status={llmCommentaryStatus} />
|
||||||
<AdminIpLogPanel entries={adminLogs} />
|
<AdminIpLogPanel entries={adminLogs} />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LlmCommentaryPanel({ status }) {
|
||||||
|
if (!status) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<div className="panel-muted text-xs uppercase">LLM Commentary</div>
|
||||||
|
<div className="surface text-xs text-slate-300">No status received yet.</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastTickAt = status.lastTickAt ? new Date(status.lastTickAt).toLocaleString() : 'never';
|
||||||
|
const nextRunAt = status.nextRunAt ? new Date(status.nextRunAt).toLocaleString() : 'n/a';
|
||||||
|
const lastPostedAt = status.lastPostedAt ? new Date(status.lastPostedAt).toLocaleString() : 'never';
|
||||||
|
const summary = status.lastSnapshotSummary || {};
|
||||||
|
const statusColor =
|
||||||
|
status.lastOutcome === 'failed'
|
||||||
|
? 'text-red-300'
|
||||||
|
: status.lastOutcome === 'posted'
|
||||||
|
? 'text-emerald-300'
|
||||||
|
: 'text-slate-300';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<div className="panel-muted text-xs uppercase">LLM Commentary</div>
|
||||||
|
<div className="surface space-y-0.5 text-xs text-slate-200">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Enabled</span>
|
||||||
|
<span>{status.enabled ? 'yes' : 'no'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Running</span>
|
||||||
|
<span>{status.running ? 'yes' : 'no'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>In flight</span>
|
||||||
|
<span>{status.inFlight ? 'yes' : 'no'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Model</span>
|
||||||
|
<span className="text-slate-300">{status.model || '--'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Server</span>
|
||||||
|
<span className="text-slate-300">{status.ollamaUrl || '--'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Frequency</span>
|
||||||
|
<span>{status.frequencyMs} ms</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Tick count</span>
|
||||||
|
<span>{status.tickCount ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Last outcome</span>
|
||||||
|
<span className={statusColor}>{status.lastOutcome || '--'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Last reason</span>
|
||||||
|
<span className="text-slate-300">{status.lastReason || '--'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Last tick</span>
|
||||||
|
<span className="text-slate-300">{lastTickAt}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Next run</span>
|
||||||
|
<span className="text-slate-300">{nextRunAt}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Last posted</span>
|
||||||
|
<span className="text-slate-300">{lastPostedAt}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Prompt chars</span>
|
||||||
|
<span>{status.lastPromptChars ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="surface space-y-0.5 text-xs text-slate-300">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Snapshot active drivers</span>
|
||||||
|
<span>{summary.activeDrivers ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Snapshot rovers</span>
|
||||||
|
<span>{summary.rovers ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span>Snapshot chat msgs</span>
|
||||||
|
<span>{summary.chatMessages ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{status.lastError ? (
|
||||||
|
<div className="surface text-xs text-red-300 break-words">
|
||||||
|
Error: {status.lastError}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{status.lastGeneratedText ? (
|
||||||
|
<div className="surface text-xs text-slate-200 break-words">
|
||||||
|
Generated: {status.lastGeneratedText}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{status.lastPostedText ? (
|
||||||
|
<div className="surface text-xs text-emerald-200 break-words">
|
||||||
|
Posted: {status.lastPostedText}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ReplaySnapshotHealth({ health }) {
|
function ReplaySnapshotHealth({ health }) {
|
||||||
if (!health) return null;
|
if (!health) return null;
|
||||||
const replay = health.replay || { sources: [], readyCount: 0, totalCount: 0 };
|
const replay = health.replay || { sources: [], readyCount: 0, totalCount: 0 };
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const SessionContext = createContext({
|
|||||||
session: null,
|
session: null,
|
||||||
logs: [],
|
logs: [],
|
||||||
adminLogs: [],
|
adminLogs: [],
|
||||||
|
llmCommentaryStatus: null,
|
||||||
login: async () => {},
|
login: async () => {},
|
||||||
setRole: async () => {},
|
setRole: async () => {},
|
||||||
requestControl: async () => {},
|
requestControl: async () => {},
|
||||||
@@ -46,6 +47,7 @@ export function SessionProvider({ children }) {
|
|||||||
const [session, setSession] = useState(null);
|
const [session, setSession] = useState(null);
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState([]);
|
||||||
const [adminLogs, setAdminLogs] = useState([]);
|
const [adminLogs, setAdminLogs] = useState([]);
|
||||||
|
const [llmCommentaryStatus, setLlmCommentaryStatus] = useState(null);
|
||||||
const [alerts, setAlerts] = useState([]);
|
const [alerts, setAlerts] = useState([]);
|
||||||
const [connected, setConnected] = useState(socket.connected);
|
const [connected, setConnected] = useState(socket.connected);
|
||||||
|
|
||||||
@@ -76,11 +78,15 @@ export function SessionProvider({ children }) {
|
|||||||
function handleAdminLogEntry(entry) {
|
function handleAdminLogEntry(entry) {
|
||||||
setAdminLogs((prev) => [...prev.slice(-199), entry]);
|
setAdminLogs((prev) => [...prev.slice(-199), entry]);
|
||||||
}
|
}
|
||||||
|
function handleLlmCommentaryStatus(payload = null) {
|
||||||
|
setLlmCommentaryStatus(payload && typeof payload === 'object' ? payload : null);
|
||||||
|
}
|
||||||
socket.on('session:sync', handleSession);
|
socket.on('session:sync', handleSession);
|
||||||
socket.on('log:init', handleLogInit);
|
socket.on('log:init', handleLogInit);
|
||||||
socket.on('log:entry', handleLogEntry);
|
socket.on('log:entry', handleLogEntry);
|
||||||
socket.on('adminlog:init', handleAdminLogInit);
|
socket.on('adminlog:init', handleAdminLogInit);
|
||||||
socket.on('adminlog:entry', handleAdminLogEntry);
|
socket.on('adminlog:entry', handleAdminLogEntry);
|
||||||
|
socket.on('llmCommentary:status', handleLlmCommentaryStatus);
|
||||||
socket.on('alert:new', (payload = {}) => {
|
socket.on('alert:new', (payload = {}) => {
|
||||||
setAlerts((prev) => [
|
setAlerts((prev) => [
|
||||||
...prev.slice(-49),
|
...prev.slice(-49),
|
||||||
@@ -96,6 +102,7 @@ export function SessionProvider({ children }) {
|
|||||||
socket.off('log:entry', handleLogEntry);
|
socket.off('log:entry', handleLogEntry);
|
||||||
socket.off('adminlog:init', handleAdminLogInit);
|
socket.off('adminlog:init', handleAdminLogInit);
|
||||||
socket.off('adminlog:entry', handleAdminLogEntry);
|
socket.off('adminlog:entry', handleAdminLogEntry);
|
||||||
|
socket.off('llmCommentary:status', handleLlmCommentaryStatus);
|
||||||
socket.off('alert:new');
|
socket.off('alert:new');
|
||||||
};
|
};
|
||||||
}, [socket]);
|
}, [socket]);
|
||||||
@@ -137,10 +144,11 @@ export function SessionProvider({ children }) {
|
|||||||
session,
|
session,
|
||||||
logs,
|
logs,
|
||||||
adminLogs,
|
adminLogs,
|
||||||
|
llmCommentaryStatus,
|
||||||
alerts,
|
alerts,
|
||||||
...actions,
|
...actions,
|
||||||
}),
|
}),
|
||||||
[actions, adminLogs, alerts, connected, logs, session],
|
[actions, adminLogs, alerts, connected, llmCommentaryStatus, logs, session],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
|
return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
|
||||||
|
|||||||
Reference in New Issue
Block a user