Reset the reset (#8079)

This commit is contained in:
Engel Nyst 2025-04-25 00:59:37 +02:00 committed by GitHub
parent 4deffa3907
commit 9b1aaa53fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 38 additions and 336 deletions

View File

@ -1646,6 +1646,32 @@
}
}
},
"/api/reset-settings": {
"post": {
"summary": "Reset settings (Deprecated)",
"description": "This endpoint is deprecated and will return a 410 Gone error. Reset functionality has been removed.",
"operationId": "resetSettings",
"deprecated": true,
"responses": {
"410": {
"description": "Feature removed",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string",
"example": "Reset settings functionality has been removed."
}
}
}
}
}
}
}
}
},
"/api/unset-settings-tokens": {
"post": {
"summary": "Unset settings tokens",
@ -1685,45 +1711,6 @@
}
}
},
"/api/reset-settings": {
"post": {
"summary": "Reset settings",
"description": "Reset user settings to defaults",
"operationId": "resetSettings",
"responses": {
"200": {
"description": "Settings reset successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
}
}
}
}
},
"500": {
"description": "Error resetting settings",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string"
}
}
}
}
}
}
}
}
},
"/api/options/models": {
"get": {
"summary": "Get models",
@ -2095,4 +2082,4 @@
"bearerAuth": []
}
]
}
}

View File

