diff --git a/frontend/src/hooks/mutation/use-save-settings.ts b/frontend/src/hooks/mutation/use-save-settings.ts index f060a5b462..1338212814 100644 --- a/frontend/src/hooks/mutation/use-save-settings.ts +++ b/frontend/src/hooks/mutation/use-save-settings.ts @@ -20,6 +20,8 @@ const saveSettingsMutationFn = async (settings: Partial) => { enable_default_condenser: settings.ENABLE_DEFAULT_CONDENSER, enable_sound_notifications: settings.ENABLE_SOUND_NOTIFICATIONS, user_consents_to_analytics: settings.user_consents_to_analytics, + enable_proactive_conversation_starters: + settings.ENABLE_PROACTIVE_CONVERSATION_STARTERS, }; await OpenHands.saveSettings(apiSettings); diff --git a/frontend/src/hooks/query/use-settings.ts b/frontend/src/hooks/query/use-settings.ts index 86f2ea626b..a995588781 100644 --- a/frontend/src/hooks/query/use-settings.ts +++ b/frontend/src/hooks/query/use-settings.ts @@ -22,6 +22,8 @@ const getSettingsQueryFn = async (): Promise => { PROVIDER_TOKENS_SET: apiSettings.provider_tokens_set, ENABLE_DEFAULT_CONDENSER: apiSettings.enable_default_condenser, ENABLE_SOUND_NOTIFICATIONS: apiSettings.enable_sound_notifications, + ENABLE_PROACTIVE_CONVERSATION_STARTERS: + apiSettings.enable_proactive_conversation_starters, USER_CONSENTS_TO_ANALYTICS: apiSettings.user_consents_to_analytics, IS_NEW_USER: false, }; diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts index fd69f53823..adc0a87f00 100644 --- a/frontend/src/i18n/declaration.ts +++ b/frontend/src/i18n/declaration.ts @@ -70,6 +70,7 @@ export enum I18nKey { SETTINGS$LLM_SETTINGS = "SETTINGS$LLM_SETTINGS", SETTINGS$GIT_SETTINGS = "SETTINGS$GIT_SETTINGS", SETTINGS$SOUND_NOTIFICATIONS = "SETTINGS$SOUND_NOTIFICATIONS", + SETTINGS$PROACTIVE_CONVERSATION_STARTERS = "SETTINGS$PROACTIVE_CONVERSATION_STARTERS", SETTINGS$CUSTOM_MODEL = "SETTINGS$CUSTOM_MODEL", GITHUB$CODE_NOT_IN_GITHUB = "GITHUB$CODE_NOT_IN_GITHUB", GITHUB$START_FROM_SCRATCH = "GITHUB$START_FROM_SCRATCH", diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index e90e8448c5..da00ea79ca 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -1052,6 +1052,21 @@ "fr": "Notifications sonores", "tr": "Ses Bildirimleri" }, + "SETTINGS$PROACTIVE_CONVERSATION_STARTERS": { + "en": "Suggest Tasks on GitHub", + "ja": "GitHubでタスクを提案", + "zh-CN": "在GitHub上推荐任务", + "zh-TW": "在GitHub上推薦任務", + "ko-KR": "GitHub에서 작업 제안", + "de": "Aufgaben auf GitHub vorschlagen", + "no": "Foreslå oppgaver på GitHub", + "it": "Suggerisci attività su GitHub", + "pt": "Sugerir tarefas no GitHub", + "es": "Sugerir tareas en GitHub", + "ar": "اقتراح المهام على GitHub", + "fr": "Suggérer des tâches sur GitHub", + "tr": "GitHub'da Görevler Öner" + }, "SETTINGS$CUSTOM_MODEL": { "en": "Custom Model", "ja": "カスタムモデル", diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index ee764f6517..c524258e5d 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -25,6 +25,8 @@ export const MOCK_DEFAULT_USER_SETTINGS: ApiSettings | PostApiSettings = { provider_tokens_set: DEFAULT_SETTINGS.PROVIDER_TOKENS_SET, enable_default_condenser: DEFAULT_SETTINGS.ENABLE_DEFAULT_CONDENSER, enable_sound_notifications: DEFAULT_SETTINGS.ENABLE_SOUND_NOTIFICATIONS, + enable_proactive_conversation_starters: + DEFAULT_SETTINGS.ENABLE_PROACTIVE_CONVERSATION_STARTERS, user_consents_to_analytics: DEFAULT_SETTINGS.USER_CONSENTS_TO_ANALYTICS, }; diff --git a/frontend/src/routes/app-settings.tsx b/frontend/src/routes/app-settings.tsx index 293cd90dad..ed03aa7e69 100644 --- a/frontend/src/routes/app-settings.tsx +++ b/frontend/src/routes/app-settings.tsx @@ -15,12 +15,14 @@ import { } from "#/utils/custom-toast-handlers"; import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message"; import { AppSettingsInputsSkeleton } from "#/components/features/settings/app-settings/app-settings-inputs-skeleton"; +import { useConfig } from "#/hooks/query/use-config"; function AppSettingsScreen() { const { t } = useTranslation(); const { mutate: saveSettings, isPending } = useSaveSettings(); const { data: settings, isLoading } = useSettings(); + const { data: config } = useConfig(); const [languageInputHasChanged, setLanguageInputHasChanged] = React.useState(false); @@ -30,6 +32,10 @@ function AppSettingsScreen() { soundNotificationsSwitchHasChanged, setSoundNotificationsSwitchHasChanged, ] = React.useState(false); + const [ + proactiveConversationsSwitchHasChanged, + setProactiveConversationsSwitchHasChanged, + ] = React.useState(false); const formAction = (formData: FormData) => { const languageLabel = formData.get("language-input")?.toString(); @@ -43,11 +49,16 @@ function AppSettingsScreen() { const enableSoundNotifications = formData.get("enable-sound-notifications-switch")?.toString() === "on"; + const enableProactiveConversations = + formData.get("enable-proactive-conversations-switch")?.toString() === + "on"; + saveSettings( { LANGUAGE: language, user_consents_to_analytics: enableAnalytics, ENABLE_SOUND_NOTIFICATIONS: enableSoundNotifications, + ENABLE_PROACTIVE_CONVERSATION_STARTERS: enableProactiveConversations, }, { onSuccess: () => { @@ -90,10 +101,19 @@ function AppSettingsScreen() { ); }; + const checkIfProactiveConversationsSwitchHasChanged = (checked: boolean) => { + const currentProactiveConversations = + !!settings?.ENABLE_PROACTIVE_CONVERSATION_STARTERS; + setProactiveConversationsSwitchHasChanged( + checked !== currentProactiveConversations, + ); + }; + const formIsClean = !languageInputHasChanged && !analyticsSwitchHasChanged && - !soundNotificationsSwitchHasChanged; + !soundNotificationsSwitchHasChanged && + !proactiveConversationsSwitchHasChanged; const shouldBeLoading = !settings || isLoading || isPending; @@ -129,6 +149,19 @@ function AppSettingsScreen() { > {t(I18nKey.SETTINGS$SOUND_NOTIFICATIONS)} + + {config?.APP_MODE === "saas" && ( + + {t(I18nKey.SETTINGS$PROACTIVE_CONVERSATION_STARTERS)} + + )} )} diff --git a/frontend/src/services/settings.ts b/frontend/src/services/settings.ts index 1c24b127d1..5caae6e71e 100644 --- a/frontend/src/services/settings.ts +++ b/frontend/src/services/settings.ts @@ -15,6 +15,7 @@ export const DEFAULT_SETTINGS: Settings = { ENABLE_DEFAULT_CONDENSER: true, ENABLE_SOUND_NOTIFICATIONS: false, USER_CONSENTS_TO_ANALYTICS: false, + ENABLE_PROACTIVE_CONVERSATION_STARTERS: false, IS_NEW_USER: true, }; diff --git a/frontend/src/types/settings.ts b/frontend/src/types/settings.ts index 96695edb7c..09253edcbc 100644 --- a/frontend/src/types/settings.ts +++ b/frontend/src/types/settings.ts @@ -21,6 +21,7 @@ export type Settings = { PROVIDER_TOKENS_SET: Partial>; ENABLE_DEFAULT_CONDENSER: boolean; ENABLE_SOUND_NOTIFICATIONS: boolean; + ENABLE_PROACTIVE_CONVERSATION_STARTERS: boolean; USER_CONSENTS_TO_ANALYTICS: boolean | null; IS_NEW_USER?: boolean; }; @@ -37,6 +38,7 @@ export type ApiSettings = { remote_runtime_resource_factor: number | null; enable_default_condenser: boolean; enable_sound_notifications: boolean; + enable_proactive_conversation_starters: boolean; user_consents_to_analytics: boolean | null; provider_tokens_set: Partial>; }; diff --git a/openhands/storage/data_models/settings.py b/openhands/storage/data_models/settings.py index 671d6e4d2d..ef40bcb5fc 100644 --- a/openhands/storage/data_models/settings.py +++ b/openhands/storage/data_models/settings.py @@ -33,6 +33,7 @@ class Settings(BaseModel): secrets_store: UserSecrets = Field(default_factory=UserSecrets, frozen=True) enable_default_condenser: bool = True enable_sound_notifications: bool = False + enable_proactive_conversation_starters: bool = True user_consents_to_analytics: bool | None = None sandbox_base_container_image: str | None = None sandbox_runtime_container_image: str | None = None