@nocobase/plugin-ai 2.1.0-beta.32 → 2.1.0-beta.34
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/ai/docs/nocobase/api/cli/env/remove.md +5 -3
- package/dist/ai/docs/nocobase/cluster-mode/index.md +5 -1
- package/dist/ai/docs/nocobase/cluster-mode/preparations.md +58 -3
- package/dist/externalVersion.js +16 -16
- package/dist/node_modules/@langchain/xai/package.json +1 -1
- package/dist/node_modules/fs-extra/package.json +1 -1
- package/dist/node_modules/jsonrepair/package.json +1 -1
- package/dist/node_modules/just-bash/package.json +1 -1
- package/dist/node_modules/nodejs-snowflake/package.json +1 -1
- package/dist/node_modules/openai/package.json +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/server/ai-employees/ai-employee.d.ts +0 -1
- package/dist/server/ai-employees/ai-employee.js +0 -41
- package/dist/server/document-loader/cached.d.ts +5 -7
- package/dist/server/document-loader/cached.js +49 -120
- package/dist/server/document-loader/loader.d.ts +1 -1
- package/dist/server/document-loader/loader.js +2 -2
- package/dist/server/document-loader/types.d.ts +1 -6
- package/dist/server/features/vector-database-provider.d.ts +8 -0
- package/dist/server/llm-providers/kimi/document-loader.d.ts +1 -1
- package/dist/server/llm-providers/kimi/document-loader.js +4 -4
- package/dist/server/llm-providers/provider.d.ts +1 -1
- package/dist/server/llm-providers/provider.js +11 -2
- package/dist/server/manager/llm-stream-manager.d.ts +16 -10
- package/dist/server/manager/llm-stream-manager.js +121 -27
- package/dist/server/migrations/20260407170416-ai-employee-knowledge-base-add-key.js +1 -1
- package/dist/server/resource/aiConversations.d.ts +12 -13
- package/dist/server/resource/aiConversations.js +117 -113
- package/dist/server/utils.d.ts +4 -0
- package/dist/server/utils.js +9 -0
- package/dist/server/workflow/nodes/employee/index.js +3 -4
- package/package.json +2 -2
|
@@ -43,24 +43,21 @@ var import_promises = __toESM(require("node:fs/promises"));
|
|
|
43
43
|
var import_node_os = __toESM(require("node:os"));
|
|
44
44
|
var import_node_path = __toESM(require("node:path"));
|
|
45
45
|
var import_documents = require("@langchain/core/documents");
|
|
46
|
-
var import_constants = require("./constants");
|
|
47
46
|
var import_utils = require("./utils");
|
|
48
47
|
class CachedDocumentLoader {
|
|
49
48
|
constructor(plugin, options) {
|
|
50
49
|
this.plugin = plugin;
|
|
51
50
|
this.options = options;
|
|
52
51
|
}
|
|
53
|
-
|
|
52
|
+
_cache = null;
|
|
53
|
+
async load(file, options) {
|
|
54
54
|
const sourceFile = this.toPlainObject(file);
|
|
55
55
|
if (!this.options.supports(sourceFile)) {
|
|
56
56
|
return {
|
|
57
57
|
supported: false,
|
|
58
58
|
fromCache: false,
|
|
59
59
|
text: "",
|
|
60
|
-
documents: []
|
|
61
|
-
meta: {
|
|
62
|
-
sourceFileId: sourceFile.id
|
|
63
|
-
}
|
|
60
|
+
documents: []
|
|
64
61
|
};
|
|
65
62
|
}
|
|
66
63
|
if (sourceFile.size === 0) {
|
|
@@ -68,132 +65,60 @@ class CachedDocumentLoader {
|
|
|
68
65
|
supported: true,
|
|
69
66
|
fromCache: false,
|
|
70
67
|
text: "",
|
|
71
|
-
documents: []
|
|
72
|
-
meta: {
|
|
73
|
-
sourceFileId: sourceFile.id
|
|
74
|
-
}
|
|
68
|
+
documents: []
|
|
75
69
|
};
|
|
76
70
|
}
|
|
77
71
|
const cached = await this.loadFromCache(sourceFile);
|
|
78
72
|
if (cached) {
|
|
79
73
|
return cached;
|
|
80
74
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
parsedMimetype: this.options.parsedMimetype,
|
|
91
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
92
|
-
});
|
|
93
|
-
return {
|
|
94
|
-
supported: true,
|
|
95
|
-
fromCache: false,
|
|
96
|
-
text,
|
|
97
|
-
documents,
|
|
98
|
-
meta: {
|
|
99
|
-
sourceFileId: sourceFile.id,
|
|
100
|
-
parsedFileId: parsedFile.id
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
} catch (error) {
|
|
104
|
-
await this.updateSourceMeta(sourceFile, {
|
|
105
|
-
status: "failed",
|
|
106
|
-
parserVersion: this.options.parserVersion,
|
|
107
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
108
|
-
error: (error == null ? void 0 : error.message) ?? String(error)
|
|
109
|
-
});
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
75
|
+
const documents = await this.options.loader.load(sourceFile, options);
|
|
76
|
+
const text = this.documentsToText(documents);
|
|
77
|
+
await this.persistParsedText(sourceFile, text);
|
|
78
|
+
return {
|
|
79
|
+
supported: true,
|
|
80
|
+
fromCache: false,
|
|
81
|
+
text,
|
|
82
|
+
documents
|
|
83
|
+
};
|
|
112
84
|
}
|
|
113
85
|
async loadFromCache(sourceFile) {
|
|
114
|
-
const
|
|
115
|
-
if (!
|
|
86
|
+
const cacheKey = this.getCacheKey(sourceFile);
|
|
87
|
+
if (!cacheKey) {
|
|
116
88
|
return null;
|
|
117
89
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
90
|
+
const cache = await this.getCache();
|
|
91
|
+
const filePath = await cache.get(cacheKey);
|
|
92
|
+
if (!filePath) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const stat = await import_promises.default.stat(filePath);
|
|
97
|
+
if (!stat.isFile()) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
120
101
|
return null;
|
|
121
102
|
}
|
|
122
|
-
const
|
|
123
|
-
const text = await this.readTextFile(parsedFile);
|
|
103
|
+
const text = await import_promises.default.readFile(filePath, "utf-8");
|
|
124
104
|
const extname = (0, import_utils.resolveExtname)(sourceFile);
|
|
125
105
|
const documents = this.toDocumentsFromText(text, sourceFile, extname);
|
|
126
106
|
return {
|
|
127
107
|
supported: true,
|
|
128
108
|
fromCache: true,
|
|
129
109
|
text,
|
|
130
|
-
documents
|
|
131
|
-
meta: {
|
|
132
|
-
sourceFileId: sourceFile.id,
|
|
133
|
-
parsedFileId: parsedFile.id
|
|
134
|
-
}
|
|
110
|
+
documents
|
|
135
111
|
};
|
|
136
112
|
}
|
|
137
113
|
async persistParsedText(sourceFile, text) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
);
|
|
142
|
-
await import_promises.default.writeFile(tempFilePath, text, "utf-8");
|
|
143
|
-
try {
|
|
144
|
-
const storageName = await this.resolveStorageName(sourceFile);
|
|
145
|
-
const created = await this.fileManager.createFileRecord({
|
|
146
|
-
collectionName: "aiFiles",
|
|
147
|
-
filePath: tempFilePath,
|
|
148
|
-
storageName,
|
|
149
|
-
values: {
|
|
150
|
-
title: `${sourceFile.title ?? sourceFile.filename ?? "document"} (parsed)`,
|
|
151
|
-
mimetype: this.options.parsedMimetype,
|
|
152
|
-
meta: {
|
|
153
|
-
parserVersion: this.options.parserVersion,
|
|
154
|
-
sourceFileId: sourceFile.id
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
return this.toPlainObject(created);
|
|
159
|
-
} finally {
|
|
160
|
-
await import_promises.default.rm(tempFilePath, { force: true });
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
async updateSourceMeta(sourceFile, documentParse) {
|
|
164
|
-
if (!sourceFile.id) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
const nextMeta = {
|
|
168
|
-
...sourceFile.meta ?? {},
|
|
169
|
-
[import_constants.DOCUMENT_PARSE_META_KEY]: documentParse
|
|
170
|
-
};
|
|
171
|
-
await this.aiFilesRepo.update({
|
|
172
|
-
filter: {
|
|
173
|
-
id: sourceFile.id
|
|
174
|
-
},
|
|
175
|
-
values: {
|
|
176
|
-
meta: nextMeta
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
async resolveStorageName(file) {
|
|
181
|
-
if (!file.storageId) {
|
|
182
|
-
return void 0;
|
|
183
|
-
}
|
|
184
|
-
if (!this.fileManager.storagesCache.size) {
|
|
185
|
-
await this.fileManager.loadStorages();
|
|
186
|
-
}
|
|
187
|
-
const storage = this.fileManager.storagesCache.get(file.storageId);
|
|
188
|
-
return storage == null ? void 0 : storage.name;
|
|
189
|
-
}
|
|
190
|
-
async readTextFile(file) {
|
|
191
|
-
const { stream } = await this.fileManager.getFileStream(file);
|
|
192
|
-
const chunks = [];
|
|
193
|
-
for await (const chunk of stream) {
|
|
194
|
-
chunks.push(chunk);
|
|
114
|
+
const cacheKey = this.getCacheKey(sourceFile);
|
|
115
|
+
if (!cacheKey) {
|
|
116
|
+
return null;
|
|
195
117
|
}
|
|
196
|
-
|
|
118
|
+
const tempFilePath = import_node_path.default.join(import_node_os.default.tmpdir(), `${cacheKey}.${Date.now()}.parsed.${this.options.parsedFileExtname}`);
|
|
119
|
+
await import_promises.default.writeFile(tempFilePath, text, "utf-8");
|
|
120
|
+
const cache = await this.getCache();
|
|
121
|
+
await cache.set(cacheKey, tempFilePath, 30 * 60 * 1e3);
|
|
197
122
|
}
|
|
198
123
|
documentsToText(documents) {
|
|
199
124
|
return documents.map((doc) => doc.pageContent).join("\n\n");
|
|
@@ -212,23 +137,27 @@ class CachedDocumentLoader {
|
|
|
212
137
|
})
|
|
213
138
|
];
|
|
214
139
|
}
|
|
215
|
-
getParseMeta(meta) {
|
|
216
|
-
if (!meta || typeof meta !== "object") {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
return meta[import_constants.DOCUMENT_PARSE_META_KEY] ?? null;
|
|
220
|
-
}
|
|
221
140
|
toPlainObject(file) {
|
|
222
141
|
if (file == null ? void 0 : file.toJSON) {
|
|
223
142
|
return file.toJSON();
|
|
224
143
|
}
|
|
225
144
|
return file;
|
|
226
145
|
}
|
|
227
|
-
|
|
228
|
-
|
|
146
|
+
async getCache() {
|
|
147
|
+
this._cache ??= await this.plugin.app.cacheManager.createCache({
|
|
148
|
+
name: "ai-employee:document-loader:parsed",
|
|
149
|
+
store: "memory"
|
|
150
|
+
});
|
|
151
|
+
return this._cache;
|
|
229
152
|
}
|
|
230
|
-
|
|
231
|
-
|
|
153
|
+
getCacheKey(sourceFile) {
|
|
154
|
+
if (!sourceFile) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
if (!sourceFile.id || !sourceFile.storageId) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return `${sourceFile.id}@${sourceFile.storageId}`;
|
|
232
161
|
}
|
|
233
162
|
}
|
|
234
163
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -12,6 +12,6 @@ import { ParseableFile } from './types';
|
|
|
12
12
|
export declare class DocumentLoader {
|
|
13
13
|
private readonly fileManager;
|
|
14
14
|
constructor(fileManager: PluginFileManagerServer);
|
|
15
|
-
load(file: ParseableFile): Promise<Document[]>;
|
|
15
|
+
load(file: ParseableFile, options?: any): Promise<Document[]>;
|
|
16
16
|
private streamToBlob;
|
|
17
17
|
}
|
|
@@ -36,12 +36,12 @@ class DocumentLoader {
|
|
|
36
36
|
constructor(fileManager) {
|
|
37
37
|
this.fileManager = fileManager;
|
|
38
38
|
}
|
|
39
|
-
async load(file) {
|
|
39
|
+
async load(file, options) {
|
|
40
40
|
const extname = (0, import_utils.resolveExtname)(file);
|
|
41
41
|
if (!import_constants.SUPPORTED_DOCUMENT_EXTNAMES.includes(extname)) {
|
|
42
42
|
return [];
|
|
43
43
|
}
|
|
44
|
-
const { stream, contentType } = await this.fileManager.getFileStream(file);
|
|
44
|
+
const { stream, contentType } = await this.fileManager.getFileStream(file, options);
|
|
45
45
|
const blob = await this.streamToBlob(stream, contentType ?? file.mimetype);
|
|
46
46
|
return await (0, import_ai.loadByWorker)(extname, blob);
|
|
47
47
|
}
|
|
@@ -21,11 +21,6 @@ export type ParsedDocumentResult = {
|
|
|
21
21
|
fromCache: boolean;
|
|
22
22
|
text: string;
|
|
23
23
|
documents: Document[];
|
|
24
|
-
meta?: {
|
|
25
|
-
sourceFileId?: string | number;
|
|
26
|
-
parsedFileId?: string | number;
|
|
27
|
-
extname?: string;
|
|
28
|
-
};
|
|
29
24
|
};
|
|
30
25
|
export type ParseableFile = {
|
|
31
26
|
id?: number | string;
|
|
@@ -38,5 +33,5 @@ export type ParseableFile = {
|
|
|
38
33
|
size?: number;
|
|
39
34
|
};
|
|
40
35
|
export type DocumentLoaderLike = {
|
|
41
|
-
load(file: ParseableFile): Promise<Document[]>;
|
|
36
|
+
load(file: ParseableFile, options?: any): Promise<Document[]>;
|
|
42
37
|
};
|
|
@@ -14,6 +14,10 @@ export interface VectorDatabaseProviderFeature {
|
|
|
14
14
|
success: boolean;
|
|
15
15
|
error?: string;
|
|
16
16
|
}>;
|
|
17
|
+
beforeCreate<T>(providerName: string, connectParams: T, options?: any): Promise<{
|
|
18
|
+
status: number;
|
|
19
|
+
message?: string;
|
|
20
|
+
}>;
|
|
17
21
|
createVectorStore<T, R>(providerName: string, embeddings: EmbeddingsInterface, connectParams: T): Promise<R>;
|
|
18
22
|
listProviders(): VectorDatabaseProviderInfo<unknown, unknown>[];
|
|
19
23
|
}
|
|
@@ -28,5 +32,9 @@ export type VectorDatabaseProvider<T, R> = {
|
|
|
28
32
|
success: boolean;
|
|
29
33
|
error?: string;
|
|
30
34
|
}>;
|
|
35
|
+
beforeCreate(connectParams: T, options?: any): Promise<{
|
|
36
|
+
status: number;
|
|
37
|
+
message?: string;
|
|
38
|
+
}>;
|
|
31
39
|
createVectorStore(embeddings: EmbeddingsInterface, connectParams: T): Promise<R>;
|
|
32
40
|
};
|
|
@@ -17,7 +17,7 @@ export declare class KimiDocumentLoader {
|
|
|
17
17
|
apiKey?: string;
|
|
18
18
|
baseURL?: string;
|
|
19
19
|
});
|
|
20
|
-
load(file: ParseableFile): Promise<Document[]>;
|
|
20
|
+
load(file: ParseableFile, options?: any): Promise<Document[]>;
|
|
21
21
|
private parseByApi;
|
|
22
22
|
private deleteRemoteFile;
|
|
23
23
|
private formatApiError;
|
|
@@ -54,8 +54,8 @@ class KimiDocumentLoader {
|
|
|
54
54
|
this.client = this.createClient();
|
|
55
55
|
}
|
|
56
56
|
client;
|
|
57
|
-
async load(file) {
|
|
58
|
-
const text = await this.parseByApi(file);
|
|
57
|
+
async load(file, options) {
|
|
58
|
+
const text = await this.parseByApi(file, options);
|
|
59
59
|
if (!text) {
|
|
60
60
|
return [];
|
|
61
61
|
}
|
|
@@ -69,12 +69,12 @@ class KimiDocumentLoader {
|
|
|
69
69
|
})
|
|
70
70
|
];
|
|
71
71
|
}
|
|
72
|
-
async parseByApi(sourceFile) {
|
|
72
|
+
async parseByApi(sourceFile, options) {
|
|
73
73
|
let uploadedFileId = "";
|
|
74
74
|
const safeFilename = import_node_path.default.basename(sourceFile.filename || "document");
|
|
75
75
|
const tempFilePath = import_node_path.default.join(import_node_os.default.tmpdir(), `${sourceFile.id ?? Date.now()}.${Date.now()}.${safeFilename}`);
|
|
76
76
|
try {
|
|
77
|
-
const { stream } = await this.fileManager.getFileStream(sourceFile);
|
|
77
|
+
const { stream } = await this.fileManager.getFileStream(sourceFile, options);
|
|
78
78
|
await (0, import_promises2.pipeline)(stream, (0, import_node_fs.createWriteStream)(tempFilePath));
|
|
79
79
|
let uploaded;
|
|
80
80
|
try {
|
|
@@ -70,7 +70,7 @@ export declare abstract class LLMProvider {
|
|
|
70
70
|
protected isApiSupportedAttachment(attachment: AttachmentModel): boolean;
|
|
71
71
|
protected isDocumentLoaderSupportedAttachment(attachment: AttachmentModel): boolean;
|
|
72
72
|
protected convertToContent(ctx: Context, attachment: any): Promise<ParsedAttachmentResult>;
|
|
73
|
-
protected loadDocument(
|
|
73
|
+
protected loadDocument(ctx: Context, attachment: any): Promise<any>;
|
|
74
74
|
getStructuredOutputOptions(structuredOutput: AIChatContext['structuredOutput']): any;
|
|
75
75
|
testFlight(): Promise<{
|
|
76
76
|
status: 'success' | 'error';
|
|
@@ -202,9 +202,18 @@ class LLMProvider {
|
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
async loadDocument(
|
|
205
|
+
async loadDocument(ctx, attachment) {
|
|
206
|
+
const referer = ctx.get("referer") || "";
|
|
207
|
+
const ua = ctx.get("user-agent") || "";
|
|
206
208
|
const safeFilename = attachment.filename ? import_node_path.default.basename(attachment.filename) : "document";
|
|
207
|
-
const parsed = await this.documentLoader.load(attachment
|
|
209
|
+
const parsed = await this.documentLoader.load(attachment, {
|
|
210
|
+
requestOptions: {
|
|
211
|
+
headers: {
|
|
212
|
+
Referer: referer,
|
|
213
|
+
"User-Agent": ua
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
208
217
|
if (!parsed.supported) {
|
|
209
218
|
return {
|
|
210
219
|
placement: "system",
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import PluginAIServer from '../plugin';
|
|
10
|
+
type LLMStreamOptions = {
|
|
11
|
+
pollInterval?: number;
|
|
12
|
+
initialWaitTimeout?: number;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
};
|
|
10
15
|
export declare class LLMStreamCachedManager {
|
|
11
16
|
private readonly plugin;
|
|
12
17
|
private cachePromise?;
|
|
@@ -14,14 +19,10 @@ export declare class LLMStreamCachedManager {
|
|
|
14
19
|
getCached(sessionId: string): LLMStreamCached;
|
|
15
20
|
clear(sessionId: string): Promise<void>;
|
|
16
21
|
append(sessionId: string, chunk: string): Promise<void>;
|
|
17
|
-
stream(sessionId: string, options?:
|
|
18
|
-
pollInterval?: number;
|
|
19
|
-
initialWaitTimeout?: number;
|
|
20
|
-
}): AsyncGenerator<string, void, void>;
|
|
22
|
+
stream(sessionId: string, options?: LLMStreamOptions): AsyncGenerator<string, void, void>;
|
|
21
23
|
private getChunks;
|
|
22
24
|
private getCache;
|
|
23
|
-
private
|
|
24
|
-
private getLockKey;
|
|
25
|
+
private get store();
|
|
25
26
|
}
|
|
26
27
|
export declare class LLMStreamCached {
|
|
27
28
|
private readonly sessionId;
|
|
@@ -30,8 +31,13 @@ export declare class LLMStreamCached {
|
|
|
30
31
|
clear(): Promise<void>;
|
|
31
32
|
append(chunk: string): Promise<void>;
|
|
32
33
|
skipped(): Promise<void>;
|
|
33
|
-
stream(options?:
|
|
34
|
-
pollInterval?: number;
|
|
35
|
-
initialWaitTimeout?: number;
|
|
36
|
-
}): AsyncGenerator<string, void, void>;
|
|
34
|
+
stream(options?: LLMStreamOptions): AsyncGenerator<string, void, void>;
|
|
37
35
|
}
|
|
36
|
+
export declare class BufferedLLMStreamCached extends LLMStreamCached {
|
|
37
|
+
private buffer;
|
|
38
|
+
private interval;
|
|
39
|
+
clear(): Promise<void>;
|
|
40
|
+
append(chunk: string): Promise<void>;
|
|
41
|
+
private flush;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
@@ -26,54 +26,59 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
var llm_stream_manager_exports = {};
|
|
28
28
|
__export(llm_stream_manager_exports, {
|
|
29
|
+
BufferedLLMStreamCached: () => BufferedLLMStreamCached,
|
|
29
30
|
LLMStreamCached: () => LLMStreamCached,
|
|
30
31
|
LLMStreamCachedManager: () => LLMStreamCachedManager
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(llm_stream_manager_exports);
|
|
33
|
-
var import_utils = require("@nocobase/utils");
|
|
34
34
|
const CACHE_NAME = "ai-llm-stream-cache";
|
|
35
|
-
const LOCK_KEY_PREFIX = "ai-llm-stream-lock";
|
|
36
|
-
const WRITE_LOCK_TTL = 3e3;
|
|
37
35
|
const CACHE_TTL = 10 * 60 * 1e3;
|
|
38
36
|
const STREAM_END_MARK = '"type":"stream_end"';
|
|
39
37
|
const SKIPPED_MARK = "__skipped__";
|
|
40
|
-
const
|
|
41
|
-
const DEFAULT_INITIAL_WAIT_TIMEOUT = 1e3;
|
|
38
|
+
const DEFAULT_INITIAL_WAIT_TIMEOUT = 1e4;
|
|
42
39
|
class LLMStreamCachedManager {
|
|
43
40
|
constructor(plugin) {
|
|
44
41
|
this.plugin = plugin;
|
|
45
42
|
}
|
|
46
43
|
cachePromise;
|
|
47
44
|
getCached(sessionId) {
|
|
45
|
+
if (this.store !== "memory") {
|
|
46
|
+
return new BufferedLLMStreamCached(sessionId, this);
|
|
47
|
+
}
|
|
48
48
|
return new LLMStreamCached(sessionId, this);
|
|
49
49
|
}
|
|
50
50
|
async clear(sessionId) {
|
|
51
|
-
await this.
|
|
52
|
-
|
|
53
|
-
await cache.del(sessionId);
|
|
54
|
-
});
|
|
51
|
+
const cache = await this.getCache();
|
|
52
|
+
await cache.del(sessionId);
|
|
55
53
|
}
|
|
56
54
|
async append(sessionId, chunk) {
|
|
57
|
-
await this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
await cache.set(sessionId, chunks, CACHE_TTL);
|
|
62
|
-
});
|
|
55
|
+
const cache = await this.getCache();
|
|
56
|
+
const chunks = await cache.get(sessionId) ?? [];
|
|
57
|
+
chunks.push(chunk);
|
|
58
|
+
await cache.set(sessionId, chunks, CACHE_TTL);
|
|
63
59
|
}
|
|
64
60
|
async *stream(sessionId, options) {
|
|
65
|
-
const pollInterval = (options == null ? void 0 : options.pollInterval) ??
|
|
61
|
+
const pollInterval = (options == null ? void 0 : options.pollInterval) ?? (this.store !== "memory" ? 1e3 : 100);
|
|
66
62
|
const initialWaitTimeout = (options == null ? void 0 : options.initialWaitTimeout) ?? DEFAULT_INITIAL_WAIT_TIMEOUT;
|
|
63
|
+
const signal = options == null ? void 0 : options.signal;
|
|
67
64
|
let offset = 0;
|
|
68
65
|
let waited = 0;
|
|
69
66
|
let completed = false;
|
|
70
67
|
while (!completed) {
|
|
68
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
71
|
const chunks = await this.getChunks(sessionId);
|
|
72
72
|
const lastSkippedIndex = chunks.lastIndexOf(SKIPPED_MARK);
|
|
73
|
+
let hasNewChunks = false;
|
|
73
74
|
if (lastSkippedIndex >= offset) {
|
|
74
75
|
offset = lastSkippedIndex + 1;
|
|
75
76
|
}
|
|
76
77
|
while (offset < chunks.length) {
|
|
78
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
hasNewChunks = true;
|
|
77
82
|
const chunk = chunks[offset++];
|
|
78
83
|
yield chunk;
|
|
79
84
|
waited = 0;
|
|
@@ -85,16 +90,15 @@ class LLMStreamCachedManager {
|
|
|
85
90
|
if (completed) {
|
|
86
91
|
return;
|
|
87
92
|
}
|
|
88
|
-
if (!
|
|
89
|
-
if (offset > 0) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
93
|
+
if (!hasNewChunks) {
|
|
92
94
|
if (waited >= initialWaitTimeout) {
|
|
93
95
|
return;
|
|
94
96
|
}
|
|
95
97
|
waited += pollInterval;
|
|
96
98
|
}
|
|
97
|
-
await (
|
|
99
|
+
if (await abortableSleep(pollInterval, signal)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
98
102
|
}
|
|
99
103
|
}
|
|
100
104
|
async getChunks(sessionId) {
|
|
@@ -104,15 +108,13 @@ class LLMStreamCachedManager {
|
|
|
104
108
|
async getCache() {
|
|
105
109
|
this.cachePromise ??= this.plugin.app.cacheManager.createCache({
|
|
106
110
|
name: CACHE_NAME,
|
|
107
|
-
store: this.
|
|
111
|
+
store: this.store
|
|
108
112
|
});
|
|
109
113
|
return this.cachePromise;
|
|
110
114
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
getLockKey(sessionId) {
|
|
115
|
-
return `${LOCK_KEY_PREFIX}:${sessionId}`;
|
|
115
|
+
get store() {
|
|
116
|
+
var _a;
|
|
117
|
+
return ((_a = this.plugin.app.options.cacheManager) == null ? void 0 : _a.defaultStore) ?? "memory";
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
class LLMStreamCached {
|
|
@@ -135,8 +137,100 @@ class LLMStreamCached {
|
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
}
|
|
140
|
+
class BufferedLLMStreamCached extends LLMStreamCached {
|
|
141
|
+
buffer = new ChunksBuffer();
|
|
142
|
+
interval;
|
|
143
|
+
async clear() {
|
|
144
|
+
clearInterval(this.interval);
|
|
145
|
+
delete this.interval;
|
|
146
|
+
await this.flush();
|
|
147
|
+
await super.clear();
|
|
148
|
+
}
|
|
149
|
+
async append(chunk) {
|
|
150
|
+
if (!this.interval) {
|
|
151
|
+
this.interval = setInterval(() => {
|
|
152
|
+
void this.flush();
|
|
153
|
+
}, 1e3);
|
|
154
|
+
}
|
|
155
|
+
this.buffer.append(chunk);
|
|
156
|
+
}
|
|
157
|
+
async flush() {
|
|
158
|
+
if (this.buffer.isEmpty()) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const chunks = this.buffer.compact();
|
|
162
|
+
this.buffer.clear();
|
|
163
|
+
const appendChunks = async () => {
|
|
164
|
+
for (const chunk of chunks) {
|
|
165
|
+
await super.append(chunk);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
await appendChunks();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
class ChunksBuffer {
|
|
172
|
+
_size = 0;
|
|
173
|
+
_chunks = [];
|
|
174
|
+
append(chunk) {
|
|
175
|
+
this._size += chunk.length;
|
|
176
|
+
this._chunks.push(chunk);
|
|
177
|
+
}
|
|
178
|
+
clear() {
|
|
179
|
+
this._size = 0;
|
|
180
|
+
this._chunks = [];
|
|
181
|
+
}
|
|
182
|
+
isEmpty() {
|
|
183
|
+
return this._size === 0;
|
|
184
|
+
}
|
|
185
|
+
get size() {
|
|
186
|
+
return this._size;
|
|
187
|
+
}
|
|
188
|
+
get chunks() {
|
|
189
|
+
return this._chunks;
|
|
190
|
+
}
|
|
191
|
+
compact() {
|
|
192
|
+
const chunks = [];
|
|
193
|
+
let buffer = "";
|
|
194
|
+
for (const chunk of this._chunks) {
|
|
195
|
+
if (chunk === SKIPPED_MARK) {
|
|
196
|
+
if (buffer) {
|
|
197
|
+
chunks.push(buffer);
|
|
198
|
+
buffer = "";
|
|
199
|
+
}
|
|
200
|
+
chunks.push(SKIPPED_MARK);
|
|
201
|
+
} else {
|
|
202
|
+
buffer += chunk;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (buffer) {
|
|
206
|
+
chunks.push(buffer);
|
|
207
|
+
}
|
|
208
|
+
return chunks;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function abortableSleep(ms, signal) {
|
|
212
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
213
|
+
return Promise.resolve(true);
|
|
214
|
+
}
|
|
215
|
+
return new Promise((resolve) => {
|
|
216
|
+
const timeout = setTimeout(() => {
|
|
217
|
+
cleanup();
|
|
218
|
+
resolve(false);
|
|
219
|
+
}, ms);
|
|
220
|
+
const cleanup = () => {
|
|
221
|
+
clearTimeout(timeout);
|
|
222
|
+
signal == null ? void 0 : signal.removeEventListener("abort", abort);
|
|
223
|
+
};
|
|
224
|
+
const abort = () => {
|
|
225
|
+
cleanup();
|
|
226
|
+
resolve(true);
|
|
227
|
+
};
|
|
228
|
+
signal == null ? void 0 : signal.addEventListener("abort", abort, { once: true });
|
|
229
|
+
});
|
|
230
|
+
}
|
|
138
231
|
// Annotate the CommonJS export names for ESM import in node:
|
|
139
232
|
0 && (module.exports = {
|
|
233
|
+
BufferedLLMStreamCached,
|
|
140
234
|
LLMStreamCached,
|
|
141
235
|
LLMStreamCachedManager
|
|
142
236
|
});
|
|
@@ -33,7 +33,7 @@ var import_server = require("@nocobase/server");
|
|
|
33
33
|
class ai_employee_knowledge_base_add_key_default extends import_server.Migration {
|
|
34
34
|
on = "afterSync";
|
|
35
35
|
// 'beforeLoad' or 'afterLoad'
|
|
36
|
-
appVersion = "<2.
|
|
36
|
+
appVersion = "<2.2.0";
|
|
37
37
|
async up() {
|
|
38
38
|
var _a, _b, _c, _d;
|
|
39
39
|
const aiEmployeesRepo = this.app.db.getRepository("aiEmployees");
|
|
@@ -7,28 +7,27 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Context, Next } from '@nocobase/actions';
|
|
10
|
-
declare function
|
|
10
|
+
declare function loginInCheck(ctx: Context, next: Next): Promise<never>;
|
|
11
11
|
declare const _default: {
|
|
12
12
|
name: string;
|
|
13
13
|
middlewares: {
|
|
14
|
-
|
|
15
|
-
handler: typeof parallelConversationsLimit;
|
|
14
|
+
handler: typeof loginInCheck;
|
|
16
15
|
}[];
|
|
17
16
|
actions: {
|
|
18
17
|
list(ctx: Context, next: Next): Promise<void>;
|
|
19
|
-
unreadCount(ctx: Context, next: Next): Promise<
|
|
20
|
-
unreadCounts(ctx: Context, next: Next): Promise<
|
|
21
|
-
create(ctx: Context, next: Next): Promise<
|
|
22
|
-
update(ctx: Context, next: Next): Promise<
|
|
18
|
+
unreadCount(ctx: Context, next: Next): Promise<void>;
|
|
19
|
+
unreadCounts(ctx: Context, next: Next): Promise<void>;
|
|
20
|
+
create(ctx: Context, next: Next): Promise<void>;
|
|
21
|
+
update(ctx: Context, next: Next): Promise<void>;
|
|
23
22
|
updateOptions(ctx: Context, next: Next): Promise<never>;
|
|
24
23
|
destroy(ctx: Context, next: Next): Promise<void>;
|
|
25
|
-
getMessages(ctx: Context, next: Next): Promise<
|
|
24
|
+
getMessages(ctx: Context, next: Next): Promise<void>;
|
|
26
25
|
updateToolArgs(ctx: Context, next: Next): Promise<any>;
|
|
27
|
-
sendMessages(ctx: Context, next: Next): Promise<
|
|
28
|
-
abort(ctx: Context, next: Next): Promise<
|
|
29
|
-
resumeStream(ctx: Context, next: Next): Promise<
|
|
30
|
-
resendMessages(ctx: Context, next: Next): Promise<
|
|
31
|
-
updateUserDecision(ctx: Context, next: Next): Promise<
|
|
26
|
+
sendMessages(ctx: Context, next: Next): Promise<void>;
|
|
27
|
+
abort(ctx: Context, next: Next): Promise<void>;
|
|
28
|
+
resumeStream(ctx: Context, next: Next): Promise<void>;
|
|
29
|
+
resendMessages(ctx: Context, next: Next): Promise<void>;
|
|
30
|
+
updateUserDecision(ctx: Context, next: Next): Promise<void>;
|
|
32
31
|
resumeToolCall(ctx: Context, next: Next): Promise<any>;
|
|
33
32
|
};
|
|
34
33
|
};
|