import { Badge } from "@/components/ui/badge"; 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { otelFormSchema, type OtelFormSchema } from "@/lib/types/schemas"; import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; import { zodResolver } from "@hookform/resolvers/zod"; import { Trash2 } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm, type Resolver } from "react-hook-form"; interface OtelFormFragmentProps { currentConfig?: { enabled?: boolean; service_name?: string; collector_url?: string; headers?: Record; trace_type?: "genai_extension" | "vercel" | "open_inference"; protocol?: "http" | "grpc"; // TLS configuration tls_ca_cert?: string; insecure?: boolean; // Metrics push configuration metrics_enabled?: boolean; metrics_endpoint?: string; metrics_push_interval?: number; }; onSave: (config: OtelFormSchema) => Promise; onDelete?: () => void; isDeleting?: boolean; isLoading?: boolean; } export function OtelFormFragment({ currentConfig: initialConfig, onSave, onDelete, isDeleting = false, isLoading = false, }: OtelFormFragmentProps) { const hasOtelAccess = useRbac(RbacResource.Observability, RbacOperation.Update); const [isSaving, setIsSaving] = useState(false); const form = useForm({ resolver: zodResolver(otelFormSchema) as Resolver, mode: "onChange", reValidateMode: "onChange", defaultValues: { enabled: initialConfig?.enabled ?? true, otel_config: { service_name: initialConfig?.service_name ?? "bifrost", collector_url: initialConfig?.collector_url ?? "", headers: initialConfig?.headers ?? {}, trace_type: initialConfig?.trace_type ?? "genai_extension", protocol: initialConfig?.protocol ?? "http", tls_ca_cert: initialConfig?.tls_ca_cert ?? "", insecure: initialConfig?.insecure ?? true, metrics_enabled: initialConfig?.metrics_enabled ?? false, metrics_endpoint: initialConfig?.metrics_endpoint ?? "", metrics_push_interval: initialConfig?.metrics_push_interval ?? 15, }, }, }); const onSubmit = (data: OtelFormSchema) => { setIsSaving(true); onSave(data).finally(() => setIsSaving(false)); }; // Re-run validation on collector_url when protocol changes so cross-field // refinement in the schema is applied immediately const protocol = form.watch("otel_config.protocol"); const metricsEnabled = form.watch("otel_config.metrics_enabled"); useEffect(() => { if (form.getValues("enabled") === false) return; form.trigger("otel_config.collector_url"); // Also re-validate metrics_endpoint when protocol changes if (metricsEnabled) { form.trigger("otel_config.metrics_endpoint"); } }, [protocol, form, metricsEnabled]); // Re-run validation on metrics_endpoint when metrics_enabled changes useEffect(() => { if (metricsEnabled) { form.trigger("otel_config.metrics_endpoint"); } }, [metricsEnabled, form]); useEffect(() => { // Reset form with new initial config when it changes form.reset({ enabled: initialConfig?.enabled ?? true, otel_config: { service_name: initialConfig?.service_name ?? "bifrost", collector_url: initialConfig?.collector_url || "", headers: initialConfig?.headers || {}, trace_type: initialConfig?.trace_type || "genai_extension", protocol: initialConfig?.protocol || "http", tls_ca_cert: initialConfig?.tls_ca_cert ?? "", insecure: initialConfig?.insecure ?? true, metrics_enabled: initialConfig?.metrics_enabled ?? false, metrics_endpoint: initialConfig?.metrics_endpoint ?? "", metrics_push_interval: initialConfig?.metrics_push_interval ?? 15, }, }); }, [form, initialConfig]); const traceTypeOptions: { value: string; label: string; disabled?: boolean; disabledReason?: string }[] = [ { value: "genai_extension", label: "OTel GenAI Extension (Recommended)" }, { value: "vercel", label: "Vercel AI SDK", disabled: true, disabledReason: "Coming soon" }, { value: "open_inference", label: "Arize OpenInference", disabled: true, disabledReason: "Coming soon" }, ]; const protocolOptions: { value: string; label: string; disabled?: boolean; disabledReason?: string }[] = [ { value: "http", label: "HTTP" }, { value: "grpc", label: "GRPC" }, ]; return (
{/* OTEL Configuration */}
( Service Name If kept empty, the service name will be set to "bifrost" )} /> ( OTLP Collector URL
{form.watch("otel_config.protocol") === "http" ? "http(s)://:/v1/traces" : ":"}
)} /> ( )} />
( Format )} /> ( Protocol )} />
{/* TLS Configuration */}
(
Insecure (Skip TLS) Skip TLS verification. Disable this to use TLS with system root CAs or a custom CA certificate.
{ field.onChange(checked); if (checked) { form.setValue("otel_config.tls_ca_cert", ""); } }} disabled={!hasOtelAccess} />
)} /> {!form.watch("otel_config.insecure") && ( ( TLS CA Certificate Path File path to the CA certificate on the Bifrost server. Leave empty to use system root CAs. )} /> )}
{/* Metrics Push Configuration */}
(

Enable Metrics Export BETA

Push metrics to an OTEL Collector for proper aggregation in cluster deployments

)} /> {form.watch("otel_config.metrics_enabled") && (
( Metrics Endpoint
{form.watch("otel_config.protocol") === "http" ? "http(s)://:/v1/metrics" : ":"}
)} /> ( Push Interval (seconds) field.onChange(e.target.value === "" ? null : Number(e.target.value))} /> How often to push metrics (1-300 seconds) )} />
)}
{/* Form Actions */}
( Enabled )} />
{onDelete && ( )} {(!form.formState.isDirty || !form.formState.isValid) && (

{!form.formState.isDirty && !form.formState.isValid ? "No changes made and validation errors present" : !form.formState.isDirty ? "No changes made" : "Please fix validation errors"}

)}
); }