Compare commits

..

1 Commits

Author SHA1 Message Date
openhands 3113757859 Sort GitHub repos by pushed_at and add search functionality 2024-12-24 12:46:12 +00:00
8 changed files with 70 additions and 65 deletions
+16
View File
@@ -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.
+1 -8
View File
@@ -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)
+2 -2
View File
@@ -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
View File
@@ -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"},
-33
View File
@@ -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'