@surf-kit/agent 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/index.cjs +131 -41
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +3 -2
- package/dist/chat/index.d.ts +3 -2
- package/dist/chat/index.js +132 -42
- package/dist/chat/index.js.map +1 -1
- package/dist/{chat-CGamM7Mz.d.cts → chat-BRY3xGg_.d.cts} +1 -1
- package/dist/{chat-BIIDOGrD.d.ts → chat-CcKc6OAR.d.ts} +1 -1
- package/dist/{hooks-CTeEqnBQ.d.ts → hooks-BLeiVk-x.d.ts} +3 -2
- package/dist/{hooks-B1NYoLLs.d.cts → hooks-CSGGLd7j.d.cts} +3 -2
- package/dist/hooks.cjs +41 -11
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +3 -3
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +41 -11
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +131 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +136 -46
- package/dist/index.js.map +1 -1
- package/dist/layouts/index.cjs +131 -41
- package/dist/layouts/index.cjs.map +1 -1
- package/dist/layouts/index.d.cts +1 -1
- package/dist/layouts/index.d.ts +1 -1
- package/dist/layouts/index.js +134 -44
- package/dist/layouts/index.js.map +1 -1
- package/dist/response/index.cjs +34 -3
- package/dist/response/index.cjs.map +1 -1
- package/dist/response/index.d.cts +1 -1
- package/dist/response/index.d.ts +1 -1
- package/dist/response/index.js +35 -4
- package/dist/response/index.js.map +1 -1
- package/dist/streaming/index.cjs +14 -3
- package/dist/streaming/index.cjs.map +1 -1
- package/dist/streaming/index.d.cts +2 -2
- package/dist/streaming/index.d.ts +2 -2
- package/dist/streaming/index.js +14 -3
- package/dist/streaming/index.js.map +1 -1
- package/dist/{streaming-x7umFHoP.d.cts → streaming-BHPXnwwo.d.cts} +3 -1
- package/dist/{streaming-Bx-ff2tt.d.ts → streaming-C6mbU7My.d.ts} +3 -1
- package/package.json +5 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as Attachment, a as ChatMessage, C as ChatError, b as ConversationSummary } from './chat-
|
|
2
|
-
import { a as StreamState, S as StreamEvent } from './streaming-
|
|
1
|
+
import { A as Attachment, a as ChatMessage, C as ChatError, b as ConversationSummary } from './chat-CcKc6OAR.js';
|
|
2
|
+
import { a as StreamState, S as StreamEvent } from './streaming-C6mbU7My.js';
|
|
3
3
|
import { A as AgentInfo } from './agent-BNSmiexZ.js';
|
|
4
4
|
|
|
5
5
|
interface AgentChatConfig {
|
|
@@ -52,6 +52,7 @@ interface AgentChatActions {
|
|
|
52
52
|
loadConversation: (conversationId: string, messages: ChatMessage[]) => void;
|
|
53
53
|
submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>;
|
|
54
54
|
retry: () => Promise<void>;
|
|
55
|
+
stop: () => void;
|
|
55
56
|
reset: () => void;
|
|
56
57
|
}
|
|
57
58
|
declare function useAgentChat(config: AgentChatConfig): {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as Attachment, a as ChatMessage, C as ChatError, b as ConversationSummary } from './chat-
|
|
2
|
-
import { a as StreamState, S as StreamEvent } from './streaming-
|
|
1
|
+
import { A as Attachment, a as ChatMessage, C as ChatError, b as ConversationSummary } from './chat-BRY3xGg_.cjs';
|
|
2
|
+
import { a as StreamState, S as StreamEvent } from './streaming-BHPXnwwo.cjs';
|
|
3
3
|
import { A as AgentInfo } from './agent-BNSmiexZ.cjs';
|
|
4
4
|
|
|
5
5
|
interface AgentChatConfig {
|
|
@@ -52,6 +52,7 @@ interface AgentChatActions {
|
|
|
52
52
|
loadConversation: (conversationId: string, messages: ChatMessage[]) => void;
|
|
53
53
|
submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>;
|
|
54
54
|
retry: () => Promise<void>;
|
|
55
|
+
stop: () => void;
|
|
55
56
|
reset: () => void;
|
|
56
57
|
}
|
|
57
58
|
declare function useAgentChat(config: AgentChatConfig): {
|
package/dist/hooks.cjs
CHANGED
|
@@ -61,6 +61,8 @@ function reducer(state, action) {
|
|
|
61
61
|
return { ...state, streamPhase: action.phase };
|
|
62
62
|
case "STREAM_CONTENT":
|
|
63
63
|
return { ...state, streamingContent: state.streamingContent + action.content };
|
|
64
|
+
case "STREAM_CONTENT_RESET":
|
|
65
|
+
return { ...state, streamingContent: "" };
|
|
64
66
|
case "STREAM_AGENT":
|
|
65
67
|
return { ...state, streamingAgent: action.agent };
|
|
66
68
|
case "SEND_SUCCESS":
|
|
@@ -106,6 +108,7 @@ function useAgentChat(config) {
|
|
|
106
108
|
configRef.current = config;
|
|
107
109
|
const lastUserMessageRef = (0, import_react.useRef)(null);
|
|
108
110
|
const lastUserAttachmentsRef = (0, import_react.useRef)(void 0);
|
|
111
|
+
const abortControllerRef = (0, import_react.useRef)(null);
|
|
109
112
|
const sendMessage = (0, import_react.useCallback)(
|
|
110
113
|
async (content, attachments) => {
|
|
111
114
|
const { apiUrl, streamPath = "/chat/stream", headers: headersOrFn, timeout = 3e4, bodyExtra } = configRef.current;
|
|
@@ -121,7 +124,15 @@ function useAgentChat(config) {
|
|
|
121
124
|
};
|
|
122
125
|
dispatch({ type: "SEND_START", message: userMessage });
|
|
123
126
|
const controller = new AbortController();
|
|
127
|
+
abortControllerRef.current = controller;
|
|
124
128
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
129
|
+
const ctx = {
|
|
130
|
+
accumulatedContent: "",
|
|
131
|
+
agentResponse: null,
|
|
132
|
+
capturedAgent: null,
|
|
133
|
+
capturedConversationId: null,
|
|
134
|
+
hadStreamError: false
|
|
135
|
+
};
|
|
125
136
|
try {
|
|
126
137
|
const url = `${apiUrl}${streamPath}`;
|
|
127
138
|
const mergedHeaders = {
|
|
@@ -142,13 +153,6 @@ function useAgentChat(config) {
|
|
|
142
153
|
}));
|
|
143
154
|
}
|
|
144
155
|
const body = JSON.stringify(requestBody);
|
|
145
|
-
const ctx = {
|
|
146
|
-
accumulatedContent: "",
|
|
147
|
-
agentResponse: null,
|
|
148
|
-
capturedAgent: null,
|
|
149
|
-
capturedConversationId: null,
|
|
150
|
-
hadStreamError: false
|
|
151
|
-
};
|
|
152
156
|
const handleEvent = (event) => {
|
|
153
157
|
switch (event.type) {
|
|
154
158
|
case "agent":
|
|
@@ -162,6 +166,10 @@ function useAgentChat(config) {
|
|
|
162
166
|
ctx.accumulatedContent += event.content;
|
|
163
167
|
dispatch({ type: "STREAM_CONTENT", content: event.content });
|
|
164
168
|
break;
|
|
169
|
+
case "delta_reset":
|
|
170
|
+
ctx.accumulatedContent = "";
|
|
171
|
+
dispatch({ type: "STREAM_CONTENT_RESET" });
|
|
172
|
+
break;
|
|
165
173
|
case "done":
|
|
166
174
|
ctx.agentResponse = event.response;
|
|
167
175
|
ctx.capturedConversationId = event.conversation_id ?? null;
|
|
@@ -245,10 +253,26 @@ function useAgentChat(config) {
|
|
|
245
253
|
} catch (err) {
|
|
246
254
|
clearTimeout(timeoutId);
|
|
247
255
|
if (err.name === "AbortError") {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
256
|
+
if (ctx.accumulatedContent) {
|
|
257
|
+
const partialMessage = {
|
|
258
|
+
id: generateMessageId(),
|
|
259
|
+
role: "assistant",
|
|
260
|
+
content: ctx.accumulatedContent,
|
|
261
|
+
agent: ctx.capturedAgent ?? void 0,
|
|
262
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
263
|
+
};
|
|
264
|
+
dispatch({
|
|
265
|
+
type: "SEND_SUCCESS",
|
|
266
|
+
message: partialMessage,
|
|
267
|
+
streamingContent: ctx.accumulatedContent,
|
|
268
|
+
conversationId: ctx.capturedConversationId
|
|
269
|
+
});
|
|
270
|
+
} else {
|
|
271
|
+
dispatch({
|
|
272
|
+
type: "SEND_ERROR",
|
|
273
|
+
error: { code: "ABORTED", message: "Request stopped", retryable: true }
|
|
274
|
+
});
|
|
275
|
+
}
|
|
252
276
|
} else {
|
|
253
277
|
dispatch({
|
|
254
278
|
type: "SEND_ERROR",
|
|
@@ -259,6 +283,8 @@ function useAgentChat(config) {
|
|
|
259
283
|
}
|
|
260
284
|
});
|
|
261
285
|
}
|
|
286
|
+
} finally {
|
|
287
|
+
abortControllerRef.current = null;
|
|
262
288
|
}
|
|
263
289
|
},
|
|
264
290
|
[state.conversationId]
|
|
@@ -286,6 +312,9 @@ function useAgentChat(config) {
|
|
|
286
312
|
await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current);
|
|
287
313
|
}
|
|
288
314
|
}, [sendMessage]);
|
|
315
|
+
const stop = (0, import_react.useCallback)(() => {
|
|
316
|
+
abortControllerRef.current?.abort();
|
|
317
|
+
}, []);
|
|
289
318
|
const reset = (0, import_react.useCallback)(() => {
|
|
290
319
|
dispatch({ type: "RESET" });
|
|
291
320
|
lastUserMessageRef.current = null;
|
|
@@ -297,6 +326,7 @@ function useAgentChat(config) {
|
|
|
297
326
|
loadConversation,
|
|
298
327
|
submitFeedback,
|
|
299
328
|
retry,
|
|
329
|
+
stop,
|
|
300
330
|
reset
|
|
301
331
|
};
|
|
302
332
|
return { state, actions };
|
package/dist/hooks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks.ts","../src/hooks/useAgentChat.ts","../src/hooks/useStreaming.ts","../src/hooks/useConversation.ts","../src/hooks/useFeedback.ts","../src/hooks/useAgentTheme.ts","../src/hooks/useCharacterDrain.ts"],"sourcesContent":["export { useAgentChat } from './hooks/useAgentChat'\nexport type { AgentChatState, AgentChatActions } from './hooks/useAgentChat'\n\nexport { useStreaming } from './hooks/useStreaming'\nexport type { UseStreamingOptions } from './hooks/useStreaming'\n\nexport { useConversation } from './hooks/useConversation'\nexport type { Conversation, UseConversationOptions } from './hooks/useConversation'\n\nexport { useFeedback } from './hooks/useFeedback'\nexport type { FeedbackState, UseFeedbackOptions, FeedbackPayload } from './hooks/useFeedback'\n\nexport { useAgentTheme } from './hooks/useAgentTheme'\nexport type { AgentThemeResult } from './hooks/useAgentTheme'\n\nexport { useCharacterDrain } from './hooks/useCharacterDrain'\nexport type { CharacterDrainResult } from './hooks/useCharacterDrain'\n","'use client'\n\nimport { useReducer, useCallback, useRef } from 'react'\nimport type { ChatMessage, ChatError, Attachment } from '../types/chat'\nimport type { AgentResponse } from '../types/agent'\nimport type { StreamState } from '../types/streaming'\nimport type { AgentChatConfig } from '../types/config'\n\n// ── State ──────────────────────────────────────────────────────────────\n\nexport interface AgentChatState {\n messages: ChatMessage[]\n conversationId: string | null\n isLoading: boolean\n error: ChatError | null\n inputValue: string\n streamPhase: StreamState['phase']\n streamingContent: string\n streamingAgent: string | null\n}\n\nconst initialState: AgentChatState = {\n messages: [],\n conversationId: null,\n isLoading: false,\n error: null,\n inputValue: '',\n streamPhase: 'idle',\n streamingContent: '',\n streamingAgent: null,\n}\n\n// ── Actions ────────────────────────────────────────────────────────────\n\ntype Action =\n | { type: 'SET_INPUT'; value: string }\n | { type: 'SEND_START'; message: ChatMessage }\n | { type: 'STREAM_PHASE'; phase: StreamState['phase'] }\n | { type: 'STREAM_CONTENT'; content: string }\n | { type: 'STREAM_AGENT'; agent: string }\n | { type: 'SEND_SUCCESS'; message: ChatMessage; streamingContent: string; conversationId: string | null }\n | { type: 'SEND_ERROR'; error: ChatError }\n | { type: 'LOAD_CONVERSATION'; conversationId: string; messages: ChatMessage[] }\n | { type: 'RESET' }\n | { type: 'CLEAR_ERROR' }\n\nfunction reducer(state: AgentChatState, action: Action): AgentChatState {\n switch (action.type) {\n case 'SET_INPUT':\n return { ...state, inputValue: action.value }\n\n case 'SEND_START':\n return {\n ...state,\n messages: [...state.messages, action.message],\n isLoading: true,\n error: null,\n inputValue: '',\n streamPhase: 'thinking',\n streamingContent: '',\n streamingAgent: null,\n }\n\n case 'STREAM_PHASE':\n return { ...state, streamPhase: action.phase }\n\n case 'STREAM_CONTENT':\n return { ...state, streamingContent: state.streamingContent + action.content }\n\n case 'STREAM_AGENT':\n return { ...state, streamingAgent: action.agent }\n\n case 'SEND_SUCCESS':\n return {\n ...state,\n messages: [...state.messages, action.message],\n conversationId: action.conversationId ?? state.conversationId,\n isLoading: false,\n streamPhase: 'idle',\n streamingContent: '',\n }\n\n case 'SEND_ERROR':\n return {\n ...state,\n isLoading: false,\n error: action.error,\n streamPhase: 'idle',\n streamingContent: '',\n streamingAgent: null,\n }\n\n case 'LOAD_CONVERSATION':\n return {\n ...state,\n conversationId: action.conversationId,\n messages: action.messages,\n error: null,\n }\n\n case 'RESET':\n return { ...initialState }\n\n case 'CLEAR_ERROR':\n return { ...state, error: null }\n\n default:\n return state\n }\n}\n\n// ── Hook ───────────────────────────────────────────────────────────────\n\nlet msgIdCounter = 0\nfunction generateMessageId(): string {\n return `msg-${Date.now()}-${++msgIdCounter}`\n}\n\nexport interface AgentChatActions {\n sendMessage: (content: string, attachments?: Attachment[]) => Promise<void>\n setInputValue: (value: string) => void\n loadConversation: (conversationId: string, messages: ChatMessage[]) => void\n submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>\n retry: () => Promise<void>\n reset: () => void\n}\n\nexport function useAgentChat(config: AgentChatConfig) {\n const [state, dispatch] = useReducer(reducer, initialState)\n const configRef = useRef(config)\n configRef.current = config\n const lastUserMessageRef = useRef<string | null>(null)\n const lastUserAttachmentsRef = useRef<Attachment[] | undefined>(undefined)\n\n const sendMessage = useCallback(\n async (content: string, attachments?: Attachment[]) => {\n const { apiUrl, streamPath = '/chat/stream', headers: headersOrFn, timeout = 30000, bodyExtra } = configRef.current\n const headers = typeof headersOrFn === 'function' ? await headersOrFn() : (headersOrFn ?? {})\n\n lastUserMessageRef.current = content\n lastUserAttachmentsRef.current = attachments\n\n const userMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'user',\n content,\n attachments,\n timestamp: new Date(),\n }\n\n dispatch({ type: 'SEND_START', message: userMessage })\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const url = `${apiUrl}${streamPath}`\n const mergedHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n }\n\n // Build request body — include attachments if present\n const requestBody: Record<string, unknown> = {\n message: content,\n conversation_id: state.conversationId,\n ...bodyExtra,\n }\n if (attachments && attachments.length > 0) {\n requestBody.attachments = attachments.map(a => ({\n filename: a.filename,\n content_type: a.content_type,\n data: a.data,\n }))\n }\n const body = JSON.stringify(requestBody)\n\n // These variables are mutated inside handleEvent (called from async stream processing).\n // TypeScript can't track mutations through closures, so we use a mutable context object.\n const ctx = {\n accumulatedContent: '',\n agentResponse: null as AgentResponse | null,\n capturedAgent: null as string | null,\n capturedConversationId: null as string | null,\n hadStreamError: false,\n }\n\n // Shared handler for parsed SSE events (used by both adapter and default paths)\n const handleEvent = (event: { type: string; [key: string]: unknown }) => {\n switch (event.type) {\n case 'agent':\n ctx.capturedAgent = event.agent as string\n dispatch({ type: 'STREAM_AGENT', agent: ctx.capturedAgent })\n break\n case 'phase':\n dispatch({ type: 'STREAM_PHASE', phase: event.phase as StreamState['phase'] })\n break\n case 'delta':\n ctx.accumulatedContent += event.content\n dispatch({ type: 'STREAM_CONTENT', content: event.content as string })\n break\n case 'done':\n ctx.agentResponse = event.response as AgentResponse\n ctx.capturedConversationId = (event.conversation_id as string) ?? null\n break\n case 'error':\n ctx.hadStreamError = true\n dispatch({ type: 'SEND_ERROR', error: event.error as ChatError })\n break\n }\n }\n\n const { streamAdapter } = configRef.current\n\n if (streamAdapter) {\n // Use the custom stream adapter (e.g. React Native XHR-based SSE)\n await streamAdapter(\n url,\n { method: 'POST', headers: mergedHeaders, body, signal: controller.signal },\n handleEvent,\n )\n clearTimeout(timeoutId)\n } else {\n // Default path: fetch + ReadableStream getReader()\n const response = await fetch(url, {\n method: 'POST',\n headers: mergedHeaders,\n body,\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n })\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'STREAM_ERROR', message: 'No response body', retryable: true },\n })\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data)\n handleEvent(event)\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n\n // If an error event was dispatched during streaming, don't dispatch success\n if (ctx.hadStreamError) return\n\n const assistantMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'assistant',\n content: ctx.agentResponse?.message ?? ctx.accumulatedContent,\n response: ctx.agentResponse ?? undefined,\n agent: ctx.capturedAgent ?? undefined,\n timestamp: new Date(),\n }\n\n dispatch({\n type: 'SEND_SUCCESS',\n message: assistantMessage,\n streamingContent: ctx.accumulatedContent,\n conversationId: ctx.capturedConversationId,\n })\n } catch (err: unknown) {\n clearTimeout(timeoutId)\n if ((err as Error).name === 'AbortError') {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'TIMEOUT', message: 'Request timed out', retryable: true },\n })\n } else {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n })\n }\n }\n },\n [state.conversationId],\n )\n\n const setInputValue = useCallback((value: string) => {\n dispatch({ type: 'SET_INPUT', value })\n }, [])\n\n const loadConversation = useCallback((conversationId: string, messages: ChatMessage[]) => {\n dispatch({ type: 'LOAD_CONVERSATION', conversationId, messages })\n }, [])\n\n const submitFeedback = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n const { apiUrl, feedbackPath = '/feedback', headers: headersOrFn } = configRef.current\n const headers = typeof headersOrFn === 'function' ? await headersOrFn() : (headersOrFn ?? {})\n await fetch(`${apiUrl}${feedbackPath}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify({ messageId, rating, comment }),\n })\n },\n [],\n )\n\n const retry = useCallback(async () => {\n if (lastUserMessageRef.current) {\n await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current)\n }\n }, [sendMessage])\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' })\n lastUserMessageRef.current = null\n lastUserAttachmentsRef.current = undefined\n }, [])\n\n const actions: AgentChatActions = {\n sendMessage,\n setInputValue,\n loadConversation,\n submitFeedback,\n retry,\n reset,\n }\n\n return { state, actions }\n}\n","'use client'\n\nimport { useState, useCallback, useRef, useEffect } from 'react'\nimport type { StreamEvent, StreamState } from '../types/streaming'\nimport type { Source } from '../types/agent'\n\nexport interface UseStreamingOptions {\n /** SSE endpoint URL */\n url: string\n /** Additional headers for fetch-based SSE (not used with native EventSource) */\n headers?: Record<string, string>\n /** Called when a complete response is received */\n onDone?: (event: Extract<StreamEvent, { type: 'done' }>) => void\n /** Called on error */\n onError?: (event: Extract<StreamEvent, { type: 'error' }>) => void\n}\n\nconst initialState: StreamState = {\n active: false,\n phase: 'idle',\n content: '',\n sources: [],\n agent: null,\n agentLabel: null,\n}\n\nexport function useStreaming(options: UseStreamingOptions) {\n const { url, headers, onDone, onError } = options\n const [state, setState] = useState<StreamState>(initialState)\n const abortRef = useRef<AbortController | null>(null)\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const stop = useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort()\n abortRef.current = null\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n }, [])\n\n const start = useCallback(\n async (body: Record<string, unknown>) => {\n // Reset state\n setState({ ...initialState, active: true, phase: 'thinking' })\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data) as StreamEvent\n processEvent(event, setState, optionsRef)\n } catch {\n // Skip malformed events\n }\n }\n }\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n }\n },\n [url, headers],\n )\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (abortRef.current) {\n abortRef.current.abort()\n }\n }\n }, [])\n\n return { state, start, stop }\n}\n\nfunction processEvent(\n event: StreamEvent,\n setState: React.Dispatch<React.SetStateAction<StreamState>>,\n optionsRef: React.MutableRefObject<UseStreamingOptions>,\n) {\n switch (event.type) {\n case 'phase':\n setState((prev) => ({ ...prev, phase: event.phase }))\n break\n case 'delta':\n setState((prev) => ({ ...prev, content: prev.content + event.content }))\n break\n case 'source':\n setState((prev) => ({ ...prev, sources: [...prev.sources, event.source] }))\n break\n case 'agent':\n setState((prev) => ({ ...prev, agent: event.agent }))\n break\n case 'done':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onDone?.(event)\n break\n case 'error':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(event)\n break\n }\n}\n","'use client'\n\nimport { useState, useCallback } from 'react'\nimport type { ChatMessage } from '../types/chat'\nimport type { ConversationSummary } from '../types/chat'\n\nexport interface Conversation {\n id: string\n title: string\n messages: ChatMessage[]\n createdAt: Date\n updatedAt: Date\n}\n\nexport interface UseConversationOptions {\n /** Enable localStorage persistence */\n persist?: boolean\n /** localStorage key prefix */\n storageKey?: string\n}\n\nconst STORAGE_PREFIX = 'surf-kit-conversations'\n\nfunction getStorageKey(prefix: string) {\n return `${prefix}:list`\n}\n\nfunction loadFromStorage(storageKey: string): Conversation[] {\n if (typeof window === 'undefined') return []\n try {\n const raw = window.localStorage.getItem(getStorageKey(storageKey))\n if (!raw) return []\n const parsed = JSON.parse(raw) as Array<Conversation & { createdAt: string; updatedAt: string }>\n return parsed.map((c) => ({\n ...c,\n createdAt: new Date(c.createdAt),\n updatedAt: new Date(c.updatedAt),\n messages: c.messages.map((m) => ({ ...m, timestamp: new Date(m.timestamp as unknown as string) })),\n }))\n } catch {\n return []\n }\n}\n\nfunction saveToStorage(storageKey: string, conversations: Conversation[]) {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(getStorageKey(storageKey), JSON.stringify(conversations))\n } catch {\n // Storage full or unavailable\n }\n}\n\nlet idCounter = 0\nfunction generateId(): string {\n return `conv-${Date.now()}-${++idCounter}`\n}\n\nexport function useConversation(options: UseConversationOptions = {}) {\n const { persist = false, storageKey = STORAGE_PREFIX } = options\n\n const [conversations, setConversations] = useState<Conversation[]>(() =>\n persist ? loadFromStorage(storageKey) : [],\n )\n const [current, setCurrent] = useState<Conversation | null>(null)\n\n const persistIfNeeded = useCallback(\n (convs: Conversation[]) => {\n if (persist) saveToStorage(storageKey, convs)\n },\n [persist, storageKey],\n )\n\n const create = useCallback(\n (title?: string): Conversation => {\n const now = new Date()\n const conv: Conversation = {\n id: generateId(),\n title: title ?? 'New Conversation',\n messages: [],\n createdAt: now,\n updatedAt: now,\n }\n setConversations((prev) => {\n const next = [conv, ...prev]\n persistIfNeeded(next)\n return next\n })\n setCurrent(conv)\n return conv\n },\n [persistIfNeeded],\n )\n\n const list = useCallback((): ConversationSummary[] => {\n return conversations.map((c) => ({\n id: c.id,\n title: c.title,\n lastMessage: c.messages.length > 0 ? c.messages[c.messages.length - 1].content : '',\n updatedAt: c.updatedAt,\n messageCount: c.messages.length,\n }))\n }, [conversations])\n\n const load = useCallback(\n (id: string): Conversation | null => {\n const conv = conversations.find((c) => c.id === id) ?? null\n setCurrent(conv)\n return conv\n },\n [conversations],\n )\n\n const remove = useCallback(\n (id: string) => {\n setConversations((prev) => {\n const next = prev.filter((c) => c.id !== id)\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? null : prev))\n },\n [persistIfNeeded],\n )\n\n const rename = useCallback(\n (id: string, title: string) => {\n setConversations((prev) => {\n const next = prev.map((c) => (c.id === id ? { ...c, title, updatedAt: new Date() } : c))\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? { ...prev, title, updatedAt: new Date() } : prev))\n },\n [persistIfNeeded],\n )\n\n const addMessage = useCallback(\n (conversationId: string, message: ChatMessage) => {\n setConversations((prev) => {\n const next = prev.map((c) =>\n c.id === conversationId\n ? { ...c, messages: [...c.messages, message], updatedAt: new Date() }\n : c,\n )\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) =>\n prev?.id === conversationId\n ? { ...prev, messages: [...prev.messages, message], updatedAt: new Date() }\n : prev,\n )\n },\n [persistIfNeeded],\n )\n\n return {\n conversations,\n current,\n create,\n list,\n load,\n delete: remove,\n rename,\n addMessage,\n }\n}\n","'use client'\n\nimport { useState, useCallback, useRef } from 'react'\n\nexport type FeedbackState = 'idle' | 'submitting' | 'submitted' | 'error'\n\nexport interface UseFeedbackOptions {\n /** API endpoint URL for feedback submission */\n url: string\n /** Additional request headers */\n headers?: Record<string, string>\n}\n\nexport interface FeedbackPayload {\n messageId: string\n rating: 'positive' | 'negative'\n comment?: string\n}\n\nexport function useFeedback(options: UseFeedbackOptions) {\n const { url, headers } = options\n const [state, setState] = useState<FeedbackState>('idle')\n const [error, setError] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n\n const submit = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n setState('submitting')\n setError(null)\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: JSON.stringify({ messageId, rating, comment }),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n setState('submitted')\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n setError((err as Error).message ?? 'Failed to submit feedback')\n setState('error')\n }\n },\n [url, headers],\n )\n\n const reset = useCallback(() => {\n setState('idle')\n setError(null)\n }, [])\n\n return { state, error, submit, reset }\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport type { AgentInfo } from '../types/agent'\n\nexport interface AgentThemeResult {\n accent: string\n icon: AgentInfo['icon'] | null\n label: string\n}\n\nconst DEFAULT_ACCENT = '#6366f1'\nconst DEFAULT_LABEL = 'Agent'\n\nexport function useAgentTheme(\n agentId: string | null | undefined,\n agentThemes?: Record<string, AgentInfo>,\n): AgentThemeResult {\n return useMemo(() => {\n if (!agentId) {\n return { accent: DEFAULT_ACCENT, icon: null, label: DEFAULT_LABEL }\n }\n\n const theme = agentThemes?.[agentId]\n if (!theme) {\n return { accent: DEFAULT_ACCENT, icon: null, label: agentId }\n }\n\n return {\n accent: theme.accent ?? DEFAULT_ACCENT,\n icon: theme.icon ?? null,\n label: theme.label,\n }\n }, [agentId, agentThemes])\n}\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n // When the slice would end with whitespace, advance past it to include\n // the next visible character. This prevents trimEnd() in the streaming\n // UI from stripping trailing newlines and causing ReactMarkdown to\n // \"jump\" between block structures in a single frame (e.g. a heading\n // suddenly gaining a following paragraph with no transition).\n while (\n nextIndex < currentTarget.length &&\n currentTarget[nextIndex - 1].trim() === ''\n ) {\n nextIndex++\n }\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n"],"mappings":"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","names":["import_react","initialState","import_react","import_react","import_react","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts","../src/hooks/useAgentChat.ts","../src/hooks/useStreaming.ts","../src/hooks/useConversation.ts","../src/hooks/useFeedback.ts","../src/hooks/useAgentTheme.ts","../src/hooks/useCharacterDrain.ts"],"sourcesContent":["export { useAgentChat } from './hooks/useAgentChat'\nexport type { AgentChatState, AgentChatActions } from './hooks/useAgentChat'\n\nexport { useStreaming } from './hooks/useStreaming'\nexport type { UseStreamingOptions } from './hooks/useStreaming'\n\nexport { useConversation } from './hooks/useConversation'\nexport type { Conversation, UseConversationOptions } from './hooks/useConversation'\n\nexport { useFeedback } from './hooks/useFeedback'\nexport type { FeedbackState, UseFeedbackOptions, FeedbackPayload } from './hooks/useFeedback'\n\nexport { useAgentTheme } from './hooks/useAgentTheme'\nexport type { AgentThemeResult } from './hooks/useAgentTheme'\n\nexport { useCharacterDrain } from './hooks/useCharacterDrain'\nexport type { CharacterDrainResult } from './hooks/useCharacterDrain'\n","'use client'\n\nimport { useReducer, useCallback, useRef } from 'react'\nimport type { ChatMessage, ChatError, Attachment } from '../types/chat'\nimport type { AgentResponse } from '../types/agent'\nimport type { StreamState } from '../types/streaming'\nimport type { AgentChatConfig } from '../types/config'\n\n// ── State ──────────────────────────────────────────────────────────────\n\nexport interface AgentChatState {\n messages: ChatMessage[]\n conversationId: string | null\n isLoading: boolean\n error: ChatError | null\n inputValue: string\n streamPhase: StreamState['phase']\n streamingContent: string\n streamingAgent: string | null\n}\n\nconst initialState: AgentChatState = {\n messages: [],\n conversationId: null,\n isLoading: false,\n error: null,\n inputValue: '',\n streamPhase: 'idle',\n streamingContent: '',\n streamingAgent: null,\n}\n\n// ── Actions ────────────────────────────────────────────────────────────\n\ntype Action =\n | { type: 'SET_INPUT'; value: string }\n | { type: 'SEND_START'; message: ChatMessage }\n | { type: 'STREAM_PHASE'; phase: StreamState['phase'] }\n | { type: 'STREAM_CONTENT'; content: string }\n | { type: 'STREAM_CONTENT_RESET' }\n | { type: 'STREAM_AGENT'; agent: string }\n | { type: 'SEND_SUCCESS'; message: ChatMessage; streamingContent: string; conversationId: string | null }\n | { type: 'SEND_ERROR'; error: ChatError }\n | { type: 'LOAD_CONVERSATION'; conversationId: string; messages: ChatMessage[] }\n | { type: 'RESET' }\n | { type: 'CLEAR_ERROR' }\n\nfunction reducer(state: AgentChatState, action: Action): AgentChatState {\n switch (action.type) {\n case 'SET_INPUT':\n return { ...state, inputValue: action.value }\n\n case 'SEND_START':\n return {\n ...state,\n messages: [...state.messages, action.message],\n isLoading: true,\n error: null,\n inputValue: '',\n streamPhase: 'thinking',\n streamingContent: '',\n streamingAgent: null,\n }\n\n case 'STREAM_PHASE':\n return { ...state, streamPhase: action.phase }\n\n case 'STREAM_CONTENT':\n return { ...state, streamingContent: state.streamingContent + action.content }\n\n case 'STREAM_CONTENT_RESET':\n return { ...state, streamingContent: '' }\n\n case 'STREAM_AGENT':\n return { ...state, streamingAgent: action.agent }\n\n case 'SEND_SUCCESS':\n return {\n ...state,\n messages: [...state.messages, action.message],\n conversationId: action.conversationId ?? state.conversationId,\n isLoading: false,\n streamPhase: 'idle',\n streamingContent: '',\n }\n\n case 'SEND_ERROR':\n return {\n ...state,\n isLoading: false,\n error: action.error,\n streamPhase: 'idle',\n streamingContent: '',\n streamingAgent: null,\n }\n\n case 'LOAD_CONVERSATION':\n return {\n ...state,\n conversationId: action.conversationId,\n messages: action.messages,\n error: null,\n }\n\n case 'RESET':\n return { ...initialState }\n\n case 'CLEAR_ERROR':\n return { ...state, error: null }\n\n default:\n return state\n }\n}\n\n// ── Hook ───────────────────────────────────────────────────────────────\n\nlet msgIdCounter = 0\nfunction generateMessageId(): string {\n return `msg-${Date.now()}-${++msgIdCounter}`\n}\n\nexport interface AgentChatActions {\n sendMessage: (content: string, attachments?: Attachment[]) => Promise<void>\n setInputValue: (value: string) => void\n loadConversation: (conversationId: string, messages: ChatMessage[]) => void\n submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>\n retry: () => Promise<void>\n stop: () => void\n reset: () => void\n}\n\nexport function useAgentChat(config: AgentChatConfig) {\n const [state, dispatch] = useReducer(reducer, initialState)\n const configRef = useRef(config)\n configRef.current = config\n const lastUserMessageRef = useRef<string | null>(null)\n const lastUserAttachmentsRef = useRef<Attachment[] | undefined>(undefined)\n const abortControllerRef = useRef<AbortController | null>(null)\n\n const sendMessage = useCallback(\n async (content: string, attachments?: Attachment[]) => {\n const { apiUrl, streamPath = '/chat/stream', headers: headersOrFn, timeout = 30000, bodyExtra } = configRef.current\n const headers = typeof headersOrFn === 'function' ? await headersOrFn() : (headersOrFn ?? {})\n\n lastUserMessageRef.current = content\n lastUserAttachmentsRef.current = attachments\n\n const userMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'user',\n content,\n attachments,\n timestamp: new Date(),\n }\n\n dispatch({ type: 'SEND_START', message: userMessage })\n\n const controller = new AbortController()\n abortControllerRef.current = controller\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // These variables are mutated inside handleEvent (called from async stream processing).\n // TypeScript can't track mutations through closures, so we use a mutable context object.\n // Defined outside try so the catch block can access accumulated content for user-initiated stop.\n const ctx = {\n accumulatedContent: '',\n agentResponse: null as AgentResponse | null,\n capturedAgent: null as string | null,\n capturedConversationId: null as string | null,\n hadStreamError: false,\n }\n\n try {\n const url = `${apiUrl}${streamPath}`\n const mergedHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n }\n\n // Build request body — include attachments if present\n const requestBody: Record<string, unknown> = {\n message: content,\n conversation_id: state.conversationId,\n ...bodyExtra,\n }\n if (attachments && attachments.length > 0) {\n requestBody.attachments = attachments.map(a => ({\n filename: a.filename,\n content_type: a.content_type,\n data: a.data,\n }))\n }\n const body = JSON.stringify(requestBody)\n\n // Shared handler for parsed SSE events (used by both adapter and default paths)\n const handleEvent = (event: { type: string; [key: string]: unknown }) => {\n switch (event.type) {\n case 'agent':\n ctx.capturedAgent = event.agent as string\n dispatch({ type: 'STREAM_AGENT', agent: ctx.capturedAgent })\n break\n case 'phase':\n dispatch({ type: 'STREAM_PHASE', phase: event.phase as StreamState['phase'] })\n break\n case 'delta':\n ctx.accumulatedContent += event.content\n dispatch({ type: 'STREAM_CONTENT', content: event.content as string })\n break\n case 'delta_reset':\n ctx.accumulatedContent = ''\n dispatch({ type: 'STREAM_CONTENT_RESET' })\n break\n case 'done':\n ctx.agentResponse = event.response as AgentResponse\n ctx.capturedConversationId = (event.conversation_id as string) ?? null\n break\n case 'error':\n ctx.hadStreamError = true\n dispatch({ type: 'SEND_ERROR', error: event.error as ChatError })\n break\n }\n }\n\n const { streamAdapter } = configRef.current\n\n if (streamAdapter) {\n // Use the custom stream adapter (e.g. React Native XHR-based SSE)\n await streamAdapter(\n url,\n { method: 'POST', headers: mergedHeaders, body, signal: controller.signal },\n handleEvent,\n )\n clearTimeout(timeoutId)\n } else {\n // Default path: fetch + ReadableStream getReader()\n const response = await fetch(url, {\n method: 'POST',\n headers: mergedHeaders,\n body,\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n })\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'STREAM_ERROR', message: 'No response body', retryable: true },\n })\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data)\n handleEvent(event)\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n\n // If an error event was dispatched during streaming, don't dispatch success\n if (ctx.hadStreamError) return\n\n const assistantMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'assistant',\n content: ctx.agentResponse?.message ?? ctx.accumulatedContent,\n response: ctx.agentResponse ?? undefined,\n agent: ctx.capturedAgent ?? undefined,\n timestamp: new Date(),\n }\n\n dispatch({\n type: 'SEND_SUCCESS',\n message: assistantMessage,\n streamingContent: ctx.accumulatedContent,\n conversationId: ctx.capturedConversationId,\n })\n } catch (err: unknown) {\n clearTimeout(timeoutId)\n if ((err as Error).name === 'AbortError') {\n // User-initiated stop: commit whatever content we have so far\n if (ctx.accumulatedContent) {\n const partialMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'assistant',\n content: ctx.accumulatedContent,\n agent: ctx.capturedAgent ?? undefined,\n timestamp: new Date(),\n }\n dispatch({\n type: 'SEND_SUCCESS',\n message: partialMessage,\n streamingContent: ctx.accumulatedContent,\n conversationId: ctx.capturedConversationId,\n })\n } else {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'ABORTED', message: 'Request stopped', retryable: true },\n })\n }\n } else {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n })\n }\n } finally {\n abortControllerRef.current = null\n }\n },\n [state.conversationId],\n )\n\n const setInputValue = useCallback((value: string) => {\n dispatch({ type: 'SET_INPUT', value })\n }, [])\n\n const loadConversation = useCallback((conversationId: string, messages: ChatMessage[]) => {\n dispatch({ type: 'LOAD_CONVERSATION', conversationId, messages })\n }, [])\n\n const submitFeedback = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n const { apiUrl, feedbackPath = '/feedback', headers: headersOrFn } = configRef.current\n const headers = typeof headersOrFn === 'function' ? await headersOrFn() : (headersOrFn ?? {})\n await fetch(`${apiUrl}${feedbackPath}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify({ messageId, rating, comment }),\n })\n },\n [],\n )\n\n const retry = useCallback(async () => {\n if (lastUserMessageRef.current) {\n await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current)\n }\n }, [sendMessage])\n\n const stop = useCallback(() => {\n abortControllerRef.current?.abort()\n }, [])\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' })\n lastUserMessageRef.current = null\n lastUserAttachmentsRef.current = undefined\n }, [])\n\n const actions: AgentChatActions = {\n sendMessage,\n setInputValue,\n loadConversation,\n submitFeedback,\n retry,\n stop,\n reset,\n }\n\n return { state, actions }\n}\n","'use client'\n\nimport { useState, useCallback, useRef, useEffect } from 'react'\nimport type { StreamEvent, StreamState } from '../types/streaming'\nimport type { Source } from '../types/agent'\n\nexport interface UseStreamingOptions {\n /** SSE endpoint URL */\n url: string\n /** Additional headers for fetch-based SSE (not used with native EventSource) */\n headers?: Record<string, string>\n /** Called when a complete response is received */\n onDone?: (event: Extract<StreamEvent, { type: 'done' }>) => void\n /** Called on error */\n onError?: (event: Extract<StreamEvent, { type: 'error' }>) => void\n}\n\nconst initialState: StreamState = {\n active: false,\n phase: 'idle',\n content: '',\n sources: [],\n agent: null,\n agentLabel: null,\n}\n\nexport function useStreaming(options: UseStreamingOptions) {\n const { url, headers, onDone, onError } = options\n const [state, setState] = useState<StreamState>(initialState)\n const abortRef = useRef<AbortController | null>(null)\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const stop = useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort()\n abortRef.current = null\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n }, [])\n\n const start = useCallback(\n async (body: Record<string, unknown>) => {\n // Reset state\n setState({ ...initialState, active: true, phase: 'thinking' })\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data) as StreamEvent\n processEvent(event, setState, optionsRef)\n } catch {\n // Skip malformed events\n }\n }\n }\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n }\n },\n [url, headers],\n )\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (abortRef.current) {\n abortRef.current.abort()\n }\n }\n }, [])\n\n return { state, start, stop }\n}\n\nfunction processEvent(\n event: StreamEvent,\n setState: React.Dispatch<React.SetStateAction<StreamState>>,\n optionsRef: React.MutableRefObject<UseStreamingOptions>,\n) {\n switch (event.type) {\n case 'phase':\n setState((prev) => ({ ...prev, phase: event.phase }))\n break\n case 'delta':\n setState((prev) => ({ ...prev, content: prev.content + event.content }))\n break\n case 'source':\n setState((prev) => ({ ...prev, sources: [...prev.sources, event.source] }))\n break\n case 'agent':\n setState((prev) => ({ ...prev, agent: event.agent }))\n break\n case 'done':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onDone?.(event)\n break\n case 'error':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(event)\n break\n }\n}\n","'use client'\n\nimport { useState, useCallback } from 'react'\nimport type { ChatMessage } from '../types/chat'\nimport type { ConversationSummary } from '../types/chat'\n\nexport interface Conversation {\n id: string\n title: string\n messages: ChatMessage[]\n createdAt: Date\n updatedAt: Date\n}\n\nexport interface UseConversationOptions {\n /** Enable localStorage persistence */\n persist?: boolean\n /** localStorage key prefix */\n storageKey?: string\n}\n\nconst STORAGE_PREFIX = 'surf-kit-conversations'\n\nfunction getStorageKey(prefix: string) {\n return `${prefix}:list`\n}\n\nfunction loadFromStorage(storageKey: string): Conversation[] {\n if (typeof window === 'undefined') return []\n try {\n const raw = window.localStorage.getItem(getStorageKey(storageKey))\n if (!raw) return []\n const parsed = JSON.parse(raw) as Array<Conversation & { createdAt: string; updatedAt: string }>\n return parsed.map((c) => ({\n ...c,\n createdAt: new Date(c.createdAt),\n updatedAt: new Date(c.updatedAt),\n messages: c.messages.map((m) => ({ ...m, timestamp: new Date(m.timestamp as unknown as string) })),\n }))\n } catch {\n return []\n }\n}\n\nfunction saveToStorage(storageKey: string, conversations: Conversation[]) {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(getStorageKey(storageKey), JSON.stringify(conversations))\n } catch {\n // Storage full or unavailable\n }\n}\n\nlet idCounter = 0\nfunction generateId(): string {\n return `conv-${Date.now()}-${++idCounter}`\n}\n\nexport function useConversation(options: UseConversationOptions = {}) {\n const { persist = false, storageKey = STORAGE_PREFIX } = options\n\n const [conversations, setConversations] = useState<Conversation[]>(() =>\n persist ? loadFromStorage(storageKey) : [],\n )\n const [current, setCurrent] = useState<Conversation | null>(null)\n\n const persistIfNeeded = useCallback(\n (convs: Conversation[]) => {\n if (persist) saveToStorage(storageKey, convs)\n },\n [persist, storageKey],\n )\n\n const create = useCallback(\n (title?: string): Conversation => {\n const now = new Date()\n const conv: Conversation = {\n id: generateId(),\n title: title ?? 'New Conversation',\n messages: [],\n createdAt: now,\n updatedAt: now,\n }\n setConversations((prev) => {\n const next = [conv, ...prev]\n persistIfNeeded(next)\n return next\n })\n setCurrent(conv)\n return conv\n },\n [persistIfNeeded],\n )\n\n const list = useCallback((): ConversationSummary[] => {\n return conversations.map((c) => ({\n id: c.id,\n title: c.title,\n lastMessage: c.messages.length > 0 ? c.messages[c.messages.length - 1].content : '',\n updatedAt: c.updatedAt,\n messageCount: c.messages.length,\n }))\n }, [conversations])\n\n const load = useCallback(\n (id: string): Conversation | null => {\n const conv = conversations.find((c) => c.id === id) ?? null\n setCurrent(conv)\n return conv\n },\n [conversations],\n )\n\n const remove = useCallback(\n (id: string) => {\n setConversations((prev) => {\n const next = prev.filter((c) => c.id !== id)\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? null : prev))\n },\n [persistIfNeeded],\n )\n\n const rename = useCallback(\n (id: string, title: string) => {\n setConversations((prev) => {\n const next = prev.map((c) => (c.id === id ? { ...c, title, updatedAt: new Date() } : c))\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? { ...prev, title, updatedAt: new Date() } : prev))\n },\n [persistIfNeeded],\n )\n\n const addMessage = useCallback(\n (conversationId: string, message: ChatMessage) => {\n setConversations((prev) => {\n const next = prev.map((c) =>\n c.id === conversationId\n ? { ...c, messages: [...c.messages, message], updatedAt: new Date() }\n : c,\n )\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) =>\n prev?.id === conversationId\n ? { ...prev, messages: [...prev.messages, message], updatedAt: new Date() }\n : prev,\n )\n },\n [persistIfNeeded],\n )\n\n return {\n conversations,\n current,\n create,\n list,\n load,\n delete: remove,\n rename,\n addMessage,\n }\n}\n","'use client'\n\nimport { useState, useCallback, useRef } from 'react'\n\nexport type FeedbackState = 'idle' | 'submitting' | 'submitted' | 'error'\n\nexport interface UseFeedbackOptions {\n /** API endpoint URL for feedback submission */\n url: string\n /** Additional request headers */\n headers?: Record<string, string>\n}\n\nexport interface FeedbackPayload {\n messageId: string\n rating: 'positive' | 'negative'\n comment?: string\n}\n\nexport function useFeedback(options: UseFeedbackOptions) {\n const { url, headers } = options\n const [state, setState] = useState<FeedbackState>('idle')\n const [error, setError] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n\n const submit = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n setState('submitting')\n setError(null)\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: JSON.stringify({ messageId, rating, comment }),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n setState('submitted')\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n setError((err as Error).message ?? 'Failed to submit feedback')\n setState('error')\n }\n },\n [url, headers],\n )\n\n const reset = useCallback(() => {\n setState('idle')\n setError(null)\n }, [])\n\n return { state, error, submit, reset }\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport type { AgentInfo } from '../types/agent'\n\nexport interface AgentThemeResult {\n accent: string\n icon: AgentInfo['icon'] | null\n label: string\n}\n\nconst DEFAULT_ACCENT = '#6366f1'\nconst DEFAULT_LABEL = 'Agent'\n\nexport function useAgentTheme(\n agentId: string | null | undefined,\n agentThemes?: Record<string, AgentInfo>,\n): AgentThemeResult {\n return useMemo(() => {\n if (!agentId) {\n return { accent: DEFAULT_ACCENT, icon: null, label: DEFAULT_LABEL }\n }\n\n const theme = agentThemes?.[agentId]\n if (!theme) {\n return { accent: DEFAULT_ACCENT, icon: null, label: agentId }\n }\n\n return {\n accent: theme.accent ?? DEFAULT_ACCENT,\n icon: theme.icon ?? null,\n label: theme.label,\n }\n }, [agentId, agentThemes])\n}\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n // When the slice would end with whitespace, advance past it to include\n // the next visible character. This prevents trimEnd() in the streaming\n // UI from stripping trailing newlines and causing ReactMarkdown to\n // \"jump\" between block structures in a single frame (e.g. a heading\n // suddenly gaining a following paragraph with no transition).\n while (\n nextIndex < currentTarget.length &&\n currentTarget[nextIndex - 1].trim() === ''\n ) {\n nextIndex++\n }\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n"],"mappings":"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","names":["import_react","initialState","import_react","import_react","import_react","import_react"]}
|
package/dist/hooks.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-
|
|
2
|
-
import './chat-
|
|
1
|
+
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-CSGGLd7j.cjs';
|
|
2
|
+
import './chat-BRY3xGg_.cjs';
|
|
3
3
|
import './agent-BNSmiexZ.cjs';
|
|
4
4
|
import 'react';
|
|
5
|
-
import './streaming-
|
|
5
|
+
import './streaming-BHPXnwwo.cjs';
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-
|
|
2
|
-
import './chat-
|
|
1
|
+
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-BLeiVk-x.js';
|
|
2
|
+
import './chat-CcKc6OAR.js';
|
|
3
3
|
import './agent-BNSmiexZ.js';
|
|
4
4
|
import 'react';
|
|
5
|
-
import './streaming-
|
|
5
|
+
import './streaming-C6mbU7My.js';
|
package/dist/hooks.js
CHANGED
|
@@ -31,6 +31,8 @@ function reducer(state, action) {
|
|
|
31
31
|
return { ...state, streamPhase: action.phase };
|
|
32
32
|
case "STREAM_CONTENT":
|
|
33
33
|
return { ...state, streamingContent: state.streamingContent + action.content };
|
|
34
|
+
case "STREAM_CONTENT_RESET":
|
|
35
|
+
return { ...state, streamingContent: "" };
|
|
34
36
|
case "STREAM_AGENT":
|
|
35
37
|
return { ...state, streamingAgent: action.agent };
|
|
36
38
|
case "SEND_SUCCESS":
|
|
@@ -76,6 +78,7 @@ function useAgentChat(config) {
|
|
|
76
78
|
configRef.current = config;
|
|
77
79
|
const lastUserMessageRef = useRef(null);
|
|
78
80
|
const lastUserAttachmentsRef = useRef(void 0);
|
|
81
|
+
const abortControllerRef = useRef(null);
|
|
79
82
|
const sendMessage = useCallback(
|
|
80
83
|
async (content, attachments) => {
|
|
81
84
|
const { apiUrl, streamPath = "/chat/stream", headers: headersOrFn, timeout = 3e4, bodyExtra } = configRef.current;
|
|
@@ -91,7 +94,15 @@ function useAgentChat(config) {
|
|
|
91
94
|
};
|
|
92
95
|
dispatch({ type: "SEND_START", message: userMessage });
|
|
93
96
|
const controller = new AbortController();
|
|
97
|
+
abortControllerRef.current = controller;
|
|
94
98
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
99
|
+
const ctx = {
|
|
100
|
+
accumulatedContent: "",
|
|
101
|
+
agentResponse: null,
|
|
102
|
+
capturedAgent: null,
|
|
103
|
+
capturedConversationId: null,
|
|
104
|
+
hadStreamError: false
|
|
105
|
+
};
|
|
95
106
|
try {
|
|
96
107
|
const url = `${apiUrl}${streamPath}`;
|
|
97
108
|
const mergedHeaders = {
|
|
@@ -112,13 +123,6 @@ function useAgentChat(config) {
|
|
|
112
123
|
}));
|
|
113
124
|
}
|
|
114
125
|
const body = JSON.stringify(requestBody);
|
|
115
|
-
const ctx = {
|
|
116
|
-
accumulatedContent: "",
|
|
117
|
-
agentResponse: null,
|
|
118
|
-
capturedAgent: null,
|
|
119
|
-
capturedConversationId: null,
|
|
120
|
-
hadStreamError: false
|
|
121
|
-
};
|
|
122
126
|
const handleEvent = (event) => {
|
|
123
127
|
switch (event.type) {
|
|
124
128
|
case "agent":
|
|
@@ -132,6 +136,10 @@ function useAgentChat(config) {
|
|
|
132
136
|
ctx.accumulatedContent += event.content;
|
|
133
137
|
dispatch({ type: "STREAM_CONTENT", content: event.content });
|
|
134
138
|
break;
|
|
139
|
+
case "delta_reset":
|
|
140
|
+
ctx.accumulatedContent = "";
|
|
141
|
+
dispatch({ type: "STREAM_CONTENT_RESET" });
|
|
142
|
+
break;
|
|
135
143
|
case "done":
|
|
136
144
|
ctx.agentResponse = event.response;
|
|
137
145
|
ctx.capturedConversationId = event.conversation_id ?? null;
|
|
@@ -215,10 +223,26 @@ function useAgentChat(config) {
|
|
|
215
223
|
} catch (err) {
|
|
216
224
|
clearTimeout(timeoutId);
|
|
217
225
|
if (err.name === "AbortError") {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
if (ctx.accumulatedContent) {
|
|
227
|
+
const partialMessage = {
|
|
228
|
+
id: generateMessageId(),
|
|
229
|
+
role: "assistant",
|
|
230
|
+
content: ctx.accumulatedContent,
|
|
231
|
+
agent: ctx.capturedAgent ?? void 0,
|
|
232
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
233
|
+
};
|
|
234
|
+
dispatch({
|
|
235
|
+
type: "SEND_SUCCESS",
|
|
236
|
+
message: partialMessage,
|
|
237
|
+
streamingContent: ctx.accumulatedContent,
|
|
238
|
+
conversationId: ctx.capturedConversationId
|
|
239
|
+
});
|
|
240
|
+
} else {
|
|
241
|
+
dispatch({
|
|
242
|
+
type: "SEND_ERROR",
|
|
243
|
+
error: { code: "ABORTED", message: "Request stopped", retryable: true }
|
|
244
|
+
});
|
|
245
|
+
}
|
|
222
246
|
} else {
|
|
223
247
|
dispatch({
|
|
224
248
|
type: "SEND_ERROR",
|
|
@@ -229,6 +253,8 @@ function useAgentChat(config) {
|
|
|
229
253
|
}
|
|
230
254
|
});
|
|
231
255
|
}
|
|
256
|
+
} finally {
|
|
257
|
+
abortControllerRef.current = null;
|
|
232
258
|
}
|
|
233
259
|
},
|
|
234
260
|
[state.conversationId]
|
|
@@ -256,6 +282,9 @@ function useAgentChat(config) {
|
|
|
256
282
|
await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current);
|
|
257
283
|
}
|
|
258
284
|
}, [sendMessage]);
|
|
285
|
+
const stop = useCallback(() => {
|
|
286
|
+
abortControllerRef.current?.abort();
|
|
287
|
+
}, []);
|
|
259
288
|
const reset = useCallback(() => {
|
|
260
289
|
dispatch({ type: "RESET" });
|
|
261
290
|
lastUserMessageRef.current = null;
|
|
@@ -267,6 +296,7 @@ function useAgentChat(config) {
|
|
|
267
296
|
loadConversation,
|
|
268
297
|
submitFeedback,
|
|
269
298
|
retry,
|
|
299
|
+
stop,
|
|
270
300
|
reset
|
|
271
301
|
};
|
|
272
302
|
return { state, actions };
|