# 安装依赖

## npm

`npm install @glodon-aiot/bot-client-ui`

## yarn

`yarn add @glodon-aiot/bot-client-ui`

安装后即可使用，**无需**再单独安装 `react`、`react-dom`、`antd` 等依赖（默认入口已内置于构建产物中）。

无论宿主是 React、Vue 还是原生 JS，**接入方式完全一致**：导入类并 `new` 实例化，组件会渲染到 `getContainer()` 返回的 DOM 节点中，与宿主框架无关。默认入口会打包底层依赖（含 React），适合开箱即用。

若你的宿主工程对 bundle 体积敏感，并且已统一管理 `@glodon-aiot/agent-cli-ui` / React 依赖，可使用去重版入口：

```javascript
import BotClientUI from '@glodon-aiot/bot-client-ui/external';
```

## 入口选型建议

- 默认推荐：`@glodon-aiot/bot-client-ui`
  - 适合 Vue / 原生 JS / 多技术栈宿主，开箱即用、零额外构建配置。
  - 依赖已内置，宿主不需要单独处理 `react` / `react-dom` 解析。
- 体积优先：`@glodon-aiot/bot-client-ui/external`
  - 适合大型 React 宿主（或微前端主应用）希望与现有依赖统一去重。
  - 该入口会外置 `@glodon-aiot/agent-cli-ui`，最终体积取决于宿主的依赖管理与打包策略。

## 从 cdn 引入 cjs 文件

一些项目无法处理 esm，请使用以下方式引入依赖。BotClientUI 将被注册在 window 对象上。
`<script src="https://cv-cdn.obs.cn-north-4.myhuaweicloud.com/glodon/libs/bot-client-ui/3.3.0/bot-client-ui.umd.cjs"></script>`

# 快速开始

## 初始化

```javascript
import BotClientUI from '@glodon-aiot/bot-client-ui';

new BotClientUI({
  token: 'aiot_resource_token_goes_here',
  getContainer: () => document.getElementById('client-ui'),
  errorHandlers: {
    token: (error) => {
      console.error(error);
    },
  },
});
```

## 在线预览：

