refactor: large codebase cleanup - consolidate schemas, migrate to Drizzle ORM, extract frontend components, modernize Docker builds
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 2m22s
Build & Deploy / build-and-push (backend) (push) Failing after 3m22s
Build & Deploy / build-and-push (proxy) (push) Successful in 1m36s
Build & Deploy / deploy (push) Skipped
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 2m22s
Build & Deploy / build-and-push (backend) (push) Failing after 3m22s
Build & Deploy / build-and-push (proxy) (push) Successful in 1m36s
Build & Deploy / deploy (push) Skipped
- Consolidate all DB schema definitions into packages/shared as single source of truth - Migrate backend from raw SQL to Drizzle ORM across all modules - Extract frontend inline UI into separate component files - Refactor discord-gateway circuitBreaker into conversationState + moderationState - Convert messageStore to Proxy singleton pattern - Add validateBody/validateQuery middleware + Zod schemas for API endpoints - Modernize Docker builds with multi-stage + pnpm deploy - Migrate CI/CD from deployment to image-based pipeline - Remove 60+ unused/dead files (~15K lines) - Update color scheme from sky-blue to teal-cyan - Move DB connection management to @bete/shared/database Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
63f21513bd
commit
5802d02e29
@@ -1,6 +1,6 @@
|
||||
import { createChildLogger } from "@bete/shared/logger";
|
||||
import { config } from "../../shared/config/config.js";
|
||||
import { getPendingMessagesByConversation } from "../message-capture/messageStore.js";
|
||||
import { messageStore } from "../message-capture/messageStore.js";
|
||||
import type { MessageRecord } from "../message-capture/types.js";
|
||||
import {
|
||||
pickBatchWithinBudget,
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
conversationProcessing,
|
||||
isConversationProcessingLocked,
|
||||
MAX_CONSECUTIVE_ERRORS,
|
||||
} from "./circuitBreaker.js";
|
||||
} from "./conversationState.js";
|
||||
|
||||
const logger = createChildLogger("batch-scheduler");
|
||||
|
||||
@@ -25,15 +25,10 @@ const logger = createChildLogger("batch-scheduler");
|
||||
/**
|
||||
* Schedules a debounced analysis run for a conversation.
|
||||
*
|
||||
* FIX #3: The async work inside setTimeout is now wrapped in an explicit
|
||||
* .catch() so DB errors don't produce unhandled promise rejections.
|
||||
* FIX #6: Calls pickBatchWithinBudget after fetching messages so token budget
|
||||
* is respected before handing the batch to the LLM.
|
||||
* FIX #7: Unified single-timer path -- always clear-and-reset one timer per
|
||||
* conversation key regardless of whether a cooldown is active. The delay is
|
||||
* simply max(cooldownRemainder+500, debounce) so the same timer serves both
|
||||
* the "throttled by error cooldown" and "normal debounce" cases, eliminating
|
||||
* the previous two-path logic that could leave both timers live simultaneously.
|
||||
* The async work inside setTimeout is wrapped in an explicit .catch() so
|
||||
* DB errors don't produce unhandled promise rejections. Uses a unified
|
||||
* single-timer path: always clear-and-reset one timer per conversation key
|
||||
* regardless of whether a cooldown is active.
|
||||
*/
|
||||
export function scheduleConversationAnalysis(conversationKey: string): void {
|
||||
if (isConversationProcessingLocked(conversationKey)) {
|
||||
@@ -65,18 +60,17 @@ export function scheduleConversationAnalysis(conversationKey: string): void {
|
||||
const timer = setTimeout(() => {
|
||||
conversationDebounceTimers.delete(conversationKey);
|
||||
|
||||
// FIX TOCTOU: Set lock synchronously BEFORE the async DB fetch starts
|
||||
if (isConversationProcessingLocked(conversationKey)) {
|
||||
return;
|
||||
}
|
||||
const processingStartedAt = Date.now();
|
||||
conversationProcessing.set(conversationKey, processingStartedAt);
|
||||
|
||||
// FIX #3: explicit .catch() -- no async arrow function to avoid unhandled rejection.
|
||||
getPendingMessagesByConversation(
|
||||
conversationKey,
|
||||
config.AI_ANALYSIS_MAX_BATCH_SIZE,
|
||||
)
|
||||
messageStore
|
||||
.getPendingMessagesByConversation(
|
||||
conversationKey,
|
||||
config.AI_ANALYSIS_MAX_BATCH_SIZE,
|
||||
)
|
||||
.then(async (messages: MessageRecord[]) => {
|
||||
if (messages.length === 0) {
|
||||
if (
|
||||
@@ -97,15 +91,14 @@ export function scheduleConversationAnalysis(conversationKey: string): void {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIX #6: trim to token budget before sending to LLM.
|
||||
let trimmed = pickBatchWithinBudget(
|
||||
processableMessages,
|
||||
config.AI_ANALYSIS_MAX_TARGET_TOKENS,
|
||||
50,
|
||||
);
|
||||
|
||||
// FIX #10: if every message individually exceeds the token budget,
|
||||
// fall back to the first message alone.
|
||||
// If every message individually exceeds the token budget,
|
||||
// fall back to the first message alone to avoid stuck-pending deadlock.
|
||||
if (trimmed.length === 0 && processableMessages.length > 0) {
|
||||
trimmed = processableMessages.slice(0, 1);
|
||||
logger.warn(
|
||||
|
||||
Reference in New Issue
Block a user