Skip to content

Commit

Permalink
chore(feature-flags): make feature flags configurable at runtime (#172)
Browse files Browse the repository at this point in the history
Signed-off-by: Radek Ježek <[email protected]>
  • Loading branch information
jezekra1 authored Jan 8, 2025
1 parent 5e308b3 commit 5df12c3
Show file tree
Hide file tree
Showing 34 changed files with 226 additions and 162 deletions.
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ API_URL=http://localhost:4000/
# Bypass authentication with jwt token from the API well-known file
DUMMY_JWT_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0QGVtYWlsLmNvbSIsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3QiLCJhdWQiOiJiZWUtdGVzdCJ9.vwkGnl7lBbzJYk6BtoW3VoA3mnNJVI-nDQU8aK7zOH-rkf2pn5cn6CKwpq7enDInIXro8WtBLNZP8Nr8GQIZKahICuP3YrPRmzv7YIW8LuXKnx1hycg5OAtj0OtQi5FYwwCxTYW9pBF2it7XwQSBcW7yYsOrvgs7jVhThCOsavX0YiAROxZIhk1idZT4Pl3egfUI_dy9iBxcn7xocTnos-94wqJNt8oCVgB8ynj75yJFHJbiQ-9Tym_V3LcMHoEyv67Jzie8KugCgdpuF6EbQqcyfYJ83q5jJpR2LiuWMuGsNSbjjDY-f1vCSMo9L9-R8KFrDylT_BzLvRBswOzW7A"

FEATURE_FLAGS='{"Knowledge":true,"Files":true,"TextExtraction":true,"FunctionTools":true,"Observe":true,"Projects":true}'

NEXT_PUBLIC_APP_NAME="BeeAI"
NEXT_PUBLIC_FEATURE_FLAGS='{"Knowledge":true,"Files":true,"FunctionTools":true}'
NEXT_PUBLIC_APP_EXAMPLES_SITE_URL="https://"
1 change: 0 additions & 1 deletion .github/workflows/publish-release-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ jobs:
push: true
build-args: |
NEXT_PUBLIC_APP_NAME=BeeAI
NEXT_PUBLIC_FEATURE_FLAGS={"Knowledge": false, "Files": true, "FunctionTools": true, "Observe": true}
NEXT_PUBLIC_USERCONTENT_SITE_URL=http://localhost:5173
tags: |
${{ vars.DOCKER_REGISTRY }}/i-am-bee/bee-ui-local:latest
Expand Down
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ ARG NEXT_PUBLIC_TOU_TEXT
ARG NEXT_PUBLIC_PRIVACY_URL
ARG NEXT_PUBLIC_DOCUMENTATION_URL
ARG NEXT_PUBLIC_FEEDBACK_URL
ARG NEXT_PUBLIC_FEATURE_FLAGS
ARG NEXT_PUBLIC_USERCONTENT_SITE_URL
ARG AUTH_JWKS_ENDPOINT
ARG DUMMY_JWT_TOKEN
Expand Down
5 changes: 5 additions & 0 deletions src/layout/providers/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ import {
useState,
} from 'react';
import { ProjectProvider } from './ProjectProvider';
import { FeatureName } from '@/utils/parseFeatureFlags';

export interface AppContextValue {
assistant: Assistant | null;
project: Project;
organization: Organization;
role: ProjectUser['role'] | null;
isProjectReadOnly?: boolean;
featureFlags: Record<FeatureName, boolean>;
onPageLeaveRef: MutableRefObject<() => void>;
}

Expand All @@ -62,11 +64,13 @@ const AppApiContext = createContext<AppApiContextValue>(
const ProjectContext = createContext<Props>(null as unknown as Props);

interface Props {
featureFlags: Record<FeatureName, boolean>;
project: Project;
organization: Organization;
}

export function AppProvider({
featureFlags,
project: initialProject,
organization,
children,
Expand Down Expand Up @@ -111,6 +115,7 @@ export function AppProvider({
<AppApiContext.Provider value={apiValue}>
<AppContext.Provider
value={{
featureFlags,
assistant: assistantData ?? assistant,
project: projectData ?? project,
organization,
Expand Down
4 changes: 2 additions & 2 deletions src/layout/providers/ModalProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ import { FallbackModal } from '@/components/FallbackModal/FallbackModal';
import { useIsomorphicLayoutEffect } from '@/hooks';
import { noop } from '@/utils/helpers';
import {
PropsWithChildren,
ReactNode,
createContext,
memo,
PropsWithChildren,
ReactNode,
useCallback,
useContext,
useState,
Expand Down
8 changes: 6 additions & 2 deletions src/layout/shell/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { fetchProject } from '@/app/api/rsc';
import { MAIN_ELEMENT_ID } from '@/utils/constants';
import { featureFlags, MAIN_ELEMENT_ID } from '@/utils/constants';
import { notFound } from 'next/navigation';
import { PropsWithChildren } from 'react';
import { AppProvider } from '../providers/AppProvider';
Expand All @@ -38,7 +38,11 @@ export async function AppShell({
if (!project) notFound();

return (
<AppProvider project={project} organization={{ id: organizationId }}>
<AppProvider
project={project}
organization={{ id: organizationId }}
featureFlags={featureFlags}
>
<div className={classes.root}>
<AppHeader />

Expand Down
5 changes: 3 additions & 2 deletions src/layout/shell/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { AssistantIcon } from '@/modules/assistants/icons/AssistantIcon';
import { ChatNavbarActions } from '@/modules/chat/ChatNavbarActions';
import { ProjectSelector } from '@/modules/projects/ProjectSelector';
import { useLayout } from '@/store/layout';
import { FeatureName, isFeatureEnabled } from '@/utils/isFeatureEnabled';
import { Button } from '@carbon/react';
import { ArrowLeft } from '@carbon/react/icons';
import { useRouter } from 'next-nprogress-bar';
Expand All @@ -34,6 +33,7 @@ import classes from './Navbar.module.scss';
import { SidebarProps } from './Sidebar';
import { SidebarButton } from './SidebarButton';
import { SkipNav } from './SkipNav';
import { useAppContext } from '@/layout/providers/AppProvider';

interface Props {
sidebarId: SidebarProps['id'];
Expand All @@ -43,6 +43,7 @@ interface Props {
export function Navbar({ sidebarId, sidebarOpen }: Props) {
const { setUserSetting } = useUserSetting();
const { onLeaveWithConfirmation } = useNavigationControl();
const { featureFlags } = useAppContext();
const router = useRouter();
const navbarProps = useLayout((state) => state.navbarProps);

Expand Down Expand Up @@ -145,7 +146,7 @@ export function Navbar({ sidebarId, sidebarOpen }: Props) {
<ChatNavbarActions assistant={navbarProps.assistant} />
)}

{isFeatureEnabled(FeatureName.Projects) && <ProjectSelector />}
{featureFlags.Projects && <ProjectSelector />}
</div>
</Container>
</header>
Expand Down
8 changes: 4 additions & 4 deletions src/layout/shell/UserNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ import { Link } from '@/components/Link/Link';
import { usePrefetchVectorStores } from '@/modules/knowledge/hooks/usePrefetchVectorStores';
import { usePrefetchTools } from '@/modules/tools/hooks/usePrefetchTools';
import { DOCUMENTATION_URL, FEEDBACK_URL } from '@/utils/constants';
import { FeatureName, isFeatureEnabled } from '@/utils/isFeatureEnabled';
import { FeatureName } from '@/utils/parseFeatureFlags';
import { ArrowUpRight } from '@carbon/react/icons';
import clsx from 'clsx';
import { usePathname } from 'next/navigation';
import { HTMLAttributes } from 'react';
import { useProjectContext } from '../providers/ProjectProvider';
import classes from './UserNav.module.scss';
import { UserProfile } from './UserProfile';
import { useAppContext } from '@/layout/providers/AppProvider';

interface Props extends HTMLAttributes<HTMLElement> {}

export function UserNav({ className }: Props) {
const pathname = usePathname();

const { project } = useProjectContext();
const { project, featureFlags } = useAppContext();

const prefetchTools = usePrefetchTools({ useDefaultParams: true });
const prefetchVectoreStores = usePrefetchVectorStores({
Expand All @@ -58,7 +58,7 @@ export function UserNav({ className }: Props) {
<nav className={clsx(classes.root, className)} aria-label="User menu">
<ul className={classes.nav}>
{ITEMS.filter(
({ featureName }) => !featureName || isFeatureEnabled(featureName),
({ featureName }) => !featureName || featureFlags[featureName],
).map(({ label, href, prefetchData }) => (
<li key={href}>
<Link
Expand Down
11 changes: 4 additions & 7 deletions src/modules/assistants/AssistantsNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,11 @@
import { useBreakpoint } from '@/hooks/useBreakpoint';
import { useFetchNextPageInView } from '@/hooks/useFetchNextPageInView';
import {
AppProvider,
useAppApiContext,
useAppContext,
} from '@/layout/providers/AppProvider';
import { useModal } from '@/layout/providers/ModalProvider';
import {
ProjectProvider,
useProjectContext,
} from '@/layout/providers/ProjectProvider';
import { getNewSessionUrl } from '@/layout/shell/NewSessionButton';
import { useLayout } from '@/store/layout';
import {
Expand Down Expand Up @@ -199,7 +196,7 @@ AgentLink.Skeleton = function Skeleton() {
};

function NewButton() {
const { project, organization } = useProjectContext();
const appContext = useAppContext();
const { openModal } = useModal();

return (
Expand All @@ -209,9 +206,9 @@ function NewButton() {
align="left"
onClick={() =>
openModal((props) => (
<ProjectProvider project={project} organization={organization}>
<AppProvider {...appContext}>
<NewAgentModal {...props} />
</ProjectProvider>
</AppProvider>
))
}
className={classes.newButton}
Expand Down
21 changes: 12 additions & 9 deletions src/modules/assistants/builder/KnowledgeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from '@/app/api/vector-stores/types';
import { DropdownSelector } from '@/components/DropdownSelector/DropdownSelector';
import { Link } from '@/components/Link/Link';
import { useAppContext } from '@/layout/providers/AppProvider';
import { AppProvider, useAppContext } from '@/layout/providers/AppProvider';
import { useModal } from '@/layout/providers/ModalProvider';
import { CreateKnowledgeModal } from '@/modules/knowledge/create/CreateKnowledgeModal';
import { KnowledgeFileCard } from '@/modules/knowledge/detail/KnowledgeFileCard';
Expand All @@ -48,7 +48,8 @@ import classes from './KnowledgeSelector.module.scss';

export function KnowledgeSelector() {
const { openModal } = useModal();
const { project, organization, isProjectReadOnly } = useAppContext();
const appContext = useAppContext();
const { project, organization, isProjectReadOnly } = appContext;
const {
field: { value, onChange },
} = useController<AssistantFormValues, 'vectorStoreId'>({
Expand Down Expand Up @@ -142,13 +143,15 @@ export function KnowledgeSelector() {
children: 'New knowledge base',
onClick: () =>
openModal((props) => (
<CreateKnowledgeModal
organizationId={organization.id}
projectId={project.id}
onCreateVectorStore={onCreateSuccess}
onSuccess={handleInvalidateData}
{...props}
/>
<AppProvider {...appContext}>
<CreateKnowledgeModal
organizationId={organization.id}
projectId={project.id}
onCreateVectorStore={onCreateSuccess}
onSuccess={handleInvalidateData}
{...props}
/>
</AppProvider>
)),
disabled: isProjectReadOnly,
}}
Expand Down
10 changes: 5 additions & 5 deletions src/modules/assistants/tools/BuilderTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { ToolResult } from '@/app/api/tools/types';
import { useAppContext } from '@/layout/providers/AppProvider';
import { AppProvider, useAppContext } from '@/layout/providers/AppProvider';
import { useModal } from '@/layout/providers/ModalProvider';
import { UserToolModal } from '@/modules/tools/manage/UserToolModal';
import { useFormContext } from 'react-hook-form';
Expand All @@ -30,7 +30,7 @@ import {

export function BuilderTools() {
const { openModal } = useModal();
const { isProjectReadOnly } = useAppContext();
const appContext = useAppContext();
const { project, organization } = useProjectContext();

const { setValue, getValues } = useFormContext<AssistantFormValues>();
Expand All @@ -51,14 +51,14 @@ export function BuilderTools() {
children: 'New Tool',
onClick: () =>
openModal((props) => (
<ProjectProvider project={project} organization={organization}>
<AppProvider {...appContext}>
<UserToolModal
onCreateSuccess={handleUserToolCreateSuccess}
{...props}
/>
</ProjectProvider>
</AppProvider>
)),
disabled: isProjectReadOnly,
disabled: appContext.isProjectReadOnly,
}}
/>
<div className={classes.body}>
Expand Down
9 changes: 5 additions & 4 deletions src/modules/assistants/tools/ToolInfoButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ import { LinkButton } from '@/components/LinkButton/LinkButton';
import { useModal } from '@/layout/providers/ModalProvider';
import { UserToolModal } from '@/modules/tools/manage/UserToolModal';
import { PublicToolModal } from '@/modules/tools/manage/PublicToolModal';
import { useAppContext } from '@/layout/providers/AppProvider';
import { AppProvider, useAppContext } from '@/layout/providers/AppProvider';

interface Props {
toolReference: ToolReference;
}

export function ToolInfoButton({ toolReference }: Props) {
const { isProjectReadOnly } = useAppContext();
const { project, organization } = useProjectContext();
const appContext = useAppContext();
const { project, organization } = appContext;
const { openModal } = useModal();
const { tool } = useToolInfo({ toolReference, project, organization });

Expand All @@ -58,7 +59,7 @@ export function ToolInfoButton({ toolReference }: Props) {
icon={ArrowUpRight}
onClick={() =>
openModal((props) => (
<ProjectProvider project={project} organization={organization}>
<AppProvider {...appContext}>
<>
{tool.type === 'user' ? (
isProjectReadOnly ? (
Expand All @@ -70,7 +71,7 @@ export function ToolInfoButton({ toolReference }: Props) {
<PublicToolModal {...props} tool={tool} />
)}
</>
</ProjectProvider>
</AppProvider>
))
}
>
Expand Down
8 changes: 3 additions & 5 deletions src/modules/chat/assistant-plan/PlanWithSources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,9 @@ import { TraceData } from '../trace/types';
import { useBuildTraceData } from '../trace/useBuildTraceData';
import { TraceInfoView } from '../trace/TraceInfoView';
import { TraceDataProvider } from '../trace/TraceDataProvider';
import { FeatureName, isFeatureEnabled } from '@/utils/isFeatureEnabled';
import { Spinner } from '@/components/Spinner/Spinner';
import { useProjectContext } from '@/layout/providers/ProjectProvider';
import { MAX_API_FETCH_LIMIT } from '@/app/api/utils';
import { useAppContext } from '@/layout/providers/AppProvider';

interface Props {
message: BotChatMessage;
Expand All @@ -55,7 +54,7 @@ interface Props {
}

function PlanWithSourcesComponent({ message, inView }: Props) {
const { project, organization } = useProjectContext();
const { project, organization, featureFlags } = useAppContext();
const { thread } = useChat();
const { setExpandedStep } = useExpandedStepActions();
const expandedStep = useExpandedStep();
Expand Down Expand Up @@ -106,8 +105,7 @@ function PlanWithSourcesComponent({ message, inView }: Props) {

const { traceData, traceError } = useBuildTraceData({
enabled:
isFeatureEnabled(FeatureName.Observe) &&
Boolean(debugMode && !message.pending && inView),
featureFlags.Observe && Boolean(debugMode && !message.pending && inView),
runId: message.run_id,
threadId: thread?.id,
});
Expand Down
5 changes: 3 additions & 2 deletions src/modules/chat/layout/InputBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
dispatchInputEventOnFormTextarea,
submitFormOnEnter,
} from '@/utils/formUtils';
import { FeatureName, isFeatureEnabled } from '@/utils/isFeatureEnabled';
import { Button } from '@carbon/react';
import { Send, StopOutlineFilled, WarningFilled } from '@carbon/react/icons';
import clsx from 'clsx';
Expand All @@ -41,6 +40,7 @@ import classes from './InputBar.module.scss';
import { PromptSuggestions } from './PromptSuggestions';
import { ThreadSettings } from './ThreadSettings';
import { UserChatMessage } from '../types';
import { useAppContext } from '@/layout/providers/AppProvider';

interface Props {
showSuggestions?: boolean;
Expand All @@ -53,6 +53,7 @@ export const InputBar = memo(function InputBar({
onMessageSubmit,
onMessageSent,
}: Props) {
const { featureFlags } = useAppContext();
const inputRef = useRef<HTMLTextAreaElement>(null);
const formRef = useRef<HTMLFormElement>(null);
const [promptSuggestionsOpen, setPromptSuggestionsOpen] = useState(false);
Expand Down Expand Up @@ -109,7 +110,7 @@ export const InputBar = memo(function InputBar({

const isPending = status !== 'ready';
const inputValue = watch('input');
const isFileUploadEnabled = isFeatureEnabled(FeatureName.Files);
const isFileUploadEnabled = featureFlags.Files;

const { ref: inputFormRef, ...inputFormProps } = register('input', {
required: true,
Expand Down
Loading

0 comments on commit 5df12c3

Please sign in to comment.