fix: add cascade to all files, return 401 when session is cleared

This commit is contained in:
Harshith Mullapudi 2025-10-24 22:10:41 +05:30
parent 6f1037e8e1
commit b78713df41
11 changed files with 108 additions and 44 deletions

View File

@ -284,33 +284,37 @@ export const Graph = forwardRef<GraphRef, GraphProps>(
// More nodes = need more space to prevent overcrowding // More nodes = need more space to prevent overcrowding
let scalingRatio: number; let scalingRatio: number;
if (nodeCount < 10) { if (nodeCount < 10) {
scalingRatio = 15; // Tight for small graphs scalingRatio = 20; // Slightly wider for small graphs
} else if (nodeCount < 50) { } else if (nodeCount < 50) {
scalingRatio = 20 + (nodeCount - 10) * 0.5; // Gradual increase scalingRatio = 30 + (nodeCount - 10) * 1.0; // Faster increase
} else if (nodeCount < 200) { } else if (nodeCount < 200) {
scalingRatio = 40 + (nodeCount - 50) * 0.2; // Slower increase scalingRatio = 70 + (nodeCount - 50) * 0.5; // More spread
} else if (nodeCount < 500) {
scalingRatio = 145 + (nodeCount - 200) * 0.3; // Continue spreading
} else { } else {
scalingRatio = Math.min(80, 70 + (nodeCount - 200) * 0.05); // Cap at 80 scalingRatio = Math.min(300, 235 + (nodeCount - 500) * 0.1); // Cap at 300
} }
// Calculate optimal gravity based on density and node count // Calculate optimal gravity based on density and node count
let gravity: number; let gravity: number;
if (density > 0.3) { if (density > 0.3) {
// Dense graphs need less gravity to prevent overcrowding // Dense graphs need less gravity to prevent overcrowding
gravity = 1 + density * 2; gravity = 0.5 + density * 1.5;
} else if (density > 0.1) { } else if (density > 0.1) {
// Medium density graphs // Medium density graphs
gravity = 3 + density * 5; gravity = 2 + density * 3;
} else { } else {
// Sparse graphs need more gravity to keep components together // Sparse graphs need more gravity to keep components together
gravity = Math.min(8, 5 + (1 - density) * 3); gravity = Math.min(6, 4 + (1 - density) * 2);
} }
// Adjust gravity based on node count // Adjust gravity based on node count - more aggressive reduction for large graphs
if (nodeCount < 20) { if (nodeCount < 20) {
gravity *= 1.5; // Smaller graphs benefit from stronger gravity gravity *= 1.5; // Smaller graphs benefit from stronger gravity
} else if (nodeCount > 100) { } else if (nodeCount > 100) {
gravity *= 0.8; // Larger graphs need gentler gravity gravity *= 0.5; // Larger graphs need much gentler gravity
} else if (nodeCount > 200) {
gravity *= 0.3; // Very large graphs need very gentle gravity
} }
// Calculate iterations based on complexity // Calculate iterations based on complexity
@ -374,10 +378,10 @@ export const Graph = forwardRef<GraphRef, GraphProps>(
settings: { settings: {
...settings, ...settings,
barnesHutOptimize: true, barnesHutOptimize: true,
strongGravityMode: true, strongGravityMode: false, // Disable strong gravity for more spread
gravity: optimalParams.gravity, gravity: optimalParams.gravity,
scalingRatio: optimalParams.scalingRatio, scalingRatio: optimalParams.scalingRatio,
slowDown: 3, slowDown: 1.5, // Reduced slowDown for better spreading
}, },
}); });

View File

