Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0e25241
feat: Integrate IBM COS connector with HMAC as default connection method
edwinjosechittilappilly Mar 9, 2026
7e05cad
Merge branch 'main' into IBM_COS_S3_SPIKE
edwinjosechittilappilly Mar 9, 2026
671f294
Add Amazon S3 connector and UI
edwinjosechittilappilly Mar 9, 2026
aacebb9
Merge pull request #1080 from langflow-ai/feat-s3
edwinjosechittilappilly Mar 9, 2026
7658d53
S3: env fallback & show bucket errors
edwinjosechittilappilly Mar 9, 2026
8676f15
Improve connector UI, sync S3, fix AWS logo
edwinjosechittilappilly Mar 9, 2026
153463e
Update connector-card.tsx
edwinjosechittilappilly Mar 9, 2026
627881d
Merge pull request #1082 from langflow-ai/feat-s3
edwinjosechittilappilly Mar 9, 2026
f2b7c70
Merge branch 'main' into IBM_COS_S3_SPIKE
edwinjosechittilappilly Mar 9, 2026
ad77804
Potential fix for code scanning alert no. 94: Information exposure th…
edwinjosechittilappilly Mar 10, 2026
a322b44
Potential fix for code scanning alert no. 89: Information exposure th…
edwinjosechittilappilly Mar 10, 2026
547e6a9
Potential fix for code scanning alert no. 90: Information exposure th…
edwinjosechittilappilly Mar 10, 2026
62697d0
Potential fix for code scanning alert no. 91: Information exposure th…
edwinjosechittilappilly Mar 10, 2026
b4a8462
Merge branch 'main' into IBM_COS_S3_SPIKE
edwinjosechittilappilly Mar 10, 2026
8b9adf5
Fix IBM COS bucket handling; update .gitignore
edwinjosechittilappilly Mar 10, 2026
0d3d074
Add COS env vars and improve connector logging
edwinjosechittilappilly Mar 10, 2026
28694ab
Refactor S3 and IBM COS connector APIs
edwinjosechittilappilly Mar 10, 2026
54b10f0
Improve S3/IBM COS error handling and logging
edwinjosechittilappilly Mar 10, 2026
44ec1cf
Add IBM COS auth endpoint and AWS S3/region envs
edwinjosechittilappilly Mar 10, 2026
37227cc
aws logo
mfortman11 Mar 11, 2026
57c4546
update ingest page
mfortman11 Mar 11, 2026
55961a3
Merge branch 'main' of github.com:langflow-ai/openrag into IBM_COS_S3…
mfortman11 Mar 11, 2026
d820160
merge fix
mfortman11 Mar 11, 2026
091cfd1
Merge branch 'main' of github.com:langflow-ai/openrag into IBM_COS_S3…
mfortman11 Mar 13, 2026
14c39a4
icons and connector design updates
mfortman11 Mar 13, 2026
985bb73
select buttons
mfortman11 Mar 13, 2026
f05568c
fix: Enhance connector availability checks to be user-scoped by addin…
ricofurtado Mar 13, 2026
f283a4a
Merge branch 'main' into IBM_COS_S3_SPIKE
edwinjosechittilappilly Mar 13, 2026
9fee2e8
Add logout support
mfortman11 Mar 16, 2026
b28467a
Merge branch 'IBM_COS_S3_SPIKE' of github.com:langflow-ai/openrag int…
mfortman11 Mar 16, 2026
2513cdc
Revert "Add logout support"
mfortman11 Mar 16, 2026
18446dd
make bucket connectors avaliable by default
mfortman11 Mar 16, 2026
7801acb
support adding via the add knowlegde on the knowledge page
mfortman11 Mar 16, 2026
6d42b54
feature flag for new connectors
mfortman11 Mar 16, 2026
7e92f0b
Merge branch 'main' of github.com:langflow-ai/openrag into IBM_COS_S3…
mfortman11 Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ documents/warmup_ocr.pdf
documents/openrag-documentation.pdf
documents/ibm_anthropic.pdf
documents/docling.pdf
/opensearch-data-new-lf
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ services:
- WEBHOOK_BASE_URL=${WEBHOOK_BASE_URL}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
- AWS_S3_ENDPOINT=${AWS_S3_ENDPOINT}
- AWS_REGION=${AWS_REGION}
- IBM_COS_API_KEY=${IBM_COS_API_KEY}
- IBM_COS_SERVICE_INSTANCE_ID=${IBM_COS_SERVICE_INSTANCE_ID}
- IBM_COS_ENDPOINT=${IBM_COS_ENDPOINT}
- IBM_COS_HMAC_ACCESS_KEY_ID=${IBM_COS_HMAC_ACCESS_KEY_ID}
- IBM_COS_HMAC_SECRET_ACCESS_KEY=${IBM_COS_HMAC_SECRET_ACCESS_KEY}
- IBM_COS_AUTH_ENDPOINT=${IBM_COS_AUTH_ENDPOINT}
- OPENSEARCH_INDEX_NAME=${OPENSEARCH_INDEX_NAME:-documents}
- LANGFLOW_KEY=${LANGFLOW_KEY}
- LANGFLOW_KEY_RETRIES=${LANGFLOW_KEY_RETRIES:-15}
Expand Down
5 changes: 5 additions & 0 deletions frontend/app/api/mutations/useConnectConnectorMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ export const useConnectConnectorMutation = () => {
`state=${result.connection_id}`;

window.location.href = authUrl;
} else {
// Direct-auth connector (e.g. IBM COS) — credentials already verified,
// no OAuth redirect needed. Refresh connector status.
queryClient.invalidateQueries({ queryKey: ["connectors"] });
toast.success(`${connector.name} connected successfully`);
}
},
});
Expand Down
40 changes: 40 additions & 0 deletions frontend/app/api/mutations/useIBMCOSConfigureMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";

