mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
Fix state dir in docker mode (#5840)
This commit is contained in:
@@ -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 \
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
};
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
Reference in New Issue
Block a user