@surf-kit/agent 0.2.2 → 0.4.0
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 +733 -222
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +13 -7
- package/dist/chat/index.d.ts +13 -7
- package/dist/chat/index.js +715 -204
- package/dist/chat/index.js.map +1 -1
- package/dist/{chat-ChYl2XjV.d.cts → chat-BRY3xGg_.d.cts} +11 -2
- package/dist/{chat--OifhIRe.d.ts → chat-CcKc6OAR.d.ts} +11 -2
- package/dist/{hooks-BGs8-4GK.d.ts → hooks-BLeiVk-x.d.ts} +22 -5
- package/dist/{hooks-DLfF18IU.d.cts → hooks-CSGGLd7j.d.cts} +22 -5
- package/dist/hooks.cjs +160 -85
- 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 +160 -85
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +794 -283
- 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 +758 -247
- package/dist/index.js.map +1 -1
- package/dist/layouts/index.cjs +754 -243
- 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 +733 -222
- package/dist/layouts/index.js.map +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +2 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/response/index.cjs +100 -15
- package/dist/response/index.cjs.map +1 -1
- package/dist/response/index.d.cts +2 -2
- package/dist/response/index.d.ts +2 -2
- package/dist/response/index.js +99 -14
- package/dist/response/index.js.map +1 -1
- package/dist/sources/index.cjs +30 -1
- package/dist/sources/index.cjs.map +1 -1
- package/dist/sources/index.js +30 -1
- package/dist/sources/index.js.map +1 -1
- package/dist/streaming/index.cjs +213 -93
- package/dist/streaming/index.cjs.map +1 -1
- package/dist/streaming/index.d.cts +4 -3
- package/dist/streaming/index.d.ts +4 -3
- package/dist/streaming/index.js +183 -73
- package/dist/streaming/index.js.map +1 -1
- package/dist/{streaming-DfT22A0z.d.cts → streaming-BHPXnwwo.d.cts} +3 -1
- package/dist/{streaming-DbQxScpi.d.ts → streaming-C6mbU7My.d.ts} +3 -1
- package/package.json +17 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { 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 {
|
|
@@ -11,14 +11,29 @@ interface AgentChatConfig {
|
|
|
11
11
|
feedbackPath?: string;
|
|
12
12
|
/** Conversations endpoint path (appended to apiUrl) */
|
|
13
13
|
conversationsPath?: string;
|
|
14
|
-
/** Request headers (e.g. Authorization) */
|
|
15
|
-
headers?: Record<string, string
|
|
14
|
+
/** Request headers (e.g. Authorization). Can be a static object or an async function that returns headers (useful for refreshing auth tokens). */
|
|
15
|
+
headers?: Record<string, string> | (() => Promise<Record<string, string>>);
|
|
16
16
|
/** Request timeout in milliseconds */
|
|
17
17
|
timeout?: number;
|
|
18
18
|
/** Enable localStorage persistence for conversations */
|
|
19
19
|
persistConversations?: boolean;
|
|
20
20
|
/** Map of agent IDs to their display config */
|
|
21
21
|
agentThemes?: Record<string, AgentInfo>;
|
|
22
|
+
/** Extra fields merged into every request body (e.g. `{ agent: "research" }`).
|
|
23
|
+
* Keys here are shallow-merged after the default fields (message, conversation_id, attachments). */
|
|
24
|
+
bodyExtra?: Record<string, unknown>;
|
|
25
|
+
/** Custom stream reader for environments without ReadableStream (e.g. React Native).
|
|
26
|
+
* When provided, this function handles sending the request and parsing SSE events
|
|
27
|
+
* instead of the default fetch + getReader() approach. */
|
|
28
|
+
streamAdapter?: (url: string, options: {
|
|
29
|
+
method: string;
|
|
30
|
+
headers: Record<string, string>;
|
|
31
|
+
body: string;
|
|
32
|
+
signal: AbortSignal;
|
|
33
|
+
}, onEvent: (event: {
|
|
34
|
+
type: string;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}) => void) => Promise<void>;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
interface AgentChatState {
|
|
@@ -29,13 +44,15 @@ interface AgentChatState {
|
|
|
29
44
|
inputValue: string;
|
|
30
45
|
streamPhase: StreamState['phase'];
|
|
31
46
|
streamingContent: string;
|
|
47
|
+
streamingAgent: string | null;
|
|
32
48
|
}
|
|
33
49
|
interface AgentChatActions {
|
|
34
|
-
sendMessage: (content: string) => Promise<void>;
|
|
50
|
+
sendMessage: (content: string, attachments?: Attachment[]) => Promise<void>;
|
|
35
51
|
setInputValue: (value: string) => void;
|
|
36
52
|
loadConversation: (conversationId: string, messages: ChatMessage[]) => void;
|
|
37
53
|
submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>;
|
|
38
54
|
retry: () => Promise<void>;
|
|
55
|
+
stop: () => void;
|
|
39
56
|
reset: () => void;
|
|
40
57
|
}
|
|
41
58
|
declare function useAgentChat(config: AgentChatConfig): {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { 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 {
|
|
@@ -11,14 +11,29 @@ interface AgentChatConfig {
|
|
|
11
11
|
feedbackPath?: string;
|
|
12
12
|
/** Conversations endpoint path (appended to apiUrl) */
|
|
13
13
|
conversationsPath?: string;
|
|
14
|
-
/** Request headers (e.g. Authorization) */
|
|
15
|
-
headers?: Record<string, string
|
|
14
|
+
/** Request headers (e.g. Authorization). Can be a static object or an async function that returns headers (useful for refreshing auth tokens). */
|
|
15
|
+
headers?: Record<string, string> | (() => Promise<Record<string, string>>);
|
|
16
16
|
/** Request timeout in milliseconds */
|
|
17
17
|
timeout?: number;
|
|
18
18
|
/** Enable localStorage persistence for conversations */
|
|
19
19
|
persistConversations?: boolean;
|
|
20
20
|
/** Map of agent IDs to their display config */
|
|
21
21
|
agentThemes?: Record<string, AgentInfo>;
|
|
22
|
+
/** Extra fields merged into every request body (e.g. `{ agent: "research" }`).
|
|
23
|
+
* Keys here are shallow-merged after the default fields (message, conversation_id, attachments). */
|
|
24
|
+
bodyExtra?: Record<string, unknown>;
|
|
25
|
+
/** Custom stream reader for environments without ReadableStream (e.g. React Native).
|
|
26
|
+
* When provided, this function handles sending the request and parsing SSE events
|
|
27
|
+
* instead of the default fetch + getReader() approach. */
|
|
28
|
+
streamAdapter?: (url: string, options: {
|
|
29
|
+
method: string;
|
|
30
|
+
headers: Record<string, string>;
|
|
31
|
+
body: string;
|
|
32
|
+
signal: AbortSignal;
|
|
33
|
+
}, onEvent: (event: {
|
|
34
|
+
type: string;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}) => void) => Promise<void>;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
interface AgentChatState {
|
|
@@ -29,13 +44,15 @@ interface AgentChatState {
|
|
|
29
44
|
inputValue: string;
|
|
30
45
|
streamPhase: StreamState['phase'];
|
|
31
46
|
streamingContent: string;
|
|
47
|
+
streamingAgent: string | null;
|
|
32
48
|
}
|
|
33
49
|
interface AgentChatActions {
|
|
34
|
-
sendMessage: (content: string) => Promise<void>;
|
|
50
|
+
sendMessage: (content: string, attachments?: Attachment[]) => Promise<void>;
|
|
35
51
|
setInputValue: (value: string) => void;
|
|
36
52
|
loadConversation: (conversationId: string, messages: ChatMessage[]) => void;
|
|
37
53
|
submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>;
|
|
38
54
|
retry: () => Promise<void>;
|
|
55
|
+
stop: () => void;
|
|
39
56
|
reset: () => void;
|
|
40
57
|
}
|
|
41
58
|
declare function useAgentChat(config: AgentChatConfig): {
|
package/dist/hooks.cjs
CHANGED
|
@@ -39,7 +39,8 @@ var initialState = {
|
|
|
39
39
|
error: null,
|
|
40
40
|
inputValue: "",
|
|
41
41
|
streamPhase: "idle",
|
|
42
|
-
streamingContent: ""
|
|
42
|
+
streamingContent: "",
|
|
43
|
+
streamingAgent: null
|
|
43
44
|
};
|
|
44
45
|
function reducer(state, action) {
|
|
45
46
|
switch (action.type) {
|
|
@@ -53,12 +54,17 @@ function reducer(state, action) {
|
|
|
53
54
|
error: null,
|
|
54
55
|
inputValue: "",
|
|
55
56
|
streamPhase: "thinking",
|
|
56
|
-
streamingContent: ""
|
|
57
|
+
streamingContent: "",
|
|
58
|
+
streamingAgent: null
|
|
57
59
|
};
|
|
58
60
|
case "STREAM_PHASE":
|
|
59
61
|
return { ...state, streamPhase: action.phase };
|
|
60
62
|
case "STREAM_CONTENT":
|
|
61
63
|
return { ...state, streamingContent: state.streamingContent + action.content };
|
|
64
|
+
case "STREAM_CONTENT_RESET":
|
|
65
|
+
return { ...state, streamingContent: "" };
|
|
66
|
+
case "STREAM_AGENT":
|
|
67
|
+
return { ...state, streamingAgent: action.agent };
|
|
62
68
|
case "SEND_SUCCESS":
|
|
63
69
|
return {
|
|
64
70
|
...state,
|
|
@@ -74,7 +80,8 @@ function reducer(state, action) {
|
|
|
74
80
|
isLoading: false,
|
|
75
81
|
error: action.error,
|
|
76
82
|
streamPhase: "idle",
|
|
77
|
-
streamingContent: ""
|
|
83
|
+
streamingContent: "",
|
|
84
|
+
streamingAgent: null
|
|
78
85
|
};
|
|
79
86
|
case "LOAD_CONVERSATION":
|
|
80
87
|
return {
|
|
@@ -100,115 +107,172 @@ function useAgentChat(config) {
|
|
|
100
107
|
const configRef = (0, import_react.useRef)(config);
|
|
101
108
|
configRef.current = config;
|
|
102
109
|
const lastUserMessageRef = (0, import_react.useRef)(null);
|
|
110
|
+
const lastUserAttachmentsRef = (0, import_react.useRef)(void 0);
|
|
111
|
+
const abortControllerRef = (0, import_react.useRef)(null);
|
|
103
112
|
const sendMessage = (0, import_react.useCallback)(
|
|
104
|
-
async (content) => {
|
|
105
|
-
const { apiUrl, streamPath = "/chat/stream", headers
|
|
113
|
+
async (content, attachments) => {
|
|
114
|
+
const { apiUrl, streamPath = "/chat/stream", headers: headersOrFn, timeout = 3e4, bodyExtra } = configRef.current;
|
|
115
|
+
const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
|
|
106
116
|
lastUserMessageRef.current = content;
|
|
117
|
+
lastUserAttachmentsRef.current = attachments;
|
|
107
118
|
const userMessage = {
|
|
108
119
|
id: generateMessageId(),
|
|
109
120
|
role: "user",
|
|
110
121
|
content,
|
|
122
|
+
attachments,
|
|
111
123
|
timestamp: /* @__PURE__ */ new Date()
|
|
112
124
|
};
|
|
113
125
|
dispatch({ type: "SEND_START", message: userMessage });
|
|
114
126
|
const controller = new AbortController();
|
|
127
|
+
abortControllerRef.current = controller;
|
|
115
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
|
+
};
|
|
116
136
|
try {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
error: {
|
|
135
|
-
code: "API_ERROR",
|
|
136
|
-
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
137
|
-
retryable: response.status >= 500
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
return;
|
|
137
|
+
const url = `${apiUrl}${streamPath}`;
|
|
138
|
+
const mergedHeaders = {
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
Accept: "text/event-stream",
|
|
141
|
+
...headers
|
|
142
|
+
};
|
|
143
|
+
const requestBody = {
|
|
144
|
+
message: content,
|
|
145
|
+
conversation_id: state.conversationId,
|
|
146
|
+
...bodyExtra
|
|
147
|
+
};
|
|
148
|
+
if (attachments && attachments.length > 0) {
|
|
149
|
+
requestBody.attachments = attachments.map((a) => ({
|
|
150
|
+
filename: a.filename,
|
|
151
|
+
content_type: a.content_type,
|
|
152
|
+
data: a.data
|
|
153
|
+
}));
|
|
141
154
|
}
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
const body = JSON.stringify(requestBody);
|
|
156
|
+
const handleEvent = (event) => {
|
|
157
|
+
switch (event.type) {
|
|
158
|
+
case "agent":
|
|
159
|
+
ctx.capturedAgent = event.agent;
|
|
160
|
+
dispatch({ type: "STREAM_AGENT", agent: ctx.capturedAgent });
|
|
161
|
+
break;
|
|
162
|
+
case "phase":
|
|
163
|
+
dispatch({ type: "STREAM_PHASE", phase: event.phase });
|
|
164
|
+
break;
|
|
165
|
+
case "delta":
|
|
166
|
+
ctx.accumulatedContent += event.content;
|
|
167
|
+
dispatch({ type: "STREAM_CONTENT", content: event.content });
|
|
168
|
+
break;
|
|
169
|
+
case "delta_reset":
|
|
170
|
+
ctx.accumulatedContent = "";
|
|
171
|
+
dispatch({ type: "STREAM_CONTENT_RESET" });
|
|
172
|
+
break;
|
|
173
|
+
case "done":
|
|
174
|
+
ctx.agentResponse = event.response;
|
|
175
|
+
ctx.capturedConversationId = event.conversation_id ?? null;
|
|
176
|
+
break;
|
|
177
|
+
case "error":
|
|
178
|
+
ctx.hadStreamError = true;
|
|
179
|
+
dispatch({ type: "SEND_ERROR", error: event.error });
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const { streamAdapter } = configRef.current;
|
|
184
|
+
if (streamAdapter) {
|
|
185
|
+
await streamAdapter(
|
|
186
|
+
url,
|
|
187
|
+
{ method: "POST", headers: mergedHeaders, body, signal: controller.signal },
|
|
188
|
+
handleEvent
|
|
189
|
+
);
|
|
190
|
+
clearTimeout(timeoutId);
|
|
191
|
+
} else {
|
|
192
|
+
const response = await fetch(url, {
|
|
193
|
+
method: "POST",
|
|
194
|
+
headers: mergedHeaders,
|
|
195
|
+
body,
|
|
196
|
+
signal: controller.signal
|
|
147
197
|
});
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
dispatch({ type: "SEND_ERROR", error: event.error });
|
|
185
|
-
return;
|
|
198
|
+
clearTimeout(timeoutId);
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
dispatch({
|
|
201
|
+
type: "SEND_ERROR",
|
|
202
|
+
error: {
|
|
203
|
+
code: "API_ERROR",
|
|
204
|
+
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
205
|
+
retryable: response.status >= 500
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const reader = response.body?.getReader();
|
|
211
|
+
if (!reader) {
|
|
212
|
+
dispatch({
|
|
213
|
+
type: "SEND_ERROR",
|
|
214
|
+
error: { code: "STREAM_ERROR", message: "No response body", retryable: true }
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const decoder = new TextDecoder();
|
|
219
|
+
let buffer = "";
|
|
220
|
+
while (true) {
|
|
221
|
+
const { done, value } = await reader.read();
|
|
222
|
+
if (done) break;
|
|
223
|
+
buffer += decoder.decode(value, { stream: true });
|
|
224
|
+
const lines = buffer.split("\n");
|
|
225
|
+
buffer = lines.pop() ?? "";
|
|
226
|
+
for (const line of lines) {
|
|
227
|
+
if (!line.startsWith("data: ")) continue;
|
|
228
|
+
const data = line.slice(6).trim();
|
|
229
|
+
if (data === "[DONE]") continue;
|
|
230
|
+
try {
|
|
231
|
+
const event = JSON.parse(data);
|
|
232
|
+
handleEvent(event);
|
|
233
|
+
} catch {
|
|
186
234
|
}
|
|
187
|
-
} catch {
|
|
188
235
|
}
|
|
189
236
|
}
|
|
190
237
|
}
|
|
238
|
+
if (ctx.hadStreamError) return;
|
|
191
239
|
const assistantMessage = {
|
|
192
240
|
id: generateMessageId(),
|
|
193
241
|
role: "assistant",
|
|
194
|
-
content: agentResponse?.message ?? accumulatedContent,
|
|
195
|
-
response: agentResponse ?? void 0,
|
|
196
|
-
agent: capturedAgent ?? void 0,
|
|
242
|
+
content: ctx.agentResponse?.message ?? ctx.accumulatedContent,
|
|
243
|
+
response: ctx.agentResponse ?? void 0,
|
|
244
|
+
agent: ctx.capturedAgent ?? void 0,
|
|
197
245
|
timestamp: /* @__PURE__ */ new Date()
|
|
198
246
|
};
|
|
199
247
|
dispatch({
|
|
200
248
|
type: "SEND_SUCCESS",
|
|
201
249
|
message: assistantMessage,
|
|
202
|
-
streamingContent: accumulatedContent,
|
|
203
|
-
conversationId: capturedConversationId
|
|
250
|
+
streamingContent: ctx.accumulatedContent,
|
|
251
|
+
conversationId: ctx.capturedConversationId
|
|
204
252
|
});
|
|
205
253
|
} catch (err) {
|
|
206
254
|
clearTimeout(timeoutId);
|
|
207
255
|
if (err.name === "AbortError") {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
+
}
|
|
212
276
|
} else {
|
|
213
277
|
dispatch({
|
|
214
278
|
type: "SEND_ERROR",
|
|
@@ -219,6 +283,8 @@ function useAgentChat(config) {
|
|
|
219
283
|
}
|
|
220
284
|
});
|
|
221
285
|
}
|
|
286
|
+
} finally {
|
|
287
|
+
abortControllerRef.current = null;
|
|
222
288
|
}
|
|
223
289
|
},
|
|
224
290
|
[state.conversationId]
|
|
@@ -231,7 +297,8 @@ function useAgentChat(config) {
|
|
|
231
297
|
}, []);
|
|
232
298
|
const submitFeedback = (0, import_react.useCallback)(
|
|
233
299
|
async (messageId, rating, comment) => {
|
|
234
|
-
const { apiUrl, feedbackPath = "/feedback", headers
|
|
300
|
+
const { apiUrl, feedbackPath = "/feedback", headers: headersOrFn } = configRef.current;
|
|
301
|
+
const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
|
|
235
302
|
await fetch(`${apiUrl}${feedbackPath}`, {
|
|
236
303
|
method: "POST",
|
|
237
304
|
headers: { "Content-Type": "application/json", ...headers },
|
|
@@ -242,12 +309,16 @@ function useAgentChat(config) {
|
|
|
242
309
|
);
|
|
243
310
|
const retry = (0, import_react.useCallback)(async () => {
|
|
244
311
|
if (lastUserMessageRef.current) {
|
|
245
|
-
await sendMessage(lastUserMessageRef.current);
|
|
312
|
+
await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current);
|
|
246
313
|
}
|
|
247
314
|
}, [sendMessage]);
|
|
315
|
+
const stop = (0, import_react.useCallback)(() => {
|
|
316
|
+
abortControllerRef.current?.abort();
|
|
317
|
+
}, []);
|
|
248
318
|
const reset = (0, import_react.useCallback)(() => {
|
|
249
319
|
dispatch({ type: "RESET" });
|
|
250
320
|
lastUserMessageRef.current = null;
|
|
321
|
+
lastUserAttachmentsRef.current = void 0;
|
|
251
322
|
}, []);
|
|
252
323
|
const actions = {
|
|
253
324
|
sendMessage,
|
|
@@ -255,6 +326,7 @@ function useAgentChat(config) {
|
|
|
255
326
|
loadConversation,
|
|
256
327
|
submitFeedback,
|
|
257
328
|
retry,
|
|
329
|
+
stop,
|
|
258
330
|
reset
|
|
259
331
|
};
|
|
260
332
|
return { state, actions };
|
|
@@ -607,7 +679,10 @@ function useCharacterDrain(target, msPerChar = 15) {
|
|
|
607
679
|
const elapsed = now - lastTimeRef.current;
|
|
608
680
|
const charsToAdvance = Math.floor(elapsed / msPerCharRef.current);
|
|
609
681
|
if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {
|
|
610
|
-
|
|
682
|
+
let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
|
|
683
|
+
while (nextIndex < currentTarget.length && currentTarget[nextIndex - 1].trim() === "") {
|
|
684
|
+
nextIndex++;
|
|
685
|
+
}
|
|
611
686
|
indexRef.current = nextIndex;
|
|
612
687
|
lastTimeRef.current = now;
|
|
613
688
|
setDisplayed(currentTarget.slice(0, nextIndex));
|