export interface IBMCOSConfigurePayload {
auth_mode: "iam" | "hmac";
endpoint: string;
// IAM
api_key?: string;
service_instance_id?: string;
auth_endpoint?: string;
// HMAC
hmac_access_key?: string;
hmac_secret_key?: string;
// Bucket selection
bucket_names?: string[];
// Updating an existing connection
connection_id?: string;
}

async function configureIBMCOS(payload: IBMCOSConfigurePayload) {
const res = await fetch("/api/connectors/ibm_cos/configure", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || "Failed to configure IBM COS");
return data as { connection_id: string; status: string };
}

export function useIBMCOSConfigureMutation() {
const queryClient = useQueryClient();

return useMutation({
mutationFn: configureIBMCOS,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["connectors"] });
queryClient.invalidateQueries({ queryKey: ["ibm-cos-defaults"] });
},
});
}
33 changes: 33 additions & 0 deletions frontend/app/api/mutations/useS3ConfigureMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";

export interface S3ConfigurePayload {
access_key?: string;
secret_key?: string;
endpoint_url?: string;
region?: string;
bucket_names?: string[];
connection_id?: string;
}

async function configureS3(payload: S3ConfigurePayload) {
const res = await fetch("/api/connectors/aws_s3/configure", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || "Failed to configure S3");
return data as { connection_id: string; status: string };
}

export function useS3ConfigureMutation() {
const queryClient = useQueryClient();

return useMutation({
mutationFn: configureS3,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["connectors"] });
queryClient.invalidateQueries({ queryKey: ["s3-defaults"] });
},
});
}
4 changes: 4 additions & 0 deletions frontend/app/api/mutations/useSyncConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ const syncConnector = async ({
size?: number;
}>;
settings?: any;
/** When true, ingest all files from the connector (bypasses the re-sync gate). */
sync_all?: boolean;
/** Restrict ingest to these bucket names (IBM COS). */
bucket_filter?: string[];
};
}): Promise<SyncResponse> => {
const response = await fetch(`/api/connectors/${connectorType}/sync`, {
Expand Down
34 changes: 34 additions & 0 deletions frontend/app/api/queries/useIBMCOSBucketStatusQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useQuery } from "@tanstack/react-query";

export interface IBMCOSBucketStatus {
name: string;
ingested_count: number;
is_synced: boolean;
}

async function fetchIBMCOSBucketStatus(
connectionId: string,
): Promise<IBMCOSBucketStatus[]> {
const res = await fetch(
`/api/connectors/ibm_cos/${connectionId}/bucket-status`,
);
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.error || "Failed to fetch bucket status");
}
const data = await res.json();
return data.buckets as IBMCOSBucketStatus[];
}

