From 1d9cf72e390859d1fd6e68a0dd516ccf2ff6cdbf Mon Sep 17 00:00:00 2001 From: chuckbutkus Date: Thu, 4 Dec 2025 23:32:20 -0500 Subject: [PATCH] JPMC Modifications (#11882) --- .../saas_nested_conversation_manager.py | 12 +++++++++- .../features/chat/expandable-message.tsx | 2 +- .../account-settings-context-menu.tsx | 9 ++++++- .../components/features/sidebar/sidebar.tsx | 6 ----- frontend/src/hooks/query/use-balance.ts | 2 +- frontend/src/routes/settings.tsx | 24 +++++++++++++++++-- 6 files changed, 43 insertions(+), 12 deletions(-) diff --git a/enterprise/server/saas_nested_conversation_manager.py b/enterprise/server/saas_nested_conversation_manager.py index e0727996de..0c5ece2675 100644 --- a/enterprise/server/saas_nested_conversation_manager.py +++ b/enterprise/server/saas_nested_conversation_manager.py @@ -70,6 +70,11 @@ RUNTIME_CONVERSATION_URL = RUNTIME_URL_PATTERN + ( else '/api/conversations/{conversation_id}' ) +RUNTIME_USERNAME = os.getenv('RUNTIME_USERNAME') +SU_TO_USER = os.getenv('SU_TO_USER', 'false') +truthy = {'1', 'true', 't', 'yes', 'y', 'on'} +SU_TO_USER = str(SU_TO_USER.lower() in truthy).lower() + # Time in seconds before a Redis entry is considered expired if not refreshed _REDIS_ENTRY_TIMEOUT_SECONDS = 300 @@ -772,7 +777,11 @@ class SaasNestedConversationManager(ConversationManager): env_vars['SERVE_FRONTEND'] = '0' env_vars['RUNTIME'] = 'local' # TODO: In the long term we may come up with a more secure strategy for user management within the nested runtime. - env_vars['USER'] = 'openhands' if config.run_as_openhands else 'root' + env_vars['USER'] = ( + RUNTIME_USERNAME + if RUNTIME_USERNAME + else ('openhands' if config.run_as_openhands else 'root') + ) env_vars['PERMITTED_CORS_ORIGINS'] = ','.join(PERMITTED_CORS_ORIGINS) env_vars['port'] = '60000' # TODO: These values are static in the runtime-api project, but do not get copied into the runtime ENV @@ -789,6 +798,7 @@ class SaasNestedConversationManager(ConversationManager): env_vars['INITIAL_NUM_WARM_SERVERS'] = '1' env_vars['INIT_GIT_IN_EMPTY_WORKSPACE'] = '1' env_vars['ENABLE_V1'] = '0' + env_vars['SU_TO_USER'] = SU_TO_USER # We need this for LLM traces tracking to identify the source of the LLM calls env_vars['WEB_HOST'] = WEB_HOST diff --git a/frontend/src/components/features/chat/expandable-message.tsx b/frontend/src/components/features/chat/expandable-message.tsx index cf9ae550d2..12942498a2 100644 --- a/frontend/src/components/features/chat/expandable-message.tsx +++ b/frontend/src/components/features/chat/expandable-message.tsx @@ -95,7 +95,7 @@ export function ExpandableMessage({ const statusIconClasses = "h-4 w-4 ml-2 inline"; if ( - config?.FEATURE_FLAGS.ENABLE_BILLING && + config?.FEATURE_FLAGS?.ENABLE_BILLING && config?.APP_MODE === "saas" && id === I18nKey.STATUS$ERROR_LLM_OUT_OF_CREDITS ) { diff --git a/frontend/src/components/features/context-menu/account-settings-context-menu.tsx b/frontend/src/components/features/context-menu/account-settings-context-menu.tsx index c09920e614..a30fe5f816 100644 --- a/frontend/src/components/features/context-menu/account-settings-context-menu.tsx +++ b/frontend/src/components/features/context-menu/account-settings-context-menu.tsx @@ -25,7 +25,14 @@ export function AccountSettingsContextMenu({ const { data: config } = useConfig(); const isSaas = config?.APP_MODE === "saas"; - const navItems = (isSaas ? SAAS_NAV_ITEMS : OSS_NAV_ITEMS).map((item) => ({ + + // Get navigation items and filter out LLM settings if the feature flag is enabled + let items = isSaas ? SAAS_NAV_ITEMS : OSS_NAV_ITEMS; + if (config?.FEATURE_FLAGS?.HIDE_LLM_SETTINGS) { + items = items.filter((item) => item.to !== "/settings"); + } + + const navItems = items.map((item) => ({ ...item, icon: React.cloneElement(item.icon, { width: 16, diff --git a/frontend/src/components/features/sidebar/sidebar.tsx b/frontend/src/components/features/sidebar/sidebar.tsx index 0580d9db3b..467e51f2dc 100644 --- a/frontend/src/components/features/sidebar/sidebar.tsx +++ b/frontend/src/components/features/sidebar/sidebar.tsx @@ -34,13 +34,7 @@ export function Sidebar() { const { pathname } = useLocation(); - // TODO: Remove HIDE_LLM_SETTINGS check once released - const shouldHideLlmSettings = - config?.FEATURE_FLAGS.HIDE_LLM_SETTINGS && config?.APP_MODE === "saas"; - React.useEffect(() => { - if (shouldHideLlmSettings) return; - if (location.pathname === "/settings") { setSettingsModalIsOpen(false); } else if ( diff --git a/frontend/src/hooks/query/use-balance.ts b/frontend/src/hooks/query/use-balance.ts index 1d89454f74..1bc7075e9f 100644 --- a/frontend/src/hooks/query/use-balance.ts +++ b/frontend/src/hooks/query/use-balance.ts @@ -13,6 +13,6 @@ export const useBalance = () => { enabled: !isOnTosPage && config?.APP_MODE === "saas" && - config?.FEATURE_FLAGS.ENABLE_BILLING, + config?.FEATURE_FLAGS?.ENABLE_BILLING, }); }; diff --git a/frontend/src/routes/settings.tsx b/frontend/src/routes/settings.tsx index 19370245b3..2d7f7cb6b6 100644 --- a/frontend/src/routes/settings.tsx +++ b/frontend/src/routes/settings.tsx @@ -34,6 +34,15 @@ export const clientLoader = async ({ request }: Route.ClientLoaderArgs) => { return redirect("/settings"); } + // If LLM settings are hidden and user tries to access the LLM settings page + if (config?.FEATURE_FLAGS?.HIDE_LLM_SETTINGS && pathname === "/settings") { + // Redirect to the first available settings page + if (isSaas) { + return redirect("/settings/user"); + } + return redirect("/settings/mcp"); + } + return null; }; @@ -52,13 +61,24 @@ function SettingsScreen() { } else { items.push(...OSS_NAV_ITEMS); } + + // Filter out LLM settings if the feature flag is enabled + if (config?.FEATURE_FLAGS?.HIDE_LLM_SETTINGS) { + return items.filter((item) => item.to !== "/settings"); + } + return items; - }, [isSaas]); + }, [isSaas, config?.FEATURE_FLAGS?.HIDE_LLM_SETTINGS]); // Current section title for the main content area const currentSectionTitle = useMemo(() => { const currentItem = navItems.find((item) => item.to === location.pathname); - return currentItem ? currentItem.text : "SETTINGS$NAV_LLM"; + if (currentItem) { + return currentItem.text; + } + + // Default to the first available navigation item if current page is not found + return navItems.length > 0 ? navItems[0].text : "SETTINGS$TITLE"; }, [navItems, location.pathname]); return (