Skip to content

Commit

Permalink
Merge branch 'main' into add-port-mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
amanape committed Dec 17, 2024
2 parents 291c496 + 7071742 commit 9cee2c3
Show file tree
Hide file tree
Showing 31 changed files with 786 additions and 568 deletions.
37 changes: 26 additions & 11 deletions .github/workflows/openhands-resolver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,32 @@ jobs:
});
- name: Install OpenHands
run: |
if [[ "${{ github.event.label.name }}" == "fix-me-experimental" ]] ||
([[ "${{ github.event_name }}" == "issue_comment" || "${{ github.event_name }}" == "pull_request_review_comment" ]] &&
[[ "${{ github.event.comment.body }}" == "@openhands-agent-exp"* ]]) ||
([[ "${{ github.event_name }}" == "pull_request_review" ]] &&
[[ "${{ github.event.review.body }}" == "@openhands-agent-exp"* ]]); then
python -m pip install --upgrade pip
pip install git+https://github.com/all-hands-ai/openhands.git
else
python -m pip install --upgrade -r requirements.txt
fi
uses: actions/github-script@v7
with:
script: |
const commentBody = `${{ github.event.comment.body || '' }}`.trim();
const reviewBody = `${{ github.event.review.body || '' }}`.trim();
const labelName = `${{ github.event.label.name || '' }}`.trim();
const eventName = `${{ github.event_name }}`.trim();
// Check conditions
const isExperimentalLabel = labelName === "fix-me-experimental";
const isIssueCommentExperimental =
(eventName === "issue_comment" || eventName === "pull_request_review_comment") &&
commentBody.includes("@openhands-agent-exp");
const isReviewCommentExperimental =
eventName === "pull_request_review" && reviewBody.includes("@openhands-agent-exp");
// Perform package installation
if (isExperimentalLabel || isIssueCommentExperimental || isReviewCommentExperimental) {
console.log("Installing experimental OpenHands...");
await exec.exec("python -m pip install --upgrade pip");
await exec.exec("pip install git+https://github.com/all-hands-ai/openhands.git");
} else {
console.log("Installing from requirements.txt...");
await exec.exec("python -m pip install --upgrade pip");
await exec.exec("pip install -r requirements.txt");
}
- name: Attempt to resolve issue
env:
Expand Down
4 changes: 2 additions & 2 deletions docs/modules/usage/micro-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Micro-agents are defined in markdown files under the `openhands/agenthub/codeact
## Available Micro-Agents

### GitHub Agent
**File**: `github.md`
**File**: `github.md`
**Triggers**: `github`, `git`

The GitHub agent specializes in GitHub API interactions and repository management. It:
Expand All @@ -30,7 +30,7 @@ Key features:
- API-first approach for GitHub operations

### NPM Agent
**File**: `npm.md`
**File**: `npm.md`
**Triggers**: `npm`

Specializes in handling npm package management with specific focus on:
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/usage/prompting-best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ When working with a customized repository:

Example customized prompt:
```
Add a new task completion feature to src/components/TaskList.tsx following our existing component patterns.
Add a new task completion feature to src/components/TaskList.tsx following our existing component patterns.
Include unit tests in tests/components/ and update the documentation in docs/features/.
The component should use our shared styling from src/styles/components.
```
Expand Down
3 changes: 1 addition & 2 deletions evaluation/benchmarks/swe_bench/run_infer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from datasets import load_dataset

import openhands.agenthub

from evaluation.utils.shared import (
EvalException,
EvalMetadata,
Expand Down Expand Up @@ -76,7 +75,7 @@ def get_instruction(instance: pd.Series, metadata: EvalMetadata):
'4. Rerun your reproduce script and confirm that the error is fixed!\n'
'5. Think about edgecases and make sure your fix handles them as well\n'
"Your thinking should be thorough and so it's fine if it's very long.\n"
)
)

if RUN_WITH_BROWSING:
instruction += (
Expand Down
16 changes: 16 additions & 0 deletions frontend/__tests__/components/chat/chat-input.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ describe("ChatInput", () => {
expect(onSubmitMock).not.toHaveBeenCalled();
});

it("should not call onSubmit when the message is only whitespace", async () => {
const user = userEvent.setup();
render(<ChatInput onSubmit={onSubmitMock} />);
const textarea = screen.getByRole("textbox");

await user.type(textarea, " ");
await user.keyboard("{Enter}");

expect(onSubmitMock).not.toHaveBeenCalled();

await user.type(textarea, " \t\n");
await user.keyboard("{Enter}");

expect(onSubmitMock).not.toHaveBeenCalled();
});

it("should disable submit", async () => {
const user = userEvent.setup();
render(<ChatInput disabled onSubmit={onSubmitMock} />);
Expand Down
45 changes: 45 additions & 0 deletions frontend/__tests__/components/jupyter/jupyter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { render, screen } from "@testing-library/react";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import { JupyterEditor } from "#/components/features/jupyter/jupyter";
import { jupyterReducer } from "#/state/jupyter-slice";
import { vi, describe, it, expect } from "vitest";

describe("JupyterEditor", () => {
const mockStore = configureStore({
reducer: {
fileState: () => ({}),
initalQuery: () => ({}),
browser: () => ({}),
chat: () => ({}),
code: () => ({}),
cmd: () => ({}),
agent: () => ({}),
jupyter: jupyterReducer,
securityAnalyzer: () => ({}),
status: () => ({}),
},
preloadedState: {
jupyter: {
cells: Array(20).fill({
content: "Test cell content",
type: "input",
output: "Test output",
}),
},
},
});

it("should have a scrollable container", () => {
render(
<Provider store={mockStore}>
<div style={{ height: "100vh" }}>
<JupyterEditor maxWidth={800} />
</div>
</Provider>
);

const container = screen.getByTestId("jupyter-container");
expect(container).toHaveClass("flex-1 overflow-y-auto");
});
});
2 changes: 1 addition & 1 deletion frontend/src/components/features/chat/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function ChatInput({

const handleSubmitMessage = () => {
const message = value || textareaRef.current?.value || "";
if (message) {
if (message.trim()) {
onSubmit(message);
onChange?.("");
if (textareaRef.current) {
Expand Down
14 changes: 9 additions & 5 deletions frontend/src/components/features/file-explorer/file-explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ import { FileExplorerHeader } from "./file-explorer-header";
import { useVSCodeUrl } from "#/hooks/query/use-vscode-url";
import { OpenVSCodeButton } from "#/components/shared/buttons/open-vscode-button";
import { addAssistantMessage } from "#/state/chat-slice";
import {
useWsClient,
WsClientProviderStatus,
} from "#/context/ws-client-provider";

interface FileExplorerProps {
isOpen: boolean;
onToggle: () => void;
}

export function FileExplorer({ isOpen, onToggle }: FileExplorerProps) {
const { status } = useWsClient();
const { t } = useTranslation();
const dispatch = useDispatch();

Expand All @@ -30,12 +35,11 @@ export function FileExplorer({ isOpen, onToggle }: FileExplorerProps) {

const { curAgentState } = useSelector((state: RootState) => state.agent);

const agentIsReady =
curAgentState !== AgentState.INIT && curAgentState !== AgentState.LOADING;

const { data: paths, refetch, error } = useListFiles();
const { mutate: uploadFiles } = useUploadFiles();
const { data: vscodeUrl } = useVSCodeUrl({ enabled: agentIsReady });
const { data: vscodeUrl } = useVSCodeUrl({
enabled: status === WsClientProviderStatus.ACTIVE,
});

const handleOpenVSCode = () => {
if (vscodeUrl?.vscode_url) {
Expand Down Expand Up @@ -166,7 +170,7 @@ export function FileExplorer({ isOpen, onToggle }: FileExplorerProps) {
{isOpen && (
<OpenVSCodeButton
onClick={handleOpenVSCode}
isDisabled={!agentIsReady}
isDisabled={status === WsClientProviderStatus.OPENING}
/>
)}
</div>
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/components/features/jupyter/jupyter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ interface JupyterEditorProps {
}

export function JupyterEditor({ maxWidth }: JupyterEditorProps) {
const { cells } = useSelector((state: RootState) => state.jupyter);
const cells = useSelector((state: RootState) => state.jupyter?.cells ?? []);
const jupyterRef = React.useRef<HTMLDivElement>(null);

const { hitBottom, scrollDomToBottom, onChatBodyScroll } =
useScrollToBottom(jupyterRef);

return (
<div className="flex-1" style={{ maxWidth }}>
<div className="flex-1 h-full flex flex-col" style={{ maxWidth }}>
<div
className="overflow-y-auto h-full"
data-testid="jupyter-container"
className="flex-1 overflow-y-auto"
ref={jupyterRef}
onScroll={(e) => onChatBodyScroll(e.currentTarget)}
>
Expand Down
29 changes: 24 additions & 5 deletions frontend/src/components/features/markdown/code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,38 @@ export function code({
const match = /language-(\w+)/.exec(className || ""); // get the language

if (!match) {
const isMultiline = String(children).includes("\n");

if (!isMultiline) {
return (
<code
className={className}
style={{
backgroundColor: "#2a3038",
padding: "0.2em 0.4em",
borderRadius: "4px",
color: "#e6edf3",
border: "1px solid #30363d",
}}
>
{children}
</code>
);
}

return (
<code
className={className}
<pre
style={{
backgroundColor: "#2a3038",
padding: "0.2em 0.4em",
padding: "1em",
borderRadius: "4px",
color: "#e6edf3",
border: "1px solid #30363d",
overflow: "auto",
}}
>
{children}
</code>
<code className={className}>{String(children).replace(/\n$/, "")}</code>
</pre>
);
}

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/context/ws-client-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { io, Socket } from "socket.io-client";
import { Settings } from "#/services/settings";
import ActionType from "#/types/action-type";
import EventLogger from "#/utils/event-logger";
import AgentState from "#/types/agent-state";
import { handleAssistantMessage } from "#/services/actions";
import { useRate } from "#/hooks/use-rate";
import AgentState from "#/types/agent-state";

const isOpenHandsMessage = (event: Record<string, unknown>) =>
event.action === "message";
Expand Down Expand Up @@ -102,10 +102,12 @@ export function WsClientProvider({
if (!Number.isNaN(parseInt(event.id as string, 10))) {
lastEventRef.current = event;
}

const extras = event.extras as Record<string, unknown>;
if (extras?.agent_state === AgentState.INIT) {
setStatus(WsClientProviderStatus.ACTIVE);
}

if (
status !== WsClientProviderStatus.ACTIVE &&
event?.observation === "error"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/_oh.app.jupyter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function Jupyter() {
}, []);

return (
<div ref={parentRef}>
<div ref={parentRef} className="h-full">
<JupyterEditor maxWidth={parentWidth} />
</div>
);
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/state/jupyter-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export type Cell = {

const initialCells: Cell[] = [];

export const cellSlice = createSlice({
name: "cell",
export const jupyterSlice = createSlice({
name: "jupyter",
initialState: {
cells: initialCells,
},
Expand All @@ -26,6 +26,7 @@ export const cellSlice = createSlice({
});

export const { appendJupyterInput, appendJupyterOutput, clearJupyter } =
cellSlice.actions;
jupyterSlice.actions;

export default cellSlice.reducer;
export const jupyterReducer = jupyterSlice.reducer;
export default jupyterReducer;
2 changes: 1 addition & 1 deletion frontend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import codeReducer from "./state/code-slice";
import fileStateReducer from "./state/file-state-slice";
import initialQueryReducer from "./state/initial-query-slice";
import commandReducer from "./state/command-slice";
import jupyterReducer from "./state/jupyter-slice";
import { jupyterReducer } from "./state/jupyter-slice";
import securityAnalyzerReducer from "./state/security-analyzer-slice";
import statusReducer from "./state/status-slice";

Expand Down
4 changes: 3 additions & 1 deletion openhands/agenthub/codeact_agent/codeact_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ def get_action_message(

# Add the LLM message (assistant) that initiated the tool calls
# (overwrites any previous message with the same response_id)
logger.debug(f'Tool calls type: {type(assistant_msg.tool_calls)}, value: {assistant_msg.tool_calls}')
logger.debug(
f'Tool calls type: {type(assistant_msg.tool_calls)}, value: {assistant_msg.tool_calls}'
)
pending_tool_call_action_messages[llm_response.id] = Message(
role=assistant_msg.role,
# tool call content SHOULD BE a string
Expand Down
Loading

0 comments on commit 9cee2c3

Please sign in to comment.