fix: document view is broken in log view

This commit is contained in:
Harshith Mullapudi 2025-10-09 15:44:24 +05:30
parent 2281dab166
commit a14b83d66d
6 changed files with 262 additions and 29 deletions

View File

@ -0,0 +1,220 @@
import ReactMarkdown from "react-markdown";
import type { Components } from "react-markdown";
import { cn } from "~/lib/utils";
const markdownComponents: Components = {
h1: ({ className, ...props }) => (
<h1
className={cn("mt-2 mb-1 text-3xl font-bold tracking-tight", className)}
{...props}
/>
),
h2: ({ className, ...props }) => (
<h2
className={cn(
"mt-2 mb-1 text-2xl font-semibold tracking-tight",
className,
)}
{...props}
/>
),
h3: ({ className, ...props }) => (
<h3
className={cn(
"mt-2 mb-1 text-xl font-semibold tracking-tight",
className,
)}
{...props}
/>
),
h4: ({ className, ...props }) => (
<h4
className={cn(
"mt-1.5 mb-0.5 text-lg font-semibold tracking-tight",
className,
)}
{...props}
/>
),
h5: ({ className, ...props }) => (
<h5
className={cn(
"mt-1.5 mb-0.5 text-base font-semibold tracking-tight",
className,
)}
{...props}
/>
),
h6: ({ className, ...props }) => (
<h6
className={cn(
"mt-1.5 mb-0.5 text-sm font-semibold tracking-tight",
className,
)}
{...props}
/>
),
p: ({ className, ...props }) => (
<p
className={cn(
"mb-1 leading-normal [&:not(:first-child)]:mt-1",
className,
)}
{...props}
/>
),
ul: ({ className, ...props }) => (
<ul
className={cn(
"my-1 ml-5 flex list-disc flex-col space-y-0 marker:text-gray-700 dark:marker:text-gray-400",
className,
)}
{...props}
/>
),
ol: ({ className, ...props }) => (
<ol
className={cn(
"my-1 ml-5 list-decimal space-y-0 marker:text-gray-700 dark:marker:text-gray-400",
className,
)}
{...props}
/>
),
li: ({ className, ...props }) => (
<li className={cn("py-0.5 pl-1 leading-normal", className)} {...props} />
),
blockquote: ({ className, ...props }) => (
<blockquote
className={cn(
"mt-1 mb-1 border-l-4 border-gray-300 pl-4 text-gray-700 italic dark:border-gray-600 dark:text-gray-300",
className,
)}
{...props}
/>
),
code: ({ className, inline, ...props }: any) =>
inline ? (
<code
className={cn(
"rounded bg-gray-100 px-1.5 py-0.5 font-mono text-sm text-gray-800 dark:bg-gray-800 dark:text-gray-200",
className,
)}
{...props}
/>
) : (
<code
className={cn(
"block rounded-lg bg-gray-100 p-4 font-mono text-sm text-gray-800 dark:bg-gray-800 dark:text-gray-200",
className,
)}
{...props}
/>
),
pre: ({ className, ...props }) => (
<pre
className={cn(
"mb-1 overflow-x-auto rounded-lg bg-gray-100 p-4 dark:bg-gray-800",
className,
)}
{...props}
/>
),
a: ({ className, ...props }) => (
<a
className={cn(
"font-medium text-blue-600 underline underline-offset-4 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300",
className,
)}
{...props}
/>
),
hr: ({ className, ...props }) => (
<hr
className={cn(
"my-2 border-t border-gray-300 dark:border-gray-600",
className,
)}
{...props}
/>
),
table: ({ className, ...props }) => (
<div className="mb-1 w-full overflow-auto">
<table
className={cn(
"w-full border-collapse border border-gray-300 dark:border-gray-600",
className,
)}
{...props}
/>
</div>
),
thead: ({ className, ...props }) => (
<thead
className={cn("bg-gray-100 dark:bg-gray-800", className)}
{...props}
/>
),
tbody: ({ className, ...props }) => (
<tbody className={cn("", className)} {...props} />
),
tr: ({ className, ...props }) => (
<tr
className={cn("border-b border-gray-300 dark:border-gray-600", className)}
{...props}
/>
),
th: ({ className, ...props }) => (
<th
className={cn(
"border border-gray-300 px-4 py-2 text-left font-semibold dark:border-gray-600",
className,
)}
{...props}
/>
),
td: ({ className, ...props }) => (
<td
className={cn(
"border border-gray-300 px-4 py-2 dark:border-gray-600",
className,
)}
{...props}
/>
),
strong: ({ className, ...props }) => (
<strong className={cn("font-bold", className)} {...props} />
),
em: ({ className, ...props }) => (
<em className={cn("italic", className)} {...props} />
),
};
interface StyledMarkdownProps {
children: string;
className?: string;
components?: Components;
}
export function StyledMarkdown({
children,
className,
components,
}: StyledMarkdownProps) {
return (
<div
className={cn(
"max-w-none",
"[&_ul_ul]:my-0.5 [&_ul_ul]:ml-4",
"[&_ol_ol]:my-0.5 [&_ol_ol]:ml-4",
"[&_ul_ol]:my-0.5 [&_ul_ol]:ml-4",
"[&_ol_ul]:my-0.5 [&_ol_ul]:ml-4",
className,
)}
>
<ReactMarkdown components={{ ...markdownComponents, ...components }}>
{children}
</ReactMarkdown>
</div>
);
}

