1import { RegistryContext } from "@effect/atom-react";
2import { scopedThreadKey } from "@t3tools/client-runtime/environment";
3import {
4 isAtomCommandInterrupted,
5 squashAtomCommandFailure,
6} from "@t3tools/client-runtime/state/runtime";
7import { useContext } from "react";
8
9import { type ComposerThreadTarget, useComposerDraftStore } from "../../composerDraftStore";
10import { environmentThreadShells, threadEnvironment } from "../../state/threads";
11import { useOrchestrationCommand } from "../../state/use-orchestration-command";
12import { stackedThreadToast, toastManager } from "../ui/toast";
13
14/** Sets an existing thread's composer pick on the thread, so any next turn runs on it. */
15export function useApplyThreadModelPick() {
16 const registry = useContext(RegistryContext);
17 const setModelSelection = useOrchestrationCommand(threadEnvironment.setModelSelection, {
18 label: "change model",
19 });
20 return async (target: ComposerThreadTarget) => {
21 if (typeof target === "string") return;
22 const store = useComposerDraftStore.getState();
23 const draft = store.draftsByThreadKey[scopedThreadKey(target)];
24 if (!draft?.modelSelectionExplicit || !draft.activeProvider) return;
25 const pick = draft.modelSelectionByProvider[draft.activeProvider];
26 const thread = registry.get(environmentThreadShells.threadShellAtom(target));
27 if (!pick || !thread || store.draftThreadsByThreadKey[scopedThreadKey(target)]) return;
28 // Another instance replaces the live session, which may hold an open question; that pick
29 // still waits for a sent message.
30 if (pick.instanceId !== thread.modelSelection.instanceId) return;
31 const result = await setModelSelection({
32 environmentId: target.environmentId,
33 input: { threadId: target.threadId, modelSelection: pick },
34 });
35 if (result._tag === "Success") {
36 useComposerDraftStore.getState().releaseModelSelection(target, pick);
37 } else if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) {
38 const error = squashAtomCommandFailure(result);
39 toastManager.add(
40 stackedThreadToast({
41 type: "error",
42 title: "Could not apply model selection",
43 description: `This model pick was not saved. ${error instanceof Error ? error.message : "Try again."}`,
44 }),
45 );
46 }
47 };
48}