Repository navigation
Conversation
…d config UI - openai_backend: strip think/citation tags; treat truncated finish_reason or empty completion as LLMTruncatedError - router: channel @-mentions (respond_to_channel_mentions, mention_channels, mention_names, incl. continuations), history_key separation from the reply-thread key, per-sender/channel mention cooldown, thinking-notice dispatch, LLM_TRUNCATED_TEXT fallback, Sources-line stripped but kept in history, continuation budget fix - config: BotConfig mt_node/mt_mesh_name/mc_mesh_name identity fields + respond_to_channel_mentions/mention_channels/mention_names/ channel_reply_cooldown_seconds; ResponseConfig thinking_notice_seconds/ thinking_notice_text; MeshCoreContextConfig.mention_channels; parse_mt_node_id/parse_mt_node_num helpers - transport/connector/mesh_reporter/chunker/responder: reply_id plumbing, MeshCore sender parsing, channel_name propagation - dashboard: Config.tsx now exposes all of the above plus a new MeshCore Context section (previously configurable only via raw YAML) - tests: new coverage for mention detection, channel reply threading, history-key separation, LLM truncation handling, thinking notice, and MeshCore channel-sender parsing Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When someone sends !addme (case-insensitive, trailing text ok) in a configured MeshCore channel (default #aida), AIDA sends a flood self-advert (rate-limited), resolves the sender's pubkey via its own contact list then CoreScope (live.mwmesh.com), adds them as a contact (signed CMD 0x12 import when CoreScope has a signature-valid advert, falling back to the existing unsigned CMD 0x09 import_contact), and DMs them after a delay so the advert has time to propagate. Detection lives directly in MeshCoreTransport._on_channel_event (meshai/meshcore_addme.py), independent of respond_to_channel_mentions and observe_channels, so it works even when those are off. The registered !addme CommandHandler (meshai/commands/addme.py) exists only for !help visibility and is a deliberate no-op everywhere else (does nothing on Meshtastic or a MeshCore DM). New MeshCoreContextConfig fields (addme_enabled, addme_channels, addme_advert_cooldown_seconds, addme_dm_delay_seconds, addme_per_user_cooldown_seconds, addme_dm_text), a new addme_provenance audit table (migration v32), and matching dashboard Config.tsx controls under MeshCore Context. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ddme DM The !addme DM text (185 bytes formatted) plus the meshcore lib's 13-byte DM header (cmd+type+attempt+timestamp+6-byte dst prefix) produced a ~194-byte CMD_SEND_TXT_MSG frame, over openhop's companion hard cap of MAX_FRAME_SIZE=176 bytes. The companion logs "Frame too long: 194" and disconnects the client, dropping meshai's entire MeshCore link for the ~2.5 min idle-reap window -- not just the one !addme reply. - transport/meshcore_transport.py: add split_text_to_frame_limit() and MESHCORE_DM_MAX_TEXT_BYTES (153) / MESHCORE_CHANNEL_MAX_TEXT_BYTES (159), derived from openhop's MAX_FRAME_SIZE=176 minus the meshcore lib's actual per-kind header (13 bytes DM/room, 7 bytes channel) minus a 10-byte safety margin. send_message_async() -- the single choke point for every MeshCore text send (DM, room, channel broadcast, and LLM replies via responder.py/chunker.py) -- now measures UTF-8 bytes and splits at sentence/word boundaries (never mid-codepoint) before any frame reaches the wire, instead of letting an oversized one take the link down. - meshcore_addme.py / config.py: shorten DEFAULT_ADDME_DM_TEXT and the matching addme_dm_text config default so a typical formatted message (even a 20-char emoji name) clears the DM budget in one frame. - Logging: _send_dm_once_async and _do_mc_broadcast_async now log repr(exc) instead of str(exc) -- a bare asyncio.TimeoutError's str() is "", which read as a blank, silent failure line. meshcore_addme's _delayed_dm now logs "send failed (<reason>)" for a raised exception, distinct from "sent, no ACK" for a clean best-effort return. - Tests: split_text_to_frame_limit (ASCII, exact boundary, multibyte/emoji near the limit, a single glued word forcing a hard cut, the addme default text with a long emoji name), send_message_async's DM/channel guard wiring through the real send path, and the addme DM text assertion updated to the new wording. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Live response.thinking_notice_seconds was set to 30 via the dashboard API hot-apply (response is not in RESTART_REQUIRED_SECTIONS and the router re-reads self.config.response fresh per message, so no restart was needed). Bring the ResponseConfig dataclass default in line and update the stale doc comment in the thinking-notice test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Three fixes for the AIDA triple-DM incident (Matt's MeshCore "Thanks hows it going?"), plus the mention-name gap it surfaced: 1. Inbound de-dupe (meshai/dedupe.py, InboundDedupeCache): drops a client's automatic resend of a DM/channel-mention message (same sender/text/sender_timestamp, or same Meshtastic packet id) before it reaches should_respond()'s rest, route(), the LLM, or history a second time. Bounded (500 entries), TTL 10 min for id-keyed entries, 90s fallback window when no stable id is available. 2. Mention matching: the mention matcher now also accepts AIDA's own live MeshCore self-name (read fresh from self_info() every check) and bot.mc_mesh_name/mt_mesh_name, so "@[AIDA-MC]" (what MeshCore apps insert when tap-mentioning a device named "AIDA-MC") is recognized alongside "@aida"/"@[AIDA]". The mention regex's end-of-token boundary was widened to also treat '-'/'_' as identifier characters, so a hyphenated name must match in full ("@AIDA-MC" matches, "@AIDA-MCX" does not). 3. meshcore_context hot-apply: PUT /api/config/meshcore_context now calls the transport's (previously dead) set_context_config(), fixing the stale-reference bug where MeshCoreTransport._mc_context kept enforcing boot-time respond_to_dms/ignore_contacts/observe_channels/addme_* until a restart, even though the section isn't restart-required. bot.* mention/channel settings were already reading the live shared Config object and needed no fix. Root-cause fix for the actual triple-DM (outbound, not inbound): two independent retry state machines were stacked -- meshai's own no-ACK path-discovery-and-resend AND MeshMonitor's virtual node's own ACK tracker/RF retries. meshai now sends each DM exactly once by default (ConnectionConfig.meshcore_client_retry, default False) and only logs whether the ACK arrived; meshcore_ack_wait_seconds default raised 6->30s to clear MeshMonitor's own retry window. meshcore_client_retry=True restores the old resend-on-no-ACK behavior for a bare MeshCore link with no external retry layer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diagnosis from Matt's MeshCore chat (13:08-13:23 UTC): a reply whose whole
body was a stripped "Sources:" line sent an empty DM (chunk_response("")
-> [""]); a separate reply truncated at the 1024-token cap slipped through
because Open WebUI didn't surface finish_reason=="length".
- Responder.send_response drops empty/whitespace-only chunks before
transmitting; if nothing is left, it logs a WARNING and sends nothing.
- router.py: when the body is empty after _strip_sources_line, send the
citations as the visible answer ("Sources: X; Y" -> "Source: X; Y")
instead of blank text; if there's no usable citation content either,
fall back to LLM_TRUNCATED_TEXT like any other failed generation.
- New LLMConfig.require_sources_line (default False): when enabled, a
reply missing its required trailing "Sources:" line is treated as
truncated (LLMTruncatedError path) rather than relayed as-is.
- openai_backend.generate() now also treats usage.completion_tokens at or
above 98% of max_tokens as a truncation signal, even when finish_reason
isn't "length", and logs which signal fired.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
openai_backend.py) so meshai can talk to Open WebUI, routing to theaida-meshmodel.#aidamention detection, a "thinking" notice while a response is generated, and a runaway-response guard (truncation/sources-line handling) in the router.This is the branch backing the meshai → AIDA (Open WebUI) cutover. Opening this PR only to get CI signal on the branch (workflow only triggers on push/PR to
main); not merging.🤖 Generated with Claude Code