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

FieldTypeRequiredDefaultDescription
apiUrlstringYes-Streaming API endpoint URL
tokenstringYes-Authentication token, passed via HTTP Header token field
containerHTMLElementYes-Host DOM element
widthnumber | stringNoAuto-fit containerWidth, supports number (px) or string
heightnumber | stringNoAuto-fit containerHeight
themeColorstringNo"#4F46E5"Theme color
theme'light' | 'dark' | 'auto'No"light"Theme mode
customCSSstringNo-Inject custom CSS
locale'zh-CN' | 'zh-TW' | 'en-US'No-UI language
placeholderstringNoDetermined by i18nInput placeholder text
mode'fast' | 'think'No"think"Chat mode
actionsActionsConfigNoAll visibleAction button visibility configuration
disclaimerstringNoDetermined by i18nDisclaimer text
welcomeTitlestringNoDetermined by i18nEmpty state welcome title
welcomeDescriptionstringNoDetermined by i18nEmpty state welcome description
presetQuestionsstring[]No6 financial questionsEmpty state preset questions
onQuestionClick(q: string) => voidNo-Preset question click callback (SDK auto-sends message)
onError(error: Error) => voidNo-Error callback
renderHistoryMessage(msg: ChatMessage) => HTMLElement | stringNo-Custom history message renderer
renderActions(msg: ChatMessage, callbacks: ActionCallbacks) => HTMLElement | undefinedNo-Custom action buttons (appended after default buttons)

ActionsConfig

FieldTypeDefaultDescription
showCopybooleantrueShow copy button
showRegeneratebooleantrueShow regenerate button
showLikebooleantrueShow like button
showSharebooleantrueShow share button

API Documentation

ChatSDK Instance Methods

Lifecycle

MethodDescription
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

MethodDescription
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

MethodDescription
updateConfig(partial: Partial<ChatConfig>)Dynamically update configuration, triggers UI refresh
getConfig(): Readonly<ChatConfig>Get a read-only copy of the current configuration

Plugin Management

MethodDescription
registerPlugin(plugin: MessageRenderPlugin)Register a message render plugin
unregisterPlugin(name: string)Unregister a plugin

State Properties

PropertyTypeDescription
isMountedbooleanWhether the SDK is mounted
isDestroyedbooleanWhether the SDK is destroyed

Event System

Register event listeners with sdk.on(event, handler), remove with sdk.off(event, handler).

Lifecycle Events

EventParametersTrigger
mountNoneAfter mount() completes
unmountNoneAfter unmount() completes
destroyNoneAfter destroy() completes

Message Events

EventParametersTrigger
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

EventParametersTrigger
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

EventParametersTrigger
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

PropertyTypeRequiredDefault
apiUrlstringYes-
tokenstringYes-
widthnumber | stringNo"100%"
heightnumber | stringNo"600px"
themeColorstringNo"#4F46E5"
customCSSstringNo-
locale'zh-CN' | 'zh-TW' | 'en-US'No"en-US"
placeholderstringNo-
renderHistoryMessage(msg: ChatMessage) => HTMLElement | stringNo-
onMessageSend(payload: { content: string }) => voidNo-
onMessageReceive(payload: { message: ChatMessage }) => voidNo-
onMessageStreaming(payload: { message: ChatMessage; chunk: string }) => voidNo-
onMessageDone(payload: { message: ChatMessage }) => voidNo-
onError(payload: { error: Error }) => voidNo-
onConfigChange(payload: { config: ChatConfig }) => voidNo-

Ref Methods (ChatWidgetRef)

MethodSignature
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, and height props do not trigger hot reload — the component must be remounted. themeColor, customCSS, locale, and placeholder support hot updates.

Vue 3

Props

Same as React (excluding callback-style props).

Events

EventParameters
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_id is auto-generated on instantiation for server-side session persistence
  • enable_thinking is determined by mode config (true for think, false for 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 module
  • type: "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.

  1. 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.

  2. 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.