[4] [Coze API][小程序] BOT 对话:流式

扣子专业版扣子

需要熟悉 小程序开发

  1. 界面效果

picture.image

  1. 配置项

botId: '你自己的Coze BOTID',

const accessToken = '你自己的 个人 Coze token'; (2个地方配置)

  1. 界面代码

doubao-chat.js

Page({
    data: {
      messages: [],
      inputValue: '',
      conversationId: null,
      botId: '你自己的Coze BOTID', 
      userId: '', // 初始化 userId
    },
  
    onLoad() {
      // 在页面加载时生成唯一的 user_id
      this.setData({
        userId: this.generateUUID() // 使用自定义的 UUID 生成函数
      });
    },
  
    generateUUID() {
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0;
        const v = c === 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
      });
    },
  
    onInput(e) {
      this.setData({
        inputValue: e.detail.value
      });
    },
  
    // 发送消息并发起对话
    sendMessage() {
      const message = this.data.inputValue;
      if (!message) return;
  
      // 检查 conversationId 是否为 null
      if (!this.data.conversationId) {
        // 如果是 null,先创建一个新的会话
        this.createEmptyConversation(() => {
          this.sendMessage(); // 在会话创建成功后再次调用 sendMessage
        });
        return;
      }
  
      // 添加用户消息到消息列表
      this.setData({
        messages: [...this.data.messages, { role: 'user', content: message }],
        inputValue: ''
      });
  
      const accessToken = '你自己的 Coze token'; // 确保使用正确的访问令牌
  
      const requestTask = wx.request({
        url: 'https://api.coze.cn/v3/chat', // 确保 URL 正确
        method: 'POST',
        enableChunked: true,
        header: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        },
        data: {
          conversation_id: this.data.conversationId, // 可选参数
          bot_id: this.data.botId.toString(), // 确保 bot_id 是字符串
          user_id: this.data.userId,
          stream: true, // 启用流式返回
          auto_save_history: true,
          additional_messages: [
            {
              role: 'user',
              content: message,
              content_type: 'text'
            }
          ]
        },
        success: (res) => {
          console.log('请求完成:', res);
        },
        fail: (err) => {
          console.error('请求失败:', err);
        }
      });
  
      requestTask.onChunkReceived((res) => {
        const chunk = this.utf8ArrayBufferToString(res.data);
        console.log('Chunk received:', chunk);
        this.handleStreamResponse(chunk);
      });
    },
  
    // 将 UTF-8 编码的 ArrayBuffer 转换为字符串
    utf8ArrayBufferToString(buffer) {
      let result = '';
      const bytes = new Uint8Array(buffer);
      let i = 0;
  
      while (i < bytes.length) {
        const byte1 = bytes[i++];
  
        if (byte1 <= 0x7F) {
          // 1-byte sequence
          result += String.fromCharCode(byte1);
        } else if (byte1 <= 0xDF) {
          // 2-byte sequence
          const byte2 = bytes[i++];
          result += String.fromCharCode(((byte1 & 0x1F) << 6) | (byte2 & 0x3F));
        } else if (byte1 <= 0xEF) {
          // 3-byte sequence
          const byte2 = bytes[i++];
          const byte3 = bytes[i++];
          result += String.fromCharCode(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F));
        } else if (byte1 <= 0xF7) {
          // 4-byte sequence
          const byte2 = bytes[i++];
          const byte3 = bytes[i++];
          const byte4 = bytes[i++];
          const codepoint = ((byte1 & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F);
          result += String.fromCodePoint(codepoint);
        }
      }
  
      return result;
    },
  
    // 处理流式响应
    handleStreamResponse(chunk) {
      // 解析流式响应数据
      const events = chunk.split('\n\n');
      events.forEach(event => {
        if (event.trim()) {
          const [eventType, eventData] = event.split('\n');
          if (eventType.includes('event:conversation.message.delta')) {
            const messageData = JSON.parse(eventData.replace('data:', '').trim());
            if (messageData.role === 'assistant') {
              this.setData({
                messages: [...this.data.messages, { role: 'assistant', content: messageData.content }]
              });
            }
          }
        }
      });
    },
  
    // 创建空对话
    createEmptyConversation(callback) {
      const accessToken = '你自己的 Coze token'; // 确保使用正确的访问令牌
  
      wx.request({
        url: 'https://api.coze.cn/v1/conversation/create', // 假设的创建会话 API 端点
        method: 'POST',
        header: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        },
        data: {
          bot_id: this.data.botId.toString(), // 确保 bot_id 是字符串
          user_id: this.data.userId
        },
        success: (res) => {
          if (res.data.code === 0) {
            const conversationId = res.data.data.id;
            this.setData({
              conversationId,
              messages: [...this.data.messages, { role: 'system', content: `空会话已创建,ID: ${conversationId}` }]
            });
            if (callback) callback(); // 执行回调函数
          } else {
            console.error('创建空会话失败:', res.data.msg);
          }
        },
        fail: (err) => {
          console.error('请求失败:', err);
        }
      });
    }
  });

doubao-chat.json

{
  "usingComponents": {}
}

doubao-chat.wxml

<view class="container">
  <view class="chat-window">
    <block wx:for="{{messages}}" wx:key="index">
      <view class="message {{item.role}}">
        <text>{{item.content}}</text>
      </view>
    </block>
  </view>
  <input class="input" placeholder="输入消息..." bindinput="onInput" value="{{inputValue}}" />
  <view class="button-group">
    <button class="create-button" bindtap="createEmptyConversation">创建空对话</button>
    <button class="send-button" bindtap="sendMessage">发送</button>
  </view>
</view>

doubao-chat.wxss

.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.chat-window {
  flex: 1;
  overflow-y: scroll;
  padding: 10px;
}

.message {
  margin: 10px;
  padding: 10px;
  border-radius: 5px;
}

.user {
  background-color: #d1e7dd;
  align-self: flex-end;
}

.assistant {
  background-color: #f8d7da;
  align-self: flex-start;
}

.system {
  background-color: #e2e3e5;
  align-self: center;
}

.input {
  border: 1px solid #ccc;
  padding: 10px;
  width: calc(100% - 20px);
  margin: 10px;
}

.button-group {
  display: flex;
  justify-content: space-between;
  padding: 10px;
}

.create-button, .send-button {
  flex: 1;
  margin: 5px;
  padding: 10px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 5px;
}
0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论