Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ jobs:
LANGFLOW_AUTO_LOGIN: "True"
LANGFLOW_NEW_USER_IS_ACTIVE: "True"
LANGFLOW_ENABLE_SUPERUSER_CLI: "True"
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WATSONX_API_KEY: ${{ secrets.WATSONX_API_KEY }}
WATSONX_ENDPOINT: ${{ secrets.WATSONX_ENDPOINT }}
WATSONX_PROJECT_ID: ${{ secrets.WATSONX_PROJECT_ID }}
OLLAMA_ENDPOINT: ${{ secrets.OLLAMA_ENDPOINT }}
GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.GOOGLE_OAUTH_CLIENT_ID }}
GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.GOOGLE_OAUTH_CLIENT_SECRET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
OPENSEARCH_PASSWORD: ${{ vars.OPENSEARCH_PASSWORD || secrets.OPENSEARCH_PASSWORD || 'OpenRag#2025!' }}

steps:
- name: Cleanup Docker cache
Expand Down Expand Up @@ -151,6 +162,7 @@ jobs:
OPENSEARCH_HOST: localhost
OPENSEARCH_PORT: "9200"
OPENSEARCH_USERNAME: admin
OPENSEARCH_PASSWORD: ${{ env.OPENSEARCH_PASSWORD }}
GOOGLE_OAUTH_CLIENT_ID: ""
GOOGLE_OAUTH_CLIENT_SECRET: ""
run: npx playwright test
Expand Down
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -527,12 +527,14 @@ factory-reset: ## Complete reset (stop, remove volumes, clear data, remove image
fi; \
if [ -d "config" ]; then \
echo "Removing config..."; \
$(CONTAINER_RUNTIME) run --rm -v "$$(pwd)/config:/data" alpine sh -c "rm -rf /data/*" 2>/dev/null || true; \
rm -rf config; \
echo "$(PURPLE)config removed$(NC)"; \
fi; \
if [ -f "keys/private_key.pem" ] || [ -f "keys/public_key.pem" ]; then \
echo "Removing JWT keys..."; \
rm -f keys/private_key.pem keys/public_key.pem; \
if [ -d "keys" ]; then \
echo "Removing keys..."; \
$(CONTAINER_RUNTIME) run --rm -v "$$(pwd)/keys:/data" alpine sh -c "rm -rf /data/*" 2>/dev/null || true; \
rm -rf keys; \
echo "$(PURPLE)JWT keys removed$(NC)"; \
fi; \
echo "$(YELLOW)Removing OpenRAG images...$(NC)"; \
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ services:
context: .
dockerfile: Dockerfile.langflow
container_name: langflow
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "${LANGFLOW_PORT:-7860}:7860"
environment:
Expand Down
6 changes: 6 additions & 0 deletions frontend/.env.test.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
DISABLE_INGEST_WITH_LANGFLOW=false
INGEST_SAMPLE_DATA=true

# Langflow Configuration
LANGFLOW_CHAT_FLOW_ID='1098eea1-6649-4e1d-aed1-b77249fb8dd0'
LANGFLOW_INGEST_FLOW_ID='5488df7c-b93f-4f87-a446-b67028bc0813'
LANGFLOW_URL_INGEST_FLOW_ID='72c3d17c-2dac-4a73-b48a-6518473d7830'
NUDGES_FLOW_ID='ebc01d31-1976-46ce-a385-b0240327226c'

# Auth
OPENSEARCH_PASSWORD=

