mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a3354404e | |||
| 80279f9d36 | |||
| 0b3d15a4d7 | |||
| 8b68d086f0 | |||
| 0f143a43c9 | |||
| e61e4d57d9 | |||
| 4e86bdf3d9 | |||
| 3cef499b81 | |||
| e2a0884ecd | |||
| 2849974729 | |||
| daa4af18d1 |
@@ -41,3 +41,8 @@ Frontend:
|
||||
- Available variables: VITE_BACKEND_HOST, VITE_USE_TLS, VITE_INSECURE_SKIP_VERIFY, VITE_FRONTEND_PORT
|
||||
- Internationalization:
|
||||
- Generate i18n declaration file: `npm run make-i18n`
|
||||
|
||||
|
||||
## Template for Github Pull Request
|
||||
|
||||
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.
|
||||
|
||||
@@ -57,7 +57,7 @@ docker run -it --rm --pull=always \
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> On a public network? See our [Hardened Docker Installation](https://docs.all-hands.dev/modules/usage/runtimes/docker#hardened-docker-installation) guide
|
||||
> On a public network? See our [Hardened Docker Installation](https://docs.all-hands.dev/modules/usage/runtimes/docker#hardened-docker-installation) guide
|
||||
> to secure your deployment by restricting network binding and implementing additional security measures.
|
||||
|
||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
||||
|
||||
@@ -21,4 +21,4 @@ OpenHands supports several different runtime environments:
|
||||
- [OpenHands Remote Runtime](./runtimes/remote.md) - Cloud-based runtime for parallel execution (beta)
|
||||
- [Modal Runtime](./runtimes/modal.md) - Runtime provided by our partners at Modal
|
||||
- [Daytona Runtime](./runtimes/daytona.md) - Runtime provided by Daytona
|
||||
- [Local Runtime](./runtimes/local.md) - Direct execution on your local machine without Docker
|
||||
- [Local Runtime](./runtimes/local.md) - Direct execution on your local machine without Docker
|
||||
|
||||
@@ -29,4 +29,4 @@ bash -i <(curl -sL https://get.daytona.io/openhands)
|
||||
|
||||
Once executed, OpenHands should be running locally and ready for use.
|
||||
|
||||
For more details and manual initialization, view the entire [README.md](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/impl/daytona/README.md)
|
||||
For more details and manual initialization, view the entire [README.md](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/impl/daytona/README.md)
|
||||
|
||||
@@ -59,4 +59,4 @@ The Local Runtime is particularly useful for:
|
||||
- CI/CD pipelines where Docker is not available.
|
||||
- Testing and development of OpenHands itself.
|
||||
- Environments where container usage is restricted.
|
||||
- Scenarios where direct file system access is required.
|
||||
- Scenarios where direct file system access is required.
|
||||
|
||||
@@ -10,4 +10,4 @@ docker run # ...
|
||||
-e RUNTIME=modal \
|
||||
-e MODAL_API_TOKEN_ID="your-id" \
|
||||
-e MODAL_API_TOKEN_SECRET="your-secret" \
|
||||
```
|
||||
```
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
OpenHands Remote Runtime is currently in beta (read [here](https://runtime.all-hands.dev/) for more details), it allows you to launch runtimes in parallel in the cloud.
|
||||
Fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSckVz_JFwg2_mOxNZjCtr7aoBFI2Mwdan3f75J_TrdMS1JV2g/viewform) to apply if you want to try this out!
|
||||
|
||||
NOTE: This runtime is specifically designed for agent evaluation purposes only through [OpenHands evaluation harness](https://github.com/All-Hands-AI/OpenHands/tree/main/evaluation). It should not be used to launch production OpenHands applications.
|
||||
NOTE: This runtime is specifically designed for agent evaluation purposes only through [OpenHands evaluation harness](https://github.com/All-Hands-AI/OpenHands/tree/main/evaluation). It should not be used to launch production OpenHands applications.
|
||||
|
||||
@@ -53,6 +53,7 @@ fi
|
||||
if [ -n "$AGENT_CONFIG" ]; then
|
||||
echo "AGENT_CONFIG: $AGENT_CONFIG"
|
||||
COMMAND="$COMMAND --agent-config $AGENT_CONFIG"
|
||||
fi
|
||||
|
||||
# Run the command
|
||||
eval $COMMAND
|
||||
|
||||
@@ -9,67 +9,67 @@ describe("formatTimeDelta", () => {
|
||||
|
||||
it("formats the yearly time correctly", () => {
|
||||
const oneYearAgo = new Date("2023-01-01T00:00:00Z");
|
||||
expect(formatTimeDelta(oneYearAgo)).toBe("1 year");
|
||||
expect(formatTimeDelta(oneYearAgo)).toBe("1y");
|
||||
|
||||
const twoYearsAgo = new Date("2022-01-01T00:00:00Z");
|
||||
expect(formatTimeDelta(twoYearsAgo)).toBe("2 years");
|
||||
expect(formatTimeDelta(twoYearsAgo)).toBe("2y");
|
||||
|
||||
const threeYearsAgo = new Date("2021-01-01T00:00:00Z");
|
||||
expect(formatTimeDelta(threeYearsAgo)).toBe("3 years");
|
||||
expect(formatTimeDelta(threeYearsAgo)).toBe("3y");
|
||||
});
|
||||
|
||||
it("formats the monthly time correctly", () => {
|
||||
const oneMonthAgo = new Date("2023-12-01T00:00:00Z");
|
||||
expect(formatTimeDelta(oneMonthAgo)).toBe("1 month");
|
||||
expect(formatTimeDelta(oneMonthAgo)).toBe("1mo");
|
||||
|
||||
const twoMonthsAgo = new Date("2023-11-01T00:00:00Z");
|
||||
expect(formatTimeDelta(twoMonthsAgo)).toBe("2 months");
|
||||
expect(formatTimeDelta(twoMonthsAgo)).toBe("2mo");
|
||||
|
||||
const threeMonthsAgo = new Date("2023-10-01T00:00:00Z");
|
||||
expect(formatTimeDelta(threeMonthsAgo)).toBe("3 months");
|
||||
expect(formatTimeDelta(threeMonthsAgo)).toBe("3mo");
|
||||
});
|
||||
|
||||
it("formats the daily time correctly", () => {
|
||||
const oneDayAgo = new Date("2023-12-31T00:00:00Z");
|
||||
expect(formatTimeDelta(oneDayAgo)).toBe("1 day");
|
||||
expect(formatTimeDelta(oneDayAgo)).toBe("1d");
|
||||
|
||||
const twoDaysAgo = new Date("2023-12-30T00:00:00Z");
|
||||
expect(formatTimeDelta(twoDaysAgo)).toBe("2 days");
|
||||
expect(formatTimeDelta(twoDaysAgo)).toBe("2d");
|
||||
|
||||
const threeDaysAgo = new Date("2023-12-29T00:00:00Z");
|
||||
expect(formatTimeDelta(threeDaysAgo)).toBe("3 days");
|
||||
expect(formatTimeDelta(threeDaysAgo)).toBe("3d");
|
||||
});
|
||||
|
||||
it("formats the hourly time correctly", () => {
|
||||
const oneHourAgo = new Date("2023-12-31T23:00:00Z");
|
||||
expect(formatTimeDelta(oneHourAgo)).toBe("1 hour");
|
||||
expect(formatTimeDelta(oneHourAgo)).toBe("1h");
|
||||
|
||||
const twoHoursAgo = new Date("2023-12-31T22:00:00Z");
|
||||
expect(formatTimeDelta(twoHoursAgo)).toBe("2 hours");
|
||||
expect(formatTimeDelta(twoHoursAgo)).toBe("2h");
|
||||
|
||||
const threeHoursAgo = new Date("2023-12-31T21:00:00Z");
|
||||
expect(formatTimeDelta(threeHoursAgo)).toBe("3 hours");
|
||||
expect(formatTimeDelta(threeHoursAgo)).toBe("3h");
|
||||
});
|
||||
|
||||
it("formats the minute time correctly", () => {
|
||||
const oneMinuteAgo = new Date("2023-12-31T23:59:00Z");
|
||||
expect(formatTimeDelta(oneMinuteAgo)).toBe("1 minute");
|
||||
expect(formatTimeDelta(oneMinuteAgo)).toBe("1m");
|
||||
|
||||
const twoMinutesAgo = new Date("2023-12-31T23:58:00Z");
|
||||
expect(formatTimeDelta(twoMinutesAgo)).toBe("2 minutes");
|
||||
expect(formatTimeDelta(twoMinutesAgo)).toBe("2m");
|
||||
|
||||
const threeMinutesAgo = new Date("2023-12-31T23:57:00Z");
|
||||
expect(formatTimeDelta(threeMinutesAgo)).toBe("3 minutes");
|
||||
expect(formatTimeDelta(threeMinutesAgo)).toBe("3m");
|
||||
});
|
||||
|
||||
it("formats the second time correctly", () => {
|
||||
const oneSecondAgo = new Date("2023-12-31T23:59:59Z");
|
||||
expect(formatTimeDelta(oneSecondAgo)).toBe("1 second");
|
||||
expect(formatTimeDelta(oneSecondAgo)).toBe("1s");
|
||||
|
||||
const twoSecondsAgo = new Date("2023-12-31T23:59:58Z");
|
||||
expect(formatTimeDelta(twoSecondsAgo)).toBe("2 seconds");
|
||||
expect(formatTimeDelta(twoSecondsAgo)).toBe("2s");
|
||||
|
||||
const threeSecondsAgo = new Date("2023-12-31T23:59:57Z");
|
||||
expect(formatTimeDelta(threeSecondsAgo)).toBe("3 seconds");
|
||||
expect(formatTimeDelta(threeSecondsAgo)).toBe("3s");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import axios from "axios";
|
||||
|
||||
export const openHands = axios.create();
|
||||
|
||||
export const openHands = axios.create({
|
||||
baseURL: `${window.location.protocol}//${import.meta.env.VITE_BACKEND_BASE_URL || window?.location.host}`,
|
||||
});
|
||||
export const setAuthTokenHeader = (token: string) => {
|
||||
openHands.defaults.headers.common.Authorization = `Bearer ${token}`;
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ export function ContextMenuListItem({
|
||||
disabled={isDisabled}
|
||||
className={cn(
|
||||
"text-sm px-4 py-2 w-full text-start hover:bg-white/10 first-of-type:rounded-t-md last-of-type:rounded-b-md",
|
||||
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
||||
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent text-nowrap",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -18,7 +18,7 @@ export function ContextMenu({
|
||||
<ul
|
||||
data-testid={testId}
|
||||
ref={ref}
|
||||
className={cn("bg-tertiary rounded-md w-[140px]", className)}
|
||||
className={cn("bg-tertiary rounded-md", className)}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
|
||||
@@ -22,11 +22,14 @@ interface ConversationCardProps {
|
||||
title: string;
|
||||
selectedRepository: string | null;
|
||||
lastUpdatedAt: string; // ISO 8601
|
||||
createdAt?: string; // ISO 8601
|
||||
status?: ProjectStatus;
|
||||
variant?: "compact" | "default";
|
||||
conversationId?: string; // Optional conversation ID for VS Code URL
|
||||
}
|
||||
|
||||
const MAX_TIME_BETWEEN_CREATION_AND_UPDATE = 1000 * 60 * 30; // 30 minutes
|
||||
|
||||
export function ConversationCard({
|
||||
onClick,
|
||||
onDelete,
|
||||
@@ -35,7 +38,10 @@ export function ConversationCard({
|
||||
isActive,
|
||||
title,
|
||||
selectedRepository,
|
||||
// lastUpdatedAt is kept in props for backward compatibility
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
lastUpdatedAt,
|
||||
createdAt,
|
||||
status = "STOPPED",
|
||||
variant = "default",
|
||||
conversationId,
|
||||
@@ -105,11 +111,10 @@ export function ConversationCard({
|
||||
|
||||
if (data.vscode_url) {
|
||||
window.open(data.vscode_url, "_blank");
|
||||
} else {
|
||||
console.error("VS Code URL not available", data.error);
|
||||
}
|
||||
// VS Code URL not available
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch VS Code URL", error);
|
||||
// Failed to fetch VS Code URL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +133,12 @@ export function ConversationCard({
|
||||
}, [titleMode]);
|
||||
|
||||
const hasContextMenu = !!(onDelete || onChangeTitle || showDisplayCostOption);
|
||||
const timeBetweenUpdateAndCreation = createdAt
|
||||
? new Date(lastUpdatedAt).getTime() - new Date(createdAt).getTime()
|
||||
: 0;
|
||||
const showUpdateTime =
|
||||
createdAt &&
|
||||
timeBetweenUpdateAndCreation > MAX_TIME_BETWEEN_CREATION_AND_UPDATE;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -205,7 +216,16 @@ export function ConversationCard({
|
||||
<ConversationRepoLink selectedRepository={selectedRepository} />
|
||||
)}
|
||||
<p className="text-xs text-neutral-400">
|
||||
<time>{formatTimeDelta(new Date(lastUpdatedAt))} ago</time>
|
||||
<span>Created </span>
|
||||
<time>
|
||||
{formatTimeDelta(new Date(createdAt || lastUpdatedAt))} ago
|
||||
</time>
|
||||
{showUpdateTime && (
|
||||
<>
|
||||
<span>, updated </span>
|
||||
<time>{formatTimeDelta(new Date(lastUpdatedAt))} ago</time>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -108,7 +108,9 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
|
||||
title={project.title}
|
||||
selectedRepository={project.selected_repository}
|
||||
lastUpdatedAt={project.last_updated_at}
|
||||
createdAt={project.created_at}
|
||||
status={project.status}
|
||||
conversationId={project.conversation_id}
|
||||
/>
|
||||
)}
|
||||
</NavLink>
|
||||
|
||||
@@ -5,23 +5,12 @@ interface ConversationRepoLinkProps {
|
||||
export function ConversationRepoLink({
|
||||
selectedRepository,
|
||||
}: ConversationRepoLinkProps) {
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
window.open(
|
||||
`https://github.com/${selectedRepository}`,
|
||||
"_blank",
|
||||
"noopener,noreferrer",
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
<span
|
||||
data-testid="conversation-card-selected-repository"
|
||||
onClick={handleClick}
|
||||
className="text-xs text-neutral-400 hover:text-neutral-200"
|
||||
className="text-xs text-neutral-400"
|
||||
>
|
||||
{selectedRepository}
|
||||
</button>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -281,6 +281,7 @@ export enum I18nKey {
|
||||
ACTION_MESSAGE$EDIT = "ACTION_MESSAGE$EDIT",
|
||||
ACTION_MESSAGE$WRITE = "ACTION_MESSAGE$WRITE",
|
||||
ACTION_MESSAGE$BROWSE = "ACTION_MESSAGE$BROWSE",
|
||||
ACTION_MESSAGE$BROWSE_INTERACTIVE = "ACTION_MESSAGE$BROWSE_INTERACTIVE",
|
||||
ACTION_MESSAGE$THINK = "ACTION_MESSAGE$THINK",
|
||||
OBSERVATION_MESSAGE$RUN = "OBSERVATION_MESSAGE$RUN",
|
||||
OBSERVATION_MESSAGE$RUN_IPYTHON = "OBSERVATION_MESSAGE$RUN_IPYTHON",
|
||||
|
||||
@@ -4193,6 +4193,21 @@
|
||||
"es": "Navegando en la web",
|
||||
"tr": "Web'de geziniyor"
|
||||
},
|
||||
"ACTION_MESSAGE$BROWSE_INTERACTIVE": {
|
||||
"en": "Interactive browsing in progress...",
|
||||
"zh-CN": "交互式浏览进行中...",
|
||||
"zh-TW": "互動式瀏覽進行中...",
|
||||
"ko-KR": "인터랙티브 브라우징 진행 중...",
|
||||
"ja": "インタラクティブブラウジング進行中...",
|
||||
"no": "Interaktiv surfing pågår...",
|
||||
"ar": "التصفح التفاعلي قيد التقدم...",
|
||||
"de": "Interaktives Browsen läuft...",
|
||||
"fr": "Navigation interactive en cours...",
|
||||
"it": "Navigazione interattiva in corso...",
|
||||
"pt": "Navegação interativa em andamento...",
|
||||
"es": "Navegación interactiva en progreso...",
|
||||
"tr": "Etkileşimli tarama devam ediyor..."
|
||||
},
|
||||
"ACTION_MESSAGE$THINK": {
|
||||
"en": "Thinking",
|
||||
"zh-CN": "思考",
|
||||
|
||||
@@ -78,20 +78,18 @@ function FileViewer() {
|
||||
<div className="flex h-full bg-base-secondary relative">
|
||||
<FileExplorer isOpen={fileExplorerIsOpen} onToggle={toggleFileExplorer} />
|
||||
<div className="w-full h-full flex flex-col">
|
||||
{selectedPath && (
|
||||
<div className="flex w-full items-center justify-between self-end p-2">
|
||||
<span className="text-sm text-neutral-500">{selectedPath}</span>
|
||||
</div>
|
||||
)}
|
||||
{selectedPath && files[selectedPath] && (
|
||||
<div className="p-4 flex-1 overflow-auto">
|
||||
<div className="h-full w-full overflow-auto">
|
||||
<SyntaxHighlighter
|
||||
language={getLanguageFromPath(selectedPath)}
|
||||
style={vscDarkPlus}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
padding: "10px",
|
||||
height: "100%",
|
||||
background: "#171717",
|
||||
fontSize: "0.875rem",
|
||||
borderRadius: 0,
|
||||
}}
|
||||
>
|
||||
{files[selectedPath]}
|
||||
|
||||
@@ -30,6 +30,7 @@ export function handleObservationMessage(message: ObservationMessage) {
|
||||
store.dispatch(appendJupyterOutput(message.content));
|
||||
break;
|
||||
case ObservationType.BROWSE:
|
||||
case ObservationType.BROWSE_INTERACTIVE:
|
||||
if (message.extras?.screenshot) {
|
||||
store.dispatch(setScreenshotSrc(message.extras?.screenshot));
|
||||
}
|
||||
@@ -178,6 +179,46 @@ export function handleObservationMessage(message: ObservationMessage) {
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case "browse_interactive":
|
||||
store.dispatch(
|
||||
addAssistantObservation({
|
||||
...baseObservation,
|
||||
observation: "browse_interactive" as const,
|
||||
extras: {
|
||||
url: String(message.extras.url || ""),
|
||||
screenshot: String(message.extras.screenshot || ""),
|
||||
error: Boolean(message.extras.error),
|
||||
open_page_urls: Array.isArray(message.extras.open_page_urls)
|
||||
? message.extras.open_page_urls
|
||||
: [],
|
||||
active_page_index: Number(message.extras.active_page_index || 0),
|
||||
dom_object:
|
||||
typeof message.extras.dom_object === "object"
|
||||
? (message.extras.dom_object as Record<string, unknown>)
|
||||
: {},
|
||||
axtree_object:
|
||||
typeof message.extras.axtree_object === "object"
|
||||
? (message.extras.axtree_object as Record<string, unknown>)
|
||||
: {},
|
||||
extra_element_properties:
|
||||
typeof message.extras.extra_element_properties === "object"
|
||||
? (message.extras.extra_element_properties as Record<
|
||||
string,
|
||||
unknown
|
||||
>)
|
||||
: {},
|
||||
last_browser_action: String(
|
||||
message.extras.last_browser_action || "",
|
||||
),
|
||||
last_browser_action_error:
|
||||
message.extras.last_browser_action_error,
|
||||
focused_element_bid: String(
|
||||
message.extras.focused_element_bid || "",
|
||||
),
|
||||
},
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case "error":
|
||||
store.dispatch(
|
||||
addAssistantObservation({
|
||||
|
||||
@@ -20,6 +20,7 @@ const HANDLED_ACTIONS: OpenHandsEventType[] = [
|
||||
"write",
|
||||
"read",
|
||||
"browse",
|
||||
"browse_interactive",
|
||||
"edit",
|
||||
];
|
||||
|
||||
@@ -108,6 +109,9 @@ export const chatSlice = createSlice({
|
||||
text = `${action.payload.args.path}\n${content}`;
|
||||
} else if (actionID === "browse") {
|
||||
text = `Browsing ${action.payload.args.url}`;
|
||||
} else if (actionID === "browse_interactive") {
|
||||
// Include the browser_actions in the content
|
||||
text = `**Action:**\n\n\`\`\`python\n${action.payload.args.browser_actions}\n\`\`\``;
|
||||
}
|
||||
if (actionID === "run" || actionID === "run_ipython") {
|
||||
if (
|
||||
@@ -127,6 +131,7 @@ export const chatSlice = createSlice({
|
||||
imageUrls: [],
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
state.messages.push(message);
|
||||
},
|
||||
|
||||
@@ -191,11 +196,11 @@ export const chatSlice = createSlice({
|
||||
} else if (observationID === "browse") {
|
||||
let content = `**URL:** ${observation.payload.extras.url}\n`;
|
||||
if (observation.payload.extras.error) {
|
||||
content += `**Error:**\n${observation.payload.extras.error}\n`;
|
||||
content += `\n\n**Error:**\n${observation.payload.extras.error}\n`;
|
||||
}
|
||||
content += `**Output:**\n${observation.payload.content}`;
|
||||
content += `\n\n**Output:**\n${observation.payload.content}`;
|
||||
if (content.length > MAX_CONTENT_LENGTH) {
|
||||
content = `${content.slice(0, MAX_CONTENT_LENGTH)}...`;
|
||||
content = `${content.slice(0, MAX_CONTENT_LENGTH)}...(truncated)`;
|
||||
}
|
||||
causeMessage.content = content;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,24 @@ export interface BrowseObservation extends OpenHandsObservationEvent<"browse"> {
|
||||
};
|
||||
}
|
||||
|
||||
export interface BrowseInteractiveObservation
|
||||
extends OpenHandsObservationEvent<"browse_interactive"> {
|
||||
source: "agent";
|
||||
extras: {
|
||||
url: string;
|
||||
screenshot: string;
|
||||
error: boolean;
|
||||
open_page_urls: string[];
|
||||
active_page_index: number;
|
||||
dom_object: Record<string, unknown>;
|
||||
axtree_object: Record<string, unknown>;
|
||||
extra_element_properties: Record<string, unknown>;
|
||||
last_browser_action: string;
|
||||
last_browser_action_error: unknown;
|
||||
focused_element_bid: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WriteObservation extends OpenHandsObservationEvent<"write"> {
|
||||
source: "agent";
|
||||
extras: {
|
||||
@@ -98,6 +116,7 @@ export type OpenHandsObservation =
|
||||
| IPythonObservation
|
||||
| DelegateObservation
|
||||
| BrowseObservation
|
||||
| BrowseInteractiveObservation
|
||||
| WriteObservation
|
||||
| ReadObservation
|
||||
| EditObservation
|
||||
|
||||
@@ -8,6 +8,9 @@ enum ObservationType {
|
||||
// The HTML contents of a URL
|
||||
BROWSE = "browse",
|
||||
|
||||
// Interactive browsing
|
||||
BROWSE_INTERACTIVE = "browse_interactive",
|
||||
|
||||
// The output of a command
|
||||
RUN = "run",
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* Formats a date into a human-readable string representing the time delta between the given date and the current date.
|
||||
* Formats a date into a compact string representing the time delta between the given date and the current date.
|
||||
* @param date The date to format
|
||||
* @returns A human-readable string representing the time delta between the given date and the current date
|
||||
* @returns A compact string representing the time delta between the given date and the current date
|
||||
*
|
||||
* @example
|
||||
* // now is 2024-01-01T00:00:00Z
|
||||
* formatTimeDelta(new Date("2023-12-31T23:59:59Z")); // "1 second"
|
||||
* formatTimeDelta(new Date("2022-01-01T00:00:00Z")); // "2 years"
|
||||
* formatTimeDelta(new Date("2023-12-31T23:59:59Z")); // "1s"
|
||||
* formatTimeDelta(new Date("2022-01-01T00:00:00Z")); // "2y"
|
||||
*/
|
||||
export const formatTimeDelta = (date: Date) => {
|
||||
const now = new Date();
|
||||
@@ -19,11 +19,10 @@ export const formatTimeDelta = (date: Date) => {
|
||||
const months = Math.floor(days / 30);
|
||||
const years = Math.floor(months / 12);
|
||||
|
||||
if (seconds < 60) return seconds === 1 ? "1 second" : `${seconds} seconds`;
|
||||
if (minutes < 60) return minutes === 1 ? "1 minute" : `${minutes} minutes`;
|
||||
if (hours < 24) return hours === 1 ? "1 hour" : `${hours} hours`;
|
||||
if (days < 30) return days === 1 ? "1 day" : `${days} days`;
|
||||
if (months < 12) return months === 1 ? "1 month" : `${months} months`;
|
||||
|
||||
return years === 1 ? "1 year" : `${years} years`;
|
||||
if (seconds < 60) return `${seconds}s`;
|
||||
if (minutes < 60) return `${minutes}m`;
|
||||
if (hours < 24) return `${hours}h`;
|
||||
if (days < 30) return `${days}d`;
|
||||
if (months < 12) return `${months}mo`;
|
||||
return `${years}y`;
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ Your primary role is to assist users by executing commands, modifying code, and
|
||||
<VERSION_CONTROL>
|
||||
* When configuring git credentials, use "openhands" as the user.name and "openhands@all-hands.dev" as the user.email by default, unless explicitly instructed otherwise.
|
||||
* Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., pushing to main, deleting repositories) unless explicitly asked to do so.
|
||||
* When committing changes, use `git status` to see all modified files, and stage all files necessary for the commit. Use `git commit -a` whenever possible.
|
||||
</VERSION_CONTROL>
|
||||
|
||||
<PROBLEM_SOLVING_WORKFLOW>
|
||||
|
||||
@@ -122,6 +122,8 @@ def initialize_repository_for_runtime(
|
||||
selected_repository,
|
||||
None,
|
||||
)
|
||||
# Run setup script if it exists
|
||||
runtime.maybe_run_setup_script()
|
||||
|
||||
return repo_directory
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ class ActionExecutor:
|
||||
|
||||
await wait_all(
|
||||
(self._init_plugin(plugin) for plugin in self.plugins_to_load),
|
||||
timeout=30,
|
||||
timeout=60,
|
||||
)
|
||||
logger.debug('All plugins initialized')
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class ActionExecutionClient(Runtime):
|
||||
attach_to_existing: bool = False,
|
||||
headless_mode: bool = True,
|
||||
user_id: str | None = None,
|
||||
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None
|
||||
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
|
||||
):
|
||||
self.session = HttpSession()
|
||||
self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from types import MappingProxyType
|
||||
from pydantic import Field
|
||||
|
||||
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
|
||||
@@ -16,4 +15,4 @@ class ConversationInitData(Settings):
|
||||
|
||||
model_config = {
|
||||
'arbitrary_types_allowed': True,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ from openhands.events.observation import (
|
||||
from openhands.events.observation.error import ErrorObservation
|
||||
from openhands.events.serialization import event_from_dict, event_to_dict
|
||||
from openhands.events.stream import EventStreamSubscriber
|
||||
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
|
||||
from openhands.llm.llm import LLM
|
||||
from openhands.server.session.agent_session import AgentSession
|
||||
from openhands.server.session.conversation_init_data import ConversationInitData
|
||||
|
||||
Generated
+16
-20
@@ -4943,18 +4943,20 @@ realtime = ["websockets (>=13,<15)"]
|
||||
|
||||
[[package]]
|
||||
name = "openhands-aci"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
description = "An Agent-Computer Interface (ACI) designed for software development agents OpenHands."
|
||||
optional = false
|
||||
python-versions = "^3.12"
|
||||
python-versions = "<4.0,>=3.12"
|
||||
groups = ["main"]
|
||||
files = []
|
||||
develop = false
|
||||
files = [
|
||||
{file = "openhands_aci-0.2.7-py3-none-any.whl", hash = "sha256:6b36fa465db6643d909efdf40ec303d27a03e6c9f568447df4bc1d9fdd7104b2"},
|
||||
{file = "openhands_aci-0.2.7.tar.gz", hash = "sha256:892c33d741e94b78ec65df178afe018869e6039ea484f7f232ee8c1bb4e440ef"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
binaryornot = "^0.4.4"
|
||||
cachetools = "^5.5.2"
|
||||
chardet = "^5.0.0"
|
||||
binaryornot = ">=0.4.4,<0.5.0"
|
||||
cachetools = ">=5.5.2,<6.0.0"
|
||||
charset-normalizer = ">=3.4.1,<4.0.0"
|
||||
flake8 = "*"
|
||||
gitpython = "*"
|
||||
grep-ast = "0.3.3"
|
||||
@@ -4963,18 +4965,12 @@ networkx = "*"
|
||||
numpy = "*"
|
||||
pandas = "*"
|
||||
scipy = "*"
|
||||
tree-sitter = "^0.24.0"
|
||||
tree-sitter-javascript = "^0.23.1"
|
||||
tree-sitter-python = "^0.23.6"
|
||||
tree-sitter-ruby = "^0.23.1"
|
||||
tree-sitter-typescript = "^0.23.2"
|
||||
whatthepatch = "^1.0.6"
|
||||
|
||||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/All-Hands-AI/openhands-aci.git"
|
||||
reference = "add-encoding-detection"
|
||||
resolved_reference = "040d9578d90894409f51ecca877b120fe696fe0b"
|
||||
tree-sitter = ">=0.24.0,<0.25.0"
|
||||
tree-sitter-javascript = ">=0.23.1,<0.24.0"
|
||||
tree-sitter-python = ">=0.23.6,<0.24.0"
|
||||
tree-sitter-ruby = ">=0.23.1,<0.24.0"
|
||||
tree-sitter-typescript = ">=0.23.2,<0.24.0"
|
||||
whatthepatch = ">=1.0.6,<2.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-api"
|
||||
@@ -9316,4 +9312,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "d3ec6b8a6c7e48420d76b7e17d5f1a3f253fa603205f90d4a8e4a614ab5e2c67"
|
||||
content-hash = "7c5c7d26747d7b42a1a7bbdc3b7e4d87bbbef851f3c51a022bf7a15082273e5a"
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ runloop-api-client = "0.26.0"
|
||||
libtmux = ">=0.37,<0.40"
|
||||
pygithub = "^2.5.0"
|
||||
joblib = "*"
|
||||
openhands-aci = "^0.2.6"
|
||||
openhands-aci = "^0.2.7"
|
||||
python-socketio = "^5.11.4"
|
||||
redis = "^5.2.0"
|
||||
sse-starlette = "^2.1.3"
|
||||
|
||||
Reference in New Issue
Block a user