chore(chat): refactor chat init into separate quota and history actors
Split `chatInitActor` into `loadQuotaActor` (guest quota fetch) and `loadHistoryActor` (local → network → save sync), and add `quotaLoaded` / `historyLoaded` state flags so the UI can display loading status during the new local-first history hydration flow. Also drop the now-unused `ChatInit` event and pipe Next.js stdout/stderr to a persistent log file in the post-receive hook for easier troubleshooting.
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
* Chat 状态机:4 个 XState actor
|
||||
*
|
||||
* 从 `chat-machine.ts` 抽出的"Actors"段:
|
||||
* - `chatInitActor`(fromPromise,**仅**游客有意义)
|
||||
* - `loadQuotaActor`(fromPromise,**仅**游客有意义 —— 拉**日**配 + 总**配**)
|
||||
* - `loadHistoryActor`(fromPromise —— local → network → save network to local 3 **步**)
|
||||
* - `sendMessageHttpActor`(fromPromise,**已** wire —— "一次发送 = 一次返回")
|
||||
* - `loadMoreHistoryActor`(fromPromise)
|
||||
* - `loadMoreHistoryActor`(fromPromise —— 翻**页**,**不**走 local-first)
|
||||
* - `chatWebSocketActor`(fromCallback,长生命周期 —— **仅** userSession 期间 invoke)
|
||||
*
|
||||
* 设计:
|
||||
@@ -23,17 +24,62 @@ import {
|
||||
PAGE_SIZE,
|
||||
chatRepo,
|
||||
localMessagesToUi,
|
||||
readInitData,
|
||||
readAndSyncHistory,
|
||||
readGuestQuota,
|
||||
sendResponseToUiMessage,
|
||||
type InitResult,
|
||||
} from "./chat-machine.helpers";
|
||||
import type { ChatEvent } from "./chat-events";
|
||||
|
||||
// ============================================================// HTTP: one-shot Promise
|
||||
// ============================================================
|
||||
/** 拉初始数据(**仅**游客有意义) */
|
||||
export const chatInitActor = fromPromise<InitResult>(async () => readInitData());
|
||||
// Init 任务 1:拉游客配额(fromPromise,**纯** local)
|
||||
// ============================================================
|
||||
/**
|
||||
* 拉游客**日**配 + 总**配**(**仅** guestSession 期间 invoke)
|
||||
* - 失**败**返 `{ remaining: 0, total: 0 }`(**不**抛,**让** UI **还**是**能**进** ready)
|
||||
* - onDone 在 state machine **里**赋 guestRemainingQuota + guestTotalQuota + 设 quotaLoaded: true
|
||||
*/
|
||||
export const loadQuotaActor = fromPromise<{
|
||||
remaining: number;
|
||||
total: number;
|
||||
}>(async () => {
|
||||
console.log("[chat-machine] loadQuotaActor ENTRY");
|
||||
const result = await readGuestQuota();
|
||||
console.log("[chat-machine] loadQuotaActor DONE", result);
|
||||
return result;
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Init 任务 2:拉 history 3 步流(fromPromise,**返**回**最**终**结**果**)
|
||||
// ============================================================
|
||||
/**
|
||||
* local → network → save network to local 3 步流(**返**回** network **最**终** messages**)
|
||||
* - 选**方**案 A(fromPromise):UI **不**会**看**到** local 中**间**态**,**直**接**显**示** network **结**果**
|
||||
* - 内部 3 步**走** helper `readAndSyncHistory`(**日**志**全**在** helper **里**)
|
||||
* - 失**败**返 local(**退**而**求**其**次**)
|
||||
*/
|
||||
export const loadHistoryActor = fromPromise<{
|
||||
messages: UiMessage[];
|
||||
hasMore: boolean;
|
||||
newOffset: number;
|
||||
localOverwritten: boolean;
|
||||
localCount: number;
|
||||
networkCount: number;
|
||||
}>(async () => {
|
||||
console.log("[chat-machine] loadHistoryActor ENTRY");
|
||||
const result = await readAndSyncHistory();
|
||||
console.log("[chat-machine] loadHistoryActor DONE", {
|
||||
finalCount: result.messages.length,
|
||||
localCount: result.localCount,
|
||||
networkCount: result.networkCount,
|
||||
hasMore: result.hasMore,
|
||||
localOverwritten: result.localOverwritten,
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// HTTP: one-shot Promise
|
||||
// ============================================================
|
||||
/**
|
||||
* HTTP 发送消息(**一次发送 = 一次返回**)
|
||||
* - 后端响应**就**是 AI 回复(`ChatSendResponse.reply: string`)
|
||||
@@ -76,7 +122,7 @@ export const sendMessageHttpActor = fromPromise<
|
||||
return { reply };
|
||||
});
|
||||
|
||||
/** 翻历史(pagination) */
|
||||
/** 翻历史(pagination)—— **不**走 local-first,**纯** server fetch */
|
||||
export const loadMoreHistoryActor = fromPromise<
|
||||
{ messages: UiMessage[]; hasMore: boolean; newOffset: number },
|
||||
{ offset: number }
|
||||
@@ -123,7 +169,8 @@ export const loadMoreHistoryActor = fromPromise<
|
||||
};
|
||||
});
|
||||
|
||||
// ============================================================// WebSocket: long-lived callback
|
||||
// ============================================================
|
||||
// WebSocket: long-lived callback
|
||||
// ============================================================
|
||||
/**
|
||||
* 长生命周期 WebSocket(**仅** `userSession` 期间 invoke)
|
||||
|
||||
Reference in New Issue
Block a user