teko-chat-sdk
v1.3.1
Published
TekoChatSDK - AI chatbot SDK for Teko ECOM apps
Downloads
1,333
Readme
TekoChatSDK
React component library giúp các ECOM app của Teko tích hợp AI chatbot với khả năng bidirectional communication — right panel có thể push context ngược lại cho AI.
How it works
ECOM App
└── <TekoChatWidget chatBffUrl="..." transportMode="rest" panels={{...}} />
│
│ createTransport(transportMode):
│ 'websocket' → WebSocketTransport (persistent WS, streaming chunks)
│ 'sse' → SSETransport (fetch SSE / text/event-stream)
│ 'rest' → RestTransport (plain JSON POST)
▼
BFF → LLM → Business ServicesUI flow:
TekoChatBubble ──click──► navigate sang /chat ──► TekoChatWidget (fullscreen)Cài đặt
yarn add teko-chat-sdk
# peer deps
yarn add react react-domUsage
TekoChatBubble là trigger điều hướng sang chat route. TekoChatWidget render fullscreen trong container.
import { TekoChatBubble } from 'teko-chat-sdk';
// App.tsx — bubble navigate sang /chat
<TekoChatBubble onClick={() => router.push('/chat')} />
// pages/chat.tsx — fullscreen chiếm toàn bộ container
type Product = { sku: string; name: string; canonical?: string };
export default function ChatPage() {
const chatRef = useRef<TekoChatWidgetRef>(null);
return (
<div style={{ height: '100vh' }}>
<TekoChatWidget
ref={chatRef}
appId="your-app-id"
chatBffUrl="https://your-bff.example.com/chat"
transportMode="rest"
panels={{
cart: {
render: ({ sendContext }) => (
<CartPage
onUpdate={(items) => sendContext({ type: 'cart_updated', items })}
/>
),
},
}}
intents={{
INTENT_VIEW_CART: {
onResponse: (_args, ctx) => ctx.openPanel('cart'),
},
INTENT_PRODUCT_SEARCH: {
onResponse: (args) => {
const products = args.products as Product[] | undefined;
if (!products?.length) return;
return products.slice(0, 8).map((p) => ({
key: p.sku,
label: p.name,
payload: { sku: p.sku, canonical: p.canonical },
}));
},
onOptionClick: (option, { sendContext, sendMessage }) => {
sendContext(option.payload ?? {});
sendMessage(option.label);
},
},
}}
getAppContext={() => ({ terminalId: 'T001' })}
/>
</div>
);
}Mobile support: SDK không tự detect viewport — truyền
layoutModevào widget:const [layoutMode, setLayoutMode] = useState(() => window.innerWidth < 768 ? 'mobile' : 'desktop' ); // <TekoChatWidget layoutMode={layoutMode} ... />Mobile behavior: fullscreen = chat full-screen, right panel slide up dạng overlay khi host app gọi
openPanel().
Bidirectional Communication
Right panel có thể push context ngược về AI để cải thiện intent detection — không cần user gõ lại thông tin.
SDK inject useChatContext() vào mọi component bên trong panel — không cần prop drilling.
// App.tsx
<TekoChatWidget
ref={chatRef}
panels={{
cart: {
render: ({ sendContext }) => <CartPage onUpdate={(items) => sendContext({ items })} />,
},
}}
intents={{
INTENT_VIEW_CART: {
onResponse: (_args, ctx) => ctx.openPanel('cart'),
},
}}
/>
// CartPage.tsx — hoặc bất kỳ component lồng sâu nào
import { useChatContext } from 'teko-chat-sdk';
function CartPage() {
const { sendContext } = useChatContext();
const handleQtyChange = (qty: number) => {
updateCart(qty);
sendContext({
type: 'cart_updated',
items: cartItems,
totalAmount: calculateTotal(),
});
};
}useChatContext() trả về { sendContext, sendMessage, activePanel }. Nếu dùng bên ngoài TekoChatWidget, sendContext là no-op (không throw).
API
<TekoChatWidget> Props
3 props bắt buộc:
| Prop | Type | Mô tả |
| --------------- | -------------------------------- | --------------------- |
| appId | string | App identifier |
| chatBffUrl | string | BFF endpoint |
| transportMode | 'websocket' \| 'sse' \| 'rest' | Giao thức kết nối BFF |
Intent & panels
| Prop | Type | Mô tả |
| --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------- |
| panels | Record<string, PanelConfig> | Panel registry — keyed by panel name. Gọi chatRef.current?.openPanel(key) để hiển thị. |
| intents | Record<string, IntentConfig> | Unified intent handler map — xem chi tiết bên dưới. |
| getAppContext | () => Record<string, unknown> \| Promise<...> | Inject app metadata vào mỗi BFF request, resolve tại send-time (luôn fresh). |
SuggestOption
Chip gợi ý hiển thị bên dưới message của AI.
interface SuggestOption {
/** Unique key để SDK track, không hiển thị cho user. */
key: string;
/** Text hiển thị trên chip. Gửi lên BFF khi user click (default behavior). */
label: string;
/** Arbitrary data đính kèm — merge vào request context khi user click (default behavior). */
payload?: Record<string, unknown>;
}IntentConfig
Mỗi entry trong intents map ứng với một toolCall.name mà BFF có thể trả về.
interface IntentConfig {
/**
* Gọi khi BFF trả toolCall có tên khớp với key trong `intents`.
*
* @param args - `toolCall.arguments` từ BFF (OpenAI tool_calls format).
* @param ctx - Helpers để tương tác với SDK mà không cần chatRef.
* - `openPanel(key)` — Mở right panel theo key đã đăng ký trong `panels`.
* - `sendContext(data)` — Lưu context; tự merge vào request khi user gửi message tiếp theo.
* - `sendMessage(text)` — Gửi message lên BFF programmatically.
*
* @returns
* - `SuggestOption[]` — SDK tự render chips và gọi `appendSuggestions()`.
* - `void` — Side effect only (VD: mở panel, lưu context).
*/
onResponse: (
args: Record<string, unknown>,
ctx: {
openPanel: (key: string) => void;
sendContext: (data: Record<string, unknown>) => void;
sendMessage: (text: string) => void;
},
) => SuggestOption[] | void | Promise<SuggestOption[] | void>;
/**
* Gọi khi user click vào suggestion chip sinh ra bởi intent này.
*
* Không set → SDK dùng default behavior: gửi `option.label` lên BFF
* và merge `option.payload` vào request context.
*
* @param option - Chip user đã click.
* @param ctx - `sendContext` và `sendMessage` (không có `openPanel`).
*/
onOptionClick?: (
option: SuggestOption,
ctx: {
sendContext: (data: Record<string, unknown>) => void;
sendMessage: (text: string) => void;
},
) => void;
}Luồng hoạt động:
BFF trả toolCall { name: "INTENT_X", arguments: {...} }
│
▼
intents["INTENT_X"].onResponse(args, ctx)
│
├─ return SuggestOption[] → SDK render chips bên dưới message
│ User click chip
│ │
│ ▼
│ onOptionClick? (nếu có)
│ hoặc default: gửi label + payload lên BFF
│
└─ return void → side effect only (VD: ctx.openPanel('cart'))PanelConfig
interface PanelConfig {
/**
* @param props.sendContext - Push context về AI từ bên trong panel.
* Dữ liệu được merge vào request khi user gửi message tiếp theo.
*/
render: (props: {
sendContext: (data: Record<string, unknown>) => void;
}) => ReactNode;
}UI customization
| Prop | Type | Default | Mô tả |
| ---------------- | ----------------------- | ------------------------------ | --------------------------------------------------------------------- |
| layoutMode | 'desktop' \| 'mobile' | 'desktop' | Desktop: split 35/65. Mobile: single column + right panel overlay. |
| primaryColor | string | '#1a73e8' | Primary color (hex) |
| botAvatar | string | — | Avatar URL cho AI |
| locale | 'vi' \| 'en' | 'vi' | Ngôn ngữ hiển thị |
| labels | Partial<ChatLabels> | — | Override từng label cụ thể |
| loadingPhrases | string[] | Built-in phrases theo locale | Text xoay vòng hiển thị sau 2s khi AI đang xử lý. Truyền [] để tắt. |
Layout / spacing
| Prop | Type | Default | Mô tả |
| -------------- | -------- | ------- | -------------------------------------------------- |
| offsetTop | number | 0 | Offset từ top (px) — tránh che fixed header |
| offsetBottom | number | 0 | Offset từ bottom (px) — tránh che fixed bottom nav |
| zIndex | number | 1031 | CSS z-index |
TekoChatWidgetRef Methods
Truy cập qua ref.current:
| Method | Signature | Mô tả |
| ------------- | ----------------------------------------------------------- | ----------------------------------------------------------------- |
| openPanel | (componentKey: string) => void | Hiển thị right panel với component key |
| sendMessage | (text: string, context?: Record<string, unknown>) => void | Gửi message lên BFF programmatically |
| sendContext | (data: Record<string, unknown>) => void | Lưu context — tự merge vào request khi user gửi message tiếp theo |
<TekoChatBubble> Props
| Prop | Type | Default | Mô tả |
| -------------- | ------------------------------------ | ----------- | ----------------------------------------- |
| onClick | () => void | — | Callback khi user click bubble (required) |
| primaryColor | string | '#1a73e8' | Primary color (hex) |
| offsetBottom | number | 0 | Offset từ bottom (px) |
| zIndex | number | 1031 | CSS z-index |
| renderBubble | (onClick: () => void) => ReactNode | — | Custom bubble UI thay thế bubble mặc định |
BFF Protocol
Chọn transportMode theo giao thức BFF implement:
| transportMode | Giao thức | Ghi chú |
| --------------- | --------- | --------------------------------------------------------------------- |
| 'websocket' | WebSocket | Persistent connection, BFF push TransportFrame JSON qua ws.send() |
| 'sse' | SSE | POST request, BFF stream text/event-stream (data: {...}\n\n) |
| 'rest' | REST JSON | POST request, BFF trả JSON một lần (không stream) |
Contributing
See DEVELOPMENT.md for dev setup, Playground, project structure, and release process.
