@coze/uniapp-api
Version:
Official Coze UniApp SDK for seamless AI integration into your applications | 扣子官方 UniApp SDK,助您轻松集成 AI 能力到应用中
299 lines (298 loc) • 11.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseWsChatClient = void 0;
const api_1 = require("@coze/api");
const pcm_stream_player_1 = __importDefault(require("../pcm-stream-player"));
const api_2 = require("../../api");
const event_names_1 = require("./event-names");
/**
* Base class for WebSocket-based chat client for WeChat Mini Program
*/
class BaseWsChatClient {
constructor(config) {
var _a;
Object.defineProperty(this, "ws", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "listeners", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "wavStreamPlayer", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "trackId", {
enumerable: true,
configurable: true,
writable: true,
value: 'default'
});
Object.defineProperty(this, "api", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "audioDeltaList", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Handle incoming audio messages from the server
*/
Object.defineProperty(this, "handleAudioMessage", {
enumerable: true,
configurable: true,
writable: true,
value: () => {
if (this.audioDeltaList.length === 0) {
return;
}
const message = this.audioDeltaList[0];
try {
this.wavStreamPlayer.addBase64PCM(message, this.trackId);
this.audioDeltaList.shift();
if (this.audioDeltaList.length > 0) {
this.handleAudioMessage();
}
}
catch (error) {
this.warn('wavStreamPlayer error', error);
}
}
});
this.api = new api_2.CozeAPI(Object.assign(Object.assign({ baseWsURL: api_1.COZE_CN_BASE_WS_URL }, config), { debug: false }));
this.config = config;
this.wavStreamPlayer = new pcm_stream_player_1.default({
sampleRate: 8000,
defaultFormat: 'g711a',
volume: (_a = this.config.playbackVolumeDefault) !== null && _a !== void 0 ? _a : 1.0,
});
}
/**
* Initialize WebSocket connection and setup event handlers
* @returns {Promise<WebSocketAPI>} The WebSocket API instance
*/
init() {
return __awaiter(this, void 0, void 0, function* () {
if (this.ws) {
return this.ws;
}
const ws = yield this.api.websockets.chat.create({
bot_id: this.config.botId,
workflow_id: this.config.workflowId,
}, this.config.websocketOptions);
this.ws = ws;
// 标记 websocket 是否已 resolve or reject
let isResolved = false;
this.trackId = `my-track-id-${Date.now()}`;
return new Promise((resolve, reject) => {
ws.onopen = () => {
this.log('ws open');
};
ws.onmessage = data => {
// Trigger all registered event listeners
this.emit(`server.${data.event_type}`, data);
switch (data.event_type) {
case api_1.WebsocketsEventType.ERROR:
this.closeWs();
if (isResolved) {
return;
}
isResolved = true;
reject(new api_1.APIError(data.data.code, {
code: data.data.code,
msg: data.data.msg,
detail: data.detail,
}, undefined, undefined));
return;
case api_1.WebsocketsEventType.CHAT_CREATED:
resolve(ws);
isResolved = true;
break;
case api_1.WebsocketsEventType.CONVERSATION_AUDIO_DELTA:
this.audioDeltaList.push(data.data.content);
if (this.audioDeltaList.length === 1) {
this.handleAudioMessage();
}
break;
case api_1.WebsocketsEventType.INPUT_AUDIO_BUFFER_SPEECH_STARTED:
this.clear();
break;
case api_1.WebsocketsEventType.CONVERSATION_AUDIO_COMPLETED:
// this.closeWs();
break;
case api_1.WebsocketsEventType.CONVERSATION_CHAT_CANCELED:
this.clear();
break;
default:
break;
}
};
ws.onerror = (error, event) => {
this.warn('ws error', error, event);
this.emit(`server.${api_1.WebsocketsEventType.ERROR}`, error);
this.closeWs();
if (isResolved) {
return;
}
isResolved = true;
reject(new api_1.APIError(error.data.code, error, error.data.msg, undefined));
};
ws.onclose = () => {
this.log('ws close');
};
});
});
}
/**
* Send a message to the chat server
* @param {CreateChatWsReq} data - The message data to send
*/
sendMessage(data) {
var _a;
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(data);
this.log('sendMessage', data);
}
/**
* Send a text message to the chat server
* @param {string} text - The text message to send
*/
sendTextMessage(text) {
this.sendMessage({
id: Date.now().toString(),
event_type: api_1.WebsocketsEventType.CONVERSATION_MESSAGE_CREATE,
data: {
role: api_1.RoleType.User,
content_type: 'text',
content: text,
},
});
}
/**
* Add event listener(s)
* @param {string | string[]} event - Event name or array of event names
* @param {WsChatCallbackHandler} callback - Event callback function
*/
on(event, callback) {
const events = Array.isArray(event) ? event : [event];
events.forEach(eventName => {
var _a;
if (!this.listeners.has(eventName)) {
this.listeners.set(eventName, new Set());
}
(_a = this.listeners.get(eventName)) === null || _a === void 0 ? void 0 : _a.add(callback);
this.log('on', eventName);
});
}
/**
* Remove event listener(s)
* @param {string | string[]} event - Event name or array of event names
* @param {WsChatCallbackHandler} callback - Event callback function to remove
*/
off(event, callback) {
const events = Array.isArray(event) ? event : [event];
events.forEach(eventName => {
var _a;
(_a = this.listeners.get(eventName)) === null || _a === void 0 ? void 0 : _a.delete(callback);
});
}
/**
* Close the WebSocket connection
*/
closeWs() {
var _a, _b;
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === 1) {
(_b = this.ws) === null || _b === void 0 ? void 0 : _b.close();
}
this.ws = null;
}
/**
* Clear audio buffers and interrupt current playback
*/
clear() {
this.log('clear');
this.audioDeltaList.length = 0;
this.wavStreamPlayer.interrupt();
this.trackId = `my-track-id-${Date.now()}`;
}
/**
* Set playback volume level
* @param {number} volume - Volume level from 0.0 (muted) to 1.0 (full volume)
*/
setPlaybackVolume(volume) {
this.wavStreamPlayer.setVolume(volume);
}
/**
* Get current playback volume level
* @returns {number} Current volume level from 0.0 (muted) to 1.0 (full volume)
*/
getPlaybackVolume() {
return this.wavStreamPlayer.getVolume();
}
/**
* Emit an event to all registered listeners
* @param {string} eventName - The name of the event
* @param {WsChatEventData} event - The event data
*/
emit(eventName, event) {
var _a, _b;
(_a = this.listeners
.get(eventName)) === null || _a === void 0 ? void 0 : _a.forEach(callback => callback(eventName, event));
(_b = this.listeners
.get(event_names_1.WsChatEventNames.ALL)) === null || _b === void 0 ? void 0 : _b.forEach(callback => callback(eventName, event));
this.log('dispatch', eventName, event);
}
/**
* Log a message if debug is enabled
* @param {...any} args - The arguments to log
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
log(...args) {
if (this.config.debug) {
console.log('[WsChatClient]', ...args);
}
return true;
}
/**
* Log a warning message if debug is enabled
* @param {...any} args - The arguments to log
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
warn(...args) {
if (this.config.debug) {
console.warn('[WsChatClient]', ...args);
}
return true;
}
}
exports.BaseWsChatClient = BaseWsChatClient;
exports.default = BaseWsChatClient;