leumas-axios
Version:
An advanced Axios wrapper with extended features and Express multi-post middleware for conversational API endpoints.
209 lines (191 loc) • 7.59 kB
JavaScript
// index.js
const axios = require('axios');
const readline = require('readline');
class AdvancedAxios {
/**
* Create an AdvancedAxios instance with optional Axios configuration.
*/
constructor(axiosConfig = {}) {
this.axiosInstance = axios.create(axiosConfig);
}
// --- Standard Axios Methods ---
request(config) {
return this.axiosInstance.request(config);
}
get(url, config = {}) {
return this.axiosInstance.get(url, config);
}
post(url, data = {}, config = {}) {
return this.axiosInstance.post(url, data, config);
}
put(url, data = {}, config = {}) {
return this.axiosInstance.put(url, data, config);
}
delete(url, config = {}) {
return this.axiosInstance.delete(url, config);
}
patch(url, data = {}, config = {}) {
return this.axiosInstance.patch(url, data, config);
}
// --- Advanced Features ---
/**
* multiGet(urls, config):
* Executes GET requests to an array of URLs concurrently and returns an array of response data.
*/
multiGet(urls, config = {}) {
const requests = urls.map(url => this.axiosInstance.get(url, config));
return Promise.all(requests).then(responses => responses.map(res => res.data));
}
/**
* multiPost(url, config, { onFeedback }):
* Executes a POST request that may require multiple steps.
*/
multiPost(url, config = {}, { onFeedback } = {}) {
const makeRequest = (payload) => {
return this.axiosInstance.post(url, payload, config)
.then(response => response.data)
.then(result => {
if (result.status === 'pending' && result.missingFields && typeof onFeedback === 'function') {
return onFeedback(result.missingFields).then(additionalData => {
const newData = { ...payload.data, ...additionalData };
const newPayload = {
sessionId: result.sessionId,
data: newData
};
return makeRequest(newPayload);
});
}
return result;
});
};
const initialPayload = { data: config.data || {} };
return makeRequest(initialPayload);
}
/**
* generativePost(url, config, { chatGptConfig }):
* Executes a POST request after using ChatGPT to “fill in” or complete the data.
* The chatGptConfig should include at a minimum:
* - outputStructure: The desired JSON structure (as an object) that must be followed exactly.
* Additionally, you may pass in:
* - apiKey, model, temperature, max_tokens, additionalPrompt.
*
* If any of these values are not provided, the function will fall back to the corresponding environment variables.
*/
generativePost(url, config = {}, { chatGptConfig } = {}) {
const data = config.data || {};
if (chatGptConfig && typeof chatGptConfig === 'object') {
// Use environment variables as defaults if values are not provided.
const apiKey = chatGptConfig.apiKey || process.env.OPENAI_API_KEY;
const model = chatGptConfig.model || process.env.OPENAI_MODEL || 'gpt-3.5-turbo';
const temperature = (typeof chatGptConfig.temperature !== 'undefined')
? chatGptConfig.temperature
: (process.env.OPENAI_TEMPERATURE ? parseFloat(process.env.OPENAI_TEMPERATURE) : 0.7);
const max_tokens = chatGptConfig.max_tokens || (process.env.OPENAI_MAX_TOKENS ? parseInt(process.env.OPENAI_MAX_TOKENS, 10) : 150);
const outputStructure = chatGptConfig.outputStructure;
const additionalPrompt = chatGptConfig.additionalPrompt || process.env.OPENAI_ADDITIONAL_PROMPT;
if (!apiKey || !outputStructure) {
return Promise.reject(new Error('Missing required chatGptConfig properties: apiKey and outputStructure are required.'));
}
// Construct a prompt for ChatGPT to complete the data according to the provided schema.
const prompt = `You are provided with incomplete data: ${JSON.stringify(data)}.
Please complete this data so that it matches exactly the following JSON structure: ${JSON.stringify(outputStructure)}.
${additionalPrompt ? additionalPrompt : ''}`;
// Call OpenAI's ChatGPT API.
return axios.post('https://api.openai.com/v1/chat/completions', {
model,
messages: [
{ role: 'user', content: prompt }
],
temperature,
max_tokens
}, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
}
}).then(response => {
const choices = response.data.choices;
if (choices && choices.length > 0) {
let generatedData;
try {
generatedData = JSON.parse(choices[0].message.content);
} catch (e) {
throw new Error("Failed to parse ChatGPT response as JSON: " + e.message);
}
// Use the generatedData as the final payload data.
return this.axiosInstance.post(url, generatedData, config)
.then(res => res.data);
} else {
throw new Error("No response from ChatGPT");
}
});
} else {
// Fallback: if no chatGptConfig provided, simply post the original data.
return this.axiosInstance.post(url, data, config).then(response => response.data);
}
}
// --- Interactive Helper Methods for MultiPost ---
/**
* askQuestion(query)
* Prompts the user via the terminal for input.
*/
static askQuestion(query) {
return new Promise(resolve => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question(query, answer => {
rl.close();
resolve(answer);
});
});
}
/**
* promptForMissingFields(missingFields)
* Prompts the user to supply values for each missing field.
*/
static async promptForMissingFields(missingFields) {
const responses = {};
for (const field of missingFields) {
const answer = await AdvancedAxios.askQuestion(`Please enter a value for "${field}": `);
responses[field] = answer;
}
return responses;
}
}
// --- MultiPost Middleware for Express ---
// In-memory session store (for demonstration purposes)
const sessions = {};
function generateSessionId() {
return Math.random().toString(36).substring(2, 15);
}
/**
* multiPostMiddleware(requiredFields)
* Express middleware for multi-step POST endpoints.
*/
function multiPostMiddleware(requiredFields) {
return (req, res, next) => {
let sessionId = req.body.sessionId || generateSessionId();
req.sessionId = sessionId;
if (!sessions[sessionId]) {
sessions[sessionId] = {};
console.log(`Created new session: ${sessionId}`);
}
sessions[sessionId] = { ...sessions[sessionId], ...req.body.data };
req.multiPostData = sessions[sessionId];
const missingFields = requiredFields.filter(field => !req.multiPostData[field]);
if (missingFields.length > 0) {
console.log(`Session ${sessionId} pending. Missing fields:`, missingFields);
return res.json({
sessionId,
status: 'pending',
missingFields
});
}
delete sessions[sessionId];
next();
};
}
AdvancedAxios.multiPostMiddleware = multiPostMiddleware;
module.exports = AdvancedAxios;