plugin-custom-llm 1.2.3 → 1.3.1
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/README.md +104 -104
- package/dist/client/index.js +1 -1
- package/dist/externalVersion.js +2 -2
- package/dist/locale/en-US.json +31 -29
- package/dist/locale/vi-VN.json +31 -29
- package/dist/locale/zh-CN.json +16 -16
- package/dist/server/llm-providers/custom-llm.js +204 -31
- package/package.json +36 -36
- package/src/client/client.d.ts +9 -0
- package/src/client/index.tsx +28 -19
- package/src/client/llm-providers/custom-llm/ModelSettings.tsx +148 -139
- package/src/client/llm-providers/custom-llm/ProviderSettings.tsx +133 -115
- package/src/client/llm-providers/custom-llm/index.ts +19 -10
- package/src/client/locale.ts +17 -8
- package/src/client/plugin.tsx +9 -0
- package/src/index.ts +9 -0
- package/src/locale/en-US.json +31 -29
- package/src/locale/vi-VN.json +31 -29
- package/src/locale/zh-CN.json +16 -16
- package/src/server/index.ts +9 -0
- package/src/server/llm-providers/custom-llm.ts +1249 -992
- package/src/server/plugin.ts +36 -27
- package/src/swagger.ts +18 -9
|
@@ -58,6 +58,95 @@ function getChatOpenAI() {
|
|
|
58
58
|
}
|
|
59
59
|
return _ChatOpenAI;
|
|
60
60
|
}
|
|
61
|
+
let _ChatOpenAICompletions = null;
|
|
62
|
+
function getChatOpenAICompletions() {
|
|
63
|
+
if (!_ChatOpenAICompletions) {
|
|
64
|
+
const mod = requireFromApp("@langchain/openai");
|
|
65
|
+
_ChatOpenAICompletions = mod.ChatOpenAICompletions;
|
|
66
|
+
}
|
|
67
|
+
return _ChatOpenAICompletions;
|
|
68
|
+
}
|
|
69
|
+
function sanitizeToolCallId(id) {
|
|
70
|
+
if (!id || typeof id !== "string") return id;
|
|
71
|
+
const idx = id.indexOf("__thought__");
|
|
72
|
+
return idx !== -1 ? id.substring(0, idx) : id;
|
|
73
|
+
}
|
|
74
|
+
function getToolCallsKey(toolCalls = []) {
|
|
75
|
+
return toolCalls.map((tc) => {
|
|
76
|
+
var _a;
|
|
77
|
+
const id = (tc == null ? void 0 : tc.id) ?? "";
|
|
78
|
+
const name = (tc == null ? void 0 : tc.name) ?? ((_a = tc == null ? void 0 : tc.function) == null ? void 0 : _a.name) ?? "";
|
|
79
|
+
return `${id}:${name}`;
|
|
80
|
+
}).join("|");
|
|
81
|
+
}
|
|
82
|
+
function collectReasoningMap(messages) {
|
|
83
|
+
var _a, _b, _c, _d;
|
|
84
|
+
const reasoningMap = /* @__PURE__ */ new Map();
|
|
85
|
+
for (const message of messages ?? []) {
|
|
86
|
+
if (((_a = message == null ? void 0 : message.getType) == null ? void 0 : _a.call(message)) !== "ai" && ((_b = message == null ? void 0 : message._getType) == null ? void 0 : _b.call(message)) !== "ai") continue;
|
|
87
|
+
if (!((_c = message == null ? void 0 : message.tool_calls) == null ? void 0 : _c.length)) continue;
|
|
88
|
+
const reasoningContent = (_d = message == null ? void 0 : message.additional_kwargs) == null ? void 0 : _d.reasoning_content;
|
|
89
|
+
if (typeof reasoningContent !== "string" || !reasoningContent) continue;
|
|
90
|
+
const key = getToolCallsKey(message.tool_calls);
|
|
91
|
+
if (key) reasoningMap.set(key, reasoningContent);
|
|
92
|
+
}
|
|
93
|
+
return reasoningMap;
|
|
94
|
+
}
|
|
95
|
+
function patchRequestMessagesReasoning(request, reasoningMap) {
|
|
96
|
+
if (!(reasoningMap == null ? void 0 : reasoningMap.size) || !Array.isArray(request == null ? void 0 : request.messages)) return;
|
|
97
|
+
const lastMsg = request.messages.at(-1);
|
|
98
|
+
if ((lastMsg == null ? void 0 : lastMsg.role) !== "tool") return;
|
|
99
|
+
for (const msg of request.messages) {
|
|
100
|
+
if ((msg == null ? void 0 : msg.role) !== "assistant") continue;
|
|
101
|
+
if (!Array.isArray(msg.tool_calls) || msg.tool_calls.length === 0) continue;
|
|
102
|
+
if (msg.reasoning_content) continue;
|
|
103
|
+
const key = getToolCallsKey(msg.tool_calls);
|
|
104
|
+
const rc = key ? reasoningMap.get(key) : void 0;
|
|
105
|
+
if (rc) msg.reasoning_content = rc;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const REASONING_MAP_KEY = "__nb_reasoning_map";
|
|
109
|
+
function createReasoningChatClass() {
|
|
110
|
+
const ChatOpenAICompletions = getChatOpenAICompletions();
|
|
111
|
+
if (!ChatOpenAICompletions) {
|
|
112
|
+
return getChatOpenAI();
|
|
113
|
+
}
|
|
114
|
+
return class ReasoningChatOpenAI extends ChatOpenAICompletions {
|
|
115
|
+
async _generate(messages, options, runManager) {
|
|
116
|
+
const reasoningMap = collectReasoningMap(messages);
|
|
117
|
+
return super._generate(messages, { ...options || {}, [REASONING_MAP_KEY]: reasoningMap }, runManager);
|
|
118
|
+
}
|
|
119
|
+
async *_streamResponseChunks(messages, options, runManager) {
|
|
120
|
+
const reasoningMap = (options == null ? void 0 : options[REASONING_MAP_KEY]) instanceof Map ? options[REASONING_MAP_KEY] : collectReasoningMap(messages);
|
|
121
|
+
yield* super._streamResponseChunks(messages, { ...options || {}, [REASONING_MAP_KEY]: reasoningMap }, runManager);
|
|
122
|
+
}
|
|
123
|
+
_convertCompletionsDeltaToBaseMessageChunk(delta, rawResponse, defaultRole) {
|
|
124
|
+
const messageChunk = super._convertCompletionsDeltaToBaseMessageChunk(delta, rawResponse, defaultRole);
|
|
125
|
+
if (delta == null ? void 0 : delta.reasoning_content) {
|
|
126
|
+
messageChunk.additional_kwargs = {
|
|
127
|
+
...messageChunk.additional_kwargs || {},
|
|
128
|
+
reasoning_content: delta.reasoning_content
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return messageChunk;
|
|
132
|
+
}
|
|
133
|
+
_convertCompletionsMessageToBaseMessage(message, rawResponse) {
|
|
134
|
+
const langChainMessage = super._convertCompletionsMessageToBaseMessage(message, rawResponse);
|
|
135
|
+
if (message == null ? void 0 : message.reasoning_content) {
|
|
136
|
+
langChainMessage.additional_kwargs = {
|
|
137
|
+
...langChainMessage.additional_kwargs || {},
|
|
138
|
+
reasoning_content: message.reasoning_content
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return langChainMessage;
|
|
142
|
+
}
|
|
143
|
+
async completionWithRetry(request, requestOptions) {
|
|
144
|
+
const reasoningMap = requestOptions == null ? void 0 : requestOptions[REASONING_MAP_KEY];
|
|
145
|
+
patchRequestMessagesReasoning(request, reasoningMap);
|
|
146
|
+
return super.completionWithRetry(request, requestOptions);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
61
150
|
let _ChatGenerationChunk = null;
|
|
62
151
|
function getChatGenerationChunk() {
|
|
63
152
|
if (!_ChatGenerationChunk) {
|
|
@@ -150,12 +239,25 @@ function getByPath(obj, dotPath) {
|
|
|
150
239
|
}
|
|
151
240
|
return current;
|
|
152
241
|
}
|
|
153
|
-
function createMappingFetch(responseMapping) {
|
|
242
|
+
function createMappingFetch(responseMapping, timeoutMs) {
|
|
154
243
|
const contentPath = responseMapping.content;
|
|
155
244
|
if (!contentPath) return void 0;
|
|
245
|
+
const toolCallsPath = responseMapping.tool_calls;
|
|
246
|
+
const finishReasonPath = responseMapping.finish_reason;
|
|
156
247
|
return async (url, init) => {
|
|
157
248
|
var _a, _b;
|
|
158
|
-
|
|
249
|
+
let response;
|
|
250
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
251
|
+
const controller = new AbortController();
|
|
252
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
253
|
+
try {
|
|
254
|
+
response = await fetch(url, { ...init, signal: controller.signal });
|
|
255
|
+
} finally {
|
|
256
|
+
clearTimeout(timer);
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
response = await fetch(url, init);
|
|
260
|
+
}
|
|
159
261
|
if (!response.ok) return response;
|
|
160
262
|
const contentType = response.headers.get("content-type") || "";
|
|
161
263
|
if (contentType.includes("text/event-stream") || ((_a = init == null ? void 0 : init.headers) == null ? void 0 : _a["Accept"]) === "text/event-stream") {
|
|
@@ -186,17 +288,28 @@ function createMappingFetch(responseMapping) {
|
|
|
186
288
|
try {
|
|
187
289
|
const parsed = JSON.parse(data);
|
|
188
290
|
const mappedContent = getByPath(parsed, contentPath);
|
|
189
|
-
|
|
291
|
+
const mappedToolCalls = toolCallsPath ? getByPath(parsed, toolCallsPath) : getByPath(parsed, "choices.0.delta.tool_calls") ?? getByPath(parsed, "delta.tool_calls");
|
|
292
|
+
const mappedFinishReason = finishReasonPath ? getByPath(parsed, finishReasonPath) : getByPath(parsed, "choices.0.finish_reason") ?? getByPath(parsed, "finish_reason");
|
|
293
|
+
if (mappedContent !== void 0 || mappedToolCalls) {
|
|
294
|
+
const delta = { role: "assistant" };
|
|
295
|
+
if (mappedContent !== void 0) {
|
|
296
|
+
delta.content = String(mappedContent);
|
|
297
|
+
}
|
|
298
|
+
if (mappedToolCalls) {
|
|
299
|
+
delta.tool_calls = mappedToolCalls;
|
|
300
|
+
}
|
|
190
301
|
const mapped = {
|
|
191
302
|
id: getByPath(parsed, responseMapping.id || "id") || "chatcmpl-custom",
|
|
192
303
|
object: "chat.completion.chunk",
|
|
193
304
|
created: Math.floor(Date.now() / 1e3),
|
|
194
305
|
model: "custom",
|
|
195
|
-
choices: [
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
306
|
+
choices: [
|
|
307
|
+
{
|
|
308
|
+
index: 0,
|
|
309
|
+
delta,
|
|
310
|
+
finish_reason: mappedFinishReason ?? null
|
|
311
|
+
}
|
|
312
|
+
]
|
|
200
313
|
};
|
|
201
314
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(mapped)}
|
|
202
315
|
|
|
@@ -228,21 +341,33 @@ function createMappingFetch(responseMapping) {
|
|
|
228
341
|
if (contentType.includes("application/json")) {
|
|
229
342
|
const body = await response.json();
|
|
230
343
|
const mappedContent = getByPath(body, contentPath);
|
|
231
|
-
|
|
344
|
+
const mappedToolCalls = toolCallsPath ? getByPath(body, toolCallsPath) : getByPath(body, "choices.0.message.tool_calls") ?? getByPath(body, "message.tool_calls");
|
|
345
|
+
const mappedFinishReason = finishReasonPath ? getByPath(body, finishReasonPath) : getByPath(body, "choices.0.finish_reason") ?? getByPath(body, "finish_reason");
|
|
346
|
+
if (mappedContent !== void 0 || mappedToolCalls) {
|
|
347
|
+
const message = {
|
|
348
|
+
role: getByPath(body, responseMapping.role || "") || "assistant"
|
|
349
|
+
};
|
|
350
|
+
if (mappedContent !== void 0) {
|
|
351
|
+
message.content = String(mappedContent);
|
|
352
|
+
} else {
|
|
353
|
+
message.content = null;
|
|
354
|
+
}
|
|
355
|
+
if (mappedToolCalls) {
|
|
356
|
+
message.tool_calls = mappedToolCalls;
|
|
357
|
+
}
|
|
232
358
|
const mapped = {
|
|
233
359
|
id: getByPath(body, responseMapping.id || "id") || "chatcmpl-custom",
|
|
234
360
|
object: "chat.completion",
|
|
235
361
|
created: Math.floor(Date.now() / 1e3),
|
|
236
362
|
model: "custom",
|
|
237
|
-
choices: [
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
363
|
+
choices: [
|
|
364
|
+
{
|
|
365
|
+
index: 0,
|
|
366
|
+
message,
|
|
367
|
+
finish_reason: mappedFinishReason ?? (mappedToolCalls ? "tool_calls" : "stop")
|
|
368
|
+
}
|
|
369
|
+
],
|
|
370
|
+
usage: body.usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
246
371
|
};
|
|
247
372
|
return new Response(JSON.stringify(mapped), {
|
|
248
373
|
status: response.status,
|
|
@@ -417,6 +542,45 @@ function fixEmptyToolProperties(model) {
|
|
|
417
542
|
};
|
|
418
543
|
return model;
|
|
419
544
|
}
|
|
545
|
+
function wrapWithToolCallIdSanitizer(model) {
|
|
546
|
+
var _a, _b;
|
|
547
|
+
const originalGenerate = (_a = model._generate) == null ? void 0 : _a.bind(model);
|
|
548
|
+
if (originalGenerate) {
|
|
549
|
+
model._generate = async function(...args) {
|
|
550
|
+
const result = await originalGenerate(...args);
|
|
551
|
+
for (const gen of (result == null ? void 0 : result.generations) ?? []) {
|
|
552
|
+
const msg = gen == null ? void 0 : gen.message;
|
|
553
|
+
if (msg == null ? void 0 : msg.tool_calls) {
|
|
554
|
+
for (const tc of msg.tool_calls) {
|
|
555
|
+
tc.id = sanitizeToolCallId(tc.id);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return result;
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
const streamMethod = typeof model._streamResponseChunks === "function" ? "_streamResponseChunks" : "_stream";
|
|
563
|
+
const originalStream = (_b = model[streamMethod]) == null ? void 0 : _b.bind(model);
|
|
564
|
+
if (originalStream) {
|
|
565
|
+
model[streamMethod] = async function* (...args) {
|
|
566
|
+
for await (const chunk of originalStream(...args)) {
|
|
567
|
+
const msg = chunk == null ? void 0 : chunk.message;
|
|
568
|
+
if (msg == null ? void 0 : msg.tool_call_chunks) {
|
|
569
|
+
for (const tc of msg.tool_call_chunks) {
|
|
570
|
+
tc.id = sanitizeToolCallId(tc.id);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (msg == null ? void 0 : msg.tool_calls) {
|
|
574
|
+
for (const tc of msg.tool_calls) {
|
|
575
|
+
tc.id = sanitizeToolCallId(tc.id);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
yield chunk;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
return model;
|
|
583
|
+
}
|
|
420
584
|
class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
421
585
|
get baseURL() {
|
|
422
586
|
return null;
|
|
@@ -431,7 +595,15 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
431
595
|
}
|
|
432
596
|
createModel() {
|
|
433
597
|
var _a;
|
|
434
|
-
const {
|
|
598
|
+
const {
|
|
599
|
+
apiKey,
|
|
600
|
+
disableStream,
|
|
601
|
+
timeout,
|
|
602
|
+
streamKeepAlive,
|
|
603
|
+
keepAliveIntervalMs,
|
|
604
|
+
keepAliveContent,
|
|
605
|
+
enableReasoning
|
|
606
|
+
} = this.serviceOptions || {};
|
|
435
607
|
const baseURL = (_a = this.serviceOptions) == null ? void 0 : _a.baseURL;
|
|
436
608
|
const { responseFormat } = this.modelOptions || {};
|
|
437
609
|
const reqConfig = this.requestConfig;
|
|
@@ -446,7 +618,7 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
446
618
|
if (reqConfig.extraBody && typeof reqConfig.extraBody === "object") {
|
|
447
619
|
Object.assign(modelKwargs, reqConfig.extraBody);
|
|
448
620
|
}
|
|
449
|
-
const
|
|
621
|
+
const ChatClass = enableReasoning ? createReasoningChatClass() : getChatOpenAI();
|
|
450
622
|
const config = {
|
|
451
623
|
apiKey,
|
|
452
624
|
...this.modelOptions,
|
|
@@ -459,18 +631,20 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
459
631
|
if (disableStream) {
|
|
460
632
|
config.streaming = false;
|
|
461
633
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
config.
|
|
634
|
+
const timeoutMs = timeout && Number(timeout) > 0 ? Number(timeout) : 0;
|
|
635
|
+
if (timeoutMs) {
|
|
636
|
+
config.timeout = timeoutMs;
|
|
637
|
+
config.configuration.timeout = timeoutMs;
|
|
465
638
|
}
|
|
466
639
|
if (reqConfig.extraHeaders && typeof reqConfig.extraHeaders === "object") {
|
|
467
640
|
config.configuration.defaultHeaders = reqConfig.extraHeaders;
|
|
468
641
|
}
|
|
469
642
|
if (resConfig.responseMapping) {
|
|
470
|
-
config.configuration.fetch = createMappingFetch(resConfig.responseMapping);
|
|
643
|
+
config.configuration.fetch = createMappingFetch(resConfig.responseMapping, timeoutMs || 12e4);
|
|
471
644
|
}
|
|
472
|
-
let model = new
|
|
645
|
+
let model = new ChatClass(config);
|
|
473
646
|
model = fixEmptyToolProperties(model);
|
|
647
|
+
model = wrapWithToolCallIdSanitizer(model);
|
|
474
648
|
if (streamKeepAlive && !disableStream) {
|
|
475
649
|
return wrapWithStreamKeepAlive(model, {
|
|
476
650
|
intervalMs: Number(keepAliveIntervalMs) || 5e3,
|
|
@@ -498,15 +672,14 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
498
672
|
workContext
|
|
499
673
|
};
|
|
500
674
|
if (toolCalls) {
|
|
501
|
-
content.tool_calls = toolCalls;
|
|
675
|
+
content.tool_calls = Array.isArray(toolCalls) ? toolCalls.map((tc) => ({ ...tc, id: sanitizeToolCallId(tc.id) })) : toolCalls;
|
|
502
676
|
}
|
|
503
677
|
if (Array.isArray(content.content)) {
|
|
504
678
|
const textBlocks = content.content.filter((block) => block.type === "text");
|
|
505
679
|
content.content = textBlocks.map((block) => block.text).join("") || "";
|
|
506
680
|
}
|
|
507
681
|
if (typeof content.content === "string") {
|
|
508
|
-
|
|
509
|
-
content.content = content.content.replace(new RegExp(escapedPrefix + ".*?(?=" + escapedPrefix + "|$)", "g"), "");
|
|
682
|
+
content.content = content.content.replaceAll(KEEPALIVE_PREFIX, "");
|
|
510
683
|
content.content = stripToolCallTags(content.content);
|
|
511
684
|
}
|
|
512
685
|
if (((_b = (_a = content.metadata) == null ? void 0 : _a.additional_kwargs) == null ? void 0 : _b.__keepalive) !== void 0) {
|
|
@@ -640,10 +813,10 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
640
813
|
* from DB. Result is cached on `ctx.state._docPixieActive` for the request lifetime.
|
|
641
814
|
*/
|
|
642
815
|
async hasDocPixieSkill(ctx) {
|
|
643
|
-
var _a, _b, _c, _d, _e;
|
|
816
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
644
817
|
if (ctx.state._docPixieActive !== void 0) return ctx.state._docPixieActive;
|
|
645
818
|
try {
|
|
646
|
-
const employeeUsername = (_c = (_b = (_a = ctx.action) == null ? void 0 : _a.params) == null ? void 0 : _b.values) == null ? void 0 : _c.aiEmployee;
|
|
819
|
+
const employeeUsername = ((_c = (_b = (_a = ctx.action) == null ? void 0 : _a.params) == null ? void 0 : _b.values) == null ? void 0 : _c.aiEmployee) ?? ((_e = (_d = ctx.action) == null ? void 0 : _d.params) == null ? void 0 : _e.aiEmployee) ?? ((_f = ctx.state) == null ? void 0 : _f.currentAiEmployee);
|
|
647
820
|
if (!employeeUsername) {
|
|
648
821
|
ctx.state._docPixieActive = false;
|
|
649
822
|
return false;
|
|
@@ -652,7 +825,7 @@ class CustomLLMProvider extends import_plugin_ai.LLMProvider {
|
|
|
652
825
|
filter: { username: String(employeeUsername) },
|
|
653
826
|
fields: ["skillSettings"]
|
|
654
827
|
});
|
|
655
|
-
const skills = ((
|
|
828
|
+
const skills = ((_h = (_g = employee == null ? void 0 : employee.get) == null ? void 0 : _g.call(employee, "skillSettings")) == null ? void 0 : _h.skills) ?? [];
|
|
656
829
|
const has = skills.some((s) => s.name === "docpixie.query.document");
|
|
657
830
|
ctx.state._docPixieActive = has;
|
|
658
831
|
return has;
|
package/package.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "plugin-custom-llm",
|
|
3
|
-
"displayName": "AI LLM: Custom (OpenAI Compatible)",
|
|
4
|
-
"displayName.zh-CN": "AI LLM:自定义(OpenAI 兼容)",
|
|
5
|
-
"description": "OpenAI-compatible LLM provider with auto response format detection for external LLM services.",
|
|
6
|
-
"version": "1.
|
|
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
|
-
],
|
|
16
|
-
"nocobase": {
|
|
17
|
-
"supportedVersions": [
|
|
18
|
-
"2.x"
|
|
19
|
-
],
|
|
20
|
-
"editionLevel": 0
|
|
21
|
-
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"@langchain/openai": "^1.0.0",
|
|
24
|
-
"@langchain/core": "^1.0.0"
|
|
25
|
-
},
|
|
26
|
-
"peerDependencies": {
|
|
27
|
-
"@nocobase/client": "2.x",
|
|
28
|
-
"@nocobase/plugin-ai": "2.x",
|
|
29
|
-
"@nocobase/server": "2.x",
|
|
30
|
-
"@nocobase/test": "2.x"
|
|
31
|
-
},
|
|
32
|
-
"keywords": [
|
|
33
|
-
"AI"
|
|
34
|
-
],
|
|
35
|
-
"license": "Apache-2.0"
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "plugin-custom-llm",
|
|
3
|
+
"displayName": "AI LLM: Custom (OpenAI Compatible)",
|
|
4
|
+
"displayName.zh-CN": "AI LLM:自定义(OpenAI 兼容)",
|
|
5
|
+
"description": "OpenAI-compatible LLM provider with auto response format detection for external LLM services.",
|
|
6
|
+
"version": "1.3.1",
|
|
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
|
+
],
|
|
16
|
+
"nocobase": {
|
|
17
|
+
"supportedVersions": [
|
|
18
|
+
"2.x"
|
|
19
|
+
],
|
|
20
|
+
"editionLevel": 0
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@langchain/openai": "^1.0.0",
|
|
24
|
+
"@langchain/core": "^1.0.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@nocobase/client": "2.x",
|
|
28
|
+
"@nocobase/plugin-ai": "2.x",
|
|
29
|
+
"@nocobase/server": "2.x",
|
|
30
|
+
"@nocobase/test": "2.x"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"AI"
|
|
34
|
+
],
|
|
35
|
+
"license": "Apache-2.0"
|
|
36
|
+
}
|
package/src/client/client.d.ts
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* This file is part of the NocoBase (R) project.
|
|
12
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
13
|
+
* Authors: NocoBase Team.
|
|
14
|
+
*
|
|
15
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
16
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
17
|
+
*/
|
|
18
|
+
|
|
10
19
|
// CSS modules
|
|
11
20
|
type CSSModuleClasses = { readonly [key: string]: string };
|
|
12
21
|
|
package/src/client/index.tsx
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
import { Plugin } from '@nocobase/client';
|
|
11
|
+
import PluginAIClient from '@nocobase/plugin-ai/client';
|
|
12
|
+
import { customLLMProviderOptions } from './llm-providers/custom-llm';
|
|
13
|
+
|
|
14
|
+
export class PluginCustomLLMClient extends Plugin {
|
|
15
|
+
async afterAdd() {}
|
|
16
|
+
|
|
17
|
+
async beforeLoad() {}
|
|
18
|
+
|
|
19
|
+
async load() {
|
|
20
|
+
this.aiPlugin.aiManager.registerLLMProvider('custom-llm', customLLMProviderOptions);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private get aiPlugin(): PluginAIClient {
|
|
24
|
+
return this.app.pm.get('ai');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default PluginCustomLLMClient;
|