Skip to content

Commit

Permalink
Merge branch 'main' into kevin
Browse files Browse the repository at this point in the history
  • Loading branch information
SmartManoj committed Feb 1, 2025
2 parents 953bda0 + 19e0c32 commit 47a4071
Show file tree
Hide file tree
Showing 21 changed files with 745 additions and 141 deletions.
1 change: 1 addition & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ jobs:
close-issue-message: 'This issue was closed because it has been stalled for over 30 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for over 30 days with no activity.'
days-before-close: 7
operations-per-run: 150
20 changes: 17 additions & 3 deletions frontend/__tests__/components/features/sidebar/sidebar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "test-utils";
import { createRoutesStub } from "react-router";
import { AxiosError } from "axios";
import { Sidebar } from "#/components/features/sidebar/sidebar";
import OpenHands from "#/api/open-hands";

Expand Down Expand Up @@ -153,11 +154,24 @@ describe("Sidebar", () => {
expect(settingsModal).toBeInTheDocument();
});

it("should open the settings modal if GET /settings fails", async () => {
vi.spyOn(OpenHands, "getSettings").mockRejectedValue(
new Error("Failed to fetch settings"),
it("should open the settings modal if GET /settings fails with a 404", async () => {
const error = new AxiosError(
"Request failed with status code 404",
"ERR_BAD_REQUEST",
undefined,
undefined,
{
status: 404,
statusText: "Not Found",
data: { message: "Settings not found" },
headers: {},
// @ts-expect-error - we only need the response object for this test
config: {},
},
);

vi.spyOn(OpenHands, "getSettings").mockRejectedValue(error);

renderSidebar();

const settingsModal = await screen.findByTestId("ai-config-modal");
Expand Down
19 changes: 17 additions & 2 deletions frontend/src/components/features/sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { FaListUl } from "react-icons/fa";
import { useDispatch } from "react-redux";
import posthog from "posthog-js";
import toast from "react-hot-toast";
import { useGitHubUser } from "#/hooks/query/use-github-user";
import { UserActions } from "./user-actions";
import { AllHandsLogoButton } from "#/components/shared/buttons/all-hands-logo-button";
Expand All @@ -28,7 +29,11 @@ export function Sidebar() {
const endSession = useEndSession();
const user = useGitHubUser();
const { data: config } = useConfig();
const { data: settings, isError: settingsError } = useSettings();
const {
data: settings,
error: settingsError,
isFetching: isFetchingSettings,
} = useSettings();
const { mutateAsync: logout } = useLogout();
const { saveUserSettings } = useCurrentSettings();

Expand All @@ -47,6 +52,16 @@ export function Sidebar() {
}
}, [user.isError]);

React.useEffect(() => {
// We don't show toast errors for settings in the global error handler
// because we have a special case for 404 errors
if (!isFetchingSettings && settingsError?.status !== 404) {
toast.error(
"Something went wrong while fetching settings. Please reload the page.",
);
}
}, [settingsError?.status, isFetchingSettings]);

const handleEndSession = () => {
dispatch(setCurrentAgentState(AgentState.LOADING));
endSession();
Expand Down Expand Up @@ -106,7 +121,7 @@ export function Sidebar() {
{accountSettingsModalOpen && (
<AccountSettingsModal onClose={handleAccountSettingsModalClose} />
)}
{(settingsError || settingsModalIsOpen) && (
{(settingsError?.status === 404 || settingsModalIsOpen) && (
<SettingsModal
settings={settings}
onClose={() => setSettingsModalIsOpen(false)}
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/hooks/query/use-active-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export const useActiveHost = () => {
},
enabled: !RUNTIME_INACTIVE_STATES.includes(curAgentState),
initialData: { hosts: [] },
meta: {
disableToast: true,
},
});

const apps = useQueries({
Expand All @@ -37,6 +40,9 @@ export const useActiveHost = () => {
}
},
refetchInterval: 3000,
meta: {
disableToast: true,
},
})),
});

Expand Down
3 changes: 3 additions & 0 deletions frontend/src/hooks/query/use-is-authed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ export const useIsAuthed = () => {
enabled: !!appMode,
staleTime: 1000 * 60 * 5, // 5 minutes
retry: false,
meta: {
disableToast: true,
},
});
};
3 changes: 3 additions & 0 deletions frontend/src/hooks/query/use-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export const useSettings = () => {
initialData: DEFAULT_SETTINGS,
staleTime: 0,
retry: false,
meta: {
disableToast: true,
},
});

React.useEffect(() => {
Expand Down
22 changes: 16 additions & 6 deletions frontend/src/query-client-config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { QueryClientConfig, QueryCache } from "@tanstack/react-query";
import { renderToastIfError } from "./utils/render-toast-if-error";

const QUERY_KEYS_TO_IGNORE = ["authenticated", "hosts", "settings"];
import toast from "react-hot-toast";
import { retrieveAxiosErrorMessage } from "./utils/retrieve-axios-error-message";

const shownErrors = new Set<string>();
export const queryClientConfig: QueryClientConfig = {
queryCache: new QueryCache({
onError: (error, query) => {
if (!QUERY_KEYS_TO_IGNORE.some((key) => query.queryKey.includes(key))) {
renderToastIfError(error);
if (!query.meta?.disableToast) {
const errorMessage = retrieveAxiosErrorMessage(error);

if (!shownErrors.has(errorMessage)) {
toast.error(errorMessage || "An error occurred");
shownErrors.add(errorMessage);

setTimeout(() => {
shownErrors.delete(errorMessage);
}, 3000);
}
}
},
}),
Expand All @@ -18,7 +27,8 @@ export const queryClientConfig: QueryClientConfig = {
},
mutations: {
onError: (error) => {
renderToastIfError(error);
const message = retrieveAxiosErrorMessage(error);
toast.error(message);
},
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { AxiosError } from "axios";
import toast from "react-hot-toast";
import { isAxiosErrorWithResponse } from "./type-guards";

/**
* Renders a toast with the error message from an Axios error
* Retrieve the error message from an Axios error
* @param error The error to render a toast for
*/
export const renderToastIfError = (error: AxiosError) => {
export const retrieveAxiosErrorMessage = (error: AxiosError) => {
let errorMessage: string | null = null;

if (isAxiosErrorWithResponse(error) && error.response?.data.error) {
Expand All @@ -15,5 +14,5 @@ export const renderToastIfError = (error: AxiosError) => {
errorMessage = error.message;
}

toast.error(errorMessage || "An error occurred");
return errorMessage || "An error occurred";
};
8 changes: 6 additions & 2 deletions openhands/runtime/utils/runtime_templates/Dockerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ RUN if [ -z "${RELEASE_TAG}" ]; then \
tar -xzf ${RELEASE_TAG}-linux-${arch}.tar.gz && \
mv -f ${RELEASE_TAG}-linux-${arch} ${OPENVSCODE_SERVER_ROOT} && \
cp ${OPENVSCODE_SERVER_ROOT}/bin/remote-cli/openvscode-server ${OPENVSCODE_SERVER_ROOT}/bin/remote-cli/code && \
rm -f ${RELEASE_TAG}-linux-${arch}.tar.gz
rm -f ${RELEASE_TAG}-linux-${arch}.tar.gz && \
# Install our custom extension
mkdir -p ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world && \
cp -r /openhands/code/openhands/runtime/utils/vscode-extensions/hello-world/* ${OPENVSCODE_SERVER_ROOT}/extensions/openhands-hello-world/

{% endmacro %}

Expand Down Expand Up @@ -109,7 +112,6 @@ RUN \
# rather than the current OpenHands release

{{ setup_base_system() }}
{{ setup_vscode_server() }}

# Install micromamba
RUN mkdir -p /openhands/micromamba/bin && \
Expand Down Expand Up @@ -145,6 +147,8 @@ COPY ./code/pyproject.toml ./code/poetry.lock /openhands/code/
COPY ./code/openhands /openhands/code/openhands
RUN chmod a+rwx /openhands/code/openhands/__init__.py

{{ setup_vscode_server() }}

# ================================================================
# END: Build from versioned image
# ================================================================
Expand Down
16 changes: 16 additions & 0 deletions openhands/runtime/utils/vscode-extensions/hello-world/extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const vscode = require('vscode');

function activate(context) {
let disposable = vscode.commands.registerCommand('openhands-hello-world.helloWorld', function () {
vscode.window.showInformationMessage('Hello from OpenHands!');
});

context.subscriptions.push(disposable);
}

function deactivate() {}

module.exports = {
activate,
deactivate
}
23 changes: 23 additions & 0 deletions openhands/runtime/utils/vscode-extensions/hello-world/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "openhands-hello-world",
"displayName": "OpenHands Hello World",
"description": "A simple hello world extension for OpenHands",
"version": "0.0.1",
"publisher": "openhands",
"engines": {
"vscode": "^1.94.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:openhands-hello-world.helloWorld"
],
"main": "./extension.js",
"contributes": {
"commands": [{
"command": "openhands-hello-world.helloWorld",
"title": "Hello World from OpenHands"
}]
}
}
2 changes: 2 additions & 0 deletions openhands/server/config/server_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class ServerConfig(ServerConfigInterface):
)
conversation_manager_class: str = 'openhands.server.conversation_manager.standalone_conversation_manager.StandaloneConversationManager'

github_service_class: str = 'openhands.server.services.github_service.GitHubService'

def verify_config(self):
if self.config_cls:
raise ValueError('Unexpected config path provided')
Expand Down
2 changes: 1 addition & 1 deletion openhands/server/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ async def __call__(self, request: Request, call_next: Callable):
settings = await settings_store.load()

if settings and settings.github_token:
request.state.github_token = settings.github_token
request.state.github_token = settings.github_token.get_secret_value()
else:
request.state.github_token = None

Expand Down
Loading

0 comments on commit 47a4071

Please sign in to comment.