Commit Graph
9 Commits
Author SHA1 Message Date
asepharyana cf1ec25c71 fix(chatbot): disable thinking + use non-streaming LLM call
- Set stream:false on the /chat/completions request so the bot gets one
  complete response instead of an SSE token stream.
- Add reasoning_effort:"none" to suppress extended-thinking/reasoning tokens
  (ignored by non-reasoning models like gemini-flash-lite).
- Add parseResponse(): handles both the JSON object 9router returns for
  stream:false and the SSE text it may still emit, delegating SSE to parseSse.
  Verified live: omniroute returns 200 application/json with message.content.
2026-08-16 17:08:29 +07:00
asepharyana 38bda66933 style: fix Biome dead-code warnings from oRPC migration (CI lint gate) 2026-08-16 14:51:47 +07:00
asepharyanaandClaude Opus 4.5 2fa1827f17 feat(backend,frontend): migrate data APIs from REST to native tRPC over WebSocket
Replace REST module routers with a single typed tRPC appRouter served over
/trpc (HTTP + WebSocket), and rewire the frontend to call it via
@trpc/client wsLink (browser) and httpLink (RSC data layer). Existing
/api/health + /api/metrics stay as plain Express for infra scraping.

Notable fixes surfaced by the live smoke test:
- Express 5 / path-to-regexp v8 rejects the /trpc/* wildcard route; use a
  prefix middleware that computes opts.path from the URL instead.
- nodeHTTPRequestHandler treats opts.path as the literal procedure path, so
  it is derived per-request from req.url.
- Two ws servers on one http.Server (the /ws voice socket + /trpc) collided
  and returned 400 on upgrade; both now use noServer + a manually routed
  server.on('upgrade') keyed by path.

Verified: BE tsc+biome+40 vitest green; FE tsc+biome green; live
HTTP and WebSocket calls returned real prod data.

Co-Authored-By: Claude Opus 4.5 (1M context) <noreply@anthropic.com>
2026-08-16 13:25:10 +07:00
asepharyanaandClaude Opus 5 (Nous Research) b67856462f feat(chatbot): expand tool set to cover all server-watcher situations
The chatbot agent now has 14 tools (was 4) so it can answer about ANY
server situation from live data instead of a static snapshot:

- get_server_stats (now also returns clean count)
- get_top_channels, get_recent_activity, get_top_flagged
- search_messages (LIKE keyword search)
- get_user_messages, get_user_profile, get_user_reputation
- get_channel_culture
- get_message_detail (full AI analysis of one message)
- get_message_reviews (human moderation queue by status)
- get_voice_recordings (with transcriptions)
- get_moderation_timeline (daily flagged/warn/clean trend)
- get_corrections (AI false-positive correction history)

Security/quality:
- Every executor now uses parameterized drizzle queries (eq/like/and).
  The old code interpolated model-supplied IDs into sql.raw() — a SQL
  injection vector. Removed.
- Split static tool *definitions* into chatbot.toolDefs.ts (no DB import)
  so the LLM-facing schema can be unit-tested without loading the
  database/config layer. chatbot.tools.ts keeps only the executor.

Verified: tsc + biome clean, 40 backend tests pass (4 new covering the
tool-contract: names unique, required args declared, full situation
coverage).

Co-Authored-By: Claude Opus 5 (Nous Research)
2026-08-16 09:22:20 +07:00
asepharyanaandClaude Opus 5 (Nous Research) 30828a5534 refactor(chatbot): drop static server-stats context, go fully tool-based
The chatbot already had an agentic tool loop (get_server_stats,
get_top_channels, get_recent_activity, get_top_flagged), but processMessage
still baked a serverInsights snapshot into the system prompt and told the
model to "answer from that data". That defeats the tools: the model answered
from a stale snapshot instead of living numbers, and the guild/channel scope
the frontend sends was never forwarded to the tools.

Changes (services/backend/src/modules/chatbot):
- Remove getServerInsights() + ServerInsights (dead after this change).
- buildSystemPrompt(): drop the hardcoded stats block; instruct the model it
  has NO memorized server numbers and MUST call a tool for any server-data
  question, answering only from tool results.
- processMessage(): stop fetching insights; pass the request guildId/channelId
  scope through to callLLM.
- callLLM(): accept scope; auto-fill empty guildId/channelId on tool calls from
  the request scope so the model never has to guess IDs and tools always query
  the right server.

Behavior: answers now come from live DB data via tools, scoped to the server
the user is chatting in. tsc + biome + 36 backend tests green.

Co-Authored-By: Claude Opus 5 (Nous Research)
2026-08-16 09:15:39 +07:00
asepharyana d1c1f3e4a7 feat(chatbot): per-user history via X-User-Id + agentic tools calling
Backend:
- New chatbot.tools.ts: 4 tools (get_server_stats, get_top_channels,
  get_recent_activity, get_top_flagged) with real DB executors
- chatbot.service: agentic loop — stream:true, parse SSE, execute
  tool_calls, feed results back, up to 4 rounds
- controller: resolve userId from X-User-Id header (no-login device
  uuid) with auth middleware precedence; history/clear scoped per user

Frontend:
- use-chatbot-user: mint UUID in localStorage, send as X-User-Id
- chatbotApi.send/getHistory/clearHistory accept userId header
- client.ts: apiRequest supports custom headers per call
- provider: history load + send + clear keyed to device user id
2026-08-03 06:24:19 +07:00
asepharyana 6293d588bc chore(lint): biome cleanup across services — format, sort imports, drop unused
- discord-gateway: 74 lint errors -> 0 (format, import sorting, unused
  imports/vars, dead breath var)
- backend: format + sort imports (11 warnings left: noExplicitAny)
- frontend: remove unused imports, drop dead breathing var, fix
  useExhaustiveDependencies (scroll keyed on messages), a11y biome-ignore
  for drag surface + stopPropagation container (mouse-only gestures)
- remaining warnings are false positives: index keys on static lists,
  <img> in static export (next/image unsupported), noExplicitAny

tsc --noEmit clean on all 3 services; vitest green (60+36).
2026-08-01 22:09:02 +07:00
Developer dcd13482c2 refactor: break monorepo into 3 standalone services (gateway, backend, frontend)
Build & Deploy / build-and-push (backend) (push) Failing after 35s
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 25s
Build & Deploy / build-and-push (proxy) (push) Failing after 25s
- Remove pnpm workspace, moon repo, and all monorepo tooling
- Delete packages/shared/, embed shared code directly into each service
- Copy packages/shared/src/* -> services/backend/src/shared/ and services/discord-gateway/src/shared/
- Replace all @bete/shared imports with @/shared/ path alias
- Remove @bete/shared workspace dependency from both services
- Update root package.json scripts from --filter to --prefix
- Rewrite Dockerfiles to build each service standalone
- Clean up biome.json, .gitignore, remove root drizzle.config.ts
2026-07-30 11:50:48 +07:00
DeveloperandClaude Opus 4.8 977a6f9653 refactor: rename mascot to chatbot across entire codebase
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 30s
Build & Deploy / build-and-push (backend) (push) Failing after 2m28s
Build & Deploy / build-and-push (proxy) (push) Successful in 2m28s
- Backend: mascot-chat module → chatbot, routes /mascot/chat → /chat
- Shared schema: pgMascotChatMessagesTable → pgChatbotMessagesTable
- Frontend: MascotProvider/useMascot → ChatbotProvider/useChatbot
- Gateway schema: update exports to match shared schema
- Docs: update all .md references (CLAUDE.md, README, specs, plans)
- All API routes, controller names, service classes, types renamed

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 15:00:29 +07:00