mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
13 Commits
react-quer
...
oh-cloud-g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a3354404e | ||
|
|
80279f9d36 | ||
|
|
0b3d15a4d7 | ||
|
|
8b68d086f0 | ||
|
|
0f143a43c9 | ||
|
|
e61e4d57d9 | ||
|
|
4e86bdf3d9 | ||
|
|
3cef499b81 | ||
|
|
e2a0884ecd | ||
|
|
2849974729 | ||
|
|
daa4af18d1 | ||
|
|
1ec1076fee | ||
|
|
65bd4be607 |
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,7 @@ export const useVSCodeUrl = (config: { enabled: boolean }) => {
|
||||
return OpenHands.getVSCodeUrl(conversationId);
|
||||
},
|
||||
enabled: !!conversationId && config.enabled,
|
||||
refetchOnMount: false,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes
|
||||
refetchOnMount: true,
|
||||
});
|
||||
|
||||
return data;
|
||||
|
||||
@@ -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`;
|
||||
};
|
||||
|
||||
36
microagents/knowledge/pdflatex.md
Normal file
36
microagents/knowledge/pdflatex.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: pdflatex
|
||||
type: knowledge
|
||||
version: 1.0.0
|
||||
agent: CodeActAgent
|
||||
triggers:
|
||||
- pdflatex
|
||||
---
|
||||
|
||||
PdfLatex is a tool that converts Latex sources into PDF. This is specifically very important for researchers, as they use it to publish their findings. It could be installed very easily using Linux terminal, though this seems an annoying task on Windows. Installation commands are given below.
|
||||
|
||||
* Install the TexLive base
|
||||
|
||||
```
|
||||
apt-get install texlive-latex-base
|
||||
```
|
||||
|
||||
* Also install the recommended and extra fonts to avoid running into errors, when trying to use pdflatex on latex files with more fonts.
|
||||
|
||||
```
|
||||
apt-get install texlive-fonts-recommended
|
||||
apt-get install texlive-fonts-extra
|
||||
```
|
||||
|
||||
* Install the extra packages,
|
||||
|
||||
```
|
||||
apt-get install texlive-latex-extra
|
||||
```
|
||||
|
||||
Once installed as above, you may be able to create PDF files from latex sources using PdfLatex as below.
|
||||
```
|
||||
pdflatex latex_source_name.tex
|
||||
```
|
||||
|
||||
Ref: http://kkpradeeban.blogspot.com/2014/04/installing-latexpdflatex-on-ubuntu.html
|
||||
@@ -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
|
||||
|
||||
@@ -17,6 +17,8 @@ class VSCodeRequirement(PluginRequirement):
|
||||
|
||||
class VSCodePlugin(Plugin):
|
||||
name: str = 'vscode'
|
||||
vscode_port: int | None = None
|
||||
vscode_connection_token: str | None = None
|
||||
|
||||
async def initialize(self, username: str):
|
||||
if username not in ['root', 'openhands']:
|
||||
|
||||
@@ -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
|
||||
|
||||
36
poetry.lock
generated
36
poetry.lock
generated
@@ -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"
|
||||
|
||||
@@ -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