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 Mar 1, 2025
2 parents 58d0661 + 5378932 commit 84836d4
Show file tree
Hide file tree
Showing 29 changed files with 331 additions and 46 deletions.
5 changes: 5 additions & 0 deletions docs/modules/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ To use these with the docker command, pass in `-e SANDBOX_<option>`. Example: `-
- Default: `false`
- Description: Use host network

- `runtime_binding_address`
- Type: `str`
- Default: `127.0.0.1`
- Description: The binding address for the runtime ports. It specifies which network interface on the host machine Docker should bind the runtime ports to.

### Linting and Plugins
- `enable_auto_lint`
- Type: `bool`
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/open-hands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class OpenHands {
code: string,
): Promise<GitHubAccessTokenResponse> {
const { data } = await openHands.post<GitHubAccessTokenResponse>(
"/api/github/callback",
"/api/keycloak/callback",
{
code,
},
Expand Down
7 changes: 3 additions & 4 deletions frontend/src/i18n/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ export enum I18nKey {
STATUS$ERROR_LLM_AUTHENTICATION = "STATUS$ERROR_LLM_AUTHENTICATION",
STATUS$ERROR_LLM_SERVICE_UNAVAILABLE = "STATUS$ERROR_LLM_SERVICE_UNAVAILABLE",
STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR = "STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR",
STATUS$ERROR_LLM_OUT_OF_CREDITS = "STATUS$ERROR_LLM_OUT_OF_CREDITS",
STATUS$ERROR_RUNTIME_DISCONNECTED = "STATUS$ERROR_RUNTIME_DISCONNECTED",
STATUS$LLM_RETRY = "STATUS$LLM_RETRY",
AGENT_ERROR$BAD_ACTION = "AGENT_ERROR$BAD_ACTION",
Expand All @@ -279,6 +280,7 @@ export enum I18nKey {
ACTION_MESSAGE$EDIT = "ACTION_MESSAGE$EDIT",
ACTION_MESSAGE$WRITE = "ACTION_MESSAGE$WRITE",
ACTION_MESSAGE$BROWSE = "ACTION_MESSAGE$BROWSE",
ACTION_MESSAGE$THINK = "ACTION_MESSAGE$THINK",
OBSERVATION_MESSAGE$RUN = "OBSERVATION_MESSAGE$RUN",
OBSERVATION_MESSAGE$RUN_IPYTHON = "OBSERVATION_MESSAGE$RUN_IPYTHON",
OBSERVATION_MESSAGE$READ = "OBSERVATION_MESSAGE$READ",
Expand Down Expand Up @@ -306,11 +308,8 @@ export enum I18nKey {
STATUS$WAITING_FOR_CLIENT = "STATUS$WAITING_FOR_CLIENT",
SUGGESTIONS$WHAT_TO_BUILD = "SUGGESTIONS$WHAT_TO_BUILD",
SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL = "SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL",
BUTTON$ENABLE_SOUND = "BUTTON$ENABLE_SOUND",
BUTTON$DISABLE_SOUND = "BUTTON$DISABLE_SOUND",
BUTTON$MARK_HELPFUL = "BUTTON$MARK_HELPFUL",
BUTTON$MARK_NOT_HELPFUL = "BUTTON$MARK_NOT_HELPFUL",
NOTIFICATION$SOUND_ENABLED = "NOTIFICATION$SOUND_ENABLED",
NOTIFICATION$SOUND_DISABLED = "NOTIFICATION$SOUND_DISABLED",
BUTTON$EXPORT_CONVERSATION = "BUTTON$EXPORT_CONVERSATION",
BILLING$CLICK_TO_TOP_UP = "BILLING$CLICK_TO_TOP_UP",
}
22 changes: 22 additions & 0 deletions frontend/src/services/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ const messageActions = {
store.dispatch(appendJupyterInput(message.args.code));
}
},
[ActionType.FINISH]: (message: ActionMessage) => {
store.dispatch(addAssistantMessage(message.args.final_thought));
let successPrediction = "";
if (message.args.task_completed === "partial") {
successPrediction =
"The agent thinks that the task was **completed partially**.";
} else if (message.args.task_completed === "false") {
successPrediction =
"The agent thinks that the task was **not completed**.";
} else if (message.args.task_completed === "true") {
successPrediction =
"The agent thinks that the task was **completed successfully**.";
}
if (successPrediction) {
// if final_thought is not empty, add a new line before the success prediction
if (message.args.final_thought) {
store.dispatch(addAssistantMessage(`\n${successPrediction}`));
} else {
store.dispatch(addAssistantMessage(successPrediction));
}
}
},
};

export function handleActionMessage(message: ActionMessage) {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/services/observations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,24 @@ export function handleObservationMessage(message: ObservationMessage) {
);
break;
case "read":
store.dispatch(
addAssistantObservation({
...baseObservation,
observation,
extras: {
path: String(message.extras.path || ""),
},
}),
);
break;
case "edit":
store.dispatch(
addAssistantObservation({
...baseObservation,
observation,
extras: {
path: String(message.extras.path || ""),
diff: String(message.extras.diff || ""),
},
}),
);
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/state/chat-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,14 @@ export const chatSlice = createSlice({
causeMessage.content
}\n\nOutput:\n\`\`\`\n${content.trim() || "[Command finished execution with no output]"}\n\`\`\``;
causeMessage.content = content; // Observation content includes the action
} else if (observationID === "read" || observationID === "edit") {
const { content } = observation.payload;
causeMessage.content = `\`\`\`${observationID === "edit" ? "diff" : "python"}\n${content}\n\`\`\``; // Content is already truncated by the ACI
} else if (observationID === "read") {
causeMessage.content = `\`\`\`\n${observation.payload.content}\n\`\`\``; // Content is already truncated by the ACI
} else if (observationID === "edit") {
if (causeMessage.success) {
causeMessage.content = `\`\`\`diff\n${observation.payload.extras.diff}\n\`\`\``; // Content is already truncated by the ACI
} else {
causeMessage.content = observation.payload.content;
}
} else if (observationID === "browse") {
let content = `**URL:** ${observation.payload.extras.url}\n`;
if (observation.payload.extras.error) {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/types/core/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export interface ThinkAction extends OpenHandsActionEvent<"think"> {
export interface FinishAction extends OpenHandsActionEvent<"finish"> {
source: "agent";
args: {
final_thought: string;
task_completed: "success" | "failure" | "partial";
outputs: Record<string, unknown>;
thought: string;
};
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types/core/observations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface EditObservation extends OpenHandsObservationEvent<"edit"> {
source: "agent";
extras: {
path: string;
diff: string;
};
}

Expand Down
56 changes: 56 additions & 0 deletions microagents/knowledge/docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
name: docker
type: knowledge
version: 1.0.0
agent: CodeActAgent
triggers:
- docker
- container
---

# Docker Installation and Usage Guide

## Installation on Debian/Ubuntu Systems

To install Docker on a Debian/Ubuntu system, follow these steps:

```bash
# Update package index
sudo apt-get update

# Install prerequisites
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release

# Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Set up the stable repository
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Update package index again
sudo apt-get update

# Install Docker Engine
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
```

## Starting Docker in Container Environments

If you're in a container environment without systemd (like this workspace), start Docker with:

```bash
# Start Docker daemon in the background
sudo dockerd > /tmp/docker.log 2>&1 &

# Wait for Docker to initialize
sleep 5
```

## Verifying Docker Installation

To verify Docker is working correctly, run the hello-world container:

```bash
sudo docker run hello-world
```

50 changes: 50 additions & 0 deletions microagents/knowledge/kubernetes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
name: kubernetes
type: knowledge
version: 1.0.0
agent: CodeActAgent
triggers:
- kubernetes
- k8s
- kube
---

# Kubernetes Local Development with KIND

## KIND Installation and Setup

KIND (Kubernetes IN Docker) is a tool for running local Kubernetes clusters using Docker containers as nodes. It's designed for testing Kubernetes applications locally.

IMPORTANT: Before you proceed with installation, make sure you have docker installed locally.

### Installation

To install KIND on a Debian/Ubuntu system:

```bash
# Download KIND binary
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64
# Make it executable
chmod +x ./kind
# Move to a directory in your PATH
sudo mv ./kind /usr/local/bin/
```

To install kubectl:

```bash
# Download kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
# Make it executable
chmod +x kubectl
# Move to a directory in your PATH
sudo mv ./kubectl /usr/local/bin/
```

### Creating a Cluster

Create a basic KIND cluster:

```bash
kind create cluster
```
5 changes: 4 additions & 1 deletion openhands/agenthub/codeact_agent/function_calling.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ def response_to_actions(response: ModelResponse) -> list[Action]:
# AgentFinishAction
# ================================================
elif tool_call.function.name == FinishTool['function']['name']:
action = AgentFinishAction()
action = AgentFinishAction(
final_thought=arguments.get('message', ''),
task_completed=arguments.get('task_completed', None),
)

# ================================================
# LLMBasedFileEditTool (LLM-based file editor, deprecated)
Expand Down
30 changes: 29 additions & 1 deletion openhands/agenthub/codeact_agent/tools/finish.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,39 @@
from litellm import ChatCompletionToolParam, ChatCompletionToolParamFunctionChunk

_FINISH_DESCRIPTION = """Finish the interaction when the task is complete OR if the assistant cannot proceed further with the task."""
_FINISH_DESCRIPTION = """Signals the completion of the current task or conversation.
Use this tool when:
- You have successfully completed the user's requested task
- You cannot proceed further due to technical limitations or missing information
The message should include:
- A clear summary of actions taken and their results
- Any next steps for the user
- Explanation if you're unable to complete the task
- Any follow-up questions if more information is needed
The task_completed field should be set to True if you believed you have completed the task, and False otherwise.
"""

FinishTool = ChatCompletionToolParam(
type='function',
function=ChatCompletionToolParamFunctionChunk(
name='finish',
description=_FINISH_DESCRIPTION,
parameters={
'type': 'object',
'required': ['message', 'task_completed'],
'properties': {
'message': {
'type': 'string',
'description': 'Final message to send to the user',
},
'task_completed': {
'type': 'string',
'enum': ['true', 'false', 'partial'],
'description': 'Whether you have completed the task.',
},
},
},
),
)
3 changes: 3 additions & 0 deletions openhands/controller/agent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,10 +744,13 @@ async def _step(self) -> None:
except Exception as e:
# FIXME: this is a hack until a litellm fix is confirmed
# Check if this is a nested context window error
# We have to rely on string-matching because LiteLLM doesn't consistently
# wrap the failure in a ContextWindowExceededError
error_str = str(e).lower()
if (
'contextwindowexceedederror' in error_str
or 'prompt is too long' in error_str
or 'input length and `max_tokens` exceed context limit' in error_str
or isinstance(e, ContextWindowExceededError)
):
if self.agent.config.enable_history_truncation:
Expand Down
4 changes: 3 additions & 1 deletion openhands/core/config/sandbox_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SandboxConfig(BaseModel):
remote_runtime_api_timeout: The timeout for the remote runtime API requests.
enable_auto_lint: Whether to enable auto-lint.
use_host_network: Whether to use the host network.
runtime_binding_address: The binding address for the runtime ports. It specifies which network interface on the host machine Docker should bind the runtime ports to.
initialize_plugins: Whether to initialize plugins.
force_rebuild_runtime: Whether to force rebuild the runtime image.
runtime_extra_deps: The extra dependencies to install in the runtime image (typically used for evaluation).
Expand Down Expand Up @@ -65,6 +66,7 @@ class SandboxConfig(BaseModel):
default=False
) # once enabled, OpenHands would lint files after editing
use_host_network: bool = Field(default=False)
runtime_binding_address: str = Field(default='127.0.0.1')
runtime_extra_build_args: list[str] | None = Field(default=None)
initialize_plugins: bool = Field(default=True)
force_rebuild_runtime: bool = Field(default=False)
Expand All @@ -75,7 +77,7 @@ class SandboxConfig(BaseModel):
close_delay: int = Field(default=15)
remote_runtime_resource_factor: int = Field(default=1)
enable_gpu: bool = Field(default=False)
docker_runtime_kwargs: str | None = Field(default=None)
docker_runtime_kwargs: dict | None = Field(default=None)
selected_repo: str | None = Field(default=None)

# custom configs
Expand Down
13 changes: 12 additions & 1 deletion openhands/events/action/agent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass, field
from enum import Enum
from typing import Any

from openhands.core.schema import ActionType
Expand Down Expand Up @@ -44,16 +45,26 @@ def __str__(self) -> str:
return ret


class AgentFinishTaskCompleted(Enum):
FALSE = 'false'
PARTIAL = 'partial'
TRUE = 'true'


@dataclass
class AgentFinishAction(Action):
"""An action where the agent finishes the task.
Attributes:
outputs (dict): The outputs of the agent, for instance "content".
final_thought (str): The message to send to the user.
task_completed (enum): Whether the agent believes the task has been completed.
outputs (dict): The other outputs of the agent, for instance "content".
thought (str): The agent's explanation of its actions.
action (str): The action type, namely ActionType.FINISH.
"""

final_thought: str = ''
task_completed: AgentFinishTaskCompleted | None = None
outputs: dict[str, Any] = field(default_factory=dict)
thought: str = ''
action: str = ActionType.FINISH
Expand Down
9 changes: 7 additions & 2 deletions openhands/events/observation/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ class FileEditObservation(Observation):
new_content: str | None = None
observation: str = ObservationType.EDIT
impl_source: FileEditSource = FileEditSource.LLM_BASED_EDIT
_diff_cache: str | None = None # Cache for the diff visualization
diff: str | None = (
None # The raw diff between old and new content, used in OH_ACI mode
)
_diff_cache: str | None = (
None # Cache for the diff visualization, used in LLM-based editing mode
)

@property
def message(self) -> str:
Expand Down Expand Up @@ -126,7 +131,7 @@ def visualize_diff(
n_context_lines: int = 2,
change_applied: bool = True,
) -> str:
"""Visualize the diff of the file edit.
"""Visualize the diff of the file edit. Used in the LLM-based editing mode.
Instead of showing the diff line by line, this function shows each hunk
of changes as a separate entity.
Expand Down
Loading

0 comments on commit 84836d4

Please sign in to comment.