npm package: @qapm/sse-monitor
Description: qapm js sse monitor client
Author: smithdeng
Latest version: 1.1.4

# RUM大模型体验分析web-sdk使用文档

sdk版本：1.1.4

## 1、接入参数说明

| 字段名                  | 是否必填 | 类型          | 说明                                                                           | 默认值                  |
| ----------------------- | -------- | ------------- | ------------------------------------------------------------------------------ | ----------------------- |
| ​**app_key**​           | 是       | string        | 上报的ID，需在RUM产品创建并获取，https://console.cloud.tencent.com/monitor/app |                         |
| ​**llm_ver**​           | 是       | string        | 大模型版本号，可通过 `setLLMVer` 动态设置                                      |                         |
| ​**include_url_reg**​   | 是       | array<regex>  | 需要监控的URL正则表达式数组                                                    |                         |
| ​**use_fetch**​         | 否       | boolean       | 是否监控 `window.fetch` 的流式请求                                             | true                    |
| ​**use_xhr**​           | 否       | boolean       | 是否监控 `window.XMLHttpRequest` 的流式请求                                    | true                    |
| ​**user_event_source**​ | 否       | boolean       | 是否监控 `window.EventSource` | true                    |
| ​**user_id**​           | 否       | string        | 用户ID，可通过 `setUserID` 动态设置                                            |                         |
| ​**qapm_base_url**​     | 否       | string        | 性能数据上报地址                                                               | https://app.rumt-zh.com |
| ​**version**​           | 否       | string        | 前端版本号                                                                     |                         |
| ​**host_ip**​           | 否       | string        | 接口服务端IP，可通过 `setHostIP` 动态设置                                      | UNSUPPORTED             |
| ​**log_level**​         | 否       | string (枚举) | 日志级别：none（关闭）、error、warn、info、debug、log                          | none                    |
| ​**onMessage**​         | 否       | function      | 每次收到消息后的回调函数                                                       | null                    |
| ​**onAfterRequest**​    | 否       | function      | sse请求结束后的回调函数                                                        | null                    |

## 2、接入方式

### （1）使用script标签的方式接入

```html
    <script src="https://qapmcdn-1253358381.cos.ap-guangzhou.myqcloud.com/web-sdk/latest/sse.qapm.js"></script>
    <script>
        const option = {
            app_key: "5f68970f-10248",
            llm_ver: "deepseek-7b",
            include_url_reg: [/conversation/, /https\:\/\/fibona\.woa\.com\/v1\/component\/session\/\d+$/],

            use_fetch: true,
            use_xhr: true,
            use_event_source: true,
            user_id: "smithabc",
            qapm_base_url: "https://app.rumt-zh.com",
            version: "1.0.0",
            host_ip: "10.2.3.40"
        }
        window.QAPM.initLLMMonitor(option)
    </script>
```

### （2）npm安装后，使用import接入

安装依赖：

```bash
npm install @qapm/sse-monitor
```

引入：

```javascript
import {
    initLLMMonitor
} from "@qapm/sse-monitor"
const option = {
    app_key: "5f68970f-10248",
    llm_ver: "deepseek-7b",
    include_url_reg: [/conversation/, /https\:\/\/fibona\.woa\.com\/v1\/component\/session\/\d+$/],

    use_fetch: true,
    use_xhr: true,
    use_event_source: true,
    user_id: "smithabc",
    qapm_base_url: "https://app.rumt-zh.com",
    version: "1.0.0",
    host_ip: "10.2.3.40"
}
initLLMMonitor(option)
```

## 3、可用接口

### （1）设置用户ID

```javascript
import {
    setUserID
} from "@qapm/sse-monitor";
setUserID("OA::smithdeng");
或者
window.QAPM.setUserID("OA::smithdeng");
```

### （2）设置模型版本

```javascript
import {
    setLLMVer
} from "@qapm/sse-monitor";
setLLMVer("hunyuan-7b");
或者
window.QAPM.setLLMVer("hunyuan-7b");
```

### （3）设置模型接口的服务端IP

```javascript
import {
    setHostIP
} from "@qapm/sse-monitor";
setHostIP("10.2.3.40");
或者
window.QAPM.setHostIP("10.2.3.40");
```

### （4）自定义上报

使用此接口可以自定义上报数据, sseEvent的数据结构请参考 ` `  ` 数据结构 `  ` ` 章节。

