Commit Graph
7 Commits
Author SHA1 Message Date
asepharyana fa72fe03cd feat(archive): show real channel/thread names in semantic search UI
Gateway archive embedder now parses metadata.channel.{channelName,threadName}
from each message and stores channel_name/thread_name in the Qdrant payload.
Backend exposes them; the semantic results card renders the thread name (or
channel name) instead of a raw #snowflake, with the ID as a last-resort
fallback for legacy points. Matches the message feed's channel-label logic
(getMessageChannelLabel).
2026-09-01 19:27:56 +07:00
asepharyana 26a690943d feat(archive): rich metadata in semantic search — username/channel/guild context + guild filter
Archive payload now stores username, channel_id, guild_id, thread_id and the
real message created_at (not embed time). Backend searchArray accepts an
optional guildId and applies a Qdrant payload filter so results can be scoped
to the guild being viewed. API/frontend expose the new fields and the
semantic results card shows who said it, in which channel, and when —
turning bare text blobs into contextual results. Old points fall back to
analyzed_at and omit the new fields gracefully.
2026-09-01 18:56:14 +07:00
asepharyana 7ef86c81ca feat(ai): audit + harden embedding pipeline
- Normalize text before embedding (strip mentions/URLs/emoji/markdown/control chars, lowercase, truncate) on both write and query sides so vectors aren't diluted and tokens aren't wasted
- embeddingClient: retry embeddings (maxRetries 2), validate batch dimension consistency, preserve index alignment for empty-normalized texts
- archiveEmbedder: store normalized text in archive payload, skip empty-normalized content
- backend: normalize search queries, make archive search similarity threshold configurable (AI_LLM_EMBEDDING_ARCHIVE_MIN_SIMILARITY, default 0.6)
2026-09-01 18:01:47 +07:00
asepharyana 50967f6481 feat(gateway): capture NSFW/age-restricted messages but skip AI analysis + exclude from public archive
Previously isAgeRestrictedMessage() early-returned in messageCreate/messageUpdate,
so NSFW-channel messages were never stored at all. Now they are captured like
any message (visible in dashboard), while the existing age-restricted skip path
(queueMessageAnalysis -> buildAgeRestrictedSkipResult) marks them clean with flag
age_restricted WITHOUT calling the LLM.

NSFW content is also deliberately kept OUT of the Qdrant public semantic-search
archive (archiveMessageEmbedded skips when isAgeRestricted), so it can't be found
via public web search. No schema change needed (metadata already carries channel.nsfw).
2026-08-30 20:13:35 +07:00
asepharyana d133cc3271 style(gateway): sort imports in archiveEmbedder (biome) 2026-08-18 15:53:18 +07:00
asepharyana 5a70a685b4 fix(gateway): correct @/ alias import style (no .js) in archiveEmbedder
Gateway @/ alias imports use no .js extension (relative imports
keep .js). The .js suffix on @/ paths caused double-extension
ERR_MODULE_NOT_FOUND (embeddingClient.js.js) at runtime.
2026-08-18 15:44:18 +07:00
asepharyana 1ae19074ee feat(gmw): moderation explainability + semantic message search
- Persist structured verdict (flags/severity/confidence/evidence) on
  moderation_actions so the public web can show WHY a message was moderated.
- Add a persistent Qdrant archive collection (gmw_message_archive); embed
  every captured message at capture time (fire-and-forget, best-effort).
- Public semantic search over the archive (backend oRPC + FE toggle on the
  messages view). Both features are read-only/public and fully automatic.

Migration: 0015_add_moderation_explainability.sql
2026-08-18 15:11:01 +07:00