REFERENCE
SDK 範例
@asgard-js/core 與 @asgard-js/react 的用法與可以直接操作的範例。
停止生成
run 是在伺服器端獨立執行的。這代表停止生成不是「切斷本地連線」——早期只呼叫
AbortController.abort() 的做法只是停止觀看,agent 其實還在跑、還在燒 token、還在寫對話紀錄,
而 UI 卻宣稱已經停了。
現在停止是送一個請求要後端暫停那個 run,而且非同步完成。
兩個時刻
stopGeneration()resolve 只代表請求被受理;run 真正停下來,是 SSE 串流走到終止事件的那一刻—— 和一個正常結束的 run 走的是同一個事件。這兩個時刻之間,channel 處於isStopping。await stopGeneration(); // ← 受理(不是「已停止」)
// … isStopping === true …
// ← 串流上的終止事件抵達,此時才回到可送出狀態用預設 footer 就有了
內建 footer 已經處理好整段流程,不需要接任何線。下面的規則只有在你用
renderFooter換掉 footer、或自己做送出入口時才需要在意。自訂 footer 的兩條規則
1. 每個送出入口都要同時擋
isStopping不是只擋
isConnecting。舊的 run 還沒真的結束,這時送出會讓兩個 run 同時寫進同一份對話紀錄。 使用者的草稿要保留,不要清掉。2. 只有
canStop為真時才顯示停止控制isConnecting一詞四義——使用者自己的回合、RESET_CHANNEL歡迎訊息、transcript 重新加入、 隱形的 nudge——其中只有第一種可以停。canStop精確地只表示那一種。function CustomFooter() {
const {
sendMessage, isConnecting, isStopping,
canStop, canForceStop, stopGeneration,
} = useAsgardContext();
const [value, setValue] = useState("");
const canSend = !isConnecting && !isStopping && value.trim().length > 0;
const onStop = (): void => {
// DOM handler 不能 reject。失敗時 SDK 會把狀態回捲成 idle,
// 控制項只是變回可按——想自己呈現錯誤就改成 await。
void stopGeneration?.({ force: canForceStop }).catch(() => undefined);
};
if (canStop || isStopping) {
return (
<button onClick={onStop} disabled={isStopping && !canForceStop}>
{canForceStop ? "強制停止" : isStopping ? "停止中…" : "停止"}
</button>
);
}
return (
<button onClick={() => sendMessage?.({ text: value })} disabled={!canSend}>
送出
</button>
);
}狀態旗標
旗標 意義 isConnectingchannel 正忙——四種原因之一,不等於「可停止」 canStop目前是使用者發起的 run,可以停 isStopping停止已受理,終止事件尚未抵達 canForceStop受理後約 10 秒仍未收到終止事件,可升級為強制停止(隱含 isStopping)canForceStop是給沒有回應的 agent 的退路,正常情況不該走到。再按一次會以force: true重呼端點,告訴後端放棄這個 run。ChannelBusyError防線除了 UI 擋,core 的
sendMessage()也會在 run 進行中直接 reject:import { ChannelBusyError, isChannelBusyError } from "@asgard-js/core";
try {
await sendMessage({ text });
} catch (err) {
if (isChannelBusyError(err)) {
// 這則訊息沒有被送出,也沒有在對話裡留下任何痕跡
}
}它在推出樂觀的 user 泡泡之前就拒絕,所以被擋下的訊息不會在對話串裡留下殘影。
對話會活過一次停止
紀錄保留、被中止的回合由後端回捲,下一則訊息延續同一段對話——不需要重建 channel。
也看看
- 自訂 Footer — 換掉整個 footer 時的注意事項
- 事件處理 — 連線與訊息事件
- Headless 模式 — 直接操作
Channel