跳到內容

串流回應

Pedelec 將 assistant output 分成兩個 callback:session.onChat() 接收完成的 logical assistant message;session.onChatDelta() 則在 Provider 提供真實增量串流時接收 best-effort incremental text。

不要把 delta 當成權威的完成回應。Provider 可能只提供完成訊息,也可能對同一份 logical output 同時提供 deltas 與 completed message。

const offChat = session.onChat((text, ctx) => {
// text 是一個完整的 logical assistant message
// ctx.type === "chat_message"
});
const offChatDelta = session.onChatDelta((delta, ctx) => {
// delta 是一個 incremental fragment
// ctx.type === "chat_delta"
});
type ChatEventContext = PedelecEventContext & {
type: "chat_message";
turnId: string;
turnStartedAt: number;
eventReceivedAt: number;
};
type ChatDeltaEventContext = PedelecEventContext & {
type: "chat_delta";
turnId: string;
turnStartedAt: number;
eventReceivedAt: number;
};

Delta 可能只有幾個字元、標點、一個詞或更大的 chunk。Chunk boundary 沒有語意。Pedelec 會保留 Provider 傳來的 delta,不會因為後續 completed message 多了文字,就自行補出缺少的 suffix delta。

如果 UI 要在 Provider 還在生成時就更新畫面,使用 onChatDelta()

let assistantText = "";
const offChatDelta = session.onChatDelta((delta) => {
assistantText += delta;
renderAssistantMessage(assistantText);
});
await session.sendText("Explain this screen.");
offChatDelta();

不要每個 delta 都建立一個 message bubble。

// 錯誤:會產生大量極短訊息。
session.onChatDelta((delta) => {
messages.push({ role: "assistant", text: delta });
});

每個 turn 建立一個 live assistant message,再 append delta。

如果應用程式需要 Provider 的完成語意輸出,直接訂閱 onChat()

session.onChat((text) => {
saveCompletedAssistantMessage(text);
});

Delta delivery 是 best-effort,而且依 Provider 能力而定。不要把 completed message 與已累積 delta 的差異視為「漏掉、應該補發」的 delta。

type AssistantMessage = {
id: string;
sessionId: string;
turnId: string;
text: string;
};
const messages = new Map<string, AssistantMessage>();
session.onChatDelta((delta, ctx) => {
const key = `${ctx.sessionId}:${ctx.turnId}`;
const current = messages.get(key) ?? {
id: key,
sessionId: ctx.sessionId,
turnId: ctx.turnId,
text: "",
};
current.text += delta;
messages.set(key, current);
renderMessages([...messages.values()]);
});

turnId 由 SDK 為一次已接受的 sendText()prepare() lifecycle 產生;chat delta 只會顯示 user turn。請當 opaque ID:

  • 不要解析其中 timestamp;
  • 不要依賴 prefix;
  • 不要當成 provider resume ID;
  • 不要期待 page reload 後保留。

即使 handler 已附著在特定 session,每個 callback 仍包含 ctx.sessionId,方便 generic store 與 logging。

session.onChatDelta((delta, ctx) => {
transcriptStore.append(ctx.sessionId, ctx.turnId, delta);
});
session.onChat((text, ctx) => {
transcriptStore.commit(ctx.sessionId, ctx.turnId, text);
});

UI 切換 active session 時,仍要正確記錄非目前可見 session 的 delta,不要誤加到 selected transcript。

  • sessionCreatedAt:browser-side session object 建立時間。
  • turnStartedAt:SDK 接受 local turn 的時間。
  • eventReceivedAt:SDK 從 Extension 收到 Core event 的時間。
  • eventEmittedAt:SDK 建立 callback context 並送出 handler 的時間。

可用於 diagnostics 與 UI metrics,但只是 browser clock,不是 distributed tracing guarantee。

Delta 很頻繁時可能造成過多 framework render。先確保 correctness,再視需要 batch:

let buffer = "";
let scheduled = false;
session.onChatDelta((delta) => {
buffer += delta;
if (scheduled) return;
scheduled = true;
requestAnimationFrame(() => {
scheduled = false;
renderAssistantMessage(buffer);
});
});

如果 Provider 提供 completed message,onChat() 是校正最終 rendered text 的權威來源。sendText() completion 由相符的內部 operation completion 驅動,不由任一 chat callback 或單獨的 idle status 決定。

每個 registration 都回傳 cleanup function:

const offChat = session.onChat(handleCompletedMessage);
const offChatDelta = session.onChatDelta(handleDelta);
onComponentCleanup(() => {
offChat();
offChatDelta();
});

Unsubscribe 只停止該 handler 的 callback,不會停止 provider turn 或 end session。

Component 重複 mount 卻不 cleanup,會造成 duplicate append 與 memory growth。

onChat() 是 completed-message callback,但不會決定 sendText() 是否 resolve。Turn completion 依相符的 operation ID;status 與 error event 是可觀察狀態/diagnostic,operation_completed 才是語意 terminal。

失敗 turn 仍可能已輸出 partial deltas。產品需決定保留、標記 interrupted 或丟棄這份 live partial response。SDK contract 不會從 deltas 合成 completed message。

Assistant messages 與 deltas 都是 provider 產生的 plain text。若將內容 render 成 Markdown 或 HTML,必須使用和其他 untrusted model output 相同的 sanitization 與 CSP。不要未經可信 sanitizer 直接放進 innerHTML

下一頁:Session 狀態與 Events