[Feat]: Per user proactive conversation starters for cloud openhands (#8272)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Rohit Malhotra 2025-05-05 16:41:56 -04:00 committed by GitHub
parent 24ffd14f14
commit 4c1ae6fd8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 60 additions and 1 deletions

View File

@ -20,6 +20,8 @@ const saveSettingsMutationFn = async (settings: Partial<PostSettings>) => {
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);

View File

@ -22,6 +22,8 @@ const getSettingsQueryFn = async (): Promise<Settings> => {
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,
};

View File

@ -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",

View File

@ -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": "カスタムモデル",

View File

@ -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,
};

View File

@ -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)}
</SettingsSwitch>
{config?.APP_MODE === "saas" && (
<SettingsSwitch
testId="enable-proactive-conversations-switch"
name="enable-proactive-conversations-switch"
defaultIsToggled={
!!settings.ENABLE_PROACTIVE_CONVERSATION_STARTERS
}
onToggle={checkIfProactiveConversationsSwitchHasChanged}
>
{t(I18nKey.SETTINGS$PROACTIVE_CONVERSATION_STARTERS)}
</SettingsSwitch>
)}
</div>
)}

View File

@ -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,
};

View File

@ -21,6 +21,7 @@ export type Settings = {
PROVIDER_TOKENS_SET: Partial<Record<Provider, string | null>>;
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<Record<Provider, string | null>>;
};

View File

@ -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