From 5deb47aacce73acd10d27759bd85e835c33046c6 Mon Sep 17 00:00:00 2001 From: Rohit Malhotra Date: Wed, 2 Apr 2025 22:47:28 -0400 Subject: [PATCH] [Fix]: Plumb full selected repo object from FE (#7668) --- frontend/src/api/open-hands.ts | 2 +- .../hooks/mutation/use-create-conversation.ts | 2 +- .../integrations/github/github_service.py | 8 ----- .../integrations/gitlab/gitlab_service.py | 10 ------ openhands/integrations/provider.py | 26 -------------- openhands/integrations/service_types.py | 3 -- openhands/runtime/base.py | 36 ++++++++++++++++--- .../server/routes/manage_conversations.py | 7 ++-- openhands/server/session/agent_session.py | 13 +++---- .../server/session/conversation_init_data.py | 3 +- 10 files changed, 46 insertions(+), 64 deletions(-) diff --git a/frontend/src/api/open-hands.ts b/frontend/src/api/open-hands.ts index e2744d6c8e..96b3889e9c 100644 --- a/frontend/src/api/open-hands.ts +++ b/frontend/src/api/open-hands.ts @@ -224,7 +224,7 @@ class OpenHands { } static async createConversation( - selectedRepository?: string, + selectedRepository?: GitRepository, initialUserMsg?: string, imageUrls?: string[], replayJson?: string, diff --git a/frontend/src/hooks/mutation/use-create-conversation.ts b/frontend/src/hooks/mutation/use-create-conversation.ts index 5ada04567a..205527fb49 100644 --- a/frontend/src/hooks/mutation/use-create-conversation.ts +++ b/frontend/src/hooks/mutation/use-create-conversation.ts @@ -20,7 +20,7 @@ export const useCreateConversation = () => { if (variables.q) dispatch(setInitialPrompt(variables.q)); return OpenHands.createConversation( - selectedRepository?.full_name || undefined, + selectedRepository || undefined, variables.q, files, replayJson || undefined, diff --git a/openhands/integrations/github/github_service.py b/openhands/integrations/github/github_service.py index 238a40a87e..2b0d0699e9 100644 --- a/openhands/integrations/github/github_service.py +++ b/openhands/integrations/github/github_service.py @@ -318,14 +318,6 @@ class GitHubService(GitService): except Exception: return [] - async def does_repo_exist(self, repository: str) -> bool: - url = f'{self.BASE_URL}/repos/{repository}' - try: - await self._fetch_data(url) - return True - except Exception: - return False - github_service_cls = os.environ.get( 'OPENHANDS_GITHUB_SERVICE_CLS', diff --git a/openhands/integrations/gitlab/gitlab_service.py b/openhands/integrations/gitlab/gitlab_service.py index 1d646b900f..91663851b9 100644 --- a/openhands/integrations/gitlab/gitlab_service.py +++ b/openhands/integrations/gitlab/gitlab_service.py @@ -174,16 +174,6 @@ class GitLabService(GitService): for repo in all_repos ] - async def does_repo_exist(self, repository: str) -> bool: - encoded_repo = quote_plus(repository) - url = f'{self.BASE_URL}/projects/{encoded_repo}' - try: - await self._fetch_data(url) - return True - except Exception as e: - print(e) - return False - gitlab_service_cls = os.environ.get( 'OPENHANDS_GITLAB_SERVICE_CLS', diff --git a/openhands/integrations/provider.py b/openhands/integrations/provider.py index 2843dfc427..d994fcda8a 100644 --- a/openhands/integrations/provider.py +++ b/openhands/integrations/provider.py @@ -228,32 +228,6 @@ class ProviderHandler: return all_repos - async def get_remote_repository_url(self, repository: str) -> str | None: - if not repository: - return None - - provider_domains = { - ProviderType.GITHUB: 'github.com', - ProviderType.GITLAB: 'gitlab.com', - } - - for provider in self.provider_tokens: - try: - service = self._get_service(provider) - repo_exists = await service.does_repo_exist(repository) - if repo_exists: - git_token = self.provider_tokens[provider].token - if git_token and provider in provider_domains: - domain = provider_domains[provider] - - if provider == ProviderType.GITLAB: - return f'https://oauth2:{git_token.get_secret_value()}@{domain}/{repository}.git' - - return f'https://{git_token.get_secret_value()}@{domain}/{repository}.git' - except Exception: - continue - return None - async def set_event_stream_secrets( self, event_stream: EventStream, diff --git a/openhands/integrations/service_types.py b/openhands/integrations/service_types.py index b60bc37a7a..88c80b3711 100644 --- a/openhands/integrations/service_types.py +++ b/openhands/integrations/service_types.py @@ -93,6 +93,3 @@ class GitService(Protocol): ) -> list[Repository]: """Get repositories for the authenticated user""" ... - - async def does_repo_exist(self, repository: str) -> bool: - """Check if a repository exists for the user""" diff --git a/openhands/runtime/base.py b/openhands/runtime/base.py index 166ca6289b..25dc490ed9 100644 --- a/openhands/runtime/base.py +++ b/openhands/runtime/base.py @@ -47,6 +47,7 @@ from openhands.integrations.provider import ( ProviderHandler, ProviderType, ) +from openhands.integrations.service_types import Repository from openhands.microagent import ( BaseMicroAgent, load_microagents_from_dir, @@ -325,14 +326,39 @@ class Runtime(FileEditRuntimeMixin): async def clone_repo( self, git_provider_tokens: PROVIDER_TOKEN_TYPE, - selected_repository: str, + selected_repository: str | Repository, selected_branch: str | None, + repository_provider: ProviderType = ProviderType.GITHUB, ) -> str: - provider_handler = ProviderHandler(provider_tokens=git_provider_tokens) - remote_repo_url = await provider_handler.get_remote_repository_url( - selected_repository + provider_domains = { + ProviderType.GITHUB: 'github.com', + ProviderType.GITLAB: 'gitlab.com', + } + + chosen_provider = ( + repository_provider + if isinstance(selected_repository, str) + else selected_repository.git_provider ) + git_token = git_provider_tokens[chosen_provider].token + if not git_token: + raise RuntimeError('Require valid git token to clone repo') + + domain = provider_domains[chosen_provider] + repository = ( + selected_repository + if isinstance(selected_repository, str) + else selected_repository.full_name + ) + + if chosen_provider == ProviderType.GITLAB: + remote_repo_url = f'https://oauth2:{git_token.get_secret_value()}@{domain}/{repository}.git' + else: + remote_repo_url = ( + f'https://{git_token.get_secret_value()}@{domain}/{repository}.git' + ) + if not remote_repo_url: raise ValueError('Missing either Git token or valid repository') @@ -341,7 +367,7 @@ class Runtime(FileEditRuntimeMixin): 'info', 'STATUS$SETTING_UP_WORKSPACE', 'Setting up workspace...' ) - dir_name = selected_repository.split('/')[-1] + dir_name = repository.split('/')[-1] # Generate a random branch name to avoid conflicts random_str = ''.join( diff --git a/openhands/server/routes/manage_conversations.py b/openhands/server/routes/manage_conversations.py index b96fc5e187..b0df59ebcc 100644 --- a/openhands/server/routes/manage_conversations.py +++ b/openhands/server/routes/manage_conversations.py @@ -13,6 +13,7 @@ from openhands.events.stream import EventStream from openhands.integrations.provider import ( PROVIDER_TOKEN_TYPE, ) +from openhands.integrations.service_types import Repository from openhands.runtime import get_runtime_cls from openhands.server.auth import ( get_github_user_id, @@ -41,7 +42,7 @@ app = APIRouter(prefix='/api') class InitSessionRequest(BaseModel): - selected_repository: str | None = None + selected_repository: Repository | None = None selected_branch: str | None = None initial_user_msg: str | None = None image_urls: list[str] | None = None @@ -51,7 +52,7 @@ class InitSessionRequest(BaseModel): async def _create_new_conversation( user_id: str | None, git_provider_tokens: PROVIDER_TOKEN_TYPE | None, - selected_repository: str | None, + selected_repository: Repository | None, selected_branch: str | None, initial_user_msg: str | None, image_urls: list[str] | None, @@ -111,7 +112,7 @@ async def _create_new_conversation( title=conversation_title, user_id=user_id, github_user_id=None, - selected_repository=selected_repository, + selected_repository=selected_repository.full_name if selected_repository else selected_repository, selected_branch=selected_branch, ) ) diff --git a/openhands/server/session/agent_session.py b/openhands/server/session/agent_session.py index 6e9cc3416a..220f74054b 100644 --- a/openhands/server/session/agent_session.py +++ b/openhands/server/session/agent_session.py @@ -17,6 +17,7 @@ from openhands.events.action import ChangeAgentStateAction, MessageAction from openhands.events.event import Event, EventSource from openhands.events.stream import EventStream from openhands.integrations.provider import PROVIDER_TOKEN_TYPE, ProviderHandler +from openhands.integrations.service_types import Repository from openhands.memory.memory import Memory from openhands.microagent.microagent import BaseMicroAgent from openhands.runtime import get_runtime_cls @@ -84,7 +85,7 @@ class AgentSession: max_budget_per_task: float | None = None, agent_to_llm_config: dict[str, LLMConfig] | None = None, agent_configs: dict[str, AgentConfig] | None = None, - selected_repository: str | None = None, + selected_repository: Repository | None = None, selected_branch: str | None = None, initial_message: MessageAction | None = None, replay_json: str | None = None, @@ -146,7 +147,7 @@ class AgentSession: repo_directory = None if self.runtime and runtime_connected and selected_repository: - repo_directory = selected_repository.split('/')[-1] + repo_directory = selected_repository.full_name.split('/')[-1] self.memory = await self._create_memory( selected_repository=selected_repository, repo_directory=repo_directory, @@ -257,7 +258,7 @@ class AgentSession: config: AppConfig, agent: Agent, git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None, - selected_repository: str | None = None, + selected_repository: Repository | None = None, selected_branch: str | None = None, ) -> bool: """Creates a runtime instance @@ -393,7 +394,7 @@ class AgentSession: return controller async def _create_memory( - self, selected_repository: str | None, repo_directory: str | None + self, selected_repository: Repository | None, repo_directory: str | None ) -> Memory: memory = Memory( event_stream=self.event_stream, @@ -407,12 +408,12 @@ class AgentSession: # loads microagents from repo/.openhands/microagents microagents: list[BaseMicroAgent] = await call_sync_from_async( - self.runtime.get_microagents_from_selected_repo, selected_repository + self.runtime.get_microagents_from_selected_repo, selected_repository.full_name if selected_repository else None ) memory.load_user_workspace_microagents(microagents) if selected_repository and repo_directory: - memory.set_repository_info(selected_repository, repo_directory) + memory.set_repository_info(selected_repository.full_name, repo_directory) return memory def _maybe_restore_state(self) -> State | None: diff --git a/openhands/server/session/conversation_init_data.py b/openhands/server/session/conversation_init_data.py index 6f825db0d4..41608cbdf5 100644 --- a/openhands/server/session/conversation_init_data.py +++ b/openhands/server/session/conversation_init_data.py @@ -1,6 +1,7 @@ from pydantic import Field from openhands.integrations.provider import PROVIDER_TOKEN_TYPE +from openhands.integrations.service_types import Repository from openhands.server.settings import Settings @@ -10,7 +11,7 @@ class ConversationInitData(Settings): """ git_provider_tokens: PROVIDER_TOKEN_TYPE | None = Field(default=None, frozen=True) - selected_repository: str | None = Field(default=None) + selected_repository: Repository | None = Field(default=None) replay_json: str | None = Field(default=None) selected_branch: str | None = Field(default=None)