@ -2,12 +2,9 @@ import { Prisma, PrismaClient } from "@core/database";
import invariant from "tiny-invariant"; import invariant from "tiny-invariant";
import { z } from "zod"; import { z } from "zod";
import { env } from "./env.server"; import { env } from "./env.server";
import { logger } from "./services/logger.service";
import { isValidDatabaseUrl } from "./utils/db"; import { isValidDatabaseUrl } from "./utils/db";
import { singleton } from "./utils/singleton"; import { singleton } from "./utils/singleton";
import { type Span } from "@opentelemetry/api";
export { Prisma }; export { Prisma };
export const prisma = singleton("prisma", getClient); export const prisma = singleton("prisma", getClient);

View File

@ -113,17 +113,25 @@ export const getClusteredGraphData = async (userId: string) => {
const session = driver.session(); const session = driver.session();
try { try {
// Get the simplified graph structure: Episode, Subject, Object with Predicate as edge // Get the simplified graph structure: Episode, Subject, Object with Predicate as edge
// Only include entities that are connected to more than 1 episode
const result = await session.run( const result = await session.run(
`// Get all statements with their episode and entity connections `// Find entities connected to more than 1 episode
MATCH (e:Episode)-[:HAS_PROVENANCE]->(s:Statement) MATCH (e:Episode)-[:HAS_PROVENANCE]->(s:Statement {userId: $userId})
WHERE s.userId = $userId MATCH (s)-[:HAS_SUBJECT|HAS_OBJECT|HAS_PREDICATE]->(ent:Entity)
WITH ent, count(DISTINCT e) as episodeCount
WHERE episodeCount > 1
WITH collect(ent.uuid) as validEntityUuids
// Get subject and object entities // Get statements where all entities are in the valid set
MATCH (e:Episode)-[:HAS_PROVENANCE]->(s:Statement {userId: $userId})
MATCH (s)-[:HAS_SUBJECT]->(subj:Entity) MATCH (s)-[:HAS_SUBJECT]->(subj:Entity)
WHERE subj.uuid IN validEntityUuids
MATCH (s)-[:HAS_PREDICATE]->(pred:Entity) MATCH (s)-[:HAS_PREDICATE]->(pred:Entity)
WHERE pred.uuid IN validEntityUuids
MATCH (s)-[:HAS_OBJECT]->(obj:Entity) MATCH (s)-[:HAS_OBJECT]->(obj:Entity)
WHERE obj.uuid IN validEntityUuids
// Return Episode, Subject, and Object as nodes with Predicate as edge label // Build relationships
WITH e, s, subj, pred, obj WITH e, s, subj, pred, obj
UNWIND [ UNWIND [
// Episode -> Subject // Episode -> Subject

View File

@ -1,5 +1,4 @@
import { json, type ActionFunctionArgs } from "@remix-run/node"; import { json } from "@remix-run/node";
import { requireUser } from "~/services/session.server";
import { deleteUser, getUserById } from "~/models/user.server"; import { deleteUser, getUserById } from "~/models/user.server";
import { sessionStorage } from "~/services/sessionStorage.server"; import { sessionStorage } from "~/services/sessionStorage.server";
import { cancelSubscriptionImmediately } from "~/services/stripe.server"; import { cancelSubscriptionImmediately } from "~/services/stripe.server";

View File

@ -110,7 +110,7 @@ export default function SingleConversation() {
} }
}, [run]); }, [run]);
const getConversations = () => { const conversations = React.useMemo(() => {
const lastConversationHistoryId = const lastConversationHistoryId =
conversationResponse?.conversationHistoryId; conversationResponse?.conversationHistoryId;
@ -124,15 +124,17 @@ export default function SingleConversation() {
); );
// Filter out any conversation history items that come after the lastConversationHistoryId // Filter out any conversation history items that come after the lastConversationHistoryId
const filteredConversationHistory = lastConversationHistoryId return lastConversationHistoryId
? sortedConversationHistory.filter((_ch, currentIndex: number) => { ? sortedConversationHistory.filter((_ch, currentIndex: number) => {
return currentIndex <= lastIndex; return currentIndex <= lastIndex;
}) })
: sortedConversationHistory; : sortedConversationHistory;
}, [conversationResponse, conversationHistory]);
const getConversations = () => {
return ( return (
<> <>
{filteredConversationHistory.map((ch: ConversationHistory) => { {conversations.map((ch: ConversationHistory) => {
return <ConversationItem key={ch.id} conversationHistory={ch} />; return <ConversationItem key={ch.id} conversationHistory={ch} />;
})} })}
</> </>

View File

@ -266,7 +266,7 @@ export const handleSessionRequest = async (
await transport.handleRequest(req, res); await transport.handleRequest(req, res);
} else { } else {
res.status(400).send("Invalid or missing session ID"); res.status(401).send("Invalid or missing session ID");
return; return;
} }
} else { } else {

View File

@ -153,7 +153,11 @@ export const ingestTask = task({
// Handle space assignment after successful ingestion // Handle space assignment after successful ingestion
try { try {
// If spaceIds were explicitly provided, immediately assign the episode to those spaces // If spaceIds were explicitly provided, immediately assign the episode to those spaces
if (episodeBody.spaceIds && episodeBody.spaceIds.length > 0 && episodeDetails.episodeUuid) { if (
episodeBody.spaceIds &&
episodeBody.spaceIds.length > 0 &&
episodeDetails.episodeUuid
) {
logger.info(`Assigning episode to explicitly provided spaces`, { logger.info(`Assigning episode to explicitly provided spaces`, {
userId: payload.userId, userId: payload.userId,
episodeId: episodeDetails.episodeUuid, episodeId: episodeDetails.episodeUuid,
@ -205,7 +209,10 @@ export const ingestTask = task({
// Auto-trigger session compaction if episode has sessionId // Auto-trigger session compaction if episode has sessionId
try { try {
if (episodeBody.sessionId && currentStatus === IngestionStatus.COMPLETED) { if (
episodeBody.sessionId &&
currentStatus === IngestionStatus.COMPLETED
) {
logger.info(`Checking if session compaction should be triggered`, { logger.info(`Checking if session compaction should be triggered`, {
userId: payload.userId, userId: payload.userId,
sessionId: episodeBody.sessionId, sessionId: episodeBody.sessionId,

View File

@ -442,7 +442,7 @@ async function handleGetSpace(args: any) {
const spaceDetails = { const spaceDetails = {
id: space.id, id: space.id,
name: space.name, name: space.name,
summary: space.summary, description: space.description,
}; };
return { return {

View File

@ -20,7 +20,7 @@ model Activity {
// Used to link the task or activity to external apps // Used to link the task or activity to external apps
sourceURL String? sourceURL String?
integrationAccount IntegrationAccount? @relation(fields: [integrationAccountId], references: [id]) integrationAccount IntegrationAccount? @relation(fields: [integrationAccountId], references: [id], onDelete: Cascade)
integrationAccountId String? integrationAccountId String?
rejectionReason String? rejectionReason String?
@ -83,7 +83,7 @@ model ConversationExecutionStep {
metadata Json? @default("{}") metadata Json? @default("{}")
conversationHistory ConversationHistory @relation(fields: [conversationHistoryId], references: [id]) conversationHistory ConversationHistory @relation(fields: [conversationHistoryId], references: [id], onDelete: Cascade)
conversationHistoryId String conversationHistoryId String
} }
@ -96,7 +96,7 @@ model ConversationHistory {
message String message String
userType UserType userType UserType
activity Activity? @relation(fields: [activityId], references: [id]) activity Activity? @relation(fields: [activityId], references: [id], onDelete: Cascade)
activityId String? activityId String?
context Json? context Json?
@ -105,7 +105,7 @@ model ConversationHistory {
user User? @relation(fields: [userId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String? userId String?
conversation Conversation @relation(fields: [conversationId], references: [id]) conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
conversationId String conversationId String
ConversationExecutionStep ConversationExecutionStep[] ConversationExecutionStep ConversationExecutionStep[]
} }
@ -124,7 +124,7 @@ model IngestionQueue {
workspaceId String workspaceId String
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
activity Activity? @relation(fields: [activityId], references: [id]) activity Activity? @relation(fields: [activityId], references: [id], onDelete: Cascade)
activityId String? activityId String?
// Error handling // Error handling
@ -168,7 +168,7 @@ model IntegrationAccount {
integratedBy User @relation(references: [id], fields: [integratedById], onDelete: Cascade) integratedBy User @relation(references: [id], fields: [integratedById], onDelete: Cascade)
integratedById String integratedById String
integrationDefinition IntegrationDefinitionV2 @relation(references: [id], fields: [integrationDefinitionId]) integrationDefinition IntegrationDefinitionV2 @relation(references: [id], fields: [integrationDefinitionId], onDelete: Cascade)
integrationDefinitionId String integrationDefinitionId String
workspace Workspace @relation(references: [id], fields: [workspaceId], onDelete: Cascade) workspace Workspace @relation(references: [id], fields: [workspaceId], onDelete: Cascade)
workspaceId String workspaceId String
@ -304,7 +304,7 @@ model OAuthClient {
workspaceId String? workspaceId String?
// Created by user (for audit trail) // Created by user (for audit trail)
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) createdBy User? @relation(fields: [createdById], references: [id], onDelete: Cascade)
createdById String? createdById String?
// Relations // Relations
@ -594,7 +594,7 @@ model WebhookConfiguration {
secret String? secret String?
isActive Boolean @default(true) isActive Boolean @default(true)
eventTypes String[] // List of event types this webhook is interested in, e.g. ["activity.created"] eventTypes String[] // List of event types this webhook is interested in, e.g. ["activity.created"]
user User? @relation(fields: [userId], references: [id]) user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String? userId String?
workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade) workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
workspaceId String? workspaceId String?

View File

@ -0,0 +1,47 @@
-- DropForeignKey
ALTER TABLE "Activity" DROP CONSTRAINT "Activity_integrationAccountId_fkey";
-- DropForeignKey
ALTER TABLE "ConversationExecutionStep" DROP CONSTRAINT "ConversationExecutionStep_conversationHistoryId_fkey";
-- DropForeignKey
ALTER TABLE "ConversationHistory" DROP CONSTRAINT "ConversationHistory_activityId_fkey";
-- DropForeignKey
ALTER TABLE "ConversationHistory" DROP CONSTRAINT "ConversationHistory_conversationId_fkey";
-- DropForeignKey
ALTER TABLE "IngestionQueue" DROP CONSTRAINT "IngestionQueue_activityId_fkey";
-- DropForeignKey
ALTER TABLE "IntegrationAccount" DROP CONSTRAINT "IntegrationAccount_integrationDefinitionId_fkey";
-- DropForeignKey
ALTER TABLE "OAuthClient" DROP CONSTRAINT "OAuthClient_createdById_fkey";
-- DropForeignKey
ALTER TABLE "WebhookConfiguration" DROP CONSTRAINT "WebhookConfiguration_userId_fkey";
-- AddForeignKey
ALTER TABLE "Activity" ADD CONSTRAINT "Activity_integrationAccountId_fkey" FOREIGN KEY ("integrationAccountId") REFERENCES "IntegrationAccount"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ConversationExecutionStep" ADD CONSTRAINT "ConversationExecutionStep_conversationHistoryId_fkey" FOREIGN KEY ("conversationHistoryId") REFERENCES "ConversationHistory"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ConversationHistory" ADD CONSTRAINT "ConversationHistory_activityId_fkey" FOREIGN KEY ("activityId") REFERENCES "Activity"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ConversationHistory" ADD CONSTRAINT "ConversationHistory_conversationId_fkey" FOREIGN KEY ("conversationId") REFERENCES "Conversation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "IngestionQueue" ADD CONSTRAINT "IngestionQueue_activityId_fkey" FOREIGN KEY ("activityId") REFERENCES "Activity"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "IntegrationAccount" ADD CONSTRAINT "IntegrationAccount_integrationDefinitionId_fkey" FOREIGN KEY ("integrationDefinitionId") REFERENCES "IntegrationDefinitionV2"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OAuthClient" ADD CONSTRAINT "OAuthClient_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "WebhookConfiguration" ADD CONSTRAINT "WebhookConfiguration_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -20,7 +20,7 @@ model Activity {
// Used to link the task or activity to external apps // Used to link the task or activity to external apps
sourceURL String? sourceURL String?
integrationAccount IntegrationAccount? @relation(fields: [integrationAccountId], references: [id]) integrationAccount IntegrationAccount? @relation(fields: [integrationAccountId], references: [id], onDelete: Cascade)
integrationAccountId String? integrationAccountId String?
rejectionReason String? rejectionReason String?
@ -83,7 +83,7 @@ model ConversationExecutionStep {
metadata Json? @default("{}") metadata Json? @default("{}")
conversationHistory ConversationHistory @relation(fields: [conversationHistoryId], references: [id]) conversationHistory ConversationHistory @relation(fields: [conversationHistoryId], references: [id], onDelete: Cascade)
conversationHistoryId String conversationHistoryId String
} }
@ -96,7 +96,7 @@ model ConversationHistory {
message String message String
userType UserType userType UserType
activity Activity? @relation(fields: [activityId], references: [id]) activity Activity? @relation(fields: [activityId], references: [id], onDelete: Cascade)
activityId String? activityId String?
context Json? context Json?
@ -105,7 +105,7 @@ model ConversationHistory {
user User? @relation(fields: [userId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String? userId String?
conversation Conversation @relation(fields: [conversationId], references: [id]) conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
conversationId String conversationId String
ConversationExecutionStep ConversationExecutionStep[] ConversationExecutionStep ConversationExecutionStep[]
} }
@ -124,7 +124,7 @@ model IngestionQueue {
workspaceId String workspaceId String
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
activity Activity? @relation(fields: [activityId], references: [id]) activity Activity? @relation(fields: [activityId], references: [id], onDelete: Cascade)
activityId String? activityId String?
// Error handling // Error handling
@ -168,7 +168,7 @@ model IntegrationAccount {
integratedBy User @relation(references: [id], fields: [integratedById], onDelete: Cascade) integratedBy User @relation(references: [id], fields: [integratedById], onDelete: Cascade)
integratedById String integratedById String
integrationDefinition IntegrationDefinitionV2 @relation(references: [id], fields: [integrationDefinitionId]) integrationDefinition IntegrationDefinitionV2 @relation(references: [id], fields: [integrationDefinitionId], onDelete: Cascade)
integrationDefinitionId String integrationDefinitionId String
workspace Workspace @relation(references: [id], fields: [workspaceId], onDelete: Cascade) workspace Workspace @relation(references: [id], fields: [workspaceId], onDelete: Cascade)
workspaceId String workspaceId String
@ -304,7 +304,7 @@ model OAuthClient {
workspaceId String? workspaceId String?
// Created by user (for audit trail) // Created by user (for audit trail)
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) createdBy User? @relation(fields: [createdById], references: [id], onDelete: Cascade)
createdById String? createdById String?
// Relations // Relations
@ -594,7 +594,7 @@ model WebhookConfiguration {
secret String? secret String?
isActive Boolean @default(true) isActive Boolean @default(true)
eventTypes String[] // List of event types this webhook is interested in, e.g. ["activity.created"] eventTypes String[] // List of event types this webhook is interested in, e.g. ["activity.created"]
user User? @relation(fields: [userId], references: [id]) user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String? userId String?
workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade) workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
workspaceId String? workspaceId String?