mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3113757859 |
@@ -104,6 +104,22 @@ export const retrieveGitHubUser = async () => {
|
||||
return user;
|
||||
};
|
||||
|
||||
export const searchPublicGitHubRepo = async (query: string): Promise<GitHubRepository | null> => {
|
||||
const response = await github.get<{ items: GitHubRepository[] }>(
|
||||
`/search/repositories`,
|
||||
{
|
||||
params: {
|
||||
q: query,
|
||||
sort: 'stars',
|
||||
order: 'desc',
|
||||
per_page: 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return response.data.items[0] || null;
|
||||
};
|
||||
|
||||
export const retrieveLatestGitHubCommit = async (
|
||||
repository: string,
|
||||
): Promise<GitHubCommit> => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useDispatch } from "react-redux";
|
||||
import posthog from "posthog-js";
|
||||
import { setSelectedRepository } from "#/state/initial-query-slice";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
import { searchPublicGitHubRepo } from "#/api/github";
|
||||
|
||||
interface GitHubRepositorySelectorProps {
|
||||
onSelect: () => void;
|
||||
@@ -16,12 +17,45 @@ export function GitHubRepositorySelector({
|
||||
}: GitHubRepositorySelectorProps) {
|
||||
const { data: config } = useConfig();
|
||||
const [selectedKey, setSelectedKey] = React.useState<string | null>(null);
|
||||
const [searchQuery, setSearchQuery] = React.useState<string>("");
|
||||
const [searchedRepo, setSearchedRepo] = React.useState<GitHubRepository | null>(null);
|
||||
const [isSearching, setIsSearching] = React.useState<boolean>(false);
|
||||
|
||||
// Add option to install app onto more repos
|
||||
const finalRepositories =
|
||||
config?.APP_MODE === "saas"
|
||||
? [{ id: -1000, full_name: "Add more repositories..." }, ...repositories]
|
||||
: repositories;
|
||||
React.useEffect(() => {
|
||||
const searchTimeout = setTimeout(async () => {
|
||||
if (searchQuery.trim()) {
|
||||
setIsSearching(true);
|
||||
try {
|
||||
const repo = await searchPublicGitHubRepo(searchQuery);
|
||||
setSearchedRepo(repo);
|
||||
} catch (error) {
|
||||
console.error("Error searching for repo:", error);
|
||||
}
|
||||
setIsSearching(false);
|
||||
} else {
|
||||
setSearchedRepo(null);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(searchTimeout);
|
||||
}, [searchQuery]);
|
||||
|
||||
// Sort repositories by pushed_at
|
||||
const sortedRepositories = [...repositories].sort((a, b) => {
|
||||
const dateA = a.pushed_at ? new Date(a.pushed_at).getTime() : 0;
|
||||
const dateB = b.pushed_at ? new Date(b.pushed_at).getTime() : 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
|
||||
// Add option to install app onto more repos and searched repo if found
|
||||
const finalRepositories = [
|
||||
...(searchedRepo ? [{
|
||||
...searchedRepo,
|
||||
full_name: `${searchedRepo.full_name} (${searchedRepo.stargazers_count} ⭐)`,
|
||||
}] : []),
|
||||
...(config?.APP_MODE === "saas" ? [{ id: -1000, full_name: "Add more repositories..." }] : []),
|
||||
...sortedRepositories,
|
||||
];
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -67,6 +101,8 @@ export function GitHubRepositorySelector({
|
||||
aria-label="GitHub Repository"
|
||||
placeholder="Select a GitHub project"
|
||||
selectedKey={selectedKey}
|
||||
inputValue={searchQuery}
|
||||
onInputChange={(value) => setSearchQuery(value)}
|
||||
inputProps={{
|
||||
classNames: {
|
||||
inputWrapper:
|
||||
@@ -76,7 +112,7 @@ export function GitHubRepositorySelector({
|
||||
onSelectionChange={(id) => handleRepoSelection(id?.toString() ?? null)}
|
||||
clearButtonProps={{ onClick: handleClearSelection }}
|
||||
listboxProps={{
|
||||
emptyContent,
|
||||
emptyContent: isSearching ? "Searching..." : emptyContent,
|
||||
}}
|
||||
>
|
||||
{finalRepositories.map((repo) => (
|
||||
|
||||
@@ -79,12 +79,6 @@ export function WsClientProvider({
|
||||
|
||||
function handleDisconnect() {
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
const sio = sioRef.current;
|
||||
if (!sio) {
|
||||
return;
|
||||
}
|
||||
sio.io.opts.query = sio.io.opts.query || {};
|
||||
sio.io.opts.query.latest_event_id = lastEventRef.current?.id;
|
||||
}
|
||||
|
||||
function handleError() {
|
||||
|
||||
@@ -15,7 +15,7 @@ ALWAYS use the GitHub API for operations instead of a web browser.
|
||||
Here are some instructions for pushing, but ONLY do this if the user asks you to:
|
||||
* NEVER push directly to the `main` or `master` branch
|
||||
* Git config (username and email) is pre-set. Do not modify.
|
||||
* You may already be on a branch starting with `openhands-workspace`. Create a new branch with a better name before pushing.
|
||||
* You may already be on a branch called `openhands-workspace`. Create a new branch with a better name before pushing.
|
||||
* Use the GitHub API to create a pull request, if you haven't already
|
||||
* Use the main branch as the base branch, unless the user requests otherwise
|
||||
* After opening or updating a pull request, send the user a short message with a link to the pull request.
|
||||
|
||||
@@ -2,8 +2,6 @@ import atexit
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
from abc import abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
@@ -205,13 +203,8 @@ class Runtime(FileEditRuntimeMixin):
|
||||
return
|
||||
url = f'https://{github_token}@github.com/{selected_repository}.git'
|
||||
dir_name = selected_repository.split('/')[1]
|
||||
# add random branch name to avoid conflicts
|
||||
random_str = ''.join(
|
||||
random.choices(string.ascii_lowercase + string.digits, k=8)
|
||||
)
|
||||
branch_name = f'openhands-workspace-{random_str}'
|
||||
action = CmdRunAction(
|
||||
command=f'git clone {url} {dir_name} ; cd {dir_name} ; git checkout -b {branch_name}',
|
||||
command=f'git clone {url} {dir_name} ; cd {dir_name} ; git checkout -b openhands-workspace'
|
||||
)
|
||||
self.log('info', f'Cloning repo: {selected_repository}')
|
||||
self.run_action(action)
|
||||
|
||||
@@ -421,8 +421,8 @@ class SessionManager:
|
||||
# Clear up local variables
|
||||
connection_ids_to_remove = list(
|
||||
connection_id
|
||||
for connection_id, conn_sid in self.local_connection_id_to_session_id.items()
|
||||
if sid == conn_sid
|
||||
for connection_id, sid in self.local_connection_id_to_session_id.items()
|
||||
if sid == sid
|
||||
)
|
||||
logger.info(f'removing connections: {connection_ids_to_remove}')
|
||||
for connnnection_id in connection_ids_to_remove:
|
||||
|
||||
Generated
+8
-9
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohappyeyeballs"
|
||||
@@ -3783,19 +3783,19 @@ pydantic = ">=1.10"
|
||||
|
||||
[[package]]
|
||||
name = "llama-index"
|
||||
version = "0.12.8"
|
||||
version = "0.12.7"
|
||||
description = "Interface between LLMs and your data"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.9"
|
||||
files = [
|
||||
{file = "llama_index-0.12.8-py3-none-any.whl", hash = "sha256:6b98ea44c225c7d230fd7f552dfcc2911ef327e3be352dc239011118242e4a28"},
|
||||
{file = "llama_index-0.12.8.tar.gz", hash = "sha256:f1578bb6873fa4f90a8645a80f4f997d184770e63bd7a2b45a98ab6e5c70fb59"},
|
||||
{file = "llama_index-0.12.7-py3-none-any.whl", hash = "sha256:9fee54e1dfdee7d1154ae6a702178052c72d81a946fce000eb80dffe98a7e9f6"},
|
||||
{file = "llama_index-0.12.7.tar.gz", hash = "sha256:2c197246a85de8e472e559b88212e4e92c167fdef9c0b131ae1f760ddcdfaca6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
llama-index-agent-openai = ">=0.4.0,<0.5.0"
|
||||
llama-index-cli = ">=0.4.0,<0.5.0"
|
||||
llama-index-core = ">=0.12.8,<0.13.0"
|
||||
llama-index-core = ">=0.12.7,<0.13.0"
|
||||
llama-index-embeddings-openai = ">=0.3.0,<0.4.0"
|
||||
llama-index-indices-managed-llama-cloud = ">=0.4.0"
|
||||
llama-index-llms-openai = ">=0.3.0,<0.4.0"
|
||||
@@ -3840,13 +3840,13 @@ llama-index-llms-openai = ">=0.3.0,<0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "llama-index-core"
|
||||
version = "0.12.8"
|
||||
version = "0.12.7"
|
||||
description = "Interface between LLMs and your data"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.9"
|
||||
files = [
|
||||
{file = "llama_index_core-0.12.8-py3-none-any.whl", hash = "sha256:7ebecbdaa1d5b6a320c050bf90525605ac03b242d26ad55f0e00a0e1df69e070"},
|
||||
{file = "llama_index_core-0.12.8.tar.gz", hash = "sha256:3b360437b4ae47b7bd1733f6492a95126e6739c7a2fd2b649ebe8bb3afea7143"},
|
||||
{file = "llama_index_core-0.12.7-py3-none-any.whl", hash = "sha256:691493915598c09b636f964e85b8baca630faa362a4a8ea130ddea8584ab8d0a"},
|
||||
{file = "llama_index_core-0.12.7.tar.gz", hash = "sha256:9935b249c08f87c124962a8ea1e301e1b5bfa7e3ffd6771b6cb59a0de9bb8cb5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -5414,7 +5414,6 @@ optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6"},
|
||||
{file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f"},
|
||||
|
||||
@@ -237,36 +237,3 @@ async def test_add_to_cluster_event_stream():
|
||||
'oh_event',
|
||||
'{"sid": "new-session-id", "message_type": "event", "data": {"event_type": "some_event"}}',
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cleanup_session_connections():
|
||||
sio = get_mock_sio()
|
||||
with (
|
||||
patch('openhands.server.session.manager._REDIS_POLL_TIMEOUT', 0.01),
|
||||
patch(
|
||||
'openhands.server.session.manager.SessionManager._redis_subscribe',
|
||||
AsyncMock(),
|
||||
),
|
||||
):
|
||||
async with SessionManager(
|
||||
sio, AppConfig(), InMemoryFileStore()
|
||||
) as session_manager:
|
||||
session_manager.local_connection_id_to_session_id.update(
|
||||
{
|
||||
'conn1': 'session1',
|
||||
'conn2': 'session1',
|
||||
'conn3': 'session2',
|
||||
'conn4': 'session2',
|
||||
}
|
||||
)
|
||||
|
||||
await session_manager._close_session('session1')
|
||||
|
||||
remaining_connections = session_manager.local_connection_id_to_session_id
|
||||
assert 'conn1' not in remaining_connections
|
||||
assert 'conn2' not in remaining_connections
|
||||
assert 'conn3' in remaining_connections
|
||||
assert 'conn4' in remaining_connections
|
||||
assert remaining_connections['conn3'] == 'session2'
|
||||
assert remaining_connections['conn4'] == 'session2'
|
||||
|
||||
Reference in New Issue
Block a user