mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 16:08:23 +08:00
* feat: support LLM profiles on cloud backends
Cloud backends had no access to LLM profiles: the LLM was configured through the flat cloud settings form and the chat composer showed a plain model picker. The cloud app-server already exposes the full profile machinery under /api/v1/settings/profiles, so wire agent-canvas to it.
- ProfilesService branches to a new cloud service (src/api/cloud/profiles-service.api.ts) when the active backend is cloud, mirroring how SettingsService delegates to fetchCloudSettings; the profile hooks and the settings manager UI then work transparently.
- The LLM settings route renders the profile manager for both backends.
- Chat-level switching on cloud: the composer shows the profile switcher, /model lists/switches profiles, and per-conversation switching routes through the app-server's server-resolved /app-conversations/{id}/switch_profile endpoint.
* fix: gate cloud LLM profile management on org role (owner/admin)
Cloud org members (role=member) have VIEW_ORG_SETTINGS only: they may view but not create/edit/rename/delete/activate LLM profiles, which the app-server reserves for owner/admin (EDIT_ORG_SETTINGS). The cloud profile settings page exposed every mutating control to all members — reported in PR review.
Surface the caller's role from the existing GET /api/organizations/{orgId}/me call and add useCanManageLlmProfiles() (local backends always true; cloud only for owner/admin, reusing the /me query so no extra request). The settings profile manager hides Add and the per-row actions menu (edit/rename/duplicate/delete/activate) for members, rendering a read-only list.
Per-conversation profile switching in chat stays available to members: the app-server's /app-conversations/{id}/switch_profile route is not org-permission-gated, so switching one's own conversation is a permitted usage action, distinct from managing the org's profiles.
* fix: read profile-manage permission from the server, with role fallback
Review follow-up: instead of hardcoding the role->permission mapping on the client (role === owner||admin), useCanManageLlmProfiles now reads the server-defined `permissions` from GET /api/organizations/{orgId}/me and gates on `edit_org_settings`. Falls back to the previous role check when an older app-server doesn't return `permissions`, so it keeps working against either backend version.
Backend companion (adds `permissions` to /me): OpenHands/OpenHands#15048.
* fix: enforce LLM-profile permissions server-side via the org-gated routes
Route cloud profile CRUD/activate through /api/organizations/{orgId}/profiles, which require EDIT_ORG_SETTINGS server-side — so a member's mutation is rejected with 403 even on a direct API call, not just hidden by the client gate. Falls back to the ungated per-user /api/v1/settings/profiles route only when no org is bound (legacy keys).
A shared cloudProfilesTarget() picks the base path; get/activate normalize the org shapes (llm -> config / llm_applied). Completes the 'validate on both client and server' review point alongside the client gate (companion: OpenHands/OpenHands#15048 exposes the permission on /me).
---------
Co-authored-by: hieptl <hieptl.developer@gmail.com>