From 4eb0a9c60848c51ede407dfe6ff37ed4ffe54ce2 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Mon, 17 Aug 2026 18:01:45 -0400 Subject: [PATCH] Offer the assistant the same formatting the mesh composer has, and drop the mark lower MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Colour is chosen by name and mapped to a fixed value here, so the model never writes a style property — the same closed-set rule the mesh store uses. --- Plugin/unraid/Tools/ai_chat_worker.php | 26 ++++++++++++++++++++++++++ Plugin/unraid/include/ai_chat.php | 22 +++++++++++++++++++++- Plugin/unraid/include/node_chat.php | 2 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/Plugin/unraid/Tools/ai_chat_worker.php b/Plugin/unraid/Tools/ai_chat_worker.php index 2da2f00..d7e67dc 100644 --- a/Plugin/unraid/Tools/ai_chat_worker.php +++ b/Plugin/unraid/Tools/ai_chat_worker.php @@ -915,6 +915,32 @@ if ($profile === 'chat' && vv_ai_chat_needs_varaverk($question)) { if ($context !== '') $system .= "PASSAGES\n" . $context; +// What the transcript can render, offered rather than required. +// +// Appended to every profile at the single point they all pass through, so a new profile cannot +// be added without it. The set is the one the mesh composer already offers, and the renderer +// maps colour NAMES to fixed values — the model never writes a colour, only chooses one, so +// nothing it emits can become a style property. +// +// Deliberately permissive in tone. The interesting question is whether it reaches for these at +// all and where; an instruction to use them would answer that question by force and tell us +// nothing. Prose stays prose. +$system .= "\n\nFORMATTING AVAILABLE TO YOU\n" + . "The transcript renders a small set of marks. Use them where they genuinely help a " + . "reader — a warning that must not be skimmed, a value worth picking out of a " + . "sentence — and not otherwise. Plain prose is the default and needs no marking.\n" + . " **bold** emphasis\n" + . " *italic* aside or term\n" + . " __underline__ rarely; reserve for something that must not be missed\n" + . " {big}…{/big} one size up\n" + . " {small}…{/small} one size down, for asides\n" + . " {green}…{/green} also blue, amber, red, violet, grey\n" + . "Colour carries meaning here: green for confirmed-good, amber for caution, red for " + . "something broken or destructive. Do not colour text decoratively — a coloured word " + . "that means nothing costs the colours their meaning everywhere else. Code fences and " + . "`inline code` work as before and remain the right choice for anything typed at a " + . "shell.\n"; + $messages = [['role' => 'system', 'content' => $system]]; $hist = json_decode($historyJson ?: '[]', true); if (is_array($hist)) { diff --git a/Plugin/unraid/include/ai_chat.php b/Plugin/unraid/include/ai_chat.php index f665cf7..cc25b04 100644 --- a/Plugin/unraid/include/ai_chat.php +++ b/Plugin/unraid/include/ai_chat.php @@ -162,7 +162,7 @@ function vv_ai_chat_assets(): void { .vv-ai-chat-wrap { background:#0b0b0b; border-radius:6px; } .vv-ai-chat-wrap::before { content:''; position:absolute; inset:0; z-index:0; pointer-events:none; - background:url('/plugins/varaverk/icons/varaverk.png') center 58%/min(84%,520px) no-repeat; + background:url('/plugins/varaverk/icons/varaverk.png') center 74%/min(84%,520px) no-repeat; opacity:.032; filter:grayscale(1); } .vv-ai-empty { color:#3a3a3a; font-size:12px; text-align:center; padding:60px 20px; line-height:1.7; } @@ -789,12 +789,32 @@ vv_ai_profiles_script(); + `
${body}
`; } + // The same palette the mesh composer offers, by NAME only. The model never supplies a colour + // value — it picks from this list and the hex is looked up here, so nothing it writes can + // become a style property. Same closed-set principle as vv_nc_clean_style() on the server. + const FMT_COLORS = { green:'#4caf50', blue:'#4a9eff', amber:'#ff9800', + red:'#ef5350', violet:'#ab7df6', grey:'#bdbdbd' }; + // ── Minimal markdown, applied strictly after escaping ──────────────── function fmt(text) { let h = esc(text); h = h.replace(/```(\w*)\n([\s\S]*?)```/g, (m, l, c) => codeCard(l, c)); h = h.replace(/`([^`\n]+)`/g, '$1'); h = h.replace(/\*\*([^*\n]+)\*\*/g, '$1'); + + // Emphasis and size. Single-asterisk italics run after bold so ** is already consumed. + h = h.replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, '$1$2'); + h = h.replace(/__([^_\n]+)__/g, '$1'); + h = h.replace(/\{big\}([\s\S]*?)\{\/big\}/g, '$1'); + h = h.replace(/\{small\}([\s\S]*?)\{\/small\}/g, '$1'); + + // {green}…{/green}. An unknown name is left as written rather than guessed at — a colour + // nobody chose is worse than a pair of braces the reader can see and ignore. + h = h.replace(/\{(\w+)\}([\s\S]*?)\{\/\1\}/g, (m, name, inner) => { + const c = FMT_COLORS[name.toLowerCase()]; + return c ? `${inner}` : m; + }); + h = h.replace(/\[(\d+)\]/g, '[$1]'); return h; } diff --git a/Plugin/unraid/include/node_chat.php b/Plugin/unraid/include/node_chat.php index 70c678f..03d79c7 100644 --- a/Plugin/unraid/include/node_chat.php +++ b/Plugin/unraid/include/node_chat.php @@ -260,7 +260,7 @@ function vv_nc_pane_markup(string $prefix, bool $meshDefault = false, it can carry the badge without competing with the text. -->