bunnyquery 1.4.2 → 1.4.4
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/README.md +74 -4
- package/bunnyquery.js +85 -13
- package/dist/engine.cjs +33 -9
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.mjs +33 -9
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/session.ts +58 -15
package/package.json
CHANGED
package/src/engine/session.ts
CHANGED
|
@@ -138,19 +138,49 @@ export class ChatSession {
|
|
|
138
138
|
})
|
|
139
139
|
.catch(function (err: any) { return { content: getErrorMessage(err), isError: true }; })
|
|
140
140
|
.then(function (result: any) {
|
|
141
|
-
//
|
|
142
|
-
// independently of the view lifecycle, so a reply
|
|
143
|
-
//
|
|
144
|
-
// the cache via typewriteLatestReply (and resumePendingRequest on
|
|
145
|
-
// remount). The displayed bubble in state.messages is updated by the
|
|
146
|
-
// caller's typewriteLatestReply, not here.
|
|
147
|
-
var existing = self.aiChatHistoryCache[params.key] || { messages: [], endOfList: false, startKeyHistory: [] };
|
|
148
|
-
self.aiChatHistoryCache[params.key] = {
|
|
149
|
-
messages: existing.messages.concat([{ role: 'assistant', content: result.content, isError: result.isError }]),
|
|
150
|
-
endOfList: existing.endOfList,
|
|
151
|
-
startKeyHistory: existing.startKeyHistory,
|
|
152
|
-
};
|
|
141
|
+
// Land the resolved reply in the shared history cache. This runs
|
|
142
|
+
// independently of the view lifecycle, so a reply is captured even
|
|
143
|
+
// if the chatbox unmounted mid-request.
|
|
153
144
|
delete self.pendingAgentRequests[params.key];
|
|
145
|
+
var existing = self.aiChatHistoryCache[params.key] || { messages: [], endOfList: false, startKeyHistory: [] };
|
|
146
|
+
var reply: ChatMessage = { role: 'assistant', content: result.content, isError: result.isError };
|
|
147
|
+
|
|
148
|
+
var onThisVisibleChat = self.host.isViewMounted() && self.getHistoryCacheKey() === params.key;
|
|
149
|
+
if (onThisVisibleChat) {
|
|
150
|
+
// The chatbox is showing THIS chat: its typewriteLatestReply will
|
|
151
|
+
// swap the "Thinking..." bubble in state.messages and re-snapshot
|
|
152
|
+
// the cache. Just append the reply for it to pick up.
|
|
153
|
+
self.aiChatHistoryCache[params.key] = {
|
|
154
|
+
messages: existing.messages.concat([reply]),
|
|
155
|
+
endOfList: existing.endOfList,
|
|
156
|
+
startKeyHistory: existing.startKeyHistory,
|
|
157
|
+
};
|
|
158
|
+
} else {
|
|
159
|
+
// Resolved while this chat is NOT the visible view (the chatbox
|
|
160
|
+
// unmounted, or the user moved to another project). No view
|
|
161
|
+
// typewriter will run, so REPLACE the trailing pending
|
|
162
|
+
// "Thinking..." bubble in the cache with the answer rather than
|
|
163
|
+
// appending a duplicate — otherwise a remount renders a stuck
|
|
164
|
+
// "Thinking..." (whose bubble was never resolved). The next
|
|
165
|
+
// fresh history fetch reconciles it either way.
|
|
166
|
+
var msgs = existing.messages.slice();
|
|
167
|
+
var idx = -1;
|
|
168
|
+
for (var i = msgs.length - 1; i >= 0; i--) {
|
|
169
|
+
var m = msgs[i];
|
|
170
|
+
if (m && m.isPending && m.role === 'assistant' && !m.isBackgroundTask) { idx = i; break; }
|
|
171
|
+
}
|
|
172
|
+
if (idx !== -1) {
|
|
173
|
+
reply._serverItemId = msgs[idx]._serverItemId;
|
|
174
|
+
msgs[idx] = reply;
|
|
175
|
+
} else {
|
|
176
|
+
msgs.push(reply);
|
|
177
|
+
}
|
|
178
|
+
self.aiChatHistoryCache[params.key] = {
|
|
179
|
+
messages: msgs,
|
|
180
|
+
endOfList: existing.endOfList,
|
|
181
|
+
startKeyHistory: existing.startKeyHistory,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
154
184
|
return result;
|
|
155
185
|
});
|
|
156
186
|
this.pendingAgentRequests[params.key] = run;
|
|
@@ -238,14 +268,23 @@ export class ChatSession {
|
|
|
238
268
|
platform: aiPlatform, model: aiModel, systemPrompt: systemPrompt, serviceId: id.serviceId,
|
|
239
269
|
history: historyForLlm,
|
|
240
270
|
});
|
|
241
|
-
var requestToken = this.state.gateRefreshToken;
|
|
242
271
|
var run = this.dispatchAgentRequest({
|
|
243
272
|
key: key, serviceId: id.serviceId, owner: id.owner, aiPlatform: aiPlatform, aiModel: aiModel,
|
|
244
273
|
systemPrompt: systemPrompt, text: composed, boundedMessages: bounded.messages, userId: chatQueue,
|
|
245
274
|
extractContent: extractContent,
|
|
246
275
|
});
|
|
276
|
+
// Render the reply into the "Thinking..." bubble whenever the chatbox is
|
|
277
|
+
// CURRENTLY showing this chat — even after an unmount/remount. The old
|
|
278
|
+
// guard bailed on `requestToken !== gateRefreshToken`, but EVERY remount
|
|
279
|
+
// bumps gateRefreshToken (refreshGate), so a request that finished after
|
|
280
|
+
// the user navigated away and back left a stuck "Thinking..." (no view
|
|
281
|
+
// typewriter ever ran, and resumePendingRequest bails when a pending
|
|
282
|
+
// bubble is already present). Gate on "is this the visible chat" instead.
|
|
283
|
+
// When it ISN'T (unmounted / another project), dispatchAgentRequest has
|
|
284
|
+
// already replaced the pending bubble with the answer IN THE CACHE, so a
|
|
285
|
+
// later loadChatHistory renders it.
|
|
247
286
|
Promise.resolve(run).catch(function () { }).then(function () {
|
|
248
|
-
if (
|
|
287
|
+
if (!(self.host.isViewMounted() && self.getHistoryCacheKey() === key)) return;
|
|
249
288
|
self.state.sending = false;
|
|
250
289
|
return Promise.resolve(self.typewriteLatestReply(key)).then(function () { self.host.scrollToBottom(true); });
|
|
251
290
|
});
|
|
@@ -789,7 +828,11 @@ export class ChatSession {
|
|
|
789
828
|
if (item.status !== 'running' && item.status !== 'pending') return;
|
|
790
829
|
if (!item.poll || !item.id) return;
|
|
791
830
|
if (self.historyItemPolls.has(item.id)) return;
|
|
792
|
-
|
|
831
|
+
// running OR pending: a live dispatchAgentRequest already polls this
|
|
832
|
+
// item; attaching a second (history) poll would double the interval
|
|
833
|
+
// fetch on a remount (skapi item.poll() loops survive unmount —
|
|
834
|
+
// they're uncancellable, so the dispatch poll is still running).
|
|
835
|
+
if ((item.status === 'running' || item.status === 'pending') && self.pendingAgentRequests[self.getHistoryCacheKey()]) return;
|
|
793
836
|
self.historyItemPolls.set(item.id, true);
|
|
794
837
|
var capturedId = item.id;
|
|
795
838
|
var pp = item.poll({
|