Gate local Gemma behind explicit terms acceptance
Build and publish frontend / publish (push) Successful in 45s
Build and publish frontend / publish (push) Successful in 45s
This commit is contained in:
+52
-2
@@ -116,6 +116,7 @@
|
||||
<footer id="version-footer">Frontend <span id="frontend-version">__FRONTEND_VERSION__</span> · Statik <span id="static-version">hämtar version…</span> · API <span id="api-version">hämtar version…</span></footer>
|
||||
<script src="/piper-runtime.js?v=__FRONTEND_VERSION__"></script>
|
||||
<script src="/voice.js?v=__FRONTEND_VERSION__"></script>
|
||||
<script src="/static/wllama-gemma3-v1/local-ai.js"></script>
|
||||
<script>
|
||||
const auth = document.querySelector('#auth');
|
||||
const play = document.querySelector('#play');
|
||||
@@ -162,6 +163,7 @@
|
||||
line.dataset.role = role;
|
||||
line.textContent = `${options.speaker || (role === 'user' ? 'Du' : 'Berättelsen')}: ${content}`;
|
||||
transcript.append(line);
|
||||
if (role === 'user' || role === 'assistant') window.localGameAI?.record({type: 'dialogue', role, content});
|
||||
line.scrollIntoView({block: 'end'});
|
||||
if (role === 'assistant') {
|
||||
latestAssistantText = content;
|
||||
@@ -206,6 +208,28 @@
|
||||
const authVoiceStatus = document.querySelector('#auth-voice-status');
|
||||
if (authVoiceStatus && !auth.hidden && message) authVoiceStatus.textContent = message;
|
||||
}
|
||||
window.addEventListener('local-ai-status', event => {
|
||||
if (!currentAccount || currentAccount.is_admin || play.hidden) return;
|
||||
let statusLine = transcript.querySelector('li[data-local-ai-status]');
|
||||
if (!statusLine) {
|
||||
const detail = typeof event.detail === 'string' ? {text: event.detail} : event.detail;
|
||||
statusLine = appendLine(detail.type === 'terms-required' ? 'assistant' : 'system', detail.text || '', {speaker: 'Lokal AI', speak: detail.type === 'terms-required'});
|
||||
statusLine.dataset.localAiStatus = 'true';
|
||||
}
|
||||
const detail = typeof event.detail === 'string' ? {text: event.detail} : event.detail;
|
||||
statusLine.replaceChildren(document.createTextNode(`Lokal AI: ${detail.text || ''}`));
|
||||
if (detail.type === 'terms-required') {
|
||||
statusLine.append(document.createTextNode(' Läs dokumenten: '));
|
||||
for (const [label, url] of [['Spelvillkor för Gemma', detail.user_terms], ['Gemma-villkor', detail.terms], ['policy för förbjuden användning', detail.policy], ['modellinformation', detail.notice]]) {
|
||||
if (!url) continue;
|
||||
const link = document.createElement('a');
|
||||
link.href = url; link.target = '_blank'; link.rel = 'noopener noreferrer';
|
||||
link.textContent = label;
|
||||
statusLine.append(link, document.createTextNode('. '));
|
||||
}
|
||||
statusLine.append(document.createElement('br'), document.createTextNode(detail.command || ''));
|
||||
}
|
||||
});
|
||||
function updateSpeechProgress(message = '') {
|
||||
const container = document.querySelector('#speech-progress-container');
|
||||
const progress = document.querySelector('#speech-progress');
|
||||
@@ -700,6 +724,7 @@
|
||||
currentAccount = await request('/api/me');
|
||||
gameState = await request('/api/world');
|
||||
showPlay();
|
||||
if (!currentAccount.is_admin) window.localGameAI?.start(currentAccount.id);
|
||||
if (!gameState.character) {
|
||||
transcript.replaceChildren();
|
||||
appendInitialPrompt();
|
||||
@@ -866,6 +891,7 @@
|
||||
presenceTimer = null;
|
||||
playerMessagesPrimed = false;
|
||||
seenPlayerMessageIds.clear();
|
||||
window.localGameAI?.stop();
|
||||
await request('/api/logout', {method: 'POST'});
|
||||
if (location.pathname !== '/admin') appendLine('assistant', 'Du har loggat ut. Logga in igen för att fortsätta spela.');
|
||||
characterDraft = null;
|
||||
@@ -918,6 +944,12 @@
|
||||
document.querySelector('#dry-run-action').disabled = !enabled;
|
||||
}
|
||||
async function handleGameMessage(message, actionMode = 'normal') {
|
||||
if (actionMode === 'normal' && /^lokal ai(?:\s|$)/i.test(message.trim())) {
|
||||
const response = await window.localGameAI?.settings(message);
|
||||
appendLine('user', message);
|
||||
appendLine('assistant', response || 'Kommandon: “lokal ai villkor”, “lokal ai godkänn villkor”, “lokal ai avvisa villkor”, “lokal ai på/av”, “lokal ai loggar på/av”, “lokal ai exportera loggar” och “lokal ai radera loggar”.');
|
||||
return;
|
||||
}
|
||||
if (/^logga ut[.!]?$/i.test(message.trim())) {
|
||||
appendLine('user', message);
|
||||
await handleLogout();
|
||||
@@ -946,13 +978,26 @@
|
||||
appendLine('user', message, actionMode === 'dry_run' ? {speaker: 'Torrkörning'} : actionMode === 'redo' ? {speaker: 'Gör om'} : {});
|
||||
messageField.disabled = true;
|
||||
try {
|
||||
let clientAssistance = null;
|
||||
if (actionMode === 'normal' && currentAccount && !currentAccount.is_admin && window.localGameAI) {
|
||||
try {
|
||||
const history = [...transcript.querySelectorAll('li')].map(line => line.textContent).filter(Boolean);
|
||||
const assisted = await window.localGameAI.assist(message, history, gameState?.world || {});
|
||||
clientAssistance = {
|
||||
intent_hint: String(assisted.intent_hint || '').slice(0, 500),
|
||||
warning: String(assisted.warning || '').slice(0, 500),
|
||||
excerpts: Array.isArray(assisted.excerpts) ? assisted.excerpts.filter(item => typeof item === 'string' && item.length <= 500 && history.some(line => line.includes(item))).slice(0, 3) : [],
|
||||
};
|
||||
if (clientAssistance.warning) appendLine('system', `Lokal textkontroll: ${clientAssistance.warning}`, {speaker: 'Lokal AI', speak: false});
|
||||
} catch (_) { /* Local assistance is optional; the original input always proceeds. */ }
|
||||
}
|
||||
let result;
|
||||
for (let attempt = 0; attempt < 2; attempt += 1) {
|
||||
try {
|
||||
result = await request('/api/dialogue', {
|
||||
method: 'POST',
|
||||
headers: {'content-type': 'application/json'},
|
||||
body: JSON.stringify({message, expected_world_version: gameState.world.version, idempotency_key: idempotencyKey, forget_previous_turn: actionMode !== 'normal', dry_run: actionMode === 'dry_run'}),
|
||||
body: JSON.stringify({message, expected_world_version: gameState.world.version, idempotency_key: idempotencyKey, forget_previous_turn: actionMode !== 'normal', dry_run: actionMode === 'dry_run', client_assistance: clientAssistance}),
|
||||
});
|
||||
break;
|
||||
} catch (error) {
|
||||
@@ -987,7 +1032,12 @@
|
||||
refreshActionTools();
|
||||
} catch (error) {
|
||||
messageField.value = message;
|
||||
appendLine('assistant', `${explainDialogueError(error)} Ditt meddelande ligger kvar i inmatningen.`);
|
||||
try {
|
||||
const history = [...transcript.querySelectorAll('li')].map(line => line.textContent).filter(Boolean);
|
||||
const localReply = await window.localGameAI?.offline(message, history, gameState?.world || {});
|
||||
if (localReply) appendLine('assistant', `Lokalt, osparat förslag: ${localReply}`, {speaker: 'Lokal hjälp'});
|
||||
else appendLine('assistant', `${explainDialogueError(error)} Ditt meddelande ligger kvar i inmatningen.`);
|
||||
} catch (_) { appendLine('assistant', `${explainDialogueError(error)} Ditt meddelande ligger kvar i inmatningen.`); }
|
||||
}
|
||||
messageField.disabled = false;
|
||||
messageField.focus();
|
||||
|
||||
Reference in New Issue
Block a user