move folders from merge
This commit is contained in:
parent
16d49c5f2d
commit
21a9562780
8 changed files with 567 additions and 1215 deletions
|
|
@ -1,95 +1,97 @@
|
||||||
import { GitBranch } from "lucide-react";
|
import { GitBranch } from "lucide-react";
|
||||||
import { motion } from "motion/react";
|
import { motion } from "motion/react";
|
||||||
import DogIcon from "@/components/icons/dog-icon";
|
import DogIcon from "@/components/logo/dog-icon";
|
||||||
import { MarkdownRenderer } from "@/components/markdown-renderer";
|
import { MarkdownRenderer } from "@/components/markdown-renderer";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { FunctionCall } from "../_types/types";
|
import type { FunctionCall } from "../types";
|
||||||
import { FunctionCalls } from "./function-calls";
|
import { FunctionCalls } from "./function-calls";
|
||||||
import { Message } from "./message";
|
import { Message } from "./message";
|
||||||
|
|
||||||
interface AssistantMessageProps {
|
interface AssistantMessageProps {
|
||||||
content: string;
|
content: string;
|
||||||
functionCalls?: FunctionCall[];
|
functionCalls?: FunctionCall[];
|
||||||
messageIndex?: number;
|
messageIndex?: number;
|
||||||
expandedFunctionCalls: Set<string>;
|
expandedFunctionCalls: Set<string>;
|
||||||
onToggle: (functionCallId: string) => void;
|
onToggle: (functionCallId: string) => void;
|
||||||
isStreaming?: boolean;
|
isStreaming?: boolean;
|
||||||
showForkButton?: boolean;
|
showForkButton?: boolean;
|
||||||
onFork?: (e: React.MouseEvent) => void;
|
onFork?: (e: React.MouseEvent) => void;
|
||||||
isCompleted?: boolean;
|
isCompleted?: boolean;
|
||||||
isInactive?: boolean;
|
isInactive?: boolean;
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AssistantMessage({
|
export function AssistantMessage({
|
||||||
content,
|
content,
|
||||||
functionCalls = [],
|
functionCalls = [],
|
||||||
messageIndex,
|
messageIndex,
|
||||||
expandedFunctionCalls,
|
expandedFunctionCalls,
|
||||||
onToggle,
|
onToggle,
|
||||||
isStreaming = false,
|
isStreaming = false,
|
||||||
showForkButton = false,
|
showForkButton = false,
|
||||||
onFork,
|
onFork,
|
||||||
isCompleted = false,
|
isCompleted = false,
|
||||||
isInactive = false,
|
isInactive = false,
|
||||||
animate = true,
|
animate = true,
|
||||||
delay = 0.2,
|
delay = 0.2,
|
||||||
}: AssistantMessageProps) {
|
}: AssistantMessageProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={animate ? { opacity: 0, y: -20 } : { opacity: 1, y: 0 }}
|
initial={animate ? { opacity: 0, y: -20 } : { opacity: 1, y: 0 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={
|
transition={
|
||||||
animate
|
animate
|
||||||
? { duration: 0.4, delay: delay, ease: "easeOut" }
|
? { duration: 0.4, delay: delay, ease: "easeOut" }
|
||||||
: { duration: 0 }
|
: { duration: 0 }
|
||||||
}
|
}
|
||||||
className={isCompleted ? "opacity-50" : ""}
|
className={isCompleted ? "opacity-50" : ""}
|
||||||
>
|
>
|
||||||
<Message
|
<Message
|
||||||
icon={
|
icon={
|
||||||
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0 select-none">
|
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0 select-none">
|
||||||
<DogIcon
|
<DogIcon
|
||||||
className="h-6 w-6 transition-colors duration-300"
|
className="h-6 w-6 transition-colors duration-300"
|
||||||
disabled={isCompleted || isInactive}
|
disabled={isCompleted || isInactive}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
showForkButton && onFork ? (
|
showForkButton && onFork ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onFork}
|
onClick={onFork}
|
||||||
className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-accent rounded text-muted-foreground hover:text-foreground"
|
className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-accent rounded text-muted-foreground hover:text-foreground"
|
||||||
title="Fork conversation from here"
|
title="Fork conversation from here"
|
||||||
>
|
>
|
||||||
<GitBranch className="h-3 w-3" />
|
<GitBranch className="h-3 w-3" />
|
||||||
</button>
|
</button>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FunctionCalls
|
<FunctionCalls
|
||||||
functionCalls={functionCalls}
|
functionCalls={functionCalls}
|
||||||
messageIndex={messageIndex}
|
messageIndex={messageIndex}
|
||||||
expandedFunctionCalls={expandedFunctionCalls}
|
expandedFunctionCalls={expandedFunctionCalls}
|
||||||
onToggle={onToggle}
|
onToggle={onToggle}
|
||||||
/>
|
/>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<MarkdownRenderer
|
<MarkdownRenderer
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-sm py-1.5 transition-colors duration-300",
|
"text-sm py-1.5 transition-colors duration-300",
|
||||||
isCompleted ? "text-placeholder-foreground" : "text-foreground",
|
isCompleted ? "text-placeholder-foreground" : "text-foreground",
|
||||||
)}
|
)}
|
||||||
chatMessage={
|
chatMessage={
|
||||||
isStreaming
|
isStreaming
|
||||||
? content +
|
? (content.trim()
|
||||||
' <span class="inline-block w-1 h-4 bg-primary ml-1 animate-pulse"></span>'
|
? content +
|
||||||
: content
|
' <span class="inline-block w-1 h-4 bg-primary ml-1 animate-pulse"></span>'
|
||||||
}
|
: '<span class="text-muted-foreground italic">Thinking<span class="thinking-dots"></span></span>')
|
||||||
/>
|
: content
|
||||||
</div>
|
}
|
||||||
</Message>
|
/>
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
</Message>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
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 { useDebouncedValue } from "@/lib/debounce";
|
import { useDebouncedValue } from "@/lib/debounce";
|
||||||
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
|
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
|
||||||
import { useGetIBMModelsQuery } from "../../api/queries/useGetModelsQuery";
|
import { useGetIBMModelsQuery } from "../../api/queries/useGetModelsQuery";
|
||||||
|
|
@ -12,201 +12,199 @@ 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,
|
||||||
}: {
|
}: {
|
||||||
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;
|
||||||
}) {
|
}) {
|
||||||
const [endpoint, setEndpoint] = useState("https://us-south.ml.cloud.ibm.com");
|
const [endpoint, setEndpoint] = useState(alreadyConfigured ? "" : "https://us-south.ml.cloud.ibm.com");
|
||||||
const [apiKey, setApiKey] = useState("");
|
const [apiKey, setApiKey] = useState("");
|
||||||
const [projectId, setProjectId] = useState("");
|
const [projectId, setProjectId] = useState("");
|
||||||
|
|
||||||
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,
|
endpoint: debouncedEndpoint ? debouncedEndpoint : undefined,
|
||||||
apiKey: debouncedApiKey,
|
apiKey: debouncedApiKey ? debouncedApiKey : undefined,
|
||||||
projectId: debouncedProjectId,
|
projectId: debouncedProjectId ? debouncedProjectId : undefined,
|
||||||
},
|
},
|
||||||
{
|
{ enabled: !!debouncedEndpoint || !!debouncedApiKey || !!debouncedProjectId || alreadyConfigured },
|
||||||
enabled: !!debouncedEndpoint && !!debouncedApiKey && !!debouncedProjectId,
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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 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>
|
||||||
<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={
|
||||||
alreadyConfigured
|
alreadyConfigured
|
||||||
? "•••••••••••••••••••••••••••••••••••••••••"
|
? "•••••••••••••••••••••••••••••••••••••••••"
|
||||||
: "your-api-key"
|
: "your-api-key"
|
||||||
}
|
}
|
||||||
value={apiKey}
|
value={apiKey}
|
||||||
onChange={(e) => setApiKey(e.target.value)}
|
onChange={(e) => setApiKey(e.target.value)}
|
||||||
disabled={alreadyConfigured}
|
disabled={alreadyConfigured}
|
||||||
/>
|
/>
|
||||||
{alreadyConfigured && (
|
{alreadyConfigured && (
|
||||||
<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>
|
||||||
{isLoadingModels && (
|
{isLoadingModels && (
|
||||||
<p className="text-mmd text-muted-foreground">
|
<p className="text-mmd text-muted-foreground">
|
||||||
Validating configuration...
|
Validating configuration...
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{modelsError && (
|
{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}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import OllamaLogo from "@/components/icons/ollama-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 OllamaLogo from "@/components/icons/ollama-logo";
|
||||||
import { useDebouncedValue } from "@/lib/debounce";
|
import { useDebouncedValue } from "@/lib/debounce";
|
||||||
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
|
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
|
||||||
import { useGetOllamaModelsQuery } from "../../api/queries/useGetModelsQuery";
|
import { useGetOllamaModelsQuery } from "../../api/queries/useGetModelsQuery";
|
||||||
|
|
@ -11,163 +11,164 @@ import { useUpdateSettings } from "../_hooks/useUpdateSettings";
|
||||||
import { ModelSelector } from "./model-selector";
|
import { ModelSelector } from "./model-selector";
|
||||||
|
|
||||||
export function OllamaOnboarding({
|
export function OllamaOnboarding({
|
||||||
setSettings,
|
setSettings,
|
||||||
sampleDataset,
|
sampleDataset,
|
||||||
setSampleDataset,
|
setSampleDataset,
|
||||||
setIsLoadingModels,
|
setIsLoadingModels,
|
||||||
isEmbedding = false,
|
isEmbedding = false,
|
||||||
alreadyConfigured = false,
|
alreadyConfigured = false,
|
||||||
}: {
|
}: {
|
||||||
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;
|
||||||
isEmbedding?: boolean;
|
isEmbedding?: boolean;
|
||||||
alreadyConfigured?: boolean;
|
alreadyConfigured?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [endpoint, setEndpoint] = useState(`http://localhost:11434`);
|
const [endpoint, setEndpoint] = useState(alreadyConfigured ? undefined : `http://localhost:11434`);
|
||||||
const [showConnecting, setShowConnecting] = useState(false);
|
const [showConnecting, setShowConnecting] = useState(false);
|
||||||
const debouncedEndpoint = useDebouncedValue(endpoint, 500);
|
const debouncedEndpoint = useDebouncedValue(endpoint, 500);
|
||||||
|
|
||||||
// Fetch models from API when endpoint is provided (debounced)
|
// Fetch models from API when endpoint is provided (debounced)
|
||||||
const {
|
const {
|
||||||
data: modelsData,
|
data: modelsData,
|
||||||
isLoading: isLoadingModels,
|
isLoading: isLoadingModels,
|
||||||
error: modelsError,
|
error: modelsError,
|
||||||
} = useGetOllamaModelsQuery(
|
} = useGetOllamaModelsQuery(
|
||||||
debouncedEndpoint ? { endpoint: debouncedEndpoint } : undefined,
|
debouncedEndpoint ? { endpoint: debouncedEndpoint } : undefined,
|
||||||
);
|
{ enabled: !!debouncedEndpoint || 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);
|
||||||
|
|
||||||
// Handle delayed display of connecting state
|
// Handle delayed display of connecting state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timeoutId: NodeJS.Timeout;
|
let timeoutId: NodeJS.Timeout;
|
||||||
|
|
||||||
if (debouncedEndpoint && isLoadingModels) {
|
if (debouncedEndpoint && isLoadingModels) {
|
||||||
timeoutId = setTimeout(() => {
|
timeoutId = setTimeout(() => {
|
||||||
setIsLoadingModels?.(true);
|
setIsLoadingModels?.(true);
|
||||||
setShowConnecting(true);
|
setShowConnecting(true);
|
||||||
}, 500);
|
}, 500);
|
||||||
} else {
|
} else {
|
||||||
setShowConnecting(false);
|
setShowConnecting(false);
|
||||||
setIsLoadingModels?.(false);
|
setIsLoadingModels?.(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (timeoutId) {
|
if (timeoutId) {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [debouncedEndpoint, isLoadingModels, setIsLoadingModels]);
|
}, [debouncedEndpoint, isLoadingModels, setIsLoadingModels]);
|
||||||
|
|
||||||
// Update settings when values change
|
// Update settings when values change
|
||||||
useUpdateSettings(
|
useUpdateSettings(
|
||||||
"ollama",
|
"ollama",
|
||||||
{
|
{
|
||||||
endpoint,
|
endpoint,
|
||||||
languageModel,
|
languageModel,
|
||||||
embeddingModel,
|
embeddingModel,
|
||||||
},
|
},
|
||||||
setSettings,
|
setSettings,
|
||||||
isEmbedding,
|
isEmbedding,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check validation state based on models query
|
// Check validation state based on models query
|
||||||
const hasConnectionError = debouncedEndpoint && modelsError;
|
const hasConnectionError = debouncedEndpoint && modelsError;
|
||||||
const hasNoModels =
|
const hasNoModels =
|
||||||
modelsData &&
|
modelsData &&
|
||||||
!modelsData.language_models?.length &&
|
!modelsData.language_models?.length &&
|
||||||
!modelsData.embedding_models?.length;
|
!modelsData.embedding_models?.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<LabelInput
|
<LabelInput
|
||||||
label="Ollama Base URL"
|
label="Ollama Base URL"
|
||||||
helperText="Base URL of your Ollama server"
|
helperText="Base URL of your Ollama server"
|
||||||
id="api-endpoint"
|
id="api-endpoint"
|
||||||
required
|
required
|
||||||
placeholder={
|
placeholder={
|
||||||
alreadyConfigured
|
alreadyConfigured
|
||||||
? "http://••••••••••••••••••••"
|
? "http://••••••••••••••••••••"
|
||||||
: "http://localhost:11434"
|
: "http://localhost:11434"
|
||||||
}
|
}
|
||||||
value={endpoint}
|
value={endpoint}
|
||||||
onChange={(e) => setEndpoint(e.target.value)}
|
onChange={(e) => setEndpoint(e.target.value)}
|
||||||
disabled={alreadyConfigured}
|
disabled={alreadyConfigured}
|
||||||
/>
|
/>
|
||||||
{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>
|
||||||
)}
|
)}
|
||||||
{showConnecting && (
|
{showConnecting && (
|
||||||
<p className="text-mmd text-muted-foreground">
|
<p className="text-mmd text-muted-foreground">
|
||||||
Connecting to Ollama server...
|
Connecting to Ollama server...
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{hasConnectionError && (
|
{hasConnectionError && (
|
||||||
<p className="text-mmd text-accent-amber-foreground">
|
<p className="text-mmd text-accent-amber-foreground">
|
||||||
Can't reach Ollama at {debouncedEndpoint}. Update the base URL or
|
Can't reach Ollama at {debouncedEndpoint}. Update the base URL or
|
||||||
start the server.
|
start the server.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{hasNoModels && (
|
{hasNoModels && (
|
||||||
<p className="text-mmd text-accent-amber-foreground">
|
<p className="text-mmd text-accent-amber-foreground">
|
||||||
No models found. Install embedding and agent models on your Ollama
|
No models found. Install embedding and agent models on your Ollama
|
||||||
server.
|
server.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isEmbedding && setEmbeddingModel && (
|
{isEmbedding && setEmbeddingModel && (
|
||||||
<LabelWrapper
|
<LabelWrapper
|
||||||
label="Embedding model"
|
label="Embedding model"
|
||||||
helperText="Model used for knowledge ingest and retrieval"
|
helperText="Model used for knowledge ingest and retrieval"
|
||||||
id="embedding-model"
|
id="embedding-model"
|
||||||
required={true}
|
required={true}
|
||||||
>
|
>
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
options={embeddingModels}
|
options={embeddingModels}
|
||||||
icon={<OllamaLogo className="w-4 h-4" />}
|
icon={<OllamaLogo className="w-4 h-4" />}
|
||||||
noOptionsPlaceholder={
|
noOptionsPlaceholder={
|
||||||
isLoadingModels
|
isLoadingModels
|
||||||
? "Loading models..."
|
? "Loading models..."
|
||||||
: "No embedding models detected. Install an embedding model to continue."
|
: "No embedding models detected. Install an embedding model to continue."
|
||||||
}
|
}
|
||||||
value={embeddingModel}
|
value={embeddingModel}
|
||||||
onValueChange={setEmbeddingModel}
|
onValueChange={setEmbeddingModel}
|
||||||
/>
|
/>
|
||||||
</LabelWrapper>
|
</LabelWrapper>
|
||||||
)}
|
)}
|
||||||
{!isEmbedding && setLanguageModel && (
|
{!isEmbedding && setLanguageModel && (
|
||||||
<LabelWrapper
|
<LabelWrapper
|
||||||
label="Language model"
|
label="Language model"
|
||||||
helperText="Model used for chat"
|
helperText="Model used for chat"
|
||||||
id="embedding-model"
|
id="embedding-model"
|
||||||
required={true}
|
required={true}
|
||||||
>
|
>
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
options={languageModels}
|
options={languageModels}
|
||||||
icon={<OllamaLogo className="w-4 h-4" />}
|
icon={<OllamaLogo className="w-4 h-4" />}
|
||||||
noOptionsPlaceholder={
|
noOptionsPlaceholder={
|
||||||
isLoadingModels
|
isLoadingModels
|
||||||
? "Loading models..."
|
? "Loading models..."
|
||||||
: "No language models detected. Install a language model to continue."
|
: "No language models detected. Install a language model to continue."
|
||||||
}
|
}
|
||||||
value={languageModel}
|
value={languageModel}
|
||||||
onValueChange={setLanguageModel}
|
onValueChange={setLanguageModel}
|
||||||
/>
|
/>
|
||||||
</LabelWrapper>
|
</LabelWrapper>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 OpenAILogo from "@/components/icons/openai-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 OpenAILogo from "@/components/icons/openai-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";
|
||||||
|
|
@ -17,152 +17,152 @@ import { useUpdateSettings } from "../_hooks/useUpdateSettings";
|
||||||
import { AdvancedOnboarding } from "./advanced";
|
import { AdvancedOnboarding } from "./advanced";
|
||||||
|
|
||||||
export function OpenAIOnboarding({
|
export function OpenAIOnboarding({
|
||||||
setSettings,
|
setSettings,
|
||||||
sampleDataset,
|
sampleDataset,
|
||||||
setSampleDataset,
|
setSampleDataset,
|
||||||
setIsLoadingModels,
|
setIsLoadingModels,
|
||||||
isEmbedding = false,
|
isEmbedding = false,
|
||||||
hasEnvApiKey = false,
|
hasEnvApiKey = false,
|
||||||
alreadyConfigured = false,
|
alreadyConfigured = false,
|
||||||
}: {
|
}: {
|
||||||
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;
|
||||||
isEmbedding?: boolean;
|
isEmbedding?: boolean;
|
||||||
hasEnvApiKey?: boolean;
|
hasEnvApiKey?: boolean;
|
||||||
alreadyConfigured?: boolean;
|
alreadyConfigured?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [apiKey, setApiKey] = useState("");
|
const [apiKey, setApiKey] = useState("");
|
||||||
const [getFromEnv, setGetFromEnv] = useState(hasEnvApiKey);
|
const [getFromEnv, setGetFromEnv] = useState(hasEnvApiKey && !alreadyConfigured);
|
||||||
const debouncedApiKey = useDebouncedValue(apiKey, 500);
|
const debouncedApiKey = useDebouncedValue(apiKey, 500);
|
||||||
|
|
||||||
// Fetch models from API when API key is provided
|
// Fetch models from API when API key is provided
|
||||||
const {
|
const {
|
||||||
data: modelsData,
|
data: modelsData,
|
||||||
isLoading: isLoadingModels,
|
isLoading: isLoadingModels,
|
||||||
error: modelsError,
|
error: modelsError,
|
||||||
} = useGetOpenAIModelsQuery(
|
} = useGetOpenAIModelsQuery(
|
||||||
getFromEnv
|
getFromEnv
|
||||||
? { apiKey: "" }
|
? { apiKey: "" }
|
||||||
: debouncedApiKey
|
: debouncedApiKey
|
||||||
? { apiKey: debouncedApiKey }
|
? { apiKey: debouncedApiKey }
|
||||||
: undefined,
|
: undefined,
|
||||||
{ enabled: debouncedApiKey !== "" || getFromEnv },
|
{ enabled: debouncedApiKey !== "" || 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 handleSampleDatasetChange = (dataset: boolean) => {
|
const handleSampleDatasetChange = (dataset: boolean) => {
|
||||||
setSampleDataset(dataset);
|
setSampleDataset(dataset);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGetFromEnvChange = (fromEnv: boolean) => {
|
const handleGetFromEnvChange = (fromEnv: boolean) => {
|
||||||
setGetFromEnv(fromEnv);
|
setGetFromEnv(fromEnv);
|
||||||
if (fromEnv) {
|
if (fromEnv) {
|
||||||
setApiKey("");
|
setApiKey("");
|
||||||
}
|
}
|
||||||
setEmbeddingModel?.("");
|
setEmbeddingModel?.("");
|
||||||
setLanguageModel?.("");
|
setLanguageModel?.("");
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoadingModels?.(isLoadingModels);
|
setIsLoadingModels?.(isLoadingModels);
|
||||||
}, [isLoadingModels, setIsLoadingModels]);
|
}, [isLoadingModels, setIsLoadingModels]);
|
||||||
|
|
||||||
// Update settings when values change
|
// Update settings when values change
|
||||||
useUpdateSettings(
|
useUpdateSettings(
|
||||||
"openai",
|
"openai",
|
||||||
{
|
{
|
||||||
apiKey,
|
apiKey,
|
||||||
languageModel,
|
languageModel,
|
||||||
embeddingModel,
|
embeddingModel,
|
||||||
},
|
},
|
||||||
setSettings,
|
setSettings,
|
||||||
isEmbedding,
|
isEmbedding,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
{!alreadyConfigured && (
|
{!alreadyConfigured && (
|
||||||
<LabelWrapper
|
<LabelWrapper
|
||||||
label="Use environment OpenAI API key"
|
label="Use environment OpenAI 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}
|
disabled={!hasEnvApiKey}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
{!hasEnvApiKey && (
|
{!hasEnvApiKey && (
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
OpenAI API key not detected in the environment.
|
OpenAI 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="OpenAI API key"
|
label="OpenAI API key"
|
||||||
helperText="The API key for your OpenAI account."
|
helperText="The API key for your OpenAI account."
|
||||||
className={modelsError ? "!border-destructive" : ""}
|
className={modelsError ? "!border-destructive" : ""}
|
||||||
id="api-key"
|
id="api-key"
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
placeholder={
|
placeholder={
|
||||||
alreadyConfigured
|
alreadyConfigured
|
||||||
? "sk-•••••••••••••••••••••••••••••••••••••••••"
|
? "sk-•••••••••••••••••••••••••••••••••••••••••"
|
||||||
: "sk-..."
|
: "sk-..."
|
||||||
}
|
}
|
||||||
value={apiKey}
|
value={apiKey}
|
||||||
onChange={(e) => setApiKey(e.target.value)}
|
onChange={(e) => setApiKey(e.target.value)}
|
||||||
disabled={alreadyConfigured}
|
disabled={alreadyConfigured}
|
||||||
/>
|
/>
|
||||||
{alreadyConfigured && (
|
{alreadyConfigured && (
|
||||||
<p className="text-mmd text-muted-foreground">
|
<p className="text-mmd text-muted-foreground">
|
||||||
Reusing key from model provider selection.
|
Reusing key from model provider selection.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{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 OpenAI API key. Verify or replace the key.
|
Invalid OpenAI API key. Verify or replace the key.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<AdvancedOnboarding
|
<AdvancedOnboarding
|
||||||
icon={<OpenAILogo className="w-4 h-4" />}
|
icon={<OpenAILogo 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}
|
||||||
setSampleDataset={handleSampleDatasetChange}
|
setSampleDataset={handleSampleDatasetChange}
|
||||||
setEmbeddingModel={setEmbeddingModel}
|
setEmbeddingModel={setEmbeddingModel}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
import { GitBranch } from "lucide-react";
|
|
||||||
import { motion } from "motion/react";
|
|
||||||
import DogIcon from "@/components/logo/dog-icon";
|
|
||||||
import { MarkdownRenderer } from "@/components/markdown-renderer";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import type { FunctionCall } from "../types";
|
|
||||||
import { FunctionCalls } from "./function-calls";
|
|
||||||
import { Message } from "./message";
|
|
||||||
|
|
||||||
interface AssistantMessageProps {
|
|
||||||
content: string;
|
|
||||||
functionCalls?: FunctionCall[];
|
|
||||||
messageIndex?: number;
|
|
||||||
expandedFunctionCalls: Set<string>;
|
|
||||||
onToggle: (functionCallId: string) => void;
|
|
||||||
isStreaming?: boolean;
|
|
||||||
showForkButton?: boolean;
|
|
||||||
onFork?: (e: React.MouseEvent) => void;
|
|
||||||
isCompleted?: boolean;
|
|
||||||
isInactive?: boolean;
|
|
||||||
animate?: boolean;
|
|
||||||
delay?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AssistantMessage({
|
|
||||||
content,
|
|
||||||
functionCalls = [],
|
|
||||||
messageIndex,
|
|
||||||
expandedFunctionCalls,
|
|
||||||
onToggle,
|
|
||||||
isStreaming = false,
|
|
||||||
showForkButton = false,
|
|
||||||
onFork,
|
|
||||||
isCompleted = false,
|
|
||||||
isInactive = false,
|
|
||||||
animate = true,
|
|
||||||
delay = 0.2,
|
|
||||||
}: AssistantMessageProps) {
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
initial={animate ? { opacity: 0, y: -20 } : { opacity: 1, y: 0 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={
|
|
||||||
animate
|
|
||||||
? { duration: 0.4, delay: delay, ease: "easeOut" }
|
|
||||||
: { duration: 0 }
|
|
||||||
}
|
|
||||||
className={isCompleted ? "opacity-50" : ""}
|
|
||||||
>
|
|
||||||
<Message
|
|
||||||
icon={
|
|
||||||
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0 select-none">
|
|
||||||
<DogIcon
|
|
||||||
className="h-6 w-6 transition-colors duration-300"
|
|
||||||
disabled={isCompleted || isInactive}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
actions={
|
|
||||||
showForkButton && onFork ? (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onFork}
|
|
||||||
className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-accent rounded text-muted-foreground hover:text-foreground"
|
|
||||||
title="Fork conversation from here"
|
|
||||||
>
|
|
||||||
<GitBranch className="h-3 w-3" />
|
|
||||||
</button>
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FunctionCalls
|
|
||||||
functionCalls={functionCalls}
|
|
||||||
messageIndex={messageIndex}
|
|
||||||
expandedFunctionCalls={expandedFunctionCalls}
|
|
||||||
onToggle={onToggle}
|
|
||||||
/>
|
|
||||||
<div className="relative">
|
|
||||||
<MarkdownRenderer
|
|
||||||
className={cn(
|
|
||||||
"text-sm py-1.5 transition-colors duration-300",
|
|
||||||
isCompleted ? "text-placeholder-foreground" : "text-foreground",
|
|
||||||
)}
|
|
||||||
chatMessage={
|
|
||||||
isStreaming
|
|
||||||
? (content.trim()
|
|
||||||
? content +
|
|
||||||
' <span class="inline-block w-1 h-4 bg-primary ml-1 animate-pulse"></span>'
|
|
||||||
: '<span class="text-muted-foreground italic">Thinking<span class="thinking-dots"></span></span>')
|
|
||||||
: content
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Message>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { LabelInput } from "@/components/label-input";
|
|
||||||
import { LabelWrapper } from "@/components/label-wrapper";
|
|
||||||
import IBMLogo from "@/components/logo/ibm-logo";
|
|
||||||
import { useDebouncedValue } from "@/lib/debounce";
|
|
||||||
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
|
|
||||||
import { useGetIBMModelsQuery } from "../../api/queries/useGetModelsQuery";
|
|
||||||
import { useModelSelection } from "../hooks/useModelSelection";
|
|
||||||
import { useUpdateSettings } from "../hooks/useUpdateSettings";
|
|
||||||
import { AdvancedOnboarding } from "./advanced";
|
|
||||||
import { ModelSelector } from "./model-selector";
|
|
||||||
|
|
||||||
export function IBMOnboarding({
|
|
||||||
isEmbedding = false,
|
|
||||||
setSettings,
|
|
||||||
sampleDataset,
|
|
||||||
setSampleDataset,
|
|
||||||
setIsLoadingModels,
|
|
||||||
alreadyConfigured = false,
|
|
||||||
}: {
|
|
||||||
isEmbedding?: boolean;
|
|
||||||
setSettings: Dispatch<SetStateAction<OnboardingVariables>>;
|
|
||||||
sampleDataset: boolean;
|
|
||||||
setSampleDataset: (dataset: boolean) => void;
|
|
||||||
setIsLoadingModels?: (isLoading: boolean) => void;
|
|
||||||
alreadyConfigured?: boolean;
|
|
||||||
}) {
|
|
||||||
const [endpoint, setEndpoint] = useState(alreadyConfigured ? "" : "https://us-south.ml.cloud.ibm.com");
|
|
||||||
const [apiKey, setApiKey] = useState("");
|
|
||||||
const [projectId, setProjectId] = useState("");
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
value: "https://us-south.ml.cloud.ibm.com",
|
|
||||||
label: "https://us-south.ml.cloud.ibm.com",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "https://eu-de.ml.cloud.ibm.com",
|
|
||||||
label: "https://eu-de.ml.cloud.ibm.com",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "https://eu-gb.ml.cloud.ibm.com",
|
|
||||||
label: "https://eu-gb.ml.cloud.ibm.com",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "https://au-syd.ml.cloud.ibm.com",
|
|
||||||
label: "https://au-syd.ml.cloud.ibm.com",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "https://jp-tok.ml.cloud.ibm.com",
|
|
||||||
label: "https://jp-tok.ml.cloud.ibm.com",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "https://ca-tor.ml.cloud.ibm.com",
|
|
||||||
label: "https://ca-tor.ml.cloud.ibm.com",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const debouncedEndpoint = useDebouncedValue(endpoint, 500);
|
|
||||||
const debouncedApiKey = useDebouncedValue(apiKey, 500);
|
|
||||||
const debouncedProjectId = useDebouncedValue(projectId, 500);
|
|
||||||
|
|
||||||
// Fetch models from API when all credentials are provided
|
|
||||||
const {
|
|
||||||
data: modelsData,
|
|
||||||
isLoading: isLoadingModels,
|
|
||||||
error: modelsError,
|
|
||||||
} = useGetIBMModelsQuery(
|
|
||||||
{
|
|
||||||
endpoint: debouncedEndpoint ? debouncedEndpoint : undefined,
|
|
||||||
apiKey: debouncedApiKey ? debouncedApiKey : undefined,
|
|
||||||
projectId: debouncedProjectId ? debouncedProjectId : undefined,
|
|
||||||
},
|
|
||||||
{ enabled: !!debouncedEndpoint || !!debouncedApiKey || !!debouncedProjectId || alreadyConfigured },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use custom hook for model selection logic
|
|
||||||
const {
|
|
||||||
languageModel,
|
|
||||||
embeddingModel,
|
|
||||||
setLanguageModel,
|
|
||||||
setEmbeddingModel,
|
|
||||||
languageModels,
|
|
||||||
embeddingModels,
|
|
||||||
} = useModelSelection(modelsData, isEmbedding);
|
|
||||||
const handleSampleDatasetChange = (dataset: boolean) => {
|
|
||||||
setSampleDataset(dataset);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsLoadingModels?.(isLoadingModels);
|
|
||||||
}, [isLoadingModels, setIsLoadingModels]);
|
|
||||||
|
|
||||||
// Update settings when values change
|
|
||||||
useUpdateSettings(
|
|
||||||
"watsonx",
|
|
||||||
{
|
|
||||||
endpoint,
|
|
||||||
apiKey,
|
|
||||||
projectId,
|
|
||||||
languageModel,
|
|
||||||
embeddingModel,
|
|
||||||
},
|
|
||||||
setSettings,
|
|
||||||
isEmbedding,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<LabelWrapper
|
|
||||||
label="watsonx.ai API Endpoint"
|
|
||||||
helperText="Base URL of the API"
|
|
||||||
id="api-endpoint"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<ModelSelector
|
|
||||||
options={alreadyConfigured ? [] : options}
|
|
||||||
value={endpoint}
|
|
||||||
custom
|
|
||||||
onValueChange={alreadyConfigured ? () => {} : setEndpoint}
|
|
||||||
searchPlaceholder="Search endpoint..."
|
|
||||||
noOptionsPlaceholder={
|
|
||||||
alreadyConfigured
|
|
||||||
? "https://•••••••••••••••••••••••••••••••••••••••••"
|
|
||||||
: "No endpoints available"
|
|
||||||
}
|
|
||||||
placeholder="Select endpoint..."
|
|
||||||
/>
|
|
||||||
{alreadyConfigured && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Reusing endpoint from model provider selection.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</LabelWrapper>
|
|
||||||
|
|
||||||
<div className="space-y-1">
|
|
||||||
<LabelInput
|
|
||||||
label="watsonx Project ID"
|
|
||||||
helperText="Project ID for the model"
|
|
||||||
id="project-id"
|
|
||||||
required
|
|
||||||
placeholder={
|
|
||||||
alreadyConfigured ? "••••••••••••••••••••••••" : "your-project-id"
|
|
||||||
}
|
|
||||||
value={projectId}
|
|
||||||
onChange={(e) => setProjectId(e.target.value)}
|
|
||||||
disabled={alreadyConfigured}
|
|
||||||
/>
|
|
||||||
{alreadyConfigured && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Reusing project ID from model provider selection.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<LabelInput
|
|
||||||
label="watsonx API key"
|
|
||||||
helperText="API key to access watsonx.ai"
|
|
||||||
id="api-key"
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
placeholder={
|
|
||||||
alreadyConfigured
|
|
||||||
? "•••••••••••••••••••••••••••••••••••••••••"
|
|
||||||
: "your-api-key"
|
|
||||||
}
|
|
||||||
value={apiKey}
|
|
||||||
onChange={(e) => setApiKey(e.target.value)}
|
|
||||||
disabled={alreadyConfigured}
|
|
||||||
/>
|
|
||||||
{alreadyConfigured && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Reusing API key from model provider selection.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{isLoadingModels && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Validating configuration...
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{modelsError && (
|
|
||||||
<p className="text-mmd text-accent-amber-foreground">
|
|
||||||
Connection failed. Check your configuration.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<AdvancedOnboarding
|
|
||||||
icon={<IBMLogo className="w-4 h-4" />}
|
|
||||||
languageModels={languageModels}
|
|
||||||
embeddingModels={embeddingModels}
|
|
||||||
languageModel={languageModel}
|
|
||||||
embeddingModel={embeddingModel}
|
|
||||||
sampleDataset={sampleDataset}
|
|
||||||
setLanguageModel={setLanguageModel}
|
|
||||||
setEmbeddingModel={setEmbeddingModel}
|
|
||||||
setSampleDataset={handleSampleDatasetChange}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { LabelInput } from "@/components/label-input";
|
|
||||||
import { LabelWrapper } from "@/components/label-wrapper";
|
|
||||||
import OllamaLogo from "@/components/logo/ollama-logo";
|
|
||||||
import { useDebouncedValue } from "@/lib/debounce";
|
|
||||||
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
|
|
||||||
import { useGetOllamaModelsQuery } from "../../api/queries/useGetModelsQuery";
|
|
||||||
import { useModelSelection } from "../hooks/useModelSelection";
|
|
||||||
import { useUpdateSettings } from "../hooks/useUpdateSettings";
|
|
||||||
import { ModelSelector } from "./model-selector";
|
|
||||||
|
|
||||||
export function OllamaOnboarding({
|
|
||||||
setSettings,
|
|
||||||
sampleDataset,
|
|
||||||
setSampleDataset,
|
|
||||||
setIsLoadingModels,
|
|
||||||
isEmbedding = false,
|
|
||||||
alreadyConfigured = false,
|
|
||||||
}: {
|
|
||||||
setSettings: Dispatch<SetStateAction<OnboardingVariables>>;
|
|
||||||
sampleDataset: boolean;
|
|
||||||
setSampleDataset: (dataset: boolean) => void;
|
|
||||||
setIsLoadingModels?: (isLoading: boolean) => void;
|
|
||||||
isEmbedding?: boolean;
|
|
||||||
alreadyConfigured?: boolean;
|
|
||||||
}) {
|
|
||||||
const [endpoint, setEndpoint] = useState(alreadyConfigured ? undefined : `http://localhost:11434`);
|
|
||||||
const [showConnecting, setShowConnecting] = useState(false);
|
|
||||||
const debouncedEndpoint = useDebouncedValue(endpoint, 500);
|
|
||||||
|
|
||||||
// Fetch models from API when endpoint is provided (debounced)
|
|
||||||
const {
|
|
||||||
data: modelsData,
|
|
||||||
isLoading: isLoadingModels,
|
|
||||||
error: modelsError,
|
|
||||||
} = useGetOllamaModelsQuery(
|
|
||||||
debouncedEndpoint ? { endpoint: debouncedEndpoint } : undefined,
|
|
||||||
{ enabled: !!debouncedEndpoint || alreadyConfigured },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use custom hook for model selection logic
|
|
||||||
const {
|
|
||||||
languageModel,
|
|
||||||
embeddingModel,
|
|
||||||
setLanguageModel,
|
|
||||||
setEmbeddingModel,
|
|
||||||
languageModels,
|
|
||||||
embeddingModels,
|
|
||||||
} = useModelSelection(modelsData, isEmbedding);
|
|
||||||
|
|
||||||
// Handle delayed display of connecting state
|
|
||||||
useEffect(() => {
|
|
||||||
let timeoutId: NodeJS.Timeout;
|
|
||||||
|
|
||||||
if (debouncedEndpoint && isLoadingModels) {
|
|
||||||
timeoutId = setTimeout(() => {
|
|
||||||
setIsLoadingModels?.(true);
|
|
||||||
setShowConnecting(true);
|
|
||||||
}, 500);
|
|
||||||
} else {
|
|
||||||
setShowConnecting(false);
|
|
||||||
setIsLoadingModels?.(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (timeoutId) {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [debouncedEndpoint, isLoadingModels, setIsLoadingModels]);
|
|
||||||
|
|
||||||
// Update settings when values change
|
|
||||||
useUpdateSettings(
|
|
||||||
"ollama",
|
|
||||||
{
|
|
||||||
endpoint,
|
|
||||||
languageModel,
|
|
||||||
embeddingModel,
|
|
||||||
},
|
|
||||||
setSettings,
|
|
||||||
isEmbedding,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check validation state based on models query
|
|
||||||
const hasConnectionError = debouncedEndpoint && modelsError;
|
|
||||||
const hasNoModels =
|
|
||||||
modelsData &&
|
|
||||||
!modelsData.language_models?.length &&
|
|
||||||
!modelsData.embedding_models?.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-1">
|
|
||||||
<LabelInput
|
|
||||||
label="Ollama Base URL"
|
|
||||||
helperText="Base URL of your Ollama server"
|
|
||||||
id="api-endpoint"
|
|
||||||
required
|
|
||||||
placeholder={
|
|
||||||
alreadyConfigured
|
|
||||||
? "http://••••••••••••••••••••"
|
|
||||||
: "http://localhost:11434"
|
|
||||||
}
|
|
||||||
value={endpoint}
|
|
||||||
onChange={(e) => setEndpoint(e.target.value)}
|
|
||||||
disabled={alreadyConfigured}
|
|
||||||
/>
|
|
||||||
{alreadyConfigured && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Reusing endpoint from model provider selection.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{showConnecting && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Connecting to Ollama server...
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{hasConnectionError && (
|
|
||||||
<p className="text-mmd text-accent-amber-foreground">
|
|
||||||
Can't reach Ollama at {debouncedEndpoint}. Update the base URL or
|
|
||||||
start the server.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{hasNoModels && (
|
|
||||||
<p className="text-mmd text-accent-amber-foreground">
|
|
||||||
No models found. Install embedding and agent models on your Ollama
|
|
||||||
server.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{isEmbedding && setEmbeddingModel && (
|
|
||||||
<LabelWrapper
|
|
||||||
label="Embedding model"
|
|
||||||
helperText="Model used for knowledge ingest and retrieval"
|
|
||||||
id="embedding-model"
|
|
||||||
required={true}
|
|
||||||
>
|
|
||||||
<ModelSelector
|
|
||||||
options={embeddingModels}
|
|
||||||
icon={<OllamaLogo className="w-4 h-4" />}
|
|
||||||
noOptionsPlaceholder={
|
|
||||||
isLoadingModels
|
|
||||||
? "Loading models..."
|
|
||||||
: "No embedding models detected. Install an embedding model to continue."
|
|
||||||
}
|
|
||||||
value={embeddingModel}
|
|
||||||
onValueChange={setEmbeddingModel}
|
|
||||||
/>
|
|
||||||
</LabelWrapper>
|
|
||||||
)}
|
|
||||||
{!isEmbedding && setLanguageModel && (
|
|
||||||
<LabelWrapper
|
|
||||||
label="Language model"
|
|
||||||
helperText="Model used for chat"
|
|
||||||
id="embedding-model"
|
|
||||||
required={true}
|
|
||||||
>
|
|
||||||
<ModelSelector
|
|
||||||
options={languageModels}
|
|
||||||
icon={<OllamaLogo className="w-4 h-4" />}
|
|
||||||
noOptionsPlaceholder={
|
|
||||||
isLoadingModels
|
|
||||||
? "Loading models..."
|
|
||||||
: "No language models detected. Install a language model to continue."
|
|
||||||
}
|
|
||||||
value={languageModel}
|
|
||||||
onValueChange={setLanguageModel}
|
|
||||||
/>
|
|
||||||
</LabelWrapper>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { LabelInput } from "@/components/label-input";
|
|
||||||
import { LabelWrapper } from "@/components/label-wrapper";
|
|
||||||
import OpenAILogo from "@/components/logo/openai-logo";
|
|
||||||
import { Switch } from "@/components/ui/switch";
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import { useDebouncedValue } from "@/lib/debounce";
|
|
||||||
import type { OnboardingVariables } from "../../api/mutations/useOnboardingMutation";
|
|
||||||
import { useGetOpenAIModelsQuery } from "../../api/queries/useGetModelsQuery";
|
|
||||||
import { useModelSelection } from "../hooks/useModelSelection";
|
|
||||||
import { useUpdateSettings } from "../hooks/useUpdateSettings";
|
|
||||||
import { AdvancedOnboarding } from "./advanced";
|
|
||||||
|
|
||||||
export function OpenAIOnboarding({
|
|
||||||
setSettings,
|
|
||||||
sampleDataset,
|
|
||||||
setSampleDataset,
|
|
||||||
setIsLoadingModels,
|
|
||||||
isEmbedding = false,
|
|
||||||
hasEnvApiKey = false,
|
|
||||||
alreadyConfigured = false,
|
|
||||||
}: {
|
|
||||||
setSettings: Dispatch<SetStateAction<OnboardingVariables>>;
|
|
||||||
sampleDataset: boolean;
|
|
||||||
setSampleDataset: (dataset: boolean) => void;
|
|
||||||
setIsLoadingModels?: (isLoading: boolean) => void;
|
|
||||||
isEmbedding?: boolean;
|
|
||||||
hasEnvApiKey?: boolean;
|
|
||||||
alreadyConfigured?: boolean;
|
|
||||||
}) {
|
|
||||||
const [apiKey, setApiKey] = useState("");
|
|
||||||
const [getFromEnv, setGetFromEnv] = useState(hasEnvApiKey && !alreadyConfigured);
|
|
||||||
const debouncedApiKey = useDebouncedValue(apiKey, 500);
|
|
||||||
|
|
||||||
// Fetch models from API when API key is provided
|
|
||||||
const {
|
|
||||||
data: modelsData,
|
|
||||||
isLoading: isLoadingModels,
|
|
||||||
error: modelsError,
|
|
||||||
} = useGetOpenAIModelsQuery(
|
|
||||||
getFromEnv
|
|
||||||
? { apiKey: "" }
|
|
||||||
: debouncedApiKey
|
|
||||||
? { apiKey: debouncedApiKey }
|
|
||||||
: undefined,
|
|
||||||
{ enabled: debouncedApiKey !== "" || getFromEnv || alreadyConfigured },
|
|
||||||
);
|
|
||||||
// Use custom hook for model selection logic
|
|
||||||
const {
|
|
||||||
languageModel,
|
|
||||||
embeddingModel,
|
|
||||||
setLanguageModel,
|
|
||||||
setEmbeddingModel,
|
|
||||||
languageModels,
|
|
||||||
embeddingModels,
|
|
||||||
} = useModelSelection(modelsData, isEmbedding);
|
|
||||||
const handleSampleDatasetChange = (dataset: boolean) => {
|
|
||||||
setSampleDataset(dataset);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleGetFromEnvChange = (fromEnv: boolean) => {
|
|
||||||
setGetFromEnv(fromEnv);
|
|
||||||
if (fromEnv) {
|
|
||||||
setApiKey("");
|
|
||||||
}
|
|
||||||
setEmbeddingModel?.("");
|
|
||||||
setLanguageModel?.("");
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsLoadingModels?.(isLoadingModels);
|
|
||||||
}, [isLoadingModels, setIsLoadingModels]);
|
|
||||||
|
|
||||||
// Update settings when values change
|
|
||||||
useUpdateSettings(
|
|
||||||
"openai",
|
|
||||||
{
|
|
||||||
apiKey,
|
|
||||||
languageModel,
|
|
||||||
embeddingModel,
|
|
||||||
},
|
|
||||||
setSettings,
|
|
||||||
isEmbedding,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="space-y-5">
|
|
||||||
{!alreadyConfigured && (
|
|
||||||
<LabelWrapper
|
|
||||||
label="Use environment OpenAI API key"
|
|
||||||
id="get-api-key"
|
|
||||||
description="Reuse the key from your environment config. Turn off to enter a different key."
|
|
||||||
flex
|
|
||||||
>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<div>
|
|
||||||
<Switch
|
|
||||||
checked={getFromEnv}
|
|
||||||
onCheckedChange={handleGetFromEnvChange}
|
|
||||||
disabled={!hasEnvApiKey}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</TooltipTrigger>
|
|
||||||
{!hasEnvApiKey && (
|
|
||||||
<TooltipContent>
|
|
||||||
OpenAI API key not detected in the environment.
|
|
||||||
</TooltipContent>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
</LabelWrapper>
|
|
||||||
)}
|
|
||||||
{(!getFromEnv || alreadyConfigured) && (
|
|
||||||
<div className="space-y-1">
|
|
||||||
<LabelInput
|
|
||||||
label="OpenAI API key"
|
|
||||||
helperText="The API key for your OpenAI account."
|
|
||||||
className={modelsError ? "!border-destructive" : ""}
|
|
||||||
id="api-key"
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
placeholder={
|
|
||||||
alreadyConfigured
|
|
||||||
? "sk-•••••••••••••••••••••••••••••••••••••••••"
|
|
||||||
: "sk-..."
|
|
||||||
}
|
|
||||||
value={apiKey}
|
|
||||||
onChange={(e) => setApiKey(e.target.value)}
|
|
||||||
disabled={alreadyConfigured}
|
|
||||||
/>
|
|
||||||
{alreadyConfigured && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Reusing key from model provider selection.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{isLoadingModels && (
|
|
||||||
<p className="text-mmd text-muted-foreground">
|
|
||||||
Validating API key...
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{modelsError && (
|
|
||||||
<p className="text-mmd text-destructive">
|
|
||||||
Invalid OpenAI API key. Verify or replace the key.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<AdvancedOnboarding
|
|
||||||
icon={<OpenAILogo className="w-4 h-4" />}
|
|
||||||
languageModels={languageModels}
|
|
||||||
embeddingModels={embeddingModels}
|
|
||||||
languageModel={languageModel}
|
|
||||||
embeddingModel={embeddingModel}
|
|
||||||
sampleDataset={sampleDataset}
|
|
||||||
setLanguageModel={setLanguageModel}
|
|
||||||
setSampleDataset={handleSampleDatasetChange}
|
|
||||||
setEmbeddingModel={setEmbeddingModel}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue