@aigne/default-memory 1.0.0
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/CHANGELOG.md +18 -0
- package/LICENSE.md +93 -0
- package/README.md +167 -0
- package/lib/cjs/default-memory-storage/index.d.ts +29 -0
- package/lib/cjs/default-memory-storage/index.js +95 -0
- package/lib/cjs/default-memory-storage/migrate.d.ts +3 -0
- package/lib/cjs/default-memory-storage/migrate.js +32 -0
- package/lib/cjs/default-memory-storage/migrations/001-init.d.ts +5 -0
- package/lib/cjs/default-memory-storage/migrations/001-init.js +22 -0
- package/lib/cjs/default-memory-storage/models/memory.d.ts +102 -0
- package/lib/cjs/default-memory-storage/models/memory.js +21 -0
- package/lib/cjs/index.d.ts +3 -0
- package/lib/cjs/index.js +19 -0
- package/lib/cjs/memory.d.ts +44 -0
- package/lib/cjs/memory.js +137 -0
- package/lib/cjs/package.json +3 -0
- package/lib/cjs/storage.d.ts +13 -0
- package/lib/cjs/storage.js +6 -0
- package/lib/dts/default-memory-storage/index.d.ts +29 -0
- package/lib/dts/default-memory-storage/migrate.d.ts +3 -0
- package/lib/dts/default-memory-storage/migrations/001-init.d.ts +5 -0
- package/lib/dts/default-memory-storage/models/memory.d.ts +102 -0
- package/lib/dts/index.d.ts +3 -0
- package/lib/dts/memory.d.ts +44 -0
- package/lib/dts/storage.d.ts +13 -0
- package/lib/esm/default-memory-storage/index.d.ts +29 -0
- package/lib/esm/default-memory-storage/index.js +91 -0
- package/lib/esm/default-memory-storage/migrate.d.ts +3 -0
- package/lib/esm/default-memory-storage/migrate.js +26 -0
- package/lib/esm/default-memory-storage/migrations/001-init.d.ts +5 -0
- package/lib/esm/default-memory-storage/migrations/001-init.js +20 -0
- package/lib/esm/default-memory-storage/models/memory.d.ts +102 -0
- package/lib/esm/default-memory-storage/models/memory.js +18 -0
- package/lib/esm/index.d.ts +3 -0
- package/lib/esm/index.js +3 -0
- package/lib/esm/memory.d.ts +44 -0
- package/lib/esm/memory.js +131 -0
- package/lib/esm/package.json +3 -0
- package/lib/esm/storage.d.ts +13 -0
- package/lib/esm/storage.js +2 -0
- package/package.json +50 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DefaultMemoryRecorder = exports.DefaultMemoryRetriever = exports.DefaultMemory = void 0;
|
|
4
|
+
const core_1 = require("@aigne/core");
|
|
5
|
+
const type_utils_js_1 = require("@aigne/core/utils/type-utils.js");
|
|
6
|
+
const index_js_1 = require("./default-memory-storage/index.js");
|
|
7
|
+
const storage_js_1 = require("./storage.js");
|
|
8
|
+
const DEFAULT_RETRIEVE_MEMORY_COUNT = 10;
|
|
9
|
+
class DefaultMemory extends core_1.MemoryAgent {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
const storage = options.storage instanceof storage_js_1.MemoryStorage
|
|
12
|
+
? options.storage
|
|
13
|
+
: new index_js_1.DefaultMemoryStorage(options.storage);
|
|
14
|
+
super({
|
|
15
|
+
...options,
|
|
16
|
+
recorder: options.recorder ?? new DefaultMemoryRecorder({ ...options, storage }),
|
|
17
|
+
retriever: options.retriever ??
|
|
18
|
+
new DefaultMemoryRetriever({
|
|
19
|
+
...options,
|
|
20
|
+
retrieveRecentMemoryCount: options.retrieveRecentMemoryCount ??
|
|
21
|
+
Math.ceil(options.retrieveMemoryCount ?? DEFAULT_RETRIEVE_MEMORY_COUNT) / 2,
|
|
22
|
+
storage,
|
|
23
|
+
}),
|
|
24
|
+
autoUpdate: options.autoUpdate ?? true,
|
|
25
|
+
});
|
|
26
|
+
this.storage = storage;
|
|
27
|
+
}
|
|
28
|
+
storage;
|
|
29
|
+
}
|
|
30
|
+
exports.DefaultMemory = DefaultMemory;
|
|
31
|
+
class DefaultMemoryRetriever extends core_1.MemoryRetriever {
|
|
32
|
+
constructor(options) {
|
|
33
|
+
super(options);
|
|
34
|
+
this.storage = options.storage;
|
|
35
|
+
this.retrieveMemoryCount = options.retrieveMemoryCount;
|
|
36
|
+
this.retrieveRecentMemoryCount = options.retrieveRecentMemoryCount;
|
|
37
|
+
this.inputKey = (0, type_utils_js_1.flat)(options.inputKey);
|
|
38
|
+
this.outputKey = (0, type_utils_js_1.flat)(options.outputKey);
|
|
39
|
+
if (options.getSearchPattern)
|
|
40
|
+
this.getSearchPattern = options.getSearchPattern;
|
|
41
|
+
if (options.formatMessage)
|
|
42
|
+
this.formatMessage = options.formatMessage;
|
|
43
|
+
if (options.formatMemory)
|
|
44
|
+
this.formatMemory = options.formatMemory;
|
|
45
|
+
}
|
|
46
|
+
storage;
|
|
47
|
+
retrieveMemoryCount;
|
|
48
|
+
retrieveRecentMemoryCount;
|
|
49
|
+
inputKey;
|
|
50
|
+
outputKey;
|
|
51
|
+
getSearchPattern = (search) => {
|
|
52
|
+
if (!search || typeof search === "string")
|
|
53
|
+
return search;
|
|
54
|
+
const obj = search && this.inputKey ? (0, type_utils_js_1.pick)(search, this.inputKey) : search;
|
|
55
|
+
return Object.values(obj)
|
|
56
|
+
.map((v) => (typeof v === "string" ? v : undefined))
|
|
57
|
+
.join("\n");
|
|
58
|
+
};
|
|
59
|
+
formatMessage = (content, key) => {
|
|
60
|
+
if (!(0, type_utils_js_1.isRecord)(content))
|
|
61
|
+
return content;
|
|
62
|
+
const obj = !key?.length ? content : (0, type_utils_js_1.pick)(content, key);
|
|
63
|
+
return Object.values(obj)
|
|
64
|
+
.map((v) => (typeof v === "string" ? v : undefined))
|
|
65
|
+
.join("\n");
|
|
66
|
+
};
|
|
67
|
+
formatMemory = (content) => {
|
|
68
|
+
if ((0, type_utils_js_1.isRecord)(content) && "input" in content && "output" in content) {
|
|
69
|
+
return {
|
|
70
|
+
input: this.formatMessage(content.input, this.inputKey),
|
|
71
|
+
output: this.formatMessage(content.output, this.outputKey),
|
|
72
|
+
source: content.source,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return content;
|
|
76
|
+
};
|
|
77
|
+
async process(input, options) {
|
|
78
|
+
const limit = input.limit ?? this.retrieveMemoryCount ?? DEFAULT_RETRIEVE_MEMORY_COUNT;
|
|
79
|
+
const search = this.getSearchPattern(input.search);
|
|
80
|
+
const recentLimit = this.retrieveRecentMemoryCount;
|
|
81
|
+
const [recent, related] = await Promise.all([
|
|
82
|
+
// Query latest messages
|
|
83
|
+
!recentLimit
|
|
84
|
+
? []
|
|
85
|
+
: this.storage
|
|
86
|
+
.search({ limit: recentLimit, orderBy: ["createdAt", "desc"] }, options)
|
|
87
|
+
.then(({ result }) => result.reverse()),
|
|
88
|
+
// Query related messages
|
|
89
|
+
!input.search
|
|
90
|
+
? []
|
|
91
|
+
: this.storage.search({ ...input, search, limit }, options).then(({ result }) => result),
|
|
92
|
+
]);
|
|
93
|
+
const recentSet = new Set(recent.map((i) => i.id));
|
|
94
|
+
const memories = related
|
|
95
|
+
// Filter out recent memories from related results
|
|
96
|
+
.filter((i) => !recentSet.has(i.id))
|
|
97
|
+
.concat(recent)
|
|
98
|
+
.slice(-limit);
|
|
99
|
+
return {
|
|
100
|
+
memories: memories.map((i) => ({
|
|
101
|
+
...i,
|
|
102
|
+
content: this.formatMemory(i.content),
|
|
103
|
+
})),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.DefaultMemoryRetriever = DefaultMemoryRetriever;
|
|
108
|
+
class DefaultMemoryRecorder extends core_1.MemoryRecorder {
|
|
109
|
+
constructor(options) {
|
|
110
|
+
super(options);
|
|
111
|
+
this.storage = options.storage;
|
|
112
|
+
this.inputKey = (0, type_utils_js_1.flat)(options.inputKey);
|
|
113
|
+
this.outputKey = (0, type_utils_js_1.flat)(options.outputKey);
|
|
114
|
+
}
|
|
115
|
+
storage;
|
|
116
|
+
inputKey;
|
|
117
|
+
outputKey;
|
|
118
|
+
async process(input, options) {
|
|
119
|
+
const newMemories = [];
|
|
120
|
+
for (const item of input.content) {
|
|
121
|
+
const { result } = await this.storage.create({
|
|
122
|
+
content: {
|
|
123
|
+
input: item.input && this.inputKey?.length ? (0, type_utils_js_1.pick)(item.input, this.inputKey) : item.input,
|
|
124
|
+
output: item.output && this.outputKey?.length
|
|
125
|
+
? (0, type_utils_js_1.pick)(item.output, this.outputKey)
|
|
126
|
+
: item.output,
|
|
127
|
+
source: item.source,
|
|
128
|
+
},
|
|
129
|
+
}, options);
|
|
130
|
+
newMemories.push(result);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
memories: newMemories,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
exports.DefaultMemoryRecorder = DefaultMemoryRecorder;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentInvokeOptions, Memory } from "@aigne/core";
|
|
2
|
+
export declare abstract class MemoryStorage {
|
|
3
|
+
abstract create(memory: Pick<Memory, "content">, options: AgentInvokeOptions): Promise<{
|
|
4
|
+
result: Memory;
|
|
5
|
+
}>;
|
|
6
|
+
abstract search(query: {
|
|
7
|
+
search?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
orderBy?: [string, "asc" | "desc"];
|
|
10
|
+
}, options: AgentInvokeOptions): Promise<{
|
|
11
|
+
result: Memory[];
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AgentInvokeOptions, Context, Memory } from "@aigne/core";
|
|
2
|
+
import type { PromiseOrValue } from "@aigne/core/utils/type-utils.js";
|
|
3
|
+
import type { SqliteRemoteDatabase } from "drizzle-orm/sqlite-proxy";
|
|
4
|
+
import { MemoryStorage } from "../storage.js";
|
|
5
|
+
import { Memories } from "./models/memory.js";
|
|
6
|
+
export interface DefaultMemoryStorageOptions {
|
|
7
|
+
url?: string;
|
|
8
|
+
getSessionId?: (context: Context) => PromiseOrValue<string>;
|
|
9
|
+
enableFTS?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class DefaultMemoryStorage extends MemoryStorage {
|
|
12
|
+
options?: DefaultMemoryStorageOptions | undefined;
|
|
13
|
+
constructor(options?: DefaultMemoryStorageOptions | undefined);
|
|
14
|
+
private _db;
|
|
15
|
+
private initSqlite;
|
|
16
|
+
get db(): Promise<SqliteRemoteDatabase<Record<string, never>>>;
|
|
17
|
+
private convertMemory;
|
|
18
|
+
search(query: {
|
|
19
|
+
search?: string;
|
|
20
|
+
limit?: number;
|
|
21
|
+
orderBy?: [keyof typeof Memories.$inferSelect, "asc" | "desc"];
|
|
22
|
+
}, { context }: AgentInvokeOptions): Promise<{
|
|
23
|
+
result: Memory[];
|
|
24
|
+
}>;
|
|
25
|
+
create(memory: Pick<Memory, "content">, { context }: AgentInvokeOptions): Promise<{
|
|
26
|
+
result: Memory;
|
|
27
|
+
}>;
|
|
28
|
+
protected segment(str: string): string[];
|
|
29
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export declare const Memories: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
2
|
+
name: "Memories";
|
|
3
|
+
schema: undefined;
|
|
4
|
+
columns: {
|
|
5
|
+
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
6
|
+
name: "id";
|
|
7
|
+
tableName: "Memories";
|
|
8
|
+
dataType: "string";
|
|
9
|
+
columnType: "SQLiteText";
|
|
10
|
+
data: string;
|
|
11
|
+
driverParam: string;
|
|
12
|
+
notNull: true;
|
|
13
|
+
hasDefault: true;
|
|
14
|
+
isPrimaryKey: true;
|
|
15
|
+
isAutoincrement: false;
|
|
16
|
+
hasRuntimeDefault: true;
|
|
17
|
+
enumValues: [string, ...string[]];
|
|
18
|
+
baseColumn: never;
|
|
19
|
+
identity: undefined;
|
|
20
|
+
generated: undefined;
|
|
21
|
+
}, {}, {
|
|
22
|
+
length: number | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
25
|
+
name: "createdAt";
|
|
26
|
+
tableName: "Memories";
|
|
27
|
+
dataType: "custom";
|
|
28
|
+
columnType: "SQLiteCustomColumn";
|
|
29
|
+
data: Date;
|
|
30
|
+
driverParam: string;
|
|
31
|
+
notNull: true;
|
|
32
|
+
hasDefault: true;
|
|
33
|
+
isPrimaryKey: false;
|
|
34
|
+
isAutoincrement: false;
|
|
35
|
+
hasRuntimeDefault: true;
|
|
36
|
+
enumValues: undefined;
|
|
37
|
+
baseColumn: never;
|
|
38
|
+
identity: undefined;
|
|
39
|
+
generated: undefined;
|
|
40
|
+
}, {}, {
|
|
41
|
+
sqliteColumnBuilderBrand: "SQLiteCustomColumnBuilderBrand";
|
|
42
|
+
}>;
|
|
43
|
+
updatedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
44
|
+
name: "updatedAt";
|
|
45
|
+
tableName: "Memories";
|
|
46
|
+
dataType: "custom";
|
|
47
|
+
columnType: "SQLiteCustomColumn";
|
|
48
|
+
data: Date;
|
|
49
|
+
driverParam: string;
|
|
50
|
+
notNull: true;
|
|
51
|
+
hasDefault: true;
|
|
52
|
+
isPrimaryKey: false;
|
|
53
|
+
isAutoincrement: false;
|
|
54
|
+
hasRuntimeDefault: true;
|
|
55
|
+
enumValues: undefined;
|
|
56
|
+
baseColumn: never;
|
|
57
|
+
identity: undefined;
|
|
58
|
+
generated: undefined;
|
|
59
|
+
}, {}, {
|
|
60
|
+
sqliteColumnBuilderBrand: "SQLiteCustomColumnBuilderBrand";
|
|
61
|
+
}>;
|
|
62
|
+
sessionId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
63
|
+
name: "sessionId";
|
|
64
|
+
tableName: "Memories";
|
|
65
|
+
dataType: "string";
|
|
66
|
+
columnType: "SQLiteText";
|
|
67
|
+
data: string;
|
|
68
|
+
driverParam: string;
|
|
69
|
+
notNull: false;
|
|
70
|
+
hasDefault: false;
|
|
71
|
+
isPrimaryKey: false;
|
|
72
|
+
isAutoincrement: false;
|
|
73
|
+
hasRuntimeDefault: false;
|
|
74
|
+
enumValues: [string, ...string[]];
|
|
75
|
+
baseColumn: never;
|
|
76
|
+
identity: undefined;
|
|
77
|
+
generated: undefined;
|
|
78
|
+
}, {}, {
|
|
79
|
+
length: number | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
content: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
82
|
+
name: "content";
|
|
83
|
+
tableName: "Memories";
|
|
84
|
+
dataType: "custom";
|
|
85
|
+
columnType: "SQLiteCustomColumn";
|
|
86
|
+
data: unknown;
|
|
87
|
+
driverParam: string;
|
|
88
|
+
notNull: true;
|
|
89
|
+
hasDefault: false;
|
|
90
|
+
isPrimaryKey: false;
|
|
91
|
+
isAutoincrement: false;
|
|
92
|
+
hasRuntimeDefault: false;
|
|
93
|
+
enumValues: undefined;
|
|
94
|
+
baseColumn: never;
|
|
95
|
+
identity: undefined;
|
|
96
|
+
generated: undefined;
|
|
97
|
+
}, {}, {
|
|
98
|
+
sqliteColumnBuilderBrand: "SQLiteCustomColumnBuilderBrand";
|
|
99
|
+
}>;
|
|
100
|
+
};
|
|
101
|
+
dialect: "sqlite";
|
|
102
|
+
}>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type AgentInvokeOptions, type AgentOptions, MemoryAgent, type MemoryAgentOptions, MemoryRecorder, type MemoryRecorderInput, type MemoryRecorderOutput, MemoryRetriever, type MemoryRetrieverInput, type MemoryRetrieverOutput } from "@aigne/core";
|
|
2
|
+
import { type DefaultMemoryStorageOptions } from "./default-memory-storage/index.js";
|
|
3
|
+
import { MemoryStorage } from "./storage.js";
|
|
4
|
+
export interface DefaultMemoryOptions extends Partial<MemoryAgentOptions>, Omit<DefaultMemoryRecorderOptions, "storage" | keyof AgentOptions>, Omit<DefaultMemoryRetrieverOptions, "storage" | keyof AgentOptions> {
|
|
5
|
+
storage?: MemoryStorage | DefaultMemoryStorageOptions;
|
|
6
|
+
}
|
|
7
|
+
export declare class DefaultMemory extends MemoryAgent {
|
|
8
|
+
constructor(options?: DefaultMemoryOptions);
|
|
9
|
+
storage: MemoryStorage;
|
|
10
|
+
}
|
|
11
|
+
export interface DefaultMemoryRetrieverOptions extends AgentOptions<MemoryRetrieverInput, MemoryRetrieverOutput> {
|
|
12
|
+
storage: MemoryStorage;
|
|
13
|
+
retrieveMemoryCount?: number;
|
|
14
|
+
retrieveRecentMemoryCount?: number;
|
|
15
|
+
inputKey?: string | string[];
|
|
16
|
+
outputKey?: string | string[];
|
|
17
|
+
getSearchPattern?: DefaultMemoryRetriever["getSearchPattern"];
|
|
18
|
+
formatMessage?: DefaultMemoryRetriever["formatMessage"];
|
|
19
|
+
formatMemory?: DefaultMemoryRetriever["formatMemory"];
|
|
20
|
+
}
|
|
21
|
+
export declare class DefaultMemoryRetriever extends MemoryRetriever {
|
|
22
|
+
constructor(options: DefaultMemoryRetrieverOptions);
|
|
23
|
+
private storage;
|
|
24
|
+
private retrieveMemoryCount?;
|
|
25
|
+
private retrieveRecentMemoryCount?;
|
|
26
|
+
private inputKey?;
|
|
27
|
+
private outputKey?;
|
|
28
|
+
private getSearchPattern;
|
|
29
|
+
private formatMessage;
|
|
30
|
+
private formatMemory;
|
|
31
|
+
process(input: MemoryRetrieverInput, options: AgentInvokeOptions): Promise<MemoryRetrieverOutput>;
|
|
32
|
+
}
|
|
33
|
+
export interface DefaultMemoryRecorderOptions extends AgentOptions<MemoryRecorderInput, MemoryRecorderOutput> {
|
|
34
|
+
storage: MemoryStorage;
|
|
35
|
+
inputKey?: string | string[];
|
|
36
|
+
outputKey?: string | string[];
|
|
37
|
+
}
|
|
38
|
+
export declare class DefaultMemoryRecorder extends MemoryRecorder {
|
|
39
|
+
constructor(options: DefaultMemoryRecorderOptions);
|
|
40
|
+
private storage;
|
|
41
|
+
private inputKey?;
|
|
42
|
+
private outputKey?;
|
|
43
|
+
process(input: MemoryRecorderInput, options: AgentInvokeOptions): Promise<MemoryRecorderOutput>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentInvokeOptions, Memory } from "@aigne/core";
|
|
2
|
+
export declare abstract class MemoryStorage {
|
|
3
|
+
abstract create(memory: Pick<Memory, "content">, options: AgentInvokeOptions): Promise<{
|
|
4
|
+
result: Memory;
|
|
5
|
+
}>;
|
|
6
|
+
abstract search(query: {
|
|
7
|
+
search?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
orderBy?: [string, "asc" | "desc"];
|
|
10
|
+
}, options: AgentInvokeOptions): Promise<{
|
|
11
|
+
result: Memory[];
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AgentInvokeOptions, Context, Memory } from "@aigne/core";
|
|
2
|
+
import type { PromiseOrValue } from "@aigne/core/utils/type-utils.js";
|
|
3
|
+
import type { SqliteRemoteDatabase } from "drizzle-orm/sqlite-proxy";
|
|
4
|
+
import { MemoryStorage } from "../storage.js";
|
|
5
|
+
import { Memories } from "./models/memory.js";
|
|
6
|
+
export interface DefaultMemoryStorageOptions {
|
|
7
|
+
url?: string;
|
|
8
|
+
getSessionId?: (context: Context) => PromiseOrValue<string>;
|
|
9
|
+
enableFTS?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class DefaultMemoryStorage extends MemoryStorage {
|
|
12
|
+
options?: DefaultMemoryStorageOptions | undefined;
|
|
13
|
+
constructor(options?: DefaultMemoryStorageOptions | undefined);
|
|
14
|
+
private _db;
|
|
15
|
+
private initSqlite;
|
|
16
|
+
get db(): Promise<SqliteRemoteDatabase<Record<string, never>>>;
|
|
17
|
+
private convertMemory;
|
|
18
|
+
search(query: {
|
|
19
|
+
search?: string;
|
|
20
|
+
limit?: number;
|
|
21
|
+
orderBy?: [keyof typeof Memories.$inferSelect, "asc" | "desc"];
|
|
22
|
+
}, { context }: AgentInvokeOptions): Promise<{
|
|
23
|
+
result: Memory[];
|
|
24
|
+
}>;
|
|
25
|
+
create(memory: Pick<Memory, "content">, { context }: AgentInvokeOptions): Promise<{
|
|
26
|
+
result: Memory;
|
|
27
|
+
}>;
|
|
28
|
+
protected segment(str: string): string[];
|
|
29
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { initDatabase } from "@aigne/sqlite";
|
|
2
|
+
import { asc, desc, eq, isNull, sql } from "drizzle-orm";
|
|
3
|
+
import { v7 } from "uuid";
|
|
4
|
+
import { stringify } from "yaml";
|
|
5
|
+
import { MemoryStorage } from "../storage.js";
|
|
6
|
+
import { migrate } from "./migrate.js";
|
|
7
|
+
import { Memories } from "./models/memory.js";
|
|
8
|
+
const DEFAULT_MAX_MEMORY_COUNT = 10;
|
|
9
|
+
export class DefaultMemoryStorage extends MemoryStorage {
|
|
10
|
+
options;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
super();
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
_db;
|
|
16
|
+
async initSqlite() {
|
|
17
|
+
const db = (await initDatabase({ url: this.options?.url }));
|
|
18
|
+
await migrate(db);
|
|
19
|
+
return db;
|
|
20
|
+
}
|
|
21
|
+
get db() {
|
|
22
|
+
this._db ??= this.initSqlite();
|
|
23
|
+
return this._db;
|
|
24
|
+
}
|
|
25
|
+
convertMemory(m) {
|
|
26
|
+
return {
|
|
27
|
+
id: m.id,
|
|
28
|
+
sessionId: m.sessionId,
|
|
29
|
+
content: m.content,
|
|
30
|
+
createdAt: m.createdAt.toISOString(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async search(query, { context }) {
|
|
34
|
+
const { limit = DEFAULT_MAX_MEMORY_COUNT } = query;
|
|
35
|
+
const sessionId = (await this.options?.getSessionId?.(context)) ?? null;
|
|
36
|
+
const db = await this.db;
|
|
37
|
+
const memories = this.options?.enableFTS && query.search
|
|
38
|
+
? await db
|
|
39
|
+
.select()
|
|
40
|
+
.from(Memories)
|
|
41
|
+
.innerJoin(sql `Memories_fts`, sql `Memories_fts.id = ${Memories.id}`)
|
|
42
|
+
.where(sql `Memories_fts MATCH ${sql.param(this.segment(query.search).join(" OR "))}`)
|
|
43
|
+
.orderBy(sql `bm25(Memories_fts)`)
|
|
44
|
+
.limit(limit)
|
|
45
|
+
.execute()
|
|
46
|
+
.then((rows) => rows.map((row) => row.Memories))
|
|
47
|
+
: await db
|
|
48
|
+
.select()
|
|
49
|
+
.from(Memories)
|
|
50
|
+
.where(sessionId ? eq(Memories.sessionId, sessionId) : isNull(Memories.sessionId))
|
|
51
|
+
.orderBy(query.orderBy
|
|
52
|
+
? (query.orderBy[1] === "asc" ? asc : desc)(sql.identifier(query.orderBy[0]))
|
|
53
|
+
: desc(Memories.id))
|
|
54
|
+
.limit(limit)
|
|
55
|
+
.execute();
|
|
56
|
+
return {
|
|
57
|
+
result: memories.map(this.convertMemory),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async create(memory, { context }) {
|
|
61
|
+
const sessionId = (await this.options?.getSessionId?.(context)) ?? null;
|
|
62
|
+
const db = await this.db;
|
|
63
|
+
const id = v7();
|
|
64
|
+
const [[result]] = await Promise.all([
|
|
65
|
+
db
|
|
66
|
+
.insert(Memories)
|
|
67
|
+
.values({
|
|
68
|
+
...memory,
|
|
69
|
+
id,
|
|
70
|
+
content: memory.content,
|
|
71
|
+
sessionId,
|
|
72
|
+
})
|
|
73
|
+
.returning()
|
|
74
|
+
.execute(),
|
|
75
|
+
this.options?.enableFTS &&
|
|
76
|
+
db.run(sql `\
|
|
77
|
+
insert into Memories_fts (id, content)
|
|
78
|
+
values (${sql.param(id)}, ${sql.param(this.segment(stringify(memory.content)).join(" "))})`),
|
|
79
|
+
]);
|
|
80
|
+
if (!result)
|
|
81
|
+
throw new Error("Failed to create memory");
|
|
82
|
+
return { result: this.convertMemory(result) };
|
|
83
|
+
}
|
|
84
|
+
segment(str) {
|
|
85
|
+
return (Array.from(new Intl.Segmenter(undefined, { granularity: "word" }).segment(str))
|
|
86
|
+
.map((i) => i.segment)
|
|
87
|
+
// Remove non-alphanumeric characters and trim whitespace
|
|
88
|
+
.map((i) => i.replace(/[^\p{L}\p{N}\s]/gu, "").trim())
|
|
89
|
+
.filter(Boolean));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm/sql";
|
|
2
|
+
import init from "./migrations/001-init.js";
|
|
3
|
+
export async function migrate(db) {
|
|
4
|
+
const migrations = [init];
|
|
5
|
+
const migrationsTable = "__drizzle_migrations";
|
|
6
|
+
const migrationTableCreate = sql `
|
|
7
|
+
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
|
|
8
|
+
id SERIAL PRIMARY KEY,
|
|
9
|
+
hash text NOT NULL
|
|
10
|
+
)
|
|
11
|
+
`;
|
|
12
|
+
await db.run(migrationTableCreate).execute();
|
|
13
|
+
const dbMigrations = await db
|
|
14
|
+
.values(sql `SELECT id, hash FROM ${sql.identifier(migrationsTable)} ORDER BY id DESC LIMIT 1`)
|
|
15
|
+
.execute();
|
|
16
|
+
const lastDbMigration = dbMigrations[0];
|
|
17
|
+
const queriesToRun = [];
|
|
18
|
+
for (const migration of migrations) {
|
|
19
|
+
if (!lastDbMigration || lastDbMigration[1] < migration.hash) {
|
|
20
|
+
queriesToRun.push(...migration.sql, `INSERT INTO \`${migrationsTable}\` ("hash") VALUES('${migration.hash}')`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
for (const query of queriesToRun) {
|
|
24
|
+
await db.run(query).execute();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
hash: "001-init",
|
|
3
|
+
sql: [
|
|
4
|
+
`\
|
|
5
|
+
CREATE TABLE "Memories" (
|
|
6
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
7
|
+
"createdAt" DATETIME NOT NULL,
|
|
8
|
+
"updatedAt" DATETIME NOT NULL,
|
|
9
|
+
"sessionId" TEXT,
|
|
10
|
+
"content" JSON NOT NULL
|
|
11
|
+
)
|
|
12
|
+
`,
|
|
13
|
+
`\
|
|
14
|
+
CREATE VIRTUAL TABLE "Memories_fts" USING fts5(
|
|
15
|
+
id UNINDEXED,
|
|
16
|
+
content
|
|
17
|
+
)
|
|
18
|
+
`,
|
|
19
|
+
],
|
|
20
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export declare const Memories: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
2
|
+
name: "Memories";
|
|
3
|
+
schema: undefined;
|
|
4
|
+
columns: {
|
|
5
|
+
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
6
|
+
name: "id";
|
|
7
|
+
tableName: "Memories";
|
|
8
|
+
dataType: "string";
|
|
9
|
+
columnType: "SQLiteText";
|
|
10
|
+
data: string;
|
|
11
|
+
driverParam: string;
|
|
12
|
+
notNull: true;
|
|
13
|
+
hasDefault: true;
|
|
14
|
+
isPrimaryKey: true;
|
|
15
|
+
isAutoincrement: false;
|
|
16
|
+
hasRuntimeDefault: true;
|
|
17
|
+
enumValues: [string, ...string[]];
|
|
18
|
+
baseColumn: never;
|
|
19
|
+
identity: undefined;
|
|
20
|
+
generated: undefined;
|
|
21
|
+
}, {}, {
|
|
22
|
+
length: number | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
25
|
+
name: "createdAt";
|
|
26
|
+
tableName: "Memories";
|
|
27
|
+
dataType: "custom";
|
|
28
|
+
columnType: "SQLiteCustomColumn";
|
|
29
|
+
data: Date;
|
|
30
|
+
driverParam: string;
|
|
31
|
+
notNull: true;
|
|
32
|
+
hasDefault: true;
|
|
33
|
+
isPrimaryKey: false;
|
|
34
|
+
isAutoincrement: false;
|
|
35
|
+
hasRuntimeDefault: true;
|
|
36
|
+
enumValues: undefined;
|
|
37
|
+
baseColumn: never;
|
|
38
|
+
identity: undefined;
|
|
39
|
+
generated: undefined;
|
|
40
|
+
}, {}, {
|
|
41
|
+
sqliteColumnBuilderBrand: "SQLiteCustomColumnBuilderBrand";
|
|
42
|
+
}>;
|
|
43
|
+
updatedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
44
|
+
name: "updatedAt";
|
|
45
|
+
tableName: "Memories";
|
|
46
|
+
dataType: "custom";
|
|
47
|
+
columnType: "SQLiteCustomColumn";
|
|
48
|
+
data: Date;
|
|
49
|
+
driverParam: string;
|
|
50
|
+
notNull: true;
|
|
51
|
+
hasDefault: true;
|
|
52
|
+
isPrimaryKey: false;
|
|
53
|
+
isAutoincrement: false;
|
|
54
|
+
hasRuntimeDefault: true;
|
|
55
|
+
enumValues: undefined;
|
|
56
|
+
baseColumn: never;
|
|
57
|
+
identity: undefined;
|
|
58
|
+
generated: undefined;
|
|
59
|
+
}, {}, {
|
|
60
|
+
sqliteColumnBuilderBrand: "SQLiteCustomColumnBuilderBrand";
|
|
61
|
+
}>;
|
|
62
|
+
sessionId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
63
|
+
name: "sessionId";
|
|
64
|
+
tableName: "Memories";
|
|
65
|
+
dataType: "string";
|
|
66
|
+
columnType: "SQLiteText";
|
|
67
|
+
data: string;
|
|
68
|
+
driverParam: string;
|
|
69
|
+
notNull: false;
|
|
70
|
+
hasDefault: false;
|
|
71
|
+
isPrimaryKey: false;
|
|
72
|
+
isAutoincrement: false;
|
|
73
|
+
hasRuntimeDefault: false;
|
|
74
|
+
enumValues: [string, ...string[]];
|
|
75
|
+
baseColumn: never;
|
|
76
|
+
identity: undefined;
|
|
77
|
+
generated: undefined;
|
|
78
|
+
}, {}, {
|
|
79
|
+
length: number | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
content: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
82
|
+
name: "content";
|
|
83
|
+
tableName: "Memories";
|
|
84
|
+
dataType: "custom";
|
|
85
|
+
columnType: "SQLiteCustomColumn";
|
|
86
|
+
data: unknown;
|
|
87
|
+
driverParam: string;
|
|
88
|
+
notNull: true;
|
|
89
|
+
hasDefault: false;
|
|
90
|
+
isPrimaryKey: false;
|
|
91
|
+
isAutoincrement: false;
|
|
92
|
+
hasRuntimeDefault: false;
|
|
93
|
+
enumValues: undefined;
|
|
94
|
+
baseColumn: never;
|
|
95
|
+
identity: undefined;
|
|
96
|
+
generated: undefined;
|
|
97
|
+
}, {}, {
|
|
98
|
+
sqliteColumnBuilderBrand: "SQLiteCustomColumnBuilderBrand";
|
|
99
|
+
}>;
|
|
100
|
+
};
|
|
101
|
+
dialect: "sqlite";
|
|
102
|
+
}>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { datetime, json } from "@aigne/sqlite/type.js";
|
|
2
|
+
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
3
|
+
import { v7 } from "uuid";
|
|
4
|
+
export const Memories = sqliteTable("Memories", {
|
|
5
|
+
id: text("id")
|
|
6
|
+
.notNull()
|
|
7
|
+
.primaryKey()
|
|
8
|
+
.$defaultFn(() => v7()),
|
|
9
|
+
createdAt: datetime("createdAt")
|
|
10
|
+
.notNull()
|
|
11
|
+
.$defaultFn(() => new Date()),
|
|
12
|
+
updatedAt: datetime("updatedAt")
|
|
13
|
+
.notNull()
|
|
14
|
+
.$defaultFn(() => new Date())
|
|
15
|
+
.$onUpdateFn(() => new Date()),
|
|
16
|
+
sessionId: text("sessionId"),
|
|
17
|
+
content: json("content").notNull(),
|
|
18
|
+
});
|