@chenchaolong/plugin-mineru 1.1.0 → 1.1.2
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mineru.client.d.ts","sourceRoot":"","sources":["../../src/lib/mineru.client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAmB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAc,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAK7C,OAAO,EAIL,wBAAwB,EAExB,0BAA0B,EAC1B,gBAAgB,EACjB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"mineru.client.d.ts","sourceRoot":"","sources":["../../src/lib/mineru.client.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,10 +3,13 @@ import { getErrorMessage } from '@xpert-ai/plugin-sdk';
|
|
|
3
3
|
import axios from 'axios';
|
|
4
4
|
import FormData from 'form-data';
|
|
5
5
|
import { randomUUID } from 'crypto';
|
|
6
|
-
import { basename } from 'path';
|
|
6
|
+
import { basename, isAbsolute } from 'path';
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import { ENV_MINERU_API_BASE_URL, ENV_MINERU_API_TOKEN, ENV_MINERU_SERVER_TYPE, } from './types.js';
|
|
9
9
|
const DEFAULT_OFFICIAL_BASE_URL = 'https://mineru.net/api/v4';
|
|
10
|
+
// Default base URL for self-hosted MinerU deployments
|
|
11
|
+
// 自托管 MinerU 部署的默认基础 URL
|
|
12
|
+
const DEFAULT_SELF_HOSTED_BASE_URL = 'http://localhost:8000';
|
|
10
13
|
export class MinerUClient {
|
|
11
14
|
get fileSystem() {
|
|
12
15
|
return this.permissions?.fileSystem;
|
|
@@ -182,7 +185,7 @@ export class MinerUClient {
|
|
|
182
185
|
const tokenFromEnv = this.configService.get(tokenEnvKey);
|
|
183
186
|
const baseUrl = baseUrlFromIntegration ||
|
|
184
187
|
baseUrlFromEnv ||
|
|
185
|
-
(this.serverType === 'official' ? DEFAULT_OFFICIAL_BASE_URL :
|
|
188
|
+
(this.serverType === 'official' ? DEFAULT_OFFICIAL_BASE_URL : DEFAULT_SELF_HOSTED_BASE_URL);
|
|
186
189
|
const token = tokenFromIntegration || tokenFromEnv;
|
|
187
190
|
return { baseUrl, token };
|
|
188
191
|
}
|
|
@@ -252,22 +255,60 @@ export class MinerUClient {
|
|
|
252
255
|
}
|
|
253
256
|
async createSelfHostedTask(options) {
|
|
254
257
|
// Validate fileSystem is available for self-hosted mode
|
|
258
|
+
// 验证文件系统是否可用于自托管模式
|
|
255
259
|
if (!this.fileSystem) {
|
|
256
260
|
throw new Error('MinerU self-hosted mode requires fileSystem permission');
|
|
257
261
|
}
|
|
258
262
|
// Validate filePath is provided
|
|
263
|
+
// 验证是否提供了文件路径
|
|
259
264
|
if (!options.filePath) {
|
|
260
265
|
throw new Error('MinerU self-hosted mode requires filePath to be provided');
|
|
261
266
|
}
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
//
|
|
267
|
+
// Resolve absolute file path
|
|
268
|
+
// 解析绝对文件路径
|
|
269
|
+
// Check if filePath is already an absolute path
|
|
270
|
+
// 检查 filePath 是否已经是绝对路径
|
|
271
|
+
const isAbsolutePath = isAbsolute(options.filePath);
|
|
272
|
+
// Also check if it looks like a full path even without leading slash
|
|
273
|
+
// 也检查它是否看起来像完整路径(即使没有前导斜杠)
|
|
274
|
+
const looksLikeFullPath = !isAbsolutePath && (options.filePath.includes('/apps/api/public/') ||
|
|
275
|
+
options.filePath.includes('/public/files/') ||
|
|
276
|
+
options.filePath.includes('/项目/') ||
|
|
277
|
+
options.filePath.includes('/xpert-dev/') ||
|
|
278
|
+
options.filePath.startsWith('Users/') ||
|
|
279
|
+
options.filePath.startsWith('home/'));
|
|
280
|
+
let filePath;
|
|
281
|
+
if (isAbsolutePath) {
|
|
282
|
+
// Use absolute path directly
|
|
283
|
+
// 直接使用绝对路径
|
|
284
|
+
filePath = options.filePath;
|
|
285
|
+
}
|
|
286
|
+
else if (looksLikeFullPath) {
|
|
287
|
+
// If it looks like a full path but doesn't start with /, add it
|
|
288
|
+
// 如果看起来像完整路径但没有以 / 开头,添加它
|
|
289
|
+
filePath = options.filePath.startsWith('/') ? options.filePath : '/' + options.filePath;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
// Use xpFileSystem.fullPath() to resolve relative path to absolute path
|
|
293
|
+
// 使用 xpFileSystem.fullPath() 将相对路径解析为绝对路径
|
|
294
|
+
filePath = this.fileSystem.fullPath(options.filePath);
|
|
295
|
+
}
|
|
296
|
+
// Validate file exists and is readable before attempting to parse
|
|
265
297
|
try {
|
|
266
|
-
await fs.promises.access(filePath, fs.constants.F_OK);
|
|
298
|
+
await fs.promises.access(filePath, fs.constants.F_OK | fs.constants.R_OK);
|
|
299
|
+
const stats = await fs.promises.stat(filePath);
|
|
300
|
+
this.logger.debug(`Processing file: ${filePath}, size: ${stats.size} bytes`);
|
|
301
|
+
if (stats.size === 0) {
|
|
302
|
+
throw new Error(`File is empty: ${filePath}`);
|
|
303
|
+
}
|
|
267
304
|
}
|
|
268
305
|
catch (error) {
|
|
269
|
-
|
|
270
|
-
|
|
306
|
+
if (error instanceof Error && error.message.includes('empty')) {
|
|
307
|
+
this.logger.error(`File is empty: ${filePath}`);
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
this.logger.error(`File not found or not readable: ${filePath}`, error instanceof Error ? error.stack : error);
|
|
311
|
+
throw new Error(`File not found or not readable: ${filePath}`);
|
|
271
312
|
}
|
|
272
313
|
const taskId = randomUUID();
|
|
273
314
|
const result = await this.invokeSelfHostedParse(filePath, options.fileName || basename(filePath), options);
|
|
@@ -276,11 +317,18 @@ export class MinerUClient {
|
|
|
276
317
|
}
|
|
277
318
|
async invokeSelfHostedParse(filePath, fileName, options) {
|
|
278
319
|
const parseUrl = this.buildApiUrl('file_parse');
|
|
320
|
+
this.logger.debug(`Sending parse request to: ${parseUrl}, file: ${fileName}`);
|
|
279
321
|
const form = new FormData();
|
|
280
322
|
// Create file read stream (file existence is already validated in createSelfHostedTask)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
323
|
+
try {
|
|
324
|
+
form.append('files', fs.createReadStream(filePath), {
|
|
325
|
+
filename: fileName,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
this.logger.error(`Failed to create read stream for file: ${filePath}`, error instanceof Error ? error.stack : error);
|
|
330
|
+
throw new Error(`Failed to read file: ${filePath}. ${error instanceof Error ? error.message : String(error)}`);
|
|
331
|
+
}
|
|
284
332
|
// form.append('files', fileBuffer, { filename: fileName, contentType: contentType || 'application/pdf' });
|
|
285
333
|
form.append('parse_method', options.parseMethod ?? 'auto');
|
|
286
334
|
form.append('return_md', 'true');
|
|
@@ -314,8 +362,21 @@ export class MinerUClient {
|
|
|
314
362
|
}
|
|
315
363
|
if (response.status !== 200) {
|
|
316
364
|
const errorMessage = getErrorMessage(response.data) || response.statusText;
|
|
317
|
-
|
|
318
|
-
|
|
365
|
+
const errorDetails = typeof response.data === 'object' ? JSON.stringify(response.data) : String(response.data);
|
|
366
|
+
this.logger.error(`MinerU self-hosted parse failed with ${response.status}: ${errorMessage}`, `Request URL: ${parseUrl}, File: ${fileName}, Details: ${errorDetails}`);
|
|
367
|
+
// Provide more helpful error message for common issues
|
|
368
|
+
let userFriendlyMessage = `MinerU self-hosted parse failed: ${response.status} ${response.statusText}`;
|
|
369
|
+
if (errorMessage) {
|
|
370
|
+
userFriendlyMessage += `. ${errorMessage}`;
|
|
371
|
+
}
|
|
372
|
+
// Check for specific error patterns
|
|
373
|
+
if (errorMessage && errorMessage.includes('0 active models')) {
|
|
374
|
+
userFriendlyMessage += ' Please ensure MinerU service has active models configured.';
|
|
375
|
+
}
|
|
376
|
+
else if (errorMessage && errorMessage.includes('NoneType')) {
|
|
377
|
+
userFriendlyMessage += ' This may indicate a configuration issue with the MinerU service.';
|
|
378
|
+
}
|
|
379
|
+
throw new Error(userFriendlyMessage);
|
|
319
380
|
}
|
|
320
381
|
return this.normalizeSelfHostedResponse(response.data);
|
|
321
382
|
}
|