Fix state dir in docker mode (#5840)

This commit is contained in:
Robert Brennan
2024-12-26 17:42:04 -05:00
committed by GitHub
parent a021045dce
commit b34209c9a0
7 changed files with 70 additions and 63 deletions

View File

@@ -49,7 +49,7 @@ docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \ -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.17-nikolaik \
-e LOG_ALL_EVENTS=true \ -e LOG_ALL_EVENTS=true \
-v /var/run/docker.sock:/var/run/docker.sock \ -v /var/run/docker.sock:/var/run/docker.sock \
-v ~/.openhands-state:/home/openhands/.openhands-state \ -v ~/.openhands-state:/.openhands-state \
-p 3000:3000 \ -p 3000:3000 \
--add-host host.docker.internal:host-gateway \ --add-host host.docker.internal:host-gateway \
--name openhands-app \ --name openhands-app \

View File

@@ -43,7 +43,8 @@ ENV WORKSPACE_BASE=/opt/workspace_base
ENV OPENHANDS_BUILD_VERSION=$OPENHANDS_BUILD_VERSION ENV OPENHANDS_BUILD_VERSION=$OPENHANDS_BUILD_VERSION
ENV SANDBOX_USER_ID=0 ENV SANDBOX_USER_ID=0
ENV FILE_STORE=local ENV FILE_STORE=local
ENV FILE_STORE_PATH=~/.openhands-state ENV FILE_STORE_PATH=/.openhands-state
RUN mkdir -p $FILE_STORE_PATH
RUN mkdir -p $WORKSPACE_BASE RUN mkdir -p $WORKSPACE_BASE
RUN apt-get update -y \ RUN apt-get update -y \

View File

@@ -25,7 +25,6 @@ export function SecurityAnalyzerInput({
</label> </label>
<Autocomplete <Autocomplete
isDisabled={isDisabled} isDisabled={isDisabled}
isRequired
id="security-analyzer" id="security-analyzer"
name="security-analyzer" name="security-analyzer"
aria-label="Security Analyzer" aria-label="Security Analyzer"

View File

@@ -7,11 +7,7 @@ import { getDefaultSettings, Settings } from "#/services/settings";
import { extractModelAndProvider } from "#/utils/extract-model-and-provider"; import { extractModelAndProvider } from "#/utils/extract-model-and-provider";
import { DangerModal } from "../confirmation-modals/danger-modal"; import { DangerModal } from "../confirmation-modals/danger-modal";
import { I18nKey } from "#/i18n/declaration"; import { I18nKey } from "#/i18n/declaration";
import { import { extractSettings, saveSettingsView } from "#/utils/settings-utils";
extractSettings,
saveSettingsView,
updateSettingsVersion,
} from "#/utils/settings-utils";
import { useEndSession } from "#/hooks/use-end-session"; import { useEndSession } from "#/hooks/use-end-session";
import { useSettings } from "#/context/settings-context"; import { useSettings } from "#/context/settings-context";
import { ModalButton } from "../../buttons/modal-button"; import { ModalButton } from "../../buttons/modal-button";
@@ -24,7 +20,6 @@ import { CustomModelInput } from "../../inputs/custom-model-input";
import { SecurityAnalyzerInput } from "../../inputs/security-analyzers-input"; import { SecurityAnalyzerInput } from "../../inputs/security-analyzers-input";
import { ModalBackdrop } from "../modal-backdrop"; import { ModalBackdrop } from "../modal-backdrop";
import { ModelSelector } from "./model-selector"; import { ModelSelector } from "./model-selector";
import { useAuth } from "#/context/auth-context";
interface SettingsFormProps { interface SettingsFormProps {
disabled?: boolean; disabled?: boolean;
@@ -45,7 +40,6 @@ export function SettingsForm({
}: SettingsFormProps) { }: SettingsFormProps) {
const { saveSettings } = useSettings(); const { saveSettings } = useSettings();
const endSession = useEndSession(); const endSession = useEndSession();
const { logout } = useAuth();
const location = useLocation(); const location = useLocation();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -98,7 +92,6 @@ export function SettingsForm({
const newSettings = extractSettings(formData); const newSettings = extractSettings(formData);
saveSettingsView(isUsingAdvancedOptions ? "advanced" : "basic"); saveSettingsView(isUsingAdvancedOptions ? "advanced" : "basic");
updateSettingsVersion(logout);
saveSettings(newSettings); saveSettings(newSettings);
resetOngoingSession(); resetOngoingSession();

View File

@@ -5,6 +5,7 @@ import { useGitHubAuthUrl } from "#/hooks/use-github-auth-url";
import { useIsAuthed } from "#/hooks/query/use-is-authed"; import { useIsAuthed } from "#/hooks/query/use-is-authed";
import { useAuth } from "#/context/auth-context"; import { useAuth } from "#/context/auth-context";
import { useSettings } from "#/context/settings-context"; import { useSettings } from "#/context/settings-context";
import { updateSettingsVersion } from "#/utils/settings-utils";
import { useConfig } from "#/hooks/query/use-config"; import { useConfig } from "#/hooks/query/use-config";
import { Sidebar } from "#/components/features/sidebar/sidebar"; import { Sidebar } from "#/components/features/sidebar/sidebar";
import { WaitlistModal } from "#/components/features/waitlist/waitlist-modal"; import { WaitlistModal } from "#/components/features/waitlist/waitlist-modal";
@@ -45,6 +46,7 @@ export function ErrorBoundary() {
export default function MainApp() { export default function MainApp() {
const { gitHubToken } = useAuth(); const { gitHubToken } = useAuth();
const { settings } = useSettings(); const { settings } = useSettings();
const { logout } = useAuth();
const [consentFormIsOpen, setConsentFormIsOpen] = React.useState( const [consentFormIsOpen, setConsentFormIsOpen] = React.useState(
!localStorage.getItem("analytics-consent"), !localStorage.getItem("analytics-consent"),
@@ -65,6 +67,10 @@ export default function MainApp() {
} }
}, [settings.LANGUAGE]); }, [settings.LANGUAGE]);
React.useEffect(() => {
updateSettingsVersion(logout);
}, []);
const isInWaitlist = const isInWaitlist =
!isFetchingAuth && !isAuthed && config.data?.APP_MODE === "saas"; !isFetchingAuth && !isAuthed && config.data?.APP_MODE === "saas";

View File

@@ -1,6 +1,6 @@
import { openHands } from "#/api/open-hands-axios"; import { openHands } from "#/api/open-hands-axios";
export const LATEST_SETTINGS_VERSION = 4; export const LATEST_SETTINGS_VERSION = 5;
export type Settings = { export type Settings = {
LLM_MODEL: string; LLM_MODEL: string;
@@ -45,54 +45,8 @@ export const getCurrentSettingsVersion = () => {
export const settingsAreUpToDate = () => export const settingsAreUpToDate = () =>
getCurrentSettingsVersion() === LATEST_SETTINGS_VERSION; getCurrentSettingsVersion() === LATEST_SETTINGS_VERSION;
export const maybeMigrateSettings = (logout: () => void) => { // TODO: localStorage settings are deprecated. Remove this after 1/31/2025
// Sometimes we ship major changes, like a new default agent. export const getLocalStorageSettings = (): Settings => {
// In this case, we may want to override a previous choice made by the user.
const currentVersion = getCurrentSettingsVersion();
if (currentVersion < 1) {
localStorage.setItem("AGENT", DEFAULT_SETTINGS.AGENT);
}
if (currentVersion < 2) {
const customModel = localStorage.getItem("CUSTOM_LLM_MODEL");
if (customModel) {
localStorage.setItem("LLM_MODEL", customModel);
}
localStorage.removeItem("CUSTOM_LLM_MODEL");
localStorage.removeItem("USING_CUSTOM_MODEL");
}
if (currentVersion < 3) {
localStorage.removeItem("token");
}
if (currentVersion < 4) {
logout();
}
};
/**
* Get the default settings
*/
export const getDefaultSettings = (): Settings => DEFAULT_SETTINGS;
/**
* Get the settings from the server or use the default settings if not found
*/
export const getSettings = async (): Promise<Settings> => {
const { data: apiSettings } =
await openHands.get<ApiSettings>("/api/settings");
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: "",
};
}
const llmModel = localStorage.getItem("LLM_MODEL"); const llmModel = localStorage.getItem("LLM_MODEL");
const baseUrl = localStorage.getItem("LLM_BASE_URL"); const baseUrl = localStorage.getItem("LLM_BASE_URL");
const agent = localStorage.getItem("AGENT"); const agent = localStorage.getItem("AGENT");
@@ -133,7 +87,61 @@ export const saveSettings = async (
const { data } = await openHands.post("/api/settings", apiSettings); const { data } = await openHands.post("/api/settings", apiSettings);
return data; return data;
} catch (error) { } catch (error) {
console.error("Error saving settings:", error);
return false; return false;
} }
}; };
export const maybeMigrateSettings = async (logout: () => void) => {
// Sometimes we ship major changes, like a new default agent.
// In this case, we may want to override a previous choice made by the user.
const currentVersion = getCurrentSettingsVersion();
if (currentVersion < 1) {
localStorage.setItem("AGENT", DEFAULT_SETTINGS.AGENT);
}
if (currentVersion < 2) {
const customModel = localStorage.getItem("CUSTOM_LLM_MODEL");
if (customModel) {
localStorage.setItem("LLM_MODEL", customModel);
}
localStorage.removeItem("CUSTOM_LLM_MODEL");
localStorage.removeItem("USING_CUSTOM_MODEL");
}
if (currentVersion < 3) {
localStorage.removeItem("token");
}
if (currentVersion < 4) {
logout();
}
if (currentVersion < 5) {
const localSettings = getLocalStorageSettings();
await saveSettings(localSettings);
}
};
/**
* Get the default settings
*/
export const getDefaultSettings = (): Settings => DEFAULT_SETTINGS;
/**
* Get the settings from the server or use the default settings if not found
*/
export const getSettings = async (): Promise<Settings> => {
const { data: apiSettings } =
await openHands.get<ApiSettings>("/api/settings");
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: "",
};
}
return getLocalStorageSettings();
};

View File

@@ -82,9 +82,9 @@ const saveSettingsView = (view: "basic" | "advanced") => {
* Updates the settings version in local storage if the current settings are not up to date. * Updates the settings version in local storage if the current settings are not up to date.
* If the settings are outdated, it attempts to migrate them before updating the version. * If the settings are outdated, it attempts to migrate them before updating the version.
*/ */
const updateSettingsVersion = (logout: () => void) => { const updateSettingsVersion = async (logout: () => void) => {
if (!settingsAreUpToDate()) { if (!settingsAreUpToDate()) {
maybeMigrateSettings(logout); await maybeMigrateSettings(logout);
localStorage.setItem( localStorage.setItem(
"SETTINGS_VERSION", "SETTINGS_VERSION",
LATEST_SETTINGS_VERSION.toString(), LATEST_SETTINGS_VERSION.toString(),