new chat style minotiring

This commit is contained in:
legop3
2026-02-25 12:57:06 -05:00
parent 18c0d0fc8d
commit 20e89839fe
6 changed files with 88 additions and 13 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -11,8 +11,8 @@
<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-BzJGyAyM.js"></script> <script type="module" crossorigin src="/assets/index-Djoi30hN.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D2M34pit.css"> <link rel="stylesheet" crossorigin href="/assets/index-CgR-sTcr.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+24 -3
View File
@@ -71,6 +71,8 @@ let status = {
lastPromptChars: 0, lastPromptChars: 0,
lastSystemPrompt: null, lastSystemPrompt: null,
lastInfoSnapshot: null, lastInfoSnapshot: null,
lastModelMessages: null,
lastModelRawOutput: null,
lastSnapshotSummary: null, lastSnapshotSummary: null,
lastClearedAt: contextResetAt, lastClearedAt: contextResetAt,
clearCount, clearCount,
@@ -186,6 +188,8 @@ function clearRuntimeHistory() {
lastClearedAt: contextResetAt, lastClearedAt: contextResetAt,
clearCount, clearCount,
lastInfoSnapshot: null, lastInfoSnapshot: null,
lastModelMessages: null,
lastModelRawOutput: null,
lastSnapshotSummary: null, lastSnapshotSummary: null,
lastGeneratedText: null, lastGeneratedText: null,
lastPostedText: null, lastPostedText: null,
@@ -413,6 +417,16 @@ function normalizeCommentary(rawText) {
return `${normalized.slice(0, MAX_OUTPUT_CHARS - 3)}...`; return `${normalized.slice(0, MAX_OUTPUT_CHARS - 3)}...`;
} }
function parseModelOutput(rawContent) {
const raw = typeof rawContent === 'string' ? rawContent : '';
const normalized = normalizeCommentary(raw);
return {
raw,
normalized,
skipped: normalized == null,
};
}
async function readSystemPrompt() { async function readSystemPrompt() {
const prompt = await fsp.readFile(PROMPT_PATH, 'utf8'); const prompt = await fsp.readFile(PROMPT_PATH, 'utf8');
const trimmed = prompt.trim(); const trimmed = prompt.trim();
@@ -482,6 +496,9 @@ async function generateCommentary(systemPrompt, snapshot) {
current_snapshot: snapshot?.current_snapshot || {}, current_snapshot: snapshot?.current_snapshot || {},
}), }),
}); });
updateStatus({
lastModelMessages: messages,
});
const payload = await ollamaClient.chat({ const payload = await ollamaClient.chat({
model, model,
@@ -494,7 +511,7 @@ async function generateCommentary(systemPrompt, snapshot) {
}, },
messages, messages,
}); });
return normalizeCommentary(payload?.message?.content); return parseModelOutput(payload?.message?.content);
} }
function defaultTickDelayMs() { function defaultTickDelayMs() {
@@ -576,12 +593,16 @@ async function runTick() {
lastInfoSnapshot: snapshot, lastInfoSnapshot: snapshot,
}); });
const systemPrompt = await readSystemPrompt(); const systemPrompt = await readSystemPrompt();
const text = await generateCommentary(systemPrompt, snapshot); const modelResult = await generateCommentary(systemPrompt, snapshot);
updateStatus({
lastModelRawOutput: modelResult?.raw || '',
});
const text = modelResult?.normalized;
if (!text) { if (!text) {
logger.info('Commentary tick produced SKIP/empty output', { tickId }); logger.info('Commentary tick produced SKIP/empty output', { tickId });
updateStatus({ updateStatus({
lastOutcome: 'skipped', lastOutcome: 'skipped',
lastReason: 'model returned SKIP/empty', lastReason: modelResult?.raw?.trim() ? 'model returned SKIP' : 'model returned empty',
lastGeneratedText: null, lastGeneratedText: null,
}); });
// Immediate retry after model skip. // Immediate retry after model skip.
+54
View File
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx'; import { useSession } from '../context/SessionContext.jsx';
import RoverRoster from './RoverRoster.jsx'; import RoverRoster from './RoverRoster.jsx';
import ChatMessageRow from './ChatMessageRow.jsx';
const MODES = [ const MODES = [
{ key: 'open', label: 'Open' }, { key: 'open', label: 'Open' },
@@ -284,6 +285,7 @@ function LlmCommentaryPanel({ status, onClearHistory, clearingHistory }) {
: status.lastOutcome === 'posted' : status.lastOutcome === 'posted'
? 'text-emerald-300' ? 'text-emerald-300'
: 'text-slate-300'; : 'text-slate-300';
const conversationRows = buildLlmConversationRows(status);
return ( return (
<div className="space-y-0.5"> <div className="space-y-0.5">
@@ -405,10 +407,62 @@ function LlmCommentaryPanel({ status, onClearHistory, clearingHistory }) {
: 'No snapshot captured yet.'} : 'No snapshot captured yet.'}
</pre> </pre>
</details> </details>
<div className="space-y-0.5">
<div className="panel-muted text-xs uppercase">Most recent LLM conversation</div>
<div className="surface max-h-72 space-y-0.5 overflow-y-auto">
{conversationRows.length ? (
conversationRows.map((row) => <ChatMessageRow key={row.id} message={row.message} />)
) : (
<div className="text-xs text-slate-300">No model conversation captured yet.</div>
)}
</div>
</div>
</div> </div>
); );
} }
function buildLlmConversationRows(status) {
const now = Date.now();
const modelMessages = Array.isArray(status?.lastModelMessages) ? status.lastModelMessages : [];
const rows = modelMessages.map((entry, index) => {
const role = String(entry?.role || '').toLowerCase();
const content =
typeof entry?.content === 'string' ? entry.content : JSON.stringify(entry?.content ?? null, null, 2);
const nickname =
role === 'system'
? 'LLM System'
: role === 'assistant'
? 'LLM Context'
: role === 'user'
? 'LLM Input'
: 'LLM Message';
return {
id: `llm-msg-${index}`,
message: {
ts: now + index,
nickname,
text: content,
role: 'spectator',
system: role === 'system',
},
};
});
if (status?.lastModelRawOutput != null) {
const raw = String(status.lastModelRawOutput);
rows.push({
id: 'llm-output',
message: {
ts: now + rows.length + 1,
nickname: 'LLM Output',
text: raw.trim() ? raw : '<empty>',
role: 'spectator',
system: true,
},
});
}
return rows;
}
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 };