Compare commits

...

2 Commits

Author SHA1 Message Date
John-Mason Shackelford
b03f8eb80d Add debug logging for LiteLLM user creation flow
Adds comprehensive logging to diagnose silent failures in the
update_settings_with_litellm_default function:

- Log environment variables at start (LITE_LLM_API_KEY set, LITE_LLM_API_URL, LOCAL_DEPLOYMENT)
- Log when missing config causes early return
- Log when LOCAL_DEPLOYMENT mode is active
- Wrap keycloak token manager call with try/catch and logging
- Log HTTP errors from /user/info endpoint with status code and response
- Log request errors (connection failures, timeouts) with error details
- Log user delete response status
- Log user creation attempt details
- Enhanced duplicate_user_email warning with response details

Co-authored-by: openhands <openhands@all-hands.dev>
2026-01-30 15:39:20 -05:00
mamoodi
7fbb48c406 Release 0.62.0 2025-11-11 09:58:32 -05:00
9 changed files with 104 additions and 19 deletions

View File

@@ -159,7 +159,7 @@ poetry run pytest ./tests/unit/test_*.py
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker
container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:0.61-nikolaik`
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:0.62-nikolaik`
## Develop inside Docker container

View File

@@ -82,17 +82,17 @@ You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)
You can also run OpenHands directly with Docker:
```bash
docker pull docker.openhands.dev/openhands/runtime:0.61-nikolaik
docker pull docker.openhands.dev/openhands/runtime:0.62-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.openhands.dev/openhands/runtime:0.61-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.openhands.dev/openhands/runtime:0.62-nikolaik \
-e LOG_ALL_EVENTS=true \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ~/.openhands:/.openhands \
-p 3000:3000 \
--add-host host.docker.internal:host-gateway \
--name openhands-app \
docker.openhands.dev/openhands/openhands:0.61
docker.openhands.dev/openhands/openhands:0.62
```
</details>

View File

@@ -12,7 +12,7 @@ services:
- SANDBOX_API_HOSTNAME=host.docker.internal
- DOCKER_HOST_ADDR=host.docker.internal
#
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/openhands/runtime:0.61-nikolaik}
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/openhands/runtime:0.62-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports:

View File

@@ -7,7 +7,7 @@ services:
image: openhands:latest
container_name: openhands-app-${DATE:-}
environment:
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.openhands.dev/openhands/runtime:0.61-nikolaik}
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.openhands.dev/openhands/runtime:0.62-nikolaik}
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of ~/.openhands for this user
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports:

View File

