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

View File

@ -4,6 +4,7 @@ import type { StatementNode } from "@core/types";
import { cn } from "~/lib/utils";
import { useNavigate } from "@remix-run/react";
import Markdown from "react-markdown";
import { StyledMarkdown } from "../common/styled-markdown";
export interface Episode {
uuid: string;
@ -54,7 +55,7 @@ export function SpaceEpisodeCard({ episode }: SpaceFactCardProps) {
>
<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">
<Markdown>{displayText}</Markdown>
<StyledMarkdown>{displayText.slice(0, 300)}</StyledMarkdown>
</div>
<div className="text-muted-foreground flex shrink-0 items-center justify-end gap-2 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
) {
// 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
const episodeDetailsPromises = episodeIds.map((episodeId: string) =>

View File

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

View File

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