fix: name each tool once, and stream the reasoning live (#18)
This commit was merged in pull request #18.
This commit is contained in:
@@ -7,6 +7,7 @@ import { isSelfHosted } from "../ollama";
|
||||
import { OllamaError } from "../ollama";
|
||||
import type { ResolvedPreferences } from "../preferences";
|
||||
import { createSession, createTurn, saveSession, withTitleFromFirstTurn } from "../sessions";
|
||||
import { EMPTY_TRACE, afterToolCall, withFinalTrace, withThinking, type ThinkingTrace } from "../thinking";
|
||||
import { toolsFor } from "../tools";
|
||||
import type { PendingAttachment, Session, VisionSupport } from "../types";
|
||||
import { errorMessage, isAbortError } from "../utils";
|
||||
@@ -61,6 +62,13 @@ export interface UseChat {
|
||||
session: Session;
|
||||
isStreaming: boolean;
|
||||
streamingResponse: string;
|
||||
/**
|
||||
* The reasoning to show right now: only the part since the last tool call.
|
||||
*
|
||||
* Deliberately not the whole trace. The full one is carried on the turn for
|
||||
* storage; this is the live view, and it is cleared each time a tool starts so
|
||||
* a long research turn does not stack every round's thinking on screen.
|
||||
*/
|
||||
streamingThinking: string;
|
||||
/** Short activity line such as "Searching the web", shown while a turn is in flight. */
|
||||
status?: string;
|
||||
@@ -102,7 +110,7 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
const liveRef = useRef(false);
|
||||
const responseRef = useRef("");
|
||||
const thinkingRef = useRef("");
|
||||
const thinkingRef = useRef<ThinkingTrace>(EMPTY_TRACE);
|
||||
const statusRef = useRef<string | undefined>(undefined);
|
||||
const flushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
@@ -131,14 +139,14 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
flushTimerRef.current = null;
|
||||
if (!liveRef.current) return;
|
||||
setStreamingResponse(responseRef.current);
|
||||
setStreamingThinking(thinkingRef.current);
|
||||
setStreamingThinking(thinkingRef.current.current);
|
||||
}, FLUSH_INTERVAL_MS);
|
||||
}, []);
|
||||
|
||||
const flushNow = useCallback(() => {
|
||||
clearFlushTimer();
|
||||
setStreamingResponse(responseRef.current);
|
||||
setStreamingThinking(thinkingRef.current);
|
||||
setStreamingThinking(thinkingRef.current.current);
|
||||
}, [clearFlushTimer]);
|
||||
|
||||
const setStatusValue = useCallback((value: string | undefined) => {
|
||||
@@ -185,7 +193,7 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
commit(started);
|
||||
|
||||
responseRef.current = "";
|
||||
thinkingRef.current = "";
|
||||
thinkingRef.current = EMPTY_TRACE;
|
||||
setStreamingResponse("");
|
||||
setStreamingThinking("");
|
||||
setError(undefined);
|
||||
@@ -242,11 +250,18 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
scheduleFlush();
|
||||
},
|
||||
onThinkingDelta: (delta) => {
|
||||
thinkingRef.current += delta;
|
||||
thinkingRef.current = withThinking(thinkingRef.current, delta);
|
||||
scheduleFlush();
|
||||
},
|
||||
onStatus: (value) => setStatusValue(value),
|
||||
onToolStart: (_name, label, count) => setActivity({ label, count }),
|
||||
onToolStart: (_name, label, count) => {
|
||||
// The reasoning that produced this call is spent, so the live
|
||||
// segment is cleared and the tool line takes its place. The full
|
||||
// trace is untouched, because the stored turn keeps all of it.
|
||||
thinkingRef.current = afterToolCall(thinkingRef.current);
|
||||
setActivity({ label, count });
|
||||
flushNow();
|
||||
},
|
||||
onToolCall: (record) => {
|
||||
toolCalls = [...toolCalls, record];
|
||||
},
|
||||
@@ -254,7 +269,7 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
});
|
||||
|
||||
responseRef.current = result.content;
|
||||
thinkingRef.current = result.thinking;
|
||||
thinkingRef.current = withFinalTrace(result.thinking);
|
||||
toolCalls = result.toolCalls;
|
||||
} catch (thrown) {
|
||||
if (isAbortError(thrown)) {
|
||||
@@ -268,7 +283,7 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
const finished = {
|
||||
...turn,
|
||||
response: responseRef.current,
|
||||
thinking: thinkingRef.current,
|
||||
thinking: thinkingRef.current.full,
|
||||
toolCalls,
|
||||
error: failure,
|
||||
};
|
||||
@@ -306,6 +321,7 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
setError(undefined);
|
||||
setStreamingResponse("");
|
||||
setStreamingThinking("");
|
||||
thinkingRef.current = EMPTY_TRACE;
|
||||
commit(next);
|
||||
},
|
||||
[commit, setStatusValue],
|
||||
|
||||
Reference in New Issue
Block a user