@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 CHANGED
@@ -1 +1,5 @@
1
- Pre release beta for testing
1
+ Release version.
2
+
3
+ Details here.
4
+
5
+ https://community.wappler.io/t/wappler-all-in-one-ai-node-js-version-2-custom-server-extension/65956
package/README.md CHANGED
@@ -1,2 +1,5 @@
1
- Pre release beta for testing
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",
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-beta",
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
- "fs": "^0.0.0"
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 getCompressedImageBase64(filePath) {
18
+ async function getFileContent(filePath) {
16
19
  try {
17
- const fullPath = path.join(process.cwd(), filePath);
18
- console.log("Checking for image at:", fullPath);
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 buffer = await sharp(fullPath)
26
- .resize(1500, 1500, { fit: 'inside', withoutEnlargement: true })
27
- .jpeg({ quality: 80 })
28
- .toBuffer();
28
+ const ext = path.extname(fullPath).toLowerCase();
29
+ const dataBuffer = fs.readFileSync(fullPath);
29
30
 
30
- console.log("Image compressed successfully. Size:", (buffer.length / 1024).toFixed(2), "KB");
31
- return `data:image/jpeg;base64,${buffer.toString('base64')}`;
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
- * HELPER: Extract text from Docs
40
- */
41
- async function getFileContent(filePath) {
42
- try {
43
- const fullPath = path.join(process.cwd(), filePath);
44
- if (!fs.existsSync(fullPath)) return null;
45
- const ext = path.extname(fullPath).toLowerCase();
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
- if (ext === '.txt' || ext === '.csv') return fs.readFileSync(fullPath, 'utf8');
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.readFile(fullPath);
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
- let question = this.parseRequired(options.question, "*", 'No Question passed');
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
- let userContent = [{ type: "text", text: question }];
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 {