langflow-chatbot
Version:
Add a Langflow-powered chatbot to your website.
174 lines (173 loc) • 9.28 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LangflowProxyService = void 0;
const langflow_client_1 = require("@datastax/langflow-client");
const config_loader_1 = require("./lib/startup/config-loader");
const flow_mapper_1 = require("./utils/flow-mapper");
const request_handler_1 = require("./lib/request-handler");
const request_utils_1 = require("./lib/request-utils");
class LangflowProxyService {
constructor(config) {
this.flowConfigs = new Map();
this.isInitialized = false;
if (!config.proxyApiBasePath || typeof config.proxyApiBasePath !== 'string' || config.proxyApiBasePath.trim() === '') {
throw new TypeError('LangflowProxyService: proxyApiBasePath is required in config and must be a non-empty string.');
}
const { langflowConnection, serverDefaults, chatbotDefaults } = (0, config_loader_1.loadBaseConfig)();
this.langflowConnectionDetails = langflowConnection;
this.proxyApiBasePath = config.proxyApiBasePath;
console.log(`LangflowProxyService: API Base Path configured to: ${this.proxyApiBasePath}`);
const clientConfig = {
baseUrl: langflowConnection.endpoint_url,
};
if (langflowConnection.api_key && langflowConnection.api_key.trim() !== '') {
clientConfig.apiKey = langflowConnection.api_key;
}
this.langflowClient = new langflow_client_1.LangflowClient(clientConfig);
console.log(`LangflowProxyService: LangflowClient initialized. Configured Endpoint: ${langflowConnection.endpoint_url}`);
const rawInstanceProfiles = (0, config_loader_1.loadInstanceConfig)(config.instanceConfigPath);
this.flowMapper = new flow_mapper_1.FlowMapper(langflowConnection.endpoint_url, langflowConnection.api_key);
this.initializationPromise = this._internalAsyncInit(rawInstanceProfiles, serverDefaults, chatbotDefaults);
}
async _internalAsyncInit(rawInstanceProfiles, serverDefaultValues, chatbotDefaultValues) {
try {
console.log("LangflowProxyService: Starting internal asynchronous initialization...");
await this.flowMapper.initialize();
console.log("LangflowProxyService: FlowMapper initialized successfully internally.");
rawInstanceProfiles.forEach(profile => {
const completeProfile = {
profileId: profile.profileId,
server: {
flowId: profile.server.flowId,
enableStream: profile.server.enableStream ?? serverDefaultValues.enableStream,
datetimeFormat: profile.server.datetimeFormat ?? serverDefaultValues.datetimeFormat,
},
chatbot: {
labels: { ...(chatbotDefaultValues.labels || {}), ...(profile.chatbot?.labels || {}) },
template: { ...(chatbotDefaultValues.template || {}), ...(profile.chatbot?.template || {}) },
floatingWidget: { ...(chatbotDefaultValues.floatingWidget || {}), ...(profile.chatbot?.floatingWidget || {}) }
}
};
const configuredFlowIdentifier = completeProfile.server.flowId;
const resolvedFlowId = this.flowMapper.getTrueFlowId(configuredFlowIdentifier);
if (resolvedFlowId) {
if (resolvedFlowId !== configuredFlowIdentifier) {
console.log(`LangflowProxyService: Resolved flow identifier '${configuredFlowIdentifier}' to UUID '${resolvedFlowId}' for profile '${completeProfile.profileId}'.`);
}
completeProfile.server.flowId = resolvedFlowId;
}
else {
console.error(`LangflowProxyService: CRITICAL - Could not resolve flow identifier '${configuredFlowIdentifier}' for profile '${completeProfile.profileId}'. This profile will not function correctly as the identifier is not a valid UUID and was not found in the flow map.`);
}
this.flowConfigs.set(completeProfile.profileId, completeProfile);
console.log(`LangflowProxyService: Loaded profile: '${completeProfile.profileId}' configured with resolved flowId '${completeProfile.server.flowId}'.`);
});
if (this.flowConfigs.size === 0) {
console.warn("LangflowProxyService: No chatbot profiles were loaded after async init. The service may not function as expected.");
}
else {
const unresolvedCount = Array.from(this.flowConfigs.values()).filter(p => !(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(p.server.flowId))).length;
if (unresolvedCount > 0) {
console.warn(`LangflowProxyService: Finished async profile loading. ${this.flowConfigs.size - unresolvedCount} profiles have a valid resolved flowId. ${unresolvedCount} profiles have unresolved flow identifiers and may not function.`);
}
else {
console.log(`LangflowProxyService: Finished async profile loading. All ${this.flowConfigs.size} profiles have a valid resolved flowId.`);
}
}
this.isInitialized = true;
console.log("LangflowProxyService: Internal asynchronous initialization complete.");
}
catch (error) {
console.error(`LangflowProxyService: CRITICAL - Error during internal asynchronous initialization: ${error.message}`);
this.isInitialized = false;
throw error;
}
}
async getChatbotProfile(profileId) {
await this.initializationPromise;
return this.flowConfigs.get(profileId);
}
getLangflowConnectionDetails() {
return this.langflowConnectionDetails;
}
async getAllFlowConfigs() {
await this.initializationPromise;
return this.flowConfigs;
}
async getAllChatbotProfiles() {
await this.initializationPromise;
return this.flowConfigs;
}
async _makeDirectLangflowApiRequest(res, path, method, queryParams) {
if (!this.langflowConnectionDetails.endpoint_url) {
console.warn(`LangflowProxyService: Attempted API call to "${path}" when Langflow endpoint URL is not configured.`);
res.statusCode = 503;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: "Langflow endpoint URL not configured in proxy." }));
return null;
}
const targetUrl = new URL(path, this.langflowConnectionDetails.endpoint_url);
if (queryParams) {
queryParams.forEach((value, key) => {
targetUrl.searchParams.append(key, value);
});
}
console.log(`LangflowProxyService: Forwarding ${method} request to Langflow: ${targetUrl.toString()}`);
const headers = {
'Accept': 'application/json',
};
if (this.langflowConnectionDetails.api_key) {
headers['Authorization'] = `Bearer ${this.langflowConnectionDetails.api_key}`;
}
try {
const response = await fetch(targetUrl.toString(), {
method: method,
headers: headers,
});
if (!response.ok) {
console.error(`LangflowProxyService: Langflow API request failed: ${response.status} ${response.statusText} for path ${path}`);
}
return response;
}
catch (error) {
console.error(`LangflowProxyService: Error during Langflow API request to ${path}:`, error);
return null;
}
}
async handleRequest(req, res) {
await this.initializationPromise;
const entryReqUrl = req.url || '';
let effectiveFullPath;
if (req.originalUrl) {
effectiveFullPath = req.originalUrl;
}
else {
effectiveFullPath = entryReqUrl;
}
let internalRoutePath;
if (effectiveFullPath.startsWith(this.proxyApiBasePath)) {
internalRoutePath = effectiveFullPath.substring(this.proxyApiBasePath.length);
if (!internalRoutePath.startsWith('/')) {
internalRoutePath = '/' + internalRoutePath;
}
}
else {
(0, request_utils_1.sendJsonError)(res, 404, "Endpoint not found. Path mismatch with proxy base.");
return;
}
req.url = internalRoutePath;
const preParsedBody = req.body;
const isBodyPreParsed = preParsedBody !== undefined;
if (isBodyPreParsed) {
}
else {
}
try {
await (0, request_handler_1.handleRequest)(req, res, this.flowConfigs, this.langflowClient, this.langflowConnectionDetails.endpoint_url, this.langflowConnectionDetails.api_key, this._makeDirectLangflowApiRequest.bind(this), this.proxyApiBasePath, preParsedBody, isBodyPreParsed);
}
finally {
req.url = entryReqUrl;
}
}
}
exports.LangflowProxyService = LangflowProxyService;