feat: add LLM profiles API layer and React Query hooks#387
Conversation
This PR adds the foundational data layer for the LLM profiles feature: ## API Layer - ProfilesService: Thin wrapper around SDK ProfilesClient with methods for list, get, save, delete, rename, and activate profile operations - Re-exports SDK types for consumer convenience ## React Query Hooks - useLlmProfiles: Query hook for listing all profiles - useSaveLlmProfile: Mutation hook for creating/updating profiles - useDeleteLlmProfile: Mutation hook for deleting profiles - useRenameLlmProfile: Mutation hook for renaming profiles - useActivateLlmProfile: Mutation hook for activating a profile All mutation hooks properly invalidate both profile list and settings caches on success, and disable global toasts (consumers handle errors). ## Utilities - deriveProfileNameFromModel: Derives a clean profile name from model strings (e.g., 'openai/gpt-4' -> 'gpt-4') - PROFILE_NAME_PATTERN: Validation regex for profile names ## Tests - 47 tests covering all new functionality - API service method tests - Hook behavior tests (success, error handling, cache invalidation) - Utility function tests Part 1 of LLM Profiles feature (PR A from split plan). No UI changes - this is purely a data layer addition. Co-authored-by: openhands <openhands@all-hands.dev>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
PR Artifacts Cleaned Up The |
all-hands-bot
left a comment
There was a problem hiding this comment.
Solid foundational work with comprehensive test coverage (47 tests). Found some test quality gaps and consistency issues that should be addressed before merge. The production code looks good overall.
- Remove client.close() calls for consistency with other services (SettingsService, SecretsService don't call close()) - Use SETTINGS_QUERY_KEYS.personal() instead of .all for precision - Add ActiveBackendProvider wrapper in useLlmProfiles tests - Add test for query key including backend.id and orgId - Add test for backend-switch cache isolation - Fix truncation test to actually exercise trailing-dash removal - Add test for model names that sanitize to empty string Addresses review feedback from all-hands-bot. Co-authored-by: openhands <openhands@all-hands.dev>
|
Addressed all 8 review comments in ae83e9a. Ready for another review. |
all-hands-bot
left a comment
There was a problem hiding this comment.
Solid foundational work with comprehensive test coverage. All previous review issues have been addressed. The API layer follows existing service patterns, hooks properly manage cache invalidation, and the 47 tests cover real behavior (not just mock assertions).
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟢 LOW
Additive changes only (new files, nothing imports them yet). Well-tested, follows established patterns (thin SDK wrapper, React Query hooks). No breaking changes, security issues, or complexity concerns. Safe to merge as Part 1 of the feature split.
npm normalizes the `github:OpenHands/typescript-client#sha` shorthand (and even an explicit `git+https://github.com/...` URL) to `git+ssh://git@github.com/...` whenever it rewrites package-lock.json during a plain `npm install`. Vercel's build environment has no GitHub SSH key, so an ssh-pinned lockfile causes Vercel to fall back to a stale cached copy of the package whose dist/clients.js predates the addition of ConversationClient, FileClient, and SharedClient. Rolldown then fails the build with: [MISSING_EXPORT] ConversationClient is not exported by node_modules/@openhands/typescript-client/dist/clients.js PR #382 fixed this once by hand-editing the lockfile, but the very next local `npm install` (e.g. PR #387 bumping React Query hooks) silently rewrote the resolved URL back to ssh and the bug returned. This change makes the Vercel build self-healing: * package.json now pins the dep as an explicit `git+https://` URL so the intent is documented in one place. * package-lock.json's top-level dep spec matches that URL; the nested `node_modules/@openhands/typescript-client` entry already resolved to https, so this brings both halves of the lockfile in sync. * vercel.json sets `installCommand` to `bash scripts/vercel-install.sh`, which: - rewrites any leftover `git+ssh://git@github.com/` resolved URLs back to https (handles future regressions), - configures `git config --global url."https://github.com/".insteadOf` for both `ssh://git@github.com/` and `git@github.com:` (handles anything npm has already normalized in cache), - then runs `npm ci` for a strict, lockfile-driven install. Locally verified: * `bash scripts/vercel-install.sh` produces a clean install with the https-resolved typescript-client. * `npm run build` and `npm run lint` both succeed after the install. * Re-running `npm install` rewrites `resolved` back to `git+ssh` as expected — the install script normalizes it again on every Vercel build, so the lockfile drift no longer breaks deploys. Refs: #384 (Vercel preview build fails: MISSING_EXPORT for SharedClient / ConversationClient / FileClient). Co-authored-by: openhands <openhands@all-hands.dev>
Summary
This PR adds the foundational data layer for the LLM profiles feature. This is Part 1 (PR A) of the split plan for #210, focusing purely on the API and hooks layer with no UI changes.
Changes
API Layer
src/api/profiles-service/profiles-service.api.ts): Thin wrapper around SDK'sProfilesClientwith methods for:listProfiles()- List all profilesgetProfile(name, exposeSecrets?)- Get profile detailssaveProfile(name, request)- Create/update profiledeleteProfile(name)- Delete profilerenameProfile(name, newName)- Rename profileactivateProfile(name)- Activate a profileProfileInfo,ProfileListResponse, etc.) for consumer convenienceReact Query Hooks
useLlmProfilesuseSaveLlmProfileuseDeleteLlmProfileuseRenameLlmProfileuseActivateLlmProfileAll mutation hooks:
Utilities
deriveProfileNameFromModel(model): Derives a clean profile name from model strings'openai/gpt-4'→'gpt-4'PROFILE_NAME_PATTERN: Validation regex for profile namesQuery Keys
LLM_PROFILES_QUERY_KEYSconstant to centralized query keysTests
47 tests covering all new functionality:
profiles-service.test.tsuse-llm-profiles.test.tsxuse-save-llm-profile.test.tsxuse-delete-llm-profile.test.tsxuse-rename-llm-profile.test.tsxuse-activate-llm-profile.test.tsxderive-profile-name.test.tsPR Split Plan
This is part of breaking down the large #210 into smaller, reviewable PRs:
Verification
This PR was created by an AI agent (OpenHands) on behalf of the user.
@malhotra5 can click here to continue refining the PR