Fixed ibm validation happening when api key is not present

This commit is contained in:
Lucas Oliveira 2025-12-05 12:58:18 -03:00
parent 2e7a4ceb4f
commit 2169df80f8

View file

@ -1,13 +1,13 @@
import type { Dispatch, SetStateAction } from "react"; import type { Dispatch, SetStateAction } from "react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import IBMLogo from "@/components/icons/ibm-logo";
import { LabelInput } from "@/components/label-input"; import { LabelInput } from "@/components/label-input";
import { LabelWrapper } from "@/components/label-wrapper"; import { LabelWrapper } from "@/components/label-wrapper";
import IBMLogo from "@/components/icons/ibm-logo";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { useDebouncedValue } from "@/lib/debounce"; import { useDebouncedValue } from "@/lib/debounce";
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation"; import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
@ -18,273 +18,273 @@ import { AdvancedOnboarding } from "./advanced";
import { ModelSelector } from "./model-selector"; import { ModelSelector } from "./model-selector";
export function IBMOnboarding({ export function IBMOnboarding({
isEmbedding = false, isEmbedding = false,
setSettings, setSettings,
sampleDataset, sampleDataset,
setSampleDataset, setSampleDataset,
setIsLoadingModels, setIsLoadingModels,
alreadyConfigured = false, alreadyConfigured = false,
existingEndpoint, existingEndpoint,
existingProjectId, existingProjectId,
hasEnvApiKey = false, hasEnvApiKey = false,
}: { }: {
isEmbedding?: boolean; isEmbedding?: boolean;
setSettings: Dispatch<SetStateAction<OnboardingVariables>>; setSettings: Dispatch<SetStateAction<OnboardingVariables>>;
sampleDataset: boolean; sampleDataset: boolean;
setSampleDataset: (dataset: boolean) => void; setSampleDataset: (dataset: boolean) => void;
setIsLoadingModels?: (isLoading: boolean) => void; setIsLoadingModels?: (isLoading: boolean) => void;
alreadyConfigured?: boolean; alreadyConfigured?: boolean;
existingEndpoint?: string; existingEndpoint?: string;
existingProjectId?: string; existingProjectId?: string;
hasEnvApiKey?: boolean; hasEnvApiKey?: boolean;
}) { }) {
const [endpoint, setEndpoint] = useState( const [endpoint, setEndpoint] = useState(
alreadyConfigured ? "" : (existingEndpoint || "https://us-south.ml.cloud.ibm.com"), alreadyConfigured
); ? ""
const [apiKey, setApiKey] = useState(""); : existingEndpoint || "https://us-south.ml.cloud.ibm.com",
const [getFromEnv, setGetFromEnv] = useState( );
hasEnvApiKey && !alreadyConfigured, const [apiKey, setApiKey] = useState("");
); const [getFromEnv, setGetFromEnv] = useState(
const [projectId, setProjectId] = useState( hasEnvApiKey && !alreadyConfigured,
alreadyConfigured ? "" : (existingProjectId || ""), );
); const [projectId, setProjectId] = useState(
alreadyConfigured ? "" : existingProjectId || "",
);
const options = [ const options = [
{ {
value: "https://us-south.ml.cloud.ibm.com", value: "https://us-south.ml.cloud.ibm.com",
label: "https://us-south.ml.cloud.ibm.com", label: "https://us-south.ml.cloud.ibm.com",
default: true, default: true,
}, },
{ {
value: "https://eu-de.ml.cloud.ibm.com", value: "https://eu-de.ml.cloud.ibm.com",
label: "https://eu-de.ml.cloud.ibm.com", label: "https://eu-de.ml.cloud.ibm.com",
default: false, default: false,
}, },
{ {
value: "https://eu-gb.ml.cloud.ibm.com", value: "https://eu-gb.ml.cloud.ibm.com",
label: "https://eu-gb.ml.cloud.ibm.com", label: "https://eu-gb.ml.cloud.ibm.com",
default: false, default: false,
}, },
{ {
value: "https://au-syd.ml.cloud.ibm.com", value: "https://au-syd.ml.cloud.ibm.com",
label: "https://au-syd.ml.cloud.ibm.com", label: "https://au-syd.ml.cloud.ibm.com",
default: false, default: false,
}, },
{ {
value: "https://jp-tok.ml.cloud.ibm.com", value: "https://jp-tok.ml.cloud.ibm.com",
label: "https://jp-tok.ml.cloud.ibm.com", label: "https://jp-tok.ml.cloud.ibm.com",
default: false, default: false,
}, },
{ {
value: "https://ca-tor.ml.cloud.ibm.com", value: "https://ca-tor.ml.cloud.ibm.com",
label: "https://ca-tor.ml.cloud.ibm.com", label: "https://ca-tor.ml.cloud.ibm.com",
default: false, default: false,
}, },
]; ];
const debouncedEndpoint = useDebouncedValue(endpoint, 500); const debouncedEndpoint = useDebouncedValue(endpoint, 500);
const debouncedApiKey = useDebouncedValue(apiKey, 500); const debouncedApiKey = useDebouncedValue(apiKey, 500);
const debouncedProjectId = useDebouncedValue(projectId, 500); const debouncedProjectId = useDebouncedValue(projectId, 500);
// Fetch models from API when all credentials are provided // Fetch models from API when all credentials are provided
const { const {
data: modelsData, data: modelsData,
isLoading: isLoadingModels, isLoading: isLoadingModels,
error: modelsError, error: modelsError,
} = useGetIBMModelsQuery( } = useGetIBMModelsQuery(
{ {
endpoint: debouncedEndpoint ? debouncedEndpoint : undefined, endpoint: debouncedEndpoint ? debouncedEndpoint : undefined,
apiKey: getFromEnv ? "" : (debouncedApiKey ? debouncedApiKey : undefined), apiKey: getFromEnv ? "" : debouncedApiKey ? debouncedApiKey : undefined,
projectId: debouncedProjectId ? debouncedProjectId : undefined, projectId: debouncedProjectId ? debouncedProjectId : undefined,
}, },
{ {
enabled: enabled:
!!debouncedEndpoint || (!!debouncedEndpoint && !!debouncedApiKey && !!debouncedProjectId) ||
!!debouncedApiKey || getFromEnv ||
!!debouncedProjectId || alreadyConfigured,
getFromEnv || },
alreadyConfigured, );
},
);
// Use custom hook for model selection logic // Use custom hook for model selection logic
const { const {
languageModel, languageModel,
embeddingModel, embeddingModel,
setLanguageModel, setLanguageModel,
setEmbeddingModel, setEmbeddingModel,
languageModels, languageModels,
embeddingModels, embeddingModels,
} = useModelSelection(modelsData, isEmbedding); } = useModelSelection(modelsData, isEmbedding);
const handleGetFromEnvChange = (fromEnv: boolean) => { const handleGetFromEnvChange = (fromEnv: boolean) => {
setGetFromEnv(fromEnv); setGetFromEnv(fromEnv);
if (fromEnv) { if (fromEnv) {
setApiKey(""); setApiKey("");
} }
setEmbeddingModel?.(""); setEmbeddingModel?.("");
setLanguageModel?.(""); setLanguageModel?.("");
}; };
const handleSampleDatasetChange = (dataset: boolean) => { const handleSampleDatasetChange = (dataset: boolean) => {
setSampleDataset(dataset); setSampleDataset(dataset);
}; };
useEffect(() => { useEffect(() => {
setIsLoadingModels?.(isLoadingModels); setIsLoadingModels?.(isLoadingModels);
}, [isLoadingModels, setIsLoadingModels]); }, [isLoadingModels, setIsLoadingModels]);
// Update settings when values change // Update settings when values change
useUpdateSettings( useUpdateSettings(
"watsonx", "watsonx",
{ {
endpoint, endpoint,
apiKey, apiKey,
projectId, projectId,
languageModel, languageModel,
embeddingModel, embeddingModel,
}, },
setSettings, setSettings,
isEmbedding, isEmbedding,
); );
return ( return (
<> <>
<div className="space-y-4"> <div className="space-y-4">
<LabelWrapper <LabelWrapper
label="watsonx.ai API Endpoint" label="watsonx.ai API Endpoint"
helperText="Base URL of the API" helperText="Base URL of the API"
id="api-endpoint" id="api-endpoint"
required required
> >
<div className="space-y-1"> <div className="space-y-1">
<ModelSelector <ModelSelector
options={alreadyConfigured ? [] : options} options={alreadyConfigured ? [] : options}
value={endpoint} value={endpoint}
custom custom
onValueChange={alreadyConfigured ? () => {} : setEndpoint} onValueChange={alreadyConfigured ? () => {} : setEndpoint}
searchPlaceholder="Search endpoint..." searchPlaceholder="Search endpoint..."
noOptionsPlaceholder={ noOptionsPlaceholder={
alreadyConfigured alreadyConfigured
? "https://•••••••••••••••••••••••••••••••••••••••••" ? "https://•••••••••••••••••••••••••••••••••••••••••"
: "No endpoints available" : "No endpoints available"
} }
placeholder="Select endpoint..." placeholder="Select endpoint..."
/> />
{alreadyConfigured && ( {alreadyConfigured && (
<p className="text-mmd text-muted-foreground"> <p className="text-mmd text-muted-foreground">
Reusing endpoint from model provider selection. Reusing endpoint from model provider selection.
</p> </p>
)} )}
</div> </div>
</LabelWrapper> </LabelWrapper>
<div className="space-y-1"> <div className="space-y-1">
<LabelInput <LabelInput
label="watsonx Project ID" label="watsonx Project ID"
helperText="Project ID for the model" helperText="Project ID for the model"
id="project-id" id="project-id"
required required
placeholder={ placeholder={
alreadyConfigured ? "••••••••••••••••••••••••" : "your-project-id" alreadyConfigured ? "••••••••••••••••••••••••" : "your-project-id"
} }
value={projectId} value={projectId}
onChange={(e) => setProjectId(e.target.value)} onChange={(e) => setProjectId(e.target.value)}
disabled={alreadyConfigured} disabled={alreadyConfigured}
/> />
{alreadyConfigured && ( {alreadyConfigured && (
<p className="text-mmd text-muted-foreground"> <p className="text-mmd text-muted-foreground">
Reusing project ID from model provider selection. Reusing project ID from model provider selection.
</p> </p>
)} )}
</div> </div>
<LabelWrapper <LabelWrapper
label="Use environment watsonx API key" label="Use environment watsonx API key"
id="get-api-key" id="get-api-key"
description="Reuse the key from your environment config. Turn off to enter a different key." description="Reuse the key from your environment config. Turn off to enter a different key."
flex flex
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div> <div>
<Switch <Switch
checked={getFromEnv} checked={getFromEnv}
onCheckedChange={handleGetFromEnvChange} onCheckedChange={handleGetFromEnvChange}
disabled={!hasEnvApiKey || alreadyConfigured} disabled={!hasEnvApiKey || alreadyConfigured}
/> />
</div> </div>
</TooltipTrigger> </TooltipTrigger>
{!hasEnvApiKey && !alreadyConfigured && ( {!hasEnvApiKey && !alreadyConfigured && (
<TooltipContent> <TooltipContent>
watsonx API key not detected in the environment. watsonx API key not detected in the environment.
</TooltipContent> </TooltipContent>
)} )}
</Tooltip> </Tooltip>
</LabelWrapper> </LabelWrapper>
{!getFromEnv && !alreadyConfigured && ( {!getFromEnv && !alreadyConfigured && (
<div className="space-y-1"> <div className="space-y-1">
<LabelInput <LabelInput
label="watsonx API key" label="watsonx API key"
helperText="API key to access watsonx.ai" helperText="API key to access watsonx.ai"
className={modelsError ? "!border-destructive" : ""} className={modelsError ? "!border-destructive" : ""}
id="api-key" id="api-key"
type="password" type="password"
required required
placeholder="your-api-key" placeholder="your-api-key"
value={apiKey} value={apiKey}
onChange={(e) => setApiKey(e.target.value)} onChange={(e) => setApiKey(e.target.value)}
/> />
{isLoadingModels && ( {isLoadingModels && (
<p className="text-mmd text-muted-foreground"> <p className="text-mmd text-muted-foreground">
Validating API key... Validating API key...
</p> </p>
)} )}
{modelsError && ( {modelsError && (
<p className="text-mmd text-destructive"> <p className="text-mmd text-destructive">
Invalid watsonx API key. Verify or replace the key. Invalid watsonx API key. Verify or replace the key.
</p> </p>
)} )}
</div> </div>
)} )}
{alreadyConfigured && ( {alreadyConfigured && (
<div className="space-y-1"> <div className="space-y-1">
<LabelInput <LabelInput
label="watsonx API key" label="watsonx API key"
helperText="API key to access watsonx.ai" helperText="API key to access watsonx.ai"
id="api-key" id="api-key"
type="password" type="password"
required required
placeholder="•••••••••••••••••••••••••••••••••••••••••" placeholder="•••••••••••••••••••••••••••••••••••••••••"
value={apiKey} value={apiKey}
onChange={(e) => setApiKey(e.target.value)} onChange={(e) => setApiKey(e.target.value)}
disabled={true} disabled={true}
/> />
<p className="text-mmd text-muted-foreground"> <p className="text-mmd text-muted-foreground">
Reusing API key from model provider selection. Reusing API key from model provider selection.
</p> </p>
</div> </div>
)} )}
{getFromEnv && isLoadingModels && ( {getFromEnv && isLoadingModels && (
<p className="text-mmd text-muted-foreground"> <p className="text-mmd text-muted-foreground">
Validating configuration... Validating configuration...
</p> </p>
)} )}
{getFromEnv && modelsError && ( {getFromEnv && modelsError && (
<p className="text-mmd text-accent-amber-foreground"> <p className="text-mmd text-accent-amber-foreground">
Connection failed. Check your configuration. Connection failed. Check your configuration.
</p> </p>
)} )}
</div> </div>
<AdvancedOnboarding <AdvancedOnboarding
icon={<IBMLogo className="w-4 h-4" />} icon={<IBMLogo className="w-4 h-4" />}
languageModels={languageModels} languageModels={languageModels}
embeddingModels={embeddingModels} embeddingModels={embeddingModels}
languageModel={languageModel} languageModel={languageModel}
embeddingModel={embeddingModel} embeddingModel={embeddingModel}
sampleDataset={sampleDataset} sampleDataset={sampleDataset}
setLanguageModel={setLanguageModel} setLanguageModel={setLanguageModel}
setEmbeddingModel={setEmbeddingModel} setEmbeddingModel={setEmbeddingModel}
setSampleDataset={handleSampleDatasetChange} setSampleDataset={handleSampleDatasetChange}
/> />
</> </>
); );
} }