export function useIBMCOSBucketStatusQuery(
connectionId: string | null | undefined,
options?: { enabled?: boolean },
) {
return useQuery<IBMCOSBucketStatus[]>({
queryKey: ["ibm-cos-bucket-status", connectionId],
queryFn: () => fetchIBMCOSBucketStatus(connectionId!),
enabled: (options?.enabled ?? true) && !!connectionId,
staleTime: 0,
refetchOnMount: "always",
});
}
23 changes: 23 additions & 0 deletions frontend/app/api/queries/useIBMCOSBucketsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useQuery } from "@tanstack/react-query";

async function fetchIBMCOSBuckets(connectionId: string): Promise<string[]> {
const res = await fetch(`/api/connectors/ibm_cos/${connectionId}/buckets`);
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.error || "Failed to list buckets");
}
const data = await res.json();
return data.buckets as string[];
}

export function useIBMCOSBucketsQuery(
connectionId: string | null | undefined,
options?: { enabled?: boolean },
) {
return useQuery<string[]>({
queryKey: ["ibm-cos-buckets", connectionId],
queryFn: () => fetchIBMCOSBuckets(connectionId!),
enabled: (options?.enabled ?? true) && !!connectionId,
staleTime: 30_000,
});
}
28 changes: 28 additions & 0 deletions frontend/app/api/queries/useIBMCOSDefaultsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useQuery } from "@tanstack/react-query";

export interface IBMCOSDefaults {
api_key_set: boolean;
service_instance_id: string;
endpoint: string;
hmac_access_key_set: boolean;
hmac_secret_key_set: boolean;
auth_mode: "iam" | "hmac";
bucket_names: string[];
connection_id: string | null;
disable_iam: boolean;
}

async function fetchIBMCOSDefaults(): Promise<IBMCOSDefaults> {
const res = await fetch("/api/connectors/ibm_cos/defaults");
if (!res.ok) throw new Error("Failed to fetch IBM COS defaults");
return res.json();
}

export function useIBMCOSDefaultsQuery(options?: { enabled?: boolean }) {
return useQuery<IBMCOSDefaults>({
queryKey: ["ibm-cos-defaults"],
queryFn: fetchIBMCOSDefaults,
enabled: options?.enabled ?? true,
staleTime: 0,
});
}
30 changes: 30 additions & 0 deletions frontend/app/api/queries/useS3BucketStatusQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useQuery } from "@tanstack/react-query";

export interface S3BucketStatus {
name: string;
ingested_count: number;
is_synced: boolean;
}

async function fetchS3BucketStatus(connectionId: string): Promise<S3BucketStatus[]> {
const res = await fetch(`/api/connectors/aws_s3/${connectionId}/bucket-status`);
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.error || "Failed to fetch bucket status");
}
const data = await res.json();
return data.buckets as S3BucketStatus[];
}

export function useS3BucketStatusQuery(
connectionId: string | null | undefined,
options?: { enabled?: boolean },
) {
return useQuery<S3BucketStatus[]>({
queryKey: ["s3-bucket-status", connectionId],
queryFn: () => fetchS3BucketStatus(connectionId!),
enabled: (options?.enabled ?? true) && !!connectionId,
staleTime: 0,
refetchOnMount: "always",
});
}
25 changes: 25 additions & 0 deletions frontend/app/api/queries/useS3DefaultsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useQuery } from "@tanstack/react-query";

export interface S3Defaults {
access_key_set: boolean;
secret_key_set: boolean;
endpoint: string;
region: string;
bucket_names: string[];
connection_id: string | null;
}

async function fetchS3Defaults(): Promise<S3Defaults> {
const res = await fetch("/api/connectors/aws_s3/defaults");
if (!res.ok) throw new Error("Failed to fetch S3 defaults");
return res.json();
}

