跳至主要内容

Headless

如果內建的 <Chatbot> 不符合你的設計,你可以完全跳過它,直接用 useChannel hook 自己組 UI。下方範例的對話介面 100% 自製, 連 SDK 的 CSS 都沒有引入。

完全自訂 UI

不使用 <Chatbot> 元件,改用 useChannel hook 直接讀取 conversation、呼叫 sendMessage,自己組出符合品牌風格的對話介面。

下方介面 100% 自製,沒有引用 SDK 內建的聊天室外觀。

載入中…

範例程式

import { useMemo, useState } from "react";
import { AsgardServiceClient } from "@asgard-js/core";
import { useChannel } from "@asgard-js/react";

export function HeadlessChat() {
const client = useMemo(
() =>
new AsgardServiceClient({
botProviderEndpoint: "https://...",
}),
[],
);

const { conversation, sendMessage, isConnecting } = useChannel({
client,
customChannelId: "my-channel",
defaultIsOpen: true,
});

const [input, setInput] = useState("");

const messages = conversation
? Array.from(conversation.messages.values())
: [];

const submit = () => {
const text = input.trim();
if (!text || isConnecting) return;
sendMessage?.({ text });
setInput("");
};

return (
<div>
{messages.map((msg) => {
if (msg.type === "user") return <div key={msg.messageId}>{msg.text}</div>;
if (msg.type === "bot") return <div key={msg.messageId}>{msg.message?.text}</div>;
if (msg.type === "tool-call")
return <div key={msg.messageId}>🔧 {msg.toolName}</div>;
return null;
})}
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.nativeEvent.isComposing) {
e.preventDefault();
submit();
}
}}
placeholder="輸入訊息…"
/>
<button onClick={submit} disabled={!input.trim() || isConnecting}>
送出
</button>
</div>
);
}

useChannel 回傳值

欄位說明
conversation目前的 Conversation,包含一個 messages Map
sendMessage送出訊息(支援 text / payload / blobIds
resetChannel重置 channel 並建立新對話
closeChannel關閉 SSE 連線
isConnecting是否正在連線
isResetting是否正在重置 channel

訊息類型

所有訊息都統一為 ConversationMessage,在 render 時用 type 區分:

  • user — 使用者送出的訊息
  • bot — 機器人回覆。bot.message.template 是 SDK 內建模板資料;串流文字在 bot.typingText,完整文字在 bot.message.text
  • tool-call — Agent 工具呼叫
  • error — 連線或執行錯誤