@ -25,7 +25,6 @@ const mock_provider_tokens_are_set: Record<Provider, boolean> = {
describe("Settings Screen", () => {
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
const saveSettingsSpy = vi.spyOn(OpenHands, "saveSettings");
const resetSettingsSpy = vi.spyOn(OpenHands, "resetSettings");
const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
const { handleLogoutMock } = vi.hoisted(() => ({
@ -67,7 +66,6 @@ describe("Settings Screen", () => {
// Use queryAllByText to handle multiple elements with the same text
expect(screen.queryAllByText("SETTINGS$LLM_SETTINGS")).not.toHaveLength(0);
screen.getByText("ACCOUNT_SETTINGS$ADDITIONAL_SETTINGS");
screen.getByText("BUTTON$RESET_TO_DEFAULTS");
screen.getByText("BUTTON$SAVE");
});
});
@ -542,54 +540,6 @@ describe("Settings Screen", () => {
});
});
test("resetting settings with no changes but having advanced enabled should hide the advanced items", async () => {
const user = userEvent.setup();
getSettingsSpy.mockResolvedValueOnce({
...MOCK_DEFAULT_USER_SETTINGS,
});
renderSettingsScreen();
await toggleAdvancedSettings(user);
const resetButton = screen.getByText("BUTTON$RESET_TO_DEFAULTS");
await user.click(resetButton);
// show modal
const modal = await screen.findByTestId("reset-modal");
expect(modal).toBeInTheDocument();
// Mock the settings that will be returned after reset
// This should be the default settings with no advanced settings enabled
getSettingsSpy.mockResolvedValueOnce({
...MOCK_DEFAULT_USER_SETTINGS,
llm_base_url: "",
confirmation_mode: false,
security_analyzer: "",
});
// confirm reset
const confirmButton = within(modal).getByText("Reset");
await user.click(confirmButton);
await waitFor(() => {
expect(
screen.queryByTestId("llm-custom-model-input"),
).not.toBeInTheDocument();
expect(
screen.queryByTestId("base-url-input"),
).not.toBeInTheDocument();
expect(screen.queryByTestId("agent-input")).not.toBeInTheDocument();
expect(
screen.queryByTestId("security-analyzer-input"),
).not.toBeInTheDocument();
expect(
screen.queryByTestId("enable-confirmation-mode-switch"),
).not.toBeInTheDocument();
});
});
it("should save if only confirmation mode is enabled", async () => {
const user = userEvent.setup();
renderSettingsScreen();
@ -762,81 +712,6 @@ describe("Settings Screen", () => {
);
});
it("should reset the settings when the 'Reset to defaults' button is clicked", async () => {
const user = userEvent.setup();
getSettingsSpy.mockResolvedValue(MOCK_DEFAULT_USER_SETTINGS);
renderSettingsScreen();
const languageInput = await screen.findByTestId("language-input");
await user.click(languageInput);
const norskOption = await screen.findByText("Norsk");
await user.click(norskOption);
expect(languageInput).toHaveValue("Norsk");
const resetButton = screen.getByText("BUTTON$RESET_TO_DEFAULTS");
await user.click(resetButton);
expect(saveSettingsSpy).not.toHaveBeenCalled();
// show modal
const modal = await screen.findByTestId("reset-modal");
expect(modal).toBeInTheDocument();
// confirm reset
const confirmButton = within(modal).getByText("Reset");
await user.click(confirmButton);
await waitFor(() => {
expect(resetSettingsSpy).toHaveBeenCalled();
});
// Mock the settings response after reset
getSettingsSpy.mockResolvedValueOnce({
...MOCK_DEFAULT_USER_SETTINGS,
llm_base_url: "",
confirmation_mode: false,
security_analyzer: "",
});
// Wait for the mutation to complete and the modal to be removed
await waitFor(() => {
expect(screen.queryByTestId("reset-modal")).not.toBeInTheDocument();
expect(
screen.queryByTestId("llm-custom-model-input"),
).not.toBeInTheDocument();
expect(screen.queryByTestId("base-url-input")).not.toBeInTheDocument();
expect(screen.queryByTestId("agent-input")).not.toBeInTheDocument();
expect(
screen.queryByTestId("security-analyzer-input"),
).not.toBeInTheDocument();
expect(
screen.queryByTestId("enable-confirmation-mode-switch"),
).not.toBeInTheDocument();
});
});
it("should cancel the reset when the 'Cancel' button is clicked", async () => {
const user = userEvent.setup();
getSettingsSpy.mockResolvedValue(MOCK_DEFAULT_USER_SETTINGS);
renderSettingsScreen();
const resetButton = await screen.findByText("BUTTON$RESET_TO_DEFAULTS");
await user.click(resetButton);
const modal = await screen.findByTestId("reset-modal");
expect(modal).toBeInTheDocument();
const cancelButton = within(modal).getByText("Cancel");
await user.click(cancelButton);
expect(saveSettingsSpy).not.toHaveBeenCalled();
expect(screen.queryByTestId("reset-modal")).not.toBeInTheDocument();
});
it("should call handleCaptureConsent with true if the save is successful", async () => {
const user = userEvent.setup();
const handleCaptureConsentSpy = vi.spyOn(
@ -1044,18 +919,5 @@ describe("Settings Screen", () => {
);
});
it("should not submit the unwanted fields when resetting", async () => {
const user = userEvent.setup();
renderSettingsScreen();
const resetButton = await screen.findByText("BUTTON$RESET_TO_DEFAULTS");
await user.click(resetButton);
const modal = await screen.findByTestId("reset-modal");
const confirmButton = within(modal).getByText("Reset");
await user.click(confirmButton);
expect(saveSettingsSpy).not.toHaveBeenCalled();
expect(resetSettingsSpy).toHaveBeenCalled();
});
});
});

View File

@ -199,14 +199,6 @@ class OpenHands {
return data.status === 200;
}
/**
* Reset user settings in server
*/
static async resetSettings(): Promise<boolean> {
const response = await openHands.post("/api/reset-settings");
return response.status === 200;
}
static async createCheckoutSession(amount: number): Promise<string> {
const { data } = await openHands.post(
"/api/billing/create-checkout-session",

View File

@ -4,15 +4,7 @@ import OpenHands from "#/api/open-hands";
import { PostSettings, PostApiSettings } from "#/types/settings";
import { useSettings } from "../query/use-settings";
const saveSettingsMutationFn = async (
settings: Partial<PostSettings> | null,
) => {
// If settings is null, we're resetting
if (settings === null) {
await OpenHands.resetSettings();
return;
}
const saveSettingsMutationFn = async (settings: Partial<PostSettings>) => {
const apiSettings: Partial<PostApiSettings> = {
llm_model: settings.LLM_MODEL,
llm_base_url: settings.LLM_BASE_URL,
@ -39,12 +31,7 @@ export const useSaveSettings = () => {
const { data: currentSettings } = useSettings();
return useMutation({
mutationFn: async (settings: Partial<PostSettings> | null) => {
if (settings === null) {
await saveSettingsMutationFn(null);
return;
}
mutationFn: async (settings: Partial<PostSettings>) => {
const newSettings = { ...currentSettings, ...settings };
await saveSettingsMutationFn(newSettings);
},

View File

@ -82,7 +82,6 @@ export enum I18nKey {
API$DONT_KNOW_KEY = "API$DONT_KNOW_KEY",
BUTTON$SAVE = "BUTTON$SAVE",
BUTTON$CLOSE = "BUTTON$CLOSE",
BUTTON$RESET_TO_DEFAULTS = "BUTTON$RESET_TO_DEFAULTS",
MODAL$CONFIRM_RESET_TITLE = "MODAL$CONFIRM_RESET_TITLE",
MODAL$CONFIRM_RESET_MESSAGE = "MODAL$CONFIRM_RESET_MESSAGE",
MODAL$END_SESSION_TITLE = "MODAL$END_SESSION_TITLE",
@ -321,7 +320,6 @@ export enum I18nKey {
SETTINGS_FORM$ENABLE_CONFIRMATION_MODE_LABEL = "SETTINGS_FORM$ENABLE_CONFIRMATION_MODE_LABEL",
SETTINGS_FORM$SAVE_LABEL = "SETTINGS_FORM$SAVE_LABEL",
SETTINGS_FORM$CLOSE_LABEL = "SETTINGS_FORM$CLOSE_LABEL",
SETTINGS_FORM$RESET_TO_DEFAULTS_LABEL = "SETTINGS_FORM$RESET_TO_DEFAULTS_LABEL",
SETTINGS_FORM$CANCEL_LABEL = "SETTINGS_FORM$CANCEL_LABEL",
SETTINGS_FORM$END_SESSION_LABEL = "SETTINGS_FORM$END_SESSION_LABEL",
SETTINGS_FORM$CHANGING_WORKSPACE_WARNING_MESSAGE = "SETTINGS_FORM$CHANGING_WORKSPACE_WARNING_MESSAGE",

View File

@ -1231,21 +1231,6 @@
"tr": "Kapat",
"de": "Schließen"
},
"BUTTON$RESET_TO_DEFAULTS": {
"en": "Reset to defaults",
"ja": "デフォルトにリセット",
"zh-CN": "重置为默认值",
"zh-TW": "還原為預設值",
"ko-KR": "기본값으로 재설정",
"no": "Tilbakestill til standard",
"it": "Ripristina valori predefiniti",
"pt": "Restaurar padrões",
"es": "Restablecer valores predeterminados",
"ar": "إعادة التعيين إلى الإعدادات الافتراضية",
"fr": "Réinitialiser aux valeurs par défaut",
"tr": "Varsayılanlara sıfırla",
"de": "Auf Standardwerte zurücksetzen"
},
"MODAL$CONFIRM_RESET_TITLE": {
"en": "Are you sure?",
"ja": "本当によろしいですか?",
@ -4541,21 +4526,6 @@
"pt": "Fechar",
"tr": "Kapat"
},
"SETTINGS_FORM$RESET_TO_DEFAULTS_LABEL": {
"en": "Reset to defaults",
"es": "Reiniciar valores por defect",
"zh-CN": "重置为默认值",
"zh-TW": "還原為預設值",
"ko-KR": "기본값으로 재설정",
"ja": "デフォルトに戻す",
"no": "Tilbakestill til standardverdier",
"ar": "إعادة التعيين إلى الإعدادات الافتراضية",
"de": "Auf Standardwerte zurücksetzen",
"fr": "Réinitialiser aux valeurs par défaut",
"it": "Ripristina valori predefiniti",
"pt": "Restaurar padrões",
"tr": "Varsayılanlara sıfırla"
},
"SETTINGS_FORM$CANCEL_LABEL": {
"en": "Cancel",
"es": "Cancelar",

View File

@ -9,7 +9,6 @@ import { SettingsDropdownInput } from "#/components/features/settings/settings-d
import { SettingsInput } from "#/components/features/settings/settings-input";
import { SettingsSwitch } from "#/components/features/settings/settings-switch";
import { LoadingSpinner } from "#/components/shared/loading-spinner";
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
import { ModelSelector } from "#/components/shared/modals/settings/model-selector";
import { useSaveSettings } from "#/hooks/mutation/use-save-settings";
import { useAIConfigOptions } from "#/hooks/query/use-ai-config-options";
@ -95,8 +94,6 @@ function AccountSettings() {
>(isAdvancedSettingsSet ? "advanced" : "basic");
const [confirmationModeIsEnabled, setConfirmationModeIsEnabled] =
React.useState(!!settings?.SECURITY_ANALYZER);
const [resetSettingsModalIsOpen, setResetSettingsModalIsOpen] =
React.useState(false);
const formRef = React.useRef<HTMLFormElement>(null);
@ -180,16 +177,6 @@ function AccountSettings() {
});
};
const handleReset = () => {
saveSettings(null, {
onSuccess: () => {
displaySuccessToast(t(I18nKey.SETTINGS$RESET));
setResetSettingsModalIsOpen(false);
setLlmConfigMode("basic");
},
});
};
React.useEffect(() => {
// If settings is still loading by the time the state is set, it will always
// default to basic settings. This is a workaround to ensure the correct
@ -527,13 +514,6 @@ function AccountSettings() {
</form>
<footer className="flex gap-6 p-6 justify-end border-t border-t-tertiary">
<BrandButton
type="button"
variant="secondary"
onClick={() => setResetSettingsModalIsOpen(true)}
>
{t(I18nKey.BUTTON$RESET_TO_DEFAULTS)}
</BrandButton>
<BrandButton
type="button"
variant="primary"
@ -544,40 +524,6 @@ function AccountSettings() {
{t(I18nKey.BUTTON$SAVE)}
</BrandButton>
</footer>
{resetSettingsModalIsOpen && (
<ModalBackdrop>
<div
data-testid="reset-modal"
className="bg-base-secondary p-4 rounded-xl flex flex-col gap-4 border border-tertiary"
>
<p>{t(I18nKey.SETTINGS$RESET_CONFIRMATION)}</p>
<div className="w-full flex gap-2">
<BrandButton
type="button"
variant="primary"
className="grow"
onClick={() => {
handleReset();
}}
>
Reset
</BrandButton>
<BrandButton
type="button"
variant="secondary"
className="grow"
onClick={() => {
setResetSettingsModalIsOpen(false);
}}
>
Cancel
</BrandButton>
</div>
</div>
</ModalBackdrop>
)}
</>
);
}

View File

@ -135,7 +135,6 @@ def event_to_dict(event: 'Event') -> dict:
k: (v.value if isinstance(v, Enum) else _convert_pydantic_to_dict(v))
for k, v in props.items()
}
logger.debug(f'extras data in event_to_dict: {d["extras"]}')
# Include success field for CmdOutputObservation
if hasattr(event, 'success'):
d['success'] = event.success

View File

@ -713,7 +713,7 @@ class LLM(RetryMixin, DebugMixin):
completion_response=response, **extra_kwargs
)
except Exception as e:
logger.error(f'Error getting cost from litellm: {e}')
logger.debug(f'Error getting cost from litellm: {e}')
if cost is None:
_model_name = '/'.join(self.config.model.split('/')[1:])

View File

@ -207,54 +207,15 @@ async def unset_settings_tokens(request: Request) -> JSONResponse:
@app.post('/reset-settings', response_model=dict[str, str])
async def reset_settings(request: Request) -> JSONResponse:
"""
Resets user settings.
Resets user settings. (Deprecated)
"""
try:
settings_store = await SettingsStoreImpl.get_instance(
config, get_user_id(request)
)
existing_settings: Settings = await settings_store.load()
settings = Settings(
language='en',
agent='CodeActAgent',
security_analyzer='',
confirmation_mode=False,
llm_model='anthropic/claude-3-5-sonnet-20241022',
llm_api_key=SecretStr(''),
llm_base_url=None,
remote_runtime_resource_factor=1,
enable_default_condenser=True,
enable_sound_notifications=False,
user_consents_to_analytics=existing_settings.user_consents_to_analytics
if existing_settings
else False,
secrets_store=existing_settings.secrets_store
)
server_config_values = server_config.get_config()
is_hide_llm_settings_enabled = server_config_values.get(
'FEATURE_FLAGS', {}
).get('HIDE_LLM_SETTINGS', False)
# We don't want the user to be able to modify these settings in SaaS
if server_config.app_mode == AppMode.SAAS and is_hide_llm_settings_enabled:
if existing_settings:
settings.llm_api_key = existing_settings.llm_api_key
settings.llm_base_url = existing_settings.llm_base_url
settings.llm_model = existing_settings.llm_model
await settings_store.store(settings)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={'message': 'Settings stored'},
)
except Exception as e:
logger.warning(f'Something went wrong resetting settings: {e}')
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={'error': 'Something went wrong resetting settings'},
)
logger.warning(
f"Deprecated endpoint /api/reset-settings called by user"
)
return JSONResponse(
status_code=status.HTTP_410_GONE,
content={'error': 'Reset settings functionality has been removed.'},
)
async def check_provider_tokens(request: Request, settings: POSTSettingsModel) -> str: