Download src/hooks/mutation/use-switch-llm-profile.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 4.21 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/src/hooks/mutation/use-switch-llm-profile.ts
- Command line
-
hf download hf://SaylorTwift/openhands/src/hooks/mutation/use-switch-llm-profile.ts
-
curl -L -o use-switch-llm-profile.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/src/hooks/mutation/use-switch-llm-profile.ts
4.21 kB
| import { useMutation, useQueryClient } from "@tanstack/react-query"; | |
| import { useTranslation } from "react-i18next"; | |
| import AgentServerConversationService from "#/api/conversation-service/agent-server-conversation-service.api"; | |
| import SettingsService from "#/api/settings-service/settings-service.api"; | |
| import { | |
| LLM_PROFILES_QUERY_KEYS, | |
| SETTINGS_QUERY_KEYS, | |
| } from "#/hooks/query/query-keys"; | |
| import { getLastRenderableEventId } from "#/hooks/chat/model-command-event-anchor"; | |
| import { | |
| recordModelSwitchMessage, | |
| stampActiveLlmProfile, | |
| } from "#/hooks/chat/record-model-switch-message"; | |
| import { displayErrorToast } from "#/utils/custom-toast-handlers"; | |
| import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message"; | |
| import { I18nKey } from "#/i18n/declaration"; | |
| import { invalidateConversationQueries } from "./conversation-mutation-utils"; | |
| interface SwitchLlmProfileVars { | |
| /** | |
| * When set, the conversation's running LLM is swapped via /switch_profile and | |
| * the user's global default profile is untouched. When null (home page), | |
| * the profile is activated globally instead. | |
| */ | |
| conversationId: string | null; | |
| profileName: string; | |
| } | |
| /** | |
| * Shared key so any picker instance can observe an in-flight switch via | |
| * `useIsMutating` — the pill button and the menu that fires the switch are | |
| * separate hook instances, so per-observer `isPending` wouldn't line up. | |
| */ | |
| export const SWITCH_LLM_PROFILE_MUTATION_KEY = ["switch-llm-profile"]; | |
| /** | |
| * Switches the LLM profile. Per-conversation when called from inside a | |
| * conversation; globally activates the profile when called from the home page. | |
| * | |
| * The confirmation message, #1082 metadata persist, and error reporting all | |
| * live in the mutation-level callbacks (not `mutate(..., callbacks)`) so they | |
| * still run after the switcher menu closes on select — React Query drops | |
| * mutate-scoped callbacks when the calling component unmounts. | |
| * | |
| * `meta.disableToast` + a tailored `onError` (rather than the global mutation | |
| * toast) so a failed switch keeps the specific "Switched to {name} failed" | |
| * message instead of a generic error (#1571 review) — unlike | |
| * use-switch-acp-model, which has no per-action message worth tailoring. | |
| */ | |
| export const useSwitchLlmProfile = () => { | |
| const queryClient = useQueryClient(); | |
| const { t } = useTranslation(); | |
| return useMutation({ | |
| mutationKey: SWITCH_LLM_PROFILE_MUTATION_KEY, | |
| mutationFn: ({ conversationId, profileName }: SwitchLlmProfileVars) => | |
| AgentServerConversationService.switchProfile(conversationId, profileName), | |
| meta: { disableToast: true }, | |
| // Anchor the inline message where the user issued the switch; captured | |
| // synchronously here because the menu unmounts before the switch resolves. | |
| onMutate: ({ conversationId }) => ({ | |
| anchorEventId: conversationId ? getLastRenderableEventId() : null, | |
| }), | |
| onError: (error, { profileName }) => { | |
| const fallback = t(I18nKey.MODEL$SWITCH_FAILED, { name: profileName }); | |
| displayErrorToast(retrieveAxiosErrorMessage(error) || fallback); | |
| }, | |
| onSuccess: (_data, { conversationId, profileName }, context) => { | |
| queryClient.invalidateQueries({ | |
| queryKey: LLM_PROFILES_QUERY_KEYS.all, | |
| }); | |
| if (conversationId) { | |
| invalidateConversationQueries(queryClient, conversationId); | |
| recordModelSwitchMessage( | |
| conversationId, | |
| profileName, | |
| context?.anchorEventId ?? null, | |
| ); | |
| // Keep the per-conversation profile identity fresh so the chat-header | |
| // switcher shows the right name after a reload (the agent-server only | |
| // round-trips the model string). #1082 | |
| stampActiveLlmProfile(conversationId, profileName); | |
| } else { | |
| // Home-page activate path (same server endpoint as | |
| // useActivateLlmProfile): clear the SettingsService cache so the next | |
| // conversation-start reads the newly activated profile's LLM config | |
| // instead of the stale encrypted settings. | |
| SettingsService.invalidateCache(); | |
| queryClient.invalidateQueries({ | |
| queryKey: SETTINGS_QUERY_KEYS.personal(), | |
| }); | |
| } | |
| }, | |
| }); | |
| }; | |