diff --git a/frontend/__tests__/components/modals/base-modal/base-modal.test.tsx b/frontend/__tests__/components/modals/base-modal/base-modal.test.tsx index 563cbca6c45a..0454de0c77ec 100644 --- a/frontend/__tests__/components/modals/base-modal/base-modal.test.tsx +++ b/frontend/__tests__/components/modals/base-modal/base-modal.test.tsx @@ -52,14 +52,10 @@ describe("BaseModal", () => { expect(screen.getByText("Save")).toBeInTheDocument(); expect(screen.getByText("Cancel")).toBeInTheDocument(); - await act(async () => { - await userEvent.click(screen.getByText("Save")); - }); + await userEvent.click(screen.getByText("Save")); expect(onPrimaryClickMock).toHaveBeenCalledTimes(1); - await act(async () => { - await userEvent.click(screen.getByText("Cancel")); - }); + await userEvent.click(screen.getByText("Cancel")); expect(onSecondaryClickMock).toHaveBeenCalledTimes(1); }); @@ -80,9 +76,7 @@ describe("BaseModal", () => { />, ); - await act(async () => { - await userEvent.click(screen.getByText("Save")); - }); + await userEvent.click(screen.getByText("Save")); expect(onOpenChangeMock).toHaveBeenCalledTimes(1); }); diff --git a/frontend/src/api/open-hands.ts b/frontend/src/api/open-hands.ts index 1d1bd0dfa824..1534be879ca5 100644 --- a/frontend/src/api/open-hands.ts +++ b/frontend/src/api/open-hands.ts @@ -11,7 +11,7 @@ import { Conversation, } from "./open-hands.types"; import { openHands } from "./open-hands-axios"; -import { ApiSettings, Settings } from "#/services/settings"; +import { ApiSettings } from "#/services/settings"; class OpenHands { /** @@ -238,13 +238,11 @@ class OpenHands { } static async createConversation( - settings: Settings, githubToken?: string, selectedRepository?: string, ): Promise { const body = { github_token: githubToken, - args: settings, selected_repository: selectedRepository, }; diff --git a/frontend/src/components/shared/buttons/icon-button.tsx b/frontend/src/components/shared/buttons/icon-button.tsx index 66c86e2a58e3..90832afed42f 100644 --- a/frontend/src/components/shared/buttons/icon-button.tsx +++ b/frontend/src/components/shared/buttons/icon-button.tsx @@ -1,9 +1,9 @@ import { Button } from "@nextui-org/react"; -import React, { MouseEventHandler, ReactElement } from "react"; +import React, { ReactElement } from "react"; export interface IconButtonProps { icon: ReactElement; - onClick: MouseEventHandler; + onClick: () => void; ariaLabel: string; testId?: string; } @@ -18,7 +18,7 @@ export function IconButton({ @@ -162,7 +162,7 @@ function SecurityInvariant() {

{t(I18nKey.INVARIANT$POLICY_LABEL)}

@@ -184,7 +184,7 @@ function SecurityInvariant() {

{t(I18nKey.INVARIANT$SETTINGS_LABEL)}

diff --git a/frontend/src/hooks/mutation/use-create-conversation.ts b/frontend/src/hooks/mutation/use-create-conversation.ts index 1a9c7e3a79aa..ceb00b82b0e2 100644 --- a/frontend/src/hooks/mutation/use-create-conversation.ts +++ b/frontend/src/hooks/mutation/use-create-conversation.ts @@ -6,13 +6,11 @@ import OpenHands from "#/api/open-hands"; import { setInitialQuery } from "#/state/initial-query-slice"; import { RootState } from "#/store"; import { useAuth } from "#/context/auth-context"; -import { useSettings } from "../query/use-settings"; export const useCreateConversation = () => { const navigate = useNavigate(); const dispatch = useDispatch(); const { gitHubToken } = useAuth(); - const { data: settings } = useSettings(); const queryClient = useQueryClient(); const { selectedRepository, files } = useSelector( @@ -27,7 +25,6 @@ export const useCreateConversation = () => { if (variables.q) dispatch(setInitialQuery(variables.q)); return OpenHands.createConversation( - settings, gitHubToken || undefined, selectedRepository || undefined, ); diff --git a/frontend/src/hooks/mutation/use-save-settings.ts b/frontend/src/hooks/mutation/use-save-settings.ts index 2fb998cb4cf6..f9731e981d5b 100644 --- a/frontend/src/hooks/mutation/use-save-settings.ts +++ b/frontend/src/hooks/mutation/use-save-settings.ts @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { ApiSettings, + DEFAULT_SETTINGS, LATEST_SETTINGS_VERSION, Settings, } from "#/services/settings"; @@ -11,11 +12,11 @@ const saveSettingsMutationFn = async (settings: Partial) => { const apiSettings: Partial = { llm_model: settings.LLM_MODEL, llm_base_url: settings.LLM_BASE_URL, - agent: settings.AGENT, - language: settings.LANGUAGE, + agent: settings.AGENT || DEFAULT_SETTINGS.AGENT, + language: settings.LANGUAGE || DEFAULT_SETTINGS.LANGUAGE, confirmation_mode: settings.CONFIRMATION_MODE, security_analyzer: settings.SECURITY_ANALYZER, - llm_api_key: settings.LLM_API_KEY, + llm_api_key: settings.LLM_API_KEY?.trim() || undefined, }; await OpenHands.saveSettings(apiSettings); diff --git a/frontend/src/hooks/query/use-settings.ts b/frontend/src/hooks/query/use-settings.ts index 796c0cab1842..f6e12e33e185 100644 --- a/frontend/src/hooks/query/use-settings.ts +++ b/frontend/src/hooks/query/use-settings.ts @@ -1,25 +1,36 @@ import { useQuery } from "@tanstack/react-query"; import React from "react"; import posthog from "posthog-js"; +import { AxiosError } from "axios"; import { DEFAULT_SETTINGS, getLocalStorageSettings } from "#/services/settings"; import OpenHands from "#/api/open-hands"; const getSettingsQueryFn = async () => { - const apiSettings = await OpenHands.getSettings(); + try { + const apiSettings = await OpenHands.getSettings(); - if (apiSettings !== null) { - return { - LLM_MODEL: apiSettings.llm_model, - LLM_BASE_URL: apiSettings.llm_base_url, - AGENT: apiSettings.agent, - LANGUAGE: apiSettings.language, - CONFIRMATION_MODE: apiSettings.confirmation_mode, - SECURITY_ANALYZER: apiSettings.security_analyzer, - LLM_API_KEY: apiSettings.llm_api_key, - }; - } + if (apiSettings !== null) { + return { + LLM_MODEL: apiSettings.llm_model, + LLM_BASE_URL: apiSettings.llm_base_url, + AGENT: apiSettings.agent, + LANGUAGE: apiSettings.language, + CONFIRMATION_MODE: apiSettings.confirmation_mode, + SECURITY_ANALYZER: apiSettings.security_analyzer, + LLM_API_KEY: apiSettings.llm_api_key, + }; + } - return getLocalStorageSettings(); + return getLocalStorageSettings(); + } catch (error) { + if (error instanceof AxiosError) { + if (error.response?.status === 404) { + return DEFAULT_SETTINGS; + } + } + + throw error; + } }; export const useSettings = () => { diff --git a/frontend/src/hooks/use-maybe-migrate-settings.ts b/frontend/src/hooks/use-maybe-migrate-settings.ts index d2bb49f3c17a..26892c9745d7 100644 --- a/frontend/src/hooks/use-maybe-migrate-settings.ts +++ b/frontend/src/hooks/use-maybe-migrate-settings.ts @@ -1,7 +1,6 @@ // Sometimes we ship major changes, like a new default agent. import React from "react"; -import { useAuth } from "#/context/auth-context"; import { useSettingsUpToDate } from "#/context/settings-up-to-date-context"; import { DEFAULT_SETTINGS, @@ -12,7 +11,6 @@ import { useSaveSettings } from "./mutation/use-save-settings"; // In this case, we may want to override a previous choice made by the user. export const useMaybeMigrateSettings = () => { - const { logout } = useAuth(); const { mutateAsync: saveSettings } = useSaveSettings(); const { isUpToDate } = useSettingsUpToDate(); @@ -35,7 +33,7 @@ export const useMaybeMigrateSettings = () => { } if (currentVersion < 4) { - logout(); + // We used to log out here, but it's breaking things } // Only save settings if user already previously saved settings diff --git a/openhands/runtime/impl/docker/docker_runtime.py b/openhands/runtime/impl/docker/docker_runtime.py index 41882eb52158..62623d53a2fc 100644 --- a/openhands/runtime/impl/docker/docker_runtime.py +++ b/openhands/runtime/impl/docker/docker_runtime.py @@ -370,7 +370,6 @@ def _find_available_port(self, max_attempts=5): @property def vscode_url(self) -> str | None: token = super().get_vscode_token() - print('got token', token) if not token: return None vscode_url = f'http://localhost:{self._host_port + 1}/?tkn={token}&folder={self.config.workspace_mount_path_in_sandbox}' diff --git a/openhands/server/routes/new_conversation.py b/openhands/server/routes/new_conversation.py index b1dd75211205..6b16698d3a73 100644 --- a/openhands/server/routes/new_conversation.py +++ b/openhands/server/routes/new_conversation.py @@ -6,10 +6,10 @@ from pydantic import BaseModel from openhands.core.logger import openhands_logger as logger +from openhands.server.data_models.conversation_metadata import ConversationMetadata from openhands.server.routes.settings import ConversationStoreImpl, SettingsStoreImpl from openhands.server.session.conversation_init_data import ConversationInitData from openhands.server.shared import config, session_manager -from openhands.server.data_models.conversation_metadata import ConversationMetadata from openhands.utils.async_utils import call_sync_from_async app = APIRouter(prefix='/api') @@ -38,9 +38,6 @@ async def new_conversation(request: Request, data: InitSessionRequest): session_init_args: dict = {} if settings: session_init_args = {**settings.__dict__, **session_init_args} - if data.args: - for key, value in data.args.items(): - session_init_args[key.lower()] = value session_init_args['github_token'] = github_token session_init_args['selected_repository'] = data.selected_repository diff --git a/openhands/server/routes/settings.py b/openhands/server/routes/settings.py index 456bb2a87377..81637e8e45ef 100644 --- a/openhands/server/routes/settings.py +++ b/openhands/server/routes/settings.py @@ -27,9 +27,14 @@ async def load_settings( try: settings_store = await SettingsStoreImpl.get_instance(config, github_token) settings = await settings_store.load() - if settings: - # For security reasons we don't ever send the api key to the client - settings.llm_api_key = 'SET' if settings.llm_api_key else None + if not settings: + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={'error': 'Settings not found'}, + ) + + # For security reasons we don't ever send the api key to the client + settings.llm_api_key = 'SET' if settings.llm_api_key else None return settings except Exception as e: logger.warning(f'Invalid token: {e}') @@ -50,14 +55,13 @@ async def store_settings( try: settings_store = await SettingsStoreImpl.get_instance(config, github_token) existing_settings = await settings_store.load() + if existing_settings: - # Only update settings that are not None with the new values - for key, value in settings.__dict__.items(): - if value is None: - setattr(settings, key, getattr(existing_settings, key)) + # LLM key isn't on the frontend, so we need to keep it if unset if settings.llm_api_key is None: settings.llm_api_key = existing_settings.llm_api_key await settings_store.store(settings) + return JSONResponse( status_code=status.HTTP_200_OK, content={'message': 'Settings stored'}, diff --git a/openhands/server/session/session.py b/openhands/server/session/session.py index da412a435c40..2cba6657057e 100644 --- a/openhands/server/session/session.py +++ b/openhands/server/session/session.py @@ -85,11 +85,9 @@ async def initialize_agent( # override default LLM config default_llm_config = self.config.get_llm_config() - default_llm_config.model = settings.llm_model or default_llm_config.model - default_llm_config.api_key = settings.llm_api_key or default_llm_config.api_key - default_llm_config.base_url = ( - settings.llm_base_url or default_llm_config.base_url - ) + default_llm_config.model = settings.llm_model or '' + default_llm_config.api_key = settings.llm_api_key + default_llm_config.base_url = settings.llm_base_url # TODO: override other LLM config & agent config groups (#2075) diff --git a/poetry.lock b/poetry.lock index 2cd8ae911b76..88d69f2c3725 100644 --- a/poetry.lock +++ b/poetry.lock @@ -941,13 +941,13 @@ numpy = "*" [[package]] name = "chromadb" -version = "0.5.23" +version = "0.6.0" description = "Chroma." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "chromadb-0.5.23-py3-none-any.whl", hash = "sha256:ffe5bdd7276d12cb682df0d38a13aa37573e6a3678e71889ac45f539ae05ad7e"}, - {file = "chromadb-0.5.23.tar.gz", hash = "sha256:360a12b9795c5a33cb1f839d14410ccbde662ef1accd36153b0ae22312edabd1"}, + {file = "chromadb-0.6.0-py3-none-any.whl", hash = "sha256:02e2c07acfc22dd5fe33cc48b89e37fbf407892f0658d534a8f94187083d7457"}, + {file = "chromadb-0.6.0.tar.gz", hash = "sha256:8f72dc9bf0ed2c6358e46a80f31c39199cdcea39b0714e67b91f13acb64251ce"}, ] [package.dependencies] @@ -974,7 +974,7 @@ pypika = ">=0.48.9" PyYAML = ">=6.0.0" rich = ">=10.11.0" tenacity = ">=8.2.3" -tokenizers = ">=0.13.2,<=0.20.3" +tokenizers = ">=0.13.2" tqdm = ">=4.65.0" typer = ">=0.9.0" typing_extensions = ">=4.5.0"