feat: release chat and mindspace UI updates
This commit is contained in:
+55
-5
@@ -266,7 +266,7 @@ export async function buildVisionPayload({
|
||||
};
|
||||
}
|
||||
|
||||
export function createTkmindProxy({ apiTarget, apiTargets, apiSecret, userAuth, llmProviderService, localFetchAsset, subscriptionService }) {
|
||||
export function createTkmindProxy({ apiTarget, apiTargets, apiSecret, userAuth, llmProviderService, localFetchAsset, subscriptionService, sessionSnapshotService }) {
|
||||
const targets = apiTargets?.length ? apiTargets : apiTarget ? [apiTarget] : [];
|
||||
const primaryTarget = targets[0] ?? apiTarget ?? '';
|
||||
let rrIdx = 0;
|
||||
@@ -296,6 +296,45 @@ export function createTkmindProxy({ apiTarget, apiTargets, apiSecret, userAuth,
|
||||
}
|
||||
}
|
||||
|
||||
function isGeneratedSessionName(name) {
|
||||
const normalized = typeof name === 'string' ? name.trim() : '';
|
||||
return /^20\d{6}(?:[_-]\d+)?$/.test(normalized);
|
||||
}
|
||||
|
||||
// A session "name" that carries no real topic: empty, or Goose's default
|
||||
// placeholder before its describe step has run.
|
||||
function hasDefaultSessionName(session) {
|
||||
if (session?.user_set_name) return false;
|
||||
const name = typeof session?.name === 'string' ? session.name.trim() : '';
|
||||
return name === '' || name === 'New Chat' || name === '新对话' || isGeneratedSessionName(name);
|
||||
}
|
||||
|
||||
// For sessions still carrying a default name, fill in a title derived from the
|
||||
// first user message (kept in the snapshot cache) so the history list shows a
|
||||
// meaningful label instead of "会话 <id>". Best-effort: never blocks the list.
|
||||
async function enrichSessionHistory(sessions) {
|
||||
if (!sessionSnapshotService?.isEnabled?.()) return;
|
||||
const needsSummary = sessions.filter(
|
||||
(session) => hasDefaultSessionName(session) || Number(session?.message_count ?? 0) === 0,
|
||||
);
|
||||
if (needsSummary.length === 0) return;
|
||||
try {
|
||||
const summaries = await sessionSnapshotService.getHistorySummaries(needsSummary.map((s) => s.id));
|
||||
for (const session of needsSummary) {
|
||||
const summary = summaries.get(session.id);
|
||||
if (!summary) continue;
|
||||
if (summary.title && hasDefaultSessionName(session)) {
|
||||
session.name = summary.title;
|
||||
}
|
||||
if (Number(session?.message_count ?? 0) === 0 && Number(summary.messageCount ?? 0) > 0) {
|
||||
session.message_count = Number(summary.messageCount);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Non-fatal: fall back to the client-side label.
|
||||
}
|
||||
}
|
||||
|
||||
async function pickTarget() {
|
||||
if (targets.length <= 1) return primaryTarget;
|
||||
for (let i = 0; i < targets.length; i += 1) {
|
||||
@@ -632,7 +671,10 @@ export function createTkmindProxy({ apiTarget, apiTargets, apiSecret, userAuth,
|
||||
if (healthyTargets < targets.length) {
|
||||
res.setHeader('X-TKMind-Degraded', '1');
|
||||
}
|
||||
res.json({ sessions: [...sessionsById.values()] });
|
||||
|
||||
const sessions = [...sessionsById.values()];
|
||||
await enrichSessionHistory(sessions);
|
||||
res.json({ sessions });
|
||||
} catch (err) {
|
||||
res.status(500).json({
|
||||
message: sanitizeUserFacingProxyMessage(
|
||||
@@ -725,6 +767,13 @@ export function createTkmindProxy({ apiTarget, apiTargets, apiSecret, userAuth,
|
||||
});
|
||||
|
||||
const source = Readable.fromWeb(upstream.body);
|
||||
|
||||
// 每 20s 发一个注释行保持连接,防止 nginx/代理因静默超时切断 SSE
|
||||
const keepalive = setInterval(() => {
|
||||
if (!res.writableEnded) res.write(': keepalive\n\n');
|
||||
}, 20000);
|
||||
const stopKeepalive = () => clearInterval(keepalive);
|
||||
|
||||
billingTransform.on('data', (chunk) => {
|
||||
res.write(chunk);
|
||||
if (pendingBalance != null) {
|
||||
@@ -732,9 +781,10 @@ export function createTkmindProxy({ apiTarget, apiTargets, apiSecret, userAuth,
|
||||
pendingBalance = null;
|
||||
}
|
||||
});
|
||||
billingTransform.on('end', () => res.end());
|
||||
billingTransform.on('error', () => res.end());
|
||||
source.on('error', () => res.end());
|
||||
billingTransform.on('end', () => { stopKeepalive(); res.end(); });
|
||||
billingTransform.on('error', () => { stopKeepalive(); res.end(); });
|
||||
source.on('error', () => { stopKeepalive(); res.end(); });
|
||||
req.on('close', stopKeepalive);
|
||||
source.pipe(billingTransform);
|
||||
} catch (err) {
|
||||
res.status(502).json({
|
||||
|
||||
Reference in New Issue
Block a user