View File

@ -9,6 +9,7 @@ import { cn, formatString } from "~/lib/utils";
import { getStatusColor } from "./utils"; import { getStatusColor } from "./utils";
import { format } from "date-fns"; import { format } from "date-fns";
import { SpaceDropdown } from "../spaces/space-dropdown"; import { SpaceDropdown } from "../spaces/space-dropdown";
import { StyledMarkdown } from "../common/styled-markdown";
interface LogDetailsProps { interface LogDetailsProps {
log: LogItem; log: LogItem;
@ -214,7 +215,7 @@ export function LogDetails({ log }: LogDetailsProps) {
{/* Log Content */} {/* Log Content */}
<div className="mb-4 w-full break-words whitespace-pre-wrap"> <div className="mb-4 w-full break-words whitespace-pre-wrap">
<div className="rounded-md"> <div className="rounded-md">
<Markdown>{log.ingestText}</Markdown> <StyledMarkdown>{log.ingestText}</StyledMarkdown>
</div> </div>
</div> </div>
</div> </div>
@ -254,7 +255,7 @@ export function LogDetails({ log }: LogDetailsProps) {
Content Content
</div> </div>
<div className="text-sm break-words whitespace-pre-wrap"> <div className="text-sm break-words whitespace-pre-wrap">
<Markdown>{episode.content}</Markdown> <StyledMarkdown>{episode.content}</StyledMarkdown>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,6 +4,7 @@ import type { StatementNode } from "@core/types";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
import { useNavigate } from "@remix-run/react"; import { useNavigate } from "@remix-run/react";
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import { StyledMarkdown } from "../common/styled-markdown";
export interface Episode { export interface Episode {
uuid: string; uuid: string;
@ -54,7 +55,7 @@ export function SpaceEpisodeCard({ episode }: SpaceFactCardProps) {
> >
<div className="flex w-full items-center justify-between gap-4"> <div className="flex w-full items-center justify-between gap-4">
<div className="inline-flex min-h-[24px] min-w-[0px] shrink items-center justify-start"> <div className="inline-flex min-h-[24px] min-w-[0px] shrink items-center justify-start">
<Markdown>{displayText}</Markdown> <StyledMarkdown>{displayText.slice(0, 300)}</StyledMarkdown>
</div> </div>
<div className="text-muted-foreground flex shrink-0 items-center justify-end gap-2 text-xs"> <div className="text-muted-foreground flex shrink-0 items-center justify-end gap-2 text-xs">
<Badge variant="secondary" className="rounded text-xs"> <Badge variant="secondary" className="rounded text-xs">

View File

@ -131,7 +131,9 @@ export const getIngestionQueueForFrontend = async (
(log.output as any)?.episodes?.length > 0 (log.output as any)?.episodes?.length > 0
) { ) {
// For DOCUMENT type: get episode details and space information for all episodes // For DOCUMENT type: get episode details and space information for all episodes
const episodeIds = (log.output as any)?.episodes; const episodeIds = (log.output as any)?.episodes.map(
(e: any) => e.episodeUuid,
);
// Fetch all episode details in parallel // Fetch all episode details in parallel
const episodeDetailsPromises = episodeIds.map((episodeId: string) => const episodeDetailsPromises = episodeIds.map((episodeId: string) =>

View File

@ -55,7 +55,7 @@ const SummaryResultSchema = z.object({
const CONFIG = { const CONFIG = {
maxEpisodesForSummary: 20, // Limit episodes for performance maxEpisodesForSummary: 20, // Limit episodes for performance
minEpisodesForSummary: 1, // Minimum episodes to generate summary minEpisodesForSummary: 1, // Minimum episodes to generate summary
summaryEpisodeThreshold: 10, // Minimum new episodes required to trigger summary (configurable) summaryEpisodeThreshold: 5, // Minimum new episodes required to trigger summary (configurable)
}; };
export const spaceSummaryQueue = queue({ export const spaceSummaryQueue = queue({
@ -85,7 +85,11 @@ export const spaceSummaryTask = task({
}); });
// Generate summary for the single space // Generate summary for the single space
const summaryResult = await generateSpaceSummary(spaceId, userId, triggerSource); const summaryResult = await generateSpaceSummary(
spaceId,
userId,
triggerSource,
);
if (summaryResult) { if (summaryResult) {
// Store the summary // Store the summary
@ -192,7 +196,10 @@ async function generateSpaceSummary(
const lastSummaryEpisodeCount = space.contextCount || 0; const lastSummaryEpisodeCount = space.contextCount || 0;
const episodeDifference = currentEpisodeCount - lastSummaryEpisodeCount; const episodeDifference = currentEpisodeCount - lastSummaryEpisodeCount;
if (episodeDifference < CONFIG.summaryEpisodeThreshold) { if (
episodeDifference < CONFIG.summaryEpisodeThreshold ||
lastSummaryEpisodeCount === 0
) {
logger.info( logger.info(
`Skipping summary generation for space ${spaceId}: only ${episodeDifference} new episodes (threshold: ${CONFIG.summaryEpisodeThreshold})`, `Skipping summary generation for space ${spaceId}: only ${episodeDifference} new episodes (threshold: ${CONFIG.summaryEpisodeThreshold})`,
{ {
@ -200,7 +207,7 @@ async function generateSpaceSummary(
lastSummaryEpisodeCount, lastSummaryEpisodeCount,
episodeDifference, episodeDifference,
threshold: CONFIG.summaryEpisodeThreshold, threshold: CONFIG.summaryEpisodeThreshold,
} },
); );
return null; return null;
} }
@ -211,7 +218,7 @@ async function generateSpaceSummary(
currentEpisodeCount, currentEpisodeCount,
lastSummaryEpisodeCount, lastSummaryEpisodeCount,
episodeDifference, episodeDifference,
} },
); );
} }
@ -361,9 +368,15 @@ async function generateUnifiedSummary(
// Space summary generation requires HIGH complexity (creative synthesis, narrative generation) // Space summary generation requires HIGH complexity (creative synthesis, narrative generation)
let responseText = ""; let responseText = "";
await makeModelCall(false, prompt, (text: string) => { await makeModelCall(
responseText = text; false,
}, undefined, 'high'); prompt,
(text: string) => {
responseText = text;
},
undefined,
"high",
);
return parseSummaryResponse(responseText); return parseSummaryResponse(responseText);
} catch (error) { } catch (error) {

View File

@ -113,10 +113,6 @@ model ConversationHistory {
model IngestionQueue { model IngestionQueue {
id String @id @default(cuid()) id String @id @default(cuid())
// Relations
space Space? @relation(fields: [spaceId], references: [id])
spaceId String?
// Queue metadata // Queue metadata
data Json // The actual data to be processed data Json // The actual data to be processed
output Json? // The processed output data output Json? // The processed output data
@ -472,29 +468,29 @@ model RecallLog {
} }
model Space { model Space {
id String @id @default(cuid()) id String @id @default(cuid())
name String name String
description String? description String?
autoMode Boolean @default(false) autoMode Boolean @default(false)
summary String? summary String?
themes String[] themes String[]
statementCount Int? contextCount Int? // Count of context items in this space (episodes, statements, etc.)
status String? status String?
icon String? icon String?
lastPatternTrigger DateTime? lastPatternTrigger DateTime?
statementCountAtLastTrigger Int? summaryGeneratedAt DateTime?
contextCountAtLastTrigger Int? // Context count when pattern was last triggered
// Relations // Relations
workspace Workspace @relation(fields: [workspaceId], references: [id]) workspace Workspace @relation(fields: [workspaceId], references: [id])
workspaceId String workspaceId String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
IngestionQueue IngestionQueue[] SpacePattern SpacePattern[]
SpacePattern SpacePattern[]
} }
model SpacePattern { model SpacePattern {