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 Oct 29, 2024
2 parents 807e71d + 421b4c1 commit 67c57c8
Show file tree
Hide file tree
Showing 49 changed files with 1,362 additions and 1,392 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ endif
# Start frontend server
start-frontend:
@echo "$(YELLOW)Starting frontend...$(RESET)"
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST_PORT) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run $(mode) -- --port $(FRONTEND_PORT)
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST_PORT) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run $(mode) -- --port $(FRONTEND_PORT) --host $(BACKEND_HOST)

# check for Windows (non-callable)
_run_check:
Expand Down
6 changes: 3 additions & 3 deletions containers/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ if [[ -n "$org_name" ]]; then
DOCKER_ORG="$org_name"
fi

# If $DOCKER_IMAGE_HASH_TAG is set, add it to the tags
if [[ -n "$DOCKER_IMAGE_HASH_TAG" ]]; then
tags+=("$DOCKER_IMAGE_HASH_TAG")
# If $DOCKER_IMAGE_SOURCE_TAG is set, add it to the tags
if [[ -n "$DOCKER_IMAGE_SOURCE_TAG" ]]; then
tags+=("$DOCKER_IMAGE_SOURCE_TAG")
fi
# If $DOCKER_IMAGE_TAG is set, add it to the tags
if [[ -n "$DOCKER_IMAGE_TAG" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion containers/runtime/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ DOCKER_BASE_DIR="./containers/runtime"
DOCKER_IMAGE=runtime
# These variables will be appended by the runtime_build.py script
# DOCKER_IMAGE_TAG=
# DOCKER_IMAGE_HASH_TAG=
# DOCKER_IMAGE_SOURCE_TAG=
38 changes: 23 additions & 15 deletions docs/modules/usage/architecture/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,22 @@ Check out the [relevant code](https://github.com/All-Hands-AI/OpenHands/blob/mai

### Image Tagging System

OpenHands uses a dual-tagging system for its runtime images to balance reproducibility with flexibility.
OpenHands uses a three-tag system for its runtime images to balance reproducibility with flexibility.
Tags may be in one of 2 formats:

- **Generic**: `oh_v{openhands_version}_{16_digit_lock_hash}` (e.g.: `oh_v0.9.9_1234567890abcdef`)
- **Specific**: `oh_v{openhands_version}_{16_digit_lock_hash}_{16_digit_source_hash}`
- **Versioned Tag**: `oh_v{openhands_version}_{base_image}` (e.g.: `oh_v0.9.9_nikolaik_s_python-nodejs_t_python3.12-nodejs22`)
- **Lock Tag**: `oh_v{openhands_version}_{16_digit_lock_hash}` (e.g.: `oh_v0.9.9_1234567890abcdef`)
- **Source Tag**: `oh_v{openhands_version}_{16_digit_lock_hash}_{16_digit_source_hash}`
(e.g.: `oh_v0.9.9_1234567890abcdef_1234567890abcdef`)

#### Lock Hash

#### Source Tag - Most Specific

This is the first 16 digits of the MD5 of the directory hash for the source directory. This gives a hash
for only the openhands source


#### Lock Tag

This hash is built from the first 16 digits of the MD5 of:
- The name of the base image upon which the image was built (e.g.: `nikolaik/python-nodejs:python3.12-nodejs22`)
Expand All @@ -86,30 +94,30 @@ This hash is built from the first 16 digits of the MD5 of:

This effectively gives a hash for the dependencies of Openhands independent of the source code.

#### Source Hash
#### Versioned Tag - Most Generic

This is the first 16 digits of the MD5 of the directory hash for the source directory. This gives a hash
for only the openhands source
This tag is a concatenation of openhands version and the base image name (transformed to fit in tag standard).

#### Build Process

When generating an image...

- OpenHands first checks whether an image with the same **Specific** tag exists. If there is such an image,
- **No re-build**: OpenHands first checks whether an image with the same **most specific source tag** exists. If there is such an image,
no build is performed - the existing image is used.
- OpenHands next checks whether an image with the **Generic** tag exists. If there is such an image,
- **Fastest re-build**: OpenHands next checks whether an image with the **generic lock tag** exists. If there is such an image,
OpenHands builds a new image based upon it, bypassing all installation steps (like `poetry install` and
`apt-get`) except a final operation to copy the current source code. The new image is tagged with a
**Specific** tag only.
- If neither a **Specific** nor **Generic** tag exists, a brand new image is built based upon the base
image (Which is a slower operation). This new image is tagged with both the **Generic** and **Specific**
tags.
**source** tag only.
- **Ok-ish re-build**: If neither a **source** nor **lock** tag exists, an image will be built based upon the **versioned** tag image.
In versioned tag image, most dependencies should already been installed hence saving time.
- **Slowest re-build**: If all of the three tags don't exists, a brand new image is built based upon the base
image (Which is a slower operation). This new image is tagged with all the **source**, **lock**, and **versioned** tags.

This dual-tagging approach allows OpenHands to efficiently manage both development and production environments.
This tagging approach allows OpenHands to efficiently manage both development and production environments.

1. Identical source code and Dockerfile always produce the same image (via hash-based tags)
2. The system can quickly rebuild images when minor changes occur (by leveraging recent compatible images)
3. The generic tag (e.g., `runtime:oh_v0.9.3_1234567890abcdef`) always points to the latest build for a particular base image and OpenHands version combination
3. The **lock** tag (e.g., `runtime:oh_v0.9.3_1234567890abcdef`) always points to the latest build for a particular base image, dependency, and OpenHands version combination

## Runtime Plugin System

Expand Down
73 changes: 73 additions & 0 deletions frontend/__tests__/components/chat-message.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, test } from "vitest";
import { ChatMessage } from "#/components/chat-message";

describe("ChatMessage", () => {
it("should render a user message", () => {
render(<ChatMessage type="user" message="Hello, World!" />);
expect(screen.getByTestId("user-message")).toBeInTheDocument();
expect(screen.getByText("Hello, World!")).toBeInTheDocument();
});

it("should render an assistant message", () => {
render(<ChatMessage type="assistant" message="Hello, World!" />);
expect(screen.getByTestId("assistant-message")).toBeInTheDocument();
expect(screen.getByText("Hello, World!")).toBeInTheDocument();
});

it.skip("should support code syntax highlighting", () => {
const code = "```js\nconsole.log('Hello, World!')\n```";
render(<ChatMessage type="user" message={code} />);

// SyntaxHighlighter breaks the code blocks into "tokens"
expect(screen.getByText("console")).toBeInTheDocument();
expect(screen.getByText("log")).toBeInTheDocument();
expect(screen.getByText("'Hello, World!'")).toBeInTheDocument();
});

it.todo("should support markdown content");

it("should render the copy to clipboard button when the user hovers over the message", async () => {
const user = userEvent.setup();
render(<ChatMessage type="user" message="Hello, World!" />);
const message = screen.getByText("Hello, World!");

expect(screen.getByTestId("copy-to-clipboard")).not.toBeVisible();

await user.hover(message);

expect(screen.getByTestId("copy-to-clipboard")).toBeVisible();
});

it("should copy content to clipboard", async () => {
const user = userEvent.setup();
render(<ChatMessage type="user" message="Hello, World!" />);
const copyToClipboardButton = screen.getByTestId("copy-to-clipboard");

await user.click(copyToClipboardButton);

expect(navigator.clipboard.readText()).resolves.toBe("Hello, World!");
});

// BUG: vi.useFakeTimers() seems to break the tests
it.todo(
"should display a checkmark for 200ms and disable the button after copying content to clipboard",
);

it("should display an error toast if copying content to clipboard fails", async () => {});

test.todo("push a toast after successfully copying content to clipboard");

it("should render a component passed as a prop", () => {
function Component() {
return <div data-testid="custom-component">Custom Component</div>;
}
render(
<ChatMessage type="user" message="Hello, World">
<Component />
</ChatMessage>,
);
expect(screen.getByTestId("custom-component")).toBeInTheDocument();
});
});
28 changes: 0 additions & 28 deletions frontend/__tests__/components/chat/Chat.test.tsx

This file was deleted.

151 changes: 0 additions & 151 deletions frontend/__tests__/components/chat/ChatInterface.test.tsx

This file was deleted.

Loading

0 comments on commit 67c57c8

Please sign in to comment.