Skip to content

Integrating SDK with Native JS

The native JS SDK provides UI, functionality, and integration similar to iframe, allowing you to directly use the HENGSHI ChatBI conversation components, styles, and features. You only need to include the AI assistant's JS file in your HTML file (or Vue/React component) and call the relevant APIs. This will require you to have some JavaScript development experience.

Quick Start

  1. Obtain the JS SDK Link

Navigate to the Settings -> Feature Configuration -> Intelligent Query Assistant -> Console page within the HENGSHI system. Click Options -> Use SDK Integration to open the integration guide page. In the drawer popup, you will find the SDK link for integration, such as:

https://develop.hengshi.org/assets/hengshi-copilot@<version>.js

Note

@<version> is the SDK version number. Please ensure that the SDK version you use matches your system version. You will also need to update it when the system is upgraded.

  1. Import the JS SDK

We provide sample code for integrating the SDK into front-end frameworks such as Vue, React, and plain JS. You can choose based on your needs. This requires you to have some front-end development experience.

js
<!DOCTYPE html>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Assistant</title>
    <script type="text/javascript" src="https://develop.hengshi.org/assets/hengshi-copilot@<version>.js" defer></script>
</head>
<body>
    <!-- Add a button at an appropriate location in your project -->
    <button id="trigger-ai">Launch AI Assistant</button>
    <!-- Add the container required by the SDK at an appropriate location in your project -->
    <div id="copilot-root" style="display: none;"></div>

    <script>
        const copilotConfig = { // Define AI Assistant configuration
            locale: 'en-US',
            draggable: {
                enable: true,
                minWidth: 440,
                minHeight: 440,
                position: {
                    x: window.innerWidth - 480,
                    y: 20,
                },
                size: {
                    width: 440,
                    height: window.innerHeight * 0.8,
                },
                onDragStop: onDragStop,
                onResize: onResize,
            },
            userConfig: {
                dataAppId: 130870,
                datasetId: 1,
            },
        };

        let copilotInstance = null;
        const button = document.getElementById('trigger-ai');
        const copilotRoot = document.getElementById('copilot-root');

        button.addEventListener('click', toggleCopilot);

        function onDragStop(event, position) {
            copilotConfig.draggable.position = position;
        }

        function onResize(event, position, size) {
            copilotConfig.draggable.position = position;
            copilotConfig.draggable.size = size;
        }

        function launchCopilot() {
            if (typeof Copilot === 'undefined') {
                (function(fn) {
                    fn(launchCopilot);
                })(requestIdleCallback || setTimeout);
            } else {
                if (!copilotInstance) {
                    copilotInstance = new Copilot(copilotRoot, copilotConfig);
                    copilotRoot.style.display = 'block';
                    button.textContent = 'Close AI Assistant';
                } else {
                    copilotInstance.show(); // Use show method to display existing instance
                }
            }
        }

        function destroyCopilot() {
            if (copilotInstance) {
                copilotInstance.hide(); // Use hide method to hide instance instead of destroying
                copilotInstance = null;
                copilotRoot.style.display = 'none';
                button.textContent = 'Launch AI Assistant';
            }
        }

        function toggleCopilot() {
            if (copilotInstance) {
                destroyCopilot();
            } else {
                launchCopilot();
            }
        }
    </script>
</body>
</html>
jsx
import React, { useState, useEffect } from 'react';

const CopilotComponent = () => {
  const [copilotInstance, setCopilotInstance] = useState(null);
  const [buttonLabel, setButtonLabel] = useState('Launch AI Assistant');

  const copilotConfig = {
    locale: 'en-US',
    draggable: {
      enable: true,
      minWidth: 440,
      minHeight: 440,
      position: {
        x: window.innerWidth - 480,
        y: 20,
      },
      size: {
        width: 440,
        height: window.innerHeight * 0.8,
      },
      onDragStop: onDragStop,
      onResize: onResize,
    },
    userConfig: {
      dataAppId: 130870,
      datasetId: 1,
    },
  };

  useEffect(() => {
    // Load SDK
    const script = document.createElement('script');
    script.src = 'https://develop.hengshi.org/assets/hengshi-copilot@<version>.js';
    script.async = true;
    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script); // Clean up loaded script
    };
  }, []);

  function onDragStop(event, position) {
    copilotConfig.draggable.position = position;
  }

  function onResize(event, position, size) {
    copilotConfig.draggable.position = position;
    copilotConfig.draggable.size = size;
  }

  const launchCopilot = () => {
    if (typeof Copilot === 'undefined') {
      (function(fn) {
        fn(launchCopilot);
      })(requestIdleCallback || setTimeout);
    } else {
      if (!copilotInstance) {
        const copilotRoot = document.getElementById('copilot-root');
        const instance = new Copilot(copilotRoot, copilotConfig);
        setCopilotInstance(instance);
        setButtonLabel('Close AI Assistant');
      } else {
        copilotInstance.show(); // Use show method to display existing instance
      }
    }
  };

  const destroyCopilot = () => {
    if (copilotInstance) {
      copilotInstance.hide(); // Use hide method to hide instance instead of destroying
      setCopilotInstance(null);
      setButtonLabel('Launch AI Assistant');
    }
  };

  const toggleCopilot = () => {
    if (copilotInstance) {
      destroyCopilot();
    } else {
      launchCopilot();
    }
  };

  return (
    <div>
      <button onClick={toggleCopilot}>{buttonLabel}</button>
      {copilotInstance && <div id="copilot-root"></div>}
    </div>
  );
};

export default CopilotComponent;
vue
<template>
  <div>
    <button @click="toggleCopilot">{{ buttonLabel }}</button>
    <div id="copilot-root" v-if="copilotInstance"></div>
  </div>
</template>

<script>
import { ref, onMounted, onBeforeUnmount } from 'vue';

export default {
  setup() {
    const copilotInstance = ref(null);
    const buttonLabel = ref('Launch AI Assistant');

    const copilotConfig = {
      locale: 'en-US',
      draggable: {
        enable: true,
        minWidth: 440,
        minHeight: 440,
        position: {
          x: window.innerWidth - 480,
          y: 20,
        },
        size: {
          width: 440,
          height: window.innerHeight * 0.8,
        },
        onDragStop: onDragStop,
        onResize: onResize,
      },
      userConfig: {
        dataAppId: 130870,
        datasetId: 1,
      },
    };

    function onDragStop(event, position) {
      copilotConfig.draggable.position = position;
    }

    function onResize(event, position, size) {
      copilotConfig.draggable.position = position;
      copilotConfig.draggable.size = size;
    }

    function launchCopilot() {
      if (typeof Copilot === 'undefined') {
        (function(fn) {
          fn(launchCopilot);
        })(requestIdleCallback || setTimeout);
      } else {
        if (!copilotInstance.value) {
          copilotInstance.value = new Copilot(document.getElementById('copilot-root'), copilotConfig);
          buttonLabel.value = 'Close AI Assistant';
        } else {
          copilotInstance.value.show(); // Use show method to display existing instance
        }
      }
    }

    function destroyCopilot() {
      if (copilotInstance.value) {
        copilotInstance.value.hide(); // Use hide method to hide instance instead of destroying
        copilotInstance.value = null;
        buttonLabel.value = 'Launch AI Assistant';
      }
    }

    function toggleCopilot() {
      if (copilotInstance.value) {
        destroyCopilot();
      } else {
        launchCopilot();
      }
    }

    return {
      copilotInstance,
      buttonLabel,
      toggleCopilot,
    };
  },
};
</script>

<style>
/* Add styles (if needed) */
</style>
  1. Start/Open your project and check if the integration is successful.

JS SDK Instance

The result of new Copilot(...) is a copilot instance, which you can use to operate Copilot.

js
{
  copilotConfig: {
    config: {...}, // Large model configuration information
    loading: false, // Whether it is loading
    onVerifyConfig: () => {}, // Verify configuration information
    onChangeProvider: () => {}, // Switch model provider
    // ...
  },
  context: {
    api: {...},
    endpoints: {...},
    // Copilot state
    isLoading: false,
    // Current session list, data format reference
    // https://api.hengshi.com/conversation.html#conversation
    conversations: [],
    setConversations: () => {},
    currentConversationId: null,
    setCurrentConversationId: () => {},
    runningChatUid: null,
    setRunningChatUid: () => {},
    setChat: (id, chatUid, chat) => {},
    // Fetch historical session list
    onFetchConversations: (offset = 0, limit = 10) => {},
    // Create session
    onCreateConversation: prompt => {},
    onFetchConversation: id => {},
    onClearConversations: () => {},
    onFetchPreviousConversations: () => {},
    // Create Q&A
    onCreateChat: body => {},
    onDeleteChat: (id, chatUid) => {},
    onFetchSuggestions: refresh => {},
    onFetchFavorites: () => {},
    onCancelChatFavorite: (id, chatUid) => {},
  },
  // Control dialog visibility
  show: () => {}, // Show the dialog
  hide: () => {}, // Hide the dialog
}

1. Controlling Dialog Visibility

The Copilot instance provides show and hide methods to control the dialog's visibility, which is more efficient than completely destroying and recreating the instance:

js
// Create a Copilot instance
const copilotInstance = new Copilot(copilotRoot, copilotConfig);

// Hide the dialog (doesn't destroy the instance, just hides the UI)
copilotInstance.hide();

// Show the dialog
copilotInstance.show();

// Toggle show/hide status
function toggleCopilot() {
  if (copilotInstance) {
    // You can check the DOM element state to determine current visibility
    const isVisible = copilotRoot.style.display !== 'none';
    if (isVisible) {
      copilotInstance.hide();
      button.textContent = 'Launch AI Assistant';
    } else {
      copilotInstance.show();
      button.textContent = 'Close AI Assistant';
    }
  }
}

Compared to completely destroying and recreating the instance, using the show/hide methods has the following advantages:

  • Maintains conversation context and state
  • Faster response time (no need to reinitialize)
  • Lower resource consumption

2. Calling HTTP API via JS

In the copilot example above, the api object provides all the methods for calling HTTP APIs. You can interact with the backend by invoking these methods, such as:

2.1 Create a conversation

js
const { body: { data: conversation }} = await copilot.api.requestCreateConversation({
  body: {
    title: 'This is the title of the conversation',
  },
}).value;
Data Structure
js
conversation = {
  "id": 135,
  "title": "This is the title of the conversation",
  "createdBy": 268,
  "createdAt": "2024-09-14 15:47:23",
  "updatedBy": 268,
  "updatedAt": "2024-09-14 15:47:23"
}

2.2. Create a Q&A

js
const { body: { data: chat }} = await copilot.api.requestCreateChat({
  id: conversation.id,
  body: {
    prompt: 'Which director has the highest-grossing movies?', // This is the user's question
    userConfig: { // Refer to the userConfig in copilotConfig above
      dataAppId: 129150,
      datasetId: 1,
    }
  },
  qs: {
    sync: true, // Whether to execute synchronously
    timeout: 1000 * 60, // Timeout duration in milliseconds
  },
}).value;
Data Structure
js
chat = {
    "conversationId": 135,
    "prompt": "Which director has the highest-grossing movies?",
    "answer": "Christopher Nolan's movies have the highest box office earnings.",
    "model": "gpt-4o",
    "uid": "08ff93ca-1972-4916-b884-35fab6f91c64",
    "temperature": 0,
    "createdBy": 268,
    "createdAt": "2024-09-14 15:47:23",
    "updatedBy": 268,
    "updatedAt": "2024-09-14 15:47:23",
    "responseAt": "2024-09-14 15:47:30",
    "isDelete": false,
    "isContextEnd": false,
    "suggestQuestions": [],
    "statusList": [
        "PENDING",
        "ANALYZE_REQUEST",
        "HQL_SELECT_FIELDS",
        "HQL_SELECT_FUNCTIONS",
        "GENERATE_HQL_QUERY",
        "DOING_HQL_QUERY",
        "DOING_SUMMARY",
        "DOING_QUESTION_SUGGESTING",
        "DONE"
    ],
    "userConfig": {
        "datasetId": 1,
        "dataAppId": 129150
    },
    "autoConfig": {
        "agentType": "HQL_AGENT"
    },
    "isCurrent": true,
    "usage": [
        {
            "completion_tokens": 24,
            "prompt_tokens": 4063,
            "total_tokens": 4087
        },
        {
            "completion_tokens": 5,
            "prompt_tokens": 1424,
            "total_tokens": 1429
        },
        {
            "completion_tokens": 49,
            "prompt_tokens": 3906,
            "total_tokens": 3955
        },
        {
            "completion_tokens": 16,
            "prompt_tokens": 299,
            "total_tokens": 315
        }
    ],
    "chartCreatedAt": "2024-09-14 15:47:30",
    "refineQuestion": "Which director has the highest-grossing movies?",
    "favorite": false,
    "charts": [
        {
            "appId": 129150,
            "datasetId": 1,
            "options": {
                "axes": [
                    {
                        "op": "{{1}}.{director}",
                        "uid": "uid1",
                        "fieldName": "director",
                        "kind": "formula",
                        "datasetId": 1,
                        "labelOrigin": "director",
                        "label": "director",
                        "isAggregate": false,
                        "value": "{{coffe_product_table}}.{director}",
                        "dataset": 1,
                        "fieldType": "string"
                    },
                    {
                        "op": "max({{1}}.{votes})",
                        "uid": "uid2",
                        "fieldName": "votes",
                        "kind": "formula",
                        "datasetId": 1,
                        "labelOrigin": "votes",
                        "label": "votes",
                        "isAggregate": true,
                        "value": "max({{coffe_product_table}}.{votes})",
                        "dataset": 1,
                        "fieldType": "number"
                    }
                ],
                "name": "Bar",
                "limit": 1,
                "sort": [
                    {
                        "op": "uid2",
                        "kind": "reference",
                        "direction": "desc"
                    },
                    {
                        "op": "uid1",
                        "kind": "reference",
                        "direction": "asc"
                    }
                ]
            },
            "dataAppId": 129150,
            "datasetIds": [
                1
            ]
        }
    ]
}

Integrating Copilot into Dashboards in HENGSHI SENSE

The method of invoking Copilot in the HENGSHI SENSE system is similar to the aforementioned HTML approach.

First, load the Copilot SDK through code in the global JS file:

js
// 1. Keep the copilot SDK synchronized with the system's store data
window.__INITIAL_STATE__ = window._hs_store_.getState();
// 2. Import the copilot SDK code
var script = document.createElement('script');
script.src = 'https://develop.hengshi.org/assets/hengshi-copilot@<version>.js';
script.async = true;
script.onload = function() {
  // 3. Reset the SDK base URL
  window.__hs_sdk_base_url__ = undefined;
  // 4. Create the SDK container
  var copilotRoot = document.createElement('div');
  copilotRoot.id = 'copilot-root';
  copilotRoot.style.width = '100%';
  copilotRoot.style.height = '100%';
  copilotRoot.style.position = 'fixed';
  copilotRoot.style.inset = '0';
  copilotRoot.style.zIndex = '9999';
  copilotRoot.style.pointerEvents = 'none';
  document.body.appendChild(copilotRoot);
  // 5. Assign values to the custom sandbox variable for use in custom JS
  window.myJS = window.myJS || {};
  window.myJS.innerWidth = window.innerWidth;
  window.myJS.innerHeight = window.innerHeight;
  window.myJS.Copilot = Copilot;
  window.myJS.CopilotRoot = copilotRoot;
};
document.body.appendChild(script);

Next, you can use a control button in the dashboard to trigger Copilot. Add a click event in the button control settings to execute the corresponding JS code.

js
if (!myJS) {
  throw new Error('hengshi copilot sdk not loaded yet');
}
if (!myJS.copilotConfig) {
  var stylesheet = `html.hengshi-copilot-sdk, html.hengshi-copilot-sdk body {width:100% !important; height: 100% !important;}
    html.hengshi-copilot-sdk body.hst {background: transparent;}
    .react-draggable {pointer-events: all;}`;
  myJS.copilotConfig = {
    locale: 'zh-CN',
    stylesheet: stylesheet,
    closable: true, // Set the dialog to be closable
    draggable: { // Enable dialog dragging functionality; set to false or omit if not needed
      enable: true,
      minWidth: 440,
      minHeight: 440,
      position: {
        x: myJS.innerWidth - 480,
        y: 20,
      },
      size: {
        width: 440,
        height: myJS.innerHeight * 0.8,
      },
      // Remember the dragged position and size
      onDragStop: onDragStop,
      onResize: onResize,
    },
    // Set the source of dialog data
    userConfig: {
      dataAppId: 354, // Data package ID
      datasetId: 26, // Dataset ID
    },
  };
}
function onDragStop(event, position) {
  myJS.copilotConfig.draggable.position = position;
}
function onResize(event, position, size) {
  myJS.copilotConfig.draggable.position = position;
  myJS.copilotConfig.draggable.size = size;
}
function launchCopilot() {
  if (typeof myJS.Copilot === 'undefined') {
    (function (fn) {
      fn(launchCopilot);
    })(requestIdleCallback || setTimeout);
  } else {
    if (!myJS.copilot) {
      myJS.copilot = new myJS.Copilot(myJS.CopilotRoot, myJS.copilotConfig);
    } else {
      myJS.copilot.show(); // Use show method to display existing instance
    }
  }
}

if (myJS.copilot) {
  myJS.copilot.hide(); // Use hide method to hide instance instead of destroying
} else {
  launchCopilot();
}

Using Copilot to Embed a Standalone Chatbox Component

In addition to embedding the full AI assistant chat functionality, the SDK also provides a static method renderChat, allowing you to embed a single chat anywhere on the page. This is ideal for displaying specific AI analysis results.

Basic Usage

html
<div class="conversation"></div>
<script src="https://develop.hengshi.org/assets/hengshi-copilot@<version>.js"></script>
<script>
  // Retrieve dialog data structure through internal API
  const chat = {
    "meta": "refineQuestion,answer,chart,chartConfig",
    "conversationId": 226,
    "uid": "00747081-9cc3-4c7c-b3de-735244498fd6",
    "prompt": "Module distribution of bug issues",
    "answer": "Bug issues are distributed relatively evenly across modules. The module with the most bugs has 4, while most other modules have 2-3 bugs.",
    "refineQuestion": "Please provide the distribution of bug issues across modules",
    "options": {
      "charts": [],
      "chartsData": []
    },
  };
  const container = document.querySelector('.conversation');
  window.Copilot.renderChat(container, chat);
</script>

Parameter Description

The Copilot.renderChat method accepts two parameters:

  1. container: A DOM element used as the container for rendering the chat content.
  2. chat: A chat data object containing the following fields:
    • meta: A string specifying the content to be rendered, which can be a combination of fields such as refineQuestion, answer, chart, and chartConfig. This field determines what content will be rendered.
      • refineQuestion: Refined question
      • answer: AI's response
      • chart: Chart
      • chartConfig: Data fetching logic
    • conversationId: Conversation ID
    • uid: Unique identifier for the conversation
    • prompt: Original question
    • answer: AI response content
    • refineQuestion: Refined question
    • charts: Array of chart configurations
    • chartsData: Array of chart data

This approach allows you to embed specific AI analysis results into web pages, reports, or dashboards without needing to provide a complete chat interface.

User Manual for Hengshi Analysis Platform