mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-07 22:14:03 -05:00
All Runtime Status Codes should be in the RuntimeStatus enum (#9601)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user