import { Button } from "@/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { HeadersTable } from "@/components/ui/headersTable"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { DefaultNetworkConfig } from "@/lib/constants/config"; import { getErrorMessage, setProviderFormDirtyState, useAppDispatch } from "@/lib/store"; import { useUpdateProviderMutation } from "@/lib/store/apis/providersApi"; import { ModelProvider, isKnownProvider } from "@/lib/types/config"; import { networkOnlyFormSchema, type NetworkOnlyFormSchema } from "@/lib/types/schemas"; import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect } from "react"; import { useForm, type Resolver } from "react-hook-form"; import { toast } from "sonner"; import { buildProviderUpdatePayload } from "../views/utils"; interface NetworkFormFragmentProps { provider: ModelProvider; } // seconds to human readable time const secondsToHumanReadable = (seconds: number) => { // Handle edge cases if (!seconds || seconds < 0 || isNaN(seconds)) { return "0 seconds"; } seconds = Math.floor(seconds); if (seconds < 60) { return `${seconds} ${seconds === 1 ? "second" : "seconds"}`; } if (seconds < 3600) { const minutes = Math.floor(seconds / 60); return `${minutes} ${minutes === 1 ? "minute" : "minutes"}`; } if (seconds < 86400) { const hours = Math.floor(seconds / 3600); return `${hours} ${hours === 1 ? "hour" : "hours"}`; } // For >= 1 day, only show non-zero components const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const minutes = Math.floor((seconds % 3600) / 60); const remainingSeconds = seconds % 60; const parts: string[] = []; parts.push(`${days} ${days === 1 ? "day" : "days"}`); if (hours > 0) parts.push(`${hours} ${hours === 1 ? "hour" : "hours"}`); if (minutes > 0) parts.push(`${minutes} ${minutes === 1 ? "minute" : "minutes"}`); if (remainingSeconds > 0) parts.push(`${remainingSeconds} ${remainingSeconds === 1 ? "second" : "seconds"}`); return parts.join(" "); }; export function NetworkFormFragment({ provider }: NetworkFormFragmentProps) { const dispatch = useAppDispatch(); const hasUpdateProviderAccess = useRbac(RbacResource.ModelProvider, RbacOperation.Update); const [updateProvider, { isLoading: isUpdatingProvider }] = useUpdateProviderMutation(); const isCustomProvider = !isKnownProvider(provider.name as string); const form = useForm({ resolver: zodResolver(networkOnlyFormSchema) as Resolver, mode: "onChange", reValidateMode: "onChange", defaultValues: { network_config: { base_url: provider.network_config?.base_url || undefined, extra_headers: provider.network_config?.extra_headers, default_request_timeout_in_seconds: provider.network_config?.default_request_timeout_in_seconds ?? DefaultNetworkConfig.default_request_timeout_in_seconds, max_retries: provider.network_config?.max_retries ?? DefaultNetworkConfig.max_retries, retry_backoff_initial: provider.network_config?.retry_backoff_initial ?? DefaultNetworkConfig.retry_backoff_initial, retry_backoff_max: provider.network_config?.retry_backoff_max ?? DefaultNetworkConfig.retry_backoff_max, insecure_skip_verify: provider.network_config?.insecure_skip_verify ?? DefaultNetworkConfig.insecure_skip_verify, ca_cert_pem: provider.network_config?.ca_cert_pem ?? DefaultNetworkConfig.ca_cert_pem, stream_idle_timeout_in_seconds: provider.network_config?.stream_idle_timeout_in_seconds ?? DefaultNetworkConfig.stream_idle_timeout_in_seconds, max_conns_per_host: provider.network_config?.max_conns_per_host ?? DefaultNetworkConfig.max_conns_per_host, enforce_http2: provider.network_config?.enforce_http2 ?? DefaultNetworkConfig.enforce_http2, }, }, }); useEffect(() => { dispatch(setProviderFormDirtyState(form.formState.isDirty)); }, [form.formState.isDirty, dispatch]); const onSubmit = (data: NetworkOnlyFormSchema) => { const requiresBaseUrl = isCustomProvider; if (requiresBaseUrl && (data.network_config?.base_url ?? "").trim() === "") { if ((provider.network_config?.base_url ?? "").trim() !== "") { toast.error("You can't remove network configuration for this provider."); } else { toast.error("Base URL is required for this provider."); } return; } // Create updated provider configuration const updatedProvider = buildProviderUpdatePayload(provider, { network_config: { ...provider.network_config, base_url: data.network_config?.base_url || undefined, extra_headers: data.network_config?.extra_headers || undefined, default_request_timeout_in_seconds: data.network_config?.default_request_timeout_in_seconds ?? 30, max_retries: data.network_config?.max_retries ?? 0, retry_backoff_initial: data.network_config?.retry_backoff_initial ?? 500, retry_backoff_max: data.network_config?.retry_backoff_max ?? 10000, insecure_skip_verify: data.network_config?.insecure_skip_verify ?? false, ca_cert_pem: data.network_config?.ca_cert_pem?.trim() || undefined, stream_idle_timeout_in_seconds: data.network_config?.stream_idle_timeout_in_seconds ?? DefaultNetworkConfig.stream_idle_timeout_in_seconds, max_conns_per_host: data.network_config?.max_conns_per_host ?? DefaultNetworkConfig.max_conns_per_host, enforce_http2: data.network_config?.enforce_http2 ?? DefaultNetworkConfig.enforce_http2, }, }); updateProvider(updatedProvider) .unwrap() .then(() => { toast.success("Provider configuration updated successfully"); form.reset(data); }) .catch((err) => { toast.error("Failed to update provider configuration", { description: getErrorMessage(err), }); }); }; useEffect(() => { // Reset form with new provider's network_config when provider.name changes form.reset({ network_config: { base_url: provider.network_config?.base_url || undefined, extra_headers: provider.network_config?.extra_headers, default_request_timeout_in_seconds: provider.network_config?.default_request_timeout_in_seconds ?? DefaultNetworkConfig.default_request_timeout_in_seconds, max_retries: provider.network_config?.max_retries ?? DefaultNetworkConfig.max_retries, retry_backoff_initial: provider.network_config?.retry_backoff_initial ?? DefaultNetworkConfig.retry_backoff_initial, retry_backoff_max: provider.network_config?.retry_backoff_max ?? DefaultNetworkConfig.retry_backoff_max, insecure_skip_verify: provider.network_config?.insecure_skip_verify ?? DefaultNetworkConfig.insecure_skip_verify, ca_cert_pem: provider.network_config?.ca_cert_pem ?? DefaultNetworkConfig.ca_cert_pem, stream_idle_timeout_in_seconds: provider.network_config?.stream_idle_timeout_in_seconds ?? DefaultNetworkConfig.stream_idle_timeout_in_seconds, max_conns_per_host: provider.network_config?.max_conns_per_host ?? DefaultNetworkConfig.max_conns_per_host, }, }); }, [form, provider.name, provider.network_config]); const baseURLRequired = isCustomProvider; const hideBaseURL = provider.name === "vllm" || provider.name === "ollama" || provider.name === "sgl"; return (
{/* Network Configuration */}
{!hideBaseURL && ( ( Base URL {baseURLRequired ? "(Required)" : "(Optional)"} )} /> )}
( Timeout (seconds) { const value = e.target.value; if (value === "") { field.onChange(undefined); return; } const parsed = Number(value); if (!Number.isNaN(parsed)) { field.onChange(parsed); } form.trigger("network_config"); }} /> {secondsToHumanReadable(field.value)} )} /> ( Stream Idle Timeout (seconds) { const value = e.target.value; if (value === "") { field.onChange(undefined); return; } const parsed = Number(value); if (!Number.isNaN(parsed)) { field.onChange(parsed); } form.trigger("network_config"); }} /> {field.value ? secondsToHumanReadable(field.value) : ""} Max time to wait for next chunk before closing a stalled stream )} /> ( Max Retries { const value = e.target.value; if (value === "") { field.onChange(undefined); return; } const parsed = Number(value); if (!Number.isNaN(parsed)) { field.onChange(parsed); } form.trigger("network_config"); }} /> )} />
( Initial Backoff (ms) { const value = e.target.value; if (value === "") { field.onChange(undefined); return; } const parsed = Number(value); if (!Number.isNaN(parsed)) { field.onChange(parsed); } form.trigger("network_config"); }} /> )} /> ( Max Backoff (ms) { const value = e.target.value; if (value === "") { field.onChange(undefined); return; } const parsed = Number(value); if (!Number.isNaN(parsed)) { field.onChange(parsed); } form.trigger("network_config"); }} /> )} />
( Max Connections Per Host { const value = e.target.value; if (value === "") { field.onChange(undefined); return; } const parsed = Number(value); if (!Number.isNaN(parsed)) { field.onChange(parsed); } form.trigger("network_config"); }} /> Max TCP connections per provider host. For HTTP/2 providers (e.g. Bedrock), each connection supports ~100 concurrent streams. )} />
(
Enforce HTTP/2 Force HTTP/2 on provider connections. Relevant for net/http-based providers (e.g. Bedrock) where each HTTP/2 connection supports ~100 concurrent streams.
)} /> ( )} />

TLS / Certificate

(
Skip TLS verification Disable TLS certificate verification for provider connections. This bypasses server certificate validation and should be used only as a last resort when a trusted CA chain cannot be configured. Prefer ca_cert_pem for self-signed or private CA deployments.
)} /> ( CA Certificate (PEM) (Optional)