diff --git a/frontend/src/types/runtime-status.ts b/frontend/src/types/runtime-status.ts index 982a2d8d2a..edc35f23bd 100644 --- a/frontend/src/types/runtime-status.ts +++ b/frontend/src/types/runtime-status.ts @@ -5,4 +5,15 @@ export type RuntimeStatus = | "STATUS$RUNTIME_STARTED" | "STATUS$SETTING_UP_WORKSPACE" | "STATUS$SETTING_UP_GIT_HOOKS" - | "STATUS$READY"; + | "STATUS$READY" + | "STATUS$ERROR" + | "STATUS$ERROR_RUNTIME_DISCONNECTED" + | "STATUS$ERROR_LLM_AUTHENTICATION" + | "STATUS$ERROR_LLM_SERVICE_UNAVAILABLE" + | "STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR" + | "STATUS$ERROR_LLM_OUT_OF_CREDITS" + | "STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION" + | "CHAT_INTERFACE$AGENT_RATE_LIMITED_STOPPED_MESSAGE" + | "STATUS$GIT_PROVIDER_AUTHENTICATION_ERROR" + | "STATUS$LLM_RETRY" + | "STATUS$ERROR_MEMORY"; diff --git a/frontend/src/utils/status.ts b/frontend/src/utils/status.ts index 93c941077c..6e50fa9e0b 100644 --- a/frontend/src/utils/status.ts +++ b/frontend/src/utils/status.ts @@ -88,11 +88,15 @@ export function getStatusCode( if (conversationStatus === "STOPPED" || runtimeStatus === "STATUS$STOPPED") { return I18nKey.CHAT_INTERFACE$STOPPED; } - if (runtimeStatus === "STATUS$BUILDING_RUNTIME") { - return I18nKey.STATUS$BUILDING_RUNTIME; - } - if (runtimeStatus === "STATUS$STARTING_RUNTIME") { - return I18nKey.STATUS$STARTING_RUNTIME; + if ( + runtimeStatus && + !["STATUS$READY", "STATUS$RUNTIME_STARTED"].includes(runtimeStatus) + ) { + const result = (I18nKey as { [key: string]: string })[runtimeStatus]; + if (result) { + return result; + } + return runtimeStatus; } if (webSocketStatus === "DISCONNECTED") { return I18nKey.CHAT_INTERFACE$DISCONNECTED; diff --git a/openhands/controller/agent_controller.py b/openhands/controller/agent_controller.py index 06587ab5cf..a1ce7dfbf3 100644 --- a/openhands/controller/agent_controller.py +++ b/openhands/controller/agent_controller.py @@ -75,6 +75,7 @@ from openhands.events.observation import ( from openhands.events.serialization.event import truncate_content from openhands.llm.llm import LLM from openhands.llm.metrics import Metrics +from openhands.runtime.runtime_status import RuntimeStatus from openhands.storage.files import FileStore # note: RESUME is only available on web GUI @@ -248,10 +249,10 @@ class AgentController: self.state.last_error = f'{type(e).__name__}: {str(e)}' if self.status_callback is not None: - err_id = '' + runtime_status = RuntimeStatus.ERROR if isinstance(e, AuthenticationError): - err_id = 'STATUS$ERROR_LLM_AUTHENTICATION' - self.state.last_error = err_id + runtime_status = RuntimeStatus.ERROR_LLM_AUTHENTICATION + self.state.last_error = runtime_status.value elif isinstance( e, ( @@ -260,20 +261,20 @@ class AgentController: APIError, ), ): - err_id = 'STATUS$ERROR_LLM_SERVICE_UNAVAILABLE' - self.state.last_error = err_id + runtime_status = RuntimeStatus.ERROR_LLM_SERVICE_UNAVAILABLE + self.state.last_error = runtime_status.value elif isinstance(e, InternalServerError): - err_id = 'STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR' - self.state.last_error = err_id + runtime_status = RuntimeStatus.ERROR_LLM_INTERNAL_SERVER_ERROR + self.state.last_error = runtime_status.value elif isinstance(e, BadRequestError) and 'ExceededBudget' in str(e): - err_id = 'STATUS$ERROR_LLM_OUT_OF_CREDITS' - self.state.last_error = err_id + runtime_status = RuntimeStatus.ERROR_LLM_OUT_OF_CREDITS + self.state.last_error = runtime_status.value elif isinstance(e, ContentPolicyViolationError) or ( isinstance(e, BadRequestError) and 'ContentPolicyViolationError' in str(e) ): - err_id = 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION' - self.state.last_error = err_id + runtime_status = RuntimeStatus.ERROR_LLM_CONTENT_POLICY_VIOLATION + self.state.last_error = runtime_status.value elif isinstance(e, RateLimitError): # Check if this is the final retry attempt if ( @@ -283,14 +284,14 @@ class AgentController: ): # All retries exhausted, set to ERROR state with a special message self.state.last_error = ( - 'CHAT_INTERFACE$AGENT_RATE_LIMITED_STOPPED_MESSAGE' + RuntimeStatus.AGENT_RATE_LIMITED_STOPPED_MESSAGE.value ) await self.set_agent_state_to(AgentState.ERROR) else: # Still retrying, set to RATE_LIMITED state await self.set_agent_state_to(AgentState.RATE_LIMITED) return - self.status_callback('error', err_id, self.state.last_error) + self.status_callback('error', runtime_status, self.state.last_error) # Set the agent state to ERROR after storing the reason await self.set_agent_state_to(AgentState.ERROR) diff --git a/openhands/core/loop.py b/openhands/core/loop.py index 9163373c14..7bf44bd68e 100644 --- a/openhands/core/loop.py +++ b/openhands/core/loop.py @@ -5,6 +5,7 @@ from openhands.core.logger import openhands_logger as logger from openhands.core.schema import AgentState from openhands.memory.memory import Memory from openhands.runtime.base import Runtime +from openhands.runtime.runtime_status import RuntimeStatus async def run_agent_until_done( @@ -19,7 +20,7 @@ async def run_agent_until_done( Note that runtime must be connected before being passed in here. """ - def status_callback(msg_type: str, msg_id: str, msg: str) -> None: + def status_callback(msg_type: str, runtime_status: RuntimeStatus, msg: str) -> None: if msg_type == 'error': logger.error(msg) if controller: diff --git a/openhands/memory/memory.py b/openhands/memory/memory.py index 63b5193cc1..5a1eb82e36 100644 --- a/openhands/memory/memory.py +++ b/openhands/memory/memory.py @@ -23,6 +23,7 @@ from openhands.microagent import ( load_microagents_from_dir, ) from openhands.runtime.base import Runtime +from openhands.runtime.runtime_status import RuntimeStatus from openhands.utils.prompt import ( ConversationInstructions, RepositoryInfo, @@ -133,7 +134,7 @@ class Memory: except Exception as e: error_str = f'Error: {str(e.__class__.__name__)}' logger.error(error_str) - self.send_error_message('STATUS$ERROR_MEMORY', error_str) + self.set_runtime_status(RuntimeStatus.ERROR_MEMORY, error_str) return def _on_workspace_context_recall( @@ -361,22 +362,24 @@ class Memory: content=conversation_instructions or '' ) - def send_error_message(self, message_id: str, message: str): + def set_runtime_status(self, status: RuntimeStatus, message: str): """Sends an error message if the callback function was provided.""" if self.status_callback: try: if self.loop is None: self.loop = asyncio.get_running_loop() asyncio.run_coroutine_threadsafe( - self._send_status_message('error', message_id, message), self.loop + self._set_runtime_status('error', status, message), self.loop ) - except RuntimeError as e: + except (RuntimeError, KeyError) as e: logger.error( f'Error sending status message: {e.__class__.__name__}', stack_info=False, ) - async def _send_status_message(self, msg_type: str, id: str, message: str): + async def _set_runtime_status( + self, msg_type: str, runtime_status: RuntimeStatus, message: str + ): """Sends a status message to the client.""" if self.status_callback: - self.status_callback(msg_type, id, message) + self.status_callback(msg_type, runtime_status, message) diff --git a/openhands/runtime/base.py b/openhands/runtime/base.py index 35ace653b3..ce3624acd4 100644 --- a/openhands/runtime/base.py +++ b/openhands/runtime/base.py @@ -113,7 +113,7 @@ class Runtime(FileEditRuntimeMixin): config: OpenHandsConfig initial_env_vars: dict[str, str] attach_to_existing: bool - status_callback: Callable[[str, str, str], None] | None + status_callback: Callable[[str, RuntimeStatus, str], None] | None runtime_status: RuntimeStatus | None _runtime_initialized: bool = False @@ -124,7 +124,7 @@ class Runtime(FileEditRuntimeMixin): sid: str = 'default', plugins: list[PluginRequirement] | None = None, env_vars: dict[str, str] | None = None, - status_callback: Callable[[str, str, str], None] | None = None, + status_callback: Callable[[str, RuntimeStatus, str], None] | None = None, attach_to_existing: bool = False, headless_mode: bool = False, user_id: str | None = None, @@ -207,16 +207,13 @@ class Runtime(FileEditRuntimeMixin): message = f'[runtime {self.sid}] {message}' getattr(logger, level)(message, stacklevel=2) - def set_runtime_status(self, runtime_status: RuntimeStatus): + def set_runtime_status( + self, runtime_status: RuntimeStatus, msg: str = '', level: str = 'info' + ): """Sends a status message if the callback function was provided.""" self.runtime_status = runtime_status if self.status_callback: - msg_id: str = runtime_status.value # type: ignore - self.status_callback('info', msg_id, runtime_status.message) - - def send_error_message(self, message_id: str, message: str): - if self.status_callback: - self.status_callback('error', message_id, message) + self.status_callback(level, runtime_status, msg) # ==================================================================== @@ -344,15 +341,13 @@ class Runtime(FileEditRuntimeMixin): else: observation = await call_sync_from_async(self.run_action, event) except Exception as e: - err_id = '' - if isinstance(e, httpx.NetworkError) or isinstance( - e, AgentRuntimeDisconnectedError - ): - err_id = 'STATUS$ERROR_RUNTIME_DISCONNECTED' + runtime_status = RuntimeStatus.ERROR + if isinstance(e, (httpx.NetworkError, AgentRuntimeDisconnectedError)): + runtime_status = RuntimeStatus.ERROR_RUNTIME_DISCONNECTED error_message = f'{type(e).__name__}: {str(e)}' self.log('error', f'Unexpected error while running action: {error_message}') self.log('error', f'Problematic action: {str(event)}') - self.send_error_message(err_id, error_message) + self.set_runtime_status(runtime_status, error_message) return observation._cause = event.id # type: ignore[attr-defined] @@ -397,7 +392,7 @@ class Runtime(FileEditRuntimeMixin): if self.status_callback: self.status_callback( - 'info', 'STATUS$SETTING_UP_WORKSPACE', 'Setting up workspace...' + 'info', RuntimeStatus.SETTING_UP_WORKSPACE, 'Setting up workspace...' ) dir_name = selected_repository.split('/')[-1] @@ -438,7 +433,7 @@ class Runtime(FileEditRuntimeMixin): if self.status_callback: self.status_callback( - 'info', 'STATUS$SETTING_UP_WORKSPACE', 'Setting up workspace...' + 'info', RuntimeStatus.SETTING_UP_WORKSPACE, 'Setting up workspace...' ) # setup scripts time out after 10 minutes @@ -470,7 +465,7 @@ class Runtime(FileEditRuntimeMixin): if self.status_callback: self.status_callback( - 'info', 'STATUS$SETTING_UP_GIT_HOOKS', 'Setting up git hooks...' + 'info', RuntimeStatus.SETTING_UP_GIT_HOOKS, 'Setting up git hooks...' ) # Ensure the git hooks directory exists diff --git a/openhands/runtime/impl/cli/cli_runtime.py b/openhands/runtime/impl/cli/cli_runtime.py index 1b076d2084..c8f661b84b 100644 --- a/openhands/runtime/impl/cli/cli_runtime.py +++ b/openhands/runtime/impl/cli/cli_runtime.py @@ -113,7 +113,7 @@ class CLIRuntime(Runtime): sid: str = 'default', plugins: list[PluginRequirement] | None = None, env_vars: dict[str, str] | None = None, - status_callback: Callable[[str, str, str], None] | None = None, + status_callback: Callable[[str, RuntimeStatus, str], None] | None = None, attach_to_existing: bool = False, headless_mode: bool = False, user_id: str | None = None, diff --git a/openhands/runtime/impl/kubernetes/kubernetes_runtime.py b/openhands/runtime/impl/kubernetes/kubernetes_runtime.py index fb11631b3f..b7cb3aeec4 100644 --- a/openhands/runtime/impl/kubernetes/kubernetes_runtime.py +++ b/openhands/runtime/impl/kubernetes/kubernetes_runtime.py @@ -252,8 +252,8 @@ class KubernetesRuntime(ActionExecutionClient): await call_sync_from_async(self._wait_until_ready) except Exception as alive_error: self.log('error', f'Failed to connect to runtime: {alive_error}') - self.send_error_message( - 'ERROR$RUNTIME_CONNECTION', + self.set_runtime_status( + RuntimeStatus.ERROR_RUNTIME_DISCONNECTED, f'Failed to connect to runtime: {alive_error}', ) raise AgentRuntimeDisconnectedError( diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 1895fa0692..34eea74056 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -134,7 +134,7 @@ class LocalRuntime(ActionExecutionClient): sid: str = 'default', plugins: list[PluginRequirement] | None = None, env_vars: dict[str, str] | None = None, - status_callback: Callable[[str, str, str], None] | None = None, + status_callback: Callable[[str, RuntimeStatus, str], None] | None = None, attach_to_existing: bool = False, headless_mode: bool = True, user_id: str | None = None, diff --git a/openhands/runtime/runtime_status.py b/openhands/runtime/runtime_status.py index de9b3b6c43..1c22d4acab 100644 --- a/openhands/runtime/runtime_status.py +++ b/openhands/runtime/runtime_status.py @@ -2,14 +2,23 @@ from enum import Enum class RuntimeStatus(Enum): - def __init__(self, value: str, message: str): - self._value_ = value - self.message = message - - STOPPED = 'STATUS$STOPPED', 'Stopped' - BUILDING_RUNTIME = 'STATUS$BUILDING_RUNTIME', 'Building runtime...' - STARTING_RUNTIME = 'STATUS$STARTING_RUNTIME', 'Starting runtime...' - RUNTIME_STARTED = 'STATUS$RUNTIME_STARTED', 'Runtime started...' - SETTING_UP_WORKSPACE = 'STATUS$SETTING_UP_WORKSPACE', 'Setting up workspace...' - SETTING_UP_GIT_HOOKS = 'STATUS$SETTING_UP_GIT_HOOKS', 'Setting up git hooks...' - READY = 'STATUS$READY', 'Ready...' + STOPPED = 'STATUS$STOPPED' + BUILDING_RUNTIME = 'STATUS$BUILDING_RUNTIME' + STARTING_RUNTIME = 'STATUS$STARTING_RUNTIME' + RUNTIME_STARTED = 'STATUS$RUNTIME_STARTED' + SETTING_UP_WORKSPACE = 'STATUS$SETTING_UP_WORKSPACE' + SETTING_UP_GIT_HOOKS = 'STATUS$SETTING_UP_GIT_HOOKS' + READY = 'STATUS$READY' + ERROR = 'STATUS$ERROR' + ERROR_RUNTIME_DISCONNECTED = 'STATUS$ERROR_RUNTIME_DISCONNECTED' + ERROR_LLM_AUTHENTICATION = 'STATUS$ERROR_LLM_AUTHENTICATION' + ERROR_LLM_SERVICE_UNAVAILABLE = 'STATUS$ERROR_LLM_SERVICE_UNAVAILABLE' + ERROR_LLM_INTERNAL_SERVER_ERROR = 'STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR' + ERROR_LLM_OUT_OF_CREDITS = 'STATUS$ERROR_LLM_OUT_OF_CREDITS' + ERROR_LLM_CONTENT_POLICY_VIOLATION = 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION' + AGENT_RATE_LIMITED_STOPPED_MESSAGE = ( + 'CHAT_INTERFACE$AGENT_RATE_LIMITED_STOPPED_MESSAGE' + ) + GIT_PROVIDER_AUTHENTICATION_ERROR = 'STATUS$GIT_PROVIDER_AUTHENTICATION_ERROR' + LLM_RETRY = 'STATUS$LLM_RETRY' + ERROR_MEMORY = 'STATUS$ERROR_MEMORY' diff --git a/openhands/server/routes/manage_conversations.py b/openhands/server/routes/manage_conversations.py index 7348afe4d3..a7c7ab5570 100644 --- a/openhands/server/routes/manage_conversations.py +++ b/openhands/server/routes/manage_conversations.py @@ -32,6 +32,7 @@ from openhands.integrations.service_types import ( ) from openhands.llm.llm import LLM from openhands.runtime import get_runtime_cls +from openhands.runtime.runtime_status import RuntimeStatus from openhands.server.data_models.agent_loop_info import AgentLoopInfo from openhands.server.data_models.conversation_info import ConversationInfo from openhands.server.data_models.conversation_info_result_set import ( @@ -189,7 +190,7 @@ async def new_conversation( content={ 'status': 'error', 'message': str(e), - 'msg_id': 'STATUS$ERROR_LLM_AUTHENTICATION', + 'msg_id': RuntimeStatus.ERROR_LLM_AUTHENTICATION.value, }, status_code=status.HTTP_400_BAD_REQUEST, ) @@ -199,7 +200,7 @@ async def new_conversation( content={ 'status': 'error', 'message': str(e), - 'msg_id': 'STATUS$GIT_PROVIDER_AUTHENTICATION_ERROR', + 'msg_id': RuntimeStatus.GIT_PROVIDER_AUTHENTICATION_ERROR.value, }, status_code=status.HTTP_400_BAD_REQUEST, ) diff --git a/openhands/server/session/agent_session.py b/openhands/server/session/agent_session.py index 2f5032e044..b4212ddc39 100644 --- a/openhands/server/session/agent_session.py +++ b/openhands/server/session/agent_session.py @@ -27,6 +27,7 @@ from openhands.microagent.microagent import BaseMicroagent from openhands.runtime import get_runtime_cls from openhands.runtime.base import Runtime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime +from openhands.runtime.runtime_status import RuntimeStatus from openhands.security import SecurityAnalyzer, options from openhands.storage.data_models.user_secrets import UserSecrets from openhands.storage.files import FileStore @@ -377,7 +378,7 @@ class AgentSession: self.logger.error(f'Runtime initialization failed: {e}') if self._status_callback: self._status_callback( - 'error', 'STATUS$ERROR_RUNTIME_DISCONNECTED', str(e) + 'error', RuntimeStatus.ERROR_RUNTIME_DISCONNECTED, str(e) ) return False diff --git a/openhands/server/session/session.py b/openhands/server/session/session.py index cca518a0e8..566b3f0b5c 100644 --- a/openhands/server/session/session.py +++ b/openhands/server/session/session.py @@ -29,6 +29,7 @@ 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.llm.llm import LLM +from openhands.runtime.runtime_status import RuntimeStatus from openhands.server.session.agent_session import AgentSession from openhands.server.session.conversation_init_data import ConversationInitData from openhands.storage.data_models.settings import Settings @@ -249,9 +250,8 @@ class Session: ) def _notify_on_llm_retry(self, retries: int, max: int) -> None: - msg_id = 'STATUS$LLM_RETRY' self.queue_status_message( - 'info', msg_id, f'Retrying LLM request, {retries} / {max}' + 'info', RuntimeStatus.LLM_RETRY, f'Retrying LLM request, {retries} / {max}' ) def on_event(self, event: Event) -> None: @@ -337,7 +337,9 @@ class Session: """Sends an error message to the client.""" await self.send({'error': True, 'message': message}) - async def _send_status_message(self, msg_type: str, id: str, message: str) -> None: + async def _send_status_message( + self, msg_type: str, runtime_status: RuntimeStatus, message: str + ) -> None: """Sends a status message to the client.""" if msg_type == 'error': agent_session = self.agent_session @@ -349,11 +351,18 @@ class Session: extra={'signal': 'agent_status_error'}, ) await self.send( - {'status_update': True, 'type': msg_type, 'id': id, 'message': message} + { + 'status_update': True, + 'type': msg_type, + 'id': runtime_status.value, + 'message': message, + } ) - def queue_status_message(self, msg_type: str, id: str, message: str) -> None: + def queue_status_message( + self, msg_type: str, runtime_status: RuntimeStatus, message: str + ) -> None: """Queues a status message to be sent asynchronously.""" asyncio.run_coroutine_threadsafe( - self._send_status_message(msg_type, id, message), self.loop + self._send_status_message(msg_type, runtime_status, message), self.loop ) diff --git a/tests/unit/test_agent_controller.py b/tests/unit/test_agent_controller.py index 1d16f7bc13..17c2de31a1 100644 --- a/tests/unit/test_agent_controller.py +++ b/tests/unit/test_agent_controller.py @@ -44,6 +44,7 @@ from openhands.runtime.base import Runtime from openhands.runtime.impl.action_execution.action_execution_client import ( ActionExecutionClient, ) +from openhands.runtime.runtime_status import RuntimeStatus from openhands.storage.memory import InMemoryFileStore @@ -229,12 +230,15 @@ async def test_react_to_content_policy_violation( # Verify the status callback was called with correct parameters mock_status_callback.assert_called_once_with( 'error', - 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION', - 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION', + RuntimeStatus.ERROR_LLM_CONTENT_POLICY_VIOLATION, + RuntimeStatus.ERROR_LLM_CONTENT_POLICY_VIOLATION.value, ) # Verify the state was updated correctly - assert controller.state.last_error == 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION' + assert ( + controller.state.last_error + == RuntimeStatus.ERROR_LLM_CONTENT_POLICY_VIOLATION.value + ) assert controller.state.agent_state == AgentState.ERROR await controller.close() @@ -829,13 +833,15 @@ async def test_notify_on_llm_retry(mock_agent, mock_event_stream, mock_status_ca ) def notify_on_llm_retry(attempt, max_attempts): - controller.status_callback('info', 'STATUS$LLM_RETRY', ANY) + controller.status_callback('info', RuntimeStatus.LLM_RETRY, ANY) # Attach the retry listener to the agent's LLM controller.agent.llm.retry_listener = notify_on_llm_retry controller.agent.llm.retry_listener(1, 2) - controller.status_callback.assert_called_once_with('info', 'STATUS$LLM_RETRY', ANY) + controller.status_callback.assert_called_once_with( + 'info', RuntimeStatus.LLM_RETRY, ANY + ) await controller.close() diff --git a/tests/unit/test_conversation.py b/tests/unit/test_conversation.py index 79808edbfb..9b6ab18132 100644 --- a/tests/unit/test_conversation.py +++ b/tests/unit/test_conversation.py @@ -15,6 +15,7 @@ from openhands.integrations.service_types import ( SuggestedTask, TaskType, ) +from openhands.runtime.runtime_status import RuntimeStatus from openhands.server.data_models.conversation_info import ConversationInfo from openhands.server.data_models.conversation_info_result_set import ( ConversationInfoResultSet, @@ -405,7 +406,9 @@ async def test_new_conversation_invalid_session_api_key(provider_handler_mock): assert 'Error authenticating with the LLM provider' in response.body.decode( 'utf-8' ) - assert 'STATUS$ERROR_LLM_AUTHENTICATION' in response.body.decode('utf-8') + assert RuntimeStatus.ERROR_LLM_AUTHENTICATION.value in response.body.decode( + 'utf-8' + ) @pytest.mark.asyncio @@ -575,7 +578,7 @@ async def test_new_conversation_with_provider_authentication_error( assert json.loads(response.body.decode('utf-8')) == { 'status': 'error', 'message': 'auth error', - 'msg_id': 'STATUS$GIT_PROVIDER_AUTHENTICATION_ERROR', + 'msg_id': RuntimeStatus.GIT_PROVIDER_AUTHENTICATION_ERROR.value, } # Verify that verify_repo_provider was called with the repository diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py index b14dc82a4b..3e59bda492 100644 --- a/tests/unit/test_session.py +++ b/tests/unit/test_session.py @@ -7,6 +7,7 @@ from litellm.exceptions import ( from openhands.core.config.llm_config import LLMConfig from openhands.core.config.openhands_config import OpenHandsConfig +from openhands.runtime.runtime_status import RuntimeStatus from openhands.server.session.session import Session from openhands.storage.memory import InMemoryFileStore @@ -64,6 +65,6 @@ async def test_notify_on_llm_retry( assert mock_litellm_completion.call_count == 2 session.queue_status_message.assert_called_once_with( - 'info', 'STATUS$LLM_RETRY', ANY + 'info', RuntimeStatus.LLM_RETRY, ANY ) await session.close()