Merge branch 'main' into 109-design-sweep-polish-adding-from-cloud-connector-screen

This commit is contained in:
boneill-ds 2025-10-06 15:58:34 -06:00 committed by GitHub
commit 819948ab3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -101,34 +101,6 @@ interface Connection {
created_at: string; created_at: string;
last_sync?: string; last_sync?: string;
} }
const DEFAULT_CONNECTORS: Connector[] = [
{
id: "google_drive",
name: "Google Drive",
description: "Google Drive is not configured.",
icon: <GoogleDriveIcon />,
status: "not_connected",
type: "google_drive",
},
{
id: "one_drive",
name: "OneDrive",
description: "OneDrive is not configured.",
icon: <OneDriveIcon />,
status: "not_connected",
type: "one_drive",
},
{
id: "amazon_s3",
name: "SharePoint",
description: "SharePoint is not configured.",
icon: <SharePointIcon />,
status: "not_connected",
type: "sharepoint",
},
];
function KnowledgeSourcesPage() { function KnowledgeSourcesPage() {
const { isAuthenticated, isNoAuthMode } = useAuth(); const { isAuthenticated, isNoAuthMode } = useAuth();
const { addTask, tasks } = useTask(); const { addTask, tasks } = useTask();
@ -203,7 +175,7 @@ function KnowledgeSourcesPage() {
onSuccess: () => { onSuccess: () => {
console.log("Setting updated successfully"); console.log("Setting updated successfully");
}, },
onError: error => { onError: (error) => {
console.error("Failed to update setting:", error.message); console.error("Failed to update setting:", error.message);
}, },
}); });
@ -304,16 +276,8 @@ function KnowledgeSourcesPage() {
const getConnectorIcon = useCallback((iconName: string) => { const getConnectorIcon = useCallback((iconName: string) => {
const iconMap: { [key: string]: React.ReactElement } = { const iconMap: { [key: string]: React.ReactElement } = {
"google-drive": <GoogleDriveIcon />, "google-drive": <GoogleDriveIcon />,
sharepoint: ( sharepoint: <SharePointIcon />,
<div className="w-8 h-8 bg-blue-700 rounded flex items-center justify-center text-white font-bold leading-none shrink-0"> onedrive: <OneDriveIcon />,
SP
</div>
),
onedrive: (
<div className="w-8 h-8 bg-white border border-gray-300 rounded flex items-center justify-center">
<OneDriveIcon />
</div>
),
}; };
return ( return (
iconMap[iconName] || ( iconMap[iconName] || (
@ -338,8 +302,8 @@ function KnowledgeSourcesPage() {
// Initialize connectors list with metadata from backend // Initialize connectors list with metadata from backend
const initialConnectors = connectorTypes const initialConnectors = connectorTypes
.filter(type => connectorsResult.connectors[type].available) // Only show available connectors .filter((type) => connectorsResult.connectors[type].available) // Only show available connectors
.map(type => ({ .map((type) => ({
id: type, id: type,
name: connectorsResult.connectors[type].name, name: connectorsResult.connectors[type].name,
description: connectorsResult.connectors[type].description, description: connectorsResult.connectors[type].description,
@ -362,8 +326,8 @@ function KnowledgeSourcesPage() {
); );
const isConnected = activeConnection !== undefined; const isConnected = activeConnection !== undefined;
setConnectors(prev => setConnectors((prev) =>
prev.map(c => prev.map((c) =>
c.type === connectorType c.type === connectorType
? { ? {
...c, ...c,
@ -382,7 +346,7 @@ function KnowledgeSourcesPage() {
const handleConnect = async (connector: Connector) => { const handleConnect = async (connector: Connector) => {
setIsConnecting(connector.id); setIsConnecting(connector.id);
setSyncResults(prev => ({ ...prev, [connector.id]: null })); setSyncResults((prev) => ({ ...prev, [connector.id]: null }));
try { try {
// Use the shared auth callback URL, same as connectors page // Use the shared auth callback URL, same as connectors page
@ -522,9 +486,9 @@ function KnowledgeSourcesPage() {
// Watch for task completions and refresh stats // Watch for task completions and refresh stats
useEffect(() => { useEffect(() => {
// Find newly completed tasks by comparing with previous state // Find newly completed tasks by comparing with previous state
const newlyCompletedTasks = tasks.filter(task => { const newlyCompletedTasks = tasks.filter((task) => {
const wasCompleted = const wasCompleted =
prevTasks.find(prev => prev.task_id === task.task_id)?.status === prevTasks.find((prev) => prev.task_id === task.task_id)?.status ===
"completed"; "completed";
return task.status === "completed" && !wasCompleted; return task.status === "completed" && !wasCompleted;
}); });
@ -578,7 +542,7 @@ function KnowledgeSourcesPage() {
fetch(`/api/reset-flow/retrieval`, { fetch(`/api/reset-flow/retrieval`, {
method: "POST", method: "POST",
}) })
.then(response => { .then((response) => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@ -591,7 +555,7 @@ function KnowledgeSourcesPage() {
handleModelChange(DEFAULT_AGENT_SETTINGS.llm_model); handleModelChange(DEFAULT_AGENT_SETTINGS.llm_model);
closeDialog(); // Close after successful completion closeDialog(); // Close after successful completion
}) })
.catch(error => { .catch((error) => {
console.error("Error restoring retrieval flow:", error); console.error("Error restoring retrieval flow:", error);
closeDialog(); // Close even on error (could show error toast instead) closeDialog(); // Close even on error (could show error toast instead)
}); });
@ -601,7 +565,7 @@ function KnowledgeSourcesPage() {
fetch(`/api/reset-flow/ingest`, { fetch(`/api/reset-flow/ingest`, {
method: "POST", method: "POST",
}) })
.then(response => { .then((response) => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@ -616,7 +580,7 @@ function KnowledgeSourcesPage() {
setPictureDescriptions(false); setPictureDescriptions(false);
closeDialog(); // Close after successful completion closeDialog(); // Close after successful completion
}) })
.catch(error => { .catch((error) => {
console.error("Error restoring ingest flow:", error); console.error("Error restoring ingest flow:", error);
closeDialog(); // Close even on error (could show error toast instead) closeDialog(); // Close even on error (could show error toast instead)
}); });
@ -735,11 +699,9 @@ function KnowledgeSourcesPage() {
// </div> // </div>
// </div> // </div>
} }
{/* Connectors Grid */} {/* Connectors Grid */}
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{DEFAULT_CONNECTORS.map(connector => { {connectors.map((connector) => {
const actualConnector = connectors.find(c => c.id === connector.id);
return ( return (
<Card key={connector.id} className="relative flex flex-col"> <Card key={connector.id} className="relative flex flex-col">
<CardHeader> <CardHeader>
@ -748,7 +710,7 @@ function KnowledgeSourcesPage() {
<div className="mb-1"> <div className="mb-1">
<div <div
className={`w-8 h-8 ${ className={`w-8 h-8 ${
actualConnector ? "bg-white" : "bg-muted grayscale" connector ? "bg-white" : "bg-muted grayscale"
} rounded flex items-center justify-center`} } rounded flex items-center justify-center`}
> >
{connector.icon} {connector.icon}
@ -756,45 +718,66 @@ function KnowledgeSourcesPage() {
</div> </div>
<CardTitle className="flex flex-row items-center gap-2"> <CardTitle className="flex flex-row items-center gap-2">
{connector.name} {connector.name}
{actualConnector && {connector && getStatusBadge(connector.status)}
getStatusBadge(actualConnector.status)}
</CardTitle> </CardTitle>
<CardDescription className="text-[13px]"> <CardDescription className="text-[13px]">
{actualConnector?.description {connector?.description
? `${actualConnector.name} is configured.` ? `${connector.name} is configured.`
: connector.description} : connector.description}
</CardDescription> </CardDescription>
</div> </div>
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="flex-1 flex flex-col justify-end space-y-4"> <CardContent className="flex-1 flex flex-col justify-end space-y-4">
{actualConnector?.status === "connected" ? ( {connector?.available ? (
<div className="space-y-3"> <div className="space-y-3">
<Button {connector?.status === "connected" ? (
onClick={() => navigateToKnowledgePage(connector)} <>
disabled={isSyncing === connector.id} <Button
className="w-full cursor-pointer" onClick={() => navigateToKnowledgePage(connector)}
size="sm" disabled={isSyncing === connector.id}
> className="w-full cursor-pointer"
<Plus className="h-4 w-4" /> size="sm"
Add Knowledge >
</Button> <Plus className="h-4 w-4" />
Add Knowledge
{syncResults[connector.id] && ( </Button>
<div className="text-xs text-muted-foreground bg-muted/50 p-2 rounded"> {syncResults[connector.id] && (
<div> <div className="text-xs text-muted-foreground bg-muted/50 p-2 rounded">
Processed:{" "} <div>
{syncResults[connector.id]?.processed || 0} Processed:{" "}
</div> {syncResults[connector.id]?.processed || 0}
<div> </div>
Added: {syncResults[connector.id]?.added || 0} <div>
</div> Added: {syncResults[connector.id]?.added || 0}
{syncResults[connector.id]?.errors && ( </div>
<div> {syncResults[connector.id]?.errors && (
Errors: {syncResults[connector.id]?.errors} <div>
Errors: {syncResults[connector.id]?.errors}
</div>
)}
</div> </div>
)} )}
</div> </>
) : (
<Button
onClick={() => handleConnect(connector)}
disabled={isConnecting === connector.id}
className="w-full cursor-pointer"
size="sm"
>
{isConnecting === connector.id ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Connecting...
</>
) : (
<>
<PlugZap className="mr-2 h-4 w-4" />
Connect
</>
)}
</Button>
)} )}
</div> </div>
) : ( ) : (
@ -882,7 +865,7 @@ function KnowledgeSourcesPage() {
} }
confirmText="Proceed" confirmText="Proceed"
confirmIcon={<ArrowUpRight />} confirmIcon={<ArrowUpRight />}
onConfirm={closeDialog => onConfirm={(closeDialog) =>
handleEditInLangflow("chat", closeDialog) handleEditInLangflow("chat", closeDialog)
} }
variant="warning" variant="warning"
@ -918,7 +901,7 @@ function KnowledgeSourcesPage() {
id="system-prompt" id="system-prompt"
placeholder="Enter your agent instructions here..." placeholder="Enter your agent instructions here..."
value={systemPrompt} value={systemPrompt}
onChange={e => setSystemPrompt(e.target.value)} onChange={(e) => setSystemPrompt(e.target.value)}
rows={6} rows={6}
className={`resize-none ${ className={`resize-none ${
systemPrompt.length > MAX_SYSTEM_PROMPT_CHARS systemPrompt.length > MAX_SYSTEM_PROMPT_CHARS
@ -1032,7 +1015,7 @@ function KnowledgeSourcesPage() {
confirmText="Proceed" confirmText="Proceed"
confirmIcon={<ArrowUpRight />} confirmIcon={<ArrowUpRight />}
variant="warning" variant="warning"
onConfirm={closeDialog => onConfirm={(closeDialog) =>
handleEditInLangflow("ingest", closeDialog) handleEditInLangflow("ingest", closeDialog)
} }
/> />
@ -1052,7 +1035,8 @@ function KnowledgeSourcesPage() {
disabled={true} disabled={true}
value={ value={
settings.knowledge?.embedding_model || settings.knowledge?.embedding_model ||
modelsData?.embedding_models?.find(m => m.default)?.value || modelsData?.embedding_models?.find((m) => m.default)
?.value ||
"text-embedding-ada-002" "text-embedding-ada-002"
} }
onValueChange={handleEmbeddingModelChange} onValueChange={handleEmbeddingModelChange}
@ -1088,7 +1072,7 @@ function KnowledgeSourcesPage() {
type="number" type="number"
min="1" min="1"
value={chunkSize} value={chunkSize}
onChange={e => handleChunkSizeChange(e.target.value)} onChange={(e) => handleChunkSizeChange(e.target.value)}
className="w-full pr-20 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" className="w-full pr-20 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
/> />
<div className="absolute inset-y-0 right-0 flex items-center"> <div className="absolute inset-y-0 right-0 flex items-center">
@ -1131,7 +1115,7 @@ function KnowledgeSourcesPage() {
type="number" type="number"
min="0" min="0"
value={chunkOverlap} value={chunkOverlap}
onChange={e => handleChunkOverlapChange(e.target.value)} onChange={(e) => handleChunkOverlapChange(e.target.value)}
className="w-full pr-20 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" className="w-full pr-20 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
/> />
<div className="absolute inset-y-0 right-0 flex items-center"> <div className="absolute inset-y-0 right-0 flex items-center">