export function useS3DefaultsQuery(options?: { enabled?: boolean }) {
return useQuery<S3Defaults>({
queryKey: ["s3-defaults"],
queryFn: fetchS3Defaults,
enabled: options?.enabled ?? true,
staleTime: 0,
});
}
7 changes: 7 additions & 0 deletions frontend/app/knowledge/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ import {
DeleteConfirmationDialog,
formatFilesToDelete,
} from "../../components/delete-confirmation-dialog";
import AwsLogo from "../../components/icons/aws-logo";
import GoogleDriveIcon from "../../components/icons/google-drive-logo";
import IBMCOSIcon from "../../components/icons/ibm-cos-icon";
import IBMLogo from "../../components/icons/ibm-logo";
import OneDriveIcon from "../../components/icons/one-drive-logo";
import SharePointIcon from "../../components/icons/share-point-logo";
import { useDeleteDocument } from "../api/mutations/useDeleteDocument";
Expand All @@ -59,6 +62,10 @@ function getSourceIcon(connectorType?: string) {
return <Globe className="h-4 w-4 text-muted-foreground flex-shrink-0" />;
case "s3":
return <Cloud className="h-4 w-4 text-foreground flex-shrink-0" />;
case "ibm_cos":
return <IBMCOSIcon className="h-4 w-4 flex-shrink-0" />;
case "aws_s3":
return <AwsLogo className="h-4 w-4 flex-shrink-0" />;
default:
return (
<FileIcon className="h-4 w-4 text-muted-foreground flex-shrink-0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,13 @@ const AnthropicSettingsDialog = ({
};

return (
<Dialog open={open} onOpenChange={(o) => { setShowRemoveConfirm(false); setOpen(o); }}>
<Dialog
open={open}
onOpenChange={(o) => {
setShowRemoveConfirm(false);
setOpen(o);
}}
>
<DialogContent className="max-w-2xl">
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)} className="grid gap-4">
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/settings/_components/card-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function CardIcon({
className={cn(
"w-8 h-8 rounded flex items-center justify-center border",
isActive
? activeBgColor
? `${activeBgColor} text-black`
: "bg-muted grayscale group-hover:bg-background",
)}
>
Expand Down
23 changes: 13 additions & 10 deletions frontend/app/settings/_components/connector-card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { Loader2, Plus, RefreshCcw, Trash2 } from "lucide-react";
import { Loader2, Plus, RefreshCcw, Settings2, Trash2 } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import {
Expand Down Expand Up @@ -29,6 +29,8 @@ interface ConnectorCardProps {
onConnect: (connector: Connector) => void;
onDisconnect: (connector: Connector) => void;
onNavigateToKnowledge: (connector: Connector) => void;
/** Optional: open a connector-specific settings/edit dialog */
onConfigure?: (connector: Connector) => void;
}

export default function ConnectorCard({
Expand All @@ -38,22 +40,19 @@ export default function ConnectorCard({
onConnect,
onDisconnect,
onNavigateToKnowledge,
onConfigure,
}: ConnectorCardProps) {
console.log(connector);
const isConnected =
connector.status === "connected" && connector.connectionId;
connector?.status === "connected" && connector?.connectionId;

return (
<Card className="group relative flex flex-col hover:bg-secondary-hover hover:border-muted-foreground transition-colors">
<CardHeader className="pb-2">
<div className="flex flex-col items-start justify-between">
<div className="flex flex-col gap-4 mb-2 w-full">
<div className="flex items-center justify-between mb-1">
<CardIcon
isActive={!!(connector?.available && isConnected)}
activeBgColor="bg-white"
>
<CardIcon isActive={!!isConnected} activeBgColor="bg-white">
{connector.icon}
</CardIcon>
{isConnected ? (
Expand All @@ -68,7 +67,7 @@ export default function ConnectorCard({
{connector.name}
</CardTitle>
<CardDescription className="text-sm">
{connector?.available
{isConnected || connector?.available
? `${connector.name} is configured.`
: "Not configured."}
</CardDescription>
Expand All @@ -93,15 +92,17 @@ export default function ConnectorCard({
</Button>
<Button
variant="outline"
onClick={() => onConnect(connector)}
onClick={() =>
onConfigure ? onConfigure(connector) : onConnect(connector)
}
disabled={isConnecting || isDisconnecting}
className="cursor-pointer"
size="iconMd"
>
{isConnecting ? (
<RefreshCcw className="h-4 w-4 animate-spin" />
) : (
<RefreshCcw className="h-4 w-4" />
<Settings2 className="h-4 w-4" />
)}
</Button>
<Button
Expand All @@ -120,7 +121,9 @@ export default function ConnectorCard({
</div>
) : (
<Button
onClick={() => onConnect(connector)}
onClick={() =>
onConfigure ? onConfigure(connector) : onConnect(connector)
}
disabled={isConnecting}
className="w-full cursor-pointer group-hover:bg-background group-hover:border-zinc-700 group-hover:text-primary"
size="sm"
Expand Down
Loading
Loading