- 对话型应用 Demo [https://code.juejin.cn/pen/7494170871938940966](https://code.juejin.cn/pen/7494170871938940966)
- Agent Demo [https://code.juejin.cn/pen/7372449505322467363](https://code.juejin.cn/pen/7372449505322467363)

# BotClientUI 构造配置

## 通用配置

| 名称           | 类型                          | 必填    | 默认值                                   | 说明                                                                                                                                                                | 适用应用类型               | 支持版本   |
| -------------- | ----------------------------- | ------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | ---------- |
| token          | string                        | 是      | -                                        | aiot 行业 AI 平台的 AI 应用的 resource token。详情查看[认证说明](https://glodon-cv-help.yuque.com/lzh2bp/eszzb5/evk1gvh3pqaexe74?singleDoc#%20《认证说明》)。       | 全部                       |            |
| apiUrl         | string                        | 否      | `https://copilot.glodon.com/api/cvforce` | 拼接规则：`${origin}/api/cvforce`，其中`origin`是部署服务的源地址。                                                                                                 | 全部                       | 3.0.8      |
| getContainer   | () => Element                 | 否      | () => document.body                      | 容器元素获取方法，默认在 document.body 中渲染                                                                                                                       | 全部                       |            |
| sessionId      | string                        | 否      | -                                        | 默认加载的会话 ID                                                                                                                                                   | Agent/对话                 |            |
| mode           | `'float' \| 'inlay'`          | 否      | 'float'                                  | 悬浮按钮模式，或者嵌入模式                                                                                                                                          | Agent/对话                 |            |
| header         | `Element \| string \| boolean` | 否     | -                                        | 应用名称                                                                                                                                                            | Agent/对话                 |            |
| footer         | `Element \| string \| boolean` | 否     | -                                        | 页脚内容                                                                                                                                                            | Agent/对话                 |            |
| sider          | boolean                       | 否      |                                          | inlay 模式可用。 是否展示左侧会话列表                                                                                                                               | Agent/对话                 |            |
| icon           | string                        | 否      |                                          | 影响 greeting 和 triggerButton 的 icon                                                                                                                              | Agent/对话                 | 3.3.1      |
| size           | [SizeConfig](#GbxmJ)          | 否      | {width: 450}                             | float 模式下：窗体大小                                                                                                                                              | 全部                       |            |
| open           | boolean                       | 否      | false                                    | float 模式下：默认打开状态                                                                                                                                          | Agent/对话                 |            |
| triggerButton  | [TriggerButtonConfig](#djhoc) | 否      | {visible: true}                          | triggerButton 设置                                                                                                                                                  | Agent/对话                 |            |
| features       | [Features](#uIpxC)            | 否      | FEATURE_DEFAULT                          | 功能配置                                                                                                                                                            | Agent/对话                 |            |
| debug          | boolean                       | 否      | false                                    | 打开 debug 模式时：<br/>1. 控制 log 输出。 2. promptValues 变为非必填项； 3. Agent 类型应用下，控制 ws 连接是否开启，debug 模式，由于收到权限限制，可能会配置失败。 | Agent/对话                 |            |
| eventListeners | [EventListeners](#IhvdK)      |         |                                          | 事件监听器配置                                                                                                                                                      | Agent/对话                 |            |
| errorHandlers  | [ErrorHandlers](#VTONU)       | 否      | -                                        | errorHandlers.token 是 token 验证失败后的处理器; errorHandlers.promptValues 非 debug 模式下，检测空值                                                               | 全部                       |            |

## 只适用于 Agent 应用的配置

| 名称    | 类型          | 必填 | 默认值 | 说明                                                                                                              |
| ------- | ------------- | ---- | ------ | ----------------------------------------------------------------------------------------------------------------- |
| plugins | PluginsConfig | 否   | -      | Agent 应用适用。<br/>插件列表，`pluginCode`与`functionCode`要与平台中注册的插件标识和 API 名称保持一致            |
| mcp     | McpConfig     | 否   | -      | Agent 应用适用。<br/>这里注册的 MCP 作用到每个会话中。<br/>如果希望特定会话才可注册 MCP，则在会话加载后动态注册。 |

# BotClientUI 实例 API

| 名称           | 类型                                         | 说明               |
| -------------- | -------------------------------------------- | ------------------ |
| reload         | `() => void`                                 |                    |
| activeSession  | `Session`                                    | 当前活跃的会话信息 |
| input          | `(message:string) => void`                   | 填充输入框内容     |
| getMessageList | `(sessionId?: string) => Promise<Message[]>` | 获取历史对话列表   |

# 类型定义

## 基础类型

```typescript
interface SizeConfig {
  width?: number | string;
  height?: number | string;
  maxWidth?: number | string;
  maxHeight?: number | string;
  minWidth?: number | string;
  minHeight?: number | string;
}

interface TriggerButtonConfig {
  style: string;
  visible: boolean;
}

interface SessionInfo {
  id: string;
  name: string;
}

interface Message {
  messageId: string;
  content: string;
  role: ChatMessageRole;
  sessionId: string;
  reference: IReference[];
  searchReference: ISearchRefernce[];
  messageTime: string;
}

enum ChatMessageRole {
  Robot = 1,
  User = 2,
  System = 3,
}

interface IReference {
  text: string;
  docId: string;
  fileId: string;
  fileName: string;
  knowledgeId: string;
  knowledgeName: string;
  score: number;
  nickname: string;
  knowledgeSourceName: string;
}

interface ISearchRefernce {
  link: string;
  title: string;
  snippet: string;
}

interface SessionPrompt {
  id?: string;
  key: string;
  name?: string;
  value: string;
  status?: PromptStatus;
}

interface Knowledge {
  id: string;
}
```

## 功能配置

```typescript
interface Feature {
  // 问候
  greeting?:
    | boolean
    | {
        icon?: string;
        text?: string; // 问候语
        questions?: boolean; // 推荐问题
        layoutBreakpoints?: number[]; // 布局断点数组
      };
  // toolbar中的"会话历史记录"
  sessionList?: boolean;
  // toolbar中的"新会话"
  newSession?: boolean; // dialog only
  // toolbar中的"上传文件"
  fileUpload?: boolean; // dialog only
  // toolbar中的"上传图片"
  imageUpload?: boolean; // agent only
  // 问题气泡
  userMessageBox?: {
    // dialog only
    copy?: boolean;
    editAgain?: boolean;
  };
  // 回答气泡
  botMessageBox?: {
    // dialog only
    // 参考列表前置
    referenceFirst?: boolean;
    // 参考列表元素设置
    reference?: {
      // 是否可查看参考源文件
      file?: boolean;
      // 是否展开参考内容
      itemExpanded?: boolean;
    };
    // 是否展示相关问题
    relatedQuesions: boolean;
    // 回答气泡底部设置
    footer: {
      items: {
        componentName: 'AnswerAgain' | 'Copy' | 'Comments';
        position: 'left' | 'right';
      }[];
    };
  };
  prompts?: boolean;
  // toolbar中的网络访问
  connectNetwork?:
    | boolean
    | { visible: boolean; default?: boolean; disabled?: boolean };
  // 参考知识库设置
  knowledges?:
    | boolean
    | { visible: boolean; default: Knowledge[]; disabled?: boolean };
  // 用户指令变量设置
  promptVariables?: boolean | { visible: boolean; default: SessionPrompt[] };
  // 支持自定义问答内容markdown的超链接元素的点击事件
  markdown?: {
    componentProps?: {
      a?: {
        onClick?: (e: { nativeEvent: PointerEvent }) => void;
      };
    };
  };
}
```

## 事件与错误处理

```typescript
interface EventListeners {
  comment: (payload: {
    applicationId: string;
    sessionId: string;
    comment: Comment;
  }) => void;
  sessionload: (payload: {
    applicationId: string;
    sessionId: string;
    session: SessionInfo;
    sessionInstance: Session;
  }) => void;
  message: (payload: {
    applicationId: string;
    sessionId: string;
    message: Message;
  }) => void;
  beforemessagesend: (payload: {
    applicationId: string;
    sessionId: string;
    message: Message;
  }) => boolean | Partil<Message> | Promise<boolean | Partil<Message>>;
}

interface ErrorHandlers {
  token: (error: any) => void;
  promptValues?: (error: any, values: { key: string; value: string }[]) => void;
}
```

## Agent 应用配置

### 插件配置

```typescript
interface PluginsConfig {
  [pluginCode: string]: {
    [functionCode: string]: Function;
  };
}

interface McpServer {
  type: 'sse';
  url: string;
  headers?: Record<string, string>;
}

interface McpConfig {
  mcpServers: {
    [key: string]: McpServer;
  };
}
```

### MCP 配置

```typescript
interface McpServer {
  type: 'sse';
  url: string;
  headers?: Record<string, string>;
}

interface McpConfig {
  mcpServers: {
    [key: string]: McpServer;
  };
}
```

# Agent 类型应用的特别说明

## 客户端插件使用

由于该类型应用一般被当作某个软件的助手使用，需要主体软件在浏览器上注册一些方法，作为与浏览器交互的桥梁。

例如，有客户端`Plugin A`，注册插件如下代码。

其中，`pluginACode`是插件标识，可以在[行业 AI 平台](https://copilot.glodon.com/)的【插件管理 - 插件详情】中获得，不支持自定义。`functionCode`对应的是`API名称`，可自定义。

```javascript
window.pluginACode = {
  functionCode1: (params, cb) => {
    console.log(
      `模拟pluginA function1 执行\n执行入参\n${JSON.stringify(arguments)}`
    );
    setTimeout(() => {
      console.log(`模拟pluginA function1 执行完成`);
      cb({
        code: 0,
        message: 'function1运行成功',
        data: {
          a: 'a',
          b: 2,
        },
      });
    }, 5000);
  },
  functionCode2: (params, cb) => {
    console.log(
      `模拟pluginA function2 执行\n执行入参\n${JSON.stringify(arguments)}`
    );
    setTimeout(() => {
      console.log(`模拟pluginA function2 执行完成`);
      cb({
        code: 0,
        message: 'function2运行成功',
        data: {
          a: 'a',
          b: 2,
        },
      });
    }, 10000);
  },
};
```

创建 Agent 应用 UI 的时候，配置`plugins`属性。完整代码如下：

```javascript
import BotClientUI from '@glodon-aiot/bot-client-ui';

new BotClientUI({
  token: 'aiot_resource_token_goes_here',
  getContainer: () => document.getElementById('agent-ui'),
  mode: 'inlay',
  plugins: {
    pluginACode: window.pluginACode,
  },
  errorHandlers: {
    token: (error) => {
      console.error(error);
    },
  },
});
```

## MCP 注册

MCP Server 信息可以在[行业 AI 平台](https://copilot.glodon.com/)的【MCP 管理 - MCP 详情】中获得。

### 全局作用的 MCP 注册

全局作用的 MCP 注册的完整代码如下：

```javascript
import BotClientUI from '@glodon-aiot/bot-client-ui';

new BotClientUI({
  token: 'aiot_resource_token_goes_here',
  getContainer: () => document.getElementById('agent-ui'),
  mode: 'inlay',
  mcp: {
    mcpServers: {
      mcpServer_ID: {
        type: 'sse',
        url: 'https://copilot.glodon.com/proxy/XXXX/mcp/XXXX/mcp-mcpServer_ID/XXXX/sse',
      },
    },
  },
  errorHandlers: {
    token: (error) => {
      console.error(error);
    },
  },
});
```

### 为当前会话注册 MCP

为当前会话注册 MCP 的完整代码如下：

```javascript
import BotClientUI from '@glodon-aiot/bot-client-ui';

const botClientUI = new BotClientUI({
  token: 'aiot_resource_token_goes_here',
  getContainer: () => document.getElementById('agent-ui'),
  mode: 'inlay',
  errorHandlers: {
    token: (error) => {
      console.error(error);
    },
  },
  eventListeners: {
    sessionload: ({ sessionInstance }) => {
      if (sessionInstance) {
        // MCP注册方法
        const addMCP = () => {
          sessionInstance.addMcpServer({
            mcpServers: {
              mcpServer_ID: {
                type: 'sse',
                url: 'https://copilot.glodon.com/proxy/XXXX/mcp/XXXX/mcp-mcpServer_ID/XXXX/sse',
              },
            },
          });
        };

        // 只能在ready事件发生后才可以注册MCP
        sessionInstance.addEventListener('ready', addMCP);
        // session不活跃之后移除事件监听
        sessionInstance.addEventListener('inactivated', () =>
          sessionInstance.removeEventListener('ready', addMCP)
        );
      }
    },
  },
});
```