```javascript
import {
    perfEvent
} from "@qapm/sse-monitor";
const sseEvent = {
    category: "PERF_NET_SSE",
    tags: {
        d1: "https://fibona.woa.com/v1/component/session/1234567890",
        d2: "fibona.woa.com",
        d3: "deepseek-7b",
        d4: "false",
        d5: "false",
        d12: "10.2.3.40",
        d15: "true",
        d16: "true",
        d20: "user_id::smithabc",
        d21: "talk_id::1234567890",
        d22: "ask_content::你好",
        d23: "answer_content::你好",
    },
    values: {
        v1: 1,
        v2: 1,
        v3: 0,
        v4: 0,
        v5: 0,
        v6: 0,
        v7: 1
    }
}
perfEvent(sseEvent, true);
或者
window.QAPM.perfEvent(sseEvent, true);
```

## 4、回调函数

### （1）每次收到消息后的回调函数，onMessage

在初始化adk的时候，可以传递一个onMessage的回调函数，该回调函数会在每次收到消息后被调用，参数如下：
| 参数名           | 类型     | 说明                |
| ---------------- | -------- | ------------------- |
| ​**cookie**​     | string   | 返回document.cookie |
| ​**reqHeader**​  | Object   | 请求的header        |
| ​**reqBody**​    | Object   | 请求的body          |
| ​**respHeader**​ | Object   | 响应的header        |
| ​**respBody**​   | Object   | 响应的body          |
| ​**message**​    | string   | 每次收到的消息      |
| ​**sseOption**​  | Object   | 初始化sdk的参数     |
| ​**sseEvent**​   | SSEEvent | 采集的sse性能数据   |

注意：onMessage必须要返回sseEvent。

```javascript
import {
    initLLMMonitor
} from "@qapm/sse-monitor"
const option = {
    app_key: "5f68970f-10248",
    llm_ver: "deepseek-7b",
    include_url_reg: [/conversation/, /https\:\/\/fibona\.woa\.com\/v1\/component\/session\/\d+$/],

    use_fetch: true,
    use_xhr: true,
    use_event_source: true,
    user_id: "smithabc",
    qapm_base_url: "https://app.rumt-zh.com",
    version: "1.0.0",
    host_ip: "10.2.3.40",
    onMessage: (cookie, reqHeader, reqBody, respHeader, respBody, message, sseOption, sseEvent) => {
        const now = Date.now();
        try {
            console.log('onMessage', message)

            // 开放自定义日志上下文字段，v20-v29
            if (message.includes("start") && !sseEvent.values.v20) {
                sseEvent.values.v20 = now; // 首包时间戳
            }

            if (message.includes("think_start") && !sseEvent.values.v21) {
                sseEvent.values.v21 = now; // 开启深度思考，思考的开始时间戳
            }

            if (message.includes("think_end") && !sseEvent.values.v22) {
                sseEvent.values.v22 = now; // 开启深度思考，思考的结束时间戳
            }

            if (message.includes("content_start") && !sseEvent.values.v23) {
                sseEvent.values.v23 = now; // 正文的开始时间戳
            }

            if (message.includes("content_end") && !sseEvent.values.v24) {
                sseEvent.values.v24 = now; // 正文的结束时间戳
            }

        } catch (e) {
            console.log('onMessage error', e)
        }

        return sseEvent;
    }
}
initLLMMonitor(option)
```

### （2）sse请求结束后的回调函数，onAfterRequest

在初始化adk的时候，可以传递一个onAfterRequest的回调函数，该回调函数会在每次请求结束后被调用，参数如下：

| 参数名           | 类型     | 说明                |
| ---------------- | -------- | ------------------- |
| ​**cookie**​     | string   | 返回document.cookie |
| ​**reqHeader**​  | Object   | 请求的header        |
| ​**reqBody**​    | Object   | 请求的body          |
| ​**respHeader**​ | Object   | 响应的header        |
| ​**respBody**​   | Object   | 响应的body          |
| ​**sseOption**​  | Object   | 初始化sdk的参数     |
| ​**sseEvent**​   | SSEEvent | 采集的sse性能数据   |

onAfterRequest必须要有返回值，如果在onAfterRequest中返回了sseEvent，则会覆盖原有的sseEvent，如果返回了false，则不会上报该sseEvent。

其中sseEvent为用户暴露如下字段：
| 参数名                       | 类型   | 说明       |
| ---------------------------- | ------ | ---------- |
| ​**sseEvent.tags.d15-d19**​  | string | 指标聚合   |
| ​**sseEvent.tags.d20-d29**​  | string | 日志上下文 |
| ​**sseEvent.values.v1-v29**​ | number | 日志上下文 |

