157 lines
7.4 KiB
JavaScript
157 lines
7.4 KiB
JavaScript
require('dotenv').config({ path: require('path').resolve(__dirname, '../.env') });
|
|
const axios = require('axios');
|
|
|
|
const WORKFLOW_URL_SCRAPE = process.env.WORKFLOW_URL_SCRAPE;
|
|
const WORKFLOW_URL_TEMPLATE = process.env.WORKFLOW_URL_TEMPLATE;
|
|
const WORKFLOW_URL_CHECK_CURL = process.env.WORKFLOW_URL_CHECK_CURL;
|
|
const WORKFLOW_VALIDATE_FIELDS = process.env.WORKFLOW_VALIDATE_FIELDS;
|
|
|
|
if (!WORKFLOW_URL_SCRAPE) throw new Error('Missing WORKFLOW_URL_SCRAPE environment variable');
|
|
if (!WORKFLOW_URL_TEMPLATE) throw new Error('Missing WORKFLOW_URL_TEMPLATE environment variable');
|
|
if (!WORKFLOW_URL_CHECK_CURL) throw new Error('Missing WORKFLOW_URL_CHECK_CURL environment variable');
|
|
if (!WORKFLOW_VALIDATE_FIELDS) throw new Error('Missing WORKFLOW_VALIDATE_FIELDS environment variable');
|
|
|
|
const TRAI_RULES_TEXT = '1) Max 160 chars. 2) Dynamic vars use {#var#}. 3) Transactional: no promo/URLs unless required. 4) Sender ID DLT-compliant. 5) Allowed punctuation only. 6) Must match event type. 7) Avoid URLs unless explicitly needed. 8) Start with event/order context.';
|
|
|
|
const EVENT_DESCRIPTIONS = {
|
|
placed: 'The customer has successfully placed an order',
|
|
confirmed: 'The order has been confirmed by the seller/warehouse',
|
|
dp_assigned: 'A delivery partner has been assigned to deliver the order',
|
|
pack: 'The order has been packed and is ready for dispatch',
|
|
cancelled: 'The order has been cancelled',
|
|
delivery_done: 'The order has been successfully delivered to the customer',
|
|
};
|
|
|
|
function requestId(prefix) {
|
|
return `${prefix}_${Date.now()}`;
|
|
}
|
|
|
|
function parseJsonField(value, fallback) {
|
|
if (typeof value !== 'string') return value ?? fallback;
|
|
try {
|
|
return JSON.parse(value);
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
async function postWorkflow(url, payload) {
|
|
try {
|
|
const response = await axios.post(url, payload, {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
maxBodyLength: Infinity,
|
|
timeout: 60000,
|
|
});
|
|
return response.data;
|
|
} catch (error) {
|
|
const details = error.response?.data ? ` | response: ${JSON.stringify(error.response.data)}` : '';
|
|
throw new Error(`Workflow API error (${url}): ${error.message}${details}`);
|
|
}
|
|
}
|
|
|
|
async function parseBrandContext(scrapedData = {}) {
|
|
const markdown = String(scrapedData.markdown || '').slice(0, 8000);
|
|
const links = Array.isArray(scrapedData.links) ? scrapedData.links.slice(0, 200) : [];
|
|
|
|
const payload = {
|
|
task: 'parse_brand_context',
|
|
request_id: requestId('parse_brand_context'),
|
|
markdown,
|
|
links_json: JSON.stringify(links),
|
|
metadata_json: JSON.stringify(scrapedData.metadata || {}),
|
|
images_json: JSON.stringify(scrapedData.images || []),
|
|
raw_json_blob: JSON.stringify(scrapedData.json || {}),
|
|
output_schema_text: 'Return ONLY valid JSON object with exactly these keys: brandName (string), tone (one of: friendly, professional, formal, casual, energetic), taglines (array of strings, max 3), colors (array of hex color strings, or empty array), relevantImageUrls (array of 3-5 absolute image URLs for logo/hero/product images only; no icons/tracking/data URLs). No markdown, no prose, no extra keys.',
|
|
must_return_json_only: 'true',
|
|
};
|
|
|
|
const data = await postWorkflow(WORKFLOW_URL_SCRAPE, payload);
|
|
const output = typeof data === 'string' ? parseJsonField(data, {}) : (data || {});
|
|
|
|
return {
|
|
brandName: String(output.brandName || '').trim() || 'Unknown Brand',
|
|
tone: ['friendly', 'professional', 'formal', 'casual', 'energetic'].includes(String(output.tone || '').toLowerCase())
|
|
? String(output.tone).toLowerCase()
|
|
: 'professional',
|
|
taglines: Array.isArray(output.taglines) ? output.taglines.slice(0, 3).map(String) : [],
|
|
colors: Array.isArray(output.colors) ? output.colors.map(String) : [],
|
|
relevantImageUrls: Array.isArray(output.relevantImageUrls) ? output.relevantImageUrls.map(String) : [],
|
|
};
|
|
}
|
|
|
|
async function generateTemplates(brandContext = {}, eventSlug, eventLabel) {
|
|
const eventDesc = EVENT_DESCRIPTIONS[eventSlug] || `A "${eventLabel}" event in the order lifecycle`;
|
|
|
|
const payload = {
|
|
task: 'generate_sms_templates',
|
|
request_id: requestId('generate_sms_templates'),
|
|
brand_name: String(brandContext.brandName || ''),
|
|
tone: String(brandContext.tone || ''),
|
|
taglines_json: JSON.stringify(Array.isArray(brandContext.taglines) ? brandContext.taglines : []),
|
|
event_slug: String(eventSlug || ''),
|
|
event_label: String(eventLabel || ''),
|
|
event_description: eventDesc,
|
|
trai_rules_text: TRAI_RULES_TEXT,
|
|
templates_count: '3',
|
|
max_chars: '160',
|
|
variable_format: '{#var#}',
|
|
output_schema_text: 'Return ONLY valid JSON object with exactly one key: templates (array of exactly 3 strings). No extra keys.',
|
|
must_return_json_only: 'true',
|
|
};
|
|
|
|
const data = await postWorkflow(WORKFLOW_URL_TEMPLATE, payload);
|
|
const output = typeof data === 'string' ? parseJsonField(data, {}) : (data || {});
|
|
const templates = Array.isArray(output.templates)
|
|
? output.templates
|
|
: parseJsonField(output.templates_json, []);
|
|
|
|
return Array.isArray(templates) ? templates.map(String).slice(0, 3) : [];
|
|
}
|
|
|
|
async function processCurl(rawCurl, approvedTemplate, eventSlug) {
|
|
const payload = {
|
|
task: 'process_provider_curl',
|
|
request_id: requestId('process_provider_curl'),
|
|
raw_curl: String(rawCurl || ''),
|
|
approved_template: String(approvedTemplate || ''),
|
|
event_slug: String(eventSlug || ''),
|
|
instructions_text: 'Identify placeholders, map to semantic field names, normalize placeholders in curl to camelCase, and build positional mapping for {#var#} tokens in approved_template.',
|
|
output_schema_text: 'Return ONLY valid JSON object with exactly these keys: processedCurl (string), variableMap (object where keys are {#var#}[index] and values are field names in camelCase). No extra keys.',
|
|
must_return_json_only: 'true',
|
|
};
|
|
|
|
const data = await postWorkflow(WORKFLOW_URL_CHECK_CURL, payload);
|
|
const output = typeof data === 'string' ? parseJsonField(data, {}) : (data || {});
|
|
const variableMap = typeof output.variableMap === 'object' && output.variableMap !== null
|
|
? output.variableMap
|
|
: parseJsonField(output.variable_map_json, {});
|
|
|
|
return {
|
|
processedCurl: String(output.processedCurl || ''),
|
|
variableMap: variableMap && typeof variableMap === 'object' ? variableMap : {},
|
|
};
|
|
}
|
|
|
|
async function validateCurlFields(rawCurl) {
|
|
const payload = {
|
|
curl_b64: Buffer.from(String(rawCurl || ''), 'utf8').toString('base64'),
|
|
};
|
|
|
|
const data = await postWorkflow(WORKFLOW_VALIDATE_FIELDS, payload);
|
|
const output = typeof data === 'string' ? parseJsonField(data, {}) : (data || {});
|
|
const isValidCurl = output.is_valid_curl === true || String(output.is_valid_curl).toLowerCase() === 'true';
|
|
|
|
return {
|
|
isValidCurl,
|
|
provider: {
|
|
providerName: String(output.provider_name || '').trim(),
|
|
senderId: String(output.dlt_sender_id || '').trim().toUpperCase(),
|
|
dltEntityId: String(output.dlt_entity_id || '').trim(),
|
|
authKey: String(output.api_auth_key || '').trim(),
|
|
},
|
|
reason: String(output.reason || '').trim(),
|
|
};
|
|
}
|
|
|
|
module.exports = { parseBrandContext, generateTemplates, processCurl, validateCurlFields };
|