plugin-docpixie 1.0.0
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/client.d.ts +1 -0
- package/client.js +1 -0
- package/dist/client/index.js +10 -0
- package/dist/externalVersion.js +17 -0
- package/dist/index.js +48 -0
- package/dist/locale/en-US.json +21 -0
- package/dist/locale/vi-VN.json +21 -0
- package/dist/server/collections/docpixie-config.js +61 -0
- package/dist/server/collections/docpixie-documents.js +71 -0
- package/dist/server/collections/docpixie-pages.js +59 -0
- package/dist/server/exceptions.js +127 -0
- package/dist/server/index.js +49 -0
- package/dist/server/plugin.js +178 -0
- package/dist/server/prompts.js +388 -0
- package/dist/server/providers/index.js +36 -0
- package/dist/server/providers/llm-adapter.js +253 -0
- package/dist/server/services/DocPixieService.js +1300 -0
- package/dist/server/types.js +24 -0
- package/package.json +40 -0
- package/server.d.ts +1 -0
- package/server.js +1 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __create = Object.create;
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
29
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
30
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
31
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
32
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
33
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
34
|
+
mod
|
|
35
|
+
));
|
|
36
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
var llm_adapter_exports = {};
|
|
38
|
+
__export(llm_adapter_exports, {
|
|
39
|
+
NocoBaseLLMAdapter: () => NocoBaseLLMAdapter
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(llm_adapter_exports);
|
|
42
|
+
var fs = __toESM(require("fs"));
|
|
43
|
+
var path = __toESM(require("path"));
|
|
44
|
+
var import_messages = require("@langchain/core/messages");
|
|
45
|
+
var import_exceptions = require("../exceptions");
|
|
46
|
+
const MIME_TYPES = {
|
|
47
|
+
".jpg": "image/jpeg",
|
|
48
|
+
".jpeg": "image/jpeg",
|
|
49
|
+
".png": "image/png",
|
|
50
|
+
".gif": "image/gif",
|
|
51
|
+
".webp": "image/webp",
|
|
52
|
+
".bmp": "image/bmp",
|
|
53
|
+
".tiff": "image/tiff",
|
|
54
|
+
".tif": "image/tiff",
|
|
55
|
+
".pdf": "application/pdf"
|
|
56
|
+
};
|
|
57
|
+
class NocoBaseLLMAdapter {
|
|
58
|
+
textModel;
|
|
59
|
+
visionModel;
|
|
60
|
+
totalCost = 0;
|
|
61
|
+
/**
|
|
62
|
+
* @param textModel - LangChain BaseChatModel for text-only calls
|
|
63
|
+
* @param visionModel - LangChain BaseChatModel for vision/multimodal calls (can be same as textModel)
|
|
64
|
+
*/
|
|
65
|
+
constructor(textModel, visionModel) {
|
|
66
|
+
this.textModel = textModel;
|
|
67
|
+
this.visionModel = visionModel || textModel;
|
|
68
|
+
}
|
|
69
|
+
// ═══════════════════════════════════════════
|
|
70
|
+
// ILLMProvider Implementation
|
|
71
|
+
// ═══════════════════════════════════════════
|
|
72
|
+
/**
|
|
73
|
+
* Process text-only messages using the text model.
|
|
74
|
+
*/
|
|
75
|
+
async processTextMessages(messages, maxTokens = 500, temperature = 0.3) {
|
|
76
|
+
try {
|
|
77
|
+
const langchainMessages = this.convertToLangChainMessages(messages);
|
|
78
|
+
const model = this.textModel.bind({
|
|
79
|
+
max_tokens: maxTokens,
|
|
80
|
+
temperature
|
|
81
|
+
});
|
|
82
|
+
const result = await model.invoke(langchainMessages);
|
|
83
|
+
this.trackCost(result);
|
|
84
|
+
return this.extractContent(result);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
throw new import_exceptions.ProviderError(
|
|
87
|
+
`Text LLM call failed: ${err.message}`,
|
|
88
|
+
"nocobase-llm"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Process multimodal messages (text + images) using the vision model.
|
|
94
|
+
* Automatically converts `image_path` content items to base64 `image_url`.
|
|
95
|
+
*/
|
|
96
|
+
async processMultimodalMessages(messages, maxTokens = 500, temperature = 0.3) {
|
|
97
|
+
try {
|
|
98
|
+
const prepared = this.prepareMessages(messages);
|
|
99
|
+
const langchainMessages = this.convertToLangChainMessages(prepared);
|
|
100
|
+
const model = this.visionModel.bind({
|
|
101
|
+
max_tokens: maxTokens,
|
|
102
|
+
temperature
|
|
103
|
+
});
|
|
104
|
+
const result = await model.invoke(langchainMessages);
|
|
105
|
+
this.trackCost(result);
|
|
106
|
+
return this.extractContent(result);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
throw new import_exceptions.ProviderError(
|
|
109
|
+
`Vision LLM call failed: ${err.message}`,
|
|
110
|
+
"nocobase-llm"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Accumulated cost of all API calls (USD) */
|
|
115
|
+
getTotalCost() {
|
|
116
|
+
return this.totalCost;
|
|
117
|
+
}
|
|
118
|
+
/** Reset cost counter */
|
|
119
|
+
resetCost() {
|
|
120
|
+
this.totalCost = 0;
|
|
121
|
+
}
|
|
122
|
+
// ═══════════════════════════════════════════
|
|
123
|
+
// Message Conversion
|
|
124
|
+
// ═══════════════════════════════════════════
|
|
125
|
+
/**
|
|
126
|
+
* Convert DocPixie's {role, content} messages to LangChain message objects.
|
|
127
|
+
*/
|
|
128
|
+
convertToLangChainMessages(messages) {
|
|
129
|
+
return messages.map((msg) => {
|
|
130
|
+
switch (msg.role) {
|
|
131
|
+
case "system":
|
|
132
|
+
return new import_messages.SystemMessage(msg.content);
|
|
133
|
+
case "assistant":
|
|
134
|
+
return new import_messages.AIMessage(typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content));
|
|
135
|
+
case "user":
|
|
136
|
+
default:
|
|
137
|
+
return new import_messages.HumanMessage({ content: msg.content });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// ═══════════════════════════════════════════
|
|
142
|
+
// Image Processing (ported from OpenAICompatibleProvider)
|
|
143
|
+
// ═══════════════════════════════════════════
|
|
144
|
+
/**
|
|
145
|
+
* Walk through messages and convert `image_path` content items
|
|
146
|
+
* to OpenAI-compatible `image_url` with base64 data URLs.
|
|
147
|
+
*
|
|
148
|
+
* Input format (DocPixie internal):
|
|
149
|
+
* ```json
|
|
150
|
+
* { "type": "image_path", "image_path": "/path/to/img.jpg", "detail": "high" }
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* Output format (OpenAI API):
|
|
154
|
+
* ```json
|
|
155
|
+
* { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,...", "detail": "high" } }
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
prepareMessages(messages) {
|
|
159
|
+
const prepared = [];
|
|
160
|
+
for (const message of messages) {
|
|
161
|
+
if (message.role === "system") {
|
|
162
|
+
prepared.push(message);
|
|
163
|
+
} else if (message.role === "user" && Array.isArray(message.content)) {
|
|
164
|
+
const processedContent = [];
|
|
165
|
+
for (const item of message.content) {
|
|
166
|
+
if (item.type === "text") {
|
|
167
|
+
processedContent.push(item);
|
|
168
|
+
} else if (item.type === "image_path") {
|
|
169
|
+
const imagePath = item.image_path;
|
|
170
|
+
if (this.validateImagePath(imagePath)) {
|
|
171
|
+
const dataUrl = this.createImageDataUrl(imagePath);
|
|
172
|
+
processedContent.push({
|
|
173
|
+
type: "image_url",
|
|
174
|
+
image_url: {
|
|
175
|
+
url: dataUrl,
|
|
176
|
+
detail: item.detail || "high"
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
processedContent.push(item);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
prepared.push({ role: message.role, content: processedContent });
|
|
185
|
+
} else {
|
|
186
|
+
prepared.push(message);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return prepared;
|
|
190
|
+
}
|
|
191
|
+
// ═══════════════════════════════════════════
|
|
192
|
+
// Image Helpers
|
|
193
|
+
// ═══════════════════════════════════════════
|
|
194
|
+
/** Encode an image file to base64 string */
|
|
195
|
+
encodeImage(imagePath) {
|
|
196
|
+
try {
|
|
197
|
+
const buffer = fs.readFileSync(imagePath);
|
|
198
|
+
return buffer.toString("base64");
|
|
199
|
+
} catch (err) {
|
|
200
|
+
throw new import_exceptions.ProviderError(
|
|
201
|
+
`Failed to encode image ${imagePath}: ${err.message}`,
|
|
202
|
+
"nocobase-llm"
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** Create a data URL for an image (data:image/jpeg;base64,...) */
|
|
207
|
+
createImageDataUrl(imagePath) {
|
|
208
|
+
const ext = path.extname(imagePath).toLowerCase();
|
|
209
|
+
const mime = MIME_TYPES[ext] || "image/jpeg";
|
|
210
|
+
const encoded = this.encodeImage(imagePath);
|
|
211
|
+
return `data:${mime};base64,${encoded}`;
|
|
212
|
+
}
|
|
213
|
+
/** Validate that an image path exists and is readable */
|
|
214
|
+
validateImagePath(imagePath) {
|
|
215
|
+
try {
|
|
216
|
+
fs.accessSync(imagePath, fs.constants.R_OK);
|
|
217
|
+
return true;
|
|
218
|
+
} catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// ═══════════════════════════════════════════
|
|
223
|
+
// Response Helpers
|
|
224
|
+
// ═══════════════════════════════════════════
|
|
225
|
+
/** Extract text content from a LangChain response */
|
|
226
|
+
extractContent(result) {
|
|
227
|
+
if (typeof result === "string") return result.trim();
|
|
228
|
+
if (result == null ? void 0 : result.content) {
|
|
229
|
+
if (typeof result.content === "string") return result.content.trim();
|
|
230
|
+
if (Array.isArray(result.content)) {
|
|
231
|
+
return result.content.filter((block) => block.type === "text").map((block) => block.text || "").join("").trim();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (result == null ? void 0 : result.text) return result.text.trim();
|
|
235
|
+
throw new import_exceptions.ProviderError(
|
|
236
|
+
`Unexpected LLM response format: ${JSON.stringify(result).substring(0, 200)}`,
|
|
237
|
+
"nocobase-llm"
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
/** Track cost from LangChain response metadata */
|
|
241
|
+
trackCost(result) {
|
|
242
|
+
const usage = result == null ? void 0 : result.usage_metadata;
|
|
243
|
+
if (usage) {
|
|
244
|
+
if (typeof usage.cost === "number") {
|
|
245
|
+
this.totalCost += usage.cost;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
251
|
+
0 && (module.exports = {
|
|
252
|
+
NocoBaseLLMAdapter
|
|
253
|
+
});
|