@@ -202,17 +202,54 @@ class SaasSettingsStore(SettingsStore):
) -> Settings | None:
logger.info(
'saas_settings_store:update_settings_with_litellm_default:start',
extra={'user_id': self.user_id},
extra={
'user_id': self.user_id,
'LITE_LLM_API_KEY_set': LITE_LLM_API_KEY is not None,
'LITE_LLM_API_URL': LITE_LLM_API_URL,
'LOCAL_DEPLOYMENT': os.environ.get('LOCAL_DEPLOYMENT'),
},
)
if LITE_LLM_API_KEY is None or LITE_LLM_API_URL is None:
logger.warning(
'saas_settings_store:update_settings_with_litellm_default:missing_config',
extra={
'user_id': self.user_id,
'LITE_LLM_API_KEY_set': LITE_LLM_API_KEY is not None,
'LITE_LLM_API_URL_set': LITE_LLM_API_URL is not None,
},
)
return None
local_deploy = os.environ.get('LOCAL_DEPLOYMENT', None)
key = LITE_LLM_API_KEY
if local_deploy:
logger.info(
'saas_settings_store:update_settings_with_litellm_default:local_deployment_mode',
extra={'user_id': self.user_id, 'LOCAL_DEPLOYMENT': local_deploy},
)
if not local_deploy:
# Get user info to add to litellm
token_manager = TokenManager()
keycloak_user_info = (
await token_manager.get_user_info_from_user_id(self.user_id) or {}
try:
keycloak_user_info = (
await token_manager.get_user_info_from_user_id(self.user_id) or {}
)
except Exception as e:
logger.error(
'saas_settings_store:update_settings_with_litellm_default:keycloak_error',
extra={
'user_id': self.user_id,
'error': str(e),
'error_type': type(e).__name__,
},
)
raise
logger.debug(
'saas_settings_store:update_settings_with_litellm_default:keycloak_info_retrieved',
extra={
'user_id': self.user_id,
'has_email': 'email' in keycloak_user_info,
},
)
async with httpx.AsyncClient(
@@ -223,10 +260,37 @@ class SaasSettingsStore(SettingsStore):
) as client:
# Get the previous max budget to prevent accidental loss
# In Litellm a get always succeeds, regardless of whether the user actually exists
response = await client.get(
f'{LITE_LLM_API_URL}/user/info?user_id={self.user_id}'
user_info_url = f'{LITE_LLM_API_URL}/user/info?user_id={self.user_id}'
logger.debug(
'saas_settings_store:update_settings_with_litellm_default:fetching_user_info',
extra={'user_id': self.user_id, 'url': user_info_url},
)
response.raise_for_status()
try:
response = await client.get(user_info_url)
response.raise_for_status()
except httpx.HTTPStatusError as e:
logger.error(
'saas_settings_store:update_settings_with_litellm_default:user_info_http_error',
extra={
'user_id': self.user_id,
'status_code': e.response.status_code,
'response_text': e.response.text[:500] if e.response.text else None,
'url': user_info_url,
},
)
raise
except httpx.RequestError as e:
logger.error(
'saas_settings_store:update_settings_with_litellm_default:user_info_request_error',
extra={
'user_id': self.user_id,
'error': str(e),
'error_type': type(e).__name__,
'url': user_info_url,
},
)
raise
response_json = response.json()
user_info = response_json.get('user_info') or {}
logger.info(
@@ -265,18 +329,39 @@ class SaasSettingsStore(SettingsStore):
# We explicitly delete here to guard against odd inherited settings on upgrade.
# We don't care if this fails with a 404
await client.post(
delete_response = await client.post(
f'{LITE_LLM_API_URL}/user/delete', json={'user_ids': [self.user_id]}
)
logger.debug(
'saas_settings_store:update_settings_with_litellm_default:user_delete_response',
extra={
'user_id': self.user_id,
'status_code': delete_response.status_code,
},
)
# Create the new litellm user
logger.debug(
'saas_settings_store:update_settings_with_litellm_default:creating_user',
extra={
'user_id': self.user_id,
'email': email,
'max_budget': max_budget,
'spend': spend,
},
)
response = await self._create_user_in_lite_llm(
client, email, max_budget, spend
)
if not response.is_success:
logger.warning(
'duplicate_user_email',
extra={'user_id': self.user_id, 'email': email},
extra={
'user_id': self.user_id,
'email': email,
'status_code': response.status_code,
'response_text': response.text[:500] if response.text else None,
},
)
# Litellm insists on unique email addresses - it is possible the email address was registered with a different user.
response = await self._create_user_in_lite_llm(

View File

@@ -1,12 +1,12 @@
{
"name": "openhands-frontend",
"version": "0.61.0",
"version": "0.62.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openhands-frontend",
"version": "0.61.0",
"version": "0.62.0",
"dependencies": {
"@heroui/react": "^2.8.4",
"@heroui/use-infinite-scroll": "^2.2.11",

View File

@@ -1,6 +1,6 @@
{
"name": "openhands-frontend",
"version": "0.61.0",
"version": "0.62.0",
"private": true,
"type": "module",
"engines": {

View File

@@ -40,7 +40,7 @@ Two configuration options are required to use the Kubernetes runtime:
2. **Runtime Container Image**: Specify the container image to use for the runtime environment
```toml
[sandbox]
runtime_container_image = "docker.openhands.dev/openhands/runtime:0.61-nikolaik"
runtime_container_image = "docker.openhands.dev/openhands/runtime:0.62-nikolaik"
```
#### Additional Kubernetes Options

View File

@@ -6,7 +6,7 @@ requires = [
[tool.poetry]
name = "openhands-ai"
version = "0.61.0"
version = "0.62.0"
description = "OpenHands: Code Less, Make More"
authors = [ "OpenHands" ]
license = "MIT"