mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-28 07:35:27 -04:00
Cookbook model workflow fixes
This commit is contained in:
+73
-17
@@ -73,6 +73,45 @@ function isCompareActive() {
|
||||
return state.isActive;
|
||||
}
|
||||
|
||||
function _compareModeLabel() {
|
||||
return ({ search: ' search providers', agent: ' agents', research: ' research models' }[state._compareMode] || ' models');
|
||||
}
|
||||
|
||||
function _setToolbarMode(mode, syncModeTools = !state.isActive) {
|
||||
const target = mode === 'agent' ? 'agent' : 'chat';
|
||||
const toggleState = Storage.loadToggleState();
|
||||
toggleState.mode = target;
|
||||
Storage.saveToggleState(toggleState);
|
||||
const agentBtn = document.getElementById('mode-agent-btn');
|
||||
const chatBtn = document.getElementById('mode-chat-btn');
|
||||
const modeToggle = agentBtn?.closest('.mode-toggle') || chatBtn?.closest('.mode-toggle') || document.querySelector('.mode-toggle');
|
||||
if (agentBtn && chatBtn) {
|
||||
agentBtn.classList.toggle('active', target === 'agent');
|
||||
chatBtn.classList.toggle('active', target === 'chat');
|
||||
agentBtn.setAttribute('aria-pressed', target === 'agent' ? 'true' : 'false');
|
||||
chatBtn.setAttribute('aria-pressed', target === 'chat' ? 'true' : 'false');
|
||||
}
|
||||
if (modeToggle) {
|
||||
modeToggle.classList.toggle('mode-chat', target === 'chat');
|
||||
modeToggle.classList.toggle('mode-right', target === 'chat');
|
||||
}
|
||||
if (syncModeTools) {
|
||||
document.querySelectorAll('[data-mode-tool]').forEach(b => { b.style.display = target === 'agent' ? '' : 'none'; });
|
||||
}
|
||||
}
|
||||
|
||||
function _syncCompareModeFromToolbar(mode) {
|
||||
if (!state.isActive) return;
|
||||
state._compareMode = mode === 'agent' ? 'agent' : 'chat';
|
||||
_setToolbarMode(state._compareMode, false);
|
||||
const headerLabel = document.querySelector('.compare-header-label');
|
||||
if (headerLabel) {
|
||||
headerLabel.textContent = 'Comparing' + _compareModeLabel() + (state._blindMode ? ' (blind)' : '') + ' · ' + state._timeout + 's timeout';
|
||||
}
|
||||
const evalWrap = document.getElementById('cmp-eval-wrap');
|
||||
if (evalWrap && typeof evalWrap._renderItems === 'function') evalWrap._renderItems();
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────
|
||||
// ── closeCompare ──
|
||||
// ────────────────────────────────────────────────────────────────────────────
|
||||
@@ -170,12 +209,7 @@ async function deactivate(teardown) {
|
||||
});
|
||||
|
||||
// Restore agent/chat mode to what it was before compare
|
||||
const _ts = Storage.loadToggleState();
|
||||
_ts.mode = state._savedMode;
|
||||
Storage.saveToggleState(_ts);
|
||||
const _ab2 = document.getElementById('mode-agent-btn'), _cb2 = document.getElementById('mode-chat-btn');
|
||||
if (_ab2 && _cb2) { _ab2.classList.toggle('active', state._savedMode === 'agent'); _cb2.classList.toggle('active', state._savedMode === 'chat'); }
|
||||
document.querySelectorAll('[data-mode-tool]').forEach(b => { b.style.display = state._savedMode === 'agent' ? '' : 'none'; });
|
||||
_setToolbarMode(state._savedMode, true);
|
||||
|
||||
// Delete unsaved sessions, then reload
|
||||
if (teardown) {
|
||||
@@ -258,19 +292,30 @@ async function _buildCompareUI() {
|
||||
if (el) state._savedIndicatorDisplay[id] = el.style.display;
|
||||
});
|
||||
|
||||
// 5. Save current mode and lock to the right one for this compare type
|
||||
// 5. Save current mode and seed the toolbar for this compare type.
|
||||
const _toggleState = Storage.loadToggleState();
|
||||
state._savedMode = _toggleState.mode || 'chat';
|
||||
const _targetMode = (state._compareMode === 'agent') ? 'agent' : 'chat';
|
||||
_toggleState.mode = _targetMode;
|
||||
Storage.saveToggleState(_toggleState);
|
||||
_setToolbarMode(_targetMode, false);
|
||||
const _ab = document.getElementById('mode-agent-btn'), _cb = document.getElementById('mode-chat-btn');
|
||||
let _modeCleanup = null;
|
||||
const _onCompareModeClick = (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.stopImmediatePropagation();
|
||||
_syncCompareModeFromToolbar(ev.currentTarget === _ab ? 'agent' : 'chat');
|
||||
};
|
||||
if (_ab && _cb) {
|
||||
_ab.classList.toggle('active', _targetMode === 'agent');
|
||||
_cb.classList.toggle('active', _targetMode === 'chat');
|
||||
_ab.addEventListener('click', _onCompareModeClick, true);
|
||||
_cb.addEventListener('click', _onCompareModeClick, true);
|
||||
_modeCleanup = document.createElement('span');
|
||||
_modeCleanup.style.display = 'none';
|
||||
_modeCleanup._cleanup = () => {
|
||||
_ab.removeEventListener('click', _onCompareModeClick, true);
|
||||
_cb.removeEventListener('click', _onCompareModeClick, true);
|
||||
};
|
||||
}
|
||||
const _modeToggle = document.querySelector('.mode-toggle');
|
||||
if (_modeToggle) { _modeToggle.style.pointerEvents = 'none'; _modeToggle.style.opacity = '0.4'; }
|
||||
if (_modeToggle) { _modeToggle.style.pointerEvents = ''; _modeToggle.style.opacity = ''; }
|
||||
|
||||
// 6. Force tool toggles per compare mode
|
||||
disableToolToggles();
|
||||
@@ -289,6 +334,7 @@ async function _buildCompareUI() {
|
||||
// 7. Hide existing chat container children (preserves event listeners)
|
||||
const container = document.getElementById('chat-container');
|
||||
state._compareElements = [];
|
||||
if (_modeCleanup) state._compareElements.push(_modeCleanup);
|
||||
Array.from(container.children).forEach(child => {
|
||||
if (child.style.display === 'none') return;
|
||||
child.dataset.cmpHidden = '1';
|
||||
@@ -302,9 +348,9 @@ async function _buildCompareUI() {
|
||||
headerBar.className = 'compare-header-bar';
|
||||
headerBar.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:6px 10px;flex-shrink:0;';
|
||||
const headerLabel = document.createElement('span');
|
||||
headerLabel.className = 'compare-header-label';
|
||||
headerLabel.style.cssText = 'font-size:10px;font-weight:400;color:var(--fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;';
|
||||
const _modeLabel = ({ search: ' search providers', agent: ' agents', research: ' research models' }[state._compareMode] || ' models');
|
||||
headerLabel.textContent = 'Comparing' + _modeLabel + (state._blindMode ? ' (blind)' : '') + ' · ' + state._timeout + 's timeout';
|
||||
headerLabel.textContent = 'Comparing' + _compareModeLabel() + (state._blindMode ? ' (blind)' : '') + ' · ' + state._timeout + 's timeout';
|
||||
// Left side: the Compare tool icon (two side-by-side panes, matching the
|
||||
// rail/sidebar icon) + the label. Other tool headers carry their icon; this
|
||||
// one was missing it.
|
||||
@@ -475,7 +521,7 @@ async function _buildCompareUI() {
|
||||
}
|
||||
const msgTA = document.getElementById('message');
|
||||
if (msgTA) {
|
||||
msgTA.placeholder = 'Enter prompt for all models...';
|
||||
msgTA.placeholder = window.matchMedia('(max-width: 767px)').matches ? '' : 'Enter prompt for all models...';
|
||||
requestAnimationFrame(() => msgTA.focus());
|
||||
}
|
||||
|
||||
@@ -891,8 +937,7 @@ async function _executeCompare(message) {
|
||||
let sharedSearchContext = null;
|
||||
let sharedSearchSources = null;
|
||||
const webChk = document.getElementById('web-toggle');
|
||||
const toggleState = Storage.loadToggleState();
|
||||
const isAgentMode = (toggleState.mode || 'chat') === 'agent';
|
||||
const isAgentMode = state._compareMode === 'agent';
|
||||
const webOn = webChk && webChk.checked;
|
||||
// In agent mode, web_search is a tool (handled per-pane); in chat mode, pre-search and share
|
||||
if (webOn && !isAgentMode) {
|
||||
@@ -1198,6 +1243,15 @@ function _setupEvalPicker() {
|
||||
|
||||
function _renderItems() {
|
||||
const mode = state._compareMode || 'chat';
|
||||
const label = btn.querySelector('.cmp-eval-label');
|
||||
if (label) {
|
||||
label.textContent = ({
|
||||
agent: 'Agent prompts',
|
||||
chat: 'Chat prompts',
|
||||
search: 'Search prompts',
|
||||
research: 'Research prompts'
|
||||
}[mode] || 'Eval prompts');
|
||||
}
|
||||
// research/html aren't first-class compare types — fall back gracefully
|
||||
const key = EVAL_PROMPTS[mode] ? mode
|
||||
: (mode === 'research' ? 'search' : 'chat');
|
||||
@@ -1258,8 +1312,10 @@ function _setupEvalPicker() {
|
||||
};
|
||||
document.addEventListener('click', _onDocClick);
|
||||
|
||||
_renderItems();
|
||||
wrap.appendChild(btn);
|
||||
wrap.appendChild(menu);
|
||||
wrap._renderItems = _renderItems;
|
||||
inputTop.appendChild(wrap);
|
||||
|
||||
// Expected-answer chip — placed above the chat-input-bar (outside it), so
|
||||
|
||||
@@ -551,23 +551,46 @@ async function streamToPane(paneIdx, sessionId, message, aiMsgEl, opts) {
|
||||
footer.className = 'msg-footer';
|
||||
const span = document.createElement('span');
|
||||
span.className = 'response-metrics';
|
||||
let text = metrics.output_tokens + ' tokens | ' + metrics.tokens_per_second + ' tok/s';
|
||||
const outputTokens = metrics.output_tokens;
|
||||
const responseTime = metrics.response_time ?? metrics.total_time;
|
||||
const explicitTps = metrics.tokens_per_second ?? metrics.gen_tps ?? metrics.tps;
|
||||
const numericOutput = Number(outputTokens);
|
||||
const numericTime = Number(responseTime);
|
||||
const numericTps = Number(explicitTps);
|
||||
const derivedTps = Number.isFinite(numericTps)
|
||||
? numericTps
|
||||
: (Number.isFinite(numericOutput) && Number.isFinite(numericTime) && numericTime > 0)
|
||||
? numericOutput / numericTime
|
||||
: null;
|
||||
const tpsLabel = derivedTps != null
|
||||
? (derivedTps >= 100 ? String(Math.round(derivedTps)) : derivedTps.toFixed(2).replace(/\.?0+$/, ''))
|
||||
: null;
|
||||
const parts = [];
|
||||
if (outputTokens != null && outputTokens !== 'undefined') {
|
||||
parts.push(outputTokens + ' tokens');
|
||||
}
|
||||
if (tpsLabel != null) {
|
||||
parts.push(tpsLabel + ' tok/s');
|
||||
}
|
||||
if (responseTime != null && responseTime !== 'undefined' && parts.length === 0) {
|
||||
parts.push(responseTime + 's');
|
||||
}
|
||||
// Add per-request cost and cost per 1000
|
||||
const _model = metrics.model || (state._selectedModels[paneIdx] && state._selectedModels[paneIdx].model) || '';
|
||||
const _cost = getModelCost(_model, metrics.input_tokens || 0, metrics.output_tokens || 0);
|
||||
// Build the metrics span with optional cost and context
|
||||
span.textContent = text;
|
||||
span.textContent = parts.join(' | ');
|
||||
if (_cost !== null) {
|
||||
const _cost1k = _cost * 1000;
|
||||
const costSpan = document.createElement('span');
|
||||
costSpan.style.color = 'var(--color-success, #4caf50)';
|
||||
costSpan.title = 'Estimated cost per 1,000 responses like this one';
|
||||
costSpan.textContent = ' | $' + (_cost1k < 1 ? _cost1k.toFixed(2) : _cost1k.toFixed(0)) + '/1k';
|
||||
costSpan.textContent = (span.textContent ? ' | ' : '') + '$' + (_cost1k < 1 ? _cost1k.toFixed(2) : _cost1k.toFixed(0)) + '/1k';
|
||||
span.appendChild(costSpan);
|
||||
}
|
||||
if (metrics.context_percent > 0) {
|
||||
const ctx = document.createElement('span');
|
||||
ctx.textContent = ' | ' + metrics.context_percent + '% ctx';
|
||||
ctx.textContent = (span.textContent ? ' | ' : '') + metrics.context_percent + '% ctx';
|
||||
if (metrics.context_percent >= 85) ctx.style.color = 'var(--color-error)';
|
||||
else if (metrics.context_percent >= 70) ctx.style.color = '#ff9900';
|
||||
span.appendChild(ctx);
|
||||
|
||||
@@ -181,7 +181,7 @@ function handleVote(winnerIdx) {
|
||||
|
||||
let html = '';
|
||||
const caret = ' <span class="pane-title-caret">▾</span>';
|
||||
if (isWinner) html = '<span style="color:var(--red);margin-right:4px;">★</span><strong>' + escapeHtml(name) + '</strong> <span style="color:var(--red);font-size:0.82em;font-weight:800;text-transform:uppercase;letter-spacing:1px;position:relative;top:-2px;">Winner!</span>' + caret;
|
||||
if (isWinner) html = '<span style="color:var(--green, #50fa7b);margin-right:4px;">★</span><strong>' + escapeHtml(name) + '</strong> <span style="color:var(--green, #50fa7b);font-size:0.82em;font-weight:800;text-transform:uppercase;letter-spacing:1px;position:relative;top:0;">Winner!</span>' + caret;
|
||||
else if (isTie) html = '<span style="opacity:0.5;margin-right:4px;">=</span><strong>' + escapeHtml(name) + '</strong>' + caret;
|
||||
else html = '<strong>' + escapeHtml(name) + '</strong>' + caret;
|
||||
el.innerHTML = html;
|
||||
|
||||
Reference in New Issue
Block a user