Skip to content

fix: fetch litellm user rows from /v2/user/info - #197

Draft
jlav wants to merge 2 commits into
mainfrom
jl/litellm-user-info-v2
Draft

fix: fetch litellm user rows from /v2/user/info#197
jlav wants to merge 2 commits into
mainfrom
jl/litellm-user-info-v2

Conversation

@jlav

@jlav jlav commented Aug 18, 2026

Copy link
Copy Markdown
Member

HUMAN:

  • A human has tested these changes.

AGENT:


Why

Login provisioning calls litellm's GET /user/info during the OAuth callback. That endpoint inlines every team the user belongs to, member rosters included. Every user gets added to one shared team, so the response is currently 10.3 MiB, and 99.89% of it is that team's 99,100-entry member list.

The call takes around 5s and httpx aborts it at its 5s default. 40-50% of them are failing right now. That surfaces as a 500 on /oauth/keycloak/callback, leaving a keycloak account with no OpenHands User row. The payload grows about 26 KiB/day with signups, so it degrades on its own.

/v2/user/info answers with the user row on its own. Keys and team objects are left out.

Summary

  • _get_user reads from /v2/user/info and returns the flat user row, or None on a 404. migrate_entries consumes that row directly.
  • _user_exists calls _get_user rather than issuing its own /user/info request.

Issue Number

Nothing filed in this repo. This came out of the activation-rate drop investigation tracked elsewhere as #638.

How to Test

Unit tests:

PYTHONPATH="enterprise:." pytest enterprise/tests/unit/test_lite_llm_manager.py

The new TestGetUser cases cover the v2 request, an unknown user, and a 500.

To confirm the v2 route is reachable through the ingress and see the size difference, against any real user id:

curl -s -o /dev/null -w '%{size_download}\n' -H "x-goog-api-key: $LITE_LLM_API_KEY" \
  "$LITE_LLM_API_URL/user/info?user_id=<uuid>"
curl -s -o /dev/null -w '%{size_download}\n' -H "x-goog-api-key: $LITE_LLM_API_KEY" \
  "$LITE_LLM_API_URL/v2/user/info?user_id=<uuid>"

End to end, signing in with a fresh keycloak account on staging should show GET /v2/user/info in the litellm access logs where GET /user/info used to be, at roughly a kilobyte instead of ten megabytes.

Video/Screenshots

n/a

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

/v2/user/info needs litellm 1.84 or newer. The proxy is on 1.94.1.

_get_all_keys_for_user still uses /user/info, since v2 omits keys. It runs on settings save rather than login, so it's not behind the callback 500s, but it does pull the same payload. /key/list?return_full_object=true is the follow-up. It needs pagination handling (size defaults to 10, max 100) and an exact user id match, because for a proxy-admin key litellm treats user_id as a case-insensitive contains.

Separately: the LITELLM_MANAGEMENT_TIMEOUT wiring from #189 shipped in 1.53.0. Before that commit the provisioning client passed no timeout= at all, which is where the 5s ceiling came from. Independent of this PR, but it explains the 5002ms wall in the write-up.


Enterprise server image for this PR:

ghcr.io/openhands/enterprise-server:sha-f568b2a

@github-actions github-actions Bot added the type: fix A bug fix label Aug 18, 2026
@github-actions

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  enterprise/storage
  lite_llm_manager.py 325-330, 806-814, 926-937, 1578-1583
Project Total  

This report was generated by python-coverage-comment-action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant