branch update

This commit is contained in:
Swayam 2025-10-07 01:20:07 +05:30
commit 22de2f2045
30 changed files with 433 additions and 377 deletions

View file

@ -41,7 +41,7 @@ import {
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
import { cn } from "@/lib/utils";
// Helper function to format date with time

View file

@ -20,7 +20,7 @@ import {
} from "@/components/ui/card";
import { Form } from "@/components/ui/form";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useConnectorEditPage } from "@/hooks/useConnectorEditPage";
import { useConnectorEditPage } from "@/hooks/use-connector-edit-page";
// Import Utils, Types, Hook, and Components
import { getConnectorTypeDisplay } from "@/lib/connectors/utils";

View file

@ -24,7 +24,7 @@ import { Input } from "@/components/ui/input";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/useSearchSourceConnectors";
} from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const apiConnectorFormSchema = z.object({

View file

@ -21,7 +21,7 @@ import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/useSearchSourceConnectors";
} from "@/hooks/use-search-source-connectors";
export default function AirtableConnectorPage() {
const router = useRouter();

View file

@ -22,7 +22,7 @@ import {
import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const clickupConnectorFormSchema = z.object({

View file

@ -24,7 +24,7 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const confluenceConnectorFormSchema = z.object({

View file

@ -37,7 +37,7 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const discordConnectorFormSchema = z.object({

View file

@ -39,7 +39,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
// Assuming useSearchSourceConnectors hook exists and works similarly
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod for GitHub PAT entry step
const githubPatFormSchema = z.object({

View file

@ -23,7 +23,7 @@ import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/useSearchSourceConnectors";
} from "@/hooks/use-search-source-connectors";
export default function GoogleCalendarConnectorPage() {
const router = useRouter();

View file

@ -23,7 +23,7 @@ import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/useSearchSourceConnectors";
} from "@/hooks/use-search-source-connectors";
export default function GoogleGmailConnectorPage() {
const router = useRouter();

View file

@ -37,7 +37,7 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const jiraConnectorFormSchema = z.object({

View file

@ -37,7 +37,7 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const linearConnectorFormSchema = z.object({

View file

@ -30,7 +30,7 @@ import {
import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const linkupApiFormSchema = z.object({

View file

@ -33,7 +33,7 @@ import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/useSearchSourceConnectors";
} from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const lumaConnectorFormSchema = z.object({

View file

@ -37,7 +37,7 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const notionConnectorFormSchema = z.object({

View file

@ -30,7 +30,7 @@ import {
import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const serperApiFormSchema = z.object({

View file

@ -37,7 +37,7 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const slackConnectorFormSchema = z.object({

View file

@ -30,7 +30,7 @@ import {
import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod
const tavilyApiFormSchema = z.object({

View file

@ -5,8 +5,8 @@ import { useParams, useRouter } from "next/navigation";
import { useEffect, useMemo } from "react";
import type { ResearchMode } from "@/components/chat";
import ChatInterface from "@/components/chat/ChatInterface";
import { useChatAPI, useChatState } from "@/hooks/use-chat";
import type { Document } from "@/hooks/use-documents";
import { useChatAPI, useChatState } from "@/hooks/useChat";
export default function ResearcherPage() {
const { search_space_id, chat_id } = useParams();

View file

@ -1,233 +1,30 @@
"use client";
import { ChevronDown, ChevronUp, ExternalLink, FileText, Loader2 } from "lucide-react";
import type React from "react";
import { useEffect, useRef, useState } from "react";
import { MarkdownViewer } from "@/components/markdown-viewer";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useDocumentByChunk } from "@/hooks/use-document-by-chunk";
import { cn } from "@/lib/utils";
import { useState } from "react";
import { SheetTrigger } from "@/components/ui/sheet";
import { SourceDetailSheet } from "./SourceDetailSheet";
export const CitationDisplay: React.FC<{ index: number; node: any }> = ({ index, node }) => {
const chunkId = Number(node?.id);
const sourceType = node?.metadata?.source_type;
const [isOpen, setIsOpen] = useState(false);
const { document, loading, error, fetchDocumentByChunk, clearDocument } = useDocumentByChunk();
const chunksContainerRef = useRef<HTMLDivElement>(null);
const highlightedChunkRef = useRef<HTMLDivElement>(null);
const [summaryOpen, setSummaryOpen] = useState(false);
// Check if this is a source type that should render directly from node
const isDirectRenderSource = sourceType === "TAVILY_API" || sourceType === "LINKUP_API";
const handleOpenChange = async (open: boolean) => {
setIsOpen(open);
if (open && chunkId && !isDirectRenderSource) {
await fetchDocumentByChunk(chunkId);
} else if (!open && !isDirectRenderSource) {
clearDocument();
}
};
useEffect(() => {
// Scroll to highlighted chunk when document loads
if (document && highlightedChunkRef.current && chunksContainerRef.current) {
setTimeout(() => {
highlightedChunkRef.current?.scrollIntoView({
behavior: "smooth",
block: "start",
});
}, 100);
}
}, [document]);
const handleUrlClick = (e: React.MouseEvent, url: string) => {
e.preventDefault();
e.stopPropagation();
window.open(url, "_blank", "noopener,noreferrer");
};
const formatDocumentType = (type: string) => {
return type
.split("_")
.map((word) => word.charAt(0) + word.slice(1).toLowerCase())
.join(" ");
};
return (
<Sheet open={isOpen} onOpenChange={handleOpenChange}>
<SourceDetailSheet
open={isOpen}
onOpenChange={setIsOpen}
chunkId={chunkId}
sourceType={sourceType}
title={node?.metadata?.title || node?.metadata?.group_name || "Source"}
description={node?.text}
url={node?.url}
>
<SheetTrigger asChild>
<span className="text-[10px] font-bold bg-slate-500 hover:bg-slate-600 text-white rounded-full w-4 h-4 inline-flex items-center justify-center align-super cursor-pointer transition-colors">
{index + 1}
</span>
</SheetTrigger>
<SheetContent side="right" className="w-full sm:max-w-5xl lg:max-w-7xl">
<SheetHeader className="px-6 py-4 border-b">
<SheetTitle className="flex items-center gap-3 text-lg">
{getConnectorIcon(sourceType)}
{document?.title || node?.metadata?.title || node?.metadata?.group_name || "Source"}
</SheetTitle>
<SheetDescription className="text-base mt-2">
{document
? formatDocumentType(document.document_type)
: sourceType && formatDocumentType(sourceType)}
</SheetDescription>
</SheetHeader>
{!isDirectRenderSource && loading && (
<div className="flex items-center justify-center h-64 px-6">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
{!isDirectRenderSource && error && (
<div className="flex items-center justify-center h-64 px-6">
<p className="text-sm text-destructive">{error}</p>
</div>
)}
{/* Direct render for TAVILY_API and LINEAR_API */}
{isDirectRenderSource && (
<ScrollArea className="h-[calc(100vh-10rem)]">
<div className="px-6 py-4">
{/* External Link */}
{node?.url && (
<div className="mb-8">
<Button
size="default"
variant="outline"
onClick={(e) => handleUrlClick(e, node.url)}
className="w-full py-3"
>
<ExternalLink className="mr-2 h-4 w-4" />
Open in Browser
</Button>
</div>
)}
{/* Source Information */}
<div className="mb-8 p-6 bg-muted/50 rounded-lg border">
<h3 className="text-base font-semibold mb-4">Source Information</h3>
<div className="text-sm text-muted-foreground mb-3 font-medium">
{node?.metadata?.title || "Untitled"}
</div>
<div className="text-sm text-foreground leading-relaxed whitespace-pre-wrap">
{node?.text || "No content available"}
</div>
</div>
</div>
</ScrollArea>
)}
{/* API-fetched document content */}
{!isDirectRenderSource && document && (
<ScrollArea className="h-[calc(100vh-10rem)]">
<div className="px-6 py-4">
{/* Document Metadata */}
{document.document_metadata && Object.keys(document.document_metadata).length > 0 && (
<div className="mb-8 p-6 bg-muted/50 rounded-lg border">
<h3 className="text-base font-semibold mb-4">Document Information</h3>
<dl className="grid grid-cols-1 gap-3 text-sm">
{Object.entries(document.document_metadata).map(([key, value]) => (
<div key={key} className="flex gap-3">
<dt className="font-medium text-muted-foreground capitalize min-w-0 flex-shrink-0">
{key.replace(/_/g, " ")}:
</dt>
<dd className="text-foreground break-words">{String(value)}</dd>
</div>
))}
</dl>
</div>
)}
{/* External Link */}
{node?.url && (
<div className="mb-8">
<Button
size="default"
variant="outline"
onClick={(e) => handleUrlClick(e, node.url)}
className="w-full py-3"
>
<ExternalLink className="mr-2 h-4 w-4" />
Open in Browser
</Button>
</div>
)}
{/* Chunks */}
<div className="space-y-6" ref={chunksContainerRef}>
<div className="mb-4">
{/* Header row: header and button side by side */}
<div className="flex flex-row items-center gap-4">
<h3 className="text-base font-semibold mb-2 md:mb-0">Document Content</h3>
{document.content && (
<Collapsible open={summaryOpen} onOpenChange={setSummaryOpen}>
<CollapsibleTrigger className="flex items-center gap-2 py-2 px-3 font-medium border rounded-md bg-muted hover:bg-muted/80 transition-colors">
<span>Summary</span>
{summaryOpen ? (
<ChevronUp className="h-4 w-4 transition-transform" />
) : (
<ChevronDown className="h-4 w-4 transition-transform" />
)}
</CollapsibleTrigger>
</Collapsible>
)}
</div>
{/* Expanded summary content: always full width, below the row */}
{document.content && (
<Collapsible open={summaryOpen} onOpenChange={setSummaryOpen}>
<CollapsibleContent className="pt-2 w-full">
<div className="p-6 bg-muted/50 rounded-lg border">
<MarkdownViewer content={document.content} />
</div>
</CollapsibleContent>
</Collapsible>
)}
</div>
{document.chunks.map((chunk, idx) => (
<div
key={chunk.id}
ref={chunk.id === chunkId ? highlightedChunkRef : null}
className={cn(
"p-6 rounded-lg border transition-all duration-300",
chunk.id === chunkId
? "bg-primary/10 border-primary shadow-md ring-1 ring-primary/20"
: "bg-background border-border hover:bg-muted/50 hover:border-muted-foreground/20"
)}
>
<div className="mb-4 flex items-center justify-between">
<span className="text-sm font-medium text-muted-foreground">
Chunk {idx + 1} of {document.chunks.length}
</span>
{chunk.id === chunkId && (
<span className="text-sm font-medium text-primary bg-primary/10 px-3 py-1 rounded-full">
Referenced Chunk
</span>
)}
</div>
<div className="text-sm text-foreground whitespace-pre-wrap leading-relaxed">
<MarkdownViewer content={chunk.content} className="max-w-fit" />
</div>
</div>
))}
</div>
</div>
</ScrollArea>
)}
</SheetContent>
</Sheet>
</SourceDetailSheet>
);
};

View file

@ -27,7 +27,7 @@ import {
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { type Document, useDocuments } from "@/hooks/use-documents";
import { useLLMConfigs, useLLMPreferences } from "@/hooks/use-llm-configs";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
const DocumentSelector = React.memo(
({

View file

@ -9,12 +9,14 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { SourceDetailSheet } from "./SourceDetailSheet";
interface Source {
id: string;
title: string;
description: string;
url: string;
sourceType: string;
}
interface SourceGroup {
@ -48,6 +50,9 @@ function getSourceIcon(type: string) {
function SourceCard({ source }: { source: Source }) {
const hasUrl = source.url && source.url.trim() !== "";
const chunkId = Number(source.id);
const sourceType = source.sourceType;
const [isOpen, setIsOpen] = useState(false);
// Clean up the description for better display
const cleanDescription = source.description
@ -55,31 +60,54 @@ function SourceCard({ source }: { source: Source }) {
.replace(/\n+/g, " ")
.trim();
const handleUrlClick = (e: React.MouseEvent, url: string) => {
e.preventDefault();
e.stopPropagation();
window.open(url, "_blank", "noopener,noreferrer");
};
return (
<Card className="border-muted hover:border-muted-foreground/20 transition-colors">
<CardHeader className="pb-3 pt-3">
<div className="flex items-start justify-between gap-2">
<CardTitle className="text-sm font-medium leading-tight line-clamp-2">
{source.title}
</CardTitle>
{hasUrl && (
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 flex-shrink-0 hover:bg-muted"
onClick={() => window.open(source.url, "_blank")}
>
<ExternalLink className="h-3.5 w-3.5" />
</Button>
)}
</div>
</CardHeader>
<CardContent className="pt-0 pb-3">
<CardDescription className="text-xs line-clamp-3 leading-relaxed text-muted-foreground">
{cleanDescription}
</CardDescription>
</CardContent>
</Card>
<SourceDetailSheet
open={isOpen}
onOpenChange={setIsOpen}
chunkId={chunkId}
sourceType={sourceType}
title={source.title}
description={source.description}
url={source.url}
>
<SheetTrigger asChild>
<Card className="border-muted hover:border-muted-foreground/20 transition-colors cursor-pointer">
<CardHeader className="pb-3 pt-3">
<div className="flex items-start justify-between gap-2">
<CardTitle className="text-sm font-medium leading-tight line-clamp-2 flex-1">
{source.title}
</CardTitle>
<div className="flex items-center gap-1 flex-shrink-0">
<Badge variant="secondary" className="text-[10px] h-5 px-2 font-mono">
#{chunkId}
</Badge>
{hasUrl && (
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 flex-shrink-0 hover:bg-muted"
onClick={(e) => handleUrlClick(e, source.url)}
>
<ExternalLink className="h-3.5 w-3.5" />
</Button>
)}
</div>
</div>
</CardHeader>
<CardContent className="pt-0 pb-3">
<CardDescription className="text-xs line-clamp-3 leading-relaxed text-muted-foreground">
{cleanDescription}
</CardDescription>
</CardContent>
</Card>
</SheetTrigger>
</SourceDetailSheet>
);
}
@ -126,6 +154,7 @@ export default function ChatSourcesDisplay({ message }: { message: Message }) {
title: node.metadata.title,
description: node.text,
url: node.url || "",
sourceType: sourceType,
})),
});
}

View file

@ -0,0 +1,244 @@
"use client";
import { ChevronDown, ChevronUp, ExternalLink, Loader2 } from "lucide-react";
import type React from "react";
import { type ReactNode, useEffect, useRef, useState } from "react";
import { MarkdownViewer } from "@/components/markdown-viewer";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useDocumentByChunk } from "@/hooks/use-document-by-chunk";
import { cn } from "@/lib/utils";
interface SourceDetailSheetProps {
open: boolean;
onOpenChange: (open: boolean) => void;
chunkId: number;
sourceType: string;
title: string;
description?: string;
url?: string;
children?: ReactNode;
}
const formatDocumentType = (type: string) => {
return type
.split("_")
.map((word) => word.charAt(0) + word.slice(1).toLowerCase())
.join(" ");
};
export function SourceDetailSheet({
open,
onOpenChange,
chunkId,
sourceType,
title,
description,
url,
children,
}: SourceDetailSheetProps) {
const { document, loading, error, fetchDocumentByChunk, clearDocument } = useDocumentByChunk();
const chunksContainerRef = useRef<HTMLDivElement>(null);
const highlightedChunkRef = useRef<HTMLDivElement>(null);
const [summaryOpen, setSummaryOpen] = useState(false);
// Check if this is a source type that should render directly from node
const isDirectRenderSource = sourceType === "TAVILY_API" || sourceType === "LINKUP_API";
useEffect(() => {
if (open && chunkId && !isDirectRenderSource) {
fetchDocumentByChunk(chunkId);
} else if (!open && !isDirectRenderSource) {
clearDocument();
}
}, [open, chunkId, isDirectRenderSource, fetchDocumentByChunk, clearDocument]);
useEffect(() => {
// Scroll to highlighted chunk when document loads
if (document && highlightedChunkRef.current && chunksContainerRef.current) {
setTimeout(() => {
highlightedChunkRef.current?.scrollIntoView({
behavior: "smooth",
block: "start",
});
}, 100);
}
}, [document]);
const handleUrlClick = (e: React.MouseEvent, clickUrl: string) => {
e.preventDefault();
e.stopPropagation();
window.open(clickUrl, "_blank", "noopener,noreferrer");
};
return (
<Sheet open={open} onOpenChange={onOpenChange}>
{children}
<SheetContent side="right" className="w-full sm:max-w-5xl lg:max-w-7xl">
<SheetHeader className="px-6 py-4 border-b">
<SheetTitle className="flex items-center gap-3 text-lg">
{getConnectorIcon(sourceType)}
{document?.title || title}
</SheetTitle>
<SheetDescription className="text-base mt-2">
{document
? formatDocumentType(document.document_type)
: sourceType && formatDocumentType(sourceType)}
</SheetDescription>
</SheetHeader>
{!isDirectRenderSource && loading && (
<div className="flex items-center justify-center h-64 px-6">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
{!isDirectRenderSource && error && (
<div className="flex items-center justify-center h-64 px-6">
<p className="text-sm text-destructive">{error}</p>
</div>
)}
{/* Direct render for TAVILY_API and LINKUP_API */}
{isDirectRenderSource && (
<ScrollArea className="h-[calc(100vh-10rem)]">
<div className="px-6 py-4">
{/* External Link */}
{url && (
<div className="mb-8">
<Button
size="default"
variant="outline"
onClick={(e) => handleUrlClick(e, url)}
className="w-full py-3"
>
<ExternalLink className="mr-2 h-4 w-4" />
Open in Browser
</Button>
</div>
)}
{/* Source Information */}
<div className="mb-8 p-6 bg-muted/50 rounded-lg border">
<h3 className="text-base font-semibold mb-4">Source Information</h3>
<div className="text-sm text-muted-foreground mb-3 font-medium">
{title || "Untitled"}
</div>
<div className="text-sm text-foreground leading-relaxed whitespace-pre-wrap">
{description || "No content available"}
</div>
</div>
</div>
</ScrollArea>
)}
{/* API-fetched document content */}
{!isDirectRenderSource && document && (
<ScrollArea className="h-[calc(100vh-10rem)]">
<div className="px-6 py-4">
{/* Document Metadata */}
{document.document_metadata && Object.keys(document.document_metadata).length > 0 && (
<div className="mb-8 p-6 bg-muted/50 rounded-lg border">
<h3 className="text-base font-semibold mb-4">Document Information</h3>
<dl className="grid grid-cols-1 gap-3 text-sm">
{Object.entries(document.document_metadata).map(([key, value]) => (
<div key={key} className="flex gap-3">
<dt className="font-medium text-muted-foreground capitalize min-w-0 flex-shrink-0">
{key.replace(/_/g, " ")}:
</dt>
<dd className="text-foreground break-words">{String(value)}</dd>
</div>
))}
</dl>
</div>
)}
{/* External Link */}
{url && (
<div className="mb-8">
<Button
size="default"
variant="outline"
onClick={(e) => handleUrlClick(e, url)}
className="w-full py-3"
>
<ExternalLink className="mr-2 h-4 w-4" />
Open in Browser
</Button>
</div>
)}
{/* Chunks */}
<div className="space-y-6" ref={chunksContainerRef}>
<div className="mb-4">
{/* Header row: header and button side by side */}
<div className="flex flex-row items-center gap-4">
<h3 className="text-base font-semibold mb-2 md:mb-0">Document Content</h3>
{document.content && (
<Collapsible open={summaryOpen} onOpenChange={setSummaryOpen}>
<CollapsibleTrigger className="flex items-center gap-2 py-2 px-3 font-medium border rounded-md bg-muted hover:bg-muted/80 transition-colors">
<span>Summary</span>
{summaryOpen ? (
<ChevronUp className="h-4 w-4 transition-transform" />
) : (
<ChevronDown className="h-4 w-4 transition-transform" />
)}
</CollapsibleTrigger>
</Collapsible>
)}
</div>
{/* Expanded summary content: always full width, below the row */}
{document.content && (
<Collapsible open={summaryOpen} onOpenChange={setSummaryOpen}>
<CollapsibleContent className="pt-2 w-full">
<div className="p-6 bg-muted/50 rounded-lg border">
<MarkdownViewer content={document.content} />
</div>
</CollapsibleContent>
</Collapsible>
)}
</div>
{document.chunks.map((chunk, idx) => (
<div
key={chunk.id}
ref={chunk.id === chunkId ? highlightedChunkRef : null}
className={cn(
"p-6 rounded-lg border transition-all duration-300",
chunk.id === chunkId
? "bg-primary/10 border-primary shadow-md ring-1 ring-primary/20"
: "bg-background border-border hover:bg-muted/50 hover:border-muted-foreground/20"
)}
>
<div className="mb-4 flex items-center justify-between">
<span className="text-sm font-medium text-muted-foreground">
Chunk {idx + 1} of {document.chunks.length}
</span>
{chunk.id === chunkId && (
<span className="text-sm font-medium text-primary bg-primary/10 px-3 py-1 rounded-full">
Referenced Chunk
</span>
)}
</div>
<div className="text-sm text-foreground whitespace-pre-wrap leading-relaxed">
<MarkdownViewer content={chunk.content} className="max-w-fit" />
</div>
</div>
))}
</div>
</div>
</ScrollArea>
)}
</SheetContent>
</Sheet>
);
}

View file

@ -17,30 +17,9 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { LLM_PROVIDERS } from "@/contracts/enums/llm-providers";
import { type CreateLLMConfig, useLLMConfigs } from "@/hooks/use-llm-configs";
const LLM_PROVIDERS = [
{ value: "OPENAI", label: "OpenAI", example: "gpt-4o, gpt-4, gpt-3.5-turbo" },
{
value: "ANTHROPIC",
label: "Anthropic",
example: "claude-3-5-sonnet-20241022, claude-3-opus-20240229",
},
{ value: "GROQ", label: "Groq", example: "llama3-70b-8192, mixtral-8x7b-32768" },
{ value: "COHERE", label: "Cohere", example: "command-r-plus, command-r" },
{ value: "HUGGINGFACE", label: "HuggingFace", example: "microsoft/DialoGPT-medium" },
{ value: "AZURE_OPENAI", label: "Azure OpenAI", example: "gpt-4, gpt-35-turbo" },
{ value: "GOOGLE", label: "Google", example: "gemini-pro, gemini-pro-vision" },
{ value: "AWS_BEDROCK", label: "AWS Bedrock", example: "anthropic.claude-v2" },
{ value: "OLLAMA", label: "Ollama", example: "llama2, codellama" },
{ value: "MISTRAL", label: "Mistral", example: "mistral-large-latest, mistral-medium" },
{ value: "TOGETHER_AI", label: "Together AI", example: "togethercomputer/llama-2-70b-chat" },
{ value: "REPLICATE", label: "Replicate", example: "meta/llama-2-70b-chat" },
{ value: "OPENROUTER", label: "OpenRouter", example: "anthropic/claude-opus-4.1, openai/gpt-5" },
{ value: "COMETAPI", label: "CometAPI", example: "gpt-4o, claude-3-5-sonnet-20241022" },
{ value: "CUSTOM", label: "Custom Provider", example: "your-custom-model" },
];
interface AddProviderStepProps {
onConfigCreated?: () => void;
onConfigDeleted?: () => void;

View file

@ -37,101 +37,9 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { LLM_PROVIDERS } from "@/contracts/enums/llm-providers";
import { type CreateLLMConfig, type LLMConfig, useLLMConfigs } from "@/hooks/use-llm-configs";
const LLM_PROVIDERS = [
{
value: "OPENAI",
label: "OpenAI",
example: "gpt-4o, gpt-4, gpt-3.5-turbo",
description: "Most popular and versatile AI models",
},
{
value: "ANTHROPIC",
label: "Anthropic",
example: "claude-3-5-sonnet-20241022, claude-3-opus-20240229",
description: "Constitutional AI with strong reasoning",
},
{
value: "GROQ",
label: "Groq",
example: "llama3-70b-8192, mixtral-8x7b-32768",
description: "Ultra-fast inference speeds",
},
{
value: "COHERE",
label: "Cohere",
example: "command-r-plus, command-r",
description: "Enterprise-focused language models",
},
{
value: "HUGGINGFACE",
label: "HuggingFace",
example: "microsoft/DialoGPT-medium",
description: "Open source model hub",
},
{
value: "AZURE_OPENAI",
label: "Azure OpenAI",
example: "gpt-4, gpt-35-turbo",
description: "Enterprise OpenAI through Azure",
},
{
value: "GOOGLE",
label: "Google",
example: "gemini-pro, gemini-pro-vision",
description: "Google's Gemini AI models",
},
{
value: "AWS_BEDROCK",
label: "AWS Bedrock",
example: "anthropic.claude-v2",
description: "AWS managed AI service",
},
{
value: "OLLAMA",
label: "Ollama",
example: "llama2, codellama",
description: "Run models locally",
},
{
value: "MISTRAL",
label: "Mistral",
example: "mistral-large-latest, mistral-medium",
description: "European AI excellence",
},
{
value: "TOGETHER_AI",
label: "Together AI",
example: "togethercomputer/llama-2-70b-chat",
description: "Decentralized AI platform",
},
{
value: "REPLICATE",
label: "Replicate",
example: "meta/llama-2-70b-chat",
description: "Run models via API",
},
{
value: "OPENROUTER",
label: "OpenRouter",
example: "anthropic/claude-opus-4.1, openai/gpt-5",
description: "API gateway and LLM marketplace that provides unified access ",
},
{
value: "COMETAPI",
label: "CometAPI",
example: "gpt-5-mini, claude-sonnet-4-5",
description: "500+ AI models through one unified API",
},
{
value: "CUSTOM",
label: "Custom Provider",
example: "your-custom-model",
description: "Your own model endpoint",
},
];
export function ModelConfigManager() {
const {
llmConfigs,

View file

@ -0,0 +1,99 @@
export interface LLMProvider {
value: string;
label: string;
example: string;
description: string;
}
export const LLM_PROVIDERS: LLMProvider[] = [
{
value: "OPENAI",
label: "OpenAI",
example: "gpt-4o, gpt-4, gpt-3.5-turbo",
description: "Industry-leading GPT models with broad capabilities",
},
{
value: "ANTHROPIC",
label: "Anthropic",
example: "claude-3-5-sonnet-20241022, claude-3-opus-20240229",
description: "Claude models with strong reasoning and long context windows",
},
{
value: "GROQ",
label: "Groq",
example: "llama3-70b-8192, mixtral-8x7b-32768",
description: "Lightning-fast inference with custom LPU hardware",
},
{
value: "COHERE",
label: "Cohere",
example: "command-r-plus, command-r",
description: "Enterprise NLP models optimized for business applications",
},
{
value: "HUGGINGFACE",
label: "HuggingFace",
example: "microsoft/DialoGPT-medium",
description: "Access thousands of open-source models",
},
{
value: "AZURE_OPENAI",
label: "Azure OpenAI",
example: "gpt-4, gpt-35-turbo",
description: "OpenAI models with Microsoft Azure enterprise features",
},
{
value: "GOOGLE",
label: "Google",
example: "gemini-pro, gemini-pro-vision",
description: "Gemini models with multimodal capabilities",
},
{
value: "AWS_BEDROCK",
label: "AWS Bedrock",
example: "anthropic.claude-v2",
description: "Fully managed foundation models on AWS infrastructure",
},
{
value: "OLLAMA",
label: "Ollama",
example: "llama2, codellama",
description: "Run open-source models locally on your machine",
},
{
value: "MISTRAL",
label: "Mistral",
example: "mistral-large-latest, mistral-medium",
description: "High-performance open-source models from Europe",
},
{
value: "TOGETHER_AI",
label: "Together AI",
example: "togethercomputer/llama-2-70b-chat",
description: "Scalable cloud platform for open-source models",
},
{
value: "REPLICATE",
label: "Replicate",
example: "meta/llama-2-70b-chat",
description: "Cloud API for running machine learning models",
},
{
value: "OPENROUTER",
label: "OpenRouter",
example: "anthropic/claude-opus-4.1, openai/gpt-5",
description: "Unified API gateway for multiple LLM providers",
},
{
value: "COMETAPI",
label: "CometAPI",
example: "gpt-5-mini, claude-sonnet-4-5",
description: "Access 500+ AI models through one unified API",
},
{
value: "CUSTOM",
label: "Custom Provider",
example: "your-custom-model",
description: "Connect to your own custom model endpoint",
},
];

View file

@ -1,3 +1,3 @@
export * from "./use-document-by-chunk";
export * from "./use-logs";
export * from "./useSearchSourceConnectors";
export * from "./use-search-source-connectors";

View file

@ -14,7 +14,7 @@ import {
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/useSearchSourceConnectors";
} from "@/hooks/use-search-source-connectors";
export function useConnectorEditPage(connectorId: number, searchSpaceId: string) {
const router = useRouter();