spice-js 2.6.39 → 2.6.40
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/build/artificial_intelligence/AI.js +296 -0
- package/build/config/ai.js +3 -0
- package/build/index.js +8 -2
- package/build/mail/providers/File.js +9 -5
- package/build/models/SpiceModel.js +147 -265
- package/build/storage/providers/Local.js +6 -12
- package/build/utility/RestHelper.js +9 -3
- package/package.json +13 -13
- package/src/artificial_intelligence/Ai.js +244 -0
- package/src/config/ai.js +3 -0
- package/src/index.js +2 -1
- package/src/mail/providers/File.js +28 -28
- package/src/models/SpiceModel.js +174 -290
- package/src/storage/providers/Local.js +69 -60
- package/src/utility/RestHelper.js +33 -29
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.default = void 0;
|
|
5
5
|
|
|
6
|
-
var _flat = _interopRequireDefault(require("flat"));
|
|
7
|
-
|
|
8
6
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
9
7
|
|
|
10
8
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
9
|
|
|
10
|
+
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
|
|
11
|
+
|
|
12
|
+
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
|
+
|
|
12
14
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
13
15
|
|
|
14
16
|
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
@@ -75,8 +77,12 @@ class RestHelper {
|
|
|
75
77
|
var content;
|
|
76
78
|
|
|
77
79
|
if (download_type == "csv") {
|
|
80
|
+
var {
|
|
81
|
+
flatten
|
|
82
|
+
} = yield Promise.resolve().then(() => _interopRequireWildcard(require("flat")));
|
|
83
|
+
|
|
78
84
|
var items = _lodash.default.map(ctx.data, item => {
|
|
79
|
-
return (
|
|
85
|
+
return flatten(item);
|
|
80
86
|
});
|
|
81
87
|
|
|
82
88
|
var fields = _lodash.default.union(_lodash.default.keys(_lodash.default.first(items)), _lodash.default.keys(_lodash.default.last(items)), _lodash.default.keys(_lodash.default.nth(items.length / 2)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spice-js",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.40",
|
|
4
4
|
"description": "spice",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -14,28 +14,28 @@
|
|
|
14
14
|
"author": "Chad Fraser <chad@sonover.com> (http://sonover.com)",
|
|
15
15
|
"license": "ISC",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"agenda": "^
|
|
17
|
+
"agenda": "^5.0.0",
|
|
18
18
|
"co": "^4.6.0",
|
|
19
|
-
"dotenv": "^
|
|
20
|
-
"flat": "^
|
|
21
|
-
"fs-extra": "^
|
|
19
|
+
"dotenv": "^16.4.5",
|
|
20
|
+
"flat": "^6.0.1",
|
|
21
|
+
"fs-extra": "^11.2.0",
|
|
22
22
|
"json2csv": "^4.5.4",
|
|
23
|
-
"juice": "^
|
|
23
|
+
"juice": "^11.0.0",
|
|
24
24
|
"kcors": "^2.2.2",
|
|
25
|
-
"koa-body": "^
|
|
26
|
-
"koa-convert": "^
|
|
27
|
-
"koa-qs": "^
|
|
28
|
-
"koa-socket-2": "^
|
|
25
|
+
"koa-body": "^6.0.1",
|
|
26
|
+
"koa-convert": "^2.0.0",
|
|
27
|
+
"koa-qs": "^3.0.0",
|
|
28
|
+
"koa-socket-2": "^2.0.0",
|
|
29
29
|
"koa-static": "^5.0.0",
|
|
30
30
|
"koa-validation": "^0.1.9",
|
|
31
31
|
"koa-views": "^6.2.3",
|
|
32
|
-
"koa2-swagger-ui": "^5.
|
|
32
|
+
"koa2-swagger-ui": "^5.10.0",
|
|
33
33
|
"lodash": "^4.17.15",
|
|
34
34
|
"node-cache": "^5.1.2",
|
|
35
35
|
"nunjucks": "^3.2.0",
|
|
36
|
-
"open": "
|
|
36
|
+
"open": "10.1.0",
|
|
37
37
|
"parse-comments": "^1.0.0",
|
|
38
|
-
"regenerator-runtime": "^0.
|
|
38
|
+
"regenerator-runtime": "^0.14.1",
|
|
39
39
|
"socket.io-client": "^4.0.1",
|
|
40
40
|
"sonover-date": "^1.0.1",
|
|
41
41
|
"uuid": "^3.3.3",
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
const crypto = require("crypto");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
import fs, { promises, WriteStream } from "fs";
|
|
5
|
+
import { pipeline } from "stream/promises";
|
|
6
|
+
import { spawn } from "child_process";
|
|
7
|
+
|
|
8
|
+
export default class AI {
|
|
9
|
+
constructor(args = {}) {
|
|
10
|
+
this.provider = args.provider || spice.config.ai.default_driver;
|
|
11
|
+
console.log(this.provider, args.provider, spice.config.ai.default_driver);
|
|
12
|
+
this.options = _.merge(
|
|
13
|
+
spice.config.ai.drivers[this.provider],
|
|
14
|
+
args.options
|
|
15
|
+
);
|
|
16
|
+
//console.log(this.provider_name, spice.config.ai.providers)
|
|
17
|
+
this.Provider = spice.config.ai.providers[this.provider];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
createUniqueDirectory() {
|
|
21
|
+
const timestamp = new Date().toISOString().replace(/[:.-]/g, "");
|
|
22
|
+
const randomString = crypto.randomBytes(4).toString("hex");
|
|
23
|
+
const uniqueDirName = `job_${timestamp}_${randomString}`;
|
|
24
|
+
const project_root = path.resolve(spice.root_path, "..");
|
|
25
|
+
const storageDir = path.join(project_root, "storage/jobs");
|
|
26
|
+
const uniqueDir = path.join(storageDir, uniqueDirName);
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(storageDir)) {
|
|
29
|
+
fs.mkdirSync(storageDir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
fs.mkdirSync(uniqueDir);
|
|
32
|
+
|
|
33
|
+
return uniqueDir;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async extractOrStoreFile(fileStream, outputDir, supportedFileTypes) {
|
|
37
|
+
// Ensure the output directory exists
|
|
38
|
+
if (!fs.existsSync(outputDir)) {
|
|
39
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Create a temporary file to store the input
|
|
43
|
+
const tempFilePath = path.join(outputDir, "temp_input");
|
|
44
|
+
await pipeline(fileStream, fs.createWriteStream(tempFilePath));
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Check file type
|
|
48
|
+
const fileType = await this.getFileType(tempFilePath);
|
|
49
|
+
|
|
50
|
+
if (_.includes(supportedFileTypes, fileType)) {
|
|
51
|
+
// If it's a JPEG or PNG, just move it to the output directory
|
|
52
|
+
const timestamp = new Date().toISOString().replace(/[:.-]/g, "");
|
|
53
|
+
const extension = fileType === "jpeg" ? "jpg" : fileType;
|
|
54
|
+
const outputPath = path.join(outputDir, `${timestamp}.${extension}`);
|
|
55
|
+
fs.renameSync(tempFilePath, outputPath);
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
58
|
+
type: fileType,
|
|
59
|
+
media_type: this.getMimeType(fileType),
|
|
60
|
+
path: outputPath,
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
} else {
|
|
64
|
+
// If it's a PDF, extract pages
|
|
65
|
+
if (fileType === "pdf") {
|
|
66
|
+
const outputPrefix = path.join(outputDir, "page");
|
|
67
|
+
|
|
68
|
+
// Use pdftoppm (part of poppler-utils) to convert PDF to images
|
|
69
|
+
await new Promise((resolve, reject) => {
|
|
70
|
+
const process = spawn("pdftoppm", [
|
|
71
|
+
"-jpeg",
|
|
72
|
+
tempFilePath,
|
|
73
|
+
outputPrefix,
|
|
74
|
+
]);
|
|
75
|
+
process.on("close", (code) => {
|
|
76
|
+
if (code === 0) resolve();
|
|
77
|
+
else
|
|
78
|
+
reject(new Error(`pdftoppm process exited with code ${code}`));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Get a list of extracted images
|
|
83
|
+
const files = fs.readdirSync(outputDir);
|
|
84
|
+
const images = files.filter(
|
|
85
|
+
(file) => file.startsWith("page") && file.endsWith(".jpg")
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return images.map((image) => {
|
|
89
|
+
return {
|
|
90
|
+
type: "jpeg",
|
|
91
|
+
media_type: this.getMimeType("jpeg"),
|
|
92
|
+
path: path.join(outputDir, image),
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
throw new Error(
|
|
97
|
+
"Unsupported file type. Only PDF, JPEG, and PNG are supported."
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error("Error processing file:", error);
|
|
103
|
+
throw error;
|
|
104
|
+
} finally {
|
|
105
|
+
// Clean up the temporary input file
|
|
106
|
+
if (fs.existsSync(tempFilePath)) {
|
|
107
|
+
fs.unlinkSync(tempFilePath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getMimeType(fileType) {
|
|
113
|
+
// Mapping of file extensions to MIME types
|
|
114
|
+
const mimeTypes = {
|
|
115
|
+
jpeg: "image/jpeg",
|
|
116
|
+
jpg: "image/jpeg", // Adding 'jpg' as it's a common alias for 'jpeg'
|
|
117
|
+
png: "image/png",
|
|
118
|
+
gif: "image/gif",
|
|
119
|
+
bmp: "image/bmp",
|
|
120
|
+
webp: "image/webp",
|
|
121
|
+
svg: "image/svg+xml",
|
|
122
|
+
pdf: "application/pdf",
|
|
123
|
+
txt: "text/plain",
|
|
124
|
+
html: "text/html",
|
|
125
|
+
css: "text/css",
|
|
126
|
+
js: "application/javascript",
|
|
127
|
+
json: "application/json",
|
|
128
|
+
xml: "application/xml",
|
|
129
|
+
csv: "text/csv",
|
|
130
|
+
mp3: "audio/mpeg",
|
|
131
|
+
wav: "audio/wav",
|
|
132
|
+
mp4: "video/mp4",
|
|
133
|
+
avi: "video/x-msvideo",
|
|
134
|
+
mov: "video/quicktime",
|
|
135
|
+
zip: "application/zip",
|
|
136
|
+
rar: "application/vnd.rar",
|
|
137
|
+
"7z": "application/x-7z-compressed",
|
|
138
|
+
// Add more MIME types as needed
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Ensure the fileType is a string and convert it to lowercase
|
|
142
|
+
if (typeof fileType !== "string") {
|
|
143
|
+
throw new TypeError("fileType must be a string");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ext = fileType.toLowerCase();
|
|
147
|
+
|
|
148
|
+
// Return the MIME type if found, otherwise return the default
|
|
149
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async getFileType(filePath) {
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
const fileSignature = Buffer.alloc(8);
|
|
155
|
+
fs.open(filePath, "r", (err, fd) => {
|
|
156
|
+
if (err) reject(err);
|
|
157
|
+
fs.read(fd, fileSignature, 0, 8, 0, (err) => {
|
|
158
|
+
if (err) reject(err);
|
|
159
|
+
fs.close(fd, (err) => {
|
|
160
|
+
if (err) console.error("Error closing file:", err);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const hex = fileSignature.toString("hex");
|
|
164
|
+
if (hex.startsWith("ffd8")) {
|
|
165
|
+
resolve("jpeg");
|
|
166
|
+
} else if (hex.startsWith("89504e47")) {
|
|
167
|
+
resolve("png");
|
|
168
|
+
} else if (fileSignature.toString("ascii").startsWith("%PDF")) {
|
|
169
|
+
resolve("pdf");
|
|
170
|
+
} else {
|
|
171
|
+
resolve("unknown");
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async deleteFolderRecursive(dirPath) {
|
|
179
|
+
if (fs.existsSync(dirPath)) {
|
|
180
|
+
for (const entry of fs.readdirSync(dirPath)) {
|
|
181
|
+
const entryPath = path.join(dirPath, entry);
|
|
182
|
+
if (fs.lstatSync(entryPath).isDirectory()) {
|
|
183
|
+
await this.deleteFolderRecursive(entryPath);
|
|
184
|
+
} else {
|
|
185
|
+
await fs.promises.unlink(entryPath);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
await fs.promises.rmdir(dirPath);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async processFiles(files) {
|
|
193
|
+
let types = this.getSupportedFileTypes();
|
|
194
|
+
const outputDir = this.createUniqueDirectory();
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
await Promise.all(
|
|
198
|
+
files.map(async (fileStream) => {
|
|
199
|
+
//let type = await this.getFileType(fileStream);
|
|
200
|
+
const imagePaths = await this.extractOrStoreFile(
|
|
201
|
+
fileStream,
|
|
202
|
+
outputDir,
|
|
203
|
+
types
|
|
204
|
+
);
|
|
205
|
+
return imagePaths;
|
|
206
|
+
})
|
|
207
|
+
)
|
|
208
|
+
).flat();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getSupportedFileTypes(options) {
|
|
212
|
+
if (this.Provider === undefined) {
|
|
213
|
+
throw new Error(`AI provider ${this.provider} is not defined`);
|
|
214
|
+
}
|
|
215
|
+
let provider = new this.Provider(this.options);
|
|
216
|
+
return provider.getSupportedTypes();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async prompt(prompt, { files, system_message, prompt_options }, options) {
|
|
220
|
+
let context_files = await this.processFiles(files);
|
|
221
|
+
try {
|
|
222
|
+
if (options) {
|
|
223
|
+
this.options = _.merge(this.options, options);
|
|
224
|
+
}
|
|
225
|
+
//console.log(this.Provider);
|
|
226
|
+
if (this.Provider === undefined) {
|
|
227
|
+
throw new Error(`AI provider ${this.provider} is not defined`);
|
|
228
|
+
}
|
|
229
|
+
let provider = new this.Provider(this.options);
|
|
230
|
+
return await provider.prompt(prompt, {
|
|
231
|
+
prompt_options,
|
|
232
|
+
system_message,
|
|
233
|
+
files: context_files,
|
|
234
|
+
});
|
|
235
|
+
} catch (e) {
|
|
236
|
+
console.error(e);
|
|
237
|
+
return { error: e.message };
|
|
238
|
+
} finally {
|
|
239
|
+
for (let context_file of context_files) {
|
|
240
|
+
await this.deleteFolderRecursive(context_file);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
package/src/config/ai.js
ADDED
package/src/index.js
CHANGED
|
@@ -7,7 +7,7 @@ global.spice = {};
|
|
|
7
7
|
require("dotenv").config({ path: path.resolve("./") });
|
|
8
8
|
|
|
9
9
|
var Validate = require("koa-validation");
|
|
10
|
-
const koaBody = require("koa-body");
|
|
10
|
+
const { koaBody } = require("koa-body");
|
|
11
11
|
spice.parent_app_dir = path.resolve("./");
|
|
12
12
|
spice.root_path = path.resolve("./build/");
|
|
13
13
|
spice.module_root_path = path.resolve(__dirname);
|
|
@@ -33,6 +33,7 @@ export { default as MailDebug } from "./mail/providers/Debug";
|
|
|
33
33
|
export { default as DebugStorage } from "./storage/providers/Debug";
|
|
34
34
|
export { default as LocalStorage } from "./storage/providers/Local";
|
|
35
35
|
export { default as Storage } from "./storage/Storage";
|
|
36
|
+
export { default as AI } from "./artificial_intelligence/Ai.js";
|
|
36
37
|
|
|
37
38
|
export { default as MailFile } from "./mail/providers/File";
|
|
38
39
|
export { default as SpiceCache } from "./cache/providers/SpiceCache";
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
let fs = require(
|
|
2
|
-
var path = require(
|
|
3
|
-
var open = require("open");
|
|
1
|
+
let fs = require("fs-extra");
|
|
2
|
+
var path = require("path");
|
|
4
3
|
|
|
5
|
-
export default class File{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
this.dest = path.resolve(args.path)
|
|
14
|
-
}
|
|
4
|
+
export default class File {
|
|
5
|
+
constructor(args = {}) {
|
|
6
|
+
this.sender = args.sender;
|
|
7
|
+
this.subject = args.subject;
|
|
8
|
+
this.htmlText = args.htmlText;
|
|
9
|
+
this.dest = path.join(path.resolve("./"), "storage/mail");
|
|
10
|
+
if (args.path) {
|
|
11
|
+
this.dest = path.resolve(args.path);
|
|
15
12
|
}
|
|
13
|
+
}
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
async send(htmlText, options = {}) {
|
|
16
|
+
const openModule = await import("open");
|
|
17
|
+
const open = openModule.default;
|
|
18
|
+
let email = {
|
|
19
|
+
to: options.to,
|
|
20
|
+
from: options.sender || this.sender,
|
|
21
|
+
subject: options.subject || this.subject,
|
|
22
|
+
htmlText: htmlText || this.htmlText,
|
|
23
|
+
};
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
fs.ensureDirSync(`${this.dest}/${email.subject}/${time}`);
|
|
28
|
-
let full_path = `${this.dest}/${email.subject}/${time}/to:${email.to} from:${email.from}.html`;
|
|
29
|
-
fs.writeFileSync(full_path, email.htmlText);
|
|
30
|
-
open(full_path);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
25
|
+
let time = new Date().getTime();
|
|
26
|
+
fs.ensureDirSync(`${this.dest}/${email.subject}/${time}`);
|
|
27
|
+
let full_path = `${this.dest}/${email.subject}/${time}/to:${email.to} from:${email.from}.html`;
|
|
28
|
+
fs.writeFileSync(full_path, email.htmlText);
|
|
29
|
+
|
|
30
|
+
open(full_path);
|
|
31
|
+
}
|
|
32
|
+
}
|