Compare commits

...

12 Commits

Author SHA1 Message Date
openhands b19476a6c5 fix: Pass base commit to send_pull_request to ensure consistent repository state 2025-01-09 05:37:11 +00:00
Graham Neubig 5458ebbd7d Fix issue #6048: Update documentation of recommended models and add deepseek (#6050)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-09 02:39:53 +00:00
Robert Brennan c411a29db4 Move GitHub Token export to backend (#6153)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-08 21:12:46 +00:00
ross 386e04a2ba Fix field deprecation in runloop runtime client (#6152) 2025-01-08 15:18:24 -05:00
sp.wack 62c4bab6ba hotfix(frontend): Prevent a redirect when clicking edit (#6151) 2025-01-08 19:53:24 +00:00
sp.wack e308b6fb6f chore(backend): Update default conversation title logic (#6138) 2025-01-08 22:30:29 +04:00
Engel Nyst 27a660fb6b Make runtime logs optional (#6141) 2025-01-08 19:20:46 +01:00
sp.wack 27d761a1fe chore(frontend): Improve conversation card (#6121) 2025-01-08 21:57:57 +04:00
Robert Brennan 8028e2c2dd fix: handle binary data in GoogleCloudFileStore.write (#6145)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-08 17:36:34 +00:00
dependabot[bot] ff9058e28a chore(deps): bump the version-all group with 6 updates (#6146)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 16:03:04 +00:00
dependabot[bot] c45caaef1f chore(deps): bump the version-all group in /frontend with 8 updates (#6144)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 15:40:43 +00:00
dependabot[bot] a3c107daa4 chore(deps): bump the docusaurus group in /docs with 7 updates (#6142)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 19:27:37 +04:00
22 changed files with 1141 additions and 1520 deletions
+6 -3
View File
@@ -259,16 +259,19 @@ jobs:
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
PYTHONPATH: ""
run: |
cd /tmp && BASE_COMMIT=$(cd repo && git rev-parse HEAD) && \
if [ "${{ steps.check_result.outputs.RESOLUTION_SUCCESS }}" == "true" ]; then
cd /tmp && python -m openhands.resolver.send_pull_request \
python -m openhands.resolver.send_pull_request \
--issue-number ${{ env.ISSUE_NUMBER }} \
--pr-type draft \
--reviewer ${{ github.actor }} | tee pr_result.txt && \
--reviewer ${{ github.actor }} \
--base-commit "$BASE_COMMIT" | tee pr_result.txt && \
grep "draft created" pr_result.txt | sed 's/.*\///g' > pr_number.txt
else
cd /tmp && python -m openhands.resolver.send_pull_request \
python -m openhands.resolver.send_pull_request \
--issue-number ${{ env.ISSUE_NUMBER }} \
--pr-type branch \
--base-commit "$BASE_COMMIT" \
--send-on-failure | tee branch_result.txt && \
grep "branch created" branch_result.txt | sed 's/.*\///g; s/.expand=1//g' > branch_name.txt
fi
+5 -14
View File
@@ -5,23 +5,14 @@ OpenHands can connect to any LLM supported by LiteLLM. However, it requires a po
## Model Recommendations
Based on our evaluations of language models for coding tasks (using the SWE-bench dataset), we can provide some
recommendations for model selection. Some analyses can be found in [this blog article comparing LLMs](https://www.all-hands.dev/blog/evaluation-of-llms-as-coding-agents-on-swe-bench-at-30x-speed) and
[this blog article with some more recent results](https://www.all-hands.dev/blog/openhands-codeact-21-an-open-state-of-the-art-software-development-agent).
When choosing a model, consider both the quality of outputs and the associated costs. Here's a summary of the findings:
- Claude 3.5 Sonnet is the best by a fair amount, achieving a 53% resolve rate on SWE-Bench Verified with the default agent in OpenHands.
- GPT-4o lags behind, and o1-mini actually performed somewhat worse than GPT-4o. We went in and analyzed the results a little, and briefly it seemed like o1 was sometimes "overthinking" things, performing extra environment configuration tasks when it could just go ahead and finish the task.
- Finally, the strongest open models were Llama 3.1 405 B and deepseek-v2.5, and they performed reasonably, even besting some of the closed models.
Please refer to the [full article](https://www.all-hands.dev/blog/evaluation-of-llms-as-coding-agents-on-swe-bench-at-30x-speed) for more details.
recommendations for model selection. Our latest benchmarking results can be found in [this spreadsheet](https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=0).
Based on these findings and community feedback, the following models have been verified to work reasonably well with OpenHands:
- claude-3-5-sonnet (recommended)
- gpt-4 / gpt-4o
- llama-3.1-405b
- deepseek-v2.5
- anthropic/claude-3-5-sonnet-20241022 (recommended)
- anthropic/claude-3-5-haiku-20241022
- deepseek/deepseek-chat
- gpt-4o
:::warning
OpenHands will issue many prompts to the LLM you configure. Most of these LLMs cost money, so be sure to set spending
+582 -740
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -15,10 +15,10 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "^3.6.3",
"@docusaurus/plugin-content-pages": "^3.6.3",
"@docusaurus/preset-classic": "^3.6.3",
"@docusaurus/theme-mermaid": "^3.6.3",
"@docusaurus/core": "^3.7.0",
"@docusaurus/plugin-content-pages": "^3.7.0",
"@docusaurus/preset-classic": "^3.7.0",
"@docusaurus/theme-mermaid": "^3.7.0",
"@mdx-js/react": "^3.1.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.4.1",
@@ -29,7 +29,7 @@
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.5.1",
"@docusaurus/tsconfig": "^3.6.3",
"@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.5.1",
"typescript": "~5.7.2"
},
@@ -18,8 +18,8 @@ describe("ConversationCard", () => {
render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
@@ -38,8 +38,8 @@ describe("ConversationCard", () => {
const { rerender } = render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
@@ -53,8 +53,8 @@ describe("ConversationCard", () => {
rerender(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
isActive
title="Conversation 1"
selectedRepository="org/selectedRepository"
lastUpdatedAt="2021-10-01T12:00:00Z"
@@ -64,32 +64,13 @@ describe("ConversationCard", () => {
screen.getByTestId("conversation-card-selected-repository");
});
it("should call onClick when the card is clicked", async () => {
const user = userEvent.setup();
render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
const card = screen.getByTestId("conversation-card");
await user.click(card);
expect(onClick).toHaveBeenCalled();
});
it("should toggle a context menu when clicking the ellipsis button", async () => {
const user = userEvent.setup();
render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
@@ -112,8 +93,8 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
@@ -136,8 +117,8 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository="org/selectedRepository"
@@ -157,8 +138,8 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
@@ -189,8 +170,8 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
@@ -213,8 +194,8 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
@@ -232,8 +213,8 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
@@ -256,8 +237,8 @@ describe("ConversationCard", () => {
it("should render the 'STOPPED' indicator by default", () => {
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
@@ -271,8 +252,8 @@ describe("ConversationCard", () => {
it("should render the other indicators when provided", () => {
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
@@ -6,6 +6,7 @@ import {
QueryClientConfig,
} from "@tanstack/react-query";
import userEvent from "@testing-library/user-event";
import { createRoutesStub } from "react-router";
import { ConversationPanel } from "#/components/features/conversation-panel/conversation-panel";
import OpenHands from "#/api/open-hands";
import { AuthProvider } from "#/context/auth-context";
@@ -13,9 +14,15 @@ import { clickOnEditButton } from "./utils";
describe("ConversationPanel", () => {
const onCloseMock = vi.fn();
const RouterStub = createRoutesStub([
{
Component: () => <ConversationPanel onClose={onCloseMock} />,
path: "/",
},
]);
const renderConversationPanel = (config?: QueryClientConfig) =>
render(<ConversationPanel onClose={onCloseMock} />, {
render(<RouterStub />, {
wrapper: ({ children }) => (
<AuthProvider>
<QueryClientProvider client={new QueryClient(config)}>
+413 -616
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -8,32 +8,32 @@
},
"dependencies": {
"@monaco-editor/react": "^4.7.0-rc.0",
"@nextui-org/react": "^2.6.10",
"@nextui-org/react": "^2.6.11",
"@react-router/node": "^7.1.1",
"@react-router/serve": "^7.1.1",
"@react-types/shared": "^3.25.0",
"@reduxjs/toolkit": "^2.5.0",
"@tanstack/react-query": "^5.62.12",
"@tanstack/react-query": "^5.63.0",
"@vitejs/plugin-react": "^4.3.2",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.4.0",
"axios": "^1.7.9",
"clsx": "^2.1.1",
"eslint-config-airbnb-typescript": "^18.0.0",
"i18next": "^24.2.0",
"i18next": "^24.2.1",
"i18next-browser-languagedetector": "^8.0.2",
"i18next-http-backend": "^3.0.1",
"isbot": "^5.1.19",
"isbot": "^5.1.20",
"jose": "^5.9.4",
"monaco-editor": "^0.52.2",
"posthog-js": "^1.203.3",
"posthog-js": "^1.205.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-highlight": "^0.15.0",
"react-hot-toast": "^2.5.1",
"react-i18next": "^15.4.0",
"react-icons": "^5.4.0",
"react-markdown": "^9.0.1",
"react-markdown": "^9.0.3",
"react-redux": "^9.2.0",
"react-router": "^7.1.1",
"react-syntax-highlighter": "^15.6.1",
@@ -78,13 +78,13 @@
"@mswjs/socket.io-binding": "^0.1.1",
"@playwright/test": "^1.49.1",
"@react-router/dev": "^7.1.1",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/typography": "^0.5.16",
"@tanstack/eslint-plugin-query": "^5.62.16",
"@testing-library/jest-dom": "^6.6.1",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^22.10.5",
"@types/react": "^19.0.2",
"@types/react": "^19.0.3",
"@types/react-dom": "^19.0.2",
"@types/react-highlight": "^0.12.8",
"@types/react-syntax-highlighter": "^15.5.13",
@@ -9,9 +9,9 @@ import { EllipsisButton } from "./ellipsis-button";
import { ConversationCardContextMenu } from "./conversation-card-context-menu";
interface ConversationCardProps {
onClick: () => void;
onDelete: () => void;
onChangeTitle: (title: string) => void;
isActive: boolean;
title: string;
selectedRepository: string | null;
lastUpdatedAt: string; // ISO 8601
@@ -19,9 +19,9 @@ interface ConversationCardProps {
}
export function ConversationCard({
onClick,
onDelete,
onChangeTitle,
isActive,
title,
selectedRepository,
lastUpdatedAt,
@@ -51,15 +51,18 @@ export function ConversationCard({
};
const handleInputClick = (event: React.MouseEvent<HTMLInputElement>) => {
event.preventDefault();
event.stopPropagation();
};
const handleDelete = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
event.stopPropagation();
onDelete();
};
const handleEdit = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
event.stopPropagation();
setTitleMode("edit");
setContextMenuVisible(false);
@@ -74,26 +77,29 @@ export function ConversationCard({
return (
<div
data-testid="conversation-card"
onClick={onClick}
className="h-[100px] w-full px-[18px] py-4 border-b border-neutral-600 cursor-pointer"
>
<div className="flex items-center justify-between space-x-1">
<input
data-testid="conversation-card-title"
ref={inputRef}
disabled={titleMode === "view"}
onClick={handleInputClick}
onBlur={handleBlur}
onKeyUp={handleKeyUp}
type="text"
defaultValue={title}
className="text-sm leading-6 font-semibold bg-transparent w-full"
/>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 w-full">
{isActive && <span className="w-2 h-2 bg-blue-500 rounded-full" />}
<input
ref={inputRef}
disabled={titleMode === "view"}
data-testid="conversation-card-title"
onClick={handleInputClick}
onBlur={handleBlur}
onKeyUp={handleKeyUp}
type="text"
defaultValue={title}
className="text-sm leading-6 font-semibold bg-transparent w-full"
/>
</div>
<div className="flex items-center gap-2 relative">
<ConversationStateIndicator status={status} />
<EllipsisButton
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
setContextMenuVisible((prev) => !prev);
}}
@@ -1,5 +1,5 @@
import React from "react";
import { useNavigate, useParams } from "react-router";
import { NavLink, useParams } from "react-router";
import { ConversationCard } from "./conversation-card";
import { useUserConversations } from "#/hooks/query/use-user-conversations";
import { useDeleteConversation } from "#/hooks/mutation/use-delete-conversation";
@@ -16,7 +16,6 @@ interface ConversationPanelProps {
export function ConversationPanel({ onClose }: ConversationPanelProps) {
const { conversationId: cid } = useParams();
const navigate = useNavigate();
const endSession = useEndSession();
const ref = useClickOutsideElement<HTMLDivElement>(onClose);
@@ -63,11 +62,6 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
});
};
const handleClickCard = (conversationId: string) => {
navigate(`/conversations/${conversationId}`);
onClose();
};
return (
<div
ref={ref}
@@ -88,18 +82,25 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
</div>
)}
{conversations?.map((project) => (
<ConversationCard
<NavLink
key={project.conversation_id}
onClick={() => handleClickCard(project.conversation_id)}
onDelete={() => handleDeleteProject(project.conversation_id)}
onChangeTitle={(title) =>
handleChangeTitle(project.conversation_id, project.title, title)
}
title={project.title}
selectedRepository={project.selected_repository}
lastUpdatedAt={project.last_updated_at}
status={project.status}
/>
to={`/conversations/${project.conversation_id}`}
onClick={onClose}
>
{({ isActive }) => (
<ConversationCard
isActive={isActive}
onDelete={() => handleDeleteProject(project.conversation_id)}
onChangeTitle={(title) =>
handleChangeTitle(project.conversation_id, project.title, title)
}
title={project.title}
selectedRepository={project.selected_repository}
lastUpdatedAt={project.last_updated_at}
status={project.status}
/>
)}
</NavLink>
))}
{confirmDeleteModalVisible && (
@@ -1,24 +1,16 @@
import React from "react";
import toast from "react-hot-toast";
import { useDispatch, useSelector } from "react-redux";
import { useAuth } from "#/context/auth-context";
import { useWsClient } from "#/context/ws-client-provider";
import { getGitHubTokenCommand } from "#/services/terminal-service";
import { setImportedProjectZip } from "#/state/initial-query-slice";
import { RootState } from "#/store";
import { base64ToBlob } from "#/utils/base64-to-blob";
import { useUploadFiles } from "../../../hooks/mutation/use-upload-files";
import { useGitHubUser } from "../../../hooks/query/use-github-user";
import { isGitHubErrorReponse } from "#/api/github-axios-instance";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
export const useHandleRuntimeActive = () => {
const { gitHubToken } = useAuth();
const { send } = useWsClient();
const dispatch = useDispatch();
const { data: user } = useGitHubUser();
const { mutate: uploadFiles } = useUploadFiles();
const { curAgentState } = useSelector((state: RootState) => state.agent);
@@ -28,11 +20,6 @@ export const useHandleRuntimeActive = () => {
(state: RootState) => state.initialQuery,
);
const userId = React.useMemo(() => {
if (user && !isGitHubErrorReponse(user)) return user.id;
return null;
}, [user]);
const handleUploadFiles = (zip: string) => {
const blob = base64ToBlob(zip);
const file = new File([blob], "imported-project.zip", {
@@ -49,13 +36,6 @@ export const useHandleRuntimeActive = () => {
dispatch(setImportedProjectZip(null));
};
React.useEffect(() => {
if (runtimeActive && userId && gitHubToken) {
// Export if the user valid, this could happen mid-session so it is handled here
send(getGitHubTokenCommand(gitHubToken));
}
}, [userId, gitHubToken, runtimeActive]);
React.useEffect(() => {
if (runtimeActive && importedProjectZip) {
handleUploadFiles(importedProjectZip);
@@ -4,9 +4,3 @@ export function getTerminalCommand(command: string, hidden: boolean = false) {
const event = { action: ActionType.RUN, args: { command, hidden } };
return event;
}
export function getGitHubTokenCommand(gitHubToken: string) {
const command = `export GITHUB_TOKEN=${gitHubToken}`;
const event = getTerminalCommand(command, true);
return event;
}
+7 -2
View File
@@ -1,6 +1,10 @@
// Here are the list of verified models and providers that we know work well with OpenHands.
export const VERIFIED_PROVIDERS = ["openai", "azure", "anthropic"];
export const VERIFIED_MODELS = ["gpt-4o", "claude-3-5-sonnet-20241022"];
export const VERIFIED_PROVIDERS = ["openai", "azure", "anthropic", "deepseek"];
export const VERIFIED_MODELS = [
"gpt-4o",
"claude-3-5-sonnet-20241022",
"deepseek-chat",
];
// LiteLLM does not return OpenAI models with the provider, so we list them here to set them ourselves for consistency
// (e.g., they return `gpt-4o` instead of `openai/gpt-4o`)
@@ -21,6 +25,7 @@ export const VERIFIED_ANTHROPIC_MODELS = [
"claude-2.1",
"claude-3-5-sonnet-20240620",
"claude-3-5-sonnet-20241022",
"claude-3-5-haiku-20241022",
"claude-3-haiku-20240307",
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
+3
View File
@@ -20,6 +20,9 @@ DISABLE_COLOR_PRINTING = False
LOG_ALL_EVENTS = os.getenv('LOG_ALL_EVENTS', 'False').lower() in ['true', '1', 'yes']
# Controls whether to stream Docker container logs
DEBUG_RUNTIME = os.getenv('DEBUG_RUNTIME', 'False').lower() in ['true', '1', 'yes']
ColorType = Literal[
'red',
'green',
@@ -10,9 +10,8 @@ from openhands.core.config import AppConfig
from openhands.core.exceptions import (
AgentRuntimeDisconnectedError,
AgentRuntimeNotFoundError,
AgentRuntimeNotReadyError,
)
from openhands.core.logger import DEBUG
from openhands.core.logger import DEBUG, DEBUG_RUNTIME
from openhands.core.logger import openhands_logger as logger
from openhands.events import EventStream
from openhands.runtime.builder import DockerRuntimeBuilder
@@ -139,7 +138,10 @@ class DockerRuntime(ActionExecutionClient):
f'Container started: {self.container_name}. VSCode URL: {self.vscode_url}',
)
self.log_streamer = LogStreamer(self.container, self.log)
if DEBUG_RUNTIME:
self.log_streamer = LogStreamer(self.container, self.log)
else:
self.log_streamer = None
if not self.attach_to_existing:
self.log('info', f'Waiting for client to become ready at {self.api_url}...')
@@ -331,9 +333,6 @@ class DockerRuntime(ActionExecutionClient):
f'Container {self.container_name} not found.'
)
if not self.log_streamer:
raise AgentRuntimeNotReadyError('Runtime client is not ready.')
self.check_if_alive()
def close(self, rm_all_containers: bool | None = None):
@@ -249,6 +249,8 @@ class RemoteRuntime(ActionExecutionClient):
timeout=60,
):
pass
self._wait_until_alive()
self.setup_initial_env()
self.log('debug', 'Runtime resumed.')
def _parse_runtime_response(self, response: requests.Response):
@@ -388,7 +390,6 @@ class RemoteRuntime(ActionExecutionClient):
elif e.response.status_code == 503:
self.log('warning', 'Runtime appears to be paused. Resuming...')
self._resume_runtime()
self._wait_until_alive()
return super()._send_action_server_request(method, url, **kwargs)
else:
raise e
@@ -115,13 +115,15 @@ class RunloopRuntime(ActionExecutionClient):
devbox = self.runloop_api_client.devboxes.create(
entrypoint=entrypoint,
setup_commands=[f'mkdir -p {self.config.workspace_mount_path_in_sandbox}'],
name=self.sid,
environment_variables={'DEBUG': 'true'} if self.config.debug else {},
prebuilt='openhands',
launch_parameters=LaunchParameters(
available_ports=[self._sandbox_port, self._vscode_port],
resource_size_request='LARGE',
launch_commands=[
f'mkdir -p {self.config.workspace_mount_path_in_sandbox}'
],
),
metadata={'container-name': self.container_name},
)
@@ -66,9 +66,10 @@ async def new_conversation(request: Request, data: InitSessionRequest):
conversation_id = uuid.uuid4().hex
logger.info(f'New conversation ID: {conversation_id}')
conversation_title = (
data.selected_repository or f'Conversation {conversation_id[:5]}'
repository_title = (
data.selected_repository.split('/')[-1] if data.selected_repository else None
)
conversation_title = f'{repository_title or "Conversation"} {conversation_id[:5]}'
logger.info(f'Saving metadata for conversation {conversation_id}')
await conversation_store.save_metadata(
@@ -180,6 +180,13 @@ class AgentSession:
logger.debug(f'Initializing runtime `{runtime_name}` now...')
runtime_cls = get_runtime_cls(runtime_name)
env_vars = (
{
'GITHUB_TOKEN': github_token,
}
if github_token
else None
)
self.runtime = runtime_cls(
config=config,
event_stream=self.event_stream,
@@ -187,6 +194,7 @@ class AgentSession:
plugins=agent.sandbox_plugins,
status_callback=self._status_callback,
headless_mode=False,
env_vars=env_vars,
)
# FIXME: this sleep is a terrible hack.
+2 -1
View File
@@ -21,7 +21,8 @@ class GoogleCloudFileStore(FileStore):
def write(self, path: str, contents: str | bytes) -> None:
blob = self.bucket.blob(path)
with blob.open('w') as f:
mode = 'wb' if isinstance(contents, bytes) else 'w'
with blob.open(mode) as f:
f.write(contents)
def read(self, path: str) -> str:
Generated
+27 -28
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
@@ -552,17 +552,17 @@ files = [
[[package]]
name = "boto3"
version = "1.35.93"
version = "1.35.94"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "boto3-1.35.93-py3-none-any.whl", hash = "sha256:7de2c44c960e486f3c57e5203ea6393c6c4f0914c5f81c789ceb8b5d2ba5d1c5"},
{file = "boto3-1.35.93.tar.gz", hash = "sha256:2446e819cf4e295833474cdcf2c92bc82718ce537e9ee1f17f7e3d237f60e69b"},
{file = "boto3-1.35.94-py3-none-any.whl", hash = "sha256:516c514fb447d6f216833d06a0781c003fcf43099a4ca2f5a363a8afe0942070"},
{file = "boto3-1.35.94.tar.gz", hash = "sha256:5aa606239f0fe0dca0506e0ad6bbe4c589048e7e6c2486cee5ec22b6aa7ec2f8"},
]
[package.dependencies]
botocore = ">=1.35.93,<1.36.0"
botocore = ">=1.35.94,<1.36.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@@ -571,13 +571,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
version = "1.35.93"
version = "1.35.94"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
files = [
{file = "botocore-1.35.93-py3-none-any.whl", hash = "sha256:47f7161000af6036f806449e3de12acdd3ec11aac7f5578e43e96241413a0f8f"},
{file = "botocore-1.35.93.tar.gz", hash = "sha256:b8d245a01e7d64c41edcf75a42be158df57b9518a83a3dbf5c7e4b8c2bc540cc"},
{file = "botocore-1.35.94-py3-none-any.whl", hash = "sha256:d784d944865d8279c79d2301fc09ac28b5221d4e7328fb4e23c642c253b9932c"},
{file = "botocore-1.35.94.tar.gz", hash = "sha256:2b3309b356541faa4d88bb957dcac1d8004aa44953c0b7d4521a6cc5d3d5d6ba"},
]
[package.dependencies]
@@ -1223,7 +1223,6 @@ files = [
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"},
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
{file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
@@ -1234,7 +1233,6 @@ files = [
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"},
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
{file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
@@ -2238,13 +2236,13 @@ tool = ["click (>=6.0.0)"]
[[package]]
name = "google-cloud-aiplatform"
version = "1.75.0"
version = "1.76.0"
description = "Vertex AI API client library"
optional = false
python-versions = ">=3.8"
files = [
{file = "google_cloud_aiplatform-1.75.0-py2.py3-none-any.whl", hash = "sha256:eb5d79b5f7210d79a22b53c93a69b5bae5680dfc829387ea020765b97786b3d0"},
{file = "google_cloud_aiplatform-1.75.0.tar.gz", hash = "sha256:eb8404abf1134b3b368535fe429c4eec2fd12d444c2e9ffbc329ddcbc72b36c9"},
{file = "google_cloud_aiplatform-1.76.0-py2.py3-none-any.whl", hash = "sha256:0b0348525b9528db7b69538ff6e86289ea2ce0d80f3784a42865fc994fe10dd1"},
{file = "google_cloud_aiplatform-1.76.0.tar.gz", hash = "sha256:910fb7fb6ef7ec73a48523872d669370755f59ac6d764dc8bf2fc91e7c0b2fca"},
]
[package.dependencies]
@@ -2259,6 +2257,7 @@ proto-plus = ">=1.22.3,<2.0.0dev"
protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev"
pydantic = "<3"
shapely = "<3.0.0dev"
typing-extensions = "*"
[package.extras]
autologging = ["mlflow (>=1.27.0,<=2.16.0)"]
@@ -2268,7 +2267,7 @@ endpoint = ["requests (>=2.28.1)"]
evaluation = ["pandas (>=1.0.0)", "tqdm (>=4.23.0)"]
full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.114.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.16.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "requests (>=2.28.1)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)"]
langchain = ["langchain (>=0.1.16,<0.4)", "langchain-core (<0.4)", "langchain-google-vertexai (<3)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)"]
langchain-testing = ["absl-py", "cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "langchain (>=0.1.16,<0.4)", "langchain-core (<0.4)", "langchain-google-vertexai (<3)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<2.10)", "pytest-xdist"]
langchain-testing = ["absl-py", "cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "langchain (>=0.1.16,<0.4)", "langchain-core (<0.4)", "langchain-google-vertexai (<3)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist", "typing-extensions"]
lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"]
metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"]
pipelines = ["pyyaml (>=5.3.1,<7)"]
@@ -2276,7 +2275,7 @@ prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<=0.114.0)", "httpx (>=0.23
private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"]
ray = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0)", "pyarrow (>=6.0.1)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "setuptools (<70.0.0)"]
ray-testing = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0)", "pyarrow (>=6.0.1)", "pytest-xdist", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "ray[train]", "scikit-learn (<1.6.0)", "setuptools (<70.0.0)", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"]
reasoningengine = ["cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<2.10)"]
reasoningengine = ["cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "typing-extensions"]
tensorboard = ["tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"]
testing = ["aiohttp", "bigframes", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.114.0)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.16.0)", "nltk", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn", "scikit-learn (<1.6.0)", "sentencepiece (>=0.2.0)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (==2.13.0)", "tensorflow (==2.16.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0)", "torch (>=2.2.0)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"]
tokenization = ["sentencepiece (>=0.2.0)"]
@@ -3708,13 +3707,13 @@ types-tqdm = "*"
[[package]]
name = "litellm"
version = "1.57.1"
version = "1.57.2"
description = "Library to easily interface with LLM API providers"
optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
files = [
{file = "litellm-1.57.1-py3-none-any.whl", hash = "sha256:f9e93689f2d96df3bcebe723d44b6e2e71b9b047ec7ebd1054b6c9bc96cd9515"},
{file = "litellm-1.57.1.tar.gz", hash = "sha256:2ce6ce1707c92fb278f828a8ea058fa12b3eeb8081dd8c10776569995e03bb6f"},
{file = "litellm-1.57.2-py3-none-any.whl", hash = "sha256:b572c0d3d3c33ff3a4d18928ac6f051d10ac159814017a817d88ec7af9a8600c"},
{file = "litellm-1.57.2.tar.gz", hash = "sha256:0a07c4e288f4bd9033335d5606d7da497f1193d51cf262b96812f40b8842a842"},
]
[package.dependencies]
@@ -4584,12 +4583,12 @@ type = ["mypy (==1.11.2)"]
[[package]]
name = "modal"
version = "0.71.3"
version = "0.71.8"
description = "Python client library for Modal"
optional = false
python-versions = ">=3.9"
files = [
{file = "modal-0.71.3-py3-none-any.whl", hash = "sha256:38d1256cbc0329867841d7ba914f793a95454bef74daf4de4631cb5af59b5df4"},
{file = "modal-0.71.8-py3-none-any.whl", hash = "sha256:48bd0e9b4f951495fdd1172b8243d477631440a8c01dd13fcab5dd46b36d8a62"},
]
[package.dependencies]
@@ -6798,13 +6797,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
[[package]]
name = "pytest-asyncio"
version = "0.25.1"
version = "0.25.2"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.9"
files = [
{file = "pytest_asyncio-0.25.1-py3-none-any.whl", hash = "sha256:c84878849ec63ff2ca509423616e071ef9cd8cc93c053aa33b5b8fb70a990671"},
{file = "pytest_asyncio-0.25.1.tar.gz", hash = "sha256:79be8a72384b0c917677e00daa711e07db15259f4d23203c59012bcd989d4aee"},
{file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"},
{file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"},
]
[package.dependencies]
@@ -7651,13 +7650,13 @@ files = [
[[package]]
name = "runloop-api-client"
version = "0.11.0"
version = "0.12.0"
description = "The official Python library for the runloop API"
optional = false
python-versions = ">=3.8"
files = [
{file = "runloop_api_client-0.11.0-py3-none-any.whl", hash = "sha256:b2b5d7922375ad4fafae39a6779884b4e1fd334dc2af0a874394c7d2e3bd5a7b"},
{file = "runloop_api_client-0.11.0.tar.gz", hash = "sha256:c6a332421e0d469d3331ef150fba2970f499c3ecc8ae0f3e0dd69292858e0eab"},
{file = "runloop_api_client-0.12.0-py3-none-any.whl", hash = "sha256:284b5982b63d70ddb43b7621e121c1be80979a03d60c4d36aebfd46f02a993aa"},
{file = "runloop_api_client-0.12.0.tar.gz", hash = "sha256:b6cc334fbf1b775732636ec1cb7d00a0128e2632d4c08240c7e2fbf93824627f"},
]
[package.dependencies]
@@ -7666,7 +7665,7 @@ distro = ">=1.7.0,<2"
httpx = ">=0.23.0,<1"
pydantic = ">=1.9.0,<3"
sniffio = "*"
typing-extensions = ">=4.7,<5"
typing-extensions = ">=4.10,<5"
[[package]]
name = "s3transfer"
@@ -9854,4 +9853,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "b525fe602cd670d85945e4a0c1768650381b193ee200b1a27174c4baf0b74e35"
content-hash = "0d22154d7688e70fbd1a45fc7262b27126cce241e4f0b6739584d80bf863656e"
+1 -1
View File
@@ -61,7 +61,7 @@ protobuf = "^4.21.6,<5.0.0" # chromadb currently fails on 5.0+
opentelemetry-api = "1.25.0"
opentelemetry-exporter-otlp-proto-grpc = "1.25.0"
modal = ">=0.66.26,<0.72.0"
runloop-api-client = "0.11.0"
runloop-api-client = "0.12.0"
libtmux = ">=0.37,<0.40"
pygithub = "^2.5.0"
joblib = "*"