Update server/wsHandler.js
Browse files- server/wsHandler.js +21 -2
server/wsHandler.js
CHANGED
|
@@ -26,6 +26,8 @@ function bcast(wsClients, userId, data, excludeWs) {
|
|
| 26 |
}
|
| 27 |
|
| 28 |
const handlers = {
|
|
|
|
|
|
|
| 29 |
'auth:login': async (ws, msg, client, wsClients) => {
|
| 30 |
const { accessToken, tempId: clientTempId } = msg;
|
| 31 |
if (!accessToken) return safeSend(ws, { type: 'auth:error', message: 'Missing token' });
|
|
@@ -153,7 +155,14 @@ const handlers = {
|
|
| 153 |
activeStreams.delete(ws);
|
| 154 |
const finalText = text || fullText;
|
| 155 |
const userEntry = buildEntry('user', content);
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
// Assets (images/video/audio) are sent to the client via chat:asset events and
|
| 158 |
// must NOT be pushed into LLM history — invalid roles cause blank responses.
|
| 159 |
const newHistory = [...(session.history || []), userEntry, asstEntry];
|
|
@@ -238,9 +247,19 @@ const handlers = {
|
|
| 238 |
function ser(s) { return { id: s.id, name: s.name, created: s.created, history: s.history || [], model: s.model }; }
|
| 239 |
|
| 240 |
function buildEntry(role, content, toolCalls = []) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
return { role, content, timestamp: Date.now(),
|
| 242 |
versions: [{ content, tail: [], timestamp: Date.now() }], currentVersionIdx: 0,
|
| 243 |
-
...(
|
| 244 |
}
|
| 245 |
|
| 246 |
async function autoName(prompt, accessToken) {
|
|
|
|
| 26 |
}
|
| 27 |
|
| 28 |
const handlers = {
|
| 29 |
+
'ping': (ws) => { safeSend(ws, { type: 'pong' }); },
|
| 30 |
+
|
| 31 |
'auth:login': async (ws, msg, client, wsClients) => {
|
| 32 |
const { accessToken, tempId: clientTempId } = msg;
|
| 33 |
if (!accessToken) return safeSend(ws, { type: 'auth:error', message: 'Missing token' });
|
|
|
|
| 155 |
activeStreams.delete(ws);
|
| 156 |
const finalText = text || fullText;
|
| 157 |
const userEntry = buildEntry('user', content);
|
| 158 |
+
// Merge: toolCalls from streamChat (OpenAI format) merged with
|
| 159 |
+
// resolved state/result info from toolCallsCollected (flat format)
|
| 160 |
+
const resolvedMap = new Map(toolCallsCollected.map(c => [c.id, c]));
|
| 161 |
+
const mergedCalls = (toolCalls || []).map(c => {
|
| 162 |
+
const resolved = resolvedMap.get(c.id) || {};
|
| 163 |
+
return { ...c, state: resolved.state || 'resolved', result: resolved.result };
|
| 164 |
+
});
|
| 165 |
+
const asstEntry = buildEntry('assistant', finalText, mergedCalls);
|
| 166 |
// Assets (images/video/audio) are sent to the client via chat:asset events and
|
| 167 |
// must NOT be pushed into LLM history — invalid roles cause blank responses.
|
| 168 |
const newHistory = [...(session.history || []), userEntry, asstEntry];
|
|
|
|
| 247 |
function ser(s) { return { id: s.id, name: s.name, created: s.created, history: s.history || [], model: s.model }; }
|
| 248 |
|
| 249 |
function buildEntry(role, content, toolCalls = []) {
|
| 250 |
+
// Normalize tool calls so the UI can always read call.name directly,
|
| 251 |
+
// regardless of whether they arrived in OpenAI format {function:{name,arguments}}
|
| 252 |
+
// or already-flat format {name, args}.
|
| 253 |
+
const normalizedCalls = toolCalls.map(c => ({
|
| 254 |
+
id: c.id,
|
| 255 |
+
name: c.name || c.function?.name,
|
| 256 |
+
args: c.args ?? (c.function?.arguments ? (() => { try { return JSON.parse(c.function.arguments); } catch { return c.function.arguments; } })() : {}),
|
| 257 |
+
state: c.state || 'resolved',
|
| 258 |
+
result: c.result,
|
| 259 |
+
}));
|
| 260 |
return { role, content, timestamp: Date.now(),
|
| 261 |
versions: [{ content, tail: [], timestamp: Date.now() }], currentVersionIdx: 0,
|
| 262 |
+
...(normalizedCalls.length ? { toolCalls: normalizedCalls } : {}) };
|
| 263 |
}
|
| 264 |
|
| 265 |
async function autoName(prompt, accessToken) {
|