@hyperbytes/wappler-all-in-one-ai-v2 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/README.md +4 -1
- package/package.json +6 -6
- package/server_connect/modules/multiaiv2.js +66 -36
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
Details here.
|
|
2
|
+
|
|
3
|
+
https://community.wappler.io/t/wappler-all-in-one-ai-node-js-version-2-custom-server-extension/65956
|
|
4
|
+
|
|
2
5
|
<a href="https://www.buymeacoffee.com/JVKdouk" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="max-width: 50%;" ></a>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperbytes/wappler-all-in-one-ai-v2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Versitile interface to chatGPT, Gemini Claude with file analysis cababilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
"postinstall": "node scripts/copyFiles.js"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
|
-
"wappler-extension
|
|
13
|
+
"wappler-extension",
|
|
14
14
|
"multifunction-ai-connector"
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
18
18
|
"@google/generative-ai": "^0.24.1",
|
|
19
|
+
"fs": "^0.0.1-security",
|
|
19
20
|
"openai": "^6.15.0",
|
|
20
|
-
"sharp": "^0.34.5",
|
|
21
|
-
"xlsx": "^0.18.5",
|
|
22
|
-
"pdf-parse": "^2.4.5",
|
|
23
21
|
"path": "^0.12.7",
|
|
24
|
-
"
|
|
22
|
+
"pdf-parse": "^2.4.5",
|
|
23
|
+
"sharp": "^0.34.5",
|
|
24
|
+
"xlsx": "^0.18.5"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -5,66 +5,90 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const XLSX = require('xlsx');
|
|
7
7
|
const pdf = require('pdf-parse');
|
|
8
|
+
const { PDFExtract } = require('pdf.js-extract');
|
|
9
|
+
const pdfExtract = new PDFExtract();
|
|
8
10
|
const sharp = require('sharp');
|
|
9
11
|
|
|
12
|
+
|
|
10
13
|
const clamp = (val, min, max) => Math.max(min, Math.min(max, val));
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* HELPER: Compresses and converts image to Base64
|
|
14
17
|
*/
|
|
15
|
-
async function
|
|
18
|
+
async function getFileContent(filePath) {
|
|
16
19
|
try {
|
|
17
|
-
const
|
|
18
|
-
|
|
20
|
+
const cleanPath = filePath.startsWith('/') ? filePath.substring(1) : filePath;
|
|
21
|
+
const fullPath = path.join(process.cwd(), cleanPath);
|
|
19
22
|
|
|
20
23
|
if (!fs.existsSync(fullPath)) {
|
|
21
24
|
console.error("FILE NOT FOUND:", fullPath);
|
|
22
25
|
return null;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
.jpeg({ quality: 80 })
|
|
28
|
-
.toBuffer();
|
|
28
|
+
const ext = path.extname(fullPath).toLowerCase();
|
|
29
|
+
const dataBuffer = fs.readFileSync(fullPath);
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} catch (err) {
|
|
33
|
-
console.error("Sharp Compression Error:", err.message);
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
31
|
+
if (ext === '.pdf') {
|
|
32
|
+
console.log("--- ATTEMPTING PDF EXTRACTION (INTERNAL REQUIRE) ---");
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
try {
|
|
35
|
+
// FORCE LOAD here to bypass Wappler's scope issues
|
|
36
|
+
const pdfParser = require('pdf-parse');
|
|
37
|
+
|
|
38
|
+
// If pdfParser itself is an object containing the function (Wappler quirk)
|
|
39
|
+
const actualFunction = typeof pdfParser === 'function' ? pdfParser : pdfParser.default;
|
|
40
|
+
|
|
41
|
+
if (typeof actualFunction !== 'function') {
|
|
42
|
+
throw new Error("Library loaded but no function found.");
|
|
43
|
+
}
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
const data = await actualFunction(dataBuffer, {
|
|
46
|
+
pagerender: async function (pageData) {
|
|
47
|
+
const textContent = await pageData.getTextContent();
|
|
48
|
+
return textContent.items.map(s => s.str).join(' ');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!data || !data.text) {
|
|
53
|
+
return "[SYSTEM: PDF read but no text found - likely a scan.]";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const extractedText = data.text.trim();
|
|
57
|
+
console.log("SUCCESS: Extracted", extractedText.length, "characters.");
|
|
58
|
+
return extractedText;
|
|
59
|
+
|
|
60
|
+
} catch (innerError) {
|
|
61
|
+
console.error("INTERNAL PDF ERROR:", innerError.message);
|
|
62
|
+
return `[SYSTEM: Error reading PDF structure: ${innerError.message}]`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (ext === '.txt' || ext === '.csv') return dataBuffer.toString('utf8');
|
|
48
67
|
if (ext === '.xlsx' || ext === '.xls') {
|
|
49
|
-
const wb = XLSX.
|
|
68
|
+
const wb = XLSX.read(dataBuffer);
|
|
50
69
|
return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
|
|
51
70
|
}
|
|
52
|
-
if (ext === '.pdf') {
|
|
53
|
-
const data = await pdf(fs.readFileSync(fullPath));
|
|
54
|
-
return data.text;
|
|
55
|
-
}
|
|
56
|
-
} catch (e) { return null; }
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
71
|
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error("GENERAL EXTRACTION ERROR:", e.message);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
60
77
|
exports.multiaiv2 = async function (options) {
|
|
61
|
-
|
|
78
|
+
|
|
79
|
+
// 1. Ensure we grab the question correctly and append any training instrutions
|
|
80
|
+
let question = this.parseRequired(options.question, "*", 'No Question passed') + ' ' + this.parseOptional(options.mytraining, "*", '');
|
|
81
|
+
|
|
82
|
+
// 2. Fix the file input assignment
|
|
83
|
+
// Use the exact key from your console log
|
|
84
|
+
const fileInput = options.image_url;
|
|
85
|
+
|
|
62
86
|
const engine = this.parse(options.engine) || 'gpt-4o-mini';
|
|
63
87
|
const maxTokens = parseInt(this.parseOptional(options.maxtokens, "*", 2000));
|
|
64
88
|
const temp = parseFloat(this.parseOptional(options.temperature, "*", 0.7));
|
|
65
|
-
const fileInput = this.parse(options.image_url);
|
|
66
89
|
|
|
67
90
|
const modelName = engine.toLowerCase();
|
|
91
|
+
|
|
68
92
|
const isReasoningModel = modelName.includes('gpt-5') || modelName.startsWith('o1') || modelName.startsWith('o3');
|
|
69
93
|
|
|
70
94
|
console.log(`Starting Request - Model: ${engine}, File: ${fileInput}`);
|
|
@@ -80,14 +104,15 @@ exports.multiaiv2 = async function (options) {
|
|
|
80
104
|
|
|
81
105
|
try {
|
|
82
106
|
// --- OpenAI (GPT-4o, GPT-5, o1) ---
|
|
107
|
+
|
|
83
108
|
if (modelName.includes('gpt') || modelName.includes('o1') || modelName.includes('o3')) {
|
|
84
109
|
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
110
|
+
let userContent = [];
|
|
85
111
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (fileInput && /\.(jpg|jpeg|png|webp)$/i.test(fileInput) && !isReasoningModel) {
|
|
112
|
+
if (fileInput && /\.(jpg|jpeg|png|webp)$/i.test(fileInput)) {
|
|
89
113
|
const b64 = await getCompressedImageBase64(fileInput);
|
|
90
114
|
if (b64) {
|
|
115
|
+
// Push image first
|
|
91
116
|
userContent.push({
|
|
92
117
|
type: "image_url",
|
|
93
118
|
image_url: { url: b64, detail: "high" }
|
|
@@ -96,11 +121,16 @@ exports.multiaiv2 = async function (options) {
|
|
|
96
121
|
}
|
|
97
122
|
}
|
|
98
123
|
|
|
124
|
+
// Add text instructions after the image
|
|
125
|
+
userContent.push({ type: "text", text: question });
|
|
126
|
+
|
|
99
127
|
const payload = {
|
|
100
128
|
model: engine,
|
|
101
129
|
messages: [{ role: 'user', content: userContent }]
|
|
102
130
|
};
|
|
103
131
|
|
|
132
|
+
|
|
133
|
+
|
|
104
134
|
if (isReasoningModel) {
|
|
105
135
|
payload.max_completion_tokens = Math.max(maxTokens, 5000);
|
|
106
136
|
} else {
|