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
231 changes: 58 additions & 173 deletions tests/integrations/python/config.json

Large diffs are not rendered by default.

64 changes: 35 additions & 29 deletions ui/app/clientLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import OnboardingWidget from "@/components/onboardingWidget";
import ProgressProvider from "@/components/progressBar";
import Sidebar from "@/components/sidebar";
import { ThemeProvider } from "@/components/themeProvider";
import Topbar from "@/components/topbar";
import TrialExpiryBanner from "@/components/trialExpiryBanner";
import { Button } from "@/components/ui/button";
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { SidebarProvider } from "@/components/ui/sidebar";
import { useStoreSync } from "@/hooks/useStoreSync";
import { TopbarProvider } from "@/lib/contexts/topbarContext";
import { WebSocketProvider } from "@/hooks/useWebSocket";
import { getErrorMessage, ReduxProvider, useGetCoreConfigQuery, useIsAuthEnabledQuery } from "@/lib/store";
import { BifrostConfig } from "@/lib/types/config";
Expand Down Expand Up @@ -51,12 +53,6 @@ function AppContent({ children }: { children: React.ReactNode }) {
// neither a fragment nor a cookie to drive the tempTokenScoped per-visitor
// logic.
const publicShell = matches.some((m) => (m.staticData as { publicShell?: boolean } | undefined)?.publicShell === true);
const pathname = useLocation({ select: (location) => location.pathname });
const mobilePageTitle = (pathname.split("/").filter(Boolean).at(-1) ?? "Dashboard")
.split("-")
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(" ");

// Probe dashboard auth state on opted-in routes. is-auth-enabled is whitelisted
// (no 401 risk) and returns whether the current cookie is a valid session.
const { data: authState, isLoading: authLoading } = useIsAuthEnabledQuery(undefined, { skip: !tempTokenScoped });
Expand Down Expand Up @@ -118,28 +114,38 @@ function AppContent({ children }: { children: React.ReactNode }) {
<WebSocketProvider>
<CookiesProvider>
<StoreSyncInitializer />
<SidebarProvider>
<Sidebar />
<div className="dark:bg-card custom-scrollbar content-container mx-0 h-dvh w-full min-w-0 overflow-auto border border-gray-200 bg-white md:my-[0.5rem] md:mr-[0.5rem] md:h-[calc(100dvh-1rem)] md:rounded-md md:px-10 dark:border-zinc-800">
<div className="bg-card sticky top-0 z-20 flex h-12 min-w-0 items-center border-b px-4 md:hidden">
<div className="flex min-w-0 items-center gap-2">
<SidebarTrigger className="shrink-0" />
<span className="truncate text-sm font-semibold">{mobilePageTitle}</span>
<TopbarProvider>
<SidebarProvider>
<Sidebar />
{/* Content column: a fixed-height flex stack so the topbar takes its
48px and the content card absorbs the remainder. The topbar has no
background of its own, so it reads as the same surface as the
sidebar (both show the page body background). */}
<div className="flex h-dvh w-full min-w-0 flex-col">
<Topbar />
{/* No w-full: in a column flex container the cross axis is width, and
an explicit 100% would sit *outside* the right margin, pushing it
off-screen. Default align-items:stretch already fills the column
minus margins. No top margin either: the card butts against the
60px topbar so its top edge lands on the same line as the sidebar's
search input, and --app-content-viewport compensates so full-height
pages still measure to the card's inner height. */}
<div className="dark:bg-card custom-scrollbar content-container mx-0 min-h-0 min-w-0 flex-1 overflow-auto border border-gray-200 bg-white md:mr-2 md:mb-2 md:rounded-md md:px-10 dark:border-zinc-800">
<TrialExpiryBanner />
<main className="custom-scrollbar content-container-inner relative mx-auto flex h-full min-h-0 flex-col overflow-y-hidden md:p-4">
{isLoading ? (
<FullPageLoader />
) : (
<FullPage config={bifrostConfig} hasError={!!error} isRetrying={isFetching} onRetry={refetch}>
{children}
</FullPage>
)}
</main>
{bifrostConfig?.is_db_connected && <OnboardingWidget />}
</div>
</div>
<TrialExpiryBanner />
<main className="custom-scrollbar content-container-inner relative mx-auto flex h-[calc(100%-3rem)] min-h-0 flex-col overflow-y-hidden md:h-full md:p-4">
{isLoading ? (
<FullPageLoader />
) : (
<FullPage config={bifrostConfig} hasError={!!error} isRetrying={isFetching} onRetry={refetch}>
{children}
</FullPage>
)}
</main>
{bifrostConfig?.is_db_connected && <OnboardingWidget />}
</div>
</SidebarProvider>
</SidebarProvider>
</TopbarProvider>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</CookiesProvider>
</WebSocketProvider>
);
Expand Down Expand Up @@ -188,8 +194,8 @@ function FullPage({
function ConfigUnreachable({ isRetrying, onRetry }: { isRetrying: boolean; onRetry: () => void }) {
return (
<div className="h-base flex items-center justify-center p-4 sm:p-6" data-testid="config-unreachable">
<section className="bg-card w-full max-w-lg rounded-xl border p-6 shadow-sm sm:p-8" aria-labelledby="config-unreachable-title">
<div className="bg-muted text-muted-foreground flex size-11 items-center justify-center rounded-lg">
<section className="bg-card w-full max-w-lg rounded-sm border p-6 sm:p-8" aria-labelledby="config-unreachable-title">
<div className="bg-muted text-muted-foreground flex size-11 items-center justify-center rounded-sm">
<WifiOff className="size-5" aria-hidden="true" />
</div>
<h1 id="config-unreachable-title" className="text-foreground mt-5 text-xl font-semibold tracking-tight">
Expand Down
15 changes: 14 additions & 1 deletion ui/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,20 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--height-base: calc(100vh - 130px);
/* Height of the app topbar, and the viewport height left underneath it for
page content. Anything rendered inside the content card must size against
--app-content-viewport rather than 100dvh, or it overflows the card by
exactly the topbar's height and gives the whole shell a second scrollbar.
Full-viewport surfaces that sit outside the card — dialogs, portalled
popovers, the sidebar, the auth-only MinimalShell — keep using 100dvh. */
--app-topbar-height: 3.25rem;
/* Pages size themselves as `calc(var(--app-content-viewport) - 1rem)` — a
leftover from when the card carried my-2. It now has only mb-2, so add
that 0.5rem back here to keep their subtraction landing exactly on the
card's inner height. */
--app-content-viewport: calc(100dvh - var(--app-topbar-height) + 0.5rem);

--height-base: calc(var(--app-content-viewport) - 130px);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/* Font size overrides - format: [size, { line-height: value }] */
--text-xs: 0.75rem;
Expand Down
2 changes: 1 addition & 1 deletion ui/app/workspace/audit-logs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import AuditLogsView from "@enterprise/components/audit-logs/auditLogsView";

export default function AuditLogsPage() {
return (
<div className="no-padding-parent no-border-parent bg-background flex h-[calc(100vh-16px)] w-full">
<div className="no-padding-parent no-border-parent bg-background flex h-[calc(var(--app-content-viewport)_-_16px)] w-full">
<AuditLogsView />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion ui/app/workspace/cluster/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import ClusterView from "@enterprise/components/cluster/clusterView";

export default function ClusterPage() {
return (
<div className="mx-auto h-[calc(100dvh-50px)] w-full max-w-7xl p-4 md:p-0">
<div className="mx-auto h-[calc(var(--app-content-viewport)_-_50px)] w-full max-w-7xl p-4 md:p-0">
<ClusterView />
</div>
);
Expand Down
18 changes: 8 additions & 10 deletions ui/app/workspace/complexity-router/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import FullPageLoader from "@/components/fullPageLoader";
import {
AlertDialog,
Expand Down Expand Up @@ -335,7 +336,7 @@ export default function ComplexityRouterPage() {
const hasErrors = Boolean(boundaryErrors || keywordErrors);

return (
<div className="no-padding-parent flex h-[calc(100dvh_-_16px)] min-w-0 flex-col">
<div className="no-padding-parent flex h-[calc(var(--app-content-viewport)_-_16px)] min-w-0 flex-col">
<ScrollArea className="min-h-0 w-full flex-1">
<form
id="complexity-router-form"
Expand All @@ -344,15 +345,12 @@ export default function ComplexityRouterPage() {
noValidate
>
{/* ── Page header ── */}
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1.5">
<h1 className="text-2xl font-semibold tracking-tight">Complexity Router</h1>
<p className="text-muted-foreground max-w-2xl text-sm leading-relaxed">
Tune how incoming requests are classified into four tiers. Thresholds and keyword lists feed the{" "}
<code className="bg-muted rounded-sm px-1 py-0.5 font-mono text-xs">complexity_tier</code> field that routing rules can
target.
</p>
</div>
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-end">
<PageTitle>
Tune how incoming requests are classified into four tiers. Thresholds and keyword lists feed the{" "}
<code className="bg-muted rounded-sm px-1 py-0.5 font-mono text-xs">complexity_tier</code> field that routing rules can
target.
</PageTitle>
<Button asChild variant="outline" size="sm" className="w-full shrink-0 sm:w-fit" data-testid="complexity-router-docs-link">
<a href={"https://docs.getbifrost.ai/features/governance/complexity-router"} target="_blank" rel="noopener noreferrer">
<ExternalLink className="size-3.5" />
Expand Down
22 changes: 10 additions & 12 deletions ui/app/workspace/config/views/cachingView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
Expand Down Expand Up @@ -264,18 +265,15 @@ export default function CachingView() {

return (
<div className="mx-auto w-full max-w-4xl space-y-6">
<div>
<h2 className="text-lg font-semibold tracking-tight">Local Cache</h2>
<p className="text-muted-foreground text-sm">
Cache responses locally with two complementary lookup paths: <b>direct</b> hash matching for exact replays, and <b>semantic</b>{" "}
similarity search for related content. Send the <b>x-bf-cache-key</b> header to scope cached responses to a tenant or feature.{" "}
{!isVectorStoreEnabled && (
<span className="text-destructive font-medium">
Requires a vector store to be configured and enabled in <code>config.json</code>.
</span>
)}
</p>
</div>
<PageTitle title="Local Cache">
Cache responses locally with two complementary lookup paths: <b>direct</b> hash matching for exact replays, and <b>semantic</b>{" "}
similarity search for related content. Send the <b>x-bf-cache-key</b> header to scope cached responses to a tenant or feature.{" "}
{!isVectorStoreEnabled && (
<span className="text-destructive font-medium">
Requires a vector store to be configured and enabled in <code>config.json</code>.
</span>
)}
</PageTitle>

{configError !== undefined && (
<div className="border-destructive/50 bg-destructive/10 rounded-sm border p-4">
Expand Down
6 changes: 2 additions & 4 deletions ui/app/workspace/config/views/clientSettingsView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
Expand Down Expand Up @@ -287,10 +288,7 @@ export default function ClientSettingsView() {

return (
<div className="mx-auto w-full max-w-4xl space-y-6">
<div>
<h2 className="text-lg font-semibold tracking-tight">Client Settings</h2>
<p className="text-muted-foreground text-sm">Configure client behavior and request handling.</p>
</div>
<PageTitle title="Client Settings">Configure client behavior and request handling.</PageTitle>

<div className="space-y-4">
{/* Drop Excess Requests */}
Expand Down
28 changes: 13 additions & 15 deletions ui/app/workspace/config/views/compatibilityView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { getErrorMessage, useGetCoreConfigQuery, useUpdateCoreConfigMutation } from "@/lib/store";
Expand Down Expand Up @@ -57,21 +58,18 @@ export default function CompatibilityView() {

return (
<div className="mx-auto w-full max-w-4xl space-y-6">
<div>
<h2 className="text-lg font-semibold tracking-tight">Compatibility</h2>
<p className="text-muted-foreground text-sm">
Configure request conversions and compatibility fallbacks.{" "}
<a
className="text-primary underline"
href="https://docs.getbifrost.ai/features/compat-plugin"
target="_blank"
rel="noopener noreferrer"
data-testid="litellm-docs-link"
>
Learn more
</a>
</p>
</div>
<PageTitle title="Compatibility">
Configure request conversions and compatibility fallbacks.{" "}
<a
className="text-primary underline"
href="https://docs.getbifrost.ai/features/compat-plugin"
target="_blank"
rel="noopener noreferrer"
data-testid="litellm-docs-link"
>
Learn more
</a>
</PageTitle>

<div className="space-y-4">
<div className="flex items-center justify-between space-x-2">
Expand Down
12 changes: 5 additions & 7 deletions ui/app/workspace/config/views/featureFlagsView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Badge } from "@/components/ui/badge";
import { Switch } from "@/components/ui/switch";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
Expand Down Expand Up @@ -27,13 +28,10 @@ export default function FeatureFlagsView() {

return (
<div className="w-full space-y-4">
<div>
<h2 className="text-lg font-semibold tracking-tight">Feature Flags</h2>
<p className="text-muted-foreground text-sm">
Toggle in-process feature flags. Flags are declared in code; values can also be set via{" "}
<code className="text-xs">config.json</code> or Helm, in which case they appear here as locked.
</p>
</div>
<PageTitle title="Feature Flags">
Toggle in-process feature flags. Flags are declared in code; values can also be set via <code className="text-xs">config.json</code>{" "}
or Helm, in which case they appear here as locked.
</PageTitle>

{isLoading && <p className="text-muted-foreground text-sm">Loading feature flags...</p>}
{isError && <p className="text-sm text-red-500">Failed to load feature flags: {getErrorMessage(error)}</p>}
Expand Down
6 changes: 2 additions & 4 deletions ui/app/workspace/config/views/loggingView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
Expand Down Expand Up @@ -76,10 +77,7 @@ export default function LoggingView() {

return (
<div className="mx-auto w-full max-w-4xl space-y-4 px-4 py-6 md:px-0">
<div>
<h2 className="text-lg font-semibold tracking-tight">Logs Settings</h2>
<p className="text-muted-foreground text-sm">Configure logging settings for requests and responses.</p>
</div>
<PageTitle title="Logs Settings">Configure logging settings for requests and responses.</PageTitle>

<div className="space-y-4">
{/* Enable Logs */}
Expand Down
6 changes: 2 additions & 4 deletions ui/app/workspace/config/views/mcpView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -256,10 +257,7 @@ export default function MCPView() {

return (
<div className="mx-auto w-full max-w-4xl space-y-4 px-4 py-6 md:px-0" data-testid="mcp-settings-view">
<div>
<h2 className="text-lg font-semibold tracking-tight">MCP Settings</h2>
<p className="text-muted-foreground text-sm">Configure MCP (Model Context Protocol) agent and tool settings.</p>
</div>
<PageTitle title="MCP Settings">Configure MCP (Model Context Protocol) agent and tool settings.</PageTitle>
<div className="space-y-4">
{/* Max Agent Depth */}
<div className="flex items-center justify-between space-x-2 rounded-sm border p-4">
Expand Down
6 changes: 2 additions & 4 deletions ui/app/workspace/config/views/modelSettingsView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
Expand Down Expand Up @@ -132,10 +133,7 @@ export default function ModelSettingsView() {
return (
<div className="mx-auto w-full max-w-7xl space-y-4" data-testid="model-settings-view">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<h2 className="text-lg font-semibold tracking-tight">Model Settings</h2>
<p className="text-muted-foreground text-sm">Configure pricing and routing behaviour.</p>
</div>
<PageTitle title="Model Settings">Configure pricing and routing behaviour.</PageTitle>

<div className="space-y-4">
{/* Pricing Datasheet URL */}
Expand Down
6 changes: 2 additions & 4 deletions ui/app/workspace/config/views/performanceTuningView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
Expand Down Expand Up @@ -87,10 +88,7 @@ export default function PerformanceTuningView() {

return (
<div className="mx-auto w-full max-w-4xl space-y-4">
<div>
<h2 className="text-lg font-semibold tracking-tight">Performance Tuning</h2>
<p className="text-muted-foreground text-sm">Configure performance-related settings.</p>
</div>
<PageTitle title="Performance Tuning">Configure performance-related settings.</PageTitle>

<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
Expand Down
6 changes: 2 additions & 4 deletions ui/app/workspace/config/views/pricingConfigView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
Expand Down Expand Up @@ -89,10 +90,7 @@ export default function PricingConfigView() {
return (
<div className="mx-auto w-full max-w-7xl space-y-4" data-testid="pricing-config-view">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<h2 className="text-lg font-semibold tracking-tight">Pricing Configuration</h2>
<p className="text-muted-foreground text-sm">Configure custom pricing datasheet and sync intervals.</p>
</div>
<PageTitle title="Pricing Configuration">Configure custom pricing datasheet and sync intervals.</PageTitle>

<div className="space-y-4">
{/* Pricing Datasheet URL */}
Expand Down
6 changes: 2 additions & 4 deletions ui/app/workspace/config/views/proxyView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PageTitle from "@/components/pageTitle";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -59,10 +60,7 @@ export default function ProxyView() {
<div className="mx-auto w-full max-w-4xl space-y-4">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<div>
<h2 className="text-lg font-semibold tracking-tight">Proxy Settings</h2>
<p className="text-muted-foreground text-sm">Configure global proxy settings for outbound requests.</p>
</div>
<PageTitle title="Proxy Settings">Configure global proxy settings for outbound requests.</PageTitle>

<fieldset disabled={!hasSettingsUpdateAccess} className="space-y-4">
{/* Enable Proxy */}
Expand Down
Loading
Loading