Expand Down
2 changes: 2 additions & 0 deletions frontend/app/chat/_components/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
autoComplete="off"
minRows={1}
placeholder="Ask a question..."
data-testid="chat-input"
disabled={loading}
className={`w-full text-sm bg-transparent focus-visible:outline-none resize-none`}
rows={1}
Expand Down Expand Up @@ -499,6 +500,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
size="iconSm"
disabled={(!input.trim() && !uploadedFile) || loading}
className="!rounded-md h-8 w-8 p-0"
data-testid="send-button"
>
{loading ? (
<Loader2 className="h-4 w-4 animate-spin" />
Expand Down
1 change: 1 addition & 0 deletions frontend/app/chat/_components/nudges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default function Nudges({
{nudges.map((suggestion: string, index: number) => (
<button
key={index}
data-testid={`suggestion-${index}`}
onClick={() => handleSuggestionClick(suggestion)}
className={cn(
onboarding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const SelectedKnowledgeFilter = ({
className={`inline-flex items-center p-1 rounded-sm text-xs font-medium transition-colors ${
filterAccentClasses[parsedFilterData?.color || "zinc"]
}`}
data-testid={`selected-knowledge-filter`}
>
{selectedFilter.name}
<button
Expand Down
1 change: 1 addition & 0 deletions frontend/app/chat/_components/user-message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export function UserMessage({
"text-foreground text-sm py-1.5 whitespace-pre-wrap break-words overflow-wrap-anywhere transition-colors duration-300",
isCompleted ? "text-placeholder-foreground" : "text-foreground",
)}
data-testid="user-message"
>
{content}
</p>
Expand Down
6 changes: 5 additions & 1 deletion frontend/app/onboarding/_components/advanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ export function AdvancedOnboarding({
return (
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Advanced settings</AccordionTrigger>
<AccordionTrigger data-testid="advanced-settings-button">
Advanced settings
</AccordionTrigger>
<AccordionContent className="space-y-6">
{hasEmbeddingModels && (
<LabelWrapper
Expand All @@ -47,6 +49,7 @@ export function AdvancedOnboarding({
>
<ModelSelector
options={embeddingModels}
data-testid="embedding-model-selector"
icon={icon}
value={embeddingModel}
onValueChange={setEmbeddingModel}
Expand All @@ -62,6 +65,7 @@ export function AdvancedOnboarding({
>
<ModelSelector
options={languageModels}
data-testid="language-model-selector"
icon={icon}
value={languageModel}
onValueChange={setLanguageModel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export function AnthropicOnboarding({
<div>
<Switch
checked={getFromEnv}
data-testid="get-from-env-switch"
onCheckedChange={handleGetFromEnvChange}
disabled={!hasEnvApiKey}
/>
Expand Down
1 change: 1 addition & 0 deletions frontend/app/onboarding/_components/ibm-onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export function IBMOnboarding({
<div>
<Switch
checked={getFromEnv}
data-testid="get-from-env-switch"
onCheckedChange={handleGetFromEnvChange}
disabled={!hasEnvApiKey || alreadyConfigured}
/>
Expand Down
43 changes: 27 additions & 16 deletions frontend/app/onboarding/_components/model-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Button, type ButtonProps } from "@/components/ui/button";
import {
Command,
CommandEmpty,
Expand Down Expand Up @@ -30,6 +30,20 @@ export type GroupedModelOption = {
icon?: React.ReactNode;
};

export interface ModelSelectorProps extends ButtonProps {
options?: ModelOption[];
groupedOptions?: GroupedModelOption[];
value: string;
icon?: React.ReactNode;
placeholder?: string;
searchPlaceholder?: string;
noOptionsPlaceholder?: string;
custom?: boolean;
onValueChange: (value: string, provider?: string) => void;
hasError?: boolean;
defaultOpen?: boolean;
}

export function ModelSelector({
options,
groupedOptions,
Expand All @@ -42,19 +56,11 @@ export function ModelSelector({
custom = false,
hasError = false,
defaultOpen = false,
}: {
options?: ModelOption[];
groupedOptions?: GroupedModelOption[];
value: string;
icon?: React.ReactNode;
placeholder?: string;
searchPlaceholder?: string;
noOptionsPlaceholder?: string;
custom?: boolean;
onValueChange: (value: string, provider?: string) => void;
hasError?: boolean;
defaultOpen?: boolean;
}) {
className,
disabled,
variant = "outline",
...props
}: ModelSelectorProps) {
const [open, setOpen] = useState(defaultOpen);
const [searchValue, setSearchValue] = useState("");

Expand Down Expand Up @@ -90,14 +96,16 @@ export function ModelSelector({
<Popover open={open} onOpenChange={setOpen} modal={false}>
<PopoverTrigger asChild>
<Button
variant="outline"
variant={variant}
role="combobox"
disabled={allOptions.length === 0}
disabled={disabled || allOptions.length === 0}
aria-expanded={open}
className={cn(
"w-full gap-2 justify-between font-normal text-sm",
hasError && "!border-destructive",
className,
)}
{...props}
>
{value ? (
<div className="flex items-center gap-2">
Expand Down Expand Up @@ -161,6 +169,7 @@ export function ModelSelector({
<CommandItem
key={option.value}
value={option.value}
data-testid={`model-option-${option.value}`}
onSelect={(currentValue) => {
if (currentValue !== value) {
onValueChange(currentValue, option.provider);
Expand Down Expand Up @@ -190,6 +199,7 @@ export function ModelSelector({
<CommandItem
key={option.value}
value={option.value}
data-testid={`model-option-${option.value}`}
onSelect={(currentValue) => {
if (currentValue !== value) {
onValueChange(currentValue, option.provider);
Expand All @@ -215,6 +225,7 @@ export function ModelSelector({
) && (
<CommandItem
value={searchValue}
data-testid={`model-custom-option-${searchValue}`}
onSelect={(currentValue) => {
if (currentValue !== value) {
onValueChange(currentValue);
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/onboarding/_components/ollama-onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export function OllamaOnboarding({
>
<ModelSelector
options={embeddingModels}
data-testid="embedding-model-selector"
icon={<OllamaLogo className="w-4 h-4" />}
noOptionsPlaceholder={
isLoadingModels
Expand All @@ -160,6 +161,7 @@ export function OllamaOnboarding({
>
<ModelSelector
options={languageModels}
data-testid="language-model-selector"
icon={<OllamaLogo className="w-4 h-4" />}
noOptionsPlaceholder={
isLoadingModels
Expand Down
11 changes: 9 additions & 2 deletions frontend/app/onboarding/_components/onboarding-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -332,10 +332,12 @@ const OnboardingCard = ({
return;
}

const hasSuccessfulTasks = relevantTasks.length > 0 &&
const hasSuccessfulTasks =
relevantTasks.length > 0 &&
(!activeTasks || (activeTasks.successful_files ?? 0) > 0);

const hasIngestionDisabledOrDone = !onboardingTaskId && currentStep === totalSteps - 1;
const hasIngestionDisabledOrDone =
!onboardingTaskId && currentStep === totalSteps - 1;

// If at least one processed file, no failures, and we've started onboarding, complete it
if (
Expand Down Expand Up @@ -470,6 +472,7 @@ const OnboardingCard = ({
{!isEmbedding && (
<TabsTrigger
value="anthropic"
data-testid={`anthropic-llm-tab`}
className={cn(
error &&
modelProvider === "anthropic" &&
Expand Down Expand Up @@ -508,6 +511,7 @@ const OnboardingCard = ({
modelProvider === "openai" &&
"data-[state=active]:border-destructive",
)}
data-testid={`openai-${isEmbedding ? "embedding" : "llm"}-tab`}
>
<TabTrigger
selected={modelProvider === "openai"}
Expand All @@ -533,6 +537,7 @@ const OnboardingCard = ({
</TabsTrigger>
<TabsTrigger
value="watsonx"
data-testid={`watsonx-${isEmbedding ? "embedding" : "llm"}-tab`}
className={cn(
error &&
modelProvider === "watsonx" &&
Expand Down Expand Up @@ -565,6 +570,7 @@ const OnboardingCard = ({
</TabsTrigger>
<TabsTrigger
value="ollama"
data-testid={`ollama-${isEmbedding ? "embedding" : "llm"}-tab`}
className={cn(
error &&
modelProvider === "ollama" &&
Expand Down Expand Up @@ -659,6 +665,7 @@ const OnboardingCard = ({
<div>
<Button
size="sm"
data-testid="onboarding-complete-button"
onClick={handleComplete}
disabled={!isComplete || isLoadingModels}
loading={onboardingMutation.isPending}
Expand Down
1 change: 1 addition & 0 deletions frontend/app/onboarding/_components/onboarding-upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
<Button
size="sm"
variant="outline"
data-testid="upload-button"
onClick={handleUploadClick}
disabled={isUploading}
>
Expand Down
1 change: 1 addition & 0 deletions frontend/app/onboarding/_components/openai-onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export function OpenAIOnboarding({
<div>
<Switch
checked={getFromEnv}
data-testid="get-from-env-switch"
onCheckedChange={handleGetFromEnvChange}
disabled={!hasEnvApiKey}
/>
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/onboarding/_components/progress-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function ProgressBar({
<div className="w-48 h-1 bg-background dark:bg-muted rounded-full overflow-hidden">
<div
className="h-full transition-all duration-300 ease-in-out"
data-testid={`progress-bar-${currentStep}`}
style={{
width: `${progressPercentage}%`,
background: "linear-gradient(to right, #773EFF, #22A7AF)",
Expand All @@ -35,6 +36,7 @@ export function ProgressBar({
{currentStep > 1 && onSkip && (
<Button
variant="link"
data-testid="skip-overview-button"
size="sm"
onClick={onSkip}
className="flex items-center gap-2 text-mmd !text-placeholder-foreground hover:!text-foreground hover:!no-underline"
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/label-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function LabelInput({
required={required}
disabled={props.disabled}
>
<Input id={id} {...props} />
<Input id={id} data-testid={id} {...props} />
</LabelWrapper>
);
}
2 changes: 2 additions & 0 deletions frontend/components/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ export function Navigation({
<button
type="button"
className="p-1 hover:bg-accent rounded"
data-testid="new-conversation-button"
onClick={handleNewConversation}
title="Start new conversation"
disabled={loading}
Expand Down Expand Up @@ -460,6 +461,7 @@ export function Navigation({
{conversations.map((conversation) => (
<button
key={conversation.response_id}
data-testid={`conversation-button-${conversation.title}`}
type="button"
className={`w-full px-3 h-11 rounded-lg group relative text-left ${
loading || isConversationsLoading
Expand Down
Loading
Loading