```javascript
import {
    initLLMMonitor
} from "@qapm/sse-monitor"
const option = {
    app_key: "5f68970f-10248",
    llm_ver: "deepseek-7b",
    include_url_reg: [/conversation/, /https\:\/\/fibona\.woa\.com\/v1\/component\/session\/\d+$/],

    use_fetch: true,
    use_xhr: true,
    use_event_source: true,
    user_id: "smithabc",
    qapm_base_url: "https://app.rumt-zh.com",
    version: "1.0.0",
    host_ip: "10.2.3.40",
    onAfterRequest: (cookie, reqHeader, reqBody, respHeader, respBody, sseOption, sseEvent) => {
        try {
            console.log('onAfterRequest', cookie, reqHeader, reqBody, respHeader, respBody, sseOption, sseEvent)

            const reqJson = reqBody;

            // 开放自定义指标聚合字段，d15-d19 
            sseEvent.tags.d15 = reqJson.openDeepThink || "false"; // 是否开启了深度思考
            sseEvent.tags.d16 = reqJson.openWebSearch || "false"; // 是否开启了联网搜索

            // 开放自定义日志上下文字段，d20-d29
            sseEvent.tags.d20 = "talk_id::" + reqJson.talkId || ""; // 对话ID
            sseEvent.tags.d21 = "ask_content::" + reqJson.askContent || ""; // 对话的问题
            sseEvent.tags.d22 = "answer_content::" + respBody || ""; // 回复的正文内容

            // 上报到灯塔
            // uploadToLightHouse(sseEvent);

        } catch (e) {
            console.log('onAfterRequest error', e)
        }

        return sseEvent;
    }
}
initLLMMonitor(option)
```

## 5、数据结构

### (1)sseEvent的数据结构

```javascript
  interface SSEEvent {
      /** 事件分类（固定为性能监控类） */
      category: "PERF_NET_SSE";
      tags: {
          /** 请求 URL */
          d1: string;
          /** 请求 HOST */
          d2: string;
          /** 模型版本 */
          d3: string;
          /** 是否错误 */
          d4: "true" | "false";
          /** 是否卡死 */
          d5: "true" | "false";
          /** 主机 IP（不支持时显示 UNSUPPORTED） */
          d12: string | "UNSUPPORTED";

          /** 开放字段d15-d19，用于指标聚合*/
          d15 ?: string;
          d16 ?: string;
          d17 ?: string;
          d18 ?: string;
          d19 ?: string;

          /** 开放字段d20-d29，用于日志上下文展示*/
          d20 ?: string;
          d21 ?: string;
          d22 ?: string;
          d23 ?: string;
          d24 ?: string;
          d25 ?: string;
          d26 ?: string;
          d27 ?: string;
          d28 ?: string;
          d29 ?: string;
      };
      values: {
          /** 总token数 */
          v1: number;
          /** 消息耗时分段统计 */
          v2: number; // [0,500)ms 的消息数量
          v3: number; // [500,1000)ms 的消息数量
          v4: number; // [1000,3000)ms 的消息数量
          v5: number; // [3000,10000)ms 的消息数量
          v6: number; // [10000,60000)ms 的消息数量

          /** 耗时累计统计 */
          v7: number; // 连接打开时间戳
          v8: number; // 首条消息时间戳
          v9: number; // 最后消息时间戳
          v10: number; // 接收到response header 的时间戳，仅xhr支持

          /** 耗时累计统计 */
          v12: number; // [0,500)ms 总耗时
          v13: number; // [500,1000)ms 总耗时
          v14: number; // [1000,3000)ms 总耗时
          v15: number; // [3000,10000)ms 总耗时
          v16: number; // [10000,60000)ms 总耗时

          /** 数据传输量 */
          v17: number; // 请求数据总量（字节）
          v18: number; // 响应数据总量（字节）

          /** 开放字段v20-v29，用于日志上下文展示*/
          v20 ?: number;
          v21 ?: number;
          v22 ?: number;
          v23 ?: number;
          v24 ?: number;
          v25 ?: number;
          v26 ?: number;
          v27 ?: number;
          v28 ?: number;
          v29 ?: number;

          /** 消息耗时分段统计 */
          v30 : number; // [60000ms,+∞)ms 的消息数量

          /** 耗时累计统计 */
          v31 : number; // [60000ms,+∞)ms 总耗时

      };
  }
```

### (2)初始化sdk的参数的数据结构

```javascript
  interface LLMMonitorOptions {
      app_key: string;
      llm_ver: string;
      include_url_reg ? : RegExp[];
      use_fetch ? : boolean;
      use_xhr ? : boolean;
      use_event_source ? : boolean;
      user_id ? : string;
      qapm_base_url ? : string;
      version ? : string;
      host_ip ? : string;
      log_level ? : string;
      onMessage ? : (
          cookie: string,
          reqHeader: Object,
          reqBody: Object,
          respHeader: Object,
          respBody: Object,
          message: string,
          sseOption: LLMMonitorOptions,
          sseEvent: SSEEvent
      ) => Object;
      onAfterRequest ? : (
          cookie: string,
          reqHeader: Object,
          reqBody: Object,
          respHeader: Object,
          respBody: Object,
          sseOption: LLMMonitorOptions,
          sseEvent: SSEEvent
      ) => Object;
  }
```