Skip to content

feat: LLM profiles route integration (PR C)#393

Merged
neubig merged 16 commits into
mainfrom
feat/llm-profiles-route-integration
May 15, 2026
Merged

feat: LLM profiles route integration (PR C)#393
neubig merged 16 commits into
mainfrom
feat/llm-profiles-route-integration

Conversation

@malhotra5
Copy link
Copy Markdown
Member

Summary

This is Part C of the LLM profiles feature split (from #210).

Depends on:

What's included

Route Integration:

  • LlmSettingsLocalView - Integrated view that combines profile list with create/edit forms
  • Updated /settings route (LLM tab) to render the integrated view
  • Extended SdkSectionSaveControl to expose form values for custom save flows

View Modes:

  1. List mode (default): Shows all profiles with active badge and action menu
  2. Create mode: Shows profile name input + LLM settings form
  3. Edit mode: Loads existing profile config and shows edit form

User Flow:

  • View profiles list with active indicator
  • Click "Add LLM Profile" to create new profile
  • Click Edit from profile action menu to edit existing profile
  • Back/Cancel buttons return to list view
  • Save button creates/updates profile and returns to list

i18n keys added:

  • SETTINGS$CREATE_PROFILE
  • SETTINGS$EDIT_PROFILE
  • SETTINGS$PROFILE_CREATED
  • SETTINGS$PROFILE_UPDATED
  • SETTINGS$MODEL_REQUIRED
  • STATUS$SAVING
  • BUTTON$BACK

Files changed

  • src/components/features/settings/llm-profiles/llm-settings-local-view.tsx (new)
  • src/components/features/settings/llm-profiles/index.ts
  • src/components/features/settings/sdk-settings/sdk-section-page.tsx
  • src/routes/llm-settings.tsx
  • src/i18n/translation.json
  • __tests__/components/settings/llm-profiles/llm-settings-local-view.test.tsx (new)

Testing

  • npm run typecheck passes
  • npm run build passes
  • 7 new tests added for LlmSettingsLocalView

This PR was created by an AI agent (OpenHands) as part of splitting #210 into smaller incremental PRs.

@malhotra5 can click here to continue refining the PR

- Add LlmSettingsLocalView component for integrated profile management
- Extend SdkSectionSaveControl to expose form values for custom save flows
- Update LLM settings route to render profile list with create/edit views
- Add i18n keys for profile create/edit UI (CREATE_PROFILE, EDIT_PROFILE,
  PROFILE_CREATED, PROFILE_UPDATED, MODEL_REQUIRED, STATUS, BUTTON)
- Add test coverage for LlmSettingsLocalView

The integrated view shows:
- Profile list with active badge and action menu
- Add Profile button that opens create form
- Edit button that loads profile config and opens edit form
- Back/Cancel buttons to return to list view

Co-authored-by: openhands <openhands@all-hands.dev>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-canvas Ready Ready Preview, Comment May 15, 2026 2:41am

Request Review

@malhotra5 malhotra5 marked this pull request as ready for review May 12, 2026 21:28
@malhotra5 malhotra5 requested a review from all-hands-bot May 12, 2026 21:28
Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The route integration works and the component structure is reasonable, but there are a few areas that need attention around test quality, component complexity, and evidence of the working UI.

Key concerns:

  • Tests only verify mocked hook calls, not actual save flow behavior
  • Main component has high complexity with multiple responsibilities
  • API key preservation logic could be more robust
  • Missing visual evidence of the working UI flow

Comment thread __tests__/components/settings/llm-profiles/llm-settings-local-view.test.tsx Outdated
Comment thread __tests__/components/settings/llm-profiles/llm-settings-local-view.test.tsx Outdated
Comment thread src/routes/llm-settings.tsx
- Improve mock typing with properly typed helper functions that provide all
  required React Query fields, eliminating incomplete 'as unknown as' casts
- Add integration test that verifies the save flow (fills in profile name,
  clicks save, verifies UI state transitions)
- Add component documentation noting future refactoring opportunity (extract
  useProfileForm, useProfileSave hooks for better testability)
- Document API key preservation behavior: currently preserves existing encrypted
  key in edit mode with no new key; note about potential 'Clear API Key' UX
  enhancement for future
- Document auto-derive name race condition: client-side uniqueness check uses
  render-time state, so concurrent profile creation by another client would
  result in server conflict error (handled gracefully)
- Document default export change in route file: LlmSettingsLocalView is now
  the default export; named export LlmSettingsScreen remains for embedded use

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Member Author

Addressed all feedback in 545fdfa. Ready for another look.

Changes made:

  1. Test improvements: Added properly typed mock helper functions and an integration test verifying the save flow
  2. Documentation: Added component-level JSDoc noting future refactoring opportunity
  3. API key preservation: Documented the behavior and noted future UX enhancement for clearing keys
  4. Auto-derive race condition: Documented that server conflict errors are handled gracefully
  5. Default export: Documented the change and verified no breaking imports

All 8 tests passing.

@malhotra5 malhotra5 requested a review from all-hands-bot May 12, 2026 21:40
The default export of llm-settings.tsx changed to render LlmSettingsLocalView
(the profiles manager). The test needs to import the named export LlmSettingsScreen
to test the form component directly.

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Member Author

Fixed CI failure in e41dc5d - the llm-settings test needed to use the named export LlmSettingsScreen instead of the default export since the default now renders LlmSettingsLocalView.

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Good taste - Clean, incremental feature addition with appropriate separation of concerns.

All previous review feedback has been addressed. The component complexity and API key preservation concerns are well-documented for future iteration.

[RISK ASSESSMENT]
🟢 LOW - Frontend-only UI changes for LLM profile management. No impact on agent behavior, tool execution, or evaluation paths.

VERDICT:
Worth merging - Solid route integration that completes the LLM profiles feature.

KEY INSIGHT:
The architecture appropriately delegates form complexity to the existing LlmSettingsScreen while keeping profile-specific concerns (naming, validation, save orchestration) at the view layer.

…0.6.0

- BrandButton: Change padding from p-2 to px-3 py-2 for better text display
- ProfileRow: Increase active badge vertical padding from py-0.5 to py-1
- Update @openhands/typescript-client from commit SHA to v0.6.0 tag

Co-authored-by: openhands <openhands@all-hands.dev>
… create mode

- LlmSettingsRoute: Render LlmSettingsScreen (standard form) for cloud backends
  and LlmSettingsLocalView (profile manager) for local backends only
- LlmSettingsLocalView: Pass empty initial values in create mode to ensure
  fresh form fields, add key prop to force form remount between profiles
- Add unit tests for cloud vs local backend rendering
- Add unit tests for create mode empty form initialization

Co-authored-by: openhands <openhands@all-hands.dev>
- Fix initialValueOverrides logic to properly check for edit mode AND
  existing initialValues before using them
- Add prefix to edit mode key for clearer remount semantics
- Add unit tests verifying edit mode populates profile name correctly
- Add unit tests verifying getProfile is called with encrypted mode

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
…g.llm

The API returns profile config with llm settings at the top level
(config.model, config.api_key, config.base_url), not nested under
config.llm. Fixed the parsing to read directly from detail.config.

Co-authored-by: openhands <openhands@all-hands.dev>
When editing a profile and changing its name:
1. Rename the profile first using ProfilesService.renameProfile
2. Then save the profile config to the new name
3. If the renamed profile was the active profile, re-activate it
   after the rename (since rename doesn't update active_profile)

This prevents creating duplicate profiles when just changing the name.

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The route integration is well-implemented and all previous review feedback has been properly addressed. However, there's one blocking issue with the dependency update that needs to be fixed before merge.

Comment thread package-lock.json Outdated
The lock file was using git+ssh:// protocol which causes Vercel build
failures since Vercel doesn't have SSH keys configured. Changed to
git+https:// and removed the integrity hash (git deps don't have one).

Co-authored-by: openhands <openhands@all-hands.dev>
@malhotra5 malhotra5 requested a review from all-hands-bot May 12, 2026 23:05
@malhotra5
Copy link
Copy Markdown
Member Author

QA went well!

@VascoSch92
Copy link
Copy Markdown
Member

Issues:

  1. The model I set during the start-up doesn't show un in the LLM Profiles (see video)
startup.mov
  1. It should be "Avaible Profiles" (see image)
avaible_profiles
  1. The color of the "Active" button is not user-friendly (see image of point 2). You can refeer to the color we are using in this PR: feat(settings): add saved LLM profiles (FE) - OSS OpenHands#14149

  2. Instead of "Create Profile" it should be "Back to LLM profiles list" (see image)

Back to LLM profiles list
  1. Profile name modify in "Create Profile" doesn't behave coherently with spaces (see video)
profile_name_modify.mov
  1. The validation logic for the Profile Name and the list view editor are inconsistent. They should be the same. In the former, a hint appears in red when there's an error, while in the latter, the field simply prevents you from typing.

Copy link
Copy Markdown
Member

@VascoSch92 VascoSch92 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of the points that I mentioned can be copy/paste from this PR: OpenHands/OpenHands#14149

…#417)

* fix(profiles): Available Profiles heading translation

* fix(profiles): use brand badge for active profile indicator

* feat(profiles): replace form heading with "Back to LLM profiles list"

* fix(profiles): unify profile-name validation and reject any whitespace

* fix(onboarding): persist onboarding LLM choice as an active profile

* refactor(profiles): drop redundant trim/wrapper after validator change
Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Good taste - Clean feature completion with solid testing and documentation.

This PR successfully completes the LLM profiles route integration (final part of the 3-PR series). The implementation is well-structured with comprehensive test coverage, proper error handling, and good inline documentation of design decisions and known limitations.

[RISK ASSESSMENT]
🟢 LOW - Frontend-only UI changes for profile management. No impact on agent behavior, tool execution, or backend evaluation paths. Built on top of existing profile APIs from merged PRs A & B.

VERDICT:
Worth merging - Solid implementation that completes the profiles feature with appropriate testing and documentation.

@VascoSch92 VascoSch92 self-requested a review May 13, 2026 16:53
- translation.json: merge LLM profile keys (branch) with THINKING/MCP/NAV/SKILLS keys (main)
- file-service-handlers.ts: take main's Docker-aware mock implementation
- mock-file-handlers.test.ts: update expectations to match main's mock data

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Member

@neubig neubig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good and I tried it out and seems to be working!

@neubig neubig dismissed VascoSch92’s stale review May 15, 2026 02:25

Vasco's not here today and asked me to review instead

@neubig neubig enabled auto-merge (squash) May 15, 2026 02:25
@neubig neubig merged commit 78bfe67 into main May 15, 2026
7 checks passed
@neubig neubig deleted the feat/llm-profiles-route-integration branch May 15, 2026 02:46
neubig added a commit that referenced this pull request May 15, 2026
* feat: integrate LLM profiles into settings route (PR C)

- Add LlmSettingsLocalView component for integrated profile management
- Extend SdkSectionSaveControl to expose form values for custom save flows
- Update LLM settings route to render profile list with create/edit views
- Add i18n keys for profile create/edit UI (CREATE_PROFILE, EDIT_PROFILE,
  PROFILE_CREATED, PROFILE_UPDATED, MODEL_REQUIRED, STATUS, BUTTON)
- Add test coverage for LlmSettingsLocalView

The integrated view shows:
- Profile list with active badge and action menu
- Add Profile button that opens create form
- Edit button that loads profile config and opens edit form
- Back/Cancel buttons to return to list view

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: address PR review feedback (#393)

- Improve mock typing with properly typed helper functions that provide all
  required React Query fields, eliminating incomplete 'as unknown as' casts
- Add integration test that verifies the save flow (fills in profile name,
  clicks save, verifies UI state transitions)
- Add component documentation noting future refactoring opportunity (extract
  useProfileForm, useProfileSave hooks for better testability)
- Document API key preservation behavior: currently preserves existing encrypted
  key in edit mode with no new key; note about potential 'Clear API Key' UX
  enhancement for future
- Document auto-derive name race condition: client-side uniqueness check uses
  render-time state, so concurrent profile creation by another client would
  result in server conflict error (handled gracefully)
- Document default export change in route file: LlmSettingsLocalView is now
  the default export; named export LlmSettingsScreen remains for embedded use

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: update llm-settings test to use named export

The default export of llm-settings.tsx changed to render LlmSettingsLocalView
(the profiles manager). The test needs to import the named export LlmSettingsScreen
to test the form component directly.

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix LLM profile button/badge sizing and update typescript-client to v0.6.0

- BrandButton: Change padding from p-2 to px-3 py-2 for better text display
- ProfileRow: Increase active badge vertical padding from py-0.5 to py-1
- Update @openhands/typescript-client from commit SHA to v0.6.0 tag

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix LLM settings to show regular form in cloud mode and empty form in create mode

- LlmSettingsRoute: Render LlmSettingsScreen (standard form) for cloud backends
  and LlmSettingsLocalView (profile manager) for local backends only
- LlmSettingsLocalView: Pass empty initial values in create mode to ensure
  fresh form fields, add key prop to force form remount between profiles
- Add unit tests for cloud vs local backend rendering
- Add unit tests for create mode empty form initialization

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix edit mode form initialization to display profile values

- Fix initialValueOverrides logic to properly check for edit mode AND
  existing initialValues before using them
- Add prefix to edit mode key for clearer remount semantics
- Add unit tests verifying edit mode populates profile name correctly
- Add unit tests verifying getProfile is called with encrypted mode

Co-authored-by: openhands <openhands@all-hands.dev>

* Add debug logging to trace edit profile data flow

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix edit profile config parsing - read from config directly not config.llm

The API returns profile config with llm settings at the top level
(config.model, config.api_key, config.base_url), not nested under
config.llm. Fixed the parsing to read directly from detail.config.

Co-authored-by: openhands <openhands@all-hands.dev>

* Handle profile rename during edit and update active profile

When editing a profile and changing its name:
1. Rename the profile first using ProfilesService.renameProfile
2. Then save the profile config to the new name
3. If the renamed profile was the active profile, re-activate it
   after the rename (since rename doesn't update active_profile)

This prevents creating duplicate profiles when just changing the name.

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix package-lock.json to use https protocol for typescript-client

The lock file was using git+ssh:// protocol which causes Vercel build
failures since Vercel doesn't have SSH keys configured. Changed to
git+https:// and removed the integrity hash (git deps don't have one).

Co-authored-by: openhands <openhands@all-hands.dev>

* fix(profiles): Available Profiles heading translation

* fix(profiles): use brand badge for active profile indicator

* feat(profiles): replace form heading with "Back to LLM profiles list"

* fix(profiles): unify profile-name validation and reject any whitespace

* fix(onboarding): persist onboarding LLM choice as an active profile

* refactor(profiles): drop redundant trim/wrapper after validator change

* feat(chat): add /model slash command for LLM profiles

* Add model profile slash completions

* chore: address model command review feedback (#418)

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: address model command follow-up review (#418)

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Graham Neubig <neubig@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants