fix(chat): raise history cap and add 30s frontend timeout
The demo chat was silently dropping conversation context past 40 turns
and leaving the thinking bubble spinning forever when the soul backend
hung — visitors saw a frozen UI with no way to know what went wrong.
Changes:
- Stored history cap raised from 40 → 200 messages so longer
conversations actually persist across page refreshes.
- History sent to backend per turn raised from 20 → 50 messages.
- 30s AbortController timeout on the /api/demo fetch — surfaces a
distinct "Took too long to respond" error instead of hanging.
- Restore script (restore-chat-js-with-preview.py) is now correctly
idempotent in both directions: detects when modal HTML is inlined
but chat JS got extracted to an asset, and re-injects fresh source
so extract-js picks up changes on the next build.
This commit is contained in:
@@ -352,7 +352,7 @@ CHAT_HTML_AND_JS = r"""
|
||||
if (!skipSave && role !== 'thinking') {
|
||||
session.messages = session.messages || [];
|
||||
session.messages.push({ role: role, text: text });
|
||||
if (session.messages.length > 40) session.messages = session.messages.slice(-40);
|
||||
if (session.messages.length > 200) session.messages = session.messages.slice(-200);
|
||||
saveSession(session);
|
||||
}
|
||||
return el;
|
||||
@@ -388,26 +388,36 @@ CHAT_HTML_AND_JS = r"""
|
||||
}
|
||||
if (turnstileVerified && !session._cfSent) { session._cfSent = true; }
|
||||
try {
|
||||
var hist = (session.messages || []).slice(-20).filter(function(m){ return m.role !== 'thinking'; }).map(function(m){
|
||||
var hist = (session.messages || []).slice(-50).filter(function(m){ return m.role !== 'thinking'; }).map(function(m){
|
||||
return {role: m.role === 'ai' ? 'assistant' : 'user', content: m.text};
|
||||
});
|
||||
var activated_nodes = _ra(session._m, msg);
|
||||
var questionsRemaining = (MAX - msgCount) - 1;
|
||||
if (questionsRemaining < 0) questionsRemaining = 0;
|
||||
var r = await fetch('/api/demo', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
message: msg,
|
||||
history: hist,
|
||||
cf_token: turnstileVerified && !session._cfSent ? turnstileToken : '',
|
||||
uid: session.uid || '',
|
||||
activated_nodes: activated_nodes,
|
||||
engram_node_count: (session._m && session._m.nodes) ? session._m.nodes.length : 0,
|
||||
questions_remaining: questionsRemaining,
|
||||
is_last_question: questionsRemaining === 0
|
||||
})
|
||||
});
|
||||
// 30s frontend timeout — surfaces a real error if the soul hangs
|
||||
// instead of leaving the thinking bubble spinning forever.
|
||||
var ctrl = new AbortController();
|
||||
var timeoutId = setTimeout(function() { ctrl.abort(); }, 30000);
|
||||
var r;
|
||||
try {
|
||||
r = await fetch('/api/demo', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
signal: ctrl.signal,
|
||||
body: JSON.stringify({
|
||||
message: msg,
|
||||
history: hist,
|
||||
cf_token: turnstileVerified && !session._cfSent ? turnstileToken : '',
|
||||
uid: session.uid || '',
|
||||
activated_nodes: activated_nodes,
|
||||
engram_node_count: (session._m && session._m.nodes) ? session._m.nodes.length : 0,
|
||||
questions_remaining: questionsRemaining,
|
||||
is_last_question: questionsRemaining === 0
|
||||
})
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
var d = await r.json();
|
||||
if (thinking) thinking.remove();
|
||||
_um(session, d.sn, d.se);
|
||||
@@ -426,7 +436,10 @@ CHAT_HTML_AND_JS = r"""
|
||||
addMsg('ai', reply || 'Stepped out for a moment. Try again.');
|
||||
} catch(e) {
|
||||
if (thinking) thinking.remove();
|
||||
addMsg('ai', 'Stepped out for a moment. Try again.');
|
||||
var msg = (e && e.name === 'AbortError')
|
||||
? 'Took too long to respond — try again.'
|
||||
: 'Stepped out for a moment. Try again.';
|
||||
addMsg('ai', msg);
|
||||
}
|
||||
if (msgCount < MAX && btn) btn.disabled = false;
|
||||
if (input) input.focus();
|
||||
@@ -451,12 +464,41 @@ OLD_LINE = '<script src=\\"/assets/js/de72b8b61d75.js\\" defer></script>'
|
||||
|
||||
def main():
|
||||
src = STYLES_EL.read_text(encoding="utf-8")
|
||||
if MARKER in src:
|
||||
print("styles.el already contains the share preview modal - skipping")
|
||||
# If the anchor `<script src=...de72b8b61d75.js...>` is still present, the
|
||||
# asset extraction took the inline source out — so the chat JS in this
|
||||
# file is the source-of-truth and we re-inject it. We fall through the
|
||||
# MARKER check in that case, replacing both the modal HTML and the
|
||||
# script-tag in one shot.
|
||||
has_anchor = OLD_LINE in src
|
||||
if MARKER in src and not has_anchor:
|
||||
# Modal is inline AND asset has been re-extracted to a fresh hash.
|
||||
# Re-running here would clobber the new hash. Bail.
|
||||
print("styles.el already inlined and re-extracted - skipping")
|
||||
return
|
||||
if OLD_LINE not in src:
|
||||
# Maybe extract-js already pulled out a different hash; fail loud.
|
||||
print("ERROR: anchor `<script src=...fc247ef45b1d.js...>` not found in styles.el")
|
||||
if MARKER in src and has_anchor:
|
||||
# Modal HTML is there but the chat JS is still in the obfuscated
|
||||
# asset. Replace the entire modal+script block so extract-js picks
|
||||
# up the fresh source on the next build.
|
||||
import re
|
||||
# Match from the modal comment opener to (and including) the OLD_LINE.
|
||||
pattern = re.compile(
|
||||
r"<!--\s*Share preview modal:.*?" + re.escape(OLD_LINE),
|
||||
re.DOTALL,
|
||||
)
|
||||
if not pattern.search(src):
|
||||
print("ERROR: modal block + anchor pair not found in styles.el")
|
||||
raise SystemExit(1)
|
||||
# Use a lambda so re.sub doesn't parse the replacement as a template
|
||||
# (the chat HTML/JS contains `\s`, `\d`, etc. which would be treated
|
||||
# as backreferences in a normal replacement string).
|
||||
replacement = CHAT_HTML_AND_JS.strip()
|
||||
new_src = pattern.sub(lambda _m: replacement, src, count=1)
|
||||
STYLES_EL.write_text(new_src, encoding="utf-8")
|
||||
print("styles.el patched: replaced existing modal+anchor with fresh chat-widget JS")
|
||||
return
|
||||
if not has_anchor:
|
||||
# No marker, no anchor — file is in an unexpected state. Fail loud.
|
||||
print(f"ERROR: anchor `{OLD_LINE}` not found in styles.el")
|
||||
print(" Either it was renamed in a prior commit, or the file is already patched.")
|
||||
raise SystemExit(1)
|
||||
new_src = src.replace(OLD_LINE, CHAT_HTML_AND_JS.strip(), 1)
|
||||
|
||||
Reference in New Issue
Block a user