Skip to content

Commit e0cd94b

Browse files
saagar210claude
andcommitted
refactor(hooks): split useLlm into model / generation / streaming hooks
The 517-line useLlm hook bundled 20 Tauri-command wrappers plus a single 7-field LlmState object. Consumers (DraftTab and SettingsTab) used cleanly disjoint subsets: DraftTab touches only streaming + non-streaming generation (9 functions, 2 state fields), SettingsTab touches only model lifecycle + context-window config (9 functions, no state fields). Every streaming token setState therefore re-rendered SettingsTab, and every model load re-rendered DraftTab, because both consumers subscribed to a single shared state object. Splits useLlm into three focused hooks, each owning its own state slice: src/hooks/useLlmModel.ts (new) State: modelInfo, isLoaded, loading, error Commands: checkModelStatus, getLoadedModel, getModelInfo, listModels, loadModel, loadCustomModel, validateGgufFile, unloadModel, getContextWindow, setContextWindow src/hooks/useLlmGeneration.ts (new) State: generating, error Commands: generate, generateWithContext, generateWithContextParams, generateFirstResponse, generateChecklist, updateChecklist, testModel (+ a runGenerating<T> internal helper that handles the common setState/try/finally pattern once instead of seven times) src/hooks/useLlmStreaming.ts (new) State: streamingText, isStreaming, generating, error Commands: generateStreaming, clearStreamingText, cancelGeneration Behavior: keeps the 500KB MAX_STREAMING_TEXT_SIZE truncation contract and the UnlistenFn ref-based cleanup src/hooks/useLlm.ts (rewritten, 517 -> 70 lines) Thin orchestrator that composes all three and re-exposes the flat API the codebase grew up with. Unifies `generating` (OR of generation/streaming) and `error` (first non-null across all three) so existing callers and the existing useLlm.test.ts see unchanged semantics. Migrations: - DraftTab: useLlm() -> useLlmStreaming() + useLlmGeneration(). Drops a subscription to loading/isLoaded/modelInfo it never read. - SettingsTab: useLlm() -> useLlmModel(). Drops a subscription to streamingText/isStreaming/generating it never read — the main user-visible win of the split, since streaming tokens no longer re-render the Settings view when it's open. - SettingsTab.test.tsx: updates the corresponding vi.mock path from "../../hooks/useLlm" to "../../hooks/useLlmModel". No behavior change. The useLlm orchestrator test (4 cases covering model mgmt / generation / streaming / errors) still passes unchanged because the orchestrator's API surface is identical. Typecheck + vitest (241/241) + eslint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 04473dc commit e0cd94b

7 files changed

Lines changed: 536 additions & 515 deletions

File tree

src/components/Draft/DraftTab.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import { WorkspaceDialogs } from "./WorkspaceDialogs";
2626
import { WorkspaceModeShell } from "./WorkspaceModeShell";
2727
import { WorkspacePanels } from "./WorkspacePanels";
2828
import { WorkspaceWorkflowStrip } from "./WorkspaceWorkflowStrip";
29-
import { useLlm } from "../../hooks/useLlm";
29+
import { useLlmGeneration } from "../../hooks/useLlmGeneration";
30+
import { useLlmStreaming } from "../../hooks/useLlmStreaming";
3031
import { useDrafts } from "../../hooks/useDrafts";
3132
import { useKb } from "../../hooks/useKb";
3233
import { useAnalytics } from "../../hooks/useAnalytics";
@@ -190,11 +191,13 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
190191
isStreaming,
191192
clearStreamingText,
192193
cancelGeneration,
194+
} = useLlmStreaming();
195+
const {
193196
generateFirstResponse,
194197
generateChecklist,
195198
updateChecklist,
196199
generateWithContextParams,
197-
} = useLlm();
200+
} = useLlmGeneration();
198201
const {
199202
saveDraft,
200203
updateDraft,

src/components/Settings/SettingsTab.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ vi.mock("../shared/Button", () => ({
121121
),
122122
}));
123123

124-
vi.mock("../../hooks/useLlm", () => ({
125-
useLlm: () => ({
124+
vi.mock("../../hooks/useLlmModel", () => ({
125+
useLlmModel: () => ({
126126
loadModel: loadModelMock,
127127
unloadModel: unloadModelMock,
128128
getLoadedModel: getLoadedModelMock,

src/components/Settings/SettingsTab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useDownload } from "../../hooks/useDownload";
1414
import { useEmbedding } from "../../hooks/useEmbedding";
1515
import { useJira } from "../../hooks/useJira";
1616
import { useKb } from "../../hooks/useKb";
17-
import { useLlm } from "../../hooks/useLlm";
17+
import { useLlmModel } from "../../hooks/useLlmModel";
1818
import { useSearchApiEmbedding } from "../../hooks/useSearchApiEmbedding";
1919
import { useSettingsOps } from "../../hooks/useSettingsOps";
2020
import {
@@ -72,7 +72,7 @@ export function SettingsTab() {
7272
setContextWindow,
7373
loadCustomModel,
7474
validateGgufFile,
75-
} = useLlm();
75+
} = useLlmModel();
7676
const {
7777
setKbFolder,
7878
getKbFolder,

0 commit comments

Comments
 (0)