mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-10 22:32:16 +02:00
feat: add Zod schemas for Surfsense docs and fix citation handling
This commit is contained in:
parent
2c3d625b35
commit
42473fe4ed
4 changed files with 57 additions and 13 deletions
|
|
@ -20,7 +20,8 @@ const CITATION_REGEX = /\[citation:(doc-)?(\d+)\]/g;
|
||||||
|
|
||||||
// Track chunk IDs to citation numbers mapping for consistent numbering
|
// Track chunk IDs to citation numbers mapping for consistent numbering
|
||||||
// This map is reset when a new message starts rendering
|
// This map is reset when a new message starts rendering
|
||||||
let chunkIdToCitationNumber: Map<number, number> = new Map();
|
// Uses string keys to differentiate between doc and regular chunks (e.g., "doc-123" vs "123")
|
||||||
|
let chunkIdToCitationNumber: Map<string, number> = new Map();
|
||||||
let nextCitationNumber = 1;
|
let nextCitationNumber = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,11 +38,11 @@ export function resetCitationCounter() {
|
||||||
*/
|
*/
|
||||||
function getCitationNumber(chunkId: number, isDocsChunk: boolean): number {
|
function getCitationNumber(chunkId: number, isDocsChunk: boolean): number {
|
||||||
const key = isDocsChunk ? `doc-${chunkId}` : String(chunkId);
|
const key = isDocsChunk ? `doc-${chunkId}` : String(chunkId);
|
||||||
const existingNumber = chunkIdToCitationNumber.get(key as unknown as number);
|
const existingNumber = chunkIdToCitationNumber.get(key);
|
||||||
if (existingNumber === undefined) {
|
if (existingNumber === undefined) {
|
||||||
chunkIdToCitationNumber.set(key as unknown as number, nextCitationNumber++);
|
chunkIdToCitationNumber.set(key, nextCitationNumber++);
|
||||||
}
|
}
|
||||||
return chunkIdToCitationNumber.get(key as unknown as number)!;
|
return chunkIdToCitationNumber.get(key)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,16 @@ import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import type {
|
||||||
|
GetDocumentByChunkResponse,
|
||||||
|
GetSurfsenseDocsByChunkResponse,
|
||||||
|
} from "@/contracts/types/document.types";
|
||||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
type DocumentData = GetDocumentByChunkResponse | GetSurfsenseDocsByChunkResponse;
|
||||||
|
|
||||||
interface SourceDetailPanelProps {
|
interface SourceDetailPanelProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
|
|
@ -133,14 +139,16 @@ export function SourceDetailPanel({
|
||||||
data: documentData,
|
data: documentData,
|
||||||
isLoading: isDocumentByChunkFetching,
|
isLoading: isDocumentByChunkFetching,
|
||||||
error: documentByChunkFetchingError,
|
error: documentByChunkFetchingError,
|
||||||
} = useQuery({
|
} = useQuery<DocumentData>({
|
||||||
queryKey: isDocsChunk
|
queryKey: isDocsChunk
|
||||||
? cacheKeys.documents.byChunk(`doc-${chunkId}`)
|
? cacheKeys.documents.byChunk(`doc-${chunkId}`)
|
||||||
: cacheKeys.documents.byChunk(chunkId.toString()),
|
: cacheKeys.documents.byChunk(chunkId.toString()),
|
||||||
queryFn: () =>
|
queryFn: async () => {
|
||||||
isDocsChunk
|
if (isDocsChunk) {
|
||||||
? documentsApiService.getSurfsenseDocByChunk(chunkId)
|
return documentsApiService.getSurfsenseDocByChunk(chunkId);
|
||||||
: documentsApiService.getDocumentByChunk({ chunk_id: chunkId }),
|
}
|
||||||
|
return documentsApiService.getDocumentByChunk({ chunk_id: chunkId });
|
||||||
|
},
|
||||||
enabled: !!chunkId && open,
|
enabled: !!chunkId && open,
|
||||||
staleTime: 5 * 60 * 1000,
|
staleTime: 5 * 60 * 1000,
|
||||||
});
|
});
|
||||||
|
|
@ -332,7 +340,7 @@ export function SourceDetailPanel({
|
||||||
{documentData?.title || title || "Source Document"}
|
{documentData?.title || title || "Source Document"}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-muted-foreground mt-0.5">
|
<p className="text-sm text-muted-foreground mt-0.5">
|
||||||
{documentData
|
{documentData && "document_type" in documentData
|
||||||
? formatDocumentType(documentData.document_type)
|
? formatDocumentType(documentData.document_type)
|
||||||
: sourceType && formatDocumentType(sourceType)}
|
: sourceType && formatDocumentType(sourceType)}
|
||||||
{documentData?.chunks && (
|
{documentData?.chunks && (
|
||||||
|
|
@ -498,7 +506,8 @@ export function SourceDetailPanel({
|
||||||
<ScrollArea className="flex-1" ref={scrollAreaRef}>
|
<ScrollArea className="flex-1" ref={scrollAreaRef}>
|
||||||
<div className="p-6 lg:p-8 max-w-4xl mx-auto space-y-6">
|
<div className="p-6 lg:p-8 max-w-4xl mx-auto space-y-6">
|
||||||
{/* Document Metadata */}
|
{/* Document Metadata */}
|
||||||
{documentData.document_metadata &&
|
{"document_metadata" in documentData &&
|
||||||
|
documentData.document_metadata &&
|
||||||
Object.keys(documentData.document_metadata).length > 0 && (
|
Object.keys(documentData.document_metadata).length > 0 && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 10 }}
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,26 @@ export const documentWithChunks = document.extend({
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Surfsense documentation schemas
|
||||||
|
* Follows the same pattern as document/documentWithChunks
|
||||||
|
*/
|
||||||
|
export const surfsenseDocsChunk = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
content: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const surfsenseDocsDocument = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
title: z.string(),
|
||||||
|
source: z.string(),
|
||||||
|
content: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const surfsenseDocsDocumentWithChunks = surfsenseDocsDocument.extend({
|
||||||
|
chunks: z.array(surfsenseDocsChunk),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get documents
|
* Get documents
|
||||||
*/
|
*/
|
||||||
|
|
@ -154,6 +174,15 @@ export const getDocumentByChunkRequest = z.object({
|
||||||
|
|
||||||
export const getDocumentByChunkResponse = documentWithChunks;
|
export const getDocumentByChunkResponse = documentWithChunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Surfsense docs by chunk
|
||||||
|
*/
|
||||||
|
export const getSurfsenseDocsByChunkRequest = z.object({
|
||||||
|
chunk_id: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getSurfsenseDocsByChunkResponse = surfsenseDocsDocumentWithChunks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update document
|
* Update document
|
||||||
*/
|
*/
|
||||||
|
|
@ -193,3 +222,8 @@ export type UpdateDocumentResponse = z.infer<typeof updateDocumentResponse>;
|
||||||
export type DeleteDocumentRequest = z.infer<typeof deleteDocumentRequest>;
|
export type DeleteDocumentRequest = z.infer<typeof deleteDocumentRequest>;
|
||||||
export type DeleteDocumentResponse = z.infer<typeof deleteDocumentResponse>;
|
export type DeleteDocumentResponse = z.infer<typeof deleteDocumentResponse>;
|
||||||
export type DocumentTypeEnum = z.infer<typeof documentTypeEnum>;
|
export type DocumentTypeEnum = z.infer<typeof documentTypeEnum>;
|
||||||
|
export type SurfsenseDocsChunk = z.infer<typeof surfsenseDocsChunk>;
|
||||||
|
export type SurfsenseDocsDocument = z.infer<typeof surfsenseDocsDocument>;
|
||||||
|
export type SurfsenseDocsDocumentWithChunks = z.infer<typeof surfsenseDocsDocumentWithChunks>;
|
||||||
|
export type GetSurfsenseDocsByChunkRequest = z.infer<typeof getSurfsenseDocsByChunkRequest>;
|
||||||
|
export type GetSurfsenseDocsByChunkResponse = z.infer<typeof getSurfsenseDocsByChunkResponse>;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
getDocumentsResponse,
|
getDocumentsResponse,
|
||||||
getDocumentTypeCountsRequest,
|
getDocumentTypeCountsRequest,
|
||||||
getDocumentTypeCountsResponse,
|
getDocumentTypeCountsResponse,
|
||||||
|
getSurfsenseDocsByChunkResponse,
|
||||||
type SearchDocumentsRequest,
|
type SearchDocumentsRequest,
|
||||||
searchDocumentsRequest,
|
searchDocumentsRequest,
|
||||||
searchDocumentsResponse,
|
searchDocumentsResponse,
|
||||||
|
|
@ -214,10 +215,9 @@ class DocumentsApiService {
|
||||||
* Used for resolving [citation:doc-XXX] citations
|
* Used for resolving [citation:doc-XXX] citations
|
||||||
*/
|
*/
|
||||||
getSurfsenseDocByChunk = async (chunkId: number) => {
|
getSurfsenseDocByChunk = async (chunkId: number) => {
|
||||||
// Response shape matches getDocumentByChunkResponse structure
|
|
||||||
return baseApiService.get(
|
return baseApiService.get(
|
||||||
`/api/v1/surfsense-docs/by-chunk/${chunkId}`,
|
`/api/v1/surfsense-docs/by-chunk/${chunkId}`,
|
||||||
getDocumentByChunkResponse
|
getSurfsenseDocsByChunkResponse
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue