plugin-custom-llm 1.2.1 → 1.2.3
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/dist/client/index.js +9 -0
- package/dist/externalVersion.js +16 -7
- package/dist/index.js +9 -0
- package/dist/server/index.js +9 -0
- package/dist/server/llm-providers/custom-llm.js +157 -5
- package/dist/server/plugin.js +9 -0
- package/dist/swagger.js +39 -0
- package/package.json +9 -1
- package/src/client/client.d.ts +249 -0
- package/src/client/index.tsx +19 -0
- package/src/client/llm-providers/custom-llm/ModelSettings.tsx +139 -0
- package/src/client/llm-providers/custom-llm/ProviderSettings.tsx +115 -0
- package/src/client/llm-providers/custom-llm/index.ts +10 -0
- package/src/client/locale.ts +8 -0
- package/{dist/client/models/index.d.ts → src/client/models/index.ts} +12 -10
- package/src/client/plugin.tsx +10 -0
- package/{dist/index.d.ts → src/index.ts} +2 -2
- package/src/locale/en-US.json +29 -0
- package/src/locale/vi-VN.json +29 -0
- package/src/locale/zh-CN.json +16 -0
- package/src/server/collections/.gitkeep +0 -0
- package/{dist/server/index.d.ts → src/server/index.ts} +1 -1
- package/src/server/llm-providers/custom-llm.ts +992 -0
- package/src/server/plugin.ts +27 -0
- package/src/swagger.ts +9 -0
- package/dist/client/index.d.ts +0 -8
- package/dist/client/llm-providers/custom-llm/ModelSettings.d.ts +0 -2
- package/dist/client/llm-providers/custom-llm/ProviderSettings.d.ts +0 -2
- package/dist/client/llm-providers/custom-llm/index.d.ts +0 -2
- package/dist/client/locale.d.ts +0 -2
- package/dist/client/plugin.d.ts +0 -5
- package/dist/server/llm-providers/custom-llm.d.ts +0 -54
- package/dist/server/plugin.d.ts +0 -12
package/dist/client/index.js
CHANGED
|
@@ -1 +1,10 @@
|
|
|
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
|
+
|
|
1
10
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react"),require("@nocobase/plugin-ai/client"),require("@nocobase/client"),require("@nocobase/utils/client"),require("antd"),require("react-i18next")):"function"==typeof define&&define.amd?define("plugin-custom-llm",["react","@nocobase/plugin-ai/client","@nocobase/client","@nocobase/utils/client","antd","react-i18next"],t):"object"==typeof exports?exports["plugin-custom-llm"]=t(require("react"),require("@nocobase/plugin-ai/client"),require("@nocobase/client"),require("@nocobase/utils/client"),require("antd"),require("react-i18next")):e["plugin-custom-llm"]=t(e.react,e["@nocobase/plugin-ai/client"],e["@nocobase/client"],e["@nocobase/utils/client"],e.antd,e["react-i18next"])}(self,function(e,t,n,o,r,i){return function(){"use strict";var a={772:function(e){e.exports=n},645:function(e){e.exports=t},584:function(e){e.exports=o},721:function(e){e.exports=r},156:function(t){t.exports=e},238:function(e){e.exports=i}},c={};function u(e){var t=c[e];if(void 0!==t)return t.exports;var n=c[e]={exports:{}};return a[e](n,n.exports,u),n.exports}u.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(t,{a:t}),t},u.d=function(e,t){for(var n in t)u.o(t,n)&&!u.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},u.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var l={};return!function(){u.r(l),u.d(l,{PluginCustomLLMClient:function(){return g},default:function(){return S}});var e=u(772),t=u(156),n=u.n(t),o=u(584),r=u(238),i="@nocobase/plugin-custom-llm",a=u(721),c=u(645),p=function(){var t=(0,r.useTranslation)(i,{nsMode:"fallback"}).t;return n().createElement("div",{style:{marginBottom:24}},n().createElement(a.Collapse,{bordered:!1,size:"small",items:[{key:"options",label:t("Options"),forceRender:!0,children:n().createElement(e.SchemaComponent,{schema:{type:"void",name:"custom-llm",properties:{temperature:{title:(0,o.tval)("Temperature",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber",default:.7,"x-component-props":{step:.1,min:0,max:2}},maxCompletionTokens:{title:(0,o.tval)("Max completion tokens",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber",default:-1},topP:{title:(0,o.tval)("Top P",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber",default:1,"x-component-props":{step:.1,min:0,max:1}},frequencyPenalty:{title:(0,o.tval)("Frequency penalty",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber",default:0,"x-component-props":{step:.1,min:-2,max:2}},presencePenalty:{title:(0,o.tval)("Presence penalty",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber",default:0,"x-component-props":{step:.1,min:-2,max:2}},responseFormat:{title:(0,o.tval)("Response format",{ns:i}),type:"string","x-decorator":"FormItem","x-component":"Select",enum:[{label:t("Text"),value:"text"},{label:t("JSON"),value:"json_object"}],default:"text"},timeout:{title:(0,o.tval)("Timeout (ms)",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber",default:6e4},maxRetries:{title:(0,o.tval)("Max retries",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber",default:1}}}})}]}))},s={components:{ProviderSettingsForm:function(){return n().createElement(e.SchemaComponent,{schema:{type:"void",properties:{apiKey:{title:(0,o.tval)("API Key",{ns:i}),type:"string",required:!0,"x-decorator":"FormItem","x-component":"TextAreaWithGlobalScope"},disableStream:{title:(0,o.tval)("Disable streaming",{ns:i}),type:"boolean","x-decorator":"FormItem","x-component":"Checkbox","x-content":(0,o.tval)("Disable streaming description",{ns:i})},streamKeepAlive:{title:(0,o.tval)("Stream keepalive",{ns:i}),type:"boolean","x-decorator":"FormItem","x-component":"Checkbox","x-content":(0,o.tval)("Stream keepalive description",{ns:i})},keepAliveIntervalMs:{title:(0,o.tval)("Keepalive interval (ms)",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber","x-component-props":{placeholder:"5000",min:1e3,step:1e3,style:{width:"100%"}},description:(0,o.tval)("Keepalive interval description",{ns:i})},keepAliveContent:{title:(0,o.tval)("Keepalive content",{ns:i}),type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:"..."},description:(0,o.tval)("Keepalive content description",{ns:i})},timeout:{title:(0,o.tval)("Timeout (ms)",{ns:i}),type:"number","x-decorator":"FormItem","x-component":"InputNumber","x-component-props":{placeholder:"120000",min:0,step:1e3,style:{width:"100%"}},description:(0,o.tval)("Timeout description",{ns:i})},requestConfig:{title:(0,o.tval)("Request config (JSON)",{ns:i}),type:"string","x-decorator":"FormItem","x-component":"Input.TextArea","x-component-props":{placeholder:JSON.stringify({extraHeaders:{},extraBody:{},modelKwargs:{}},null,2),rows:6,style:{fontFamily:"monospace",fontSize:12}},description:(0,o.tval)("Request config description",{ns:i})},responseConfig:{title:(0,o.tval)("Response config (JSON)",{ns:i}),type:"string","x-decorator":"FormItem","x-component":"Input.TextArea","x-component-props":{placeholder:JSON.stringify({contentPath:"auto",reasoningKey:"reasoning_content",responseMapping:{content:"message.response"}},null,2),rows:8,style:{fontFamily:"monospace",fontSize:12}},description:(0,o.tval)("Response config description",{ns:i})}}}})},ModelSettingsForm:function(){return n().createElement(e.SchemaComponent,{components:{Options:p,ModelSelect:c.ModelSelect},schema:{type:"void",properties:{model:{title:(0,o.tval)("Model",{ns:i}),type:"string",required:!0,"x-decorator":"FormItem","x-component":"ModelSelect"},options:{type:"void","x-component":"Options"}}}})}}};function m(e,t,n,o,r,i,a){try{var c=e[i](a),u=c.value}catch(e){n(e);return}c.done?t(u):Promise.resolve(u).then(o,r)}function f(e){return function(){var t=this,n=arguments;return new Promise(function(o,r){var i=e.apply(t,n);function a(e){m(i,o,r,a,c,"next",e)}function c(e){m(i,o,r,a,c,"throw",e)}a(void 0)})}}function d(e,t,n){return(d=x()?Reflect.construct:function(e,t,n){var o=[null];o.push.apply(o,t);var r=new(Function.bind.apply(e,o));return n&&b(r,n.prototype),r}).apply(null,arguments)}function y(e){return(y=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function b(e,t){return(b=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function v(e){var t="function"==typeof Map?new Map:void 0;return(v=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return d(e,arguments,y(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),b(n,e)})(e)}function x(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(x=function(){return!!e})()}function h(e,t){var n,o,r,i,a={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){var u=[i,c];if(n)throw TypeError("Generator is already executing.");for(;a;)try{if(n=1,o&&(r=2&u[0]?o.return:u[0]?o.throw||((r=o.return)&&r.call(o),0):o.next)&&!(r=r.call(o,u[1])).done)return r;switch(o=0,r&&(u=[2&u[0],r.value]),u[0]){case 0:case 1:r=u;break;case 4:return a.label++,{value:u[1],done:!1};case 5:a.label++,o=u[1],u=[0];continue;case 7:u=a.ops.pop(),a.trys.pop();continue;default:if(!(r=(r=a.trys).length>0&&r[r.length-1])&&(6===u[0]||2===u[0])){a=0;continue}if(3===u[0]&&(!r||u[1]>r[0]&&u[1]<r[3])){a.label=u[1];break}if(6===u[0]&&a.label<r[1]){a.label=r[1],r=u;break}if(r&&a.label<r[2]){a.label=r[2],a.ops.push(u);break}r[2]&&a.ops.pop(),a.trys.pop();continue}u=t.call(e,a)}catch(e){u=[6,e],o=0}finally{n=r=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}}}var g=function(e){var t;if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");function n(){var e,t;if(!(this instanceof n))throw TypeError("Cannot call a class as a function");return e=n,t=arguments,e=y(e),function(e,t){var n;if(t&&("object"==((n=t)&&"undefined"!=typeof Symbol&&n.constructor===Symbol?"symbol":typeof n)||"function"==typeof t))return t;if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(this,x()?Reflect.construct(e,t||[],y(this).constructor):e.apply(this,t))}return n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),e&&b(n,e),t=[{key:"afterAdd",value:function(){return f(function(){return h(this,function(e){return[2]})})()}},{key:"beforeLoad",value:function(){return f(function(){return h(this,function(e){return[2]})})()}},{key:"load",value:function(){var e=this;return f(function(){return h(this,function(t){return e.aiPlugin.aiManager.registerLLMProvider("custom-llm",s),[2]})})()}},{key:"aiPlugin",get:function(){return this.app.pm.get("ai")}}],function(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}(n.prototype,t),n}(v(e.Plugin)),S=g}(),l}()});
|
package/dist/externalVersion.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
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
|
+
|
|
1
10
|
module.exports = {
|
|
2
|
-
"@nocobase/client": "2.0.
|
|
3
|
-
"@nocobase/plugin-ai": "2.0.
|
|
11
|
+
"@nocobase/client": "2.0.32",
|
|
12
|
+
"@nocobase/plugin-ai": "2.0.32",
|
|
4
13
|
"react-i18next": "11.18.6",
|
|
5
|
-
"@nocobase/server": "2.0.
|
|
6
|
-
"@nocobase/flow-engine": "2.0.
|
|
7
|
-
"@nocobase/database": "2.0.
|
|
14
|
+
"@nocobase/server": "2.0.32",
|
|
15
|
+
"@nocobase/flow-engine": "2.0.32",
|
|
16
|
+
"@nocobase/database": "2.0.32",
|
|
8
17
|
"axios": "1.14.0",
|
|
9
|
-
"@nocobase/actions": "2.0.
|
|
18
|
+
"@nocobase/actions": "2.0.32",
|
|
10
19
|
"react": "18.3.1",
|
|
11
|
-
"@nocobase/utils": "2.0.
|
|
20
|
+
"@nocobase/utils": "2.0.32",
|
|
12
21
|
"antd": "5.24.2"
|
|
13
22
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
package/dist/server/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -1,3 +1,12 @@
|
|
|
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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -560,7 +569,13 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
560
569
|
* We cannot fix that in plugin-ai (core), so we re-implement file reading here
|
|
561
570
|
* with the prefix stripped before the cwd join.
|
|
562
571
|
*/
|
|
563
|
-
|
|
572
|
+
/**
|
|
573
|
+
* Reads the attachment and returns its base64-encoded content plus, when the
|
|
574
|
+
* file lives on the local filesystem, the resolved absolute path so callers
|
|
575
|
+
* can hand that path directly to tools like DocPixie and avoid a second
|
|
576
|
+
* write-to-disk round-trip.
|
|
577
|
+
*/
|
|
578
|
+
async readFileData(ctx, attachment) {
|
|
564
579
|
const fileManager = this.app.pm.get("file-manager");
|
|
565
580
|
const rawUrl = await fileManager.getFileURL(attachment);
|
|
566
581
|
const url = decodeURIComponent(rawUrl);
|
|
@@ -569,11 +584,18 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
569
584
|
const ua = ctx.get("user-agent") || "";
|
|
570
585
|
const response = await import_axios.default.get(url, {
|
|
571
586
|
responseType: "arraybuffer",
|
|
572
|
-
// Default 30s timeout to prevent slow file servers from blocking indefinitely
|
|
573
587
|
timeout: 3e4,
|
|
574
588
|
headers: { referer, "User-Agent": ua }
|
|
575
589
|
});
|
|
576
|
-
return Buffer.from(response.data).toString("base64");
|
|
590
|
+
return { base64: Buffer.from(response.data).toString("base64") };
|
|
591
|
+
}
|
|
592
|
+
if (url.includes("/api/attachments:stream")) {
|
|
593
|
+
const { stream } = await fileManager.getFileStream(attachment);
|
|
594
|
+
const chunks = [];
|
|
595
|
+
for await (const chunk of stream) {
|
|
596
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
597
|
+
}
|
|
598
|
+
return { base64: Buffer.concat(chunks).toString("base64") };
|
|
577
599
|
}
|
|
578
600
|
let localPath = url;
|
|
579
601
|
const appPublicPath = (process.env.APP_PUBLIC_PATH || "/").replace(/\/+$/, "");
|
|
@@ -586,7 +608,7 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
586
608
|
throw new Error(`Attachment path escapes storage root: ${localPath}`);
|
|
587
609
|
}
|
|
588
610
|
const data = await import_promises.default.readFile(absPath);
|
|
589
|
-
return Buffer.from(data).toString("base64");
|
|
611
|
+
return { base64: Buffer.from(data).toString("base64"), absPath };
|
|
590
612
|
}
|
|
591
613
|
/**
|
|
592
614
|
* Override parseAttachment to convert all attachments into formats that
|
|
@@ -604,11 +626,141 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
604
626
|
* This method is entirely self-contained — it does not call super — so it is
|
|
605
627
|
* safe to use without modifying plugin-ai core.
|
|
606
628
|
*/
|
|
629
|
+
/**
|
|
630
|
+
* Try to extract text from an attachment using DocPixie (if available and
|
|
631
|
+
* the file type is supported). Returns null if DocPixie is unavailable,
|
|
632
|
+
* not ready, or the file type is not supported.
|
|
633
|
+
*/
|
|
634
|
+
/**
|
|
635
|
+
* Check whether the DocPixie skill (`docpixie.query.document`) is configured
|
|
636
|
+
* on the AI employee that initiated this request.
|
|
637
|
+
*
|
|
638
|
+
* Reads `ctx.action.params.values.aiEmployee` (the employee username set by the
|
|
639
|
+
* `sendMessages` action handler), then looks up the employee's `skillSettings`
|
|
640
|
+
* from DB. Result is cached on `ctx.state._docPixieActive` for the request lifetime.
|
|
641
|
+
*/
|
|
642
|
+
async hasDocPixieSkill(ctx) {
|
|
643
|
+
var _a, _b, _c, _d, _e;
|
|
644
|
+
if (ctx.state._docPixieActive !== void 0) return ctx.state._docPixieActive;
|
|
645
|
+
try {
|
|
646
|
+
const employeeUsername = (_c = (_b = (_a = ctx.action) == null ? void 0 : _a.params) == null ? void 0 : _b.values) == null ? void 0 : _c.aiEmployee;
|
|
647
|
+
if (!employeeUsername) {
|
|
648
|
+
ctx.state._docPixieActive = false;
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
const employee = await ctx.db.getRepository("aiEmployees").findOne({
|
|
652
|
+
filter: { username: String(employeeUsername) },
|
|
653
|
+
fields: ["skillSettings"]
|
|
654
|
+
});
|
|
655
|
+
const skills = ((_e = (_d = employee == null ? void 0 : employee.get) == null ? void 0 : _d.call(employee, "skillSettings")) == null ? void 0 : _e.skills) ?? [];
|
|
656
|
+
const has = skills.some((s) => s.name === "docpixie.query.document");
|
|
657
|
+
ctx.state._docPixieActive = has;
|
|
658
|
+
return has;
|
|
659
|
+
} catch {
|
|
660
|
+
ctx.state._docPixieActive = false;
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Run the full DocPixie ingestion pipeline (extract pages → generate summary → index).
|
|
666
|
+
* Returns a formatted `<processed_document>` context block the LLM can use immediately,
|
|
667
|
+
* plus a clear instruction to call the RAG tool with the returned documentId for details.
|
|
668
|
+
*
|
|
669
|
+
* Prefers passing `absPath` directly for local-storage files to avoid a second
|
|
670
|
+
* write-to-disk round-trip. Falls back to Buffer for remote / S3 files.
|
|
671
|
+
*
|
|
672
|
+
* Returns null if DocPixie is unavailable, not configured, or processing fails.
|
|
673
|
+
*/
|
|
674
|
+
async tryDocPixieFullProcess(fileData, filename, ctx) {
|
|
675
|
+
var _a, _b, _c, _d;
|
|
676
|
+
try {
|
|
677
|
+
const docpixie = this.app.pm.get("docpixie");
|
|
678
|
+
if (!((_b = (_a = docpixie == null ? void 0 : docpixie.service) == null ? void 0 : _a.isReady) == null ? void 0 : _b.call(_a))) return null;
|
|
679
|
+
const userId = (_d = (_c = ctx.state) == null ? void 0 : _c.currentUser) == null ? void 0 : _d.id;
|
|
680
|
+
let result;
|
|
681
|
+
if (fileData.absPath) {
|
|
682
|
+
result = await docpixie.service.processDocumentFromPath(fileData.absPath, filename, { userId });
|
|
683
|
+
} else {
|
|
684
|
+
const buffer = Buffer.from(fileData.base64, "base64");
|
|
685
|
+
result = await docpixie.service.processDocumentFromBuffer(buffer, filename, { userId });
|
|
686
|
+
}
|
|
687
|
+
const { documentId, summary, pageCount } = result;
|
|
688
|
+
const summaryText = (summary == null ? void 0 : summary.trim()) || "No summary available.";
|
|
689
|
+
return `<processed_document id="${documentId}" filename="${filename}" pages="${pageCount}">
|
|
690
|
+
<summary>
|
|
691
|
+
${summaryText}
|
|
692
|
+
</summary>
|
|
693
|
+
<rag_instruction>This document is fully indexed. Call docpixie.query.document with documentId=${documentId} to retrieve specific details.</rag_instruction>
|
|
694
|
+
</processed_document>`;
|
|
695
|
+
} catch {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Try to extract text from an attachment using DocPixie (transient — no DB indexing).
|
|
701
|
+
* When `absPath` is provided (local-storage file), DocPixie reads the file
|
|
702
|
+
* directly — no Buffer decode/re-encode or extra temp-file write.
|
|
703
|
+
* Falls back to `extractTextFromBuffer` for remote/S3 files.
|
|
704
|
+
* Returns null if DocPixie is unavailable, not ready, or file type unsupported.
|
|
705
|
+
*/
|
|
706
|
+
async tryDocPixieExtract(fileData, filename) {
|
|
707
|
+
try {
|
|
708
|
+
const docpixie = this.app.pm.get("docpixie");
|
|
709
|
+
if (!(docpixie == null ? void 0 : docpixie.service)) return null;
|
|
710
|
+
let text;
|
|
711
|
+
if (fileData.absPath) {
|
|
712
|
+
text = await docpixie.service.extractTextFromPath(fileData.absPath, filename);
|
|
713
|
+
} else {
|
|
714
|
+
const buffer = Buffer.from(fileData.base64, "base64");
|
|
715
|
+
text = await docpixie.service.extractTextFromBuffer(buffer, filename);
|
|
716
|
+
}
|
|
717
|
+
return text || null;
|
|
718
|
+
} catch {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
607
722
|
async parseAttachment(ctx, attachment) {
|
|
608
723
|
const mimetype = attachment.mimetype || "application/octet-stream";
|
|
609
724
|
const filename = attachment.filename || attachment.name || "file";
|
|
610
|
-
const
|
|
725
|
+
const fileData = await this.readFileData(ctx, attachment);
|
|
726
|
+
const { base64: data } = fileData;
|
|
727
|
+
const isDocPixieSupported = mimetype === "application/pdf" || mimetype.startsWith("image/");
|
|
728
|
+
if (isDocPixieSupported && await this.hasDocPixieSkill(ctx)) {
|
|
729
|
+
const contextBlock = await this.tryDocPixieFullProcess(fileData, filename, ctx);
|
|
730
|
+
if (contextBlock) {
|
|
731
|
+
return {
|
|
732
|
+
placement: "contentBlocks",
|
|
733
|
+
content: { type: "text", text: contextBlock }
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (mimetype === "application/pdf") {
|
|
738
|
+
const extracted = await this.tryDocPixieExtract(fileData, filename);
|
|
739
|
+
if (extracted) {
|
|
740
|
+
return {
|
|
741
|
+
placement: "contentBlocks",
|
|
742
|
+
content: {
|
|
743
|
+
type: "text",
|
|
744
|
+
text: `<attachment filename="${filename}" type="${mimetype}">
|
|
745
|
+
${extracted}
|
|
746
|
+
</attachment>`
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
}
|
|
611
751
|
if (mimetype.startsWith("image/")) {
|
|
752
|
+
const extracted = await this.tryDocPixieExtract(fileData, filename);
|
|
753
|
+
if (extracted) {
|
|
754
|
+
return {
|
|
755
|
+
placement: "contentBlocks",
|
|
756
|
+
content: {
|
|
757
|
+
type: "text",
|
|
758
|
+
text: `<attachment filename="${filename}" type="${mimetype}">
|
|
759
|
+
${extracted}
|
|
760
|
+
</attachment>`
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
}
|
|
612
764
|
return {
|
|
613
765
|
placement: "contentBlocks",
|
|
614
766
|
content: {
|
package/dist/server/plugin.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
package/dist/swagger.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
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 __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var swagger_exports = {};
|
|
28
|
+
__export(swagger_exports, {
|
|
29
|
+
default: () => swagger_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(swagger_exports);
|
|
32
|
+
var swagger_default = {
|
|
33
|
+
info: {
|
|
34
|
+
title: "NocoBase API - Custom LLM Plugin",
|
|
35
|
+
description: "Registers a custom OpenAI-compatible LLM provider with the AI plugin. This plugin has no direct HTTP endpoints \u2014 all API access is through the AI API plugin gateway (`/api/ai-llm/v1/*`) using models registered under the `custom-llm` service."
|
|
36
|
+
},
|
|
37
|
+
tags: [{ name: "custom-llm", description: "Custom LLM provider (no direct endpoints)" }],
|
|
38
|
+
paths: {}
|
|
39
|
+
};
|
package/package.json
CHANGED
|
@@ -3,8 +3,16 @@
|
|
|
3
3
|
"displayName": "AI LLM: Custom (OpenAI Compatible)",
|
|
4
4
|
"displayName.zh-CN": "AI LLM:自定义(OpenAI 兼容)",
|
|
5
5
|
"description": "OpenAI-compatible LLM provider with auto response format detection for external LLM services.",
|
|
6
|
-
"version": "1.2.
|
|
6
|
+
"version": "1.2.3",
|
|
7
7
|
"main": "dist/server/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"client.js",
|
|
11
|
+
"client.d.ts",
|
|
12
|
+
"server.js",
|
|
13
|
+
"server.d.ts",
|
|
14
|
+
"src"
|
|
15
|
+
],
|
|
8
16
|
"nocobase": {
|
|
9
17
|
"supportedVersions": [
|
|
10
18
|
"2.x"
|
|
@@ -0,0 +1,249 @@
|
|
|
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
|
+
// CSS modules
|
|
11
|
+
type CSSModuleClasses = { readonly [key: string]: string };
|
|
12
|
+
|
|
13
|
+
declare module '*.module.css' {
|
|
14
|
+
const classes: CSSModuleClasses;
|
|
15
|
+
export default classes;
|
|
16
|
+
}
|
|
17
|
+
declare module '*.module.scss' {
|
|
18
|
+
const classes: CSSModuleClasses;
|
|
19
|
+
export default classes;
|
|
20
|
+
}
|
|
21
|
+
declare module '*.module.sass' {
|
|
22
|
+
const classes: CSSModuleClasses;
|
|
23
|
+
export default classes;
|
|
24
|
+
}
|
|
25
|
+
declare module '*.module.less' {
|
|
26
|
+
const classes: CSSModuleClasses;
|
|
27
|
+
export default classes;
|
|
28
|
+
}
|
|
29
|
+
declare module '*.module.styl' {
|
|
30
|
+
const classes: CSSModuleClasses;
|
|
31
|
+
export default classes;
|
|
32
|
+
}
|
|
33
|
+
declare module '*.module.stylus' {
|
|
34
|
+
const classes: CSSModuleClasses;
|
|
35
|
+
export default classes;
|
|
36
|
+
}
|
|
37
|
+
declare module '*.module.pcss' {
|
|
38
|
+
const classes: CSSModuleClasses;
|
|
39
|
+
export default classes;
|
|
40
|
+
}
|
|
41
|
+
declare module '*.module.sss' {
|
|
42
|
+
const classes: CSSModuleClasses;
|
|
43
|
+
export default classes;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// CSS
|
|
47
|
+
declare module '*.css' { }
|
|
48
|
+
declare module '*.scss' { }
|
|
49
|
+
declare module '*.sass' { }
|
|
50
|
+
declare module '*.less' { }
|
|
51
|
+
declare module '*.styl' { }
|
|
52
|
+
declare module '*.stylus' { }
|
|
53
|
+
declare module '*.pcss' { }
|
|
54
|
+
declare module '*.sss' { }
|
|
55
|
+
|
|
56
|
+
// Built-in asset types
|
|
57
|
+
// see `src/node/constants.ts`
|
|
58
|
+
|
|
59
|
+
// images
|
|
60
|
+
declare module '*.apng' {
|
|
61
|
+
const src: string;
|
|
62
|
+
export default src;
|
|
63
|
+
}
|
|
64
|
+
declare module '*.png' {
|
|
65
|
+
const src: string;
|
|
66
|
+
export default src;
|
|
67
|
+
}
|
|
68
|
+
declare module '*.jpg' {
|
|
69
|
+
const src: string;
|
|
70
|
+
export default src;
|
|
71
|
+
}
|
|
72
|
+
declare module '*.jpeg' {
|
|
73
|
+
const src: string;
|
|
74
|
+
export default src;
|
|
75
|
+
}
|
|
76
|
+
declare module '*.jfif' {
|
|
77
|
+
const src: string;
|
|
78
|
+
export default src;
|
|
79
|
+
}
|
|
80
|
+
declare module '*.pjpeg' {
|
|
81
|
+
const src: string;
|
|
82
|
+
export default src;
|
|
83
|
+
}
|
|
84
|
+
declare module '*.pjp' {
|
|
85
|
+
const src: string;
|
|
86
|
+
export default src;
|
|
87
|
+
}
|
|
88
|
+
declare module '*.gif' {
|
|
89
|
+
const src: string;
|
|
90
|
+
export default src;
|
|
91
|
+
}
|
|
92
|
+
declare module '*.svg' {
|
|
93
|
+
const src: string;
|
|
94
|
+
export default src;
|
|
95
|
+
}
|
|
96
|
+
declare module '*.ico' {
|
|
97
|
+
const src: string;
|
|
98
|
+
export default src;
|
|
99
|
+
}
|
|
100
|
+
declare module '*.webp' {
|
|
101
|
+
const src: string;
|
|
102
|
+
export default src;
|
|
103
|
+
}
|
|
104
|
+
declare module '*.avif' {
|
|
105
|
+
const src: string;
|
|
106
|
+
export default src;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// media
|
|
110
|
+
declare module '*.mp4' {
|
|
111
|
+
const src: string;
|
|
112
|
+
export default src;
|
|
113
|
+
}
|
|
114
|
+
declare module '*.webm' {
|
|
115
|
+
const src: string;
|
|
116
|
+
export default src;
|
|
117
|
+
}
|
|
118
|
+
declare module '*.ogg' {
|
|
119
|
+
const src: string;
|
|
120
|
+
export default src;
|
|
121
|
+
}
|
|
122
|
+
declare module '*.mp3' {
|
|
123
|
+
const src: string;
|
|
124
|
+
export default src;
|
|
125
|
+
}
|
|
126
|
+
declare module '*.wav' {
|
|
127
|
+
const src: string;
|
|
128
|
+
export default src;
|
|
129
|
+
}
|
|
130
|
+
declare module '*.flac' {
|
|
131
|
+
const src: string;
|
|
132
|
+
export default src;
|
|
133
|
+
}
|
|
134
|
+
declare module '*.aac' {
|
|
135
|
+
const src: string;
|
|
136
|
+
export default src;
|
|
137
|
+
}
|
|
138
|
+
declare module '*.opus' {
|
|
139
|
+
const src: string;
|
|
140
|
+
export default src;
|
|
141
|
+
}
|
|
142
|
+
declare module '*.mov' {
|
|
143
|
+
const src: string;
|
|
144
|
+
export default src;
|
|
145
|
+
}
|
|
146
|
+
declare module '*.m4a' {
|
|
147
|
+
const src: string;
|
|
148
|
+
export default src;
|
|
149
|
+
}
|
|
150
|
+
declare module '*.vtt' {
|
|
151
|
+
const src: string;
|
|
152
|
+
export default src;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// fonts
|
|
156
|
+
declare module '*.woff' {
|
|
157
|
+
const src: string;
|
|
158
|
+
export default src;
|
|
159
|
+
}
|
|
160
|
+
declare module '*.woff2' {
|
|
161
|
+
const src: string;
|
|
162
|
+
export default src;
|
|
163
|
+
}
|
|
164
|
+
declare module '*.eot' {
|
|
165
|
+
const src: string;
|
|
166
|
+
export default src;
|
|
167
|
+
}
|
|
168
|
+
declare module '*.ttf' {
|
|
169
|
+
const src: string;
|
|
170
|
+
export default src;
|
|
171
|
+
}
|
|
172
|
+
declare module '*.otf' {
|
|
173
|
+
const src: string;
|
|
174
|
+
export default src;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// other
|
|
178
|
+
declare module '*.webmanifest' {
|
|
179
|
+
const src: string;
|
|
180
|
+
export default src;
|
|
181
|
+
}
|
|
182
|
+
declare module '*.pdf' {
|
|
183
|
+
const src: string;
|
|
184
|
+
export default src;
|
|
185
|
+
}
|
|
186
|
+
declare module '*.txt' {
|
|
187
|
+
const src: string;
|
|
188
|
+
export default src;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// wasm?init
|
|
192
|
+
declare module '*.wasm?init' {
|
|
193
|
+
const initWasm: (options?: WebAssembly.Imports) => Promise<WebAssembly.Instance>;
|
|
194
|
+
export default initWasm;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// web worker
|
|
198
|
+
declare module '*?worker' {
|
|
199
|
+
const workerConstructor: {
|
|
200
|
+
new(options?: { name?: string }): Worker;
|
|
201
|
+
};
|
|
202
|
+
export default workerConstructor;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
declare module '*?worker&inline' {
|
|
206
|
+
const workerConstructor: {
|
|
207
|
+
new(options?: { name?: string }): Worker;
|
|
208
|
+
};
|
|
209
|
+
export default workerConstructor;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
declare module '*?worker&url' {
|
|
213
|
+
const src: string;
|
|
214
|
+
export default src;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
declare module '*?sharedworker' {
|
|
218
|
+
const sharedWorkerConstructor: {
|
|
219
|
+
new(options?: { name?: string }): SharedWorker;
|
|
220
|
+
};
|
|
221
|
+
export default sharedWorkerConstructor;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
declare module '*?sharedworker&inline' {
|
|
225
|
+
const sharedWorkerConstructor: {
|
|
226
|
+
new(options?: { name?: string }): SharedWorker;
|
|
227
|
+
};
|
|
228
|
+
export default sharedWorkerConstructor;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
declare module '*?sharedworker&url' {
|
|
232
|
+
const src: string;
|
|
233
|
+
export default src;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
declare module '*?raw' {
|
|
237
|
+
const src: string;
|
|
238
|
+
export default src;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
declare module '*?url' {
|
|
242
|
+
const src: string;
|
|
243
|
+
export default src;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
declare module '*?inline' {
|
|
247
|
+
const src: string;
|
|
248
|
+
export default src;
|
|
249
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from '@nocobase/client';
|
|
2
|
+
import PluginAIClient from '@nocobase/plugin-ai/client';
|
|
3
|
+
import { customLLMProviderOptions } from './llm-providers/custom-llm';
|
|
4
|
+
|
|
5
|
+
export class PluginCustomLLMClient extends Plugin {
|
|
6
|
+
async afterAdd() {}
|
|
7
|
+
|
|
8
|
+
async beforeLoad() {}
|
|
9
|
+
|
|
10
|
+
async load() {
|
|
11
|
+
this.aiPlugin.aiManager.registerLLMProvider('custom-llm', customLLMProviderOptions);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private get aiPlugin(): PluginAIClient {
|
|
15
|
+
return this.app.pm.get('ai');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default PluginCustomLLMClient;
|