mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-01 14:57:36 +08:00
feat(ai): auto clean up ai conversation`s checkpoint data (#8855)
* feat(ai): add langChain checkpoints cleaner * feat(ai): add cron job for checkpoint cleaning
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { BaseCheckpointSaver } from '@langchain/langgraph-checkpoint';
|
||||
import { Op } from '@nocobase/database';
|
||||
import { SequelizeCollectionManager } from '@nocobase/data-source-manager';
|
||||
|
||||
export type AIConversationsType = {
|
||||
sessionId: string;
|
||||
thread: number;
|
||||
};
|
||||
|
||||
export class CheckpointCleaner {
|
||||
constructor(
|
||||
private readonly provideCollectionManager: () => { collectionManager: SequelizeCollectionManager },
|
||||
private readonly checkpointSaver: BaseCheckpointSaver,
|
||||
) {}
|
||||
|
||||
async cleanOutdated(expiredAt: Date) {
|
||||
const outdatedConversations = await this.aiConversationsModel.findAll({
|
||||
attributes: ['sessionId', 'thread'],
|
||||
where: {
|
||||
updatedAt: {
|
||||
[Op.lt]: expiredAt,
|
||||
},
|
||||
thread: {
|
||||
[Op.ne]: 0,
|
||||
},
|
||||
},
|
||||
raw: true,
|
||||
});
|
||||
|
||||
if (!outdatedConversations.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionIds = outdatedConversations.map((conversation) => conversation.sessionId);
|
||||
const latestMessageRefs = await this.aiMessagesModel.findAll({
|
||||
attributes: ['sessionId', [this.sequelize.fn('MAX', this.sequelize.col('messageId')), 'messageId']],
|
||||
where: {
|
||||
sessionId: {
|
||||
[Op.in]: sessionIds,
|
||||
},
|
||||
},
|
||||
group: ['sessionId'],
|
||||
raw: true,
|
||||
});
|
||||
|
||||
if (!latestMessageRefs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const latestMessageIds = latestMessageRefs.map((row) => row.messageId);
|
||||
const latestMessages = await this.aiMessagesModel.findAll({
|
||||
attributes: ['sessionId', 'updatedAt', 'toolCalls'],
|
||||
where: {
|
||||
messageId: {
|
||||
[Op.in]: latestMessageIds,
|
||||
},
|
||||
},
|
||||
raw: true,
|
||||
});
|
||||
|
||||
const latestMessageMap = new Map(latestMessages.map((message) => [message.sessionId, message]));
|
||||
const conversationsToClean: AIConversationsType[] = [];
|
||||
for (const conversation of outdatedConversations) {
|
||||
const latestMessage = latestMessageMap.get(conversation.sessionId);
|
||||
if (!latestMessage) {
|
||||
continue;
|
||||
}
|
||||
const latestMessageAt = latestMessage.updatedAt ? new Date(latestMessage.updatedAt) : undefined;
|
||||
const hasToolCalls = Array.isArray(latestMessage.toolCalls)
|
||||
? latestMessage.toolCalls.length > 0
|
||||
: !!latestMessage.toolCalls;
|
||||
if (latestMessageAt && latestMessageAt < expiredAt && !hasToolCalls) {
|
||||
conversationsToClean.push(conversation as unknown as AIConversationsType);
|
||||
}
|
||||
}
|
||||
|
||||
if (!conversationsToClean.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.clean(conversationsToClean);
|
||||
}
|
||||
|
||||
async clean(conversations: AIConversationsType[]): Promise<void> {
|
||||
if (!conversations?.length) {
|
||||
return;
|
||||
}
|
||||
const threadIds = this.getThreadIds(conversations);
|
||||
await this.aiConversationsModel.update(
|
||||
{ thread: 0 },
|
||||
{
|
||||
where: {
|
||||
sessionId: {
|
||||
[Op.in]: conversations.map((x) => x.sessionId),
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
for (const threadId of threadIds) {
|
||||
await this.checkpointSaver.deleteThread(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
private getThreadIds(conversations: AIConversationsType[]): string[] {
|
||||
const threadIds = [];
|
||||
for (const conversation of conversations) {
|
||||
for (let i = conversation.thread; i > 0; i--) {
|
||||
threadIds.push(`${conversation.sessionId}:${i}`);
|
||||
}
|
||||
}
|
||||
return threadIds;
|
||||
}
|
||||
|
||||
private get aiConversationsModel() {
|
||||
return this.collectionManager.getCollection('aiConversations').model;
|
||||
}
|
||||
|
||||
private get aiMessagesModel() {
|
||||
return this.collectionManager.getCollection('aiMessages').model;
|
||||
}
|
||||
|
||||
private get sequelize() {
|
||||
return this.collectionManager.db.sequelize;
|
||||
}
|
||||
|
||||
private get collectionManager() {
|
||||
return this.provideCollectionManager().collectionManager;
|
||||
}
|
||||
}
|
||||
@@ -7,734 +7,5 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { RunnableConfig } from '@langchain/core/runnables';
|
||||
import {
|
||||
BaseCheckpointSaver,
|
||||
ChannelVersions,
|
||||
Checkpoint,
|
||||
CheckpointListOptions,
|
||||
CheckpointMetadata,
|
||||
CheckpointTuple,
|
||||
maxChannelVersion,
|
||||
PendingWrite,
|
||||
SerializerProtocol,
|
||||
TASKS,
|
||||
WRITES_IDX_MAP,
|
||||
} from '@langchain/langgraph-checkpoint';
|
||||
import { SequelizeCollectionManager } from '@nocobase/data-source-manager';
|
||||
import { FindOptions, Op } from '@nocobase/database';
|
||||
|
||||
export class SequelizeCollectionSaver extends BaseCheckpointSaver {
|
||||
constructor(
|
||||
private readonly provideCollectionManager: () => { collectionManager: SequelizeCollectionManager },
|
||||
serde?: SerializerProtocol,
|
||||
) {
|
||||
super(serde);
|
||||
}
|
||||
|
||||
async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {
|
||||
const { thread_id, checkpoint_ns = '', checkpoint_id } = config.configurable ?? {};
|
||||
|
||||
let findOptions: FindOptions;
|
||||
if (checkpoint_id) {
|
||||
findOptions = {
|
||||
where: {
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
checkpointId: checkpoint_id,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
findOptions = {
|
||||
where: {
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
},
|
||||
order: [['checkpointId', 'DESC']],
|
||||
limit: 1,
|
||||
};
|
||||
}
|
||||
|
||||
const checkpointRow = (await this.checkpointsModel.findOne(findOptions))?.toJSON();
|
||||
if (!checkpointRow) {
|
||||
return undefined;
|
||||
}
|
||||
const { threadId, checkpointNs, checkpointId, parentCheckpointId } = checkpointRow;
|
||||
|
||||
checkpointRow.channelValues = [];
|
||||
for (const [channel, version] of Object.entries(checkpointRow.checkpoint.channel_versions ?? {})) {
|
||||
const blob = (
|
||||
await this.checkpointBlobsModel.findOne({
|
||||
where: {
|
||||
threadId,
|
||||
checkpointNs,
|
||||
channel,
|
||||
version: String(version),
|
||||
},
|
||||
})
|
||||
)?.toJSON();
|
||||
if (!blob) {
|
||||
continue;
|
||||
}
|
||||
checkpointRow.channelValues.push([
|
||||
new TextEncoder().encode(blob.channel),
|
||||
new TextEncoder().encode(blob.type),
|
||||
blob.blob ? Uint8Array.from(blob.blob) : null,
|
||||
]);
|
||||
}
|
||||
|
||||
const checkpointWrites = await this.getCheckpointWrites([threadId], checkpointNs, checkpointId);
|
||||
checkpointRow.pendingWrites = checkpointWrites[`${threadId}:${checkpointNs}:${checkpointId}`];
|
||||
|
||||
if (checkpointRow.checkpoint.v < 4 && checkpointRow.parentCheckpointId != null) {
|
||||
const sendsResult = await this.getPendingSends([threadId]);
|
||||
const pendingSends = sendsResult[`${threadId}:${parentCheckpointId}`];
|
||||
if (pendingSends?.length) {
|
||||
await this._migratePendingSends(pendingSends, checkpointRow);
|
||||
}
|
||||
}
|
||||
|
||||
const checkpoint = await this._loadCheckpoint(checkpointRow.checkpoint, checkpointRow.channelValues);
|
||||
|
||||
const finalConfig = {
|
||||
configurable: {
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint_id: checkpointId,
|
||||
},
|
||||
};
|
||||
const metadata = await this._loadMetadata(checkpointRow.metadata);
|
||||
const parentConfig = parentCheckpointId
|
||||
? {
|
||||
configurable: {
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint_id: parentCheckpointId,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const pendingWrites = await this._loadWrites(checkpointRow.pendingWrites);
|
||||
|
||||
return {
|
||||
config: finalConfig,
|
||||
checkpoint,
|
||||
metadata,
|
||||
parentConfig,
|
||||
pendingWrites,
|
||||
};
|
||||
}
|
||||
|
||||
async *list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple> {
|
||||
const { filter, before, limit } = options ?? {};
|
||||
const findOptions = this._searchWhere(config, filter, before);
|
||||
findOptions.order = [['checkpointId', 'DESC']];
|
||||
if (limit !== undefined) {
|
||||
findOptions.limit = Number.parseInt(limit.toString(), 10); // sanitize via parseInt, as limit could be an externally provided value
|
||||
}
|
||||
|
||||
const result = (await this.checkpointsModel.findAll(findOptions)).map((x) => x.toJSON());
|
||||
const [checkpointWrites, checkpointBlobs] = await Promise.all([
|
||||
this.getCheckpointWrites(result.map((x) => x.threadId)),
|
||||
this.getCheckpointBlobs(result.map((x) => x.threadId)),
|
||||
]);
|
||||
|
||||
for (const checkpointRow of result) {
|
||||
const { threadId, checkpointNs, checkpointId } = checkpointRow;
|
||||
checkpointRow.channelValues = Object.entries(checkpointRow.checkpoint.channel_versions ?? {})
|
||||
.map(([channel, version]) => checkpointBlobs[`${threadId}:${checkpointNs}:${channel}:${version}`])
|
||||
.filter((x) => x?.length)
|
||||
.flatMap((x) => [...x]);
|
||||
checkpointRow.pendingWrites = checkpointWrites[`${threadId}:${checkpointNs}:${checkpointId}`];
|
||||
}
|
||||
const toMigrate = result.filter((row) => row.checkpoint.v < 4 && row.parentCheckpointId != null);
|
||||
|
||||
if (toMigrate.length > 0) {
|
||||
const sendsResult = await this.getPendingSends(result.map((x) => x.threadId));
|
||||
for (const row of toMigrate) {
|
||||
const pendingSends = sendsResult[`${row.threadId}:${row.parentCheckpointId}`];
|
||||
if (pendingSends?.length) {
|
||||
await this._migratePendingSends(pendingSends, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const value of result) {
|
||||
yield {
|
||||
config: {
|
||||
configurable: {
|
||||
thread_id: value.threadId,
|
||||
checkpoint_ns: value.checkpointNs,
|
||||
checkpoint_id: value.checkpointId,
|
||||
},
|
||||
},
|
||||
checkpoint: await this._loadCheckpoint(value.checkpoint, value.channelValues),
|
||||
metadata: await this._loadMetadata(value.metadata),
|
||||
parentConfig: value.parentCheckpointId
|
||||
? {
|
||||
configurable: {
|
||||
thread_id: value.threadId,
|
||||
checkpoint_ns: value.checkpointNs,
|
||||
checkpoint_id: value.parentCheckpointId,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
pendingWrites: await this._loadWrites(value.pendingWrites),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async _migratePendingSends(
|
||||
pendingSends: [Uint8Array, Uint8Array][],
|
||||
mutableRow: {
|
||||
channelValues: [Uint8Array, Uint8Array, Uint8Array][];
|
||||
checkpoint: Omit<Checkpoint, 'pending_sends' | 'channel_values'>;
|
||||
},
|
||||
) {
|
||||
const textEncoder = new TextEncoder();
|
||||
const textDecoder = new TextDecoder();
|
||||
const row = mutableRow;
|
||||
|
||||
const [enc, blob] = await this.serde.dumpsTyped(
|
||||
await Promise.all(pendingSends.map(([enc, blob]) => this.serde.loadsTyped(textDecoder.decode(enc), blob))),
|
||||
);
|
||||
|
||||
row.channelValues ??= [];
|
||||
row.channelValues.push([textEncoder.encode(TASKS), textEncoder.encode(enc), blob]);
|
||||
|
||||
// add to versions
|
||||
row.checkpoint.channel_versions[TASKS] =
|
||||
Object.keys(mutableRow.checkpoint.channel_versions).length > 0
|
||||
? maxChannelVersion(...Object.values(mutableRow.checkpoint.channel_versions))
|
||||
: this.getNextVersion(undefined);
|
||||
}
|
||||
|
||||
async put(
|
||||
config: RunnableConfig,
|
||||
checkpoint: Checkpoint,
|
||||
metadata: CheckpointMetadata,
|
||||
newVersions: ChannelVersions,
|
||||
): Promise<RunnableConfig> {
|
||||
if (config.configurable === undefined) {
|
||||
throw new Error(`Missing "configurable" field in "config" param`);
|
||||
}
|
||||
const { thread_id, checkpoint_ns = '', checkpoint_id } = config.configurable;
|
||||
|
||||
const nextConfig = {
|
||||
configurable: {
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint_id: checkpoint.id,
|
||||
},
|
||||
};
|
||||
const serializedCheckpoint = this._dumpCheckpoint(checkpoint);
|
||||
const serializedBlobs = await this._dumpBlobs(thread_id, checkpoint_ns, checkpoint.channel_values, newVersions);
|
||||
const serializedMetadata = await this._dumpMetadata(metadata);
|
||||
|
||||
return await this.sequelize.transaction(async (transaction) => {
|
||||
const checkpointBlobs = await this.checkpointBlobsModel.findAll({
|
||||
where: {
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
},
|
||||
attributes: {
|
||||
exclude: ['blob'],
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
const duplicateBlobsFilter = checkpointBlobs.map(({ channel, version }) => `${channel}:${version}`);
|
||||
|
||||
await this.checkpointBlobsModel.bulkCreate(
|
||||
serializedBlobs
|
||||
.filter(
|
||||
([_threadId, _checkpointNs, channel, version]) => !duplicateBlobsFilter.includes(`${channel}:${version}`),
|
||||
)
|
||||
.map(([threadId, checkpointNs, channel, version, type, blob]) => ({
|
||||
threadId,
|
||||
checkpointNs,
|
||||
channel,
|
||||
version,
|
||||
type,
|
||||
blob: blob ? Buffer.from(blob) : null,
|
||||
})),
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
const checkPointFindOptions: FindOptions = {
|
||||
where: { threadId: thread_id, checkpointNs: checkpoint_ns, checkpointId: checkpoint.id },
|
||||
transaction,
|
||||
};
|
||||
const existed = await this.checkpointsModel.count(checkPointFindOptions);
|
||||
if (existed === 0) {
|
||||
await this.checkpointsModel.create(
|
||||
{
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
checkpointId: checkpoint.id,
|
||||
parentCheckpointId: checkpoint_id,
|
||||
checkpoint: serializedCheckpoint,
|
||||
metadata: serializedMetadata,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await this.checkpointsModel.update(
|
||||
{
|
||||
checkpoint: serializedCheckpoint,
|
||||
metadata: serializedMetadata,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
where: checkPointFindOptions.where,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return nextConfig;
|
||||
});
|
||||
}
|
||||
async putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void> {
|
||||
if (!config.configurable?.thread_id) {
|
||||
throw new Error('config.configurable.thread_id is required');
|
||||
}
|
||||
const dumpedWrites = await this._dumpWrites(
|
||||
config.configurable?.thread_id,
|
||||
config.configurable?.checkpoint_ns,
|
||||
config.configurable?.checkpoint_id,
|
||||
taskId,
|
||||
writes,
|
||||
);
|
||||
return await this.sequelize.transaction(async (transaction) => {
|
||||
const checkpointWrites = await this.checkpointWritesModel.findAll({
|
||||
where: {
|
||||
threadId: config.configurable?.thread_id,
|
||||
checkpointNs: config.configurable?.checkpoint_ns,
|
||||
checkpointId: config.configurable?.checkpoint_id,
|
||||
taskId,
|
||||
},
|
||||
attributes: {
|
||||
exclude: ['blob'],
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
const duplicateWritesFilter = checkpointWrites.map(
|
||||
({ threadId, checkpointNs, checkpointId, taskId, idx }) =>
|
||||
`${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${idx}`,
|
||||
);
|
||||
const dumpedWritesInclude = ([threadId, checkpointNs, checkpointId, taskId, idx]: (typeof dumpedWrites)[0]) =>
|
||||
duplicateWritesFilter.includes(`${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${idx}`);
|
||||
const dumpedWritesExclude = (item) => !dumpedWritesInclude(item);
|
||||
|
||||
await this.checkpointWritesModel.bulkCreate(
|
||||
dumpedWrites
|
||||
.filter(dumpedWritesExclude)
|
||||
.map(([threadId, checkpointNs, checkpointId, taskId, idx, channel, type, blob]) => ({
|
||||
threadId,
|
||||
checkpointNs,
|
||||
checkpointId,
|
||||
taskId,
|
||||
idx,
|
||||
channel,
|
||||
type,
|
||||
blob: blob ? Buffer.from(blob) : null,
|
||||
})),
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
for (const [threadId, checkpointNs, checkpointId, taskId, idx, channel, type, blob] of dumpedWrites.filter(
|
||||
dumpedWritesInclude,
|
||||
)) {
|
||||
await this.checkpointWritesModel.update(
|
||||
{
|
||||
channel,
|
||||
type,
|
||||
blob: blob ? Buffer.from(blob) : null,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
threadId,
|
||||
checkpointNs,
|
||||
checkpointId,
|
||||
taskId,
|
||||
idx,
|
||||
},
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteThread(threadId: string): Promise<void> {
|
||||
return this.sequelize.transaction(async (transaction) => {
|
||||
await this.checkpointsModel.destroy({
|
||||
where: {
|
||||
threadId,
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
await this.checkpointBlobsModel.destroy({
|
||||
where: {
|
||||
threadId,
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
await this.checkpointWritesModel.destroy({
|
||||
where: {
|
||||
threadId,
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected async _loadCheckpoint(
|
||||
checkpoint: Omit<Checkpoint, 'pending_sends' | 'channel_values'>,
|
||||
channelValues: [Uint8Array, Uint8Array, Uint8Array][],
|
||||
): Promise<Checkpoint> {
|
||||
return {
|
||||
...checkpoint,
|
||||
channel_values: await this._loadBlobs(channelValues),
|
||||
};
|
||||
}
|
||||
|
||||
protected async _loadBlobs(blobValues: [Uint8Array, Uint8Array, Uint8Array][]): Promise<Record<string, unknown>> {
|
||||
if (!blobValues || blobValues.length === 0) {
|
||||
return {};
|
||||
}
|
||||
const textDecoder = new TextDecoder();
|
||||
const entries = await Promise.all(
|
||||
blobValues
|
||||
.filter(([, t]) => textDecoder.decode(t) !== 'empty')
|
||||
.map(async ([k, t, v]) => [textDecoder.decode(k), await this.serde.loadsTyped(textDecoder.decode(t), v)]),
|
||||
);
|
||||
return Object.fromEntries(entries);
|
||||
}
|
||||
|
||||
protected async _loadMetadata(metadata: Record<string, unknown>) {
|
||||
const [type, dumpedValue] = await this.serde.dumpsTyped(metadata);
|
||||
return this.serde.loadsTyped(type, dumpedValue);
|
||||
}
|
||||
|
||||
protected async _loadWrites(
|
||||
writes: [Uint8Array, Uint8Array, Uint8Array, Uint8Array][],
|
||||
): Promise<[string, string, unknown][]> {
|
||||
const decoder = new TextDecoder();
|
||||
return writes
|
||||
? await Promise.all(
|
||||
writes.map(async ([tid, channel, t, v]) => [
|
||||
decoder.decode(tid),
|
||||
decoder.decode(channel),
|
||||
await this.serde.loadsTyped(decoder.decode(t), v),
|
||||
]),
|
||||
)
|
||||
: [];
|
||||
}
|
||||
|
||||
protected async _dumpBlobs(
|
||||
threadId: string,
|
||||
checkpointNs: string,
|
||||
values: Record<string, unknown>,
|
||||
versions: ChannelVersions,
|
||||
): Promise<[string, string, string, string, string, Uint8Array | undefined][]> {
|
||||
if (Object.keys(versions).length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
Object.entries(versions).map(async ([k, ver]) => {
|
||||
const [type, value] = k in values ? await this.serde.dumpsTyped(values[k]) : ['empty', null];
|
||||
return [threadId, checkpointNs, k, ver.toString(), type, value ? new Uint8Array(value) : undefined];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
protected _dumpCheckpoint(checkpoint: Checkpoint) {
|
||||
const serialized: Record<string, unknown> = { ...checkpoint };
|
||||
if ('channel_values' in serialized) delete serialized.channel_values;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
protected async _dumpMetadata(metadata: CheckpointMetadata) {
|
||||
const [, serializedMetadata] = await this.serde.dumpsTyped(metadata);
|
||||
// We need to remove null characters before writing
|
||||
return JSON.parse(new TextDecoder().decode(serializedMetadata).replace(/\0/g, ''));
|
||||
}
|
||||
|
||||
protected async _dumpWrites(
|
||||
threadId: string,
|
||||
checkpointNs: string,
|
||||
checkpointId: string,
|
||||
taskId: string,
|
||||
writes: [string, unknown][],
|
||||
): Promise<[string, string, string, string, number, string, string, Uint8Array][]> {
|
||||
return Promise.all(
|
||||
writes.map(async ([channel, value], idx) => {
|
||||
const [type, serializedValue] = await this.serde.dumpsTyped(value);
|
||||
return [
|
||||
threadId,
|
||||
checkpointNs,
|
||||
checkpointId,
|
||||
taskId,
|
||||
WRITES_IDX_MAP[channel] ?? idx,
|
||||
channel,
|
||||
type,
|
||||
new Uint8Array(serializedValue),
|
||||
];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
protected _searchWhere(
|
||||
config?: RunnableConfig,
|
||||
filter?: Record<string, unknown>,
|
||||
before?: RunnableConfig,
|
||||
): FindOptions {
|
||||
const findOptions: FindOptions = {};
|
||||
|
||||
// construct predicate for config filter
|
||||
if (config?.configurable?.thread_id) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['threadId'] = config.configurable.thread_id;
|
||||
}
|
||||
|
||||
// strict checks for undefined/null because empty strings are falsy
|
||||
if (config?.configurable?.checkpoint_ns !== undefined && config?.configurable?.checkpoint_ns !== null) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['checkpointNs'] = config.configurable.checkpoint_ns;
|
||||
}
|
||||
|
||||
if (config?.configurable?.checkpoint_id) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['checkpointId'] = config.configurable.checkpoint_id;
|
||||
}
|
||||
|
||||
// construct predicate for metadata filter
|
||||
if (filter && Object.keys(filter).length > 0) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
if (this.sequelize.getDialect() === 'postgres') {
|
||||
findOptions.where['metadata'] = {
|
||||
[Op.contains]: filter,
|
||||
};
|
||||
} else {
|
||||
// TODO mysql mariadb sqlite mssql oracle`s metadata filter to be add
|
||||
}
|
||||
}
|
||||
|
||||
// construct predicate for `before`
|
||||
if (before?.configurable?.checkpoint_id !== undefined) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['checkpointId'] = {
|
||||
$lt: before.configurable.checkpoint_id,
|
||||
};
|
||||
}
|
||||
|
||||
return findOptions;
|
||||
}
|
||||
|
||||
private async getCheckpointWrites(
|
||||
threadIds: string[],
|
||||
checkpointNs?: string,
|
||||
checkpointId?: string,
|
||||
): Promise<Record<string, [Uint8Array, Uint8Array, Uint8Array, Uint8Array][]>> {
|
||||
if (!threadIds?.length) {
|
||||
return {};
|
||||
}
|
||||
const where = {
|
||||
threadId:
|
||||
threadIds.length === 1
|
||||
? threadIds[0]
|
||||
: {
|
||||
[Op.in]: threadIds,
|
||||
},
|
||||
};
|
||||
if (checkpointNs) {
|
||||
where['checkpointNs'] = checkpointNs;
|
||||
}
|
||||
if (checkpointId) {
|
||||
where['checkpointId'] = checkpointId;
|
||||
}
|
||||
const writes = await this.checkpointWritesModel.findAll({
|
||||
where,
|
||||
});
|
||||
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
taskId: string;
|
||||
channel: string;
|
||||
type: string;
|
||||
blob: Uint8Array;
|
||||
idx: number;
|
||||
}[]
|
||||
> = {};
|
||||
for (const write of writes) {
|
||||
const key = `${write.threadId}:${write.checkpointNs}:${write.checkpointId}`;
|
||||
if (!result[key]) {
|
||||
result[key] = [];
|
||||
}
|
||||
result[key].push({
|
||||
taskId: write.taskId,
|
||||
channel: write.channel,
|
||||
type: write.type,
|
||||
blob: write.blob ? Uint8Array.from(write.blob) : null,
|
||||
idx: write.idx,
|
||||
});
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(result).map(([key, list]) => [
|
||||
key,
|
||||
[...list]
|
||||
.sort((a, b) => {
|
||||
if (a.taskId !== b.taskId) {
|
||||
return a.taskId.localeCompare(b.taskId);
|
||||
}
|
||||
return a.idx - b.idx;
|
||||
})
|
||||
.map(({ taskId, channel, type, blob }) => [
|
||||
new TextEncoder().encode(taskId),
|
||||
new TextEncoder().encode(channel),
|
||||
new TextEncoder().encode(type),
|
||||
blob,
|
||||
]),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private async getPendingSends(threadIds: string[]): Promise<Record<string, [Uint8Array, Uint8Array][]>> {
|
||||
if (!threadIds?.length) {
|
||||
return {};
|
||||
}
|
||||
const writes = await this.checkpointWritesModel.findAll({
|
||||
where: {
|
||||
threadId: {
|
||||
[Op.in]: threadIds,
|
||||
},
|
||||
channel: TASKS,
|
||||
},
|
||||
});
|
||||
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
taskId: string;
|
||||
channel: string;
|
||||
type: string;
|
||||
blob: Uint8Array;
|
||||
idx: number;
|
||||
}[]
|
||||
> = {};
|
||||
for (const write of writes) {
|
||||
const key = `${write.threadId}:${write.checkpointId}`;
|
||||
if (!result[key]) {
|
||||
result[key] = [];
|
||||
}
|
||||
result[key].push({
|
||||
taskId: write.taskId,
|
||||
channel: write.channel,
|
||||
type: write.type,
|
||||
blob: write.blob ? Uint8Array.from(write.blob) : null,
|
||||
idx: write.idx,
|
||||
});
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(result).map(([key, list]) => [
|
||||
key,
|
||||
[...list]
|
||||
.sort((a, b) => {
|
||||
if (a.taskId !== b.taskId) {
|
||||
return a.taskId.localeCompare(b.taskId);
|
||||
}
|
||||
return a.idx - b.idx;
|
||||
})
|
||||
.map(({ type, blob }) => [new TextEncoder().encode(type), blob]),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private async getCheckpointBlobs(
|
||||
threadIds: string[],
|
||||
): Promise<Record<string, [Uint8Array, Uint8Array, Uint8Array][]>> {
|
||||
const blobs = await this.checkpointBlobsModel.findAll({
|
||||
where: {
|
||||
threadId:
|
||||
threadIds.length === 1
|
||||
? threadIds[0]
|
||||
: {
|
||||
[Op.in]: threadIds,
|
||||
},
|
||||
},
|
||||
});
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
taskId: string;
|
||||
checkpointNs: string;
|
||||
channel: string;
|
||||
version: string;
|
||||
type: string;
|
||||
blob: Uint8Array;
|
||||
}[]
|
||||
> = {};
|
||||
for (const blob of blobs) {
|
||||
const key = `${blob.threadId}:${blob.checkpointNs}:${blob.channel}:${blob.version}`;
|
||||
if (!result[key]) {
|
||||
result[key] = [];
|
||||
}
|
||||
result[key].push({
|
||||
taskId: blob.taskId,
|
||||
checkpointNs: blob.checkpointNs,
|
||||
channel: blob.channel,
|
||||
version: blob.version,
|
||||
type: blob.type,
|
||||
blob: blob.blob ? Uint8Array.from(blob.blob) : null,
|
||||
});
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(result).map(([key, list]) => [
|
||||
key,
|
||||
list.map(({ channel, type, blob }) => [
|
||||
new TextEncoder().encode(channel),
|
||||
new TextEncoder().encode(type),
|
||||
blob,
|
||||
]),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private get checkpointsModel() {
|
||||
return this.collectionManager.getCollection('lcCheckpoints').model;
|
||||
}
|
||||
|
||||
private get checkpointBlobsModel() {
|
||||
return this.collectionManager.getCollection('lcCheckpointBlobs').model;
|
||||
}
|
||||
|
||||
private get checkpointWritesModel() {
|
||||
return this.collectionManager.getCollection('lcCheckpointWrites').model;
|
||||
}
|
||||
|
||||
private get sequelize() {
|
||||
return this.collectionManager.db.sequelize;
|
||||
}
|
||||
|
||||
private get collectionManager() {
|
||||
return this.provideCollectionManager().collectionManager;
|
||||
}
|
||||
}
|
||||
export { SequelizeCollectionSaver } from './saver';
|
||||
export { CheckpointCleaner } from './cleaner';
|
||||
|
||||
@@ -0,0 +1,740 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { RunnableConfig } from '@langchain/core/runnables';
|
||||
import {
|
||||
BaseCheckpointSaver,
|
||||
ChannelVersions,
|
||||
Checkpoint,
|
||||
CheckpointListOptions,
|
||||
CheckpointMetadata,
|
||||
CheckpointTuple,
|
||||
maxChannelVersion,
|
||||
PendingWrite,
|
||||
SerializerProtocol,
|
||||
TASKS,
|
||||
WRITES_IDX_MAP,
|
||||
} from '@langchain/langgraph-checkpoint';
|
||||
import { SequelizeCollectionManager } from '@nocobase/data-source-manager';
|
||||
import { FindOptions, Op } from '@nocobase/database';
|
||||
|
||||
export class SequelizeCollectionSaver extends BaseCheckpointSaver {
|
||||
constructor(
|
||||
private readonly provideCollectionManager: () => { collectionManager: SequelizeCollectionManager },
|
||||
serde?: SerializerProtocol,
|
||||
) {
|
||||
super(serde);
|
||||
}
|
||||
|
||||
async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {
|
||||
const { thread_id, checkpoint_ns = '', checkpoint_id } = config.configurable ?? {};
|
||||
|
||||
let findOptions: FindOptions;
|
||||
if (checkpoint_id) {
|
||||
findOptions = {
|
||||
where: {
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
checkpointId: checkpoint_id,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
findOptions = {
|
||||
where: {
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
},
|
||||
order: [['checkpointId', 'DESC']],
|
||||
limit: 1,
|
||||
};
|
||||
}
|
||||
|
||||
const checkpointRow = (await this.checkpointsModel.findOne(findOptions))?.toJSON();
|
||||
if (!checkpointRow) {
|
||||
return undefined;
|
||||
}
|
||||
const { threadId, checkpointNs, checkpointId, parentCheckpointId } = checkpointRow;
|
||||
|
||||
checkpointRow.channelValues = [];
|
||||
for (const [channel, version] of Object.entries(checkpointRow.checkpoint.channel_versions ?? {})) {
|
||||
const blob = (
|
||||
await this.checkpointBlobsModel.findOne({
|
||||
where: {
|
||||
threadId,
|
||||
checkpointNs,
|
||||
channel,
|
||||
version: String(version),
|
||||
},
|
||||
})
|
||||
)?.toJSON();
|
||||
if (!blob) {
|
||||
continue;
|
||||
}
|
||||
checkpointRow.channelValues.push([
|
||||
new TextEncoder().encode(blob.channel),
|
||||
new TextEncoder().encode(blob.type),
|
||||
blob.blob ? Uint8Array.from(blob.blob) : null,
|
||||
]);
|
||||
}
|
||||
|
||||
const checkpointWrites = await this.getCheckpointWrites([threadId], checkpointNs, checkpointId);
|
||||
checkpointRow.pendingWrites = checkpointWrites[`${threadId}:${checkpointNs}:${checkpointId}`];
|
||||
|
||||
if (checkpointRow.checkpoint.v < 4 && checkpointRow.parentCheckpointId != null) {
|
||||
const sendsResult = await this.getPendingSends([threadId]);
|
||||
const pendingSends = sendsResult[`${threadId}:${parentCheckpointId}`];
|
||||
if (pendingSends?.length) {
|
||||
await this._migratePendingSends(pendingSends, checkpointRow);
|
||||
}
|
||||
}
|
||||
|
||||
const checkpoint = await this._loadCheckpoint(checkpointRow.checkpoint, checkpointRow.channelValues);
|
||||
|
||||
const finalConfig = {
|
||||
configurable: {
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint_id: checkpointId,
|
||||
},
|
||||
};
|
||||
const metadata = await this._loadMetadata(checkpointRow.metadata);
|
||||
const parentConfig = parentCheckpointId
|
||||
? {
|
||||
configurable: {
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint_id: parentCheckpointId,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const pendingWrites = await this._loadWrites(checkpointRow.pendingWrites);
|
||||
|
||||
return {
|
||||
config: finalConfig,
|
||||
checkpoint,
|
||||
metadata,
|
||||
parentConfig,
|
||||
pendingWrites,
|
||||
};
|
||||
}
|
||||
|
||||
async *list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple> {
|
||||
const { filter, before, limit } = options ?? {};
|
||||
const findOptions = this._searchWhere(config, filter, before);
|
||||
findOptions.order = [['checkpointId', 'DESC']];
|
||||
if (limit !== undefined) {
|
||||
findOptions.limit = Number.parseInt(limit.toString(), 10); // sanitize via parseInt, as limit could be an externally provided value
|
||||
}
|
||||
|
||||
const result = (await this.checkpointsModel.findAll(findOptions)).map((x) => x.toJSON());
|
||||
const [checkpointWrites, checkpointBlobs] = await Promise.all([
|
||||
this.getCheckpointWrites(result.map((x) => x.threadId)),
|
||||
this.getCheckpointBlobs(result.map((x) => x.threadId)),
|
||||
]);
|
||||
|
||||
for (const checkpointRow of result) {
|
||||
const { threadId, checkpointNs, checkpointId } = checkpointRow;
|
||||
checkpointRow.channelValues = Object.entries(checkpointRow.checkpoint.channel_versions ?? {})
|
||||
.map(([channel, version]) => checkpointBlobs[`${threadId}:${checkpointNs}:${channel}:${version}`])
|
||||
.filter((x) => x?.length)
|
||||
.flatMap((x) => [...x]);
|
||||
checkpointRow.pendingWrites = checkpointWrites[`${threadId}:${checkpointNs}:${checkpointId}`];
|
||||
}
|
||||
const toMigrate = result.filter((row) => row.checkpoint.v < 4 && row.parentCheckpointId != null);
|
||||
|
||||
if (toMigrate.length > 0) {
|
||||
const sendsResult = await this.getPendingSends(result.map((x) => x.threadId));
|
||||
for (const row of toMigrate) {
|
||||
const pendingSends = sendsResult[`${row.threadId}:${row.parentCheckpointId}`];
|
||||
if (pendingSends?.length) {
|
||||
await this._migratePendingSends(pendingSends, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const value of result) {
|
||||
yield {
|
||||
config: {
|
||||
configurable: {
|
||||
thread_id: value.threadId,
|
||||
checkpoint_ns: value.checkpointNs,
|
||||
checkpoint_id: value.checkpointId,
|
||||
},
|
||||
},
|
||||
checkpoint: await this._loadCheckpoint(value.checkpoint, value.channelValues),
|
||||
metadata: await this._loadMetadata(value.metadata),
|
||||
parentConfig: value.parentCheckpointId
|
||||
? {
|
||||
configurable: {
|
||||
thread_id: value.threadId,
|
||||
checkpoint_ns: value.checkpointNs,
|
||||
checkpoint_id: value.parentCheckpointId,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
pendingWrites: await this._loadWrites(value.pendingWrites),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async _migratePendingSends(
|
||||
pendingSends: [Uint8Array, Uint8Array][],
|
||||
mutableRow: {
|
||||
channelValues: [Uint8Array, Uint8Array, Uint8Array][];
|
||||
checkpoint: Omit<Checkpoint, 'pending_sends' | 'channel_values'>;
|
||||
},
|
||||
) {
|
||||
const textEncoder = new TextEncoder();
|
||||
const textDecoder = new TextDecoder();
|
||||
const row = mutableRow;
|
||||
|
||||
const [enc, blob] = await this.serde.dumpsTyped(
|
||||
await Promise.all(pendingSends.map(([enc, blob]) => this.serde.loadsTyped(textDecoder.decode(enc), blob))),
|
||||
);
|
||||
|
||||
row.channelValues ??= [];
|
||||
row.channelValues.push([textEncoder.encode(TASKS), textEncoder.encode(enc), blob]);
|
||||
|
||||
// add to versions
|
||||
row.checkpoint.channel_versions[TASKS] =
|
||||
Object.keys(mutableRow.checkpoint.channel_versions).length > 0
|
||||
? maxChannelVersion(...Object.values(mutableRow.checkpoint.channel_versions))
|
||||
: this.getNextVersion(undefined);
|
||||
}
|
||||
|
||||
async put(
|
||||
config: RunnableConfig,
|
||||
checkpoint: Checkpoint,
|
||||
metadata: CheckpointMetadata,
|
||||
newVersions: ChannelVersions,
|
||||
): Promise<RunnableConfig> {
|
||||
if (config.configurable === undefined) {
|
||||
throw new Error(`Missing "configurable" field in "config" param`);
|
||||
}
|
||||
const { thread_id, checkpoint_ns = '', checkpoint_id } = config.configurable;
|
||||
|
||||
const nextConfig = {
|
||||
configurable: {
|
||||
thread_id,
|
||||
checkpoint_ns,
|
||||
checkpoint_id: checkpoint.id,
|
||||
},
|
||||
};
|
||||
const serializedCheckpoint = this._dumpCheckpoint(checkpoint);
|
||||
const serializedBlobs = await this._dumpBlobs(thread_id, checkpoint_ns, checkpoint.channel_values, newVersions);
|
||||
const serializedMetadata = await this._dumpMetadata(metadata);
|
||||
|
||||
return await this.sequelize.transaction(async (transaction) => {
|
||||
const checkpointBlobs = await this.checkpointBlobsModel.findAll({
|
||||
where: {
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
},
|
||||
attributes: {
|
||||
exclude: ['blob'],
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
const duplicateBlobsFilter = checkpointBlobs.map(({ channel, version }) => `${channel}:${version}`);
|
||||
|
||||
await this.checkpointBlobsModel.bulkCreate(
|
||||
serializedBlobs
|
||||
.filter(
|
||||
([_threadId, _checkpointNs, channel, version]) => !duplicateBlobsFilter.includes(`${channel}:${version}`),
|
||||
)
|
||||
.map(([threadId, checkpointNs, channel, version, type, blob]) => ({
|
||||
threadId,
|
||||
checkpointNs,
|
||||
channel,
|
||||
version,
|
||||
type,
|
||||
blob: blob ? Buffer.from(blob) : null,
|
||||
})),
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
const checkPointFindOptions: FindOptions = {
|
||||
where: { threadId: thread_id, checkpointNs: checkpoint_ns, checkpointId: checkpoint.id },
|
||||
transaction,
|
||||
};
|
||||
const existed = await this.checkpointsModel.count(checkPointFindOptions);
|
||||
if (existed === 0) {
|
||||
await this.checkpointsModel.create(
|
||||
{
|
||||
threadId: thread_id,
|
||||
checkpointNs: checkpoint_ns,
|
||||
checkpointId: checkpoint.id,
|
||||
parentCheckpointId: checkpoint_id,
|
||||
checkpoint: serializedCheckpoint,
|
||||
metadata: serializedMetadata,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await this.checkpointsModel.update(
|
||||
{
|
||||
checkpoint: serializedCheckpoint,
|
||||
metadata: serializedMetadata,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
where: checkPointFindOptions.where,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return nextConfig;
|
||||
});
|
||||
}
|
||||
async putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void> {
|
||||
if (!config.configurable?.thread_id) {
|
||||
throw new Error('config.configurable.thread_id is required');
|
||||
}
|
||||
const dumpedWrites = await this._dumpWrites(
|
||||
config.configurable?.thread_id,
|
||||
config.configurable?.checkpoint_ns,
|
||||
config.configurable?.checkpoint_id,
|
||||
taskId,
|
||||
writes,
|
||||
);
|
||||
return await this.sequelize.transaction(async (transaction) => {
|
||||
const checkpointWrites = await this.checkpointWritesModel.findAll({
|
||||
where: {
|
||||
threadId: config.configurable?.thread_id,
|
||||
checkpointNs: config.configurable?.checkpoint_ns,
|
||||
checkpointId: config.configurable?.checkpoint_id,
|
||||
taskId,
|
||||
},
|
||||
attributes: {
|
||||
exclude: ['blob'],
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
const duplicateWritesFilter = checkpointWrites.map(
|
||||
({ threadId, checkpointNs, checkpointId, taskId, idx }) =>
|
||||
`${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${idx}`,
|
||||
);
|
||||
const dumpedWritesInclude = ([threadId, checkpointNs, checkpointId, taskId, idx]: (typeof dumpedWrites)[0]) =>
|
||||
duplicateWritesFilter.includes(`${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${idx}`);
|
||||
const dumpedWritesExclude = (item) => !dumpedWritesInclude(item);
|
||||
|
||||
await this.checkpointWritesModel.bulkCreate(
|
||||
dumpedWrites
|
||||
.filter(dumpedWritesExclude)
|
||||
.map(([threadId, checkpointNs, checkpointId, taskId, idx, channel, type, blob]) => ({
|
||||
threadId,
|
||||
checkpointNs,
|
||||
checkpointId,
|
||||
taskId,
|
||||
idx,
|
||||
channel,
|
||||
type,
|
||||
blob: blob ? Buffer.from(blob) : null,
|
||||
})),
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
for (const [threadId, checkpointNs, checkpointId, taskId, idx, channel, type, blob] of dumpedWrites.filter(
|
||||
dumpedWritesInclude,
|
||||
)) {
|
||||
await this.checkpointWritesModel.update(
|
||||
{
|
||||
channel,
|
||||
type,
|
||||
blob: blob ? Buffer.from(blob) : null,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
threadId,
|
||||
checkpointNs,
|
||||
checkpointId,
|
||||
taskId,
|
||||
idx,
|
||||
},
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteThread(threadId: string): Promise<void> {
|
||||
return this.sequelize.transaction(async (transaction) => {
|
||||
await this.checkpointsModel.destroy({
|
||||
where: {
|
||||
threadId,
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
await this.checkpointBlobsModel.destroy({
|
||||
where: {
|
||||
threadId,
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
await this.checkpointWritesModel.destroy({
|
||||
where: {
|
||||
threadId,
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected async _loadCheckpoint(
|
||||
checkpoint: Omit<Checkpoint, 'pending_sends' | 'channel_values'>,
|
||||
channelValues: [Uint8Array, Uint8Array, Uint8Array][],
|
||||
): Promise<Checkpoint> {
|
||||
return {
|
||||
...checkpoint,
|
||||
channel_values: await this._loadBlobs(channelValues),
|
||||
};
|
||||
}
|
||||
|
||||
protected async _loadBlobs(blobValues: [Uint8Array, Uint8Array, Uint8Array][]): Promise<Record<string, unknown>> {
|
||||
if (!blobValues || blobValues.length === 0) {
|
||||
return {};
|
||||
}
|
||||
const textDecoder = new TextDecoder();
|
||||
const entries = await Promise.all(
|
||||
blobValues
|
||||
.filter(([, t]) => textDecoder.decode(t) !== 'empty')
|
||||
.map(async ([k, t, v]) => [textDecoder.decode(k), await this.serde.loadsTyped(textDecoder.decode(t), v)]),
|
||||
);
|
||||
return Object.fromEntries(entries);
|
||||
}
|
||||
|
||||
protected async _loadMetadata(metadata: Record<string, unknown>) {
|
||||
const [type, dumpedValue] = await this.serde.dumpsTyped(metadata);
|
||||
return this.serde.loadsTyped(type, dumpedValue);
|
||||
}
|
||||
|
||||
protected async _loadWrites(
|
||||
writes: [Uint8Array, Uint8Array, Uint8Array, Uint8Array][],
|
||||
): Promise<[string, string, unknown][]> {
|
||||
const decoder = new TextDecoder();
|
||||
return writes
|
||||
? await Promise.all(
|
||||
writes.map(async ([tid, channel, t, v]) => [
|
||||
decoder.decode(tid),
|
||||
decoder.decode(channel),
|
||||
await this.serde.loadsTyped(decoder.decode(t), v),
|
||||
]),
|
||||
)
|
||||
: [];
|
||||
}
|
||||
|
||||
protected async _dumpBlobs(
|
||||
threadId: string,
|
||||
checkpointNs: string,
|
||||
values: Record<string, unknown>,
|
||||
versions: ChannelVersions,
|
||||
): Promise<[string, string, string, string, string, Uint8Array | undefined][]> {
|
||||
if (Object.keys(versions).length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
Object.entries(versions).map(async ([k, ver]) => {
|
||||
const [type, value] = k in values ? await this.serde.dumpsTyped(values[k]) : ['empty', null];
|
||||
return [threadId, checkpointNs, k, ver.toString(), type, value ? new Uint8Array(value) : undefined];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
protected _dumpCheckpoint(checkpoint: Checkpoint) {
|
||||
const serialized: Record<string, unknown> = { ...checkpoint };
|
||||
if ('channel_values' in serialized) delete serialized.channel_values;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
protected async _dumpMetadata(metadata: CheckpointMetadata) {
|
||||
const [, serializedMetadata] = await this.serde.dumpsTyped(metadata);
|
||||
// We need to remove null characters before writing
|
||||
return JSON.parse(new TextDecoder().decode(serializedMetadata).replace(/\0/g, ''));
|
||||
}
|
||||
|
||||
protected async _dumpWrites(
|
||||
threadId: string,
|
||||
checkpointNs: string,
|
||||
checkpointId: string,
|
||||
taskId: string,
|
||||
writes: [string, unknown][],
|
||||
): Promise<[string, string, string, string, number, string, string, Uint8Array][]> {
|
||||
return Promise.all(
|
||||
writes.map(async ([channel, value], idx) => {
|
||||
const [type, serializedValue] = await this.serde.dumpsTyped(value);
|
||||
return [
|
||||
threadId,
|
||||
checkpointNs,
|
||||
checkpointId,
|
||||
taskId,
|
||||
WRITES_IDX_MAP[channel] ?? idx,
|
||||
channel,
|
||||
type,
|
||||
new Uint8Array(serializedValue),
|
||||
];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
protected _searchWhere(
|
||||
config?: RunnableConfig,
|
||||
filter?: Record<string, unknown>,
|
||||
before?: RunnableConfig,
|
||||
): FindOptions {
|
||||
const findOptions: FindOptions = {};
|
||||
|
||||
// construct predicate for config filter
|
||||
if (config?.configurable?.thread_id) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['threadId'] = config.configurable.thread_id;
|
||||
}
|
||||
|
||||
// strict checks for undefined/null because empty strings are falsy
|
||||
if (config?.configurable?.checkpoint_ns !== undefined && config?.configurable?.checkpoint_ns !== null) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['checkpointNs'] = config.configurable.checkpoint_ns;
|
||||
}
|
||||
|
||||
if (config?.configurable?.checkpoint_id) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['checkpointId'] = config.configurable.checkpoint_id;
|
||||
}
|
||||
|
||||
// construct predicate for metadata filter
|
||||
if (filter && Object.keys(filter).length > 0) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
if (this.sequelize.getDialect() === 'postgres') {
|
||||
findOptions.where['metadata'] = {
|
||||
[Op.contains]: filter,
|
||||
};
|
||||
} else {
|
||||
// TODO mysql mariadb sqlite mssql oracle`s metadata filter to be add
|
||||
}
|
||||
}
|
||||
|
||||
// construct predicate for `before`
|
||||
if (before?.configurable?.checkpoint_id !== undefined) {
|
||||
if (!findOptions.where) {
|
||||
findOptions.where = {};
|
||||
}
|
||||
findOptions.where['checkpointId'] = {
|
||||
$lt: before.configurable.checkpoint_id,
|
||||
};
|
||||
}
|
||||
|
||||
return findOptions;
|
||||
}
|
||||
|
||||
private async getCheckpointWrites(
|
||||
threadIds: string[],
|
||||
checkpointNs?: string,
|
||||
checkpointId?: string,
|
||||
): Promise<Record<string, [Uint8Array, Uint8Array, Uint8Array, Uint8Array][]>> {
|
||||
if (!threadIds?.length) {
|
||||
return {};
|
||||
}
|
||||
const where = {
|
||||
threadId:
|
||||
threadIds.length === 1
|
||||
? threadIds[0]
|
||||
: {
|
||||
[Op.in]: threadIds,
|
||||
},
|
||||
};
|
||||
if (checkpointNs) {
|
||||
where['checkpointNs'] = checkpointNs;
|
||||
}
|
||||
if (checkpointId) {
|
||||
where['checkpointId'] = checkpointId;
|
||||
}
|
||||
const writes = await this.checkpointWritesModel.findAll({
|
||||
where,
|
||||
});
|
||||
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
taskId: string;
|
||||
channel: string;
|
||||
type: string;
|
||||
blob: Uint8Array;
|
||||
idx: number;
|
||||
}[]
|
||||
> = {};
|
||||
for (const write of writes) {
|
||||
const key = `${write.threadId}:${write.checkpointNs}:${write.checkpointId}`;
|
||||
if (!result[key]) {
|
||||
result[key] = [];
|
||||
}
|
||||
result[key].push({
|
||||
taskId: write.taskId,
|
||||
channel: write.channel,
|
||||
type: write.type,
|
||||
blob: write.blob ? Uint8Array.from(write.blob) : null,
|
||||
idx: write.idx,
|
||||
});
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(result).map(([key, list]) => [
|
||||
key,
|
||||
[...list]
|
||||
.sort((a, b) => {
|
||||
if (a.taskId !== b.taskId) {
|
||||
return a.taskId.localeCompare(b.taskId);
|
||||
}
|
||||
return a.idx - b.idx;
|
||||
})
|
||||
.map(({ taskId, channel, type, blob }) => [
|
||||
new TextEncoder().encode(taskId),
|
||||
new TextEncoder().encode(channel),
|
||||
new TextEncoder().encode(type),
|
||||
blob,
|
||||
]),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private async getPendingSends(threadIds: string[]): Promise<Record<string, [Uint8Array, Uint8Array][]>> {
|
||||
if (!threadIds?.length) {
|
||||
return {};
|
||||
}
|
||||
const writes = await this.checkpointWritesModel.findAll({
|
||||
where: {
|
||||
threadId: {
|
||||
[Op.in]: threadIds,
|
||||
},
|
||||
channel: TASKS,
|
||||
},
|
||||
});
|
||||
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
taskId: string;
|
||||
channel: string;
|
||||
type: string;
|
||||
blob: Uint8Array;
|
||||
idx: number;
|
||||
}[]
|
||||
> = {};
|
||||
for (const write of writes) {
|
||||
const key = `${write.threadId}:${write.checkpointId}`;
|
||||
if (!result[key]) {
|
||||
result[key] = [];
|
||||
}
|
||||
result[key].push({
|
||||
taskId: write.taskId,
|
||||
channel: write.channel,
|
||||
type: write.type,
|
||||
blob: write.blob ? Uint8Array.from(write.blob) : null,
|
||||
idx: write.idx,
|
||||
});
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(result).map(([key, list]) => [
|
||||
key,
|
||||
[...list]
|
||||
.sort((a, b) => {
|
||||
if (a.taskId !== b.taskId) {
|
||||
return a.taskId.localeCompare(b.taskId);
|
||||
}
|
||||
return a.idx - b.idx;
|
||||
})
|
||||
.map(({ type, blob }) => [new TextEncoder().encode(type), blob]),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private async getCheckpointBlobs(
|
||||
threadIds: string[],
|
||||
): Promise<Record<string, [Uint8Array, Uint8Array, Uint8Array][]>> {
|
||||
const blobs = await this.checkpointBlobsModel.findAll({
|
||||
where: {
|
||||
threadId:
|
||||
threadIds.length === 1
|
||||
? threadIds[0]
|
||||
: {
|
||||
[Op.in]: threadIds,
|
||||
},
|
||||
},
|
||||
});
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
taskId: string;
|
||||
checkpointNs: string;
|
||||
channel: string;
|
||||
version: string;
|
||||
type: string;
|
||||
blob: Uint8Array;
|
||||
}[]
|
||||
> = {};
|
||||
for (const blob of blobs) {
|
||||
const key = `${blob.threadId}:${blob.checkpointNs}:${blob.channel}:${blob.version}`;
|
||||
if (!result[key]) {
|
||||
result[key] = [];
|
||||
}
|
||||
result[key].push({
|
||||
taskId: blob.taskId,
|
||||
checkpointNs: blob.checkpointNs,
|
||||
channel: blob.channel,
|
||||
version: blob.version,
|
||||
type: blob.type,
|
||||
blob: blob.blob ? Uint8Array.from(blob.blob) : null,
|
||||
});
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(result).map(([key, list]) => [
|
||||
key,
|
||||
list.map(({ channel, type, blob }) => [
|
||||
new TextEncoder().encode(channel),
|
||||
new TextEncoder().encode(type),
|
||||
blob,
|
||||
]),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private get checkpointsModel() {
|
||||
return this.collectionManager.getCollection('lcCheckpoints').model;
|
||||
}
|
||||
|
||||
private get checkpointBlobsModel() {
|
||||
return this.collectionManager.getCollection('lcCheckpointBlobs').model;
|
||||
}
|
||||
|
||||
private get checkpointWritesModel() {
|
||||
return this.collectionManager.getCollection('lcCheckpointWrites').model;
|
||||
}
|
||||
|
||||
private get sequelize() {
|
||||
return this.collectionManager.db.sequelize;
|
||||
}
|
||||
|
||||
private get collectionManager() {
|
||||
return this.provideCollectionManager().collectionManager;
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import { AICodingManager } from './manager/ai-coding-manager';
|
||||
import { kimiProviderOptions } from './llm-providers/kimi';
|
||||
import { DocumentLoaders } from './document-loader';
|
||||
import type PluginFileManagerServer from '@nocobase/plugin-file-manager';
|
||||
import { CheckpointCleaner, SequelizeCollectionSaver } from './ai-employees/checkpoints';
|
||||
// import { tongyiProviderOptions } from './llm-providers/tongyi';
|
||||
|
||||
export class PluginAIServer extends Plugin {
|
||||
@@ -74,6 +75,19 @@ export class PluginAIServer extends Plugin {
|
||||
},
|
||||
});
|
||||
this.snowflake = new Snowflake(pluginRecord?.createdAt.getTime());
|
||||
this.app.cronJobManager.addJob({
|
||||
cronTime: '0 0 2 * * *',
|
||||
onTick: async () => {
|
||||
try {
|
||||
const checkpointSaver = new SequelizeCollectionSaver(() => this.app.mainDataSource);
|
||||
const checkpointCleaner = new CheckpointCleaner(() => this.app.mainDataSource, checkpointSaver);
|
||||
const expiredAt = new Date(Date.now() - 48 * 60 * 60 * 1000);
|
||||
await checkpointCleaner.cleanOutdated(expiredAt);
|
||||
} catch (e) {
|
||||
this.app.log.error('langChain checkpoint clean job fail', e);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
|
||||
@@ -92,19 +92,6 @@
|
||||
dependencies:
|
||||
"@alicloud/tea-typescript" "^1.5.1"
|
||||
|
||||
"@alicloud/dingtalk@2.2.24":
|
||||
version "2.2.24"
|
||||
resolved "https://registry.npmjs.org/@alicloud/dingtalk/-/dingtalk-2.2.24.tgz#df986380bf6a832ef85d02a63c1ba9234e1c67e5"
|
||||
integrity sha512-OHhYAbIyVhug6bm4pNVElJmZYpgHkvKL6QEkpRFE0geduOUhwB6hY3iLNW2C7RjijUmsWsSvyQRkKWHYgw+8Rg==
|
||||
dependencies:
|
||||
"@alicloud/endpoint-util" "^0.0.2"
|
||||
"@alicloud/gateway-dingtalk" "^1.0.2"
|
||||
"@alicloud/gateway-spi" "^0.0.8"
|
||||
"@alicloud/openapi-client" "^0.4.14"
|
||||
"@alicloud/openapi-util" "^0.3.2"
|
||||
"@alicloud/tea-typescript" "^1.7.1"
|
||||
"@alicloud/tea-util" "^1.4.9"
|
||||
|
||||
"@alicloud/dysmsapi20170525@2.0.17":
|
||||
version "2.0.17"
|
||||
resolved "https://registry.npmmirror.com/@alicloud/dysmsapi20170525/-/dysmsapi20170525-2.0.17.tgz#a350a443f52456b823772345dd57cc5fe2e6c8da"
|
||||
@@ -124,20 +111,6 @@
|
||||
"@alicloud/tea-typescript" "^1.5.1"
|
||||
kitx "^2.0.0"
|
||||
|
||||
"@alicloud/endpoint-util@^0.0.2":
|
||||
version "0.0.2"
|
||||
resolved "https://registry.npmjs.org/@alicloud/endpoint-util/-/endpoint-util-0.0.2.tgz#a86fade1fac4242442e1d19e030f5aa31070d2d3"
|
||||
integrity sha512-7aqVtcRzM0dVUE7bHLP2wKFuZygx5V6MTHBIbhGH3gfkN3/VZ9LlrxhkEfOCYtWfAyo9q0t9ScxQ4khvhweMqw==
|
||||
|
||||
"@alicloud/gateway-dingtalk@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/@alicloud/gateway-dingtalk/-/gateway-dingtalk-1.0.2.tgz#3970f07324c59935892f5b9abce66e6c2ae29dfc"
|
||||
integrity sha512-T8ml6kth/nCRthrtHIYnCYv7+q/41SnJaR8c99491azNSPcmMmgxis5ujYIl5irKm0cvoOCCjI9EWUFb2Tx7JA==
|
||||
dependencies:
|
||||
"@alicloud/gateway-spi" "^0.0.8"
|
||||
"@alicloud/tea-typescript" "^1.7.1"
|
||||
"@alicloud/tea-util" "^1.4.5"
|
||||
|
||||
"@alicloud/gateway-pop@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.npmjs.org/@alicloud/gateway-pop/-/gateway-pop-0.0.6.tgz#6b84cdbb3b8334f4ad8bca29b736d9c60fcce8cf"
|
||||
@@ -175,7 +148,7 @@
|
||||
"@alicloud/tea-util" "^1.4.9"
|
||||
"@alicloud/tea-xml" "0.0.3"
|
||||
|
||||
"@alicloud/openapi-client@^0.4.14", "@alicloud/openapi-client@^0.4.8", "@alicloud/openapi-client@^0.4.9":
|
||||
"@alicloud/openapi-client@^0.4.8":
|
||||
version "0.4.15"
|
||||
resolved "https://registry.npmjs.org/@alicloud/openapi-client/-/openapi-client-0.4.15.tgz#fde48ae16af661897883db920a3242e6466447d8"
|
||||
integrity sha512-4VE0/k5ZdQbAhOSTqniVhuX1k5DUeUMZv74degn3wIWjLY6Bq+hxjaGsaHYlLZ2gA5wUrs8NcI5TE+lIQS3iiA==
|
||||
@@ -241,7 +214,7 @@
|
||||
"@alicloud/tea-typescript" "^1.5.1"
|
||||
kitx "^2.0.0"
|
||||
|
||||
"@alicloud/tea-util@^1.4.5", "@alicloud/tea-util@^1.4.8":
|
||||
"@alicloud/tea-util@^1.4.8":
|
||||
version "1.4.11"
|
||||
resolved "https://registry.npmjs.org/@alicloud/tea-util/-/tea-util-1.4.11.tgz#17fee4f84f41730ba196c27824f8d0d16f9753b5"
|
||||
integrity sha512-HyPEEQ8F0WoZegiCp7sVdrdm6eBOB+GCvGl4182u69LDFktxfirGLcAx3WExUr1zFWkq2OSmBroTwKQ4w/+Yww==
|
||||
@@ -1456,14 +1429,6 @@
|
||||
"@smithy/util-utf8" "^4.0.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/protocol-http@^3.374.0":
|
||||
version "3.374.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.374.0.tgz#e35e76096b995bbed803897a9f4587d11ca34088"
|
||||
integrity sha512-9WpRUbINdGroV3HiZZIBoJvL2ndoWk39OfwxWs2otxByppJZNN14bg/lvCx5e8ggHUti7IBk5rb0nqQZ4m05pg==
|
||||
dependencies:
|
||||
"@smithy/protocol-http" "^1.1.0"
|
||||
tslib "^2.5.0"
|
||||
|
||||
"@aws-sdk/region-config-resolver@3.734.0":
|
||||
version "3.734.0"
|
||||
resolved "https://registry.npmmirror.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz#45ffbc56a3e94cc5c9e0cd596b0fda60f100f70b"
|
||||
@@ -1476,35 +1441,6 @@
|
||||
"@smithy/util-middleware" "^4.0.1"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/s3-presigned-post@3.750.0":
|
||||
version "3.750.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.750.0.tgz#9478be88ed4fe4577090d214784c10345a8c55d3"
|
||||
integrity sha512-pKCc/ZMj4rSnMwRyRiMfmTIPj5ODc0VM11+Lkywl+rEWru9kH05fww6TYximZuiBcixbaMVkQ4ePXj6DNsRB4w==
|
||||
dependencies:
|
||||
"@aws-sdk/client-s3" "3.750.0"
|
||||
"@aws-sdk/types" "3.734.0"
|
||||
"@aws-sdk/util-format-url" "3.734.0"
|
||||
"@smithy/middleware-endpoint" "^4.0.5"
|
||||
"@smithy/signature-v4" "^5.0.1"
|
||||
"@smithy/types" "^4.1.0"
|
||||
"@smithy/util-hex-encoding" "^4.0.0"
|
||||
"@smithy/util-utf8" "^4.0.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/s3-request-presigner@3.750.0":
|
||||
version "3.750.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.750.0.tgz#7c014d6d30a4a8820ad3dcd49a411ad6d637939e"
|
||||
integrity sha512-G4GNngNQlh9EyJZj2WKOOikX0Fev1WSxTV/XJugaHlpnVriebvi3GzolrgxUpRrcGpFGWjmAxLi/gYxTUla1ow==
|
||||
dependencies:
|
||||
"@aws-sdk/signature-v4-multi-region" "3.750.0"
|
||||
"@aws-sdk/types" "3.734.0"
|
||||
"@aws-sdk/util-format-url" "3.734.0"
|
||||
"@smithy/middleware-endpoint" "^4.0.5"
|
||||
"@smithy/protocol-http" "^5.0.1"
|
||||
"@smithy/smithy-client" "^4.1.5"
|
||||
"@smithy/types" "^4.1.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/signature-v4-multi-region@3.750.0":
|
||||
version "3.750.0"
|
||||
resolved "https://registry.npmmirror.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.750.0.tgz#b948dfc7ab7fbcb97e0df6bdffc03b3f3cecb49a"
|
||||
@@ -1562,16 +1498,6 @@
|
||||
"@smithy/util-endpoints" "^3.0.1"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/util-format-url@3.734.0":
|
||||
version "3.734.0"
|
||||
resolved "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.734.0.tgz#d78c48d7fc9ff3e15e93d92620bf66b9d1e115fd"
|
||||
integrity sha512-TxZMVm8V4aR/QkW9/NhujvYpPZjUYqzLwSge5imKZbWFR806NP7RMwc5ilVuHF/bMOln/cVHkl42kATElWBvNw==
|
||||
dependencies:
|
||||
"@aws-sdk/types" "3.734.0"
|
||||
"@smithy/querystring-builder" "^4.0.1"
|
||||
"@smithy/types" "^4.1.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@aws-sdk/util-locate-window@^3.0.0":
|
||||
version "3.465.0"
|
||||
resolved "https://registry.npmmirror.com/@aws-sdk/util-locate-window/-/util-locate-window-3.465.0.tgz#0471428fb5eb749d4b72c427f5726f7b61fb90eb"
|
||||
@@ -5713,77 +5639,6 @@
|
||||
dependencies:
|
||||
js-tiktoken "^1.0.12"
|
||||
|
||||
"@ldapjs/asn1@2.0.0", "@ldapjs/asn1@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz#e25fa38fcf0b4310275d6a5a05fe4603efef5eb4"
|
||||
integrity sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA==
|
||||
|
||||
"@ldapjs/asn1@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-1.2.0.tgz#5e99338fb39ff518c205827bec0fd9a6bf6b42db"
|
||||
integrity sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw==
|
||||
|
||||
"@ldapjs/attribute@1.0.0", "@ldapjs/attribute@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/attribute/-/attribute-1.0.0.tgz#d81d626080584c1c80ef300a214458f9f78a8abb"
|
||||
integrity sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==
|
||||
dependencies:
|
||||
"@ldapjs/asn1" "2.0.0"
|
||||
"@ldapjs/protocol" "^1.2.1"
|
||||
process-warning "^2.1.0"
|
||||
|
||||
"@ldapjs/change@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/change/-/change-1.0.0.tgz#34818a3a31cb337d3b90ab853bb7fa90517c2c4f"
|
||||
integrity sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==
|
||||
dependencies:
|
||||
"@ldapjs/asn1" "2.0.0"
|
||||
"@ldapjs/attribute" "1.0.0"
|
||||
|
||||
"@ldapjs/controls@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/controls/-/controls-2.1.0.tgz#28449cd4352f9389fb52fbf699cfa62f3e8762e6"
|
||||
integrity sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==
|
||||
dependencies:
|
||||
"@ldapjs/asn1" "^1.2.0"
|
||||
"@ldapjs/protocol" "^1.2.1"
|
||||
|
||||
"@ldapjs/dn@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/dn/-/dn-1.1.0.tgz#3687c86d658d2e10aedc2c65a1ef40155dd7370b"
|
||||
integrity sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==
|
||||
dependencies:
|
||||
"@ldapjs/asn1" "2.0.0"
|
||||
process-warning "^2.1.0"
|
||||
|
||||
"@ldapjs/filter@^2.1.1":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/filter/-/filter-2.1.1.tgz#34f4774aa17086ed0186afe11c698f13dd586d56"
|
||||
integrity sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==
|
||||
dependencies:
|
||||
"@ldapjs/asn1" "2.0.0"
|
||||
"@ldapjs/protocol" "^1.2.1"
|
||||
process-warning "^2.1.0"
|
||||
|
||||
"@ldapjs/messages@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/messages/-/messages-1.3.0.tgz#dea3c35de6e768e54abd3c7fbaee151d3d01f386"
|
||||
integrity sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==
|
||||
dependencies:
|
||||
"@ldapjs/asn1" "^2.0.0"
|
||||
"@ldapjs/attribute" "^1.0.0"
|
||||
"@ldapjs/change" "^1.0.0"
|
||||
"@ldapjs/controls" "^2.1.0"
|
||||
"@ldapjs/dn" "^1.1.0"
|
||||
"@ldapjs/filter" "^2.1.1"
|
||||
"@ldapjs/protocol" "^1.2.1"
|
||||
process-warning "^2.2.0"
|
||||
|
||||
"@ldapjs/protocol@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz#d58d371d6958f28095e8de23b35341bcaba55cf3"
|
||||
integrity sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==
|
||||
|
||||
"@lerna/add@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f"
|
||||
@@ -8423,14 +8278,6 @@
|
||||
"@smithy/types" "^4.1.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/protocol-http@^1.1.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.2.0.tgz#a554e4dabb14508f0bc2cdef9c3710e2b294be04"
|
||||
integrity sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==
|
||||
dependencies:
|
||||
"@smithy/types" "^1.2.0"
|
||||
tslib "^2.5.0"
|
||||
|
||||
"@smithy/protocol-http@^3.0.11":
|
||||
version "3.0.11"
|
||||
resolved "https://registry.npmmirror.com/@smithy/protocol-http/-/protocol-http-3.0.11.tgz#a9ea712fe7cc3375378ac68d9168a7b6cd0b6f65"
|
||||
@@ -8541,13 +8388,6 @@
|
||||
"@smithy/util-stream" "^4.1.1"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/types@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz#9dc65767b0ee3d6681704fcc67665d6fc9b6a34e"
|
||||
integrity sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==
|
||||
dependencies:
|
||||
tslib "^2.5.0"
|
||||
|
||||
"@smithy/types@^2.7.0":
|
||||
version "2.7.0"
|
||||
resolved "https://registry.npmmirror.com/@smithy/types/-/types-2.7.0.tgz#6ed9ba5bff7c4d28c980cff967e6d8456840a4f3"
|
||||
@@ -9195,13 +9035,6 @@
|
||||
resolved "https://registry.npmmirror.com/@types/ali-oss/-/ali-oss-6.23.1.tgz#65a2841a8be69ceb3fbf25654d7d07676632b100"
|
||||
integrity sha512-4mmCq2gUPBaPo6UlLIS/wMc7LxzDCzEUlRJAbLEk68Ntw7KWuF4RUwrQz3gtJkAeIYQ/R6PN3IvPTqk8daVVKQ==
|
||||
|
||||
"@types/amqplib@^0.10.7":
|
||||
version "0.10.8"
|
||||
resolved "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.8.tgz#23f2945d055e9fd583da672aa5ec0c7350b9e4e6"
|
||||
integrity sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/archiver@^5.3.1":
|
||||
version "5.3.4"
|
||||
resolved "https://registry.npmmirror.com/@types/archiver/-/archiver-5.3.4.tgz#32172d5a56f165b5b4ac902e366248bf03d3ae84"
|
||||
@@ -9260,13 +9093,6 @@
|
||||
"@types/connect" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/carbone@^3.2.5":
|
||||
version "3.2.5"
|
||||
resolved "https://registry.npmjs.org/@types/carbone/-/carbone-3.2.5.tgz#3adc4c70a9c5f28e071363cb11bcf44243a4befb"
|
||||
integrity sha512-qApQQTj87OrULWkMHZVhf370KM2egpV5/W1Xrr0dCDTkVMNJKjD0yw3WlNftel5Co2Pv/E3BvqO8RQzRCtnBTw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/connect@*", "@types/connect@3.4.38":
|
||||
version "3.4.38"
|
||||
resolved "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
|
||||
@@ -9833,13 +9659,6 @@
|
||||
"@types/koa-compose" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ldapjs@^3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-3.0.6.tgz#63aec9036c2acfb0e0b7322df336cda2c37f8bbe"
|
||||
integrity sha512-E2Tn1ltJDYBsidOT9QG4engaQeQzRQ9aYNxVmjCkD33F7cIeLPgrRDXAYs0O35mK2YDU20c/+ZkNjeAPRGLM0Q==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lerna__package@*":
|
||||
version "5.1.3"
|
||||
resolved "https://registry.npmmirror.com/@types/lerna__package/-/lerna__package-5.1.3.tgz#3604531e882229dee8e3f2bd8c819c405e2fcb43"
|
||||
@@ -11035,11 +10854,6 @@
|
||||
loupe "^2.3.7"
|
||||
pretty-format "^29.7.0"
|
||||
|
||||
"@wecom/crypto@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/@wecom/crypto/-/crypto-1.0.1.tgz#6918ed9829043b06075eaa8cff84de2475476a58"
|
||||
integrity sha512-K4Ilkl1l64ceJDbj/kflx8ND/J88pcl8tKx4Ivp7IiCrshRJU+Uo5uWCjAa+PjUiLIdcQSZ4m4d0t1npMPCX5A==
|
||||
|
||||
"@xmldom/xmldom@^0.8.10", "@xmldom/xmldom@^0.8.5", "@xmldom/xmldom@^0.8.6", "@xmldom/xmldom@^0.8.8":
|
||||
version "0.8.11"
|
||||
resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608"
|
||||
@@ -11104,11 +10918,6 @@ abort-controller@^3.0.0:
|
||||
dependencies:
|
||||
event-target-shim "^5.0.0"
|
||||
|
||||
abstract-logging@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839"
|
||||
integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==
|
||||
|
||||
accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
||||
@@ -11411,14 +11220,6 @@ amp@0.3.1, amp@~0.3.1:
|
||||
resolved "https://registry.npmmirror.com/amp/-/amp-0.3.1.tgz#6adf8d58a74f361e82c1fa8d389c079e139fc47d"
|
||||
integrity sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==
|
||||
|
||||
amqplib@^0.10.7:
|
||||
version "0.10.9"
|
||||
resolved "https://registry.npmjs.org/amqplib/-/amqplib-0.10.9.tgz#5b744c21d624f9307d0399e4d339b7354675831c"
|
||||
integrity sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==
|
||||
dependencies:
|
||||
buffer-more-ints "~1.0.0"
|
||||
url-parse "~1.5.10"
|
||||
|
||||
animated-scroll-to@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmmirror.com/animated-scroll-to/-/animated-scroll-to-2.3.0.tgz#01d7a82db7ace7017eae11c5ebbafd3b0270bced"
|
||||
@@ -12387,13 +12188,6 @@ babel-runtime@^6.26.0:
|
||||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.11.0"
|
||||
|
||||
backoff@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f"
|
||||
integrity sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==
|
||||
dependencies:
|
||||
precond "0.2"
|
||||
|
||||
bail@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmmirror.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
|
||||
@@ -12820,11 +12614,6 @@ buffer-indexof-polyfill@~1.0.0:
|
||||
resolved "https://registry.npmmirror.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c"
|
||||
integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==
|
||||
|
||||
buffer-more-ints@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz#ef4f8e2dddbad429ed3828a9c55d44f05c611422"
|
||||
integrity sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==
|
||||
|
||||
buffer-writer@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
|
||||
@@ -13198,18 +12987,6 @@ capture-stack-trace@^1.0.0:
|
||||
resolved "https://registry.npmmirror.com/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz#1c43f6b059d4249e7f3f8724f15f048b927d3a8a"
|
||||
integrity sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==
|
||||
|
||||
carbone@^3.5.6:
|
||||
version "3.5.6"
|
||||
resolved "https://registry.npmjs.org/carbone/-/carbone-3.5.6.tgz#19ebf24d1f3b337e1c12903e5cf35ff7e3d9e6c8"
|
||||
integrity sha512-bjTEJAmVQnMJoFAIs6Z0tAUpQoglUH0i4dLV34m8rCKWKagfhAM/zJyyKkU6n9EeyuuoOfCyppsShdvYr0ZfDw==
|
||||
dependencies:
|
||||
dayjs "=1.11.11"
|
||||
dayjs-timezone-iana-plugin "=0.1.0"
|
||||
debug "=4.3.5"
|
||||
which "=2.0.2"
|
||||
yauzl "=2.10.0"
|
||||
yazl "=2.5.1"
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.npmmirror.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
@@ -15325,12 +15102,7 @@ dateformat@^3.0.0:
|
||||
resolved "https://registry.npmmirror.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
dayjs-timezone-iana-plugin@=0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmjs.org/dayjs-timezone-iana-plugin/-/dayjs-timezone-iana-plugin-0.1.0.tgz#216613f6ec80106ab8be025cf5935018c901e997"
|
||||
integrity sha512-xc8cIZmi4oKr2nfu41I/FDWZKa8n8YaRMxSz9MrpXTNo8c6ZsjZuIoy5RPNmLXPqntFuITWI8obB7lUA+CdzGQ==
|
||||
|
||||
dayjs@1.11.13, dayjs@=1.11.11, dayjs@^1.11.10, dayjs@^1.11.11, dayjs@^1.11.7, dayjs@^1.11.8, dayjs@^1.11.9, dayjs@^1.8.34, dayjs@^1.9.1, dayjs@~1.11.13, dayjs@~1.8.24:
|
||||
dayjs@1.11.13, dayjs@^1.11.10, dayjs@^1.11.11, dayjs@^1.11.7, dayjs@^1.11.8, dayjs@^1.11.9, dayjs@^1.8.34, dayjs@^1.9.1, dayjs@~1.11.13, dayjs@~1.8.24:
|
||||
version "1.11.13"
|
||||
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||
@@ -15375,13 +15147,6 @@ debug@4.3.4:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@=4.3.5:
|
||||
version "4.3.5"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
|
||||
integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^3.1.0, debug@^3.2.6, debug@^3.2.7:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||
@@ -15843,13 +15608,6 @@ dingbat-to-unicode@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz#5091dd673241453e6b5865e26e5a4452cdef5c83"
|
||||
integrity sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==
|
||||
|
||||
dingtalk-jsapi@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/dingtalk-jsapi/-/dingtalk-jsapi-3.1.1.tgz#bf0f978aa9f23efc0140845f0f010f3662e1311b"
|
||||
integrity sha512-Xwt4kv6EZEf5MZWR42yzIHKI95e+Hlqz6X6tcjCiLIyTN8crA87lgs5s3beN7epwCRHcPUyQ7vVm5Q/H3hiUCQ==
|
||||
dependencies:
|
||||
promise-polyfill "^7.1.0"
|
||||
|
||||
dir-glob@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
||||
@@ -21429,11 +21187,6 @@ joi@^17.13.3:
|
||||
"@sideway/formula" "^3.0.1"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
jose@^4.15.9:
|
||||
version "4.15.9"
|
||||
resolved "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100"
|
||||
integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==
|
||||
|
||||
joycon@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmmirror.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
|
||||
@@ -22112,26 +21865,6 @@ lazystream@^1.0.0:
|
||||
dependencies:
|
||||
readable-stream "^2.0.5"
|
||||
|
||||
ldapjs@^3.0.7:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.npmjs.org/ldapjs/-/ldapjs-3.0.7.tgz#c69fe2965bc50a747bce834f8183f1f77c3be75d"
|
||||
integrity sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==
|
||||
dependencies:
|
||||
"@ldapjs/asn1" "^2.0.0"
|
||||
"@ldapjs/attribute" "^1.0.0"
|
||||
"@ldapjs/change" "^1.0.0"
|
||||
"@ldapjs/controls" "^2.1.0"
|
||||
"@ldapjs/dn" "^1.1.0"
|
||||
"@ldapjs/filter" "^2.1.1"
|
||||
"@ldapjs/messages" "^1.3.0"
|
||||
"@ldapjs/protocol" "^1.2.1"
|
||||
abstract-logging "^2.0.1"
|
||||
assert-plus "^1.0.0"
|
||||
backoff "^2.5.0"
|
||||
once "^1.4.0"
|
||||
vasync "^2.2.1"
|
||||
verror "^1.10.1"
|
||||
|
||||
leac@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.npmmirror.com/leac/-/leac-0.6.0.tgz#dcf136e382e666bd2475f44a1096061b70dc0912"
|
||||
@@ -24760,11 +24493,6 @@ nano-memoize@^3.0.16:
|
||||
resolved "https://registry.npmmirror.com/nano-memoize/-/nano-memoize-3.0.16.tgz#454100602713973ac8639bde301e255dd54920ea"
|
||||
integrity sha512-JyK96AKVGAwVeMj3MoMhaSXaUNqgMbCRSQB3trUV8tYZfWEzqUBKdK1qJpfuNXgKeHOx1jv/IEYTM659ly7zUA==
|
||||
|
||||
nanoid@3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||
|
||||
nanoid@^2.1.0:
|
||||
version "2.1.11"
|
||||
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
|
||||
@@ -25279,11 +25007,6 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1
|
||||
resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||
|
||||
object-hash@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5"
|
||||
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
|
||||
|
||||
object-hash@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
|
||||
@@ -25455,11 +25178,6 @@ officeparser@^5.2.0:
|
||||
pdfjs-dist "^5.3.31"
|
||||
yauzl "^3.1.3"
|
||||
|
||||
oidc-token-hash@^5.0.3:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz#be8a8885c7e2478d21a674e15afa31f1bcc4a61f"
|
||||
integrity sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==
|
||||
|
||||
ollama@^0.6.3:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.npmmirror.com/ollama/-/ollama-0.6.3.tgz#b188573dd0ccb3b4759c1f8fa85067cb17f6673c"
|
||||
@@ -25613,16 +25331,6 @@ opener@^1.5.2:
|
||||
resolved "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
|
||||
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
|
||||
|
||||
openid-client@^5.4.2:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz#34cace862a3e6472ed7d0a8616ef73b7fb85a9c3"
|
||||
integrity sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==
|
||||
dependencies:
|
||||
jose "^4.15.9"
|
||||
lru-cache "^6.0.0"
|
||||
object-hash "^2.2.0"
|
||||
oidc-token-hash "^5.0.3"
|
||||
|
||||
opt-cli@1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.npmmirror.com/opt-cli/-/opt-cli-1.5.1.tgz#04db447b13c96b992eb31685266f4ed0d9736dc2"
|
||||
@@ -27428,11 +27136,6 @@ postgres-interval@^1.1.0:
|
||||
dependencies:
|
||||
xtend "^4.0.0"
|
||||
|
||||
precond@0.2:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac"
|
||||
integrity sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
@@ -27576,11 +27279,6 @@ process-warning@^1.0.0:
|
||||
resolved "https://registry.npmmirror.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616"
|
||||
integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==
|
||||
|
||||
process-warning@^2.1.0, process-warning@^2.2.0:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz#70d8a3251aab0eafe3a595d8ae2c5d2277f096a5"
|
||||
integrity sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==
|
||||
|
||||
process-warning@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/process-warning/-/process-warning-3.0.0.tgz#96e5b88884187a1dce6f5c3166d611132058710b"
|
||||
@@ -27601,11 +27299,6 @@ promise-inflight@^1.0.1:
|
||||
resolved "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
|
||||
integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
|
||||
|
||||
promise-polyfill@^7.1.0:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-7.1.2.tgz#ab05301d8c28536301622d69227632269a70ca3b"
|
||||
integrity sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ==
|
||||
|
||||
promise-retry@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmmirror.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d"
|
||||
@@ -27849,7 +27542,7 @@ qs@~6.5.2:
|
||||
resolved "https://registry.npmmirror.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
|
||||
integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
|
||||
|
||||
query-string@6.14.1, query-string@^6.13.6, query-string@^6.13.8, query-string@^6.9.0:
|
||||
query-string@^6.13.6, query-string@^6.13.8, query-string@^6.9.0:
|
||||
version "6.14.1"
|
||||
resolved "https://registry.npmmirror.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a"
|
||||
integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==
|
||||
@@ -27864,11 +27557,6 @@ querystring-es3@^0.2.0:
|
||||
resolved "https://registry.npmmirror.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||
integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==
|
||||
|
||||
querystringify@^2.1.1:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
||||
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
@@ -27976,16 +27664,6 @@ raw-body@2.5.2, raw-body@^2.3.3:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-body@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f"
|
||||
integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.6.3"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-body@~2.5.3:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2"
|
||||
@@ -33297,14 +32975,6 @@ url-parse-lax@^3.0.0:
|
||||
dependencies:
|
||||
prepend-http "^2.0.0"
|
||||
|
||||
url-parse@~1.5.10:
|
||||
version "1.5.10"
|
||||
resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
|
||||
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
url-template@^2.0.8:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
|
||||
@@ -33549,13 +33219,6 @@ vary@^1, vary@^1.1.2, vary@~1.1.2:
|
||||
resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
||||
|
||||
vasync@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz#d881379ff3685e4affa8e775cf0fd369262a201b"
|
||||
integrity sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==
|
||||
dependencies:
|
||||
verror "1.10.0"
|
||||
|
||||
vditor@^3.10.3:
|
||||
version "3.10.4"
|
||||
resolved "https://registry.npmmirror.com/vditor/-/vditor-3.10.4.tgz#df7e5cdf8c737b588152b2119942ff0e0904c9cd"
|
||||
@@ -33572,15 +33235,6 @@ verror@1.10.0:
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
verror@^1.10.1:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb"
|
||||
integrity sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
vfile-location@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmmirror.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0"
|
||||
@@ -33998,13 +33652,6 @@ which-typed-array@^1.1.16, which-typed-array@^1.1.19, which-typed-array@^1.1.2:
|
||||
gopd "^1.2.0"
|
||||
has-tostringtag "^1.0.2"
|
||||
|
||||
which@=2.0.2, which@^2.0.1, which@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
which@^1.2.12, which@^1.2.9, which@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.npmmirror.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
@@ -34012,6 +33659,13 @@ which@^1.2.12, which@^1.2.9, which@^1.3.1:
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
which@^2.0.1, which@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
which@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a"
|
||||
@@ -34543,7 +34197,7 @@ yargs@~3.10.0:
|
||||
decamelize "^1.0.0"
|
||||
window-size "0.1.0"
|
||||
|
||||
yauzl@=2.10.0, yauzl@^2.10.0, yauzl@^2.4.2:
|
||||
yauzl@^2.10.0, yauzl@^2.4.2:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
|
||||
@@ -34559,13 +34213,6 @@ yauzl@^3.1.3, yauzl@^3.2.0:
|
||||
buffer-crc32 "~0.2.3"
|
||||
pend "~1.2.0"
|
||||
|
||||
yazl@=2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
|
||||
integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
|
||||
ylru@^1.2.0:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.npmmirror.com/ylru/-/ylru-1.3.2.tgz#0de48017473275a4cbdfc83a1eaf67c01af8a785"
|
||||
|
||||
Reference in New Issue
Block a user