• exec/chat_llm.js

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Sun May 31 19:06:06 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/833cf8fc7bd53a679d83dd04
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: route issue priority/recommendation queries to git_issues

    classify_intent had no rule for issue queries phrased by importance, so the model free-formed and fabricated issue numbers + titles (observed in #synchronet: #12345 "Enhance Security Flags", then #98765 after being told it was hallucinating). Add routes so these reach the tool and the model picks from REAL issues:
    - "highest priority / most critical / most important (etc.) issue" and
    "which issue would you recommend" -> git_issues kind=recent (opened)
    - "most recent / latest / last (open|closed) issue" (singular / embedded,
    e.g. "poem about the most recently closed gitlab issue") -> git_issues
    recent, state from the open/closed word (fixes the model inventing a
    lookup iid:12345 for "most recently closed issue")
    Guarded against false positives -- "I have a critical issue with X" stays unrouted.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Aug 10 03:17:45 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/a7718428415a389396c1ec21
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: treat a repeated question as a failed answer

    Reply-text similarity cannot detect a stuck conversation. A user asked
    the same question three times running and got three answers that meant
    the same thing but scored only 0.33-0.58 against each other, because
    the model rewords itself at this temperature. Genuinely new answers on
    a single topic score 0.42-0.62, so the ranges overlap and no threshold separates them.

    The question itself is the reliable signal: someone retyping what they
    just asked is telling you the previous answer did not land, whatever
    its wording. When the incoming question matches one of that speaker's
    own recent questions, the previous answer is treated as a repeat
    outright, bypassing the similarity test, and the existing regenerate- then-deflect path runs.

    Only the speaker's own turns are compared -- the bot restating a
    question back is not the user asking twice. Similarity still governs
    whether the REGENERATED answer counts as another repeat, at a looser
    threshold, since the strict one would essentially never fire there.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Aug 10 03:17:45 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/e77a8de7278d146a717e0fcf
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: police gitlab.synchro.net URLs against fabrication

    The fabrication filter covered the wiki and the curated archive domains
    but not GitLab, so invented commit links passed straight through. A
    user was handed a commit URL for a SHA that does not exist, said the
    link was broken, and was handed a second invented one. A plausible
    commit URL is precisely the kind of thing someone clicks and trusts.

    gitlab.synchro.net now sits alongside the other policed domains, and
    the notion of a valid URL is generalised rather than special-cased:
    a URL may be cited if it appeared in the retrieved context, in the
    transcript the model was handed, in a tool result, or in the curated
    archive index.

    Tool results are the source that matters here -- an issue lookup
    returns a real issue URL, and nothing downstream could previously tell
    that apart from a URL the model composed. Both tool-execution paths
    record what they returned.

    Retrieved-context URLs are now collected from the injected text rather
    than only from wiki-prefixed provenance, so enabling a crawler whose
    chunks carry GitLab links cannot have the filter strip the very URLs
    retrieval just supplied.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Tue Sep 22 20:05:24 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/397b470253e6b75bb98d38f9
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: stop the fabrication filter deleting real GitLab issue URLs

    The URL policing added in e77a8de727 (maximum-44-into, 2026-08-10) keys
    every URL in a reply against the set the tools and retrieval actually
    returned, and deletes the rest. It matches them by exact text.

    GitLab serves one issue at two paths: /-/issues/<iid>, which every
    human-facing link uses and which a model writes unprompted, and the newer /-/work_items/<iid>, which is what the git_issues tool returns. So the
    filter saw a URL no tool had produced and removed a link that was real,
    leaving the clause that introduced it pointing nowhere -- "check for the
    full list", "see the wiki: See for the full details".

    Normalize the two spellings to one key so either matches.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Fri Oct 2 15:10:06 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/b503bdb9cbe54284353a706b
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: let the guru tell people which model it runs on

    Asked "what LLM are you using?" on IRC, the guru deflected ("whatever
    LLM is configured in chat_llm.ini") because nothing in its prompt named
    the model, and the question pulled the generic howto:llm-guru wiki text
    into retrieval, which it then parroted.

    Add an @llm_model@ persona macro, expanded from the configured model=,
    so a persona can state the real model and it tracks config changes.
    Classify "which model/LLM/AI are you" style questions as conversational
    so they skip retrieval like the other about-the-bot questions.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Fri Oct 2 15:10:06 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/f79e31c6dc4b3dc94adb171d
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: convert em-dashes and curly quotes in replies to ASCII

    The persona forbids em-dashes, but qwen3.5:9b emits them anyway, along
    with curly quotes and en-dashes, which mojibake on CP437 terminals and
    in IRC relays. sanitize_reply() now maps em-dash to " -- ", a spaced
    en-dash to " -- " and a bare one to "-", curly quotes to straight ones,
    and an ellipsis to "...".

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Fri Oct 16 21:19:03 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/2a48d15a6f3e497f8af7b03a
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: reject a provider/endpoint mismatch at config load

    Ollama serves both its native /api/chat and an OpenAI-compatible /v1/chat/completions on the same port, so pairing the wrong provider with
    an endpoint doesn't fail at the HTTP level. provider=openai against
    /api/chat failed only after the full generation, with "no choices in
    response: <raw JSON>"; a sysop (Keyop) hit exactly that in #synchronet
    while following the advice to switch endpoints.

    load_config() now compares the endpoint's path with the provider
    (/api/chat = ollama, .../chat/completions = openai) for both endpoint and endpoint_fallback, and throws an error naming the fix, before any
    request. For URLs whose path doesn't say (e.g. behind a proxy), the
    response parsers also recognize the other API's reply shape and say so
    instead of dumping the raw JSON.

    Tested against Ollama: both mismatches fail at once (0.0s) with the new messages; both correct pairings still reply; the live config still
    loads; chat_openai() aimed at /api/chat reports the native shape.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Fri Oct 16 21:19:03 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/87062103db874c07c2048efb
    Modified Files:
    exec/chat_llm.js
    Log Message:
    chat_llm: honor "= false" for boolean chat_llm.ini keys

    iniGetObject() already converts true/false values to booleans, but load_config() and its callers compared these keys against the STRING
    'false', which never matched. So setting any of memory_persist,
    reply_dedup, memory_summary_in_prompt, stream or simulate_typos to false
    was silently ignored. memory_persist = false, the documented setting for
    bots in public channels that shouldn't retain anything about strangers,
    still wrote per-speaker memory files.

    New cfg_bool() normalizes all five in load_config() (booleans, numbers,
    and false/no/off/0 strings; unset keeps the default of true), and the
    use sites test the booleans.

    Tested: an ini with the five keys off yields false for each, unset
    yields true, and a chat turn with memory_persist = false leaves the
    speaker's memory file untouched.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net