@xinghunm/ai-chat 1.2.0 → 1.2.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/index.d.mts +29 -5
- package/dist/index.d.ts +29 -5
- package/dist/index.js +300 -139
- package/dist/index.mjs +292 -131
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -49,6 +49,28 @@ var DEFAULT_AI_CHAT_LABELS = {
|
|
|
49
49
|
questionnaireOtherPlaceholder: "Other"
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
// src/lib/chat-session.ts
|
|
53
|
+
var DRAFT_CHAT_SESSION_ID_PREFIX = "draft-session-";
|
|
54
|
+
var draftChatSessionSequence = 0;
|
|
55
|
+
var createDraftChatSessionId = () => `${DRAFT_CHAT_SESSION_ID_PREFIX}${Date.now()}-${draftChatSessionSequence++}`;
|
|
56
|
+
var isDraftChatSessionId = (sessionId) => Boolean(sessionId?.startsWith(DRAFT_CHAT_SESSION_ID_PREFIX));
|
|
57
|
+
var createDraftChatSession = ({
|
|
58
|
+
model,
|
|
59
|
+
mode = DEFAULT_CHAT_AGENT_MODE,
|
|
60
|
+
nowIso: nowIso2,
|
|
61
|
+
createSessionId = createDraftChatSessionId
|
|
62
|
+
}) => {
|
|
63
|
+
const iso = nowIso2();
|
|
64
|
+
return {
|
|
65
|
+
sessionId: createSessionId(),
|
|
66
|
+
title: "New Chat",
|
|
67
|
+
createdAt: iso,
|
|
68
|
+
updatedAt: iso,
|
|
69
|
+
model,
|
|
70
|
+
mode
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
52
74
|
// src/store/chat-store.ts
|
|
53
75
|
var DEFAULT_CHAT_SESSION_TITLE = "New Chat";
|
|
54
76
|
var IMAGE_MESSAGE_SESSION_TITLE = "Image message";
|
|
@@ -182,6 +204,15 @@ var createChatStore = (initialState) => createStore((set, get) => ({
|
|
|
182
204
|
isStoppingBySession: nextIsStoppingBySession
|
|
183
205
|
});
|
|
184
206
|
},
|
|
207
|
+
startNewChat: () => {
|
|
208
|
+
const state = get();
|
|
209
|
+
const session = createDraftChatSession({
|
|
210
|
+
model: "",
|
|
211
|
+
mode: state.preferredMode,
|
|
212
|
+
nowIso: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
213
|
+
});
|
|
214
|
+
get().createSession(session);
|
|
215
|
+
},
|
|
185
216
|
setActiveSession: (sessionId) => {
|
|
186
217
|
set({ activeSessionId: sessionId });
|
|
187
218
|
},
|
|
@@ -699,6 +730,8 @@ var AiChatProvider = (props) => {
|
|
|
699
730
|
});
|
|
700
731
|
const retryRef = useRef(async () => {
|
|
701
732
|
});
|
|
733
|
+
const stopRef = useRef(async (_sessionId) => {
|
|
734
|
+
});
|
|
702
735
|
const defaultApiBaseUrl = "apiBaseUrl" in props ? props.apiBaseUrl : void 0;
|
|
703
736
|
const defaultAuthToken = "authToken" in props ? props.authToken : void 0;
|
|
704
737
|
const defaultTransformStreamPacket = "transformStreamPacket" in props ? props.transformStreamPacket : void 0;
|
|
@@ -742,6 +775,7 @@ var AiChatProvider = (props) => {
|
|
|
742
775
|
labels: { ...DEFAULT_AI_CHAT_LABELS, ...labels },
|
|
743
776
|
sendRef,
|
|
744
777
|
retryRef,
|
|
778
|
+
stopRef,
|
|
745
779
|
renderMessageBlock,
|
|
746
780
|
handleQuestionnaireSubmit,
|
|
747
781
|
handleConfirmationSubmit,
|
|
@@ -762,6 +796,7 @@ var AiChatProvider = (props) => {
|
|
|
762
796
|
renderMessageBlock,
|
|
763
797
|
sendRef,
|
|
764
798
|
retryRef,
|
|
799
|
+
stopRef,
|
|
765
800
|
store,
|
|
766
801
|
transport
|
|
767
802
|
]
|
|
@@ -1566,7 +1601,6 @@ import {
|
|
|
1566
1601
|
useState as useState2
|
|
1567
1602
|
} from "react";
|
|
1568
1603
|
import styled4 from "@emotion/styled";
|
|
1569
|
-
import { InputField as Input } from "@xinghunm/compass-ui";
|
|
1570
1604
|
|
|
1571
1605
|
// src/components/chat-thread/components/questionnaire-card-helpers.ts
|
|
1572
1606
|
var OTHER_OPTION_VALUE = "__other__";
|
|
@@ -2367,7 +2401,7 @@ var TextInput = styled4.input`
|
|
|
2367
2401
|
color: rgba(255, 255, 255, 0.34);
|
|
2368
2402
|
}
|
|
2369
2403
|
`;
|
|
2370
|
-
var InlineOtherInput = styled4
|
|
2404
|
+
var InlineOtherInput = styled4.input`
|
|
2371
2405
|
width: 100%;
|
|
2372
2406
|
margin-top: 0;
|
|
2373
2407
|
|
|
@@ -3755,19 +3789,20 @@ var ChatThread = () => {
|
|
|
3755
3789
|
if (!activeSessionId)
|
|
3756
3790
|
return;
|
|
3757
3791
|
clearSessionError(activeSessionId);
|
|
3758
|
-
void retryRef.current();
|
|
3792
|
+
void retryRef.current(activeSessionId);
|
|
3759
3793
|
}, [activeSessionId, clearSessionError, retryRef]);
|
|
3760
3794
|
const handleQuestionnaireSubmit = useCallback3(
|
|
3761
3795
|
async (submission) => {
|
|
3796
|
+
const sourceSessionId = activeSessionId;
|
|
3762
3797
|
if (customQuestionnaireSubmit) {
|
|
3763
3798
|
const handled = await customQuestionnaireSubmit(submission, {
|
|
3764
|
-
sessionId:
|
|
3799
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3765
3800
|
mode: activeSessionMode
|
|
3766
3801
|
});
|
|
3767
3802
|
if (handled !== false) {
|
|
3768
|
-
if (
|
|
3803
|
+
if (sourceSessionId && submission.sourceMessageId) {
|
|
3769
3804
|
updateQA(
|
|
3770
|
-
|
|
3805
|
+
sourceSessionId,
|
|
3771
3806
|
submission.sourceMessageId,
|
|
3772
3807
|
submission.questionnaireId,
|
|
3773
3808
|
submission.answers
|
|
@@ -3776,10 +3811,13 @@ var ChatThread = () => {
|
|
|
3776
3811
|
return;
|
|
3777
3812
|
}
|
|
3778
3813
|
}
|
|
3779
|
-
await sendRef.current(submission.content
|
|
3780
|
-
|
|
3814
|
+
await sendRef.current(submission.content, {
|
|
3815
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3816
|
+
includeComposerAttachments: false
|
|
3817
|
+
});
|
|
3818
|
+
if (sourceSessionId && submission.sourceMessageId) {
|
|
3781
3819
|
updateQA(
|
|
3782
|
-
|
|
3820
|
+
sourceSessionId,
|
|
3783
3821
|
submission.sourceMessageId,
|
|
3784
3822
|
submission.questionnaireId,
|
|
3785
3823
|
submission.answers
|
|
@@ -3790,16 +3828,20 @@ var ChatThread = () => {
|
|
|
3790
3828
|
);
|
|
3791
3829
|
const handleConfirmation = useCallback3(
|
|
3792
3830
|
async (submission) => {
|
|
3831
|
+
const sourceSessionId = activeSessionId;
|
|
3793
3832
|
if (customConfirmationSubmit) {
|
|
3794
3833
|
const handled = await customConfirmationSubmit(submission, {
|
|
3795
|
-
sessionId:
|
|
3834
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3796
3835
|
mode: activeSessionMode
|
|
3797
3836
|
});
|
|
3798
3837
|
if (handled !== false) {
|
|
3799
3838
|
return;
|
|
3800
3839
|
}
|
|
3801
3840
|
}
|
|
3802
|
-
await sendRef.current(submission.content
|
|
3841
|
+
await sendRef.current(submission.content, {
|
|
3842
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3843
|
+
includeComposerAttachments: false
|
|
3844
|
+
});
|
|
3803
3845
|
},
|
|
3804
3846
|
[activeSessionId, activeSessionMode, sendRef, customConfirmationSubmit]
|
|
3805
3847
|
);
|
|
@@ -3936,25 +3978,6 @@ import { useEffect as useEffect7, useLayoutEffect as useLayoutEffect3, useRef as
|
|
|
3936
3978
|
import styled14 from "@emotion/styled";
|
|
3937
3979
|
|
|
3938
3980
|
// src/components/chat-composer/lib/chat-composer.ts
|
|
3939
|
-
var DRAFT_CHAT_SESSION_ID_PREFIX = "draft-session-";
|
|
3940
|
-
var createDraftChatSessionId = () => `${DRAFT_CHAT_SESSION_ID_PREFIX}${Date.now()}`;
|
|
3941
|
-
var isDraftChatSessionId = (sessionId) => Boolean(sessionId?.startsWith(DRAFT_CHAT_SESSION_ID_PREFIX));
|
|
3942
|
-
var createDraftChatSession = ({
|
|
3943
|
-
model,
|
|
3944
|
-
mode = DEFAULT_CHAT_AGENT_MODE,
|
|
3945
|
-
nowIso: nowIso2,
|
|
3946
|
-
createSessionId
|
|
3947
|
-
}) => {
|
|
3948
|
-
const iso = nowIso2();
|
|
3949
|
-
return {
|
|
3950
|
-
sessionId: createSessionId(),
|
|
3951
|
-
title: "New Chat",
|
|
3952
|
-
createdAt: iso,
|
|
3953
|
-
updatedAt: iso,
|
|
3954
|
-
model,
|
|
3955
|
-
mode
|
|
3956
|
-
};
|
|
3957
|
-
};
|
|
3958
3981
|
var createUserMessage = ({
|
|
3959
3982
|
sessionId,
|
|
3960
3983
|
content,
|
|
@@ -4155,19 +4178,20 @@ var normalizeChatErrorMessage = (message, labels) => {
|
|
|
4155
4178
|
return trimmedMessage;
|
|
4156
4179
|
};
|
|
4157
4180
|
var useChatComposer = () => {
|
|
4158
|
-
const { transport, enableImageAttachments, labels } = useChatContext();
|
|
4181
|
+
const { transport, enableImageAttachments, labels, store } = useChatContext();
|
|
4159
4182
|
const activeSessionId = useChatStore((s) => s.activeSessionId);
|
|
4160
4183
|
const activeSession = useChatStore(
|
|
4161
4184
|
(s) => s.sessions.find((x) => x.sessionId === s.activeSessionId) ?? null
|
|
4162
4185
|
);
|
|
4163
4186
|
const preferredMode = useChatStore((s) => s.preferredMode);
|
|
4164
4187
|
const streamingSessionId = useChatStore(
|
|
4165
|
-
(s) =>
|
|
4188
|
+
(s) => s.activeSessionId && s.isStreamingBySession[s.activeSessionId] ? s.activeSessionId : null
|
|
4166
4189
|
);
|
|
4167
4190
|
const isStreaming = Boolean(streamingSessionId);
|
|
4168
|
-
const isStopping = useChatStore(
|
|
4169
|
-
|
|
4170
|
-
|
|
4191
|
+
const isStopping = useChatStore((s) => {
|
|
4192
|
+
const currentStreamingSessionId = s.activeSessionId && s.isStreamingBySession[s.activeSessionId] ? s.activeSessionId : null;
|
|
4193
|
+
return currentStreamingSessionId ? s.isStoppingBySession[currentStreamingSessionId] ?? false : false;
|
|
4194
|
+
});
|
|
4171
4195
|
const createSession = useChatStore((s) => s.createSession);
|
|
4172
4196
|
const replaceSessionId = useChatStore((s) => s.replaceSessionId);
|
|
4173
4197
|
const appendMessage = useChatStore((s) => s.appendMessage);
|
|
@@ -4204,9 +4228,9 @@ var useChatComposer = () => {
|
|
|
4204
4228
|
const [selectedMode, setSelectedModeLocal] = useState6(DEFAULT_CHAT_AGENT_MODE);
|
|
4205
4229
|
const [attachmentNotice, setAttachmentNotice] = useState6(null);
|
|
4206
4230
|
const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
|
|
4207
|
-
const
|
|
4208
|
-
const
|
|
4209
|
-
const
|
|
4231
|
+
const abortControllerBySessionRef = useRef7(/* @__PURE__ */ new Map());
|
|
4232
|
+
const stopRequestBySessionRef = useRef7(/* @__PURE__ */ new Map());
|
|
4233
|
+
const lastRequestBySessionRef = useRef7(/* @__PURE__ */ new Map());
|
|
4210
4234
|
useEffect6(() => {
|
|
4211
4235
|
setSelectedModel(
|
|
4212
4236
|
(current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
|
|
@@ -4229,33 +4253,47 @@ var useChatComposer = () => {
|
|
|
4229
4253
|
return () => window.clearTimeout(timeoutId);
|
|
4230
4254
|
}, [attachmentNotice]);
|
|
4231
4255
|
const clearStopTimeout = (sessionId) => {
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
if (sessionId && stopRequestRef.current.sessionId !== sessionId)
|
|
4256
|
+
const stopRequest = stopRequestBySessionRef.current.get(sessionId);
|
|
4257
|
+
if (!stopRequest || stopRequest.timeoutId === null) {
|
|
4235
4258
|
return;
|
|
4236
|
-
if (stopRequestRef.current.timeoutId !== null) {
|
|
4237
|
-
window.clearTimeout(stopRequestRef.current.timeoutId);
|
|
4238
|
-
stopRequestRef.current.timeoutId = null;
|
|
4239
4259
|
}
|
|
4260
|
+
window.clearTimeout(stopRequest.timeoutId);
|
|
4261
|
+
stopRequest.timeoutId = null;
|
|
4240
4262
|
};
|
|
4241
4263
|
const clearStopRequest = useCallback4((sessionId) => {
|
|
4242
|
-
if (!stopRequestRef.current)
|
|
4243
|
-
return;
|
|
4244
|
-
if (sessionId && stopRequestRef.current.sessionId !== sessionId)
|
|
4245
|
-
return;
|
|
4246
4264
|
clearStopTimeout(sessionId);
|
|
4247
|
-
|
|
4265
|
+
stopRequestBySessionRef.current.delete(sessionId);
|
|
4248
4266
|
}, []);
|
|
4267
|
+
const moveSessionRuntimeState = useCallback4(
|
|
4268
|
+
(previousSessionId, nextSessionId) => {
|
|
4269
|
+
if (previousSessionId === nextSessionId) {
|
|
4270
|
+
return;
|
|
4271
|
+
}
|
|
4272
|
+
const abortController = abortControllerBySessionRef.current.get(previousSessionId);
|
|
4273
|
+
if (abortController) {
|
|
4274
|
+
abortControllerBySessionRef.current.set(nextSessionId, abortController);
|
|
4275
|
+
abortControllerBySessionRef.current.delete(previousSessionId);
|
|
4276
|
+
}
|
|
4277
|
+
const stopRequest = stopRequestBySessionRef.current.get(previousSessionId);
|
|
4278
|
+
if (stopRequest) {
|
|
4279
|
+
stopRequestBySessionRef.current.set(nextSessionId, stopRequest);
|
|
4280
|
+
stopRequestBySessionRef.current.delete(previousSessionId);
|
|
4281
|
+
}
|
|
4282
|
+
},
|
|
4283
|
+
[]
|
|
4284
|
+
);
|
|
4249
4285
|
const finalizeStop = useCallback4(
|
|
4250
4286
|
(sessionId) => {
|
|
4251
|
-
|
|
4252
|
-
|
|
4287
|
+
const stopRequest = stopRequestBySessionRef.current.get(sessionId);
|
|
4288
|
+
if (stopRequest) {
|
|
4289
|
+
if (stopRequest.finalized) {
|
|
4253
4290
|
return;
|
|
4254
|
-
|
|
4291
|
+
}
|
|
4292
|
+
stopRequest.finalized = true;
|
|
4255
4293
|
}
|
|
4256
4294
|
clearStopTimeout(sessionId);
|
|
4257
|
-
|
|
4258
|
-
|
|
4295
|
+
abortControllerBySessionRef.current.get(sessionId)?.abort();
|
|
4296
|
+
abortControllerBySessionRef.current.delete(sessionId);
|
|
4259
4297
|
finalizeStoppedStreamingMessage(sessionId);
|
|
4260
4298
|
clearStopRequest(sessionId);
|
|
4261
4299
|
},
|
|
@@ -4270,7 +4308,7 @@ var useChatComposer = () => {
|
|
|
4270
4308
|
model,
|
|
4271
4309
|
mode
|
|
4272
4310
|
}) => {
|
|
4273
|
-
clearStopRequest();
|
|
4311
|
+
clearStopRequest(localSessionId);
|
|
4274
4312
|
let currentSessionId = localSessionId;
|
|
4275
4313
|
clearSessionError(currentSessionId);
|
|
4276
4314
|
const assistantMessage = createAssistantStreamingMessage({
|
|
@@ -4279,10 +4317,19 @@ var useChatComposer = () => {
|
|
|
4279
4317
|
createMessageId: () => `assistant-${Date.now()}`
|
|
4280
4318
|
});
|
|
4281
4319
|
startStreamingMessage(currentSessionId, assistantMessage);
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4320
|
+
abortControllerBySessionRef.current.get(currentSessionId)?.abort();
|
|
4321
|
+
const abortController = new AbortController();
|
|
4322
|
+
abortControllerBySessionRef.current.set(currentSessionId, abortController);
|
|
4323
|
+
lastRequestBySessionRef.current.set(currentSessionId, {
|
|
4324
|
+
localSessionId,
|
|
4325
|
+
sessionId,
|
|
4326
|
+
content,
|
|
4327
|
+
attachments: attachments2,
|
|
4328
|
+
model,
|
|
4329
|
+
mode
|
|
4330
|
+
});
|
|
4285
4331
|
let accumulated = "";
|
|
4332
|
+
let streamSettled = false;
|
|
4286
4333
|
try {
|
|
4287
4334
|
await transport.startStream({
|
|
4288
4335
|
sessionId,
|
|
@@ -4290,13 +4337,15 @@ var useChatComposer = () => {
|
|
|
4290
4337
|
mode,
|
|
4291
4338
|
content,
|
|
4292
4339
|
attachments: attachments2,
|
|
4293
|
-
signal:
|
|
4340
|
+
signal: abortController.signal,
|
|
4294
4341
|
onSessionId: (nextSessionId) => {
|
|
4295
4342
|
if (!nextSessionId || nextSessionId === currentSessionId)
|
|
4296
4343
|
return;
|
|
4297
|
-
|
|
4344
|
+
const previousSessionId = currentSessionId;
|
|
4345
|
+
replaceSessionId(previousSessionId, nextSessionId);
|
|
4346
|
+
moveSessionRuntimeState(previousSessionId, nextSessionId);
|
|
4298
4347
|
currentSessionId = nextSessionId;
|
|
4299
|
-
|
|
4348
|
+
const nextRequest = {
|
|
4300
4349
|
localSessionId: nextSessionId,
|
|
4301
4350
|
sessionId: nextSessionId,
|
|
4302
4351
|
content,
|
|
@@ -4304,6 +4353,8 @@ var useChatComposer = () => {
|
|
|
4304
4353
|
model,
|
|
4305
4354
|
mode
|
|
4306
4355
|
};
|
|
4356
|
+
lastRequestBySessionRef.current.delete(previousSessionId);
|
|
4357
|
+
lastRequestBySessionRef.current.set(nextSessionId, nextRequest);
|
|
4307
4358
|
},
|
|
4308
4359
|
onUpdate: (update) => {
|
|
4309
4360
|
accumulated = resolveAccumulatedContent(accumulated, update);
|
|
@@ -4313,16 +4364,18 @@ var useChatComposer = () => {
|
|
|
4313
4364
|
});
|
|
4314
4365
|
},
|
|
4315
4366
|
onDone: () => {
|
|
4316
|
-
|
|
4367
|
+
streamSettled = true;
|
|
4368
|
+
if (stopRequestBySessionRef.current.has(currentSessionId)) {
|
|
4317
4369
|
finalizeStop(currentSessionId);
|
|
4318
4370
|
return;
|
|
4319
4371
|
}
|
|
4320
4372
|
completeStreamingMessage(currentSessionId);
|
|
4321
|
-
|
|
4373
|
+
abortControllerBySessionRef.current.delete(currentSessionId);
|
|
4322
4374
|
clearStopRequest(currentSessionId);
|
|
4323
4375
|
},
|
|
4324
4376
|
onError: (streamError) => {
|
|
4325
|
-
|
|
4377
|
+
streamSettled = true;
|
|
4378
|
+
if (stopRequestBySessionRef.current.has(currentSessionId)) {
|
|
4326
4379
|
finalizeStop(currentSessionId);
|
|
4327
4380
|
return;
|
|
4328
4381
|
}
|
|
@@ -4331,12 +4384,24 @@ var useChatComposer = () => {
|
|
|
4331
4384
|
currentSessionId,
|
|
4332
4385
|
normalizeChatErrorMessage(streamError.message, labels)
|
|
4333
4386
|
);
|
|
4334
|
-
|
|
4387
|
+
abortControllerBySessionRef.current.delete(currentSessionId);
|
|
4335
4388
|
clearStopRequest(currentSessionId);
|
|
4336
4389
|
}
|
|
4337
4390
|
});
|
|
4338
|
-
} catch {
|
|
4339
|
-
|
|
4391
|
+
} catch (streamError) {
|
|
4392
|
+
abortControllerBySessionRef.current.delete(currentSessionId);
|
|
4393
|
+
if (streamSettled || abortController.signal.aborted || !store.getState().isStreamingBySession[currentSessionId]) {
|
|
4394
|
+
return;
|
|
4395
|
+
}
|
|
4396
|
+
finalizeStoppedStreamingMessage(currentSessionId);
|
|
4397
|
+
setSessionError(
|
|
4398
|
+
currentSessionId,
|
|
4399
|
+
normalizeChatErrorMessage(
|
|
4400
|
+
streamError instanceof Error ? streamError.message : void 0,
|
|
4401
|
+
labels
|
|
4402
|
+
)
|
|
4403
|
+
);
|
|
4404
|
+
clearStopRequest(currentSessionId);
|
|
4340
4405
|
}
|
|
4341
4406
|
},
|
|
4342
4407
|
[
|
|
@@ -4345,40 +4410,48 @@ var useChatComposer = () => {
|
|
|
4345
4410
|
clearStopRequest,
|
|
4346
4411
|
finalizeStop,
|
|
4347
4412
|
labels,
|
|
4413
|
+
moveSessionRuntimeState,
|
|
4348
4414
|
startStreamingMessage,
|
|
4349
4415
|
replaceSessionId,
|
|
4350
4416
|
patchStreamingMessage,
|
|
4351
4417
|
completeStreamingMessage,
|
|
4352
4418
|
finalizeStoppedStreamingMessage,
|
|
4353
|
-
setSessionError
|
|
4419
|
+
setSessionError,
|
|
4420
|
+
store
|
|
4354
4421
|
]
|
|
4355
4422
|
);
|
|
4356
4423
|
const send = useCallback4(
|
|
4357
|
-
async (contentOverride) => {
|
|
4424
|
+
async (contentOverride, options) => {
|
|
4358
4425
|
const content = (contentOverride ?? value).trim();
|
|
4426
|
+
const includeComposerAttachments = options?.includeComposerAttachments ?? true;
|
|
4427
|
+
const composerAttachmentCount = includeComposerAttachments ? attachments.length : 0;
|
|
4359
4428
|
if (!canSendChatMessage({
|
|
4360
4429
|
value: content,
|
|
4361
|
-
attachmentCount:
|
|
4430
|
+
attachmentCount: composerAttachmentCount,
|
|
4362
4431
|
isModelsLoading,
|
|
4363
4432
|
isModelsError,
|
|
4364
4433
|
hasModels
|
|
4365
4434
|
})) {
|
|
4366
4435
|
return;
|
|
4367
4436
|
}
|
|
4368
|
-
|
|
4437
|
+
const storeState = store.getState();
|
|
4438
|
+
const currentActiveSessionId = options?.sessionId ?? storeState.activeSessionId;
|
|
4439
|
+
const currentActiveSession = storeState.sessions.find((session2) => session2.sessionId === currentActiveSessionId) ?? null;
|
|
4440
|
+
const currentMode = currentActiveSession?.mode ?? selectedMode;
|
|
4441
|
+
if (!(selectedModel || currentActiveSession?.model || availableModels[0]?.id)) {
|
|
4369
4442
|
return;
|
|
4370
4443
|
}
|
|
4371
|
-
const resolvedModel = selectedModel ||
|
|
4444
|
+
const resolvedModel = selectedModel || currentActiveSession?.model || availableModels[0]?.id || "local-image";
|
|
4372
4445
|
const { localSessionId, sessionId, session } = resolveSendSession({
|
|
4373
|
-
activeSessionId,
|
|
4446
|
+
activeSessionId: currentActiveSessionId,
|
|
4374
4447
|
selectedModel: resolvedModel,
|
|
4375
|
-
selectedMode,
|
|
4448
|
+
selectedMode: currentMode,
|
|
4376
4449
|
nowIso,
|
|
4377
4450
|
createSessionId: createDraftChatSessionId
|
|
4378
4451
|
});
|
|
4379
4452
|
if (session)
|
|
4380
4453
|
createSession(session);
|
|
4381
|
-
const messageAttachments = takeMessageAttachments();
|
|
4454
|
+
const messageAttachments = includeComposerAttachments ? takeMessageAttachments() : void 0;
|
|
4382
4455
|
const userMessage = createUserMessage({
|
|
4383
4456
|
sessionId: localSessionId,
|
|
4384
4457
|
content,
|
|
@@ -4388,15 +4461,17 @@ var useChatComposer = () => {
|
|
|
4388
4461
|
createMessageId: () => `user-${Date.now()}`
|
|
4389
4462
|
});
|
|
4390
4463
|
appendMessage(localSessionId, userMessage);
|
|
4391
|
-
|
|
4392
|
-
|
|
4464
|
+
if (includeComposerAttachments) {
|
|
4465
|
+
setAttachmentNotice(null);
|
|
4466
|
+
setValue("");
|
|
4467
|
+
}
|
|
4393
4468
|
await runStream({
|
|
4394
4469
|
localSessionId,
|
|
4395
4470
|
sessionId,
|
|
4396
4471
|
content,
|
|
4397
4472
|
attachments: messageAttachments,
|
|
4398
4473
|
model: resolvedModel,
|
|
4399
|
-
mode:
|
|
4474
|
+
mode: currentMode
|
|
4400
4475
|
});
|
|
4401
4476
|
},
|
|
4402
4477
|
[
|
|
@@ -4406,16 +4481,47 @@ var useChatComposer = () => {
|
|
|
4406
4481
|
isModelsError,
|
|
4407
4482
|
hasModels,
|
|
4408
4483
|
selectedModel,
|
|
4409
|
-
activeSession,
|
|
4410
4484
|
availableModels,
|
|
4411
|
-
activeSessionId,
|
|
4412
4485
|
selectedMode,
|
|
4413
4486
|
createSession,
|
|
4414
4487
|
takeMessageAttachments,
|
|
4415
4488
|
appendMessage,
|
|
4416
|
-
runStream
|
|
4489
|
+
runStream,
|
|
4490
|
+
store
|
|
4417
4491
|
]
|
|
4418
4492
|
);
|
|
4493
|
+
const stopSession = useCallback4(
|
|
4494
|
+
async (sessionId) => {
|
|
4495
|
+
const storeState = store.getState();
|
|
4496
|
+
const isSessionStreaming = storeState.isStreamingBySession[sessionId] ?? false;
|
|
4497
|
+
const isSessionStopping = storeState.isStoppingBySession[sessionId] ?? false;
|
|
4498
|
+
if (!isSessionStreaming || isSessionStopping) {
|
|
4499
|
+
return;
|
|
4500
|
+
}
|
|
4501
|
+
if (isDraftChatSessionId(sessionId)) {
|
|
4502
|
+
finalizeStop(sessionId);
|
|
4503
|
+
return;
|
|
4504
|
+
}
|
|
4505
|
+
requestStopStreaming(sessionId);
|
|
4506
|
+
stopRequestBySessionRef.current.set(sessionId, {
|
|
4507
|
+
timeoutId: window.setTimeout(() => {
|
|
4508
|
+
finalizeStop(sessionId);
|
|
4509
|
+
}, STOP_WAIT_TIMEOUT_MS),
|
|
4510
|
+
finalized: false
|
|
4511
|
+
});
|
|
4512
|
+
try {
|
|
4513
|
+
const result = await transport.terminateStream(sessionId);
|
|
4514
|
+
if (!result.terminated) {
|
|
4515
|
+
console.error("Failed to terminate chat session: server returned not terminated");
|
|
4516
|
+
}
|
|
4517
|
+
finalizeStop(sessionId);
|
|
4518
|
+
} catch (err) {
|
|
4519
|
+
console.error("Failed to terminate chat session", err);
|
|
4520
|
+
finalizeStop(sessionId);
|
|
4521
|
+
}
|
|
4522
|
+
},
|
|
4523
|
+
[finalizeStop, requestStopStreaming, store, transport]
|
|
4524
|
+
);
|
|
4419
4525
|
return {
|
|
4420
4526
|
state: {
|
|
4421
4527
|
value,
|
|
@@ -4455,43 +4561,27 @@ var useChatComposer = () => {
|
|
|
4455
4561
|
setPreferredMode(mode);
|
|
4456
4562
|
if (activeSessionId)
|
|
4457
4563
|
setSessionMode(activeSessionId, mode);
|
|
4458
|
-
if (
|
|
4459
|
-
|
|
4564
|
+
if (activeSessionId) {
|
|
4565
|
+
const previousRequest = lastRequestBySessionRef.current.get(activeSessionId);
|
|
4566
|
+
if (previousRequest) {
|
|
4567
|
+
lastRequestBySessionRef.current.set(activeSessionId, { ...previousRequest, mode });
|
|
4568
|
+
}
|
|
4460
4569
|
}
|
|
4461
4570
|
},
|
|
4462
4571
|
reloadModels: () => void fetchModels(),
|
|
4572
|
+
stopSession,
|
|
4463
4573
|
stop: async () => {
|
|
4464
4574
|
if (!streamingSessionId)
|
|
4465
4575
|
return;
|
|
4466
|
-
|
|
4467
|
-
return;
|
|
4468
|
-
if (isDraftChatSessionId(streamingSessionId)) {
|
|
4469
|
-
finalizeStop(streamingSessionId);
|
|
4470
|
-
return;
|
|
4471
|
-
}
|
|
4472
|
-
requestStopStreaming(streamingSessionId);
|
|
4473
|
-
stopRequestRef.current = {
|
|
4474
|
-
sessionId: streamingSessionId,
|
|
4475
|
-
timeoutId: window.setTimeout(() => {
|
|
4476
|
-
finalizeStop(streamingSessionId);
|
|
4477
|
-
}, STOP_WAIT_TIMEOUT_MS),
|
|
4478
|
-
finalized: false
|
|
4479
|
-
};
|
|
4480
|
-
try {
|
|
4481
|
-
const result = await transport.terminateStream(streamingSessionId);
|
|
4482
|
-
if (!result.terminated) {
|
|
4483
|
-
console.error("Failed to terminate chat session: server returned not terminated");
|
|
4484
|
-
}
|
|
4485
|
-
finalizeStop(streamingSessionId);
|
|
4486
|
-
} catch (err) {
|
|
4487
|
-
console.error("Failed to terminate chat session", err);
|
|
4488
|
-
finalizeStop(streamingSessionId);
|
|
4489
|
-
}
|
|
4576
|
+
await stopSession(streamingSessionId);
|
|
4490
4577
|
},
|
|
4491
|
-
retry: () => {
|
|
4492
|
-
if (!
|
|
4578
|
+
retry: (sessionId) => {
|
|
4579
|
+
if (!sessionId)
|
|
4580
|
+
return;
|
|
4581
|
+
const request = lastRequestBySessionRef.current.get(sessionId);
|
|
4582
|
+
if (!request)
|
|
4493
4583
|
return;
|
|
4494
|
-
void runStream(
|
|
4584
|
+
void runStream(request);
|
|
4495
4585
|
}
|
|
4496
4586
|
}
|
|
4497
4587
|
};
|
|
@@ -5138,7 +5228,7 @@ var ChatComposerView = ({
|
|
|
5138
5228
|
}
|
|
5139
5229
|
) : null,
|
|
5140
5230
|
/* @__PURE__ */ jsx15(
|
|
5141
|
-
|
|
5231
|
+
Input,
|
|
5142
5232
|
{
|
|
5143
5233
|
ref: inputRef,
|
|
5144
5234
|
"data-testid": "chat-composer-input",
|
|
@@ -5199,15 +5289,16 @@ var ChatComposerView = ({
|
|
|
5199
5289
|
] }) });
|
|
5200
5290
|
};
|
|
5201
5291
|
var ChatComposer = () => {
|
|
5202
|
-
const { labels, sendRef, retryRef, enableImageAttachments } = useChatContext();
|
|
5292
|
+
const { labels, sendRef, retryRef, stopRef, enableImageAttachments } = useChatContext();
|
|
5203
5293
|
const { state, actions } = useChatComposer();
|
|
5204
5294
|
const { send, retry } = actions;
|
|
5205
5295
|
useEffect7(() => {
|
|
5206
5296
|
sendRef.current = send;
|
|
5207
|
-
retryRef.current = async () => {
|
|
5208
|
-
retry();
|
|
5297
|
+
retryRef.current = async (sessionId) => {
|
|
5298
|
+
retry(sessionId);
|
|
5209
5299
|
};
|
|
5210
|
-
|
|
5300
|
+
stopRef.current = actions.stopSession;
|
|
5301
|
+
}, [actions.stopSession, retry, retryRef, send, sendRef, stopRef]);
|
|
5211
5302
|
const modeLabels = {
|
|
5212
5303
|
ask: labels.modeLabelAsk,
|
|
5213
5304
|
plan: labels.modeLabelPlan,
|
|
@@ -5282,7 +5373,7 @@ var InputArea = styled14.div`
|
|
|
5282
5373
|
grid-area: input;
|
|
5283
5374
|
position: relative;
|
|
5284
5375
|
`;
|
|
5285
|
-
var
|
|
5376
|
+
var Input = styled14.textarea`
|
|
5286
5377
|
--textarea-line-height: ${CHAT_COMPOSER_LINE_HEIGHT_PX}px;
|
|
5287
5378
|
--textarea-min-rows: ${CHAT_COMPOSER_MIN_ROWS};
|
|
5288
5379
|
--textarea-max-rows: ${CHAT_COMPOSER_MAX_ROWS};
|
|
@@ -5459,28 +5550,17 @@ var ChatConversationList = () => {
|
|
|
5459
5550
|
const { labels } = useChatContext();
|
|
5460
5551
|
const sessions = useChatStore((s) => s.sessions);
|
|
5461
5552
|
const activeSessionId = useChatStore((s) => s.activeSessionId);
|
|
5462
|
-
const
|
|
5463
|
-
const createSession = useChatStore((s) => s.createSession);
|
|
5553
|
+
const startNewChat = useChatStore((s) => s.startNewChat);
|
|
5464
5554
|
const setActiveSession = useChatStore((s) => s.setActiveSession);
|
|
5465
5555
|
const modeLabels = {
|
|
5466
5556
|
ask: labels.modeLabelAsk,
|
|
5467
5557
|
plan: labels.modeLabelPlan,
|
|
5468
5558
|
agent: labels.modeLabelAgent
|
|
5469
5559
|
};
|
|
5470
|
-
const handleCreateSession = () => {
|
|
5471
|
-
const session = createDraftChatSession({
|
|
5472
|
-
// Model is intentionally deferred: ChatComposer resolves selectedModel at send time.
|
|
5473
|
-
model: "",
|
|
5474
|
-
mode: preferredMode,
|
|
5475
|
-
nowIso: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
5476
|
-
createSessionId: createDraftChatSessionId
|
|
5477
|
-
});
|
|
5478
|
-
createSession(session);
|
|
5479
|
-
};
|
|
5480
5560
|
return /* @__PURE__ */ jsxs12(Container3, { children: [
|
|
5481
5561
|
/* @__PURE__ */ jsxs12(Toolbar, { children: [
|
|
5482
5562
|
/* @__PURE__ */ jsx17(Title3, { children: "Sessions" }),
|
|
5483
|
-
/* @__PURE__ */ jsx17(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick:
|
|
5563
|
+
/* @__PURE__ */ jsx17(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: startNewChat, children: labels.newChat })
|
|
5484
5564
|
] }),
|
|
5485
5565
|
/* @__PURE__ */ jsx17(List2, { "data-testid": "chat-session-list", children: sessions.map((session) => /* @__PURE__ */ jsx17(
|
|
5486
5566
|
ChatSessionItem,
|
|
@@ -5531,8 +5611,70 @@ var List2 = styled16.div`
|
|
|
5531
5611
|
`;
|
|
5532
5612
|
|
|
5533
5613
|
// src/components/ai-chat/index.tsx
|
|
5534
|
-
import { jsx as jsx18, jsxs as jsxs13 } from "@emotion/react/jsx-runtime";
|
|
5535
|
-
var
|
|
5614
|
+
import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs13 } from "@emotion/react/jsx-runtime";
|
|
5615
|
+
var QuickActions = ({ renderNewChatTrigger }) => {
|
|
5616
|
+
const { labels, stopRef, store } = useChatContext();
|
|
5617
|
+
const startNewChat = useChatStore((state) => state.startNewChat);
|
|
5618
|
+
const activeSessionId = useChatStore((state) => state.activeSessionId);
|
|
5619
|
+
const isActiveSessionStreaming = useChatStore(
|
|
5620
|
+
(state) => state.activeSessionId ? state.isStreamingBySession[state.activeSessionId] ?? false : false
|
|
5621
|
+
);
|
|
5622
|
+
const isActiveSessionStopping = useChatStore(
|
|
5623
|
+
(state) => state.activeSessionId ? state.isStoppingBySession[state.activeSessionId] ?? false : false
|
|
5624
|
+
);
|
|
5625
|
+
const createNewSession = () => {
|
|
5626
|
+
startNewChat();
|
|
5627
|
+
};
|
|
5628
|
+
const stopActiveSession = async () => {
|
|
5629
|
+
const currentState = store.getState();
|
|
5630
|
+
const currentSessionId = currentState.activeSessionId;
|
|
5631
|
+
const isCurrentSessionStreaming = currentSessionId ? currentState.isStreamingBySession[currentSessionId] ?? false : false;
|
|
5632
|
+
if (!currentSessionId || !isCurrentSessionStreaming) {
|
|
5633
|
+
return;
|
|
5634
|
+
}
|
|
5635
|
+
await stopRef.current(currentSessionId);
|
|
5636
|
+
};
|
|
5637
|
+
const handleStartNewChat = async () => {
|
|
5638
|
+
const currentState = store.getState();
|
|
5639
|
+
const currentSessionId = currentState.activeSessionId;
|
|
5640
|
+
const isCurrentSessionStreaming = currentSessionId ? currentState.isStreamingBySession[currentSessionId] ?? false : false;
|
|
5641
|
+
if (currentSessionId && isCurrentSessionStreaming) {
|
|
5642
|
+
void stopRef.current(currentSessionId);
|
|
5643
|
+
}
|
|
5644
|
+
createNewSession();
|
|
5645
|
+
};
|
|
5646
|
+
const triggerProps = {
|
|
5647
|
+
activeSessionId,
|
|
5648
|
+
isStreaming: isActiveSessionStreaming,
|
|
5649
|
+
isStopping: isActiveSessionStopping,
|
|
5650
|
+
createNewSession,
|
|
5651
|
+
stopActiveSession,
|
|
5652
|
+
startNewChat: handleStartNewChat
|
|
5653
|
+
};
|
|
5654
|
+
if (renderNewChatTrigger) {
|
|
5655
|
+
return /* @__PURE__ */ jsx18(QuickActionsRow, { children: /* @__PURE__ */ jsx18(NewChatTriggerRenderer, { renderNewChatTrigger, triggerProps }) });
|
|
5656
|
+
}
|
|
5657
|
+
return /* @__PURE__ */ jsx18(QuickActionsRow, { children: /* @__PURE__ */ jsx18(
|
|
5658
|
+
QuickActionButton,
|
|
5659
|
+
{
|
|
5660
|
+
type: "button",
|
|
5661
|
+
"data-testid": "chat-start-new-session",
|
|
5662
|
+
onClick: () => void handleStartNewChat(),
|
|
5663
|
+
disabled: isActiveSessionStopping,
|
|
5664
|
+
children: labels.newChat
|
|
5665
|
+
}
|
|
5666
|
+
) });
|
|
5667
|
+
};
|
|
5668
|
+
var NewChatTriggerRenderer = ({
|
|
5669
|
+
renderNewChatTrigger,
|
|
5670
|
+
triggerProps
|
|
5671
|
+
}) => /* @__PURE__ */ jsx18(Fragment5, { children: renderNewChatTrigger(triggerProps) });
|
|
5672
|
+
var AiChat = ({
|
|
5673
|
+
showConversationList = false,
|
|
5674
|
+
showNewChatButton = false,
|
|
5675
|
+
renderNewChatTrigger,
|
|
5676
|
+
...providerProps
|
|
5677
|
+
}) => /* @__PURE__ */ jsx18(
|
|
5536
5678
|
ConfigProvider,
|
|
5537
5679
|
{
|
|
5538
5680
|
theme: {
|
|
@@ -5570,6 +5712,7 @@ var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE_
|
|
|
5570
5712
|
children: /* @__PURE__ */ jsx18(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ jsxs13(Root, { "data-testid": "ai-chat", children: [
|
|
5571
5713
|
showConversationList ? /* @__PURE__ */ jsx18(ChatConversationList, {}) : null,
|
|
5572
5714
|
/* @__PURE__ */ jsxs13(Workspace, { children: [
|
|
5715
|
+
showNewChatButton && !showConversationList ? /* @__PURE__ */ jsx18(QuickActions, { renderNewChatTrigger }) : null,
|
|
5573
5716
|
/* @__PURE__ */ jsx18(ChatThread, {}),
|
|
5574
5717
|
/* @__PURE__ */ jsx18(ChatComposer, {})
|
|
5575
5718
|
] })
|
|
@@ -5591,6 +5734,24 @@ var Workspace = styled17.section`
|
|
|
5591
5734
|
min-height: 0;
|
|
5592
5735
|
overflow: hidden;
|
|
5593
5736
|
`;
|
|
5737
|
+
var QuickActionsRow = styled17.div`
|
|
5738
|
+
display: flex;
|
|
5739
|
+
justify-content: flex-end;
|
|
5740
|
+
padding: 12px 12px 0;
|
|
5741
|
+
`;
|
|
5742
|
+
var QuickActionButton = styled17.button`
|
|
5743
|
+
border: none;
|
|
5744
|
+
border-radius: 12px;
|
|
5745
|
+
padding: 10px 14px;
|
|
5746
|
+
background: rgba(255, 255, 255, 0.08);
|
|
5747
|
+
color: var(--text-primary, #fcfbf8);
|
|
5748
|
+
cursor: pointer;
|
|
5749
|
+
|
|
5750
|
+
&:disabled {
|
|
5751
|
+
opacity: 0.5;
|
|
5752
|
+
cursor: not-allowed;
|
|
5753
|
+
}
|
|
5754
|
+
`;
|
|
5594
5755
|
export {
|
|
5595
5756
|
AiChat,
|
|
5596
5757
|
AiChatProvider,
|