UNPKG

@gravityai-dev/gravity-server

Version:

Integration SDK for the Gravity AI orchestration platform - Connect any AI platform in minutes

1,117 lines (1,099 loc) 37.9 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { AI_RESULT_CHANNEL: () => AI_RESULT_CHANNEL, BasePublisher: () => BasePublisher, BatchPublisher: () => BatchPublisher, CardPublisher: () => CardPublisher, ChatState: () => ChatState, EventBus: () => EventBus, FormPublisher: () => FormPublisher, INTERNAL_REQUEST_CHANNEL: () => INTERNAL_REQUEST_CHANNEL, JsonDataPublisher: () => JsonDataPublisher, MessageChunkPublisher: () => MessageChunkPublisher, MessageType: () => MessageType, NodeExecutionPublisher: () => NodeExecutionPublisher, ProgressPublisher: () => ProgressPublisher, Publisher: () => Publisher, QUERY_MESSAGE_CHANNEL: () => QUERY_MESSAGE_CHANNEL, QuestionsPublisher: () => QuestionsPublisher, Redis: () => import_ioredis.default, SYSTEM_CHANNEL: () => SYSTEM_CHANNEL, StatePublisher: () => StatePublisher, SystemPublisher: () => SystemPublisher, TIMEOUTS: () => TIMEOUTS, TYPE_TO_TYPENAME: () => TYPE_TO_TYPENAME, TextPublisher: () => TextPublisher, WORKFLOW_EXECUTION_CHANNEL: () => WORKFLOW_EXECUTION_CHANNEL, WORKFLOW_STATE_CHANNEL: () => WORKFLOW_STATE_CHANNEL, closeAllConnections: () => closeAllConnections, createAudioChunk: () => createAudioChunk, getBatchPublisher: () => getBatchPublisher, getCardPublisher: () => getCardPublisher, getFormPublisher: () => getFormPublisher, getJsonDataPublisher: () => getJsonDataPublisher, getMessageChunkPublisher: () => getMessageChunkPublisher, getNodeExecutionPublisher: () => getNodeExecutionPublisher, getOptionsFromConfig: () => getOptionsFromConfig, getProgressPublisher: () => getProgressPublisher, getPubSubConnection: () => getPubSubConnection, getQuestionsPublisher: () => getQuestionsPublisher, getRedisOptions: () => getRedisOptions, getStandardConnection: () => getStandardConnection, getStatePublisher: () => getStatePublisher, getSystemPublisher: () => getSystemPublisher, getTextPublisher: () => getTextPublisher }); module.exports = __toCommonJS(index_exports); // src/types.ts var MessageType = /* @__PURE__ */ ((MessageType2) => { MessageType2["TEXT"] = "TEXT"; MessageType2["JSON_DATA"] = "JSON_DATA"; MessageType2["IMAGE_RESPONSE"] = "IMAGE_RESPONSE"; MessageType2["TOOL_OUTPUT"] = "TOOL_OUTPUT"; MessageType2["ACTION_SUGGESTION"] = "ACTION_SUGGESTION"; MessageType2["METADATA"] = "METADATA"; MessageType2["AUDIO_CHUNK"] = "AUDIO_CHUNK"; MessageType2["SYSTEM_MESSAGE"] = "SYSTEM_MESSAGE"; MessageType2["PROGRESS_UPDATE"] = "PROGRESS_UPDATE"; MessageType2["MESSAGE_CHUNK"] = "MESSAGE_CHUNK"; MessageType2["STATE"] = "STATE"; MessageType2["CARD"] = "CARD"; MessageType2["QUESTIONS"] = "QUESTIONS"; MessageType2["FORM"] = "FORM"; MessageType2["NODE_EXECUTION_EVENT"] = "NODE_EXECUTION_EVENT"; return MessageType2; })(MessageType || {}); var ChatState = /* @__PURE__ */ ((ChatState2) => { ChatState2["IDLE"] = "IDLE"; ChatState2["ACTIVE"] = "ACTIVE"; ChatState2["COMPLETE"] = "COMPLETE"; ChatState2["THINKING"] = "THINKING"; ChatState2["RESPONDING"] = "RESPONDING"; ChatState2["WAITING"] = "WAITING"; ChatState2["ERROR"] = "ERROR"; ChatState2["CANCELLED"] = "CANCELLED"; return ChatState2; })(ChatState || {}); var SYSTEM_CHANNEL = "gravity:system"; var AI_RESULT_CHANNEL = "gravity:output"; var QUERY_MESSAGE_CHANNEL = "gravity:query"; var INTERNAL_REQUEST_CHANNEL = "gravity:internal"; var WORKFLOW_EXECUTION_CHANNEL = "workflow:execution"; var WORKFLOW_STATE_CHANNEL = "gravity:workflow:state"; var TIMEOUTS = { DEFAULT: 5e3, REQUEST: 1e4 }; var TYPE_TO_TYPENAME = { ["TEXT" /* TEXT */]: "Text", ["MESSAGE_CHUNK" /* MESSAGE_CHUNK */]: "MessageChunk", ["JSON_DATA" /* JSON_DATA */]: "JsonData", ["ACTION_SUGGESTION" /* ACTION_SUGGESTION */]: "ActionSuggestion", ["METADATA" /* METADATA */]: "Metadata", ["IMAGE_RESPONSE" /* IMAGE_RESPONSE */]: "ImageResponse", ["TOOL_OUTPUT" /* TOOL_OUTPUT */]: "ToolOutput", ["AUDIO_CHUNK" /* AUDIO_CHUNK */]: "AudioChunk", ["STATE" /* STATE */]: "State", ["SYSTEM_MESSAGE" /* SYSTEM_MESSAGE */]: "SystemMessage", ["PROGRESS_UPDATE" /* PROGRESS_UPDATE */]: "ProgressUpdate", ["CARD" /* CARD */]: "Card", ["QUESTIONS" /* QUESTIONS */]: "Questions", ["FORM" /* FORM */]: "Form", ["NODE_EXECUTION_EVENT" /* NODE_EXECUTION_EVENT */]: "NodeExecutionEvent" }; // src/RedisManager.ts var import_ioredis = __toESM(require("ioredis")); var standardConnections = /* @__PURE__ */ new Map(); var pubsubConnections = /* @__PURE__ */ new Map(); function createConfig(options) { return { host: options.host, port: options.port, password: options.password, username: options.username, db: options.db || 0, retryStrategy: (times) => Math.min(times * 50, 2e3), maxRetriesPerRequest: 3, enableOfflineQueue: true // Add other common options here }; } function getStandardConnection(options) { const connectionKey = `${options.host}:${options.port}:${options.db || 0}:${options.username || "default"}`; if (standardConnections.has(connectionKey)) { return standardConnections.get(connectionKey); } const config = { host: options.host, port: options.port, username: options.username, password: options.password, db: options.db || 0, retryStrategy: (times) => Math.min(times * 50, 2e3), maxRetriesPerRequest: 3, enableOfflineQueue: true }; const client = new import_ioredis.default(config); standardConnections.set(connectionKey, client); return client; } function getPubSubConnection(options) { const connectionKey = `${options.host}:${options.port}:${options.db || 0}:${options.username || "default"}`; if (pubsubConnections.has(connectionKey)) { return pubsubConnections.get(connectionKey); } const config = createConfig(options); const client = new import_ioredis.default(config); pubsubConnections.set(connectionKey, client); return client; } function getRedisOptions() { return { host: process.env.REDIS_HOST, port: parseInt(process.env.REDIS_PORT, 10), username: process.env.REDIS_USERNAME, password: process.env.REDIS_PASSWORD }; } function getOptionsFromConfig(host, port, username, password) { return { host, port, username: username || void 0, password: password || void 0 }; } async function closeAllConnections() { const closePromises = []; standardConnections.forEach((client, key) => { closePromises.push( client.quit().then(() => { standardConnections.delete(key); return `Standard connection ${key}`; }) ); }); pubsubConnections.forEach((client, key) => { closePromises.push( client.quit().then(() => { pubsubConnections.delete(key); return `PubSub connection ${key}`; }) ); }); await Promise.all(closePromises); } // src/messaging/Publisher.ts var Publisher = class _Publisher { constructor(options, providerId) { this.redis = getStandardConnection(options); this.providerId = providerId; } static fromRedisCredentials(redisOptions, providerId) { return new _Publisher(redisOptions, providerId); } static fromConfig(host, port, password, providerId, username, db) { const redisOptions = getOptionsFromConfig(host, port, username, password); return new _Publisher(redisOptions, providerId); } getProviderId() { return this.providerId; } getRedisConnection() { return this.redis; } /** * Publish system-level events * Used by EventBus and system services */ async publishSystem(message) { await this.redis.publish(SYSTEM_CHANNEL, JSON.stringify(message)); } /** * Publish to arbitrary event channels * Used by EventBus, n8n resolver, and health monitor */ async publishEvent(eventType, payload) { await this.redis.publish(eventType, JSON.stringify(payload)); } async disconnect() { this.redis = null; } }; // src/messaging/SimpleEventBus.ts var EventBus = class _EventBus { constructor(options, serviceId) { this.options = options; this.serviceId = serviceId; this.handlers = /* @__PURE__ */ new Map(); this.publisher = new Publisher(options, serviceId); this.subscriber = getPubSubConnection(options); this.setupSubscriber(); } static fromRedisConfig(host, port, password, serviceId, username, db) { const options = { host, port, password, username, db: db || 0 }; return new _EventBus(options, serviceId); } static fromCredentials(host, port, password, serviceId) { return _EventBus.fromRedisConfig(host, port, password, serviceId); } setupSubscriber() { this.subscriber.on("message", (channel, message) => { const handlers = this.handlers.get(channel); if (!handlers || handlers.size === 0) return; try { const event = JSON.parse(message); handlers.forEach((handler) => { try { handler(event); } catch (error) { console.error(`[EventBus] Handler error on channel ${channel}:`, error); } }); } catch (error) { console.error(`[EventBus] Failed to parse message on channel ${channel}:`, error); } }); } async publish(channel, payload) { await this.publisher.publishEvent(channel, payload); } async subscribe(channel, handler) { if (!this.handlers.has(channel)) { this.handlers.set(channel, /* @__PURE__ */ new Set()); await this.subscriber.subscribe(channel); } this.handlers.get(channel).add(handler); return async () => { const handlers = this.handlers.get(channel); if (!handlers) return; handlers.delete(handler); if (handlers.size === 0) { this.handlers.delete(channel); await this.subscriber.unsubscribe(channel); } }; } async disconnect() { await Promise.all([ this.publisher.disconnect(), this.subscriber.quit() ]); } }; // src/messaging/publishers/base.ts var import_uuid = require("uuid"); var BasePublisher = class { /** * Creates a new BasePublisher instance * * @param {Redis} redis - Redis connection instance for publishing * @param {string} providerId - Unique identifier for the service/provider * * @example * ```typescript * const redis = new Redis({ * host: "localhost", * port: 6379 * }); * const publisher = new MyPublisher(redis, "my-service"); * ``` */ constructor(redis, providerId) { this.redis = redis; this.providerId = providerId; } /** * Gets the provider ID for the publisher * * This method is used by other components (like EventBus) to access * the provider ID when creating related instances. * * @returns {string} The provider ID * * @example * ```typescript * const providerId = publisher.getProviderId(); * console.log(`Publisher provider: ${providerId}`); * ``` */ getProviderId() { return this.providerId; } /** * Gets the Redis connection for the publisher * * This method exposes the Redis connection for use by other components * that need to create their own connections with the same configuration. * * @returns {Redis} The Redis connection instance * * @example * ```typescript * const redis = publisher.getRedisConnection(); * const options = { * host: redis.options.host, * port: redis.options.port * }; * ``` */ getRedisConnection() { return this.redis; } /** * Creates a base message with the given partial data * * This method constructs a complete BaseMessage by merging provided fields * with defaults. It ensures all required fields are present and validates * that chatId, conversationId, and userId are provided. * * @protected * @param {Partial<BaseMessage>} partial - Partial message data to merge with defaults * @returns {BaseMessage} Complete base message with all required fields * @throws {Error} If chatId, conversationId, or userId are missing * * @example * ```typescript * const baseMessage = this.createBaseMessage({ * chatId: "chat123", * conversationId: "conv456", * userId: "user789", * type: MessageType.TEXT * }); * // Result: { * // id: "generated-uuid", * // chatId: "chat123", * // conversationId: "conv456", * // userId: "user789", * // providerId: "my-service", * // timestamp: "2023-12-08T10:30:00.000Z", * // type: MessageType.TEXT * // } * ``` */ createBaseMessage(partial) { if (!partial.chatId || !partial.conversationId || !partial.userId) { throw new Error("chatId, conversationId, and userId are required"); } return { id: partial.id || (0, import_uuid.v4)(), chatId: partial.chatId, conversationId: partial.conversationId, userId: partial.userId, providerId: partial.providerId || this.providerId, timestamp: partial.timestamp || (/* @__PURE__ */ new Date()).toISOString(), type: partial.type || "TEXT" /* TEXT */ }; } /** * Publishes a message to the given channel * * This method serializes the message to JSON and publishes it to the * specified Redis channel. This is the core publishing mechanism used * by all specialized publishers. * * @protected * @param {GravityMessage} message - The message object to publish * @param {PublishOptions} [options] - Optional publishing options * @returns {Promise<void>} Promise that resolves when message is published * * @example * ```typescript * const message = { * __typename: "Text", * text: "Hello, world!", * ...baseMessage * }; * await this.publish(message, { channel: "custom:channel" }); * ``` */ async publish(message, options) { const channel = options?.channel || AI_RESULT_CHANNEL; await this.publishToStream(channel, message); } /** * Publishes a message to Redis Streams for guaranteed delivery * * @protected * @param {string} channel - The channel name * @param {GravityMessage} message - The message to publish * @returns {Promise<string>} The stream entry ID */ async publishToStream(channel, message) { const streamKey = "workflow:events:stream"; try { const conversationId = message.conversationId || ""; const entryId = await this.redis.xadd( streamKey, "*", // Auto-generate ID "channel", channel, "conversationId", conversationId, // Add for Redis Streams filtering "message", JSON.stringify(message), "timestamp", Date.now().toString(), "providerId", this.providerId ); if (!entryId) { console.error(`[BasePublisher] Failed to add entry to stream ${streamKey} - no entry ID returned`); } await this.redis.publish(channel, JSON.stringify(message)); return entryId || ""; } catch (error) { console.error(`[BasePublisher] Error publishing to stream ${streamKey}:`, error); try { await this.redis.publish(channel, JSON.stringify(message)); console.warn(`[BasePublisher] Fell back to pub/sub only for channel ${channel}`); } catch (pubsubError) { console.error(`[BasePublisher] Failed to publish to pub/sub as well:`, pubsubError); throw pubsubError; } return ""; } } }; // src/messaging/publishers/progressUpdate.ts var ProgressPublisher = class extends BasePublisher { /** * Publishes a progress update * * @param message - The progress message text * @param progress - Optional progress percentage (0-100) * @param baseMessage - Base message with required fields (chatId, conversationId, userId) * @param options - Optional publishing options (e.g., custom channel) */ async publishProgressUpdate(message, progress, baseMessage, options) { const progressUpdate = { ...this.createBaseMessage(baseMessage), __typename: "ProgressUpdate", component: { type: "ProgressUpdate", props: { message, progress } } }; await this.publish(progressUpdate, options); } }; var progressPublisherInstance = null; function getProgressPublisher(host, port, password, providerId, username, db) { if (!progressPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("ProgressPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); progressPublisherInstance = new ProgressPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return progressPublisherInstance; } // src/messaging/publishers/messageChunk.ts var MessageChunkPublisher = class extends BasePublisher { /** * Publishes a message chunk * * @param text - The text content of the chunk * @param baseMessage - Base message with required fields (chatId, conversationId, userId) * @param index - Optional sequence index for ordering * @param options - Optional publishing options (e.g., custom channel) */ async publishMessageChunk(text, baseMessage, index, options) { const messageChunk = { ...this.createBaseMessage(baseMessage), __typename: "MessageChunk", component: { type: "MessageChunk", props: { text, index } } }; await this.publish(messageChunk, options); } }; var messageChunkPublisherInstance = null; function getMessageChunkPublisher(host, port, password, providerId, username, db) { if (!messageChunkPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { console.error("[ERROR] MessageChunkPublisher missing required parameters:", { hasHost: !!host, hasPort: !!port, hasPassword: password !== void 0, hasProviderId: !!providerId }); throw new Error("MessageChunkPublisher requires host, port, password, and providerId on first call"); } console.log("[DEBUG] Creating new MessageChunkPublisher instance with Redis config:", { host, port, providerId }); const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); messageChunkPublisherInstance = new MessageChunkPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } else { console.log("[DEBUG] Returning existing MessageChunkPublisher instance"); } return messageChunkPublisherInstance; } // src/messaging/publishers/text.ts var TextPublisher = class extends BasePublisher { /** * Publishes a text message * * @param text - The text content * @param baseMessage - Base message with required fields (chatId, conversationId, userId) * @param options - Optional publishing options (e.g., custom channel) */ async publishText(text, baseMessage, options) { const textMessage = { ...this.createBaseMessage(baseMessage), __typename: "Text", component: { type: "Text", props: { text } } }; await this.publish(textMessage, options); } }; var textPublisherInstance = null; function getTextPublisher(host, port, password, providerId, username, db) { if (!textPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("TextPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); textPublisherInstance = new TextPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return textPublisherInstance; } // src/messaging/publishers/jsonData.ts var JsonDataPublisher = class extends BasePublisher { /** * Publishes a JSON data message * * @param data - The JSON data to publish * @param baseMessage - Base message with required fields (chatId, conversationId, userId) * @param options - Optional publishing options (e.g., custom channel) */ async publishJsonData(data, baseMessage, options) { const jsonMessage = { ...this.createBaseMessage(baseMessage), __typename: "JsonData", component: { type: "JsonData", props: { data } } }; await this.publish(jsonMessage, options); } }; var jsonDataPublisherInstance = null; function getJsonDataPublisher(host, port, password, providerId, username, db) { if (!jsonDataPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("JsonDataPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); jsonDataPublisherInstance = new JsonDataPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return jsonDataPublisherInstance; } // src/messaging/publishers/audioChunk.ts function createAudioChunk(base, audioData, format, textReference, sourceType, duration, index) { return { ...base, __typename: "AudioChunk", component: { type: "AudioChunk", props: { audioData, format, textReference, sourceType, duration, index } } }; } // src/messaging/publishers/state.ts var StatePublisher = class extends BasePublisher { /** * Publishes a chat state update * * @param state - The chat state (e.g., THINKING, RESPONDING, etc.) * @param baseMessage - Base message with required fields (chatId, conversationId, userId) * @param label - Optional human-readable label for the state * @param data - Optional additional data for the state * @param variables - Optional variables associated with the state * @param options - Optional publishing options (e.g., custom channel) */ async publishState(state, baseMessage, label, data, variables, options) { const stateMessage = { ...this.createBaseMessage(baseMessage), __typename: "State", component: { type: "State", props: { state, ...label && { label } } }, ...data && { data }, ...label && { label }, ...variables && { variables } }; if (!stateMessage.conversationId) { throw new Error("conversationId is required for publishing state messages"); } await this.publish(stateMessage, options); } /** * Convenience method to publish common state transitions */ async publishThinking(baseMessage, options) { await this.publishState("THINKING", baseMessage, "Thinking...", void 0, void 0, options); } async publishResponding(baseMessage, options) { await this.publishState("RESPONDING", baseMessage, "Responding...", void 0, void 0, options); } async publishWaiting(baseMessage, options) { await this.publishState("WAITING", baseMessage, "Waiting for input...", void 0, void 0, options); } async publishComplete(baseMessage, options) { await this.publishState("COMPLETE", baseMessage, "Complete", void 0, void 0, options); } async publishError(error, baseMessage, options) { await this.publishState("ERROR", baseMessage, "Error occurred", { error }, void 0, options); } }; var statePublisherInstance = null; function getStatePublisher(host, port, password, providerId, username, db) { if (!statePublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("StatePublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); statePublisherInstance = new StatePublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return statePublisherInstance; } // src/messaging/publishers/system.ts var SystemPublisher = class extends BasePublisher { /** * Publishes a system message * * System messages are used for service notifications, errors, and warnings. * * @param message - The system message text * @param level - The message level (info, warning, or error) * @param baseMessage - Base message with required fields (chatId, conversationId, userId) * @param options - Optional publishing options (e.g., custom channel) * @returns Promise that resolves when message is published * * @example * ```typescript * // Service startup notification * await publisher.publishSystemMessage("Service started", "info", { * chatId: "chat123", * conversationId: "conv123", * userId: "user456" * }); * * // Error notification * await publisher.publishSystemMessage("Database connection failed", "error", { * chatId: "chat123", * conversationId: "conv123", * userId: "user456" * }); * ``` */ async publishSystemMessage(message, level, baseMessage, options) { const systemMessage = { ...this.createBaseMessage(baseMessage), __typename: "SystemMessage", message, level }; await this.publish(systemMessage, options); } }; var systemPublisherInstance = null; function getSystemPublisher(host, port, password, providerId, username, db) { if (!systemPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("SystemPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); systemPublisherInstance = new SystemPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return systemPublisherInstance; } // src/messaging/publishers/cards.ts var CardPublisher = class extends BasePublisher { /** * Publish card data with flexible JSON structure * @param cardData - Any JSON structure for card data * @param baseMessage - Base message properties * @param options - Optional publishing options (e.g., custom channel) */ async publishCard(cardData, baseMessage, options) { const message = { ...this.createBaseMessage(baseMessage), __typename: "Cards", component: { type: "cards", props: cardData // Pass through any JSON structure } }; await this.publish(message, options); } /** * Publish multiple cards (for workflow service compatibility) * @param cardsData - Array of card data or single card data * @param baseMessage - Base message properties * @param options - Optional publishing options (e.g., custom channel) */ async publishCards(cardsData, baseMessage, options) { const cardsArray = Array.isArray(cardsData) ? cardsData : [cardsData]; const message = { ...this.createBaseMessage(baseMessage), __typename: "Cards", component: { type: "cards", props: cardsArray // Pass the entire array } }; await this.publish(message, options); } }; var cardPublisherInstance = null; function getCardPublisher(host, port, password, providerId, username, db) { if (!cardPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("CardPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); cardPublisherInstance = new CardPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return cardPublisherInstance; } // src/messaging/publishers/questions.ts var QuestionsPublisher = class extends BasePublisher { /** * Publish follow-up questions * @param questions - Array of question strings * @param baseMessage - Base message properties * @param options - Optional publishing options (e.g., custom channel) */ async publishQuestions(questions, baseMessage, options) { const message = { ...this.createBaseMessage(baseMessage), __typename: "Questions", component: { type: "questions", props: questions // Pass the array of question strings } }; await this.publish(message, options); } }; var questionsPublisherInstance = null; function getQuestionsPublisher(host, port, password, providerId, username, db) { if (!questionsPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("QuestionsPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); questionsPublisherInstance = new QuestionsPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return questionsPublisherInstance; } // src/messaging/publishers/forms.ts var FormPublisher = class extends BasePublisher { /** * Publish a form structure * @param formData - Form configuration with steps and inputs * @param baseMessage - Base message properties * @param options - Optional publishing options (e.g., custom channel) */ async publishForm(formData, baseMessage, options) { const message = { ...this.createBaseMessage(baseMessage), __typename: "Form", component: { type: "form", props: formData // Pass through the form structure directly } }; await this.publish(message, options); } }; var formPublisherInstance = null; function getFormPublisher(host, port, password, providerId, username, db) { if (!formPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("FormPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); formPublisherInstance = new FormPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return formPublisherInstance; } // src/messaging/publishers/batch.ts var BatchPublisher = class extends BasePublisher { /** * Publish multiple messages in a single batch operation * Maximum performance - single Redis roundtrip for all messages * * @param messages - Array of messages to publish * @param defaultChannel - Default channel if message doesn't specify one */ async publishBatch(messages, defaultChannel) { if (messages.length === 0) return; const pipeline = this.redis.pipeline(); for (const { message, channel } of messages) { const targetChannel = channel || defaultChannel || message.conversationId || "gravity:output"; pipeline.publish(targetChannel, JSON.stringify(message)); } await pipeline.exec(); } /** * Publish multiple messages to same channel * Even faster when all messages go to same destination */ async publishBatchToChannel(messages, channel) { if (messages.length === 0) return; const pipeline = this.redis.pipeline(); for (const message of messages) { pipeline.publish(channel, JSON.stringify(message)); } await pipeline.exec(); } /** * Publish streaming chunks in batch * Optimized for high-frequency message chunks */ async publishStreamingBatch(chunks, baseMessage, channel) { if (chunks.length === 0) return; const pipeline = this.redis.pipeline(); const targetChannel = channel || baseMessage.conversationId || "gravity:output"; for (const chunk of chunks) { const message = { ...this.createBaseMessage(baseMessage), __typename: "MessageChunk", text: chunk }; pipeline.publish(targetChannel, JSON.stringify(message)); } await pipeline.exec(); } }; var batchPublisherInstance = null; function getBatchPublisher(host, port, password, providerId, username, db) { if (!batchPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("BatchPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); batchPublisherInstance = new BatchPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return batchPublisherInstance; } // src/messaging/publishers/nodeExecution.ts var NodeExecutionPublisher = class extends BasePublisher { /** * Publishes a workflow node completion event * * @param workflowId - Workflow ID * @param executionId - Execution ID * @param nodeId - Node ID that completed * @param nodeType - Type of node * @param status - Completion status * @param result - Node execution result * @param error - Error message if failed * @param duration - Execution duration in milliseconds * @param triggeredSignals - Signals triggered by this completion * @param baseMessage - Base message with required fields (chatId, conversationId, userId) * @param options - Optional publishing options (e.g., custom channel) */ async publishNodeCompletion(workflowId, executionId, nodeId, nodeType, status, result, error, duration, triggeredSignals, baseMessage, options) { const sanitizeOutputsForSubscription = (outputs) => { if (!outputs || typeof outputs !== "object") return outputs; const sanitized = { ...outputs }; if (sanitized.content && typeof sanitized.content === "string") { const contentSize = Buffer.byteLength(sanitized.content, "utf8"); if (contentSize > 5e4) { sanitized.content = `[CONTENT_TRUNCATED: ${contentSize} bytes - use downloadUrl]`; } } return sanitized; }; const nodeExecutionEvent = { ...baseMessage, // Include conversationId, chatId, userId, providerId __typename: "NodeExecutionEvent", executionId, workflowId, nodeId, nodeType, state: status === "completed" ? "COMPLETED" : status === "running" ? "STARTED" : "ERROR", timestamp: (/* @__PURE__ */ new Date()).toISOString(), duration, outputs: status === "completed" ? sanitizeOutputsForSubscription(result) : null, error: status === "failed" ? error : null, triggeredSignals: triggeredSignals.map((signal) => ({ __typename: "TriggeredSignal", targetNode: signal.targetNode, signal: signal.signal, inputs: signal.inputs })) }; const customOptions = { ...options, channel: options?.channel || AI_RESULT_CHANNEL }; await this.publish(nodeExecutionEvent, customOptions); } }; var nodeExecutionPublisherInstance = null; function getNodeExecutionPublisher(host, port, password, providerId, username, db) { if (!nodeExecutionPublisherInstance) { if (!host || !port || password === void 0 || !providerId) { throw new Error("NodeExecutionPublisher requires host, port, password, and providerId on first call"); } const publisher = Publisher.fromConfig(host, port, password, providerId, username, db); nodeExecutionPublisherInstance = new NodeExecutionPublisher( publisher.getRedisConnection(), publisher.getProviderId() ); } return nodeExecutionPublisherInstance; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AI_RESULT_CHANNEL, BasePublisher, BatchPublisher, CardPublisher, ChatState, EventBus, FormPublisher, INTERNAL_REQUEST_CHANNEL, JsonDataPublisher, MessageChunkPublisher, MessageType, NodeExecutionPublisher, ProgressPublisher, Publisher, QUERY_MESSAGE_CHANNEL, QuestionsPublisher, Redis, SYSTEM_CHANNEL, StatePublisher, SystemPublisher, TIMEOUTS, TYPE_TO_TYPENAME, TextPublisher, WORKFLOW_EXECUTION_CHANNEL, WORKFLOW_STATE_CHANNEL, closeAllConnections, createAudioChunk, getBatchPublisher, getCardPublisher, getFormPublisher, getJsonDataPublisher, getMessageChunkPublisher, getNodeExecutionPublisher, getOptionsFromConfig, getProgressPublisher, getPubSubConnection, getQuestionsPublisher, getRedisOptions, getStandardConnection, getStatePublisher, getSystemPublisher, getTextPublisher }); //# sourceMappingURL=index.js.map