Agent SDK AI Chat Widget for Financial Platforms Embeddable Chatbot iTick Documentation SDK & Developer Tools Official iTick Agent SDK providing plug-and-play AI chat capabilities for financial information platforms. Features ShadowDOM style isolation, virtual scrolling, Markdown rendering, streaming SSE output, and React/Vue 3 framework wrappers.
Agent SDK Documentation
iTick AI Chat SDK — Plug-and-play AI conversational capabilities for financial information platforms.
Core Features
- ShadowDOM Style Isolation — Component styles are fully encapsulated and unaffected by host page CSS
- Virtual Scrolling — High-performance rendering supporting tens of thousands of messages
- Markdown Rendering — Built-in GFM format rendering with table support, syntax highlighting, and streaming output
- Multi-language — Built-in support for Simplified Chinese, Traditional Chinese, and English
- Theme Switching — Supports light, dark, and system-following modes
- Dual Modes — Fast mode and think (intelligent analysis) mode, switchable at runtime
- Streaming SSE — Real-time streaming output based on Server-Sent Events with cancellation support
- Message Segmentation — Alternating streaming output of think and content segments, each think module with independent state
- Framework Agnostic — Pure TypeScript core with official React and Vue 3 wrappers
Installation
# Core package (required)
npm install @itick/chat-core
# React wrapper (optional)
npm install @itick/chat-react
# Vue 3 wrapper (optional)
npm install @itick/chat-vue
Or include the UMD build directly via <script> tag:
<script src="https://unpkg.com/@itick/chat-core/dist/chat-core.umd.js"></script>
<script>
const sdk = new ChatSDK.ChatSDK({ /* ... */ });
</script>
Quick Start
Vanilla JavaScript
import { ChatSDK } from '@itick/chat-core';
const sdk = new ChatSDK({
apiUrl: 'https://agent.itick.org/agent/stream/v1',
token: 'your-api-token',
container: document.getElementById('chat-container'),
width: '100%',
height: '600px',
});
// Listen to events
sdk.on('message:receive', ({ message }) => {
console.log('Message received:', message.content);
});
sdk.on('error', ({ error }) => {
console.error('Request error:', error);
});
// Mount to the page
sdk.mount();
React
import { ChatWidget, type ChatWidgetRef } from '@itick/chat-react';
import { useRef } from 'react';
function App() {
const chatRef = useRef<ChatWidgetRef>(null);
return (
<ChatWidget
ref={chatRef}
apiUrl="https://agent.itick.org/agent/stream/v1"
token="your-api-token"
width="100%"
height="600px"
locale="en-US"
onMessageReceive={({ message }) => console.log(message)}
/>
);
}
Vue 3
<template>
<ChatWidget
ref="chatRef"
:apiUrl="apiUrl"
:token="token"
width="100%"
height="600px"
locale="en-US"
@messageReceive="onReceive"
/>
</template>
<script setup>
import { ref } from 'vue';
import { ChatWidget } from '@itick/chat-vue';
const chatRef = ref();
const apiUrl = 'https://agent.itick.org/agent/stream/v1';
const token = 'your-api-token';
function onReceive({ message }) {
console.log(message);
}
</script>
Core Configuration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
apiUrl | string | Yes | - | Streaming API endpoint URL |
token | string | Yes | - | Authentication token, passed via HTTP Header token field |
container | HTMLElement | Yes | - | Host DOM element |
width | number | string | No | Auto-fit container | Width, supports number (px) or string |
height | number | string | No | Auto-fit container | Height |
themeColor | string | No | "#4F46E5" | Theme color |
theme | 'light' | 'dark' | 'auto' | No | "light" | Theme mode |
customCSS | string | No | - | Inject custom CSS |
locale | 'zh-CN' | 'zh-TW' | 'en-US' | No | - | UI language |
placeholder | string | No | Determined by i18n | Input placeholder text |
mode | 'fast' | 'think' | No | "think" | Chat mode |
actions | ActionsConfig | No | All visible | Action button visibility configuration |
disclaimer | string | No | Determined by i18n | Disclaimer text |
welcomeTitle | string | No | Determined by i18n | Empty state welcome title |
welcomeDescription | string | No | Determined by i18n | Empty state welcome description |
presetQuestions | string[] | No | 6 financial questions | Empty state preset questions |
onQuestionClick | (q: string) => void | No | - | Preset question click callback (SDK auto-sends message) |
onError | (error: Error) => void | No | - | Error callback |
renderHistoryMessage | (msg: ChatMessage) => HTMLElement | string | No | - | Custom history message renderer |
renderActions | (msg: ChatMessage, callbacks: ActionCallbacks) => HTMLElement | undefined | No | - | Custom action buttons (appended after default buttons) |
ActionsConfig
| Field | Type | Default | Description |
|---|---|---|---|
showCopy | boolean | true | Show copy button |
showRegenerate | boolean | true | Show regenerate button |
showLike | boolean | true | Show like button |
showShare | boolean | true | Show share button |
API Documentation
ChatSDK Instance Methods
Lifecycle
| Method | Description |
|---|---|
mount() | Mount to container, creates ShadowDOM and renders UI. Throws error if already destroyed |
unmount() | Remove ShadowDOM, cancel streaming requests, preserves instance and message data |
destroy() | Completely destroy instance, clears all resources and event listeners |
Message Management
| Method | Description |
|---|---|
sendMessage(content: string): Promise<void> | Send a message, auto-creates user and AI messages, initiates streaming request |
clearMessages() | Clear all messages |
getMessages(): ChatMessage[] | Get a copy of the message list |
setHistoryMessages(messages: ChatMessage[]) | Set history message list |
Configuration Management
| Method | Description |
|---|---|
updateConfig(partial: Partial<ChatConfig>) | Dynamically update configuration, triggers UI refresh |
getConfig(): Readonly<ChatConfig> | Get a read-only copy of the current configuration |
Plugin Management
| Method | Description |
|---|---|
registerPlugin(plugin: MessageRenderPlugin) | Register a message render plugin |
unregisterPlugin(name: string) | Unregister a plugin |
State Properties
| Property | Type | Description |
|---|---|---|
isMounted | boolean | Whether the SDK is mounted |
isDestroyed | boolean | Whether the SDK is destroyed |
Event System
Register event listeners with sdk.on(event, handler), remove with sdk.off(event, handler).
Lifecycle Events
| Event | Parameters | Trigger |
|---|---|---|
mount | None | After mount() completes |
unmount | None | After unmount() completes |
destroy | None | After destroy() completes |
Message Events
| Event | Parameters | Trigger |
|---|---|---|
message:send | { content: string } | When user sends a message |
message:receive | { message: ChatMessage } | After AI message streaming completes |
message:streaming | { message: ChatMessage; chunk: string } | Each content chunk received |
message:thinking | { message: ChatMessage; chunk: string } | Each think chunk received |
message:done | { message: ChatMessage } | Streaming output complete (including manual cancellation) |
Action Events
| Event | Parameters | Trigger |
|---|---|---|
actions:copy | { messageId, content, message } | Copy button clicked |
actions:regenerate | { messageId, message } | Regenerate button clicked |
actions:like | { messageId, content, message } | Like button clicked |
actions:share | { messageId, content, message } | Share button clicked |
Other Events
| Event | Parameters | Trigger |
|---|---|---|
error | { error: Error } | When streaming request fails |
config:change | { config: ChatConfig } | After updateConfig() is called |
Usage Examples
sdk.on('message:streaming', ({ chunk }) => {
console.log('Streaming chunk:', chunk);
});
sdk.on('message:done', ({ message }) => {
console.log('Message complete:', message.content);
});
sdk.on('actions:copy', ({ content }) => {
console.log('User copied:', content);
});
Message Structure
interface ChatMessage {
id: string; // Unique identifier
role: 'user' | 'assistant' | 'system';
segments: MessageSegment[]; // Think and content mixed in stream order
content: string; // All content segments concatenated (backward compatible)
timestamp: number; // Unix timestamp
status?: 'sending' | 'streaming' | 'done' | 'error';
metadata?: Record<string, unknown>;
}
interface MessageSegment {
type: 'think' | 'content';
content: string;
status: 'streaming' | 'done';
}
The segments array stores think and content blocks in the order they arrive via streaming. For example:
[think] → [content] → [think] → [content] → ...
Each think module has an independent streaming/done state and auto-collapses when complete.
Advanced Features
Theme Switching
// Set on initialization
const sdk = new ChatSDK({ theme: 'auto', /* ... */ });
// Switch at runtime
sdk.updateConfig({ theme: 'dark' });
Three modes supported: light, dark, and auto (system-following).
Internationalization
const sdk = new ChatSDK({ locale: 'en-US', /* ... */ });
// Switch at runtime
sdk.updateConfig({ locale: 'zh-CN' });
Supported: zh-CN (Simplified Chinese), zh-TW (Traditional Chinese), en-US (English).
Custom CSS
const sdk = new ChatSDK({
customCSS: `
.chat-message-user .chat-message-bubble {
border-radius: 20px;
}
`,
/* ... */
});
CSS is injected via ShadowDOM and does not affect the host page. Theme color can be overridden via CSS variables:
--chat-primary: #4F46E5;
--chat-primary-hover: #4338CA;
Custom Action Buttons
const sdk = new ChatSDK({
// Hide some buttons
actions: {
showLike: false,
showShare: false,
},
// Add custom button
renderActions: (message, callbacks) => {
const btn = document.createElement('button');
btn.textContent = 'Favorite';
btn.addEventListener('click', () => {
callbacks.onCopy(message.content);
});
return btn;
},
/* ... */
});
Mode Switching
Users can switch between Fast mode and Think mode at the bottom-left of the input box. Think mode displays the AI's reasoning process.
// Set on initialization
const sdk = new ChatSDK({ mode: 'fast', /* ... */ });
// Switch at runtime
sdk.updateConfig({ mode: 'think' });
Message Render Plugins
Plugins can override the default Markdown rendering behavior:
interface MessageRenderPlugin {
name: string;
match: (message: ChatMessage) => boolean;
render: (message: ChatMessage, shadowRoot: ShadowRoot) => HTMLElement;
update?: (element: HTMLElement, message: ChatMessage) => void;
}
sdk.registerPlugin({
name: 'code-highlight',
match: (msg) => msg.content.includes('```'),
render: (msg) => {
const el = document.createElement('div');
el.innerHTML = /* Custom render logic */;
return el;
},
update: (el, msg) => {
el.innerHTML = /* Streaming update logic */;
},
});
// Unregister plugin
sdk.unregisterPlugin('code-highlight');
Note: Registering a plugin overrides the built-in Markdown renderer. Plugins are matched in reverse registration order — later registered plugins have higher priority.
History Message Rendering
const sdk = new ChatSDK({
renderHistoryMessage: (message) => {
// Return HTML string or HTMLElement
return `<div class="custom-message">${message.content}</div>`;
},
/* ... */
});
Preset Questions
const sdk = new ChatSDK({
presetQuestions: [
'How are the three major US stock indices performing today?',
'What is the current BTC/USDT price and 24h change?',
],
onQuestionClick: (question) => {
console.log('User clicked question:', question);
// SDK has already auto-sent the message, this is just a notification hook
},
/* ... */
});
Framework Integration
React
Props
| Property | Type | Required | Default |
|---|---|---|---|
apiUrl | string | Yes | - |
token | string | Yes | - |
width | number | string | No | "100%" |
height | number | string | No | "600px" |
themeColor | string | No | "#4F46E5" |
customCSS | string | No | - |
locale | 'zh-CN' | 'zh-TW' | 'en-US' | No | "en-US" |
placeholder | string | No | - |
renderHistoryMessage | (msg: ChatMessage) => HTMLElement | string | No | - |
onMessageSend | (payload: { content: string }) => void | No | - |
onMessageReceive | (payload: { message: ChatMessage }) => void | No | - |
onMessageStreaming | (payload: { message: ChatMessage; chunk: string }) => void | No | - |
onMessageDone | (payload: { message: ChatMessage }) => void | No | - |
onError | (payload: { error: Error }) => void | No | - |
onConfigChange | (payload: { config: ChatConfig }) => void | No | - |
Ref Methods (ChatWidgetRef)
| Method | Signature |
|---|---|
sendMessage | (content: string) => Promise<void> |
clearMessages | () => void |
getMessages | () => ChatMessage[] |
setHistoryMessages | (messages: ChatMessage[]) => void |
registerPlugin | (plugin: MessageRenderPlugin) => void |
unregisterPlugin | (name: string) => void |
updateConfig | (partial: Partial<ChatConfig>) => void |
getSDK | () => ChatSDK | null |
Note: Changes to
apiUrl,token,width, andheightprops do not trigger hot reload — the component must be remounted.themeColor,customCSS,locale, andplaceholdersupport hot updates.
Vue 3
Props
Same as React (excluding callback-style props).
Events
| Event | Parameters |
|---|---|
messageSend | { content: string } |
messageReceive | { message: ChatMessage } |
messageStreaming | { message: ChatMessage; chunk: string } |
messageDone | { message: ChatMessage } |
error | { error: Error } |
configChange | { config: ChatConfig } |
Expose Methods
Identical to React ChatWidgetRef, plus getSDK().
API Format
Request Format
The SDK sends a POST request to apiUrl:
Headers:
Content-Type: application/json
token: {your-token}
Accept: text/event-stream
Body:
{
"input": {
"messages": [
{ "type": "human", "content": "User message content" }
]
},
"config": {
"configurable": {
"thread_id": "{uuid}",
"enable_thinking": true
}
}
}
thread_idis auto-generated on instantiation for server-side session persistenceenable_thinkingis determined bymodeconfig (truefor think,falsefor fast)- Only messages with
role === 'user'are sent
SSE Response Format
Server must return Content-Type: text/event-stream, each data entry formatted as:
data: {"type": "think", "content": "Thinking process..."}
data: {"type": "content", "content": "Reply content..."}
data: [DONE]
type: "think"— Reasoning process, rendered as a collapsible think moduletype: "content"— Actual reply content, rendered as a Markdown bubble[DONE]— Stream end marker
Also compatible with OpenAI format (choices[0].delta.content).
Development & Debugging
# Install dependencies
npm install
# Build all packages
npm run build
# Lint code
npm run lint
FAQ
How do I customize styles?
Inject custom CSS via the customCSS config, or set a theme color via themeColor. All styles are isolated via ShadowDOM, and CSS variable names are prefixed with --chat-.
How do I handle errors?
Listen via the onError config or the error event:
const sdk = new ChatSDK({
onError: (error) => { /* Handle error */ },
/* ... */
});
sdk.on('error', ({ error }) => {
console.error(error);
});
How do I cancel a streaming request?
Users can click the send button (which becomes a stop button during streaming). Programmatic cancellation is available via sdk.unmount() or sdk.destroy().
How do I preserve conversation context?
thread_id is generated on instantiation. As long as you don't call destroy() to recreate the instance, multiple conversations within the same instance will share the thread_id, allowing the server to maintain session context.
Does virtual scrolling affect message rendering?
No. Virtual scrolling only optimizes DOM node creation and recycling. Message content is fully preserved via the segments and content fields, and action buttons and suggested questions are automatically appended when the message status becomes done.
- MCP Server
Official iTick MCP Server providing REST API queries and WebSocket real-time data subscriptions for basics, stocks, indices, futures, funds, forex, and cryptocurrencies.
- Subscribe and Renew
How to subscribe and renew package plans on the iTick platform. The complete process of selecting a package, confirming orders, and completing payments, along with renewal operation guidelines to help users easily manage service subscriptions.