Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated all the env. branch: implemented feature flags #108

Merged
merged 3 commits into from
Sep 12, 2024
Merged
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
6 changes: 6 additions & 0 deletions backend/enmedd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@
from enmedd.server.documents.credential import router as credential_router
from enmedd.server.documents.document import router as document_router
from enmedd.server.enmedd_api.ingestion import router as enmedd_api_server
from enmedd.server.feature_flags.api import (
instance_admin_router as ff_instance_admin_router,
)
from enmedd.server.feature_flags.api import router as ff_settings_router
from enmedd.server.features.assistant.api import admin_router as admin_assistant_router
from enmedd.server.features.assistant.api import basic_router as assistant_router
from enmedd.server.features.document_set.api import router as document_set_router
Expand Down Expand Up @@ -289,6 +293,8 @@ def get_application() -> FastAPI:
include_router_with_global_prefix_prepended(application, gpts_router)
include_router_with_global_prefix_prepended(application, settings_router)
include_router_with_global_prefix_prepended(application, settings_admin_router)
include_router_with_global_prefix_prepended(application, ff_instance_admin_router)
include_router_with_global_prefix_prepended(application, ff_settings_router)
include_router_with_global_prefix_prepended(application, llm_admin_router)
include_router_with_global_prefix_prepended(application, llm_router)
include_router_with_global_prefix_prepended(
Expand Down
1 change: 1 addition & 0 deletions backend/enmedd/server/auth_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
("/users/{id}", {"GET"}),
("/users/{id}", {"PATCH"}),
("/users/{id}", {"DELETE"}),
("/ff", {"GET"}),
# oauth
("/auth/oauth/authorize", {"GET"}),
("/auth/oauth/callback", {"GET"}),
Expand Down
30 changes: 30 additions & 0 deletions backend/enmedd/server/feature_flags/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException

from enmedd.auth.users import current_admin_user
from enmedd.db.models import User
from enmedd.server.feature_flags.models import FeatureFlags
from enmedd.server.feature_flags.store import load_feature_flags
from enmedd.server.feature_flags.store import store_feature_flags


router = APIRouter(prefix="/ff")
instance_admin_router = APIRouter(prefix="/ff/instance-admin")


@router.get("")
def fetch_feature_flags() -> FeatureFlags:
return load_feature_flags()


# only instance admin can only turn on and off feature flags
@instance_admin_router.put("")
def put_feature_flags(
feature_flags: FeatureFlags, _: User | None = Depends(current_admin_user)
) -> None:
try:
feature_flags.check_validity()
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
store_feature_flags(feature_flags)
16 changes: 16 additions & 0 deletions backend/enmedd/server/feature_flags/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pydantic import BaseModel


class FeatureFlags(BaseModel):
"""Features Control"""

profile_page: bool = False
multi_teamspace: bool = False
multi_workspace: bool = False
query_history: bool = False
whitelabelling: bool = False
share_chat: bool = False
explore_assistants: bool = False

def check_validity(self) -> None:
return
33 changes: 33 additions & 0 deletions backend/enmedd/server/feature_flags/store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from typing import cast

from fastapi import Depends

from enmedd.auth.users import current_admin_user
from enmedd.db.models import User
from enmedd.dynamic_configs.factory import get_dynamic_config_store
from enmedd.dynamic_configs.interface import ConfigNotFoundError
from enmedd.server.feature_flags.models import FeatureFlags
from enmedd.utils.logger import setup_logger

_FEATURE_FLAG_KEY = "enmedd_feature_flag"
logger = setup_logger()


def load_feature_flags() -> FeatureFlags:
dynamic_config_store = get_dynamic_config_store()
try:
feature_flag = FeatureFlags(
**cast(dict, dynamic_config_store.load(_FEATURE_FLAG_KEY))
)
except ConfigNotFoundError:
feature_flag = FeatureFlags()
dynamic_config_store.store(_FEATURE_FLAG_KEY, feature_flag.dict())

return feature_flag


def store_feature_flags(
feature_flag: FeatureFlags, _: User | None = Depends(current_admin_user)
) -> None:
logger.info("Updating feature flag values")
get_dynamic_config_store().store(_FEATURE_FLAG_KEY, feature_flag.dict())
11 changes: 11 additions & 0 deletions web/src/app/admin/settings/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,19 @@ export interface EnterpriseSettings {
custom_popup_content: string | null;
}

export interface FeatureFlags {
profile_page: boolean;
multi_teamspace: boolean;
multi_workspace: boolean;
query_history: boolean;
whitelabelling: boolean;
share_chat: boolean;
explore_assistants: boolean;
}

export interface CombinedSettings {
settings: Settings;
featureFlags: FeatureFlags;
enterpriseSettings: EnterpriseSettings | null;
customAnalyticsScript: string | null;
}
39 changes: 20 additions & 19 deletions web/src/app/chat/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1118,26 +1118,27 @@ export function ChatPage({
{liveAssistant && (
<TopBar toggleLeftSideBar={toggleLeftSideBar}>
<div className="flex ml-auto gap-2 items-center">
{/* {chatSessionIdRef.current !== null && (
<ShareChatSessionModal
chatSessionId={chatSessionIdRef.current}
existingSharedStatus={chatSessionSharedStatus}
onShare={(shared) =>
setChatSessionSharedStatus(
shared
? ChatSessionSharedStatus.Public
: ChatSessionSharedStatus.Private
)
}
>
<div
onClick={() => setSharingModalVisible(true)}
className="h-10 w-10 hover:bg-light hover:text-accent-foreground inline-flex items-center gap-1.5 justify-center whitespace-nowrap rounded-regular text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
{settings?.featureFlags.share_chat &&
chatSessionIdRef.current !== null && (
<ShareChatSessionModal
chatSessionId={chatSessionIdRef.current}
existingSharedStatus={chatSessionSharedStatus}
onShare={(shared) =>
setChatSessionSharedStatus(
shared
? ChatSessionSharedStatus.Public
: ChatSessionSharedStatus.Private
)
}
>
<Share size={20} />
</div>
</ShareChatSessionModal>
)} */}
<div
onClick={() => setSharingModalVisible(true)}
className="h-10 w-10 hover:bg-light hover:text-accent-foreground inline-flex items-center gap-1.5 justify-center whitespace-nowrap rounded-regular text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
<Share size={20} />
</div>
</ShareChatSessionModal>
)}

{retrievalEnabled && showDocSidebar && (
<Button onClick={toggleSidebar} variant="ghost" size="icon">
Expand Down
30 changes: 18 additions & 12 deletions web/src/app/chat/sessionSidebar/ChatSessionDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useRouter } from "next/navigation";
import { ChatSession } from "../interfaces";
import { useState, useEffect } from "react";
import { useState, useEffect, useContext } from "react";
import { deleteChatSession, renameChatSession } from "../lib";
import { DeleteChatModal } from "../modal/DeleteChatModal";
import { BasicSelectable } from "@/components/BasicClickable";
Expand All @@ -24,6 +24,7 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { SettingsContext } from "@/components/settings/SettingsProvider";

export function ChatSessionDisplay({
chatSession,
Expand All @@ -39,6 +40,7 @@ export function ChatSessionDisplay({
toggleSideBar?: () => void;
}) {
const router = useRouter();
const combinedSettings = useContext(SettingsContext);
const [isDeletionModalVisible, setIsDeletionModalVisible] = useState(false);
const [isRenamingChat, setIsRenamingChat] = useState(false);
const [chatName, setChatName] = useState(chatSession.name);
Expand Down Expand Up @@ -137,18 +139,22 @@ export function ChatSessionDisplay({
</PopoverTrigger>
<PopoverContent>
<div className="flex flex-col w-full">
{/* <ShareChatSessionModal
chatSessionId={chatSession.id}
existingSharedStatus={chatSession.shared_status}
>
<Button
variant="ghost"
className="w-full flex justify-start hover:bg-primary hover:text-inverted"
{combinedSettings?.featureFlags.share_chat && (
<ShareChatSessionModal
chatSessionId={chatSession.id}
existingSharedStatus={
chatSession.shared_status
}
>
<Share2 className="mr-2" size={16} />
Share
</Button>
</ShareChatSessionModal> */}
<Button
variant="ghost"
className="w-full flex justify-start hover:bg-primary hover:text-inverted"
>
<Share2 className="mr-2" size={16} />
Share
</Button>
</ShareChatSessionModal>
)}
<Button
variant="ghost"
onClick={() => setIsRenamingChat(true)}
Expand Down
24 changes: 13 additions & 11 deletions web/src/app/chat/sessionSidebar/ChatSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,19 @@ export const ChatSidebar = ({
<MessageCircleMore size={16} className="min-w-4 min-h-4" />
Chat
</Link>
{/* <Link
href="/assistants/mine"
className={`flex px-4 py-2 h-10 rounded-regular cursor-pointer items-center gap-2 ${
isAssistant
? "bg-primary text-white"
: "hover:bg-hover-light"
}`}
>
<Headset size={16} />
<span className="truncate">Explore Assistants</span>
</Link> */}
{combinedSettings.featureFlags.explore_assistants && (
<Link
href="/assistants/mine"
className={`flex px-4 py-2 h-10 rounded-regular cursor-pointer items-center gap-2 ${
isAssistant
? "bg-primary text-white"
: "hover:bg-hover-light"
}`}
>
<Headset size={16} />
<span className="truncate">Explore Assistants</span>
</Link>
)}
</>
)}
<Separator className="mt-4" />
Expand Down
10 changes: 5 additions & 5 deletions web/src/app/profile/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ export default function Profile({ user }: { user: UserTypes | null }) {
<TabsTrigger value="profile">
<User size={16} className="mr-2" /> Profile
</TabsTrigger>
{/* <TabsTrigger value="security" disabled>
<TabsTrigger value="security">
<Lock size={16} className="mr-2" /> Security
</TabsTrigger>
<TabsTrigger value="billing" disabled>
<TabsTrigger value="billing">
<CreditCard size={16} className="mr-2" /> Billing
</TabsTrigger> */}
</TabsTrigger>
</TabsList>
<TabsContent value="profile">
<ProfileTab user={user} />
</TabsContent>
{/* <TabsContent value="security">
<TabsContent value="security">
<SecurityTab user={user} />
</TabsContent>
<TabsContent value="billing">
<SecurityTab user={user} />
</TabsContent> */}
</TabsContent>
</Tabs>
</div>
);
Expand Down
22 changes: 11 additions & 11 deletions web/src/app/profile/tabContent/profileTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Logo from "../../../../public/logo.png";
export default function ProfileTab({ user }: { user: UserTypes | null }) {
return (
<>
{/* <div className="flex py-8 border-b">
<div className="flex py-8 border-b">
<div className="w-[500px] text-sm">
<span className="font-semibold text-inverted-inverted">
Your Photo
Expand All @@ -22,14 +22,14 @@ export default function ProfileTab({ user }: { user: UserTypes | null }) {
<User size={25} className="mx-auto" />
)}
</div>
<Button disabled variant="link" className="text-error px-2">
<Button variant="link" className="text-error px-2">
Delete
</Button>
<Button disabled variant="link" className="px-2">
<Button variant="link" className="px-2">
Update
</Button>
</div>
</div> */}
</div>

<div className="py-8 border-b flex flex-col gap-5">
<div className="flex items-center">
Expand All @@ -40,7 +40,7 @@ export default function ProfileTab({ user }: { user: UserTypes | null }) {
<span className="font-semibold text-inverted-inverted">
{user?.full_name || "Unknown User"}
</span>{" "}
{/* <Button variant="outline">Edit</Button> */}
<Button variant="outline">Edit</Button>
</div>
</div>
<div className="flex items-center">
Expand All @@ -53,7 +53,7 @@ export default function ProfileTab({ user }: { user: UserTypes | null }) {
<span className="font-semibold text-inverted-inverted">
{user?.company_name || "No Company"}
</span>
{/* <Button variant="outline">Edit</Button> */}
<Button variant="outline">Edit</Button>
</div>
</div>
<div className="flex items-center">
Expand All @@ -64,12 +64,12 @@ export default function ProfileTab({ user }: { user: UserTypes | null }) {
<span className="font-semibold text-inverted-inverted">
{user?.email || "anonymous@gmail.com"}
</span>{" "}
{/* <Button variant="outline">Edit</Button> */}
<Button variant="outline">Edit</Button>
</div>
</div>
</div>

{/* <div className="flex py-8 border-b">
<div className="flex py-8 border-b">
<div className="w-[500px] text-sm">
<span className="font-semibold text-inverted-inverted">
Teamspaces Joined
Expand Down Expand Up @@ -100,13 +100,13 @@ export default function ProfileTab({ user }: { user: UserTypes | null }) {
<Button variant="outline">Manage Team</Button>
</div>
</div>
</div> */}
{/*
</div>

<div className="flex py-8 justify-end">
<div className="flex gap-3">
<Button>Save Changes</Button>
</div>
</div> */}
</div>
</>
);
}
Loading
Loading