UNPKG

@butlerbot/sdk

Version:

The official ButlerBot SDK

273 lines (272 loc) 11 kB
"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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Conversation = void 0; const eventsource_1 = require("eventsource"); const config_1 = require("../config"); const url_formatter_1 = require("../util/url_formatter"); const DEFAULT_CONVO_API_V = "v4"; class Conversation { constructor(config) { this.events = new Map(); this.convoId = config.convoId; const serverUrl = config.serverUrl || config_1.CONFIG.server; this.endpoints = { conversation: serverUrl + (config.convoPath || config.path || config_1.CONFIG.paths.conversation[config.chatApiV || DEFAULT_CONVO_API_V].base), history: serverUrl + (config.historyPath || config_1.CONFIG.paths.history.chat.v1.base), progressStream: serverUrl + (config.progressStreamPath || config_1.CONFIG.paths.progress.v4.stream), progress: serverUrl + (config.progressPath || config_1.CONFIG.paths.progress.v4.base), }; this.apiKey = config.apiKey; this.debug = config.debug || false; } // SETTERS /** * Sets the endpoint where the request is sent to, appended to server URL * @deprecated Use setConversationEndpoint() instead */ setEndpoint(endpoint) { this.endpoints.conversation = endpoint; return this; } /** Sets the conversation endpoint where the request is sent to, appended to server URL */ setConversationEndpoint(conversationEndpoint) { this.endpoints.conversation = conversationEndpoint; return this; } /** Sets the history endpoint where the quest is sent to, appended to server URL */ setHistoryEndpoint(historyEndpoint) { this.endpoints.history = historyEndpoint; return this; } /** Sets the progress stream endpoint where the request is sent to, appended to server URL */ setProgressStreamEndpoint(progressStreamEndpoint) { this.endpoints.progressStream = progressStreamEndpoint; return this; } /** Sets the progress endpoint where the request is sent to, appended to server URL */ setProgressEndpoint(progressEndpoint) { this.endpoints.progress = progressEndpoint; return this; } /** Sets the conversation ID, has to be an existing conversation ID, undefined otherwise */ setConvoId(convoId) { this.convoId = convoId; return this; } /** Sets the AI model used for the next interaction, e.g Claude-Sonnet, GPT-4 */ setModel(model) { this.options = Object.assign(Object.assign({}, this.options), { model }); return this; } /** Sets additional instructions for location-specific context */ setInstructions(instructions) { this.options = Object.assign(Object.assign({}, this.options), { instructions }); return this; } /** Sets the platform where the chat is occurring, used internally for logging - ignore in most contexts */ setPlatform(platform) { this.options = Object.assign(Object.assign({}, this.options), { platform }); return this; } /** Sets a custom personality configuration for the AI */ setPersonality(personality) { this.options = Object.assign(Object.assign({}, this.options), { personality }); return this; } // GETTERS /** Gets the current conversation ID */ getConvoId() { return this.convoId; } /** * Gets the current endpoint * @deprecated Use getConversationEndpoint() instead */ getEndpoint() { return this.endpoints.conversation; } /** Gets the current conversation endpoint */ getConversationEndpoint() { return this.endpoints.conversation; } /** Gets the current history endpoint */ getHistoryEndpoint() { return this.endpoints.history; } /** Gets the current progress stream endpoint */ getProgressStreamEndpoint() { return this.endpoints.progressStream; } /** Gets the current progress endpoint */ getProgressEndpoint() { return this.endpoints.progress; } /** Gets the current options object */ getModel() { var _a; return (_a = this.options) === null || _a === void 0 ? void 0 : _a.model; } /** Gets the current location-specific instructions */ getInstructions() { var _a; return (_a = this.options) === null || _a === void 0 ? void 0 : _a.instructions; } /** Gets the current platform */ getPlatform() { var _a; return (_a = this.options) === null || _a === void 0 ? void 0 : _a.platform; } /** Gets the current personality configuration */ getPersonality() { var _a; return (_a = this.options) === null || _a === void 0 ? void 0 : _a.personality; } // EVENT EMITTER hasEmitter(event) { return this.events.has(event); } addEmitter(event) { if (!this.hasEmitter(event)) this.events.set(event, new Map()); } removeEmitter(event) { this.events.delete(event); } addListener(event, cb) { if (!this.hasEmitter(event)) this.addEmitter(event); const listeners = this.events.get(event); const id = crypto.randomUUID(); listeners.set(id, cb); return id; } removeListener(event, id) { const listeners = this.events.get(event); if (listeners) listeners.delete(id); } emit(event, ...args) { const listeners = this.events.get(event); if (listeners) listeners.forEach(listener => listener(...args)); } /** * Fires when the conversation ID is set * if convoId is already set when this is called, fires immediately * */ onConvoId(cb) { if (this.convoId) cb(this.convoId); return this.addListener("convoId", cb); } /** Removes a convoId listener */ offConvoId(listenerId) { this.removeListener("convoId", listenerId); } /** * Fires once when the conversation ID is set, then removes the listener. * If convoId is already set, fires immediately */ onceConvoId(cb) { if (this.convoId) { cb(this.convoId); return; } const id = this.addListener("convoId", (convoId) => { cb(convoId); this.offConvoId(id); }); return id; } // SSE HANDLER handleSSE(url, cb, options = {}) { const sse = new eventsource_1.EventSource(url); sse.addEventListener("message", (event) => { const data = JSON.parse(event.data); const convoId = data.success ? data.data.convoId : undefined; if (convoId && options.onConvoId) options.onConvoId(convoId); cb(data); if (data.data.quitStream) sse.close(); }); sse.addEventListener("error", (event) => { if (this.debug) console.warn(`[Stream Error: ${url}]`, event); }); return sse; } // GETTERS /** Fetches the conversation state from the server, including message history and metadata */ fetchState() { return __awaiter(this, void 0, void 0, function* () { if (!this.convoId) throw new Error("Conversation ID is not set"); const url = (0, url_formatter_1.formatURL)(`${this.endpoints.history}/${this.convoId}`, undefined, { apiKey: this.apiKey, debug: this.debug }); const response = yield fetch(url); if (!response.ok) { const errorText = yield response.text(); throw new Error(`Failed to fetch conversation state: ${response.status} ${response.statusText} - ${errorText}`); } const data = yield response.json(); return data; }); } /** Fetches the conversation progress stream from the server */ fetchProgressStream(cb) { if (!this.convoId) throw new Error("Conversation ID is not set"); const url = (0, url_formatter_1.formatURL)(this.endpoints.progressStream, { chatId: this.convoId }, { apiKey: this.apiKey, debug: this.debug }); return this.handleSSE(url, cb); } /** * Fetches the conversation progress from the server * Returns undefined if no active turn progress */ fetchProgress(options) { return __awaiter(this, void 0, void 0, function* () { if (!this.convoId) throw new Error("Conversation ID is not set"); const payload = { chatId: this.convoId, }; if ((options === null || options === void 0 ? void 0 : options.includeCompleted) !== undefined) payload["includeCompleted"] = options.includeCompleted; const url = (0, url_formatter_1.formatURL)(this.endpoints.progress, payload, { apiKey: this.apiKey, debug: this.debug }); const response = yield fetch(url, { headers: (options === null || options === void 0 ? void 0 : options.lastEventId) ? { "last-event-id": options.lastEventId } : undefined }); if (!response.ok) { const errorText = yield response.text(); throw new Error(`Failed to fetch conversation progress: ${response.status} ${response.statusText} - ${errorText}`); } if (response.status === 204) return undefined; // No content const data = yield response.json(); return data.events; }); } // LIFE CYCLE /** Sends a message into the conversation */ send(message, cb, options) { const payload = Object.assign(Object.assign({ message }, this.options), options // overwrite convos for this call ); if (this.convoId) payload.chatId = this.convoId; const url = (0, url_formatter_1.formatURL)(this.endpoints.conversation, payload, { apiKey: this.apiKey, debug: this.debug }); return this.handleSSE(url, cb, { onConvoId: (convoId) => { this.convoId = convoId; this.emit("convoId", convoId); } }); } } exports.Conversation = Conversation;