Skip to content

通过 VANILLA JS SDK 集成

vanilla js sdk 提供的 UI、功能与 iframe 集成类似,直接使用衡石 ChatBI 的对话组件、样式和功能,只需要在你的 HTML 文件中(或 Vue/React 组件中)引入 AI 助手的 JS 文件,并调用相关 API。这将需要您拥有一定的 JavaScript 开发经验。

快速上手

  1. 获取 js sdk 链接

进入衡石系统内设置->功能配置->智能查数助手->控制台页面,点击选项->使用 SDK 嵌入打开集成页面指南,在抽屉弹窗中,您将找到用于集成的 SDK 链接,例如

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

注意

@<version> 是 SDK 的版本号,请确保您使用的 SDK 版本与您的系统版本一致。系统升级时也需要更新。

  1. 引入 js sdk

我们提供了在 vue、react、纯 js 等前端框架中集成 SDK 的示例代码,您可以根据需要进行选择,这要求您需要有一定的前端开发经验。

js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI助手</title>
    <script type="text/javascript" src="https://develop.hengshi.org/assets/hengshi-copilot@<version>.js" defer></script>
</head>
<body>
    <!-- 在你的项目中合适的位置增加按钮 -->
    <button id="trigger-ai">唤起AI助手</button>
    <!-- 在你的项目中合适的位置增加 sdk 所需容器 -->
    <div id="copilot-root" style="display: none;"></div>

    <script>
        const copilotConfig = { // 定义 AI 助手配置
            locale: 'zh-CN',
            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 = '关闭AI助手';
                }
            }
        }

        function destroyCopilot() {
            if (copilotInstance) {
                copilotInstance.destroy();
                copilotInstance = null;
                copilotRoot.style.display = 'none';
                button.textContent = '唤起AI助手';
            }
        }

        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('唤起AI助手');

  const copilotConfig = {
    locale: 'zh-CN',
    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(() => {
    // 加载 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); // 清理加载的脚本
    };
  }, []);

  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('关闭AI助手');
      }
    }
  };

  const destroyCopilot = () => {
    if (copilotInstance) {
      copilotInstance.destroy();
      setCopilotInstance(null);
      setButtonLabel('唤起AI助手');
    }
  };

  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('唤起AI助手');

    const copilotConfig = {
      locale: 'zh-CN',
      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 = '关闭AI助手';
        }
      }
    }

    function destroyCopilot() {
      if (copilotInstance.value) {
        copilotInstance.value.destroy();
        copilotInstance.value = null;
        buttonLabel.value = '唤起AI助手';
      }
    }

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

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

<style>
/* 添加样式(如果需要) */
</style>
  1. 启动/打开你的项目,检查是否成功集成。

JS SDK 实例

new Copilot(...) 的结果是一个 copilot 实例,你可以通过这个实例来操作 Copilot。

js
{
  copilotConfig: {
    config: {...}, // 大模型配置信息
    loading: false, // 是否正在加载
    onVerifyConfig: () => {}, // 验证配置信息
    onChangeProvider: () => {}, // 切换模型提供商
    // ...
  },
  context: {
    api: {...},
    endpoints: {...},
    // Copilot 状态
    isLoading: false,
    // 当前会话列表,数据格式参照
    // https://api.hengshi.com/conversation.html#conversation
    conversations: [],
    setConversations: () => {},
    currentConversationId: null,
    setCurrentConversationId: () => {},
    runningChatUid: null,
    setRunningChatUid: () => {},
    setChat: (id, chatUid, chat) => {},
    // 获取历史对话列表
    onFetchConversations: (offset = 0, limit = 10) => {},
    // 创建对话
    onCreateConversation: prompt => {},
    onFetchConversation: id => {},
    onClearConversations: () => {},
    onFetchPreviousConversations: () => {},
    // 创建问答
    onCreateChat: body => {},
    onDeleteChat: (id, chatUid) => {},
    onFetchSuggestions: refresh => {},
    onFetchFavorites: () => {},
    onCancelChatFavorite: (id, chatUid) => {},
  },
}

2. 通过 JS 调用 HTTP API

在上述 copilot 实例中,api 对象中提供了所有 HTTP API 的调用方法,你可以通过调用这些方法来与后端进行交互,如:

js
// 1. 创建对话
const { body: { data: conversation }} = await copilot.api.requestCreateConversation({
  body: {
    title: '这里是本次对话的标题',
  },
}).value;
/**
conversation = {
  "id": 135,
  "title": "这里是本次对话的标题",
  "createdBy": 268,
  "createdAt": "2024-09-14 15:47:23",
  "updatedBy": 268,
  "updatedAt": "2024-09-14 15:47:23"
}
*/
// 2. 创建问答
const { body: { data: chat }} = await copilot.api.requestCreateChat({
  id: conversation.id,
  body: {
    prompt: '哪个导演拍的电影票房最高', // 这里是用户的问题
    userConfig: { // 参照上述 copilotConfig 中的 userConfig
      dataAppId: 129150,
      datasetId: 1,
    }
  },
  qs: {
    sync: true, // 是否同步执行
    timeout: 1000 * 60, // 超时时间,单位为毫秒
  },
}).value;
/**
chat = {
    "conversationId": 135,
    "prompt": "哪个导演拍的电影票房最高",
    "answer": "克里斯托弗·诺兰导演拍的电影票房最高。",
    "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": "哪个导演拍的电影票房最高",
    "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_产品表}}.{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_产品表}}.{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
            ]
        }
    ]
}
*/

在衡石系统内的仪表盘里集成 Copilot

在衡石系统中调用 Copilot 方式与上述 HTML 方式是类似的。

首先,在全局 JS 文件中通过代码加载 Copilot SDK:

js
// 1. 保持 copilot sdk 与系统的 store 数据同步
window.__INITIAL_STATE__ = window._hs_store_.getState();
// 2. 引入 copilot sdk 代码
var script = document.createElement('script');
script.src = 'https://develop.hengshi.org/assets/hengshi-copilot@<version>.js';
script.async = true;
script.onload = function() {
  // 3. 重置 sdk base url
  window.__hs_sdk_base_url__ = undefined;
  // 4. 创建 sdk 容器
  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. 赋值给自定义 sandbox 变量以便自定义 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);

然后,您可以在仪表盘中使用控件按钮来唤起 Copilot,在按钮控件的设置中添加点击事件,执行相应的 JS 代码。

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, // 设置对话框可关闭
    draggable: { // 设置对话框拖动功能,不需要拖动功能可以设置为 false 或不设置
      enable: true,
      minWidth: 440,
      minHeight: 440,
      position: {
        x: myJS.innerWidth - 480,
        y: 20,
      },
      size: {
        width: 440,
        height: myJS.innerHeight * 0.8,
      },
      // 设定记住拖动的位置和大小
      onDragStop: onDragStop,
      onResize: onResize,
    },
    // 设置对话数据来源
    userConfig: {
      dataAppId: 354, // 数据包 id
      datasetId: 26, // 数据集 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 {
    myJS.copilot = new myJS.Copilot(myJS.CopilotRoot, myJS.copilotConfig);
  }
}
if (myJS.copilot) {
  myJS.copilot.destroy();
  myJS.copilot = null;
} else {
  launchCopilot();
}

衡石分析平台使用手册