@elizaos/plugin-sql 1.0.0-beta.44 → 1.0.0-beta.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-EYE2J2N6.js +346 -0
- package/dist/{chunk-K7ZVMPCJ.js.map → chunk-EYE2J2N6.js.map} +1 -1
- package/dist/index.js +2483 -10
- package/dist/index.js.map +1 -1
- package/dist/migrate.js +54 -1
- package/dist/migrate.js.map +1 -1
- package/package.json +15 -6
- package/dist/chunk-K7ZVMPCJ.js +0 -2
- package/dist/index.d.ts +0 -30
- package/dist/migrate.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -1,27 +1,1223 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
PGliteClientManager,
|
|
3
|
+
PostgresConnectionManager,
|
|
4
|
+
__name
|
|
5
|
+
} from "./chunk-EYE2J2N6.js";
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
import * as os from "node:os";
|
|
9
|
+
import { logger as logger4 } from "@elizaos/core";
|
|
10
|
+
|
|
11
|
+
// src/pglite/adapter.ts
|
|
12
|
+
import { logger as logger2 } from "@elizaos/core";
|
|
13
|
+
import { drizzle } from "drizzle-orm/pglite";
|
|
14
|
+
|
|
15
|
+
// src/base.ts
|
|
16
|
+
import {
|
|
17
|
+
DatabaseAdapter,
|
|
18
|
+
logger,
|
|
19
|
+
stringToUuid
|
|
20
|
+
} from "@elizaos/core";
|
|
21
|
+
import {
|
|
22
|
+
and,
|
|
23
|
+
cosineDistance,
|
|
24
|
+
count,
|
|
25
|
+
desc,
|
|
26
|
+
eq,
|
|
27
|
+
gte,
|
|
28
|
+
inArray,
|
|
29
|
+
lte,
|
|
30
|
+
or,
|
|
31
|
+
sql as sql12,
|
|
32
|
+
not
|
|
33
|
+
} from "drizzle-orm";
|
|
34
|
+
import { v4 } from "uuid";
|
|
35
|
+
|
|
36
|
+
// src/schema/embedding.ts
|
|
37
|
+
import { sql as sql6 } from "drizzle-orm";
|
|
38
|
+
import { check as check2, foreignKey as foreignKey2, index as index2, pgTable as pgTable6, uuid as uuid6, vector as vector2 } from "drizzle-orm/pg-core";
|
|
39
|
+
import { VECTOR_DIMS } from "@elizaos/core";
|
|
40
|
+
|
|
41
|
+
// src/schema/memory.ts
|
|
42
|
+
import { relations, sql as sql5 } from "drizzle-orm";
|
|
43
|
+
import {
|
|
44
|
+
boolean as boolean2,
|
|
45
|
+
check,
|
|
46
|
+
foreignKey,
|
|
47
|
+
index,
|
|
48
|
+
jsonb as jsonb5,
|
|
49
|
+
pgTable as pgTable5,
|
|
50
|
+
text as text5,
|
|
51
|
+
uuid as uuid5
|
|
52
|
+
} from "drizzle-orm/pg-core";
|
|
53
|
+
|
|
54
|
+
// src/schema/agent.ts
|
|
55
|
+
import { sql } from "drizzle-orm";
|
|
56
|
+
import { boolean, jsonb, pgTable, text, unique, uuid } from "drizzle-orm/pg-core";
|
|
57
|
+
|
|
58
|
+
// src/schema/types.ts
|
|
59
|
+
import { customType } from "drizzle-orm/pg-core";
|
|
60
|
+
var stringJsonb = customType({
|
|
61
|
+
dataType() {
|
|
62
|
+
return "jsonb";
|
|
63
|
+
},
|
|
64
|
+
toDriver(value) {
|
|
65
|
+
return JSON.stringify(value);
|
|
66
|
+
},
|
|
67
|
+
fromDriver(value) {
|
|
68
|
+
return JSON.stringify(value);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
var numberTimestamp = customType({
|
|
72
|
+
dataType() {
|
|
73
|
+
return "timestamptz";
|
|
74
|
+
},
|
|
75
|
+
toDriver(value) {
|
|
76
|
+
return new Date(value).toISOString();
|
|
77
|
+
},
|
|
78
|
+
fromDriver(value) {
|
|
79
|
+
return new Date(value).getTime();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// src/schema/agent.ts
|
|
84
|
+
var agentTable = pgTable(
|
|
85
|
+
"agents",
|
|
86
|
+
{
|
|
87
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
88
|
+
enabled: boolean("enabled").default(true).notNull(),
|
|
89
|
+
createdAt: numberTimestamp("createdAt").default(sql`now()`).notNull(),
|
|
90
|
+
updatedAt: numberTimestamp("updatedAt").default(sql`now()`).notNull(),
|
|
91
|
+
// Character
|
|
92
|
+
name: text("name"),
|
|
93
|
+
username: text("username"),
|
|
94
|
+
system: text("system"),
|
|
95
|
+
bio: jsonb("bio").$type().notNull(),
|
|
96
|
+
messageExamples: jsonb("message_examples").$type().default(sql`'[]'::jsonb`),
|
|
97
|
+
postExamples: jsonb("post_examples").$type().default(sql`'[]'::jsonb`),
|
|
98
|
+
topics: jsonb("topics").$type().default(sql`'[]'::jsonb`),
|
|
99
|
+
adjectives: jsonb("adjectives").$type().default(sql`'[]'::jsonb`),
|
|
100
|
+
knowledge: jsonb("knowledge").$type().default(sql`'[]'::jsonb`),
|
|
101
|
+
plugins: jsonb("plugins").$type().default(sql`'[]'::jsonb`),
|
|
102
|
+
settings: jsonb("settings").$type().default(sql`'{}'::jsonb`),
|
|
103
|
+
style: jsonb("style").$type().default(sql`'{}'::jsonb`)
|
|
104
|
+
},
|
|
105
|
+
(table) => {
|
|
106
|
+
return {
|
|
107
|
+
nameUnique: unique("name_unique").on(table.name)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// src/schema/entity.ts
|
|
113
|
+
import { sql as sql2 } from "drizzle-orm";
|
|
114
|
+
import { jsonb as jsonb2, pgTable as pgTable2, text as text2, unique as unique2, uuid as uuid2 } from "drizzle-orm/pg-core";
|
|
115
|
+
var entityTable = pgTable2(
|
|
116
|
+
"entities",
|
|
117
|
+
{
|
|
118
|
+
id: uuid2("id").notNull().primaryKey(),
|
|
119
|
+
agentId: uuid2("agentId").notNull().references(() => agentTable.id, {
|
|
120
|
+
onDelete: "cascade"
|
|
121
|
+
}),
|
|
122
|
+
createdAt: numberTimestamp("createdAt").default(sql2`now()`).notNull(),
|
|
123
|
+
names: text2("names").array().default(sql2`'{}'::text[]`),
|
|
124
|
+
metadata: jsonb2("metadata").default(sql2`'{}'::jsonb`)
|
|
125
|
+
},
|
|
126
|
+
(table) => {
|
|
127
|
+
return {
|
|
128
|
+
idAgentIdUnique: unique2("id_agent_id_unique").on(table.id, table.agentId)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// src/schema/room.ts
|
|
134
|
+
import { sql as sql4 } from "drizzle-orm";
|
|
135
|
+
import { jsonb as jsonb4, pgTable as pgTable4, text as text4, uuid as uuid4 } from "drizzle-orm/pg-core";
|
|
136
|
+
|
|
137
|
+
// src/schema/world.ts
|
|
138
|
+
import { sql as sql3 } from "drizzle-orm";
|
|
139
|
+
import { jsonb as jsonb3, pgTable as pgTable3, text as text3, uuid as uuid3 } from "drizzle-orm/pg-core";
|
|
140
|
+
var worldTable = pgTable3("worlds", {
|
|
141
|
+
id: uuid3("id").notNull().primaryKey().default(sql3`gen_random_uuid()`),
|
|
142
|
+
agentId: uuid3("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
143
|
+
name: text3("name").notNull(),
|
|
144
|
+
metadata: jsonb3("metadata"),
|
|
145
|
+
serverId: text3("serverId").notNull(),
|
|
146
|
+
createdAt: numberTimestamp("createdAt").default(sql3`now()`).notNull()
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// src/schema/room.ts
|
|
150
|
+
var roomTable = pgTable4("rooms", {
|
|
151
|
+
id: uuid4("id").notNull().primaryKey().default(sql4`gen_random_uuid()`),
|
|
152
|
+
agentId: uuid4("agentId").references(() => agentTable.id, {
|
|
153
|
+
onDelete: "cascade"
|
|
154
|
+
}),
|
|
155
|
+
source: text4("source").notNull(),
|
|
156
|
+
type: text4("type").notNull(),
|
|
157
|
+
serverId: text4("serverId"),
|
|
158
|
+
worldId: uuid4("worldId").references(() => worldTable.id, {
|
|
159
|
+
onDelete: "cascade"
|
|
160
|
+
}),
|
|
161
|
+
name: text4("name"),
|
|
162
|
+
metadata: jsonb4("metadata"),
|
|
163
|
+
channelId: text4("channelId"),
|
|
164
|
+
createdAt: numberTimestamp("createdAt").default(sql4`now()`).notNull()
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// src/schema/memory.ts
|
|
168
|
+
var memoryTable = pgTable5(
|
|
169
|
+
"memories",
|
|
170
|
+
{
|
|
171
|
+
id: uuid5("id").primaryKey().notNull(),
|
|
172
|
+
type: text5("type").notNull(),
|
|
173
|
+
createdAt: numberTimestamp("createdAt").default(sql5`now()`).notNull(),
|
|
174
|
+
content: jsonb5("content").notNull(),
|
|
175
|
+
entityId: uuid5("entityId").references(() => entityTable.id, {
|
|
176
|
+
onDelete: "cascade"
|
|
177
|
+
}),
|
|
178
|
+
agentId: uuid5("agentId").references(() => agentTable.id, {
|
|
179
|
+
onDelete: "cascade"
|
|
180
|
+
}),
|
|
181
|
+
roomId: uuid5("roomId").references(() => roomTable.id, {
|
|
182
|
+
onDelete: "cascade"
|
|
183
|
+
}),
|
|
184
|
+
worldId: uuid5("worldId").references(() => worldTable.id, {
|
|
185
|
+
onDelete: "set null"
|
|
186
|
+
}),
|
|
187
|
+
unique: boolean2("unique").default(true).notNull(),
|
|
188
|
+
metadata: jsonb5("metadata").default({}).notNull()
|
|
189
|
+
},
|
|
190
|
+
(table) => [
|
|
191
|
+
index("idx_memories_type_room").on(table.type, table.roomId),
|
|
192
|
+
index("idx_memories_world_id").on(table.worldId),
|
|
193
|
+
foreignKey({
|
|
194
|
+
name: "fk_room",
|
|
195
|
+
columns: [table.roomId],
|
|
196
|
+
foreignColumns: [roomTable.id]
|
|
197
|
+
}).onDelete("cascade"),
|
|
198
|
+
foreignKey({
|
|
199
|
+
name: "fk_user",
|
|
200
|
+
columns: [table.entityId],
|
|
201
|
+
foreignColumns: [entityTable.id]
|
|
202
|
+
}).onDelete("cascade"),
|
|
203
|
+
foreignKey({
|
|
204
|
+
name: "fk_agent",
|
|
205
|
+
columns: [table.agentId],
|
|
206
|
+
foreignColumns: [agentTable.id]
|
|
207
|
+
}).onDelete("cascade"),
|
|
208
|
+
foreignKey({
|
|
209
|
+
name: "fk_world",
|
|
210
|
+
columns: [table.worldId],
|
|
211
|
+
foreignColumns: [worldTable.id]
|
|
212
|
+
}).onDelete("set null"),
|
|
213
|
+
index("idx_memories_metadata_type").on(sql5`((metadata->>'type'))`),
|
|
214
|
+
index("idx_memories_document_id").on(sql5`((metadata->>'documentId'))`),
|
|
215
|
+
index("idx_fragments_order").on(
|
|
216
|
+
sql5`((metadata->>'documentId'))`,
|
|
217
|
+
sql5`((metadata->>'position'))`
|
|
218
|
+
),
|
|
219
|
+
check(
|
|
220
|
+
"fragment_metadata_check",
|
|
221
|
+
sql5`
|
|
2
222
|
CASE
|
|
3
223
|
WHEN metadata->>'type' = 'fragment' THEN
|
|
4
224
|
metadata ? 'documentId' AND
|
|
5
225
|
metadata ? 'position'
|
|
6
226
|
ELSE true
|
|
7
227
|
END
|
|
8
|
-
`
|
|
228
|
+
`
|
|
229
|
+
),
|
|
230
|
+
check(
|
|
231
|
+
"document_metadata_check",
|
|
232
|
+
sql5`
|
|
9
233
|
CASE
|
|
10
234
|
WHEN metadata->>'type' = 'document' THEN
|
|
11
235
|
metadata ? 'timestamp'
|
|
12
236
|
ELSE true
|
|
13
237
|
END
|
|
14
|
-
`)]),br=Ye(a,({one:m})=>({embedding:m(l)}));var L={[v.SMALL]:"dim384",[v.MEDIUM]:"dim512",[v.LARGE]:"dim768",[v.XL]:"dim1024",[v.XXL]:"dim1536",[v.XXXL]:"dim3072"},l=at("embeddings",{id:Te("id").primaryKey().defaultRandom().notNull(),memoryId:Te("memory_id").references(()=>a.id),createdAt:T("created_at").default(Ee`now()`).notNull(),dim384:F("dim_384",{dimensions:v.SMALL}),dim512:F("dim_512",{dimensions:v.MEDIUM}),dim768:F("dim_768",{dimensions:v.LARGE}),dim1024:F("dim_1024",{dimensions:v.XL}),dim1536:F("dim_1536",{dimensions:v.XXL}),dim3072:F("dim_3072",{dimensions:v.XXXL})},m=>[rt("embedding_source_check",Ee`"memory_id" IS NOT NULL`),it("idx_embedding_memory").on(m.memoryId),nt({name:"fk_embedding_memory",columns:[m.memoryId],foreignColumns:[a.id]}).onDelete("cascade")]);import{sql as Ae}from"drizzle-orm";import{jsonb as ot,pgTable as st,text as dt,unique as mt,uuid as _e}from"drizzle-orm/pg-core";var S=st("cache",{id:_e("id").notNull().primaryKey().default(Ae`gen_random_uuid()`),key:dt("key").notNull(),agentId:_e("agentId").notNull().references(()=>b.id,{onDelete:"cascade"}),value:ot("value").notNull(),createdAt:T("createdAt").default(Ae`now()`).notNull(),expiresAt:T("expiresAt")},m=>[mt("cache_key_agent_unique").on(m.key,m.agentId)]);import{sql as Pe}from"drizzle-orm";import{jsonb as ct,pgTable as lt,text as ut,uuid as j}from"drizzle-orm/pg-core";var f=lt("components",{id:j("id").primaryKey().defaultRandom(),entityId:j("entityId").notNull().references(()=>h.id,{onDelete:"cascade"}),agentId:j("agentId").notNull().references(()=>b.id,{onDelete:"cascade"}),roomId:j("roomId").notNull().references(()=>c.id,{onDelete:"cascade"}),worldId:j("worldId").references(()=>E.id,{onDelete:"cascade"}),sourceEntityId:j("sourceEntityId").references(()=>h.id,{onDelete:"cascade"}),type:ut("type").notNull(),data:ct("data").default(Pe`'{}'::jsonb`),createdAt:T("createdAt").default(Pe`now()`).notNull()});import{sql as gt}from"drizzle-orm";import{foreignKey as Ne,jsonb as yt,pgTable as ht,text as It,uuid as oe}from"drizzle-orm/pg-core";var N=ht("logs",{id:oe("id").defaultRandom().notNull(),createdAt:T("createdAt").default(gt`now()`).notNull(),entityId:oe("entityId").notNull().references(()=>h.id),body:yt("body").notNull(),type:It("type").notNull(),roomId:oe("roomId").notNull().references(()=>c.id)},m=>[Ne({name:"fk_room",columns:[m.roomId],foreignColumns:[c.id]}).onDelete("cascade"),Ne({name:"fk_user",columns:[m.entityId],foreignColumns:[h.id]}).onDelete("cascade")]);import{sql as Me}from"drizzle-orm";import{foreignKey as Se,index as ve,pgTable as bt,text as ft,uuid as Z}from"drizzle-orm/pg-core";var u=bt("participants",{id:Z("id").notNull().primaryKey().default(Me`gen_random_uuid()`),createdAt:T("createdAt").default(Me`now()`).notNull(),entityId:Z("entityId").references(()=>h.id,{onDelete:"cascade"}),roomId:Z("roomId").references(()=>c.id,{onDelete:"cascade"}),agentId:Z("agentId").references(()=>b.id,{onDelete:"cascade"}),roomState:ft("roomState")},m=>[ve("idx_participants_user").on(m.entityId),ve("idx_participants_room").on(m.roomId),Se({name:"fk_room",columns:[m.roomId],foreignColumns:[c.id]}).onDelete("cascade"),Se({name:"fk_user",columns:[m.entityId],foreignColumns:[h.id]}).onDelete("cascade")]);import{sql as qe}from"drizzle-orm";import{foreignKey as Be,index as pt,jsonb as wt,pgTable as Dt,text as Ut,unique as Et,uuid as Y}from"drizzle-orm/pg-core";var _=Dt("relationships",{id:Y("id").notNull().primaryKey().default(qe`gen_random_uuid()`),createdAt:T("createdAt").default(qe`now()`).notNull(),sourceEntityId:Y("sourceEntityId").notNull().references(()=>h.id,{onDelete:"cascade"}),targetEntityId:Y("targetEntityId").notNull().references(()=>h.id,{onDelete:"cascade"}),agentId:Y("agentId").notNull().references(()=>b.id,{onDelete:"cascade"}),tags:Ut("tags").array(),metadata:wt("metadata")},m=>[pt("idx_relationships_users").on(m.sourceEntityId,m.targetEntityId),Et("unique_relationship").on(m.sourceEntityId,m.targetEntityId,m.agentId),Be({name:"fk_user_a",columns:[m.sourceEntityId],foreignColumns:[h.id]}).onDelete("cascade"),Be({name:"fk_user_b",columns:[m.targetEntityId],foreignColumns:[h.id]}).onDelete("cascade")]);import{jsonb as Tt,pgTable as At,text as se,timestamp as Re,uuid as Q}from"drizzle-orm/pg-core";var P=At("tasks",{id:Q("id").primaryKey().defaultRandom(),name:se("name").notNull(),description:se("description").notNull(),roomId:Q("room_id"),worldId:Q("world_id"),agentId:Q("agent_id").notNull(),tags:se("tags").array(),metadata:Tt("metadata"),createdAt:Re("created_at").defaultNow(),updatedAt:Re("updated_at").defaultNow()});var de=class de extends _t{constructor(e){super();this.maxRetries=3;this.baseDelay=1e3;this.maxDelay=1e4;this.jitterMax=1e3;this.embeddingDimension=L[384];this.agentId=e}async withRetry(e){let t=new Error("Unknown error");for(let r=1;r<=this.maxRetries;r++)try{return await e()}catch(i){if(t=i,r<this.maxRetries){let s=Math.min(this.baseDelay*2**(r-1),this.maxDelay),d=Math.random()*this.jitterMax,y=s+d;o.warn(`Database operation failed (attempt ${r}/${this.maxRetries}):`,{error:i instanceof Error?i.message:String(i),nextRetryIn:`${(y/1e3).toFixed(1)}s`}),await new Promise(I=>setTimeout(I,y))}else throw o.error("Max retry attempts reached:",{error:i instanceof Error?i.message:String(i),totalAttempts:r}),i instanceof Error?i:new Error(String(i))}throw t}async ensureAgentExists(e){if(!e.name)throw new Error("Agent name is required");let r=(await this.getAgents()).find(i=>i.name===e.name);return r||(e.id=Pt(e.name??O()),await this.createAgent(e),e)}async ensureEmbeddingDimension(e){let t=await this.db.select({embedding:l}).from(a).innerJoin(l,n(l.memoryId,a.id)).where(n(a.agentId,this.agentId)).limit(1);if(t.length>0){let r=Object.entries(L).find(([i,s])=>t[0].embedding[s]!==null)}this.embeddingDimension=L[e]}async getAgent(e){return this.withDatabase(async()=>{let t=await this.db.select().from(b).where(n(b.id,e)).limit(1);return t.length===0?null:t[0]})}async getAgents(){return this.withDatabase(async()=>await this.db.select().from(b))}async createAgent(e){return this.withDatabase(async()=>{try{return await this.db.transaction(async t=>{await t.insert(b).values({...e})}),o.debug("Agent created successfully:",{agentId:e.id}),!0}catch(t){return o.error("Error creating agent:",{error:t instanceof Error?t.message:String(t),agentId:e.id,agent:e}),!1}})}async updateAgent(e,t){return this.withDatabase(async()=>{try{if(!e)throw new Error("Agent ID is required for update");return await this.db.transaction(async r=>{t?.settings&&(t.settings=await this.mergeAgentSettings(r,e,t.settings)),await r.update(b).set({...t,updatedAt:Date.now()}).where(n(b.id,e))}),o.debug("Agent updated successfully:",{agentId:e}),!0}catch(r){return o.error("Error updating agent:",{error:r instanceof Error?r.message:String(r),agentId:e,agent:t}),!1}})}async mergeAgentSettings(e,t,r){let i=await e.select({settings:b.settings}).from(b).where(n(b.id,t)).limit(1);if(i.length===0||!i[0].settings)return r;let s=i[0].settings;if(r.secrets){let d=s.secrets||{},y=r.secrets,I={...d};for(let[R,A]of Object.entries(y))A===null?delete I[R]:I[R]=A;r.secrets=I}return{...s,...r}}async deleteAgent(e){return o.debug(`[DB] Starting deletion of agent with ID: ${e}`),this.withDatabase(async()=>{try{return o.debug(`[DB] Beginning database transaction for deleting agent: ${e}`),await new Promise((r,i)=>{let s=setTimeout(()=>{o.error(`[DB] Transaction timeout reached for agent deletion: ${e}`),i(new Error("Database transaction timeout"))},3e4);this.db.transaction(async d=>{try{o.debug(`[DB] Fetching entities for agent: ${e}`);let I=(await d.select({entityId:h.id}).from(h).where(n(h.agentId,e))).map(g=>g.entityId);o.debug(`[DB] Found ${I.length} entities to delete for agent ${e}`),o.debug(`[DB] Fetching rooms for agent: ${e}`);let A=(await d.select({roomId:c.id}).from(c).where(n(c.agentId,e))).map(g=>g.roomId);if(o.debug(`[DB] Found ${A.length} rooms for agent ${e}`),o.debug("[DB] Explicitly deleting ALL logs with matching entityIds and roomIds"),I.length>0){o.debug(`[DB] Deleting logs for ${I.length} entities (first batch)`);let g=50;for(let p=0;p<I.length;p+=g){let q=I.slice(p,p+g);o.debug(`[DB] Processing entity logs batch ${p/g+1} with ${q.length} entities`),await d.delete(N).where(D(N.entityId,q))}o.debug("[DB] Entity logs deletion completed successfully")}if(A.length>0){o.debug(`[DB] Deleting logs for ${A.length} rooms (first batch)`);let g=50;for(let p=0;p<A.length;p+=g){let q=A.slice(p,p+g);o.debug(`[DB] Processing room logs batch ${p/g+1} with ${q.length} rooms`),await d.delete(N).where(D(N.roomId,q))}o.debug("[DB] Room logs deletion completed successfully")}let U=[];I.length>0&&(o.debug("[DB] Finding memories belonging to entities"),U=(await d.select({id:a.id}).from(a).where(D(a.entityId,I))).map(p=>p.id),o.debug(`[DB] Found ${U.length} memories belonging to entities`)),o.debug("[DB] Finding memories belonging to agent directly");let X=await d.select({id:a.id}).from(a).where(n(a.agentId,e));if(U=[...U,...X.map(g=>g.id)],o.debug(`[DB] Found total of ${U.length} memories to delete`),A.length>0){o.debug("[DB] Finding memories belonging to rooms");let g=await d.select({id:a.id}).from(a).where(D(a.roomId,A));U=[...U,...g.map(p=>p.id)],o.debug(`[DB] Updated total to ${U.length} memories to delete`)}if(U.length>0){o.debug(`[DB] Deleting embeddings for ${U.length} memories`);let g=100;for(let p=0;p<U.length;p+=g){let q=U.slice(p,p+g);await d.delete(l).where(D(l.memoryId,q))}o.debug("[DB] Embeddings deleted successfully")}if(U.length>0){o.debug(`[DB] Deleting ${U.length} memories`);let g=100;for(let p=0;p<U.length;p+=g){let q=U.slice(p,p+g);await d.delete(a).where(D(a.id,q))}o.debug("[DB] Memories deleted successfully")}I.length>0&&(o.debug("[DB] Deleting components for entities"),await d.delete(f).where(D(f.entityId,I)),o.debug("[DB] Components deleted successfully")),I.length>0&&(o.debug("[DB] Deleting source entity references in components"),await d.delete(f).where(D(f.sourceEntityId,I)),o.debug("[DB] Source entity references deleted successfully")),A.length>0&&(o.debug("[DB] Deleting participations for rooms"),await d.delete(u).where(D(u.roomId,A)),o.debug("[DB] Participations deleted for rooms")),o.debug("[DB] Deleting agent participations"),await d.delete(u).where(n(u.agentId,e)),o.debug("[DB] Agent participations deleted"),A.length>0&&(o.debug("[DB] Deleting rooms"),await d.delete(c).where(D(c.id,A)),o.debug("[DB] Rooms deleted successfully")),o.debug("[DB] Deleting cache entries"),await d.delete(S).where(n(S.agentId,e)),o.debug("[DB] Cache entries deleted successfully"),o.debug("[DB] Deleting relationships"),I.length>0&&(await d.delete(_).where(D(_.sourceEntityId,I)),await d.delete(_).where(D(_.targetEntityId,I))),await d.delete(_).where(n(_.agentId,e)),o.debug("[DB] Relationships deleted successfully"),I.length>0&&(o.debug("[DB] Deleting entities"),await d.delete(h).where(n(h.agentId,e)),o.debug("[DB] Entities deleted successfully")),o.debug("[DB] Checking for world references");let re=await d.select({id:E.id}).from(E).where(n(E.agentId,e));if(re.length>0){let g=await d.select({newAgentId:b.id}).from(b).where(qt(n(b.id,e))).limit(1);if(g.length>0)await d.update(E).set({agentId:g[0].newAgentId}).where(n(E.agentId,e));else{let p=re.map(q=>q.id);o.debug(`[DB] Found ${p.length} worlds to delete`),await d.delete(E).where(D(E.id,p)),o.debug("[DB] Worlds deleted successfully")}}else o.debug("[DB] No worlds found for this agent");o.debug(`[DB] Deleting agent ${e}`),await d.delete(b).where(n(b.id,e)),o.debug("[DB] Agent deleted successfully"),r(!0)}catch(y){o.error("[DB] Error in transaction:",y),i(y)}}).catch(d=>{clearTimeout(s),i(d)})}),o.success(`[DB] Agent ${e} successfully deleted`),!0}catch(t){throw o.error(`[DB] Error in database transaction for agent deletion ${e}:`,t),t instanceof Error&&(o.error(`[DB] Error name: ${t.name}, message: ${t.message}`),o.error(`[DB] Error stack: ${t.stack}`)),t}})}async countAgents(){return this.withDatabase(async()=>{try{return(await this.db.select({count:Mt()}).from(b))[0]?.count||0}catch(e){return o.error("Error counting agents:",{error:e instanceof Error?e.message:String(e)}),0}})}async cleanupAgents(){return this.withDatabase(async()=>{try{await this.db.delete(b),o.success("Successfully cleaned up agent table")}catch(e){throw o.error("Error cleaning up agent table:",{error:e instanceof Error?e.message:String(e)}),e}})}async getEntityById(e){return this.withDatabase(async()=>{let t=await this.db.select({entity:h,components:f}).from(h).leftJoin(f,n(f.entityId,h.id)).where(n(h.id,e));if(t.length===0)return null;let r=t[0].entity;return r.components=t.filter(i=>i.components).map(i=>i.components),r})}async getEntitiesForRoom(e,t){return this.withDatabase(async()=>{let r=this.db.select({entity:h,...t&&{components:f}}).from(u).leftJoin(h,w(n(u.entityId,h.id),n(h.agentId,this.agentId)));t&&r.leftJoin(f,n(f.entityId,h.id));let i=await r.where(n(u.roomId,e)),s=new Map;for(let d of i){if(!d.entity)continue;let y=d.entity.id;if(!s.has(y)){let I={...d.entity,components:t?[]:void 0};s.set(y,I)}if(t&&d.components){let I=s.get(y);I&&(I.components||(I.components=[]),I.components.push(d.components))}}return Array.from(s.values())})}async createEntity(e){return this.withDatabase(async()=>{try{return await this.db.transaction(async t=>(await t.insert(h).values(e),o.debug("Entity created successfully:",{entity:e}),!0))}catch(t){return o.error("Error creating entity:",{error:t instanceof Error?t.message:String(t),entityId:e.id,name:e.metadata?.name}),o.trace(t),!1}})}async ensureEntityExists(e){if(!e.id)return o.error("Entity ID is required for ensureEntityExists"),!1;try{return await this.getEntityById(e.id)?!0:await this.createEntity(e)}catch(t){return o.error("Error ensuring entity exists:",{error:t instanceof Error?t.message:String(t),entityId:e.id}),!1}}async updateEntity(e){return this.withDatabase(async()=>{await this.db.update(h).set(e).where(w(n(h.id,e.id),n(h.agentId,e.agentId)))})}async getComponent(e,t,r,i){return this.withDatabase(async()=>{let s=[n(f.entityId,e),n(f.type,t)];r&&s.push(n(f.worldId,r)),i&&s.push(n(f.sourceEntityId,i));let d=await this.db.select().from(f).where(w(...s));return d.length>0?d[0]:null})}async getComponents(e,t,r){return this.withDatabase(async()=>{let i=[n(f.entityId,e)];return t&&i.push(n(f.worldId,t)),r&&i.push(n(f.sourceEntityId,r)),await this.db.select({id:f.id,entityId:f.entityId,type:f.type,data:f.data,worldId:f.worldId,sourceEntityId:f.sourceEntityId,createdAt:f.createdAt}).from(f).where(w(...i))})}async createComponent(e){return this.withDatabase(async()=>(await this.db.insert(f).values(e),!0))}async updateComponent(e){return this.withDatabase(async()=>{await this.db.update(f).set(e).where(n(f.id,e.id))})}async deleteComponent(e){return this.withDatabase(async()=>{await this.db.delete(f).where(n(f.id,e))})}async getMemories(e){let{entityId:t,agentId:r,roomId:i,worldId:s,tableName:d,count:y,unique:I,start:R,end:A}=e;if(!d)throw new Error("tableName is required");return this.withDatabase(async()=>{let U=[n(a.type,d)];R&&U.push(xe(a.createdAt,R)),t&&U.push(n(a.entityId,t)),i&&U.push(n(a.roomId,i)),s&&U.push(n(a.worldId,s)),A&&U.push(St(a.createdAt,A)),I&&U.push(n(a.unique,!0)),r&&U.push(n(a.agentId,r));let X=this.db.select({memory:{id:a.id,type:a.type,createdAt:a.createdAt,content:a.content,entityId:a.entityId,agentId:a.agentId,roomId:a.roomId,unique:a.unique,metadata:a.metadata},embedding:l[this.embeddingDimension]}).from(a).leftJoin(l,n(l.memoryId,a.id)).where(w(...U)).orderBy(z(a.createdAt));return(e.count?await X.limit(e.count):await X).map(g=>({id:g.memory.id,type:g.memory.type,createdAt:g.memory.createdAt,content:typeof g.memory.content=="string"?JSON.parse(g.memory.content):g.memory.content,entityId:g.memory.entityId,agentId:g.memory.agentId,roomId:g.memory.roomId,unique:g.memory.unique,metadata:g.memory.metadata,embedding:g.embedding?Array.from(g.embedding):void 0}))})}async getMemoriesByRoomIds(e){return this.withDatabase(async()=>{if(e.roomIds.length===0)return[];let t=[n(a.type,e.tableName),D(a.roomId,e.roomIds)];t.push(n(a.agentId,this.agentId));let r=this.db.select({id:a.id,type:a.type,createdAt:a.createdAt,content:a.content,entityId:a.entityId,agentId:a.agentId,roomId:a.roomId,unique:a.unique,metadata:a.metadata}).from(a).where(w(...t)).orderBy(z(a.createdAt));return(e.limit?await r.limit(e.limit):await r).map(s=>({id:s.id,createdAt:s.createdAt,content:typeof s.content=="string"?JSON.parse(s.content):s.content,entityId:s.entityId,agentId:s.agentId,roomId:s.roomId,unique:s.unique,metadata:s.metadata}))})}async getMemoryById(e){return this.withDatabase(async()=>{let t=await this.db.select({memory:a,embedding:l[this.embeddingDimension]}).from(a).leftJoin(l,n(a.id,l.memoryId)).where(n(a.id,e)).limit(1);if(t.length===0)return null;let r=t[0];return{id:r.memory.id,createdAt:r.memory.createdAt,content:typeof r.memory.content=="string"?JSON.parse(r.memory.content):r.memory.content,entityId:r.memory.entityId,agentId:r.memory.agentId,roomId:r.memory.roomId,unique:r.memory.unique,embedding:r.embedding??void 0}})}async getMemoriesByIds(e,t){return this.withDatabase(async()=>{if(e.length===0)return[];let r=[D(a.id,e)];return t&&r.push(n(a.type,t)),(await this.db.select({memory:a,embedding:l[this.embeddingDimension]}).from(a).leftJoin(l,n(l.memoryId,a.id)).where(w(...r)).orderBy(z(a.createdAt))).map(s=>({id:s.memory.id,createdAt:s.memory.createdAt,content:typeof s.memory.content=="string"?JSON.parse(s.memory.content):s.memory.content,entityId:s.memory.entityId,agentId:s.memory.agentId,roomId:s.memory.roomId,unique:s.memory.unique,metadata:s.memory.metadata,embedding:s.embedding??void 0}))})}async getCachedEmbeddings(e){return this.withDatabase(async()=>{try{return(await this.db.execute(M`
|
|
238
|
+
`
|
|
239
|
+
)
|
|
240
|
+
]
|
|
241
|
+
);
|
|
242
|
+
var memoryRelations = relations(memoryTable, ({ one }) => ({
|
|
243
|
+
embedding: one(embeddingTable)
|
|
244
|
+
}));
|
|
245
|
+
|
|
246
|
+
// src/schema/embedding.ts
|
|
247
|
+
var DIMENSION_MAP = {
|
|
248
|
+
[VECTOR_DIMS.SMALL]: "dim384",
|
|
249
|
+
[VECTOR_DIMS.MEDIUM]: "dim512",
|
|
250
|
+
[VECTOR_DIMS.LARGE]: "dim768",
|
|
251
|
+
[VECTOR_DIMS.XL]: "dim1024",
|
|
252
|
+
[VECTOR_DIMS.XXL]: "dim1536",
|
|
253
|
+
[VECTOR_DIMS.XXXL]: "dim3072"
|
|
254
|
+
};
|
|
255
|
+
var embeddingTable = pgTable6(
|
|
256
|
+
"embeddings",
|
|
257
|
+
{
|
|
258
|
+
id: uuid6("id").primaryKey().defaultRandom().notNull(),
|
|
259
|
+
memoryId: uuid6("memory_id").references(() => memoryTable.id),
|
|
260
|
+
createdAt: numberTimestamp("created_at").default(sql6`now()`).notNull(),
|
|
261
|
+
dim384: vector2("dim_384", { dimensions: VECTOR_DIMS.SMALL }),
|
|
262
|
+
dim512: vector2("dim_512", { dimensions: VECTOR_DIMS.MEDIUM }),
|
|
263
|
+
dim768: vector2("dim_768", { dimensions: VECTOR_DIMS.LARGE }),
|
|
264
|
+
dim1024: vector2("dim_1024", { dimensions: VECTOR_DIMS.XL }),
|
|
265
|
+
dim1536: vector2("dim_1536", { dimensions: VECTOR_DIMS.XXL }),
|
|
266
|
+
dim3072: vector2("dim_3072", { dimensions: VECTOR_DIMS.XXXL })
|
|
267
|
+
},
|
|
268
|
+
(table) => [
|
|
269
|
+
check2("embedding_source_check", sql6`"memory_id" IS NOT NULL`),
|
|
270
|
+
index2("idx_embedding_memory").on(table.memoryId),
|
|
271
|
+
foreignKey2({
|
|
272
|
+
name: "fk_embedding_memory",
|
|
273
|
+
columns: [table.memoryId],
|
|
274
|
+
foreignColumns: [memoryTable.id]
|
|
275
|
+
}).onDelete("cascade")
|
|
276
|
+
]
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
// src/schema/cache.ts
|
|
280
|
+
import { sql as sql7 } from "drizzle-orm";
|
|
281
|
+
import { jsonb as jsonb6, pgTable as pgTable7, text as text6, unique as unique4, uuid as uuid7 } from "drizzle-orm/pg-core";
|
|
282
|
+
var cacheTable = pgTable7(
|
|
283
|
+
"cache",
|
|
284
|
+
{
|
|
285
|
+
id: uuid7("id").notNull().primaryKey().default(sql7`gen_random_uuid()`),
|
|
286
|
+
key: text6("key").notNull(),
|
|
287
|
+
agentId: uuid7("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
288
|
+
value: jsonb6("value").notNull(),
|
|
289
|
+
createdAt: numberTimestamp("createdAt").default(sql7`now()`).notNull(),
|
|
290
|
+
expiresAt: numberTimestamp("expiresAt")
|
|
291
|
+
},
|
|
292
|
+
(table) => [unique4("cache_key_agent_unique").on(table.key, table.agentId)]
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// src/schema/component.ts
|
|
296
|
+
import { sql as sql8 } from "drizzle-orm";
|
|
297
|
+
import { jsonb as jsonb7, pgTable as pgTable8, text as text7, uuid as uuid8 } from "drizzle-orm/pg-core";
|
|
298
|
+
var componentTable = pgTable8("components", {
|
|
299
|
+
id: uuid8("id").primaryKey().defaultRandom(),
|
|
300
|
+
entityId: uuid8("entityId").notNull().references(() => entityTable.id, { onDelete: "cascade" }),
|
|
301
|
+
agentId: uuid8("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
302
|
+
roomId: uuid8("roomId").notNull().references(() => roomTable.id, { onDelete: "cascade" }),
|
|
303
|
+
worldId: uuid8("worldId").references(() => worldTable.id, {
|
|
304
|
+
onDelete: "cascade"
|
|
305
|
+
}),
|
|
306
|
+
sourceEntityId: uuid8("sourceEntityId").references(() => entityTable.id, {
|
|
307
|
+
onDelete: "cascade"
|
|
308
|
+
}),
|
|
309
|
+
type: text7("type").notNull(),
|
|
310
|
+
data: jsonb7("data").default(sql8`'{}'::jsonb`),
|
|
311
|
+
createdAt: numberTimestamp("createdAt").default(sql8`now()`).notNull()
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// src/schema/log.ts
|
|
315
|
+
import { sql as sql9 } from "drizzle-orm";
|
|
316
|
+
import { foreignKey as foreignKey3, jsonb as jsonb8, pgTable as pgTable9, text as text8, uuid as uuid9 } from "drizzle-orm/pg-core";
|
|
317
|
+
var logTable = pgTable9(
|
|
318
|
+
"logs",
|
|
319
|
+
{
|
|
320
|
+
id: uuid9("id").defaultRandom().notNull(),
|
|
321
|
+
createdAt: numberTimestamp("createdAt").default(sql9`now()`).notNull(),
|
|
322
|
+
entityId: uuid9("entityId").notNull().references(() => entityTable.id),
|
|
323
|
+
body: jsonb8("body").notNull(),
|
|
324
|
+
type: text8("type").notNull(),
|
|
325
|
+
roomId: uuid9("roomId").notNull().references(() => roomTable.id)
|
|
326
|
+
},
|
|
327
|
+
(table) => [
|
|
328
|
+
foreignKey3({
|
|
329
|
+
name: "fk_room",
|
|
330
|
+
columns: [table.roomId],
|
|
331
|
+
foreignColumns: [roomTable.id]
|
|
332
|
+
}).onDelete("cascade"),
|
|
333
|
+
foreignKey3({
|
|
334
|
+
name: "fk_user",
|
|
335
|
+
columns: [table.entityId],
|
|
336
|
+
foreignColumns: [entityTable.id]
|
|
337
|
+
}).onDelete("cascade")
|
|
338
|
+
]
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// src/schema/participant.ts
|
|
342
|
+
import { sql as sql10 } from "drizzle-orm";
|
|
343
|
+
import { foreignKey as foreignKey4, index as index3, pgTable as pgTable10, text as text9, uuid as uuid10 } from "drizzle-orm/pg-core";
|
|
344
|
+
var participantTable = pgTable10(
|
|
345
|
+
"participants",
|
|
346
|
+
{
|
|
347
|
+
id: uuid10("id").notNull().primaryKey().default(sql10`gen_random_uuid()`),
|
|
348
|
+
createdAt: numberTimestamp("createdAt").default(sql10`now()`).notNull(),
|
|
349
|
+
entityId: uuid10("entityId").references(() => entityTable.id, {
|
|
350
|
+
onDelete: "cascade"
|
|
351
|
+
}),
|
|
352
|
+
roomId: uuid10("roomId").references(() => roomTable.id, {
|
|
353
|
+
onDelete: "cascade"
|
|
354
|
+
}),
|
|
355
|
+
agentId: uuid10("agentId").references(() => agentTable.id, {
|
|
356
|
+
onDelete: "cascade"
|
|
357
|
+
}),
|
|
358
|
+
roomState: text9("roomState")
|
|
359
|
+
},
|
|
360
|
+
(table) => [
|
|
361
|
+
// unique("participants_user_room_agent_unique").on(table.entityId, table.roomId, table.agentId),
|
|
362
|
+
index3("idx_participants_user").on(table.entityId),
|
|
363
|
+
index3("idx_participants_room").on(table.roomId),
|
|
364
|
+
foreignKey4({
|
|
365
|
+
name: "fk_room",
|
|
366
|
+
columns: [table.roomId],
|
|
367
|
+
foreignColumns: [roomTable.id]
|
|
368
|
+
}).onDelete("cascade"),
|
|
369
|
+
foreignKey4({
|
|
370
|
+
name: "fk_user",
|
|
371
|
+
columns: [table.entityId],
|
|
372
|
+
foreignColumns: [entityTable.id]
|
|
373
|
+
}).onDelete("cascade")
|
|
374
|
+
]
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// src/schema/relationship.ts
|
|
378
|
+
import { sql as sql11 } from "drizzle-orm";
|
|
379
|
+
import { foreignKey as foreignKey5, index as index4, jsonb as jsonb9, pgTable as pgTable11, text as text10, unique as unique6, uuid as uuid11 } from "drizzle-orm/pg-core";
|
|
380
|
+
var relationshipTable = pgTable11(
|
|
381
|
+
"relationships",
|
|
382
|
+
{
|
|
383
|
+
id: uuid11("id").notNull().primaryKey().default(sql11`gen_random_uuid()`),
|
|
384
|
+
createdAt: numberTimestamp("createdAt").default(sql11`now()`).notNull(),
|
|
385
|
+
sourceEntityId: uuid11("sourceEntityId").notNull().references(() => entityTable.id, { onDelete: "cascade" }),
|
|
386
|
+
targetEntityId: uuid11("targetEntityId").notNull().references(() => entityTable.id, { onDelete: "cascade" }),
|
|
387
|
+
agentId: uuid11("agentId").notNull().references(() => agentTable.id, { onDelete: "cascade" }),
|
|
388
|
+
tags: text10("tags").array(),
|
|
389
|
+
metadata: jsonb9("metadata")
|
|
390
|
+
},
|
|
391
|
+
(table) => [
|
|
392
|
+
index4("idx_relationships_users").on(table.sourceEntityId, table.targetEntityId),
|
|
393
|
+
unique6("unique_relationship").on(table.sourceEntityId, table.targetEntityId, table.agentId),
|
|
394
|
+
foreignKey5({
|
|
395
|
+
name: "fk_user_a",
|
|
396
|
+
columns: [table.sourceEntityId],
|
|
397
|
+
foreignColumns: [entityTable.id]
|
|
398
|
+
}).onDelete("cascade"),
|
|
399
|
+
foreignKey5({
|
|
400
|
+
name: "fk_user_b",
|
|
401
|
+
columns: [table.targetEntityId],
|
|
402
|
+
foreignColumns: [entityTable.id]
|
|
403
|
+
}).onDelete("cascade")
|
|
404
|
+
]
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
// src/schema/tasks.ts
|
|
408
|
+
import { jsonb as jsonb10, pgTable as pgTable12, text as text11, timestamp, uuid as uuid12 } from "drizzle-orm/pg-core";
|
|
409
|
+
var taskTable = pgTable12("tasks", {
|
|
410
|
+
id: uuid12("id").primaryKey().defaultRandom(),
|
|
411
|
+
name: text11("name").notNull(),
|
|
412
|
+
description: text11("description").notNull(),
|
|
413
|
+
roomId: uuid12("room_id"),
|
|
414
|
+
worldId: uuid12("world_id"),
|
|
415
|
+
agentId: uuid12("agent_id").notNull(),
|
|
416
|
+
tags: text11("tags").array(),
|
|
417
|
+
metadata: jsonb10("metadata"),
|
|
418
|
+
createdAt: timestamp("created_at").defaultNow(),
|
|
419
|
+
updatedAt: timestamp("updated_at").defaultNow()
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// src/base.ts
|
|
423
|
+
var BaseDrizzleAdapter = class extends DatabaseAdapter {
|
|
424
|
+
static {
|
|
425
|
+
__name(this, "BaseDrizzleAdapter");
|
|
426
|
+
}
|
|
427
|
+
maxRetries = 3;
|
|
428
|
+
baseDelay = 1e3;
|
|
429
|
+
maxDelay = 1e4;
|
|
430
|
+
jitterMax = 1e3;
|
|
431
|
+
embeddingDimension = DIMENSION_MAP[384];
|
|
432
|
+
agentId;
|
|
433
|
+
/**
|
|
434
|
+
* Constructor for creating a new instance of Agent with the specified agentId.
|
|
435
|
+
*
|
|
436
|
+
* @param {UUID} agentId - The unique identifier for the agent.
|
|
437
|
+
*/
|
|
438
|
+
constructor(agentId) {
|
|
439
|
+
super();
|
|
440
|
+
this.agentId = agentId;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Executes the given operation with retry logic.
|
|
444
|
+
* @template T
|
|
445
|
+
* @param {() => Promise<T>} operation - The operation to be executed.
|
|
446
|
+
* @returns {Promise<T>} A promise that resolves with the result of the operation.
|
|
447
|
+
*/
|
|
448
|
+
async withRetry(operation) {
|
|
449
|
+
let lastError = new Error("Unknown error");
|
|
450
|
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
|
451
|
+
try {
|
|
452
|
+
return await operation();
|
|
453
|
+
} catch (error) {
|
|
454
|
+
lastError = error;
|
|
455
|
+
if (attempt < this.maxRetries) {
|
|
456
|
+
const backoffDelay = Math.min(this.baseDelay * 2 ** (attempt - 1), this.maxDelay);
|
|
457
|
+
const jitter = Math.random() * this.jitterMax;
|
|
458
|
+
const delay = backoffDelay + jitter;
|
|
459
|
+
logger.warn(`Database operation failed (attempt ${attempt}/${this.maxRetries}):`, {
|
|
460
|
+
error: error instanceof Error ? error.message : String(error),
|
|
461
|
+
nextRetryIn: `${(delay / 1e3).toFixed(1)}s`
|
|
462
|
+
});
|
|
463
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
464
|
+
} else {
|
|
465
|
+
logger.error("Max retry attempts reached:", {
|
|
466
|
+
error: error instanceof Error ? error.message : String(error),
|
|
467
|
+
totalAttempts: attempt
|
|
468
|
+
});
|
|
469
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
throw lastError;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Asynchronously ensures that an agent exists by checking if an agent with the same name already exists in the system.
|
|
477
|
+
* If the agent does not exist, it will be created with the provided data.
|
|
478
|
+
*
|
|
479
|
+
* @param {Partial<Agent>} agent - The partial data of the agent to ensure its existence.
|
|
480
|
+
* @returns {Promise<void>} - A promise that resolves when the agent is successfully ensured.
|
|
481
|
+
* @throws {Error} - If the agent name is not provided or if there is an issue creating the agent.
|
|
482
|
+
*/
|
|
483
|
+
async ensureAgentExists(agent) {
|
|
484
|
+
if (!agent.name) {
|
|
485
|
+
throw new Error("Agent name is required");
|
|
486
|
+
}
|
|
487
|
+
const agents = await this.getAgents();
|
|
488
|
+
const existingAgent = agents.find(
|
|
489
|
+
(a) => a.name === agent.name
|
|
490
|
+
);
|
|
491
|
+
if (existingAgent) {
|
|
492
|
+
return existingAgent;
|
|
493
|
+
}
|
|
494
|
+
agent.id = stringToUuid(agent.name ?? v4());
|
|
495
|
+
await this.createAgent(agent);
|
|
496
|
+
return agent;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Asynchronously ensures that the given embedding dimension is valid for the agent.
|
|
500
|
+
*
|
|
501
|
+
* @param {number} dimension - The dimension to ensure for the embedding.
|
|
502
|
+
* @returns {Promise<void>} - Resolves once the embedding dimension is ensured.
|
|
503
|
+
*/
|
|
504
|
+
async ensureEmbeddingDimension(dimension) {
|
|
505
|
+
const existingMemory = await this.db.select({
|
|
506
|
+
embedding: embeddingTable
|
|
507
|
+
}).from(memoryTable).innerJoin(embeddingTable, eq(embeddingTable.memoryId, memoryTable.id)).where(eq(memoryTable.agentId, this.agentId)).limit(1);
|
|
508
|
+
if (existingMemory.length > 0) {
|
|
509
|
+
const usedDimension = Object.entries(DIMENSION_MAP).find(
|
|
510
|
+
([_, colName]) => existingMemory[0].embedding[colName] !== null
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
this.embeddingDimension = DIMENSION_MAP[dimension];
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Asynchronously retrieves an agent by their ID from the database.
|
|
517
|
+
* @param {UUID} agentId - The ID of the agent to retrieve.
|
|
518
|
+
* @returns {Promise<Agent | null>} A promise that resolves to the retrieved agent or null if not found.
|
|
519
|
+
*/
|
|
520
|
+
async getAgent(agentId) {
|
|
521
|
+
return this.withDatabase(async () => {
|
|
522
|
+
const result = await this.db.select().from(agentTable).where(eq(agentTable.id, agentId)).limit(1);
|
|
523
|
+
if (result.length === 0) return null;
|
|
524
|
+
return result[0];
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Asynchronously retrieves a list of agents from the database.
|
|
529
|
+
*
|
|
530
|
+
* @returns {Promise<Agent[]>} A Promise that resolves to an array of Agent objects.
|
|
531
|
+
*/
|
|
532
|
+
async getAgents() {
|
|
533
|
+
return this.withDatabase(async () => {
|
|
534
|
+
const result = await this.db.select().from(agentTable);
|
|
535
|
+
return result;
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Asynchronously creates a new agent record in the database.
|
|
540
|
+
*
|
|
541
|
+
* @param {Partial<Agent>} agent The agent object to be created.
|
|
542
|
+
* @returns {Promise<boolean>} A promise that resolves to a boolean indicating the success of the operation.
|
|
543
|
+
*/
|
|
544
|
+
async createAgent(agent) {
|
|
545
|
+
return this.withDatabase(async () => {
|
|
546
|
+
try {
|
|
547
|
+
await this.db.transaction(async (tx) => {
|
|
548
|
+
await tx.insert(agentTable).values({
|
|
549
|
+
...agent
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
logger.debug("Agent created successfully:", {
|
|
553
|
+
agentId: agent.id
|
|
554
|
+
});
|
|
555
|
+
return true;
|
|
556
|
+
} catch (error) {
|
|
557
|
+
logger.error("Error creating agent:", {
|
|
558
|
+
error: error instanceof Error ? error.message : String(error),
|
|
559
|
+
agentId: agent.id,
|
|
560
|
+
agent
|
|
561
|
+
});
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Updates an agent in the database with the provided agent ID and data.
|
|
568
|
+
* @param {UUID} agentId - The unique identifier of the agent to update.
|
|
569
|
+
* @param {Partial<Agent>} agent - The partial agent object containing the fields to update.
|
|
570
|
+
* @returns {Promise<boolean>} - A boolean indicating if the agent was successfully updated.
|
|
571
|
+
*/
|
|
572
|
+
async updateAgent(agentId, agent) {
|
|
573
|
+
return this.withDatabase(async () => {
|
|
574
|
+
try {
|
|
575
|
+
if (!agentId) {
|
|
576
|
+
throw new Error("Agent ID is required for update");
|
|
577
|
+
}
|
|
578
|
+
await this.db.transaction(async (tx) => {
|
|
579
|
+
if (agent?.settings) {
|
|
580
|
+
agent.settings = await this.mergeAgentSettings(tx, agentId, agent.settings);
|
|
581
|
+
}
|
|
582
|
+
await tx.update(agentTable).set({
|
|
583
|
+
...agent,
|
|
584
|
+
updatedAt: Date.now()
|
|
585
|
+
}).where(eq(agentTable.id, agentId));
|
|
586
|
+
});
|
|
587
|
+
logger.debug("Agent updated successfully:", {
|
|
588
|
+
agentId
|
|
589
|
+
});
|
|
590
|
+
return true;
|
|
591
|
+
} catch (error) {
|
|
592
|
+
logger.error("Error updating agent:", {
|
|
593
|
+
error: error instanceof Error ? error.message : String(error),
|
|
594
|
+
agentId,
|
|
595
|
+
agent
|
|
596
|
+
});
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Merges updated agent settings with existing settings in the database,
|
|
603
|
+
* with special handling for nested objects like secrets.
|
|
604
|
+
* @param tx - The database transaction
|
|
605
|
+
* @param agentId - The ID of the agent
|
|
606
|
+
* @param updatedSettings - The settings object with updates
|
|
607
|
+
* @returns The merged settings object
|
|
608
|
+
* @private
|
|
609
|
+
*/
|
|
610
|
+
async mergeAgentSettings(tx, agentId, updatedSettings) {
|
|
611
|
+
const currentAgent = await tx.select({ settings: agentTable.settings }).from(agentTable).where(eq(agentTable.id, agentId)).limit(1);
|
|
612
|
+
if (currentAgent.length === 0 || !currentAgent[0].settings) {
|
|
613
|
+
return updatedSettings;
|
|
614
|
+
}
|
|
615
|
+
const currentSettings = currentAgent[0].settings;
|
|
616
|
+
if (updatedSettings.secrets) {
|
|
617
|
+
const currentSecrets = currentSettings.secrets || {};
|
|
618
|
+
const updatedSecrets = updatedSettings.secrets;
|
|
619
|
+
const mergedSecrets = { ...currentSecrets };
|
|
620
|
+
for (const [key, value] of Object.entries(updatedSecrets)) {
|
|
621
|
+
if (value === null) {
|
|
622
|
+
delete mergedSecrets[key];
|
|
623
|
+
} else {
|
|
624
|
+
mergedSecrets[key] = value;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
updatedSettings.secrets = mergedSecrets;
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
...currentSettings,
|
|
631
|
+
...updatedSettings
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Asynchronously deletes an agent with the specified UUID and all related entries.
|
|
636
|
+
*
|
|
637
|
+
* @param {UUID} agentId - The UUID of the agent to be deleted.
|
|
638
|
+
* @returns {Promise<boolean>} - A boolean indicating if the deletion was successful.
|
|
639
|
+
*/
|
|
640
|
+
async deleteAgent(agentId) {
|
|
641
|
+
logger.debug(`[DB] Starting deletion of agent with ID: ${agentId}`);
|
|
642
|
+
return this.withDatabase(async () => {
|
|
643
|
+
try {
|
|
644
|
+
logger.debug(`[DB] Beginning database transaction for deleting agent: ${agentId}`);
|
|
645
|
+
const deletePromise = new Promise((resolve, reject) => {
|
|
646
|
+
const timeoutId = setTimeout(() => {
|
|
647
|
+
logger.error(`[DB] Transaction timeout reached for agent deletion: ${agentId}`);
|
|
648
|
+
reject(new Error("Database transaction timeout"));
|
|
649
|
+
}, 3e4);
|
|
650
|
+
this.db.transaction(async (tx) => {
|
|
651
|
+
try {
|
|
652
|
+
logger.debug(`[DB] Fetching entities for agent: ${agentId}`);
|
|
653
|
+
const entities = await tx.select({ entityId: entityTable.id }).from(entityTable).where(eq(entityTable.agentId, agentId));
|
|
654
|
+
const entityIds = entities.map((e) => e.entityId);
|
|
655
|
+
logger.debug(
|
|
656
|
+
`[DB] Found ${entityIds.length} entities to delete for agent ${agentId}`
|
|
657
|
+
);
|
|
658
|
+
logger.debug(`[DB] Fetching rooms for agent: ${agentId}`);
|
|
659
|
+
const rooms = await tx.select({ roomId: roomTable.id }).from(roomTable).where(eq(roomTable.agentId, agentId));
|
|
660
|
+
const roomIds = rooms.map((r) => r.roomId);
|
|
661
|
+
logger.debug(`[DB] Found ${roomIds.length} rooms for agent ${agentId}`);
|
|
662
|
+
logger.debug(
|
|
663
|
+
`[DB] Explicitly deleting ALL logs with matching entityIds and roomIds`
|
|
664
|
+
);
|
|
665
|
+
if (entityIds.length > 0) {
|
|
666
|
+
logger.debug(`[DB] Deleting logs for ${entityIds.length} entities (first batch)`);
|
|
667
|
+
const BATCH_SIZE = 50;
|
|
668
|
+
for (let i = 0; i < entityIds.length; i += BATCH_SIZE) {
|
|
669
|
+
const batch = entityIds.slice(i, i + BATCH_SIZE);
|
|
670
|
+
logger.debug(
|
|
671
|
+
`[DB] Processing entity logs batch ${i / BATCH_SIZE + 1} with ${batch.length} entities`
|
|
672
|
+
);
|
|
673
|
+
await tx.delete(logTable).where(inArray(logTable.entityId, batch));
|
|
674
|
+
}
|
|
675
|
+
logger.debug(`[DB] Entity logs deletion completed successfully`);
|
|
676
|
+
}
|
|
677
|
+
if (roomIds.length > 0) {
|
|
678
|
+
logger.debug(`[DB] Deleting logs for ${roomIds.length} rooms (first batch)`);
|
|
679
|
+
const BATCH_SIZE = 50;
|
|
680
|
+
for (let i = 0; i < roomIds.length; i += BATCH_SIZE) {
|
|
681
|
+
const batch = roomIds.slice(i, i + BATCH_SIZE);
|
|
682
|
+
logger.debug(
|
|
683
|
+
`[DB] Processing room logs batch ${i / BATCH_SIZE + 1} with ${batch.length} rooms`
|
|
684
|
+
);
|
|
685
|
+
await tx.delete(logTable).where(inArray(logTable.roomId, batch));
|
|
686
|
+
}
|
|
687
|
+
logger.debug(`[DB] Room logs deletion completed successfully`);
|
|
688
|
+
}
|
|
689
|
+
let memoryIds = [];
|
|
690
|
+
if (entityIds.length > 0) {
|
|
691
|
+
logger.debug(`[DB] Finding memories belonging to entities`);
|
|
692
|
+
const memories = await tx.select({ id: memoryTable.id }).from(memoryTable).where(inArray(memoryTable.entityId, entityIds));
|
|
693
|
+
memoryIds = memories.map((m) => m.id);
|
|
694
|
+
logger.debug(`[DB] Found ${memoryIds.length} memories belonging to entities`);
|
|
695
|
+
}
|
|
696
|
+
logger.debug(`[DB] Finding memories belonging to agent directly`);
|
|
697
|
+
const agentMemories = await tx.select({ id: memoryTable.id }).from(memoryTable).where(eq(memoryTable.agentId, agentId));
|
|
698
|
+
memoryIds = [...memoryIds, ...agentMemories.map((m) => m.id)];
|
|
699
|
+
logger.debug(`[DB] Found total of ${memoryIds.length} memories to delete`);
|
|
700
|
+
if (roomIds.length > 0) {
|
|
701
|
+
logger.debug(`[DB] Finding memories belonging to rooms`);
|
|
702
|
+
const roomMemories = await tx.select({ id: memoryTable.id }).from(memoryTable).where(inArray(memoryTable.roomId, roomIds));
|
|
703
|
+
memoryIds = [...memoryIds, ...roomMemories.map((m) => m.id)];
|
|
704
|
+
logger.debug(`[DB] Updated total to ${memoryIds.length} memories to delete`);
|
|
705
|
+
}
|
|
706
|
+
if (memoryIds.length > 0) {
|
|
707
|
+
logger.debug(`[DB] Deleting embeddings for ${memoryIds.length} memories`);
|
|
708
|
+
const BATCH_SIZE = 100;
|
|
709
|
+
for (let i = 0; i < memoryIds.length; i += BATCH_SIZE) {
|
|
710
|
+
const batch = memoryIds.slice(i, i + BATCH_SIZE);
|
|
711
|
+
await tx.delete(embeddingTable).where(inArray(embeddingTable.memoryId, batch));
|
|
712
|
+
}
|
|
713
|
+
logger.debug(`[DB] Embeddings deleted successfully`);
|
|
714
|
+
}
|
|
715
|
+
if (memoryIds.length > 0) {
|
|
716
|
+
logger.debug(`[DB] Deleting ${memoryIds.length} memories`);
|
|
717
|
+
const BATCH_SIZE = 100;
|
|
718
|
+
for (let i = 0; i < memoryIds.length; i += BATCH_SIZE) {
|
|
719
|
+
const batch = memoryIds.slice(i, i + BATCH_SIZE);
|
|
720
|
+
await tx.delete(memoryTable).where(inArray(memoryTable.id, batch));
|
|
721
|
+
}
|
|
722
|
+
logger.debug(`[DB] Memories deleted successfully`);
|
|
723
|
+
}
|
|
724
|
+
if (entityIds.length > 0) {
|
|
725
|
+
logger.debug(`[DB] Deleting components for entities`);
|
|
726
|
+
await tx.delete(componentTable).where(inArray(componentTable.entityId, entityIds));
|
|
727
|
+
logger.debug(`[DB] Components deleted successfully`);
|
|
728
|
+
}
|
|
729
|
+
if (entityIds.length > 0) {
|
|
730
|
+
logger.debug(`[DB] Deleting source entity references in components`);
|
|
731
|
+
await tx.delete(componentTable).where(inArray(componentTable.sourceEntityId, entityIds));
|
|
732
|
+
logger.debug(`[DB] Source entity references deleted successfully`);
|
|
733
|
+
}
|
|
734
|
+
if (roomIds.length > 0) {
|
|
735
|
+
logger.debug(`[DB] Deleting participations for rooms`);
|
|
736
|
+
await tx.delete(participantTable).where(inArray(participantTable.roomId, roomIds));
|
|
737
|
+
logger.debug(`[DB] Participations deleted for rooms`);
|
|
738
|
+
}
|
|
739
|
+
logger.debug(`[DB] Deleting agent participations`);
|
|
740
|
+
await tx.delete(participantTable).where(eq(participantTable.agentId, agentId));
|
|
741
|
+
logger.debug(`[DB] Agent participations deleted`);
|
|
742
|
+
if (roomIds.length > 0) {
|
|
743
|
+
logger.debug(`[DB] Deleting rooms`);
|
|
744
|
+
await tx.delete(roomTable).where(inArray(roomTable.id, roomIds));
|
|
745
|
+
logger.debug(`[DB] Rooms deleted successfully`);
|
|
746
|
+
}
|
|
747
|
+
logger.debug(`[DB] Deleting cache entries`);
|
|
748
|
+
await tx.delete(cacheTable).where(eq(cacheTable.agentId, agentId));
|
|
749
|
+
logger.debug(`[DB] Cache entries deleted successfully`);
|
|
750
|
+
logger.debug(`[DB] Deleting relationships`);
|
|
751
|
+
if (entityIds.length > 0) {
|
|
752
|
+
await tx.delete(relationshipTable).where(inArray(relationshipTable.sourceEntityId, entityIds));
|
|
753
|
+
await tx.delete(relationshipTable).where(inArray(relationshipTable.targetEntityId, entityIds));
|
|
754
|
+
}
|
|
755
|
+
await tx.delete(relationshipTable).where(eq(relationshipTable.agentId, agentId));
|
|
756
|
+
logger.debug(`[DB] Relationships deleted successfully`);
|
|
757
|
+
if (entityIds.length > 0) {
|
|
758
|
+
logger.debug(`[DB] Deleting entities`);
|
|
759
|
+
await tx.delete(entityTable).where(eq(entityTable.agentId, agentId));
|
|
760
|
+
logger.debug(`[DB] Entities deleted successfully`);
|
|
761
|
+
}
|
|
762
|
+
logger.debug(`[DB] Checking for world references`);
|
|
763
|
+
const worlds = await tx.select({ id: worldTable.id }).from(worldTable).where(eq(worldTable.agentId, agentId));
|
|
764
|
+
if (worlds.length > 0) {
|
|
765
|
+
const newAgent = await tx.select({ newAgentId: agentTable.id }).from(agentTable).where(not(eq(agentTable.id, agentId))).limit(1);
|
|
766
|
+
if (newAgent.length > 0) {
|
|
767
|
+
await tx.update(worldTable).set({ agentId: newAgent[0].newAgentId }).where(eq(worldTable.agentId, agentId));
|
|
768
|
+
} else {
|
|
769
|
+
const worldIds = worlds.map((w) => w.id);
|
|
770
|
+
logger.debug(`[DB] Found ${worldIds.length} worlds to delete`);
|
|
771
|
+
await tx.delete(worldTable).where(inArray(worldTable.id, worldIds));
|
|
772
|
+
logger.debug(`[DB] Worlds deleted successfully`);
|
|
773
|
+
}
|
|
774
|
+
} else {
|
|
775
|
+
logger.debug(`[DB] No worlds found for this agent`);
|
|
776
|
+
}
|
|
777
|
+
logger.debug(`[DB] Deleting agent ${agentId}`);
|
|
778
|
+
await tx.delete(agentTable).where(eq(agentTable.id, agentId));
|
|
779
|
+
logger.debug(`[DB] Agent deleted successfully`);
|
|
780
|
+
resolve(true);
|
|
781
|
+
} catch (error) {
|
|
782
|
+
logger.error(`[DB] Error in transaction:`, error);
|
|
783
|
+
reject(error);
|
|
784
|
+
}
|
|
785
|
+
}).catch((transactionError) => {
|
|
786
|
+
clearTimeout(timeoutId);
|
|
787
|
+
reject(transactionError);
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
await deletePromise;
|
|
791
|
+
logger.success(`[DB] Agent ${agentId} successfully deleted`);
|
|
792
|
+
return true;
|
|
793
|
+
} catch (error) {
|
|
794
|
+
logger.error(`[DB] Error in database transaction for agent deletion ${agentId}:`, error);
|
|
795
|
+
if (error instanceof Error) {
|
|
796
|
+
logger.error(`[DB] Error name: ${error.name}, message: ${error.message}`);
|
|
797
|
+
logger.error(`[DB] Error stack: ${error.stack}`);
|
|
798
|
+
}
|
|
799
|
+
throw error;
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Count all agents in the database
|
|
805
|
+
* Used primarily for maintenance and cleanup operations
|
|
806
|
+
*/
|
|
807
|
+
/**
|
|
808
|
+
* Asynchronously counts the number of agents in the database.
|
|
809
|
+
* @returns {Promise<number>} A Promise that resolves to the number of agents in the database.
|
|
810
|
+
*/
|
|
811
|
+
async countAgents() {
|
|
812
|
+
return this.withDatabase(async () => {
|
|
813
|
+
try {
|
|
814
|
+
const result = await this.db.select({ count: count() }).from(agentTable);
|
|
815
|
+
return result[0]?.count || 0;
|
|
816
|
+
} catch (error) {
|
|
817
|
+
logger.error("Error counting agents:", {
|
|
818
|
+
error: error instanceof Error ? error.message : String(error)
|
|
819
|
+
});
|
|
820
|
+
return 0;
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Clean up the agents table by removing all agents
|
|
826
|
+
* This is used during server startup to ensure no orphaned agents exist
|
|
827
|
+
* from previous crashes or improper shutdowns
|
|
828
|
+
*/
|
|
829
|
+
async cleanupAgents() {
|
|
830
|
+
return this.withDatabase(async () => {
|
|
831
|
+
try {
|
|
832
|
+
await this.db.delete(agentTable);
|
|
833
|
+
logger.success("Successfully cleaned up agent table");
|
|
834
|
+
} catch (error) {
|
|
835
|
+
logger.error("Error cleaning up agent table:", {
|
|
836
|
+
error: error instanceof Error ? error.message : String(error)
|
|
837
|
+
});
|
|
838
|
+
throw error;
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Asynchronously retrieves an entity and its components by entity ID.
|
|
844
|
+
* @param {UUID} entityId - The unique identifier of the entity to retrieve.
|
|
845
|
+
* @returns {Promise<Entity | null>} A Promise that resolves to the entity with its components if found, null otherwise.
|
|
846
|
+
*/
|
|
847
|
+
async getEntityById(entityId) {
|
|
848
|
+
return this.withDatabase(async () => {
|
|
849
|
+
const result = await this.db.select({
|
|
850
|
+
entity: entityTable,
|
|
851
|
+
components: componentTable
|
|
852
|
+
}).from(entityTable).leftJoin(componentTable, eq(componentTable.entityId, entityTable.id)).where(eq(entityTable.id, entityId));
|
|
853
|
+
if (result.length === 0) return null;
|
|
854
|
+
const entity = result[0].entity;
|
|
855
|
+
entity.components = result.filter((row) => row.components).map((row) => row.components);
|
|
856
|
+
return entity;
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Asynchronously retrieves all entities for a given room, optionally including their components.
|
|
861
|
+
* @param {UUID} roomId - The unique identifier of the room to get entities for
|
|
862
|
+
* @param {boolean} [includeComponents] - Whether to include component data for each entity
|
|
863
|
+
* @returns {Promise<Entity[]>} A Promise that resolves to an array of entities in the room
|
|
864
|
+
*/
|
|
865
|
+
async getEntitiesForRoom(roomId, includeComponents) {
|
|
866
|
+
return this.withDatabase(async () => {
|
|
867
|
+
const query = this.db.select({
|
|
868
|
+
entity: entityTable,
|
|
869
|
+
...includeComponents && { components: componentTable }
|
|
870
|
+
}).from(participantTable).leftJoin(
|
|
871
|
+
entityTable,
|
|
872
|
+
and(eq(participantTable.entityId, entityTable.id), eq(entityTable.agentId, this.agentId))
|
|
873
|
+
);
|
|
874
|
+
if (includeComponents) {
|
|
875
|
+
query.leftJoin(componentTable, eq(componentTable.entityId, entityTable.id));
|
|
876
|
+
}
|
|
877
|
+
const result = await query.where(eq(participantTable.roomId, roomId));
|
|
878
|
+
const entitiesByIdMap = /* @__PURE__ */ new Map();
|
|
879
|
+
for (const row of result) {
|
|
880
|
+
if (!row.entity) continue;
|
|
881
|
+
const entityId = row.entity.id;
|
|
882
|
+
if (!entitiesByIdMap.has(entityId)) {
|
|
883
|
+
const entity = {
|
|
884
|
+
...row.entity,
|
|
885
|
+
components: includeComponents ? [] : void 0
|
|
886
|
+
};
|
|
887
|
+
entitiesByIdMap.set(entityId, entity);
|
|
888
|
+
}
|
|
889
|
+
if (includeComponents && row.components) {
|
|
890
|
+
const entity = entitiesByIdMap.get(entityId);
|
|
891
|
+
if (entity) {
|
|
892
|
+
if (!entity.components) {
|
|
893
|
+
entity.components = [];
|
|
894
|
+
}
|
|
895
|
+
entity.components.push(row.components);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return Array.from(entitiesByIdMap.values());
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Asynchronously creates a new entity in the database.
|
|
904
|
+
* @param {Entity} entity - The entity object to be created.
|
|
905
|
+
* @returns {Promise<boolean>} A Promise that resolves to a boolean indicating the success of the operation.
|
|
906
|
+
*/
|
|
907
|
+
async createEntity(entity) {
|
|
908
|
+
return this.withDatabase(async () => {
|
|
909
|
+
try {
|
|
910
|
+
return await this.db.transaction(async (tx) => {
|
|
911
|
+
await tx.insert(entityTable).values(entity);
|
|
912
|
+
logger.debug("Entity created successfully:", {
|
|
913
|
+
entity
|
|
914
|
+
});
|
|
915
|
+
return true;
|
|
916
|
+
});
|
|
917
|
+
} catch (error) {
|
|
918
|
+
logger.error("Error creating entity:", {
|
|
919
|
+
error: error instanceof Error ? error.message : String(error),
|
|
920
|
+
entityId: entity.id,
|
|
921
|
+
name: entity.metadata?.name
|
|
922
|
+
});
|
|
923
|
+
logger.trace(error);
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Asynchronously ensures an entity exists, creating it if it doesn't
|
|
930
|
+
* @param entity The entity to ensure exists
|
|
931
|
+
* @returns Promise resolving to boolean indicating success
|
|
932
|
+
*/
|
|
933
|
+
async ensureEntityExists(entity) {
|
|
934
|
+
if (!entity.id) {
|
|
935
|
+
logger.error("Entity ID is required for ensureEntityExists");
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
const existingEntity = await this.getEntityById(entity.id);
|
|
940
|
+
if (!existingEntity) {
|
|
941
|
+
return await this.createEntity(entity);
|
|
942
|
+
}
|
|
943
|
+
return true;
|
|
944
|
+
} catch (error) {
|
|
945
|
+
logger.error("Error ensuring entity exists:", {
|
|
946
|
+
error: error instanceof Error ? error.message : String(error),
|
|
947
|
+
entityId: entity.id
|
|
948
|
+
});
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Asynchronously updates an entity in the database.
|
|
954
|
+
* @param {Entity} entity - The entity object to be updated.
|
|
955
|
+
* @returns {Promise<void>} A Promise that resolves when the entity is updated.
|
|
956
|
+
*/
|
|
957
|
+
async updateEntity(entity) {
|
|
958
|
+
return this.withDatabase(async () => {
|
|
959
|
+
await this.db.update(entityTable).set(entity).where(and(eq(entityTable.id, entity.id), eq(entityTable.agentId, entity.agentId)));
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
async getComponent(entityId, type, worldId, sourceEntityId) {
|
|
963
|
+
return this.withDatabase(async () => {
|
|
964
|
+
const conditions = [eq(componentTable.entityId, entityId), eq(componentTable.type, type)];
|
|
965
|
+
if (worldId) {
|
|
966
|
+
conditions.push(eq(componentTable.worldId, worldId));
|
|
967
|
+
}
|
|
968
|
+
if (sourceEntityId) {
|
|
969
|
+
conditions.push(eq(componentTable.sourceEntityId, sourceEntityId));
|
|
970
|
+
}
|
|
971
|
+
const result = await this.db.select().from(componentTable).where(and(...conditions));
|
|
972
|
+
return result.length > 0 ? result[0] : null;
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Asynchronously retrieves all components for a given entity, optionally filtered by world and source entity.
|
|
977
|
+
* @param {UUID} entityId - The unique identifier of the entity to retrieve components for
|
|
978
|
+
* @param {UUID} [worldId] - Optional world ID to filter components by
|
|
979
|
+
* @param {UUID} [sourceEntityId] - Optional source entity ID to filter components by
|
|
980
|
+
* @returns {Promise<Component[]>} A Promise that resolves to an array of components
|
|
981
|
+
*/
|
|
982
|
+
async getComponents(entityId, worldId, sourceEntityId) {
|
|
983
|
+
return this.withDatabase(async () => {
|
|
984
|
+
const conditions = [eq(componentTable.entityId, entityId)];
|
|
985
|
+
if (worldId) {
|
|
986
|
+
conditions.push(eq(componentTable.worldId, worldId));
|
|
987
|
+
}
|
|
988
|
+
if (sourceEntityId) {
|
|
989
|
+
conditions.push(eq(componentTable.sourceEntityId, sourceEntityId));
|
|
990
|
+
}
|
|
991
|
+
const result = await this.db.select({
|
|
992
|
+
id: componentTable.id,
|
|
993
|
+
entityId: componentTable.entityId,
|
|
994
|
+
type: componentTable.type,
|
|
995
|
+
data: componentTable.data,
|
|
996
|
+
worldId: componentTable.worldId,
|
|
997
|
+
sourceEntityId: componentTable.sourceEntityId,
|
|
998
|
+
createdAt: componentTable.createdAt
|
|
999
|
+
}).from(componentTable).where(and(...conditions));
|
|
1000
|
+
return result;
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Asynchronously creates a new component in the database.
|
|
1005
|
+
* @param {Component} component - The component object to be created.
|
|
1006
|
+
* @returns {Promise<boolean>} A Promise that resolves to a boolean indicating the success of the operation.
|
|
1007
|
+
*/
|
|
1008
|
+
async createComponent(component) {
|
|
1009
|
+
return this.withDatabase(async () => {
|
|
1010
|
+
await this.db.insert(componentTable).values(component);
|
|
1011
|
+
return true;
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Asynchronously updates an existing component in the database.
|
|
1016
|
+
* @param {Component} component - The component object to be updated.
|
|
1017
|
+
* @returns {Promise<void>} A Promise that resolves when the component is updated.
|
|
1018
|
+
*/
|
|
1019
|
+
async updateComponent(component) {
|
|
1020
|
+
return this.withDatabase(async () => {
|
|
1021
|
+
await this.db.update(componentTable).set(component).where(eq(componentTable.id, component.id));
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Asynchronously deletes a component from the database.
|
|
1026
|
+
* @param {UUID} componentId - The unique identifier of the component to delete.
|
|
1027
|
+
* @returns {Promise<void>} A Promise that resolves when the component is deleted.
|
|
1028
|
+
*/
|
|
1029
|
+
async deleteComponent(componentId) {
|
|
1030
|
+
return this.withDatabase(async () => {
|
|
1031
|
+
await this.db.delete(componentTable).where(eq(componentTable.id, componentId));
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Asynchronously retrieves memories from the database based on the provided parameters.
|
|
1036
|
+
* @param {Object} params - The parameters for retrieving memories.
|
|
1037
|
+
* @param {UUID} params.roomId - The ID of the room to retrieve memories for.
|
|
1038
|
+
* @param {number} [params.count] - The maximum number of memories to retrieve.
|
|
1039
|
+
* @param {boolean} [params.unique] - Whether to retrieve unique memories only.
|
|
1040
|
+
* @param {string} [params.tableName] - The name of the table to retrieve memories from.
|
|
1041
|
+
* @param {number} [params.start] - The start date to retrieve memories from.
|
|
1042
|
+
* @param {number} [params.end] - The end date to retrieve memories from.
|
|
1043
|
+
* @returns {Promise<Memory[]>} A Promise that resolves to an array of memories.
|
|
1044
|
+
*/
|
|
1045
|
+
async getMemories(params) {
|
|
1046
|
+
const { entityId, agentId, roomId, worldId, tableName, count: count2, unique: unique7, start, end } = params;
|
|
1047
|
+
if (!tableName) throw new Error("tableName is required");
|
|
1048
|
+
return this.withDatabase(async () => {
|
|
1049
|
+
const conditions = [eq(memoryTable.type, tableName)];
|
|
1050
|
+
if (start) {
|
|
1051
|
+
conditions.push(gte(memoryTable.createdAt, start));
|
|
1052
|
+
}
|
|
1053
|
+
if (entityId) {
|
|
1054
|
+
conditions.push(eq(memoryTable.entityId, entityId));
|
|
1055
|
+
}
|
|
1056
|
+
if (roomId) {
|
|
1057
|
+
conditions.push(eq(memoryTable.roomId, roomId));
|
|
1058
|
+
}
|
|
1059
|
+
if (worldId) {
|
|
1060
|
+
conditions.push(eq(memoryTable.worldId, worldId));
|
|
1061
|
+
}
|
|
1062
|
+
if (end) {
|
|
1063
|
+
conditions.push(lte(memoryTable.createdAt, end));
|
|
1064
|
+
}
|
|
1065
|
+
if (unique7) {
|
|
1066
|
+
conditions.push(eq(memoryTable.unique, true));
|
|
1067
|
+
}
|
|
1068
|
+
if (agentId) {
|
|
1069
|
+
conditions.push(eq(memoryTable.agentId, agentId));
|
|
1070
|
+
}
|
|
1071
|
+
const query = this.db.select({
|
|
1072
|
+
memory: {
|
|
1073
|
+
id: memoryTable.id,
|
|
1074
|
+
type: memoryTable.type,
|
|
1075
|
+
createdAt: memoryTable.createdAt,
|
|
1076
|
+
content: memoryTable.content,
|
|
1077
|
+
entityId: memoryTable.entityId,
|
|
1078
|
+
agentId: memoryTable.agentId,
|
|
1079
|
+
roomId: memoryTable.roomId,
|
|
1080
|
+
unique: memoryTable.unique,
|
|
1081
|
+
metadata: memoryTable.metadata
|
|
1082
|
+
},
|
|
1083
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
1084
|
+
}).from(memoryTable).leftJoin(embeddingTable, eq(embeddingTable.memoryId, memoryTable.id)).where(and(...conditions)).orderBy(desc(memoryTable.createdAt));
|
|
1085
|
+
const rows = params.count ? await query.limit(params.count) : await query;
|
|
1086
|
+
return rows.map((row) => ({
|
|
1087
|
+
id: row.memory.id,
|
|
1088
|
+
type: row.memory.type,
|
|
1089
|
+
createdAt: row.memory.createdAt,
|
|
1090
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
1091
|
+
entityId: row.memory.entityId,
|
|
1092
|
+
agentId: row.memory.agentId,
|
|
1093
|
+
roomId: row.memory.roomId,
|
|
1094
|
+
unique: row.memory.unique,
|
|
1095
|
+
metadata: row.memory.metadata,
|
|
1096
|
+
embedding: row.embedding ? Array.from(row.embedding) : void 0
|
|
1097
|
+
}));
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Asynchronously retrieves memories from the database based on the provided parameters.
|
|
1102
|
+
* @param {Object} params - The parameters for retrieving memories.
|
|
1103
|
+
* @param {UUID[]} params.roomIds - The IDs of the rooms to retrieve memories for.
|
|
1104
|
+
* @param {string} params.tableName - The name of the table to retrieve memories from.
|
|
1105
|
+
* @param {number} [params.limit] - The maximum number of memories to retrieve.
|
|
1106
|
+
* @returns {Promise<Memory[]>} A Promise that resolves to an array of memories.
|
|
1107
|
+
*/
|
|
1108
|
+
async getMemoriesByRoomIds(params) {
|
|
1109
|
+
return this.withDatabase(async () => {
|
|
1110
|
+
if (params.roomIds.length === 0) return [];
|
|
1111
|
+
const conditions = [
|
|
1112
|
+
eq(memoryTable.type, params.tableName),
|
|
1113
|
+
inArray(memoryTable.roomId, params.roomIds)
|
|
1114
|
+
];
|
|
1115
|
+
conditions.push(eq(memoryTable.agentId, this.agentId));
|
|
1116
|
+
const query = this.db.select({
|
|
1117
|
+
id: memoryTable.id,
|
|
1118
|
+
type: memoryTable.type,
|
|
1119
|
+
createdAt: memoryTable.createdAt,
|
|
1120
|
+
content: memoryTable.content,
|
|
1121
|
+
entityId: memoryTable.entityId,
|
|
1122
|
+
agentId: memoryTable.agentId,
|
|
1123
|
+
roomId: memoryTable.roomId,
|
|
1124
|
+
unique: memoryTable.unique,
|
|
1125
|
+
metadata: memoryTable.metadata
|
|
1126
|
+
}).from(memoryTable).where(and(...conditions)).orderBy(desc(memoryTable.createdAt));
|
|
1127
|
+
const rows = params.limit ? await query.limit(params.limit) : await query;
|
|
1128
|
+
return rows.map((row) => ({
|
|
1129
|
+
id: row.id,
|
|
1130
|
+
createdAt: row.createdAt,
|
|
1131
|
+
content: typeof row.content === "string" ? JSON.parse(row.content) : row.content,
|
|
1132
|
+
entityId: row.entityId,
|
|
1133
|
+
agentId: row.agentId,
|
|
1134
|
+
roomId: row.roomId,
|
|
1135
|
+
unique: row.unique,
|
|
1136
|
+
metadata: row.metadata
|
|
1137
|
+
}));
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Asynchronously retrieves a memory by its unique identifier.
|
|
1142
|
+
* @param {UUID} id - The unique identifier of the memory to retrieve.
|
|
1143
|
+
* @returns {Promise<Memory | null>} A Promise that resolves to the memory if found, null otherwise.
|
|
1144
|
+
*/
|
|
1145
|
+
async getMemoryById(id) {
|
|
1146
|
+
return this.withDatabase(async () => {
|
|
1147
|
+
const result = await this.db.select({
|
|
1148
|
+
memory: memoryTable,
|
|
1149
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
1150
|
+
}).from(memoryTable).leftJoin(embeddingTable, eq(memoryTable.id, embeddingTable.memoryId)).where(eq(memoryTable.id, id)).limit(1);
|
|
1151
|
+
if (result.length === 0) return null;
|
|
1152
|
+
const row = result[0];
|
|
1153
|
+
return {
|
|
1154
|
+
id: row.memory.id,
|
|
1155
|
+
createdAt: row.memory.createdAt,
|
|
1156
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
1157
|
+
entityId: row.memory.entityId,
|
|
1158
|
+
agentId: row.memory.agentId,
|
|
1159
|
+
roomId: row.memory.roomId,
|
|
1160
|
+
unique: row.memory.unique,
|
|
1161
|
+
embedding: row.embedding ?? void 0
|
|
1162
|
+
};
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Asynchronously retrieves memories from the database based on the provided parameters.
|
|
1167
|
+
* @param {Object} params - The parameters for retrieving memories.
|
|
1168
|
+
* @param {UUID[]} params.memoryIds - The IDs of the memories to retrieve.
|
|
1169
|
+
* @param {string} [params.tableName] - The name of the table to retrieve memories from.
|
|
1170
|
+
* @returns {Promise<Memory[]>} A Promise that resolves to an array of memories.
|
|
1171
|
+
*/
|
|
1172
|
+
async getMemoriesByIds(memoryIds, tableName) {
|
|
1173
|
+
return this.withDatabase(async () => {
|
|
1174
|
+
if (memoryIds.length === 0) return [];
|
|
1175
|
+
const conditions = [inArray(memoryTable.id, memoryIds)];
|
|
1176
|
+
if (tableName) {
|
|
1177
|
+
conditions.push(eq(memoryTable.type, tableName));
|
|
1178
|
+
}
|
|
1179
|
+
const rows = await this.db.select({
|
|
1180
|
+
memory: memoryTable,
|
|
1181
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
1182
|
+
}).from(memoryTable).leftJoin(embeddingTable, eq(embeddingTable.memoryId, memoryTable.id)).where(and(...conditions)).orderBy(desc(memoryTable.createdAt));
|
|
1183
|
+
return rows.map((row) => ({
|
|
1184
|
+
id: row.memory.id,
|
|
1185
|
+
createdAt: row.memory.createdAt,
|
|
1186
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
1187
|
+
entityId: row.memory.entityId,
|
|
1188
|
+
agentId: row.memory.agentId,
|
|
1189
|
+
roomId: row.memory.roomId,
|
|
1190
|
+
unique: row.memory.unique,
|
|
1191
|
+
metadata: row.memory.metadata,
|
|
1192
|
+
embedding: row.embedding ?? void 0
|
|
1193
|
+
}));
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Asynchronously retrieves cached embeddings from the database based on the provided parameters.
|
|
1198
|
+
* @param {Object} opts - The parameters for retrieving cached embeddings.
|
|
1199
|
+
* @param {string} opts.query_table_name - The name of the table to retrieve embeddings from.
|
|
1200
|
+
* @param {number} opts.query_threshold - The threshold for the levenshtein distance.
|
|
1201
|
+
* @param {string} opts.query_input - The input string to search for.
|
|
1202
|
+
* @param {string} opts.query_field_name - The name of the field to retrieve embeddings from.
|
|
1203
|
+
* @param {string} opts.query_field_sub_name - The name of the sub-field to retrieve embeddings from.
|
|
1204
|
+
* @param {number} opts.query_match_count - The maximum number of matches to retrieve.
|
|
1205
|
+
* @returns {Promise<{ embedding: number[]; levenshtein_score: number }[]>} A Promise that resolves to an array of cached embeddings.
|
|
1206
|
+
*/
|
|
1207
|
+
async getCachedEmbeddings(opts) {
|
|
1208
|
+
return this.withDatabase(async () => {
|
|
1209
|
+
try {
|
|
1210
|
+
const results = await this.db.execute(sql12`
|
|
15
1211
|
WITH content_text AS (
|
|
16
1212
|
SELECT
|
|
17
1213
|
m.id,
|
|
18
1214
|
COALESCE(
|
|
19
|
-
m.content->>${
|
|
1215
|
+
m.content->>${opts.query_field_sub_name},
|
|
20
1216
|
''
|
|
21
1217
|
) as content_text
|
|
22
1218
|
FROM memories m
|
|
23
|
-
WHERE m.type = ${
|
|
24
|
-
AND m.content->>${
|
|
1219
|
+
WHERE m.type = ${opts.query_table_name}
|
|
1220
|
+
AND m.content->>${opts.query_field_sub_name} IS NOT NULL
|
|
25
1221
|
),
|
|
26
1222
|
embedded_text AS (
|
|
27
1223
|
SELECT
|
|
@@ -40,10 +1236,1287 @@ import{a as x,b as ge,c as ye}from"./chunk-K7ZVMPCJ.js";import*as ke from"node:o
|
|
|
40
1236
|
)
|
|
41
1237
|
SELECT
|
|
42
1238
|
embedding,
|
|
43
|
-
levenshtein(${
|
|
1239
|
+
levenshtein(${opts.query_input}, content_text) as levenshtein_score
|
|
44
1240
|
FROM embedded_text
|
|
45
|
-
WHERE levenshtein(${
|
|
1241
|
+
WHERE levenshtein(${opts.query_input}, content_text) <= ${opts.query_threshold}
|
|
46
1242
|
ORDER BY levenshtein_score
|
|
47
|
-
LIMIT ${
|
|
48
|
-
`)).rows.map(r=>({embedding:Array.isArray(r.embedding)?r.embedding:typeof r.embedding=="string"?JSON.parse(r.embedding):[],levenshtein_score:Number(r.levenshtein_score)})).filter(r=>Array.isArray(r.embedding))}catch(t){if(o.error("Error in getCachedEmbeddings:",{error:t instanceof Error?t.message:String(t),tableName:e.query_table_name,fieldName:e.query_field_name}),t instanceof Error&&t.message==="levenshtein argument exceeds maximum length of 255 characters")return[];throw t}})}async log(e){return this.withDatabase(async()=>{try{await this.db.transaction(async t=>{await t.insert(N).values({body:M`${e.body}::jsonb`,entityId:e.entityId,roomId:e.roomId,type:e.type})})}catch(t){throw o.error("Failed to create log entry:",{error:t instanceof Error?t.message:String(t),type:e.type,roomId:e.roomId,entityId:e.entityId}),t}})}async getLogs(e){let{entityId:t,roomId:r,type:i,count:s,offset:d}=e;return this.withDatabase(async()=>await this.db.select().from(N).where(w(n(N.entityId,t),r?n(N.roomId,r):void 0,i?n(N.type,i):void 0)).orderBy(z(N.createdAt)).limit(s??10).offset(d??0))}async deleteLog(e){return this.withDatabase(async()=>{await this.db.delete(N).where(n(N.id,e))})}async searchMemories(e){return await this.searchMemoriesByEmbedding(e.embedding,{match_threshold:e.match_threshold,count:e.count,roomId:e.roomId,worldId:e.worldId,entityId:e.entityId,unique:e.unique,tableName:e.tableName})}async searchMemoriesByEmbedding(e,t){return this.withDatabase(async()=>{let r=e.map(y=>Number.isFinite(y)?Number(y.toFixed(6)):0),i=M`1 - (${Nt(l[this.embeddingDimension],r)})`,s=[n(a.type,t.tableName)];return t.unique&&s.push(n(a.unique,!0)),s.push(n(a.agentId,this.agentId)),t.roomId&&s.push(n(a.roomId,t.roomId)),t.worldId&&s.push(n(a.worldId,t.worldId)),t.entityId&&s.push(n(a.entityId,t.entityId)),t.match_threshold&&s.push(xe(i,t.match_threshold)),(await this.db.select({memory:a,similarity:i,embedding:l[this.embeddingDimension]}).from(l).innerJoin(a,n(a.id,l.memoryId)).where(w(...s)).orderBy(z(i)).limit(t.count??10)).map(y=>({id:y.memory.id,type:y.memory.type,createdAt:y.memory.createdAt,content:typeof y.memory.content=="string"?JSON.parse(y.memory.content):y.memory.content,entityId:y.memory.entityId,agentId:y.memory.agentId,roomId:y.memory.roomId,worldId:y.memory.worldId,unique:y.memory.unique,metadata:y.memory.metadata,embedding:y.embedding??void 0,similarity:y.similarity}))})}async createMemory(e,t){o.debug("DrizzleAdapter createMemory:",{memoryId:e.id,embeddingLength:e.embedding?.length,contentLength:e.content?.text?.length});let r=e.id??O();if(await this.getMemoryById(r))return o.debug("Memory already exists, skipping creation:",{memoryId:r}),r;let s=!0;e.embedding&&Array.isArray(e.embedding)&&(s=(await this.searchMemoriesByEmbedding(e.embedding,{tableName:t,roomId:e.roomId,worldId:e.worldId,entityId:e.entityId,match_threshold:.95,count:1})).length===0);let d=typeof e.content=="string"?JSON.parse(e.content):e.content;return await this.db.transaction(async y=>{if(await y.insert(a).values([{id:r,type:t,content:M`${d}::jsonb`,metadata:M`${e.metadata||{}}::jsonb`,entityId:e.entityId,roomId:e.roomId,worldId:e.worldId,agentId:e.agentId,unique:e.unique??s,createdAt:e.createdAt}]),e.embedding&&Array.isArray(e.embedding)){let I={id:O(),memoryId:r,createdAt:e.createdAt},R=e.embedding.map(A=>Number.isFinite(A)?Number(A.toFixed(6)):0);I[this.embeddingDimension]=R,await y.insert(l).values([I])}}),r}async updateMemory(e){return this.withDatabase(async()=>{try{return o.debug("Updating memory:",{memoryId:e.id,hasEmbedding:!!e.embedding}),await this.db.transaction(async t=>{if(e.content){let r=typeof e.content=="string"?JSON.parse(e.content):e.content;await t.update(a).set({content:M`${r}::jsonb`,...e.metadata&&{metadata:M`${e.metadata}::jsonb`}}).where(n(a.id,e.id))}else e.metadata&&await t.update(a).set({metadata:M`${e.metadata}::jsonb`}).where(n(a.id,e.id));if(e.embedding&&Array.isArray(e.embedding)){let r=e.embedding.map(s=>Number.isFinite(s)?Number(s.toFixed(6)):0);if((await t.select({id:l.id}).from(l).where(n(l.memoryId,e.id)).limit(1)).length>0){let s={};s[this.embeddingDimension]=r,await t.update(l).set(s).where(n(l.memoryId,e.id))}else{let s={id:O(),memoryId:e.id,createdAt:Date.now()};s[this.embeddingDimension]=r,await t.insert(l).values([s])}}}),o.debug("Memory updated successfully:",{memoryId:e.id}),!0}catch(t){return o.error("Error updating memory:",{error:t instanceof Error?t.message:String(t),memoryId:e.id}),!1}})}async deleteMemory(e){return this.withDatabase(async()=>{await this.db.transaction(async t=>{await this.deleteMemoryFragments(t,e),await t.delete(l).where(n(l.memoryId,e)),await t.delete(a).where(n(a.id,e))}),o.debug("Memory and related fragments removed successfully:",{memoryId:e})})}async deleteMemoryFragments(e,t){let r=await this.getMemoryFragments(e,t);if(r.length>0){let i=r.map(s=>s.id);await e.delete(l).where(D(l.memoryId,i)),await e.delete(a).where(D(a.id,i)),o.debug("Deleted related fragments:",{documentId:t,fragmentCount:r.length})}}async getMemoryFragments(e,t){return await e.select({id:a.id}).from(a).where(w(n(a.agentId,this.agentId),M`${a.metadata}->>'documentId' = ${t}`))}async deleteAllMemories(e,t){return this.withDatabase(async()=>{await this.db.transaction(async r=>{let i=await r.select({id:a.id}).from(a).where(w(n(a.roomId,e),n(a.type,t)));i.length>0&&(await r.delete(l).where(D(l.memoryId,i.map(s=>s.id))),await r.delete(a).where(w(n(a.roomId,e),n(a.type,t))))}),o.debug("All memories removed successfully:",{roomId:e,tableName:t})})}async countMemories(e,t=!0,r=""){if(!r)throw new Error("tableName is required");return this.withDatabase(async()=>{let i=[n(a.roomId,e),n(a.type,r)];t&&i.push(n(a.unique,!0));let s=await this.db.select({count:M`count(*)`}).from(a).where(w(...i));return Number(s[0]?.count??0)})}async getRoom(e){return this.withDatabase(async()=>{let t=await this.db.select({id:c.id,channelId:c.channelId,agentId:c.agentId,serverId:c.serverId,worldId:c.worldId,type:c.type,source:c.source}).from(c).where(w(n(c.id,e),n(c.agentId,this.agentId))).limit(1);return t.length===0?null:t[0]})}async getRooms(e){return this.withDatabase(async()=>await this.db.select().from(c).where(n(c.worldId,e)))}async updateRoom(e){return this.withDatabase(async()=>{await this.db.update(c).set({...e,agentId:this.agentId}).where(n(c.id,e.id))})}async createRoom({id:e,name:t,agentId:r,source:i,type:s,channelId:d,serverId:y,worldId:I,metadata:R}){return this.withDatabase(async()=>{let A=e||O();return await this.db.insert(c).values({id:A,name:t,agentId:r,source:i,type:s,channelId:d,serverId:y,worldId:I,metadata:R}).onConflictDoNothing({target:c.id}),A})}async deleteRoom(e){if(!e)throw new Error("Room ID is required");return this.withDatabase(async()=>{await this.db.transaction(async t=>{await t.delete(c).where(n(c.id,e))})})}async getRoomsForParticipant(e){return this.withDatabase(async()=>(await this.db.select({roomId:u.roomId}).from(u).innerJoin(c,n(u.roomId,c.id)).where(w(n(u.entityId,e),n(c.agentId,this.agentId)))).map(r=>r.roomId))}async getRoomsForParticipants(e){return this.withDatabase(async()=>(await this.db.selectDistinct({roomId:u.roomId}).from(u).innerJoin(c,n(u.roomId,c.id)).where(w(D(u.entityId,e),n(c.agentId,this.agentId)))).map(r=>r.roomId))}async addParticipant(e,t){return this.withDatabase(async()=>{try{return await this.db.insert(u).values({entityId:e,roomId:t,agentId:this.agentId}).onConflictDoNothing(),!0}catch(r){return o.error("Error adding participant",{error:r instanceof Error?r.message:String(r),entityId:e,roomId:t,agentId:this.agentId}),!1}})}async removeParticipant(e,t){return this.withDatabase(async()=>{try{let i=(await this.db.transaction(async s=>await s.delete(u).where(w(n(u.entityId,e),n(u.roomId,t))).returning())).length>0;return o.debug(`Participant ${i?"removed":"not found"}:`,{entityId:e,roomId:t,removed:i}),i}catch(r){return o.error("Failed to remove participant:",{error:r instanceof Error?r.message:String(r),entityId:e,roomId:t}),!1}})}async getParticipantsForEntity(e){return this.withDatabase(async()=>{let t=await this.db.select({id:u.id,entityId:u.entityId,roomId:u.roomId}).from(u).where(n(u.entityId,e)),r=await this.getEntityById(e);return r?t.map(i=>({id:i.id,entity:r})):[]})}async getParticipantsForRoom(e){return this.withDatabase(async()=>(await this.db.select({entityId:u.entityId}).from(u).where(n(u.roomId,e))).map(r=>r.entityId))}async getParticipantUserState(e,t){return this.withDatabase(async()=>(await this.db.select({roomState:u.roomState}).from(u).where(w(n(u.roomId,e),n(u.entityId,t),n(u.agentId,this.agentId))).limit(1))[0]?.roomState??null)}async setParticipantUserState(e,t,r){return this.withDatabase(async()=>{try{await this.db.transaction(async i=>{await i.update(u).set({roomState:r}).where(w(n(u.roomId,e),n(u.entityId,t),n(u.agentId,this.agentId)))})}catch(i){throw o.error("Failed to set participant user state:",{roomId:e,entityId:t,state:r,error:i instanceof Error?i.message:String(i)}),i}})}async createRelationship(e){return this.withDatabase(async()=>{let r={id:O(),sourceEntityId:e.sourceEntityId,targetEntityId:e.targetEntityId,agentId:this.agentId,tags:e.tags||[],metadata:e.metadata||{}};try{return await this.db.insert(_).values(r),!0}catch(i){return o.error("Error creating relationship:",{error:i instanceof Error?i.message:String(i),saveParams:r}),!1}})}async updateRelationship(e){return this.withDatabase(async()=>{try{await this.db.update(_).set({tags:e.tags||[],metadata:e.metadata||{}}).where(n(_.id,e.id))}catch(t){throw o.error("Error updating relationship:",{error:t instanceof Error?t.message:String(t),relationship:e}),t}})}async getRelationship(e){return this.withDatabase(async()=>{try{let t=await this.db.select().from(_).where(w(n(_.sourceEntityId,e.sourceEntityId),n(_.targetEntityId,e.targetEntityId),n(_.agentId,this.agentId))).limit(1);return t.length===0?null:{id:t[0].id,sourceEntityId:t[0].sourceEntityId,targetEntityId:t[0].targetEntityId,agentId:t[0].agentId,tags:t[0].tags||[],metadata:t[0].metadata||{},createdAt:t[0].createdAt?.toString()}}catch(t){return o.error("Error getting relationship:",{error:t instanceof Error?t.message:String(t),params:e}),null}})}async getRelationships(e){return this.withDatabase(async()=>{try{let t=this.db.select().from(_).where(w(vt(n(_.sourceEntityId,e.entityId),n(_.targetEntityId,e.entityId)),n(_.agentId,this.agentId)));if(e.tags&&e.tags.length>0){let i=e.tags.map(s=>`'${s.replace(/'/g,"''")}'`).join(", ");t=t.where(M`${_.tags} @> ARRAY[${M.raw(i)}]::text[]`)}return(await t).map(i=>({id:i.id,sourceEntityId:i.sourceEntityId,targetEntityId:i.targetEntityId,agentId:i.agentId,tags:i.tags||[],metadata:i.metadata||{},createdAt:i.createdAt?.toString()}))}catch(t){return o.error("Error getting relationships:",{error:t instanceof Error?t.message:String(t),params:e}),[]}})}async getCache(e){return this.withDatabase(async()=>{try{return(await this.db.select().from(S).where(w(n(S.agentId,this.agentId),n(S.key,e))))[0]?.value}catch(t){o.error("Error fetching cache",{error:t instanceof Error?t.message:String(t),key:e,agentId:this.agentId});return}})}async setCache(e,t){return this.withDatabase(async()=>{try{return await this.db.transaction(async r=>{await r.insert(S).values({key:e,agentId:this.agentId,value:t}).onConflictDoUpdate({target:[S.key,S.agentId],set:{value:t}})}),!0}catch(r){return o.error("Error setting cache",{error:r instanceof Error?r.message:String(r),key:e,agentId:this.agentId}),!1}})}async deleteCache(e){return this.withDatabase(async()=>{try{return await this.db.transaction(async t=>{await t.delete(S).where(w(n(S.agentId,this.agentId),n(S.key,e)))}),!0}catch(t){return o.error("Error deleting cache",{error:t instanceof Error?t.message:String(t),key:e,agentId:this.agentId}),!1}})}async createWorld(e){return this.withDatabase(async()=>{let t=e.id||O();return await this.db.insert(E).values({...e,id:t}),t})}async getWorld(e){return this.withDatabase(async()=>(await this.db.select().from(E).where(n(E.id,e)))[0])}async getAllWorlds(){return this.withDatabase(async()=>await this.db.select().from(E).where(n(E.agentId,this.agentId)))}async updateWorld(e){return this.withDatabase(async()=>{await this.db.update(E).set(e).where(n(E.id,e.id))})}async removeWorld(e){return this.withDatabase(async()=>{await this.db.delete(E).where(n(E.id,e))})}async createTask(e){return this.withRetry(async()=>this.withDatabase(async()=>{let t=new Date,r=e.metadata||{},i={id:e.id,name:e.name,description:e.description,roomId:e.roomId,worldId:e.worldId,tags:e.tags,metadata:r,createdAt:t,updatedAt:t,agentId:this.agentId};return(await this.db.insert(P).values(i).returning({id:P.id}))[0].id}))}async getTasks(e){return this.withRetry(async()=>this.withDatabase(async()=>{let t=this.db.select().from(P).where(n(P.agentId,this.agentId));if(e.roomId&&(t=t.where(n(P.roomId,e.roomId))),e.tags&&e.tags.length>0){let i=e.tags.map(s=>`'${s.replace(/'/g,"''")}'`).join(", ");t=t.where(M`${P.tags} @> ARRAY[${M.raw(i)}]::text[]`)}return(await t).map(i=>({id:i.id,name:i.name,description:i.description,roomId:i.roomId,worldId:i.worldId,tags:i.tags,metadata:i.metadata}))}))}async getTasksByName(e){return this.withRetry(async()=>this.withDatabase(async()=>(await this.db.select().from(P).where(w(n(P.name,e),n(P.agentId,this.agentId)))).map(r=>({id:r.id,name:r.name,description:r.description,roomId:r.roomId,worldId:r.worldId,tags:r.tags||[],metadata:r.metadata||{}}))))}async getTask(e){return this.withRetry(async()=>this.withDatabase(async()=>{let t=await this.db.select().from(P).where(w(n(P.id,e),n(P.agentId,this.agentId))).limit(1);if(t.length===0)return null;let r=t[0];return{id:r.id,name:r.name,description:r.description,roomId:r.roomId,worldId:r.worldId,tags:r.tags||[],metadata:r.metadata||{}}}))}async updateTask(e,t){await this.withRetry(async()=>{await this.withDatabase(async()=>{let r={};if(t.name!==void 0&&(r.name=t.name),t.description!==void 0&&(r.description=t.description),t.roomId!==void 0&&(r.roomId=t.roomId),t.worldId!==void 0&&(r.worldId=t.worldId),t.tags!==void 0&&(r.tags=t.tags),t.updatedAt=Date.now(),t.metadata){let i=await this.getTask(e);if(i){let d={...i.metadata||{},...t.metadata};r.metadata=d}else r.metadata={...t.metadata}}await this.db.update(P).set(r).where(w(n(P.id,e),n(P.agentId,this.agentId)))})})}async deleteTask(e){await this.withRetry(async()=>{await this.withDatabase(async()=>{await this.db.delete(P).where(w(n(P.id,e),n(P.agentId,this.agentId)))})})}async getMemoriesByServerId(e){return this.withDatabase(async()=>{let t=await this.db.select({roomId:c.id}).from(c).where(n(c.serverId,e.serverId));if(t.length===0)return[];let r=t.map(d=>d.roomId),i=this.db.select({memory:a,embedding:l[this.embeddingDimension]}).from(a).leftJoin(l,n(l.memoryId,a.id)).where(D(a.roomId,r)).orderBy(z(a.createdAt));return(e.count?await i.limit(e.count):await i).map(d=>({id:d.memory.id,type:d.memory.type,createdAt:d.memory.createdAt,content:typeof d.memory.content=="string"?JSON.parse(d.memory.content):d.memory.content,entityId:d.memory.entityId,agentId:d.memory.agentId,roomId:d.memory.roomId,unique:d.memory.unique,embedding:d.embedding??void 0}))})}async deleteRoomsByServerId(e){return this.withDatabase(async()=>{await this.db.transaction(async t=>{let r=await t.select({roomId:c.id}).from(c).where(n(c.serverId,e));if(r.length===0)return;let i=r.map(s=>s.roomId);await t.delete(l).where(D(l.memoryId,t.select({id:a.id}).from(a).where(D(a.roomId,i)))),await t.delete(a).where(D(a.roomId,i)),await t.delete(u).where(D(u.roomId,i)),await t.delete(N).where(D(N.roomId,i)),await t.delete(c).where(D(c.id,i))}),o.debug("Rooms and related logs deleted successfully for server:",{serverId:e})})}};x(de,"BaseDrizzleAdapter");var J=de;var me=class me extends J{constructor(e,t){super(e);this.embeddingDimension=L[384];this.manager=t,this.db=Bt(this.manager.getConnection())}async withDatabase(e){return this.manager.isShuttingDown()?(Ce.warn("Database is shutting down"),null):e()}async init(){try{await this.manager.runMigrations()}catch(e){throw Ce.error("Failed to initialize database:",e),e}}async close(){await this.manager.close()}};x(me,"PgliteDatabaseAdapter");var ee=me;import{logger as $e}from"@elizaos/core";import{drizzle as Rt}from"drizzle-orm/node-postgres";var ce=class ce extends J{constructor(e,t){super(e);this.manager=t;this.embeddingDimension=L[384];this.manager=t}async withDatabase(e){return await this.withRetry(async()=>{let t=await this.manager.getClient();try{let r=Rt(t);return this.db=r,await e()}finally{t.release()}})}async init(){try{await this.manager.runMigrations(),$e.debug("PgDatabaseAdapter initialized successfully")}catch(e){throw $e.error("Failed to initialize PgDatabaseAdapter:",e),e}}async close(){await this.manager.close()}};x(ce,"PgDatabaseAdapter");var te=ce;var le=Symbol.for("@elizaos/plugin-sql/global-singletons"),ue=global;ue[le]||(ue[le]={});var W=ue[le];function xt(m){return m&&typeof m=="string"&&m.startsWith("~")?m.replace(/^~/,ke.homedir()):m}x(xt,"expandTildePath");function Ct(m,B){if(m.dataDir&&(m.dataDir=xt(m.dataDir)),m.postgresUrl)return W.postgresConnectionManager||(W.postgresConnectionManager=new ye(m.postgresUrl)),new te(B,W.postgresConnectionManager);let e=m.dataDir??"./elizadb";return W.pgLiteClientManager||(W.pgLiteClientManager=new ge({dataDir:e})),new ee(B,W.pgLiteClientManager)}x(Ct,"createDatabaseAdapter");var $t={name:"sql",description:"SQL database adapter plugin using Drizzle ORM",init:x(async(m,B)=>{let e={dataDir:B.getSetting("PGLITE_DATA_DIR")??"./pglite",postgresUrl:B.getSetting("POSTGRES_URL")};try{let t=Ct(e,B.agentId);Le.success("Database connection established successfully"),B.registerDatabaseAdapter(t)}catch(t){throw Le.error("Failed to initialize database:",t),t}},"init")},Wn=$t;export{Ct as createDatabaseAdapter,Wn as default};
|
|
1243
|
+
LIMIT ${opts.query_match_count}
|
|
1244
|
+
`);
|
|
1245
|
+
return results.rows.map((row) => ({
|
|
1246
|
+
embedding: Array.isArray(row.embedding) ? row.embedding : typeof row.embedding === "string" ? JSON.parse(row.embedding) : [],
|
|
1247
|
+
levenshtein_score: Number(row.levenshtein_score)
|
|
1248
|
+
})).filter((row) => Array.isArray(row.embedding));
|
|
1249
|
+
} catch (error) {
|
|
1250
|
+
logger.error("Error in getCachedEmbeddings:", {
|
|
1251
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1252
|
+
tableName: opts.query_table_name,
|
|
1253
|
+
fieldName: opts.query_field_name
|
|
1254
|
+
});
|
|
1255
|
+
if (error instanceof Error && error.message === "levenshtein argument exceeds maximum length of 255 characters") {
|
|
1256
|
+
return [];
|
|
1257
|
+
}
|
|
1258
|
+
throw error;
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Asynchronously logs an event in the database.
|
|
1264
|
+
* @param {Object} params - The parameters for logging an event.
|
|
1265
|
+
* @param {Object} params.body - The body of the event to log.
|
|
1266
|
+
* @param {UUID} params.entityId - The ID of the entity associated with the event.
|
|
1267
|
+
* @param {UUID} params.roomId - The ID of the room associated with the event.
|
|
1268
|
+
* @param {string} params.type - The type of the event to log.
|
|
1269
|
+
* @returns {Promise<void>} A Promise that resolves when the event is logged.
|
|
1270
|
+
*/
|
|
1271
|
+
async log(params) {
|
|
1272
|
+
return this.withDatabase(async () => {
|
|
1273
|
+
try {
|
|
1274
|
+
await this.db.transaction(async (tx) => {
|
|
1275
|
+
await tx.insert(logTable).values({
|
|
1276
|
+
body: sql12`${params.body}::jsonb`,
|
|
1277
|
+
entityId: params.entityId,
|
|
1278
|
+
roomId: params.roomId,
|
|
1279
|
+
type: params.type
|
|
1280
|
+
});
|
|
1281
|
+
});
|
|
1282
|
+
} catch (error) {
|
|
1283
|
+
logger.error("Failed to create log entry:", {
|
|
1284
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1285
|
+
type: params.type,
|
|
1286
|
+
roomId: params.roomId,
|
|
1287
|
+
entityId: params.entityId
|
|
1288
|
+
});
|
|
1289
|
+
throw error;
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Asynchronously retrieves logs from the database based on the provided parameters.
|
|
1295
|
+
* @param {Object} params - The parameters for retrieving logs.
|
|
1296
|
+
* @param {UUID} params.entityId - The ID of the entity associated with the logs.
|
|
1297
|
+
* @param {UUID} [params.roomId] - The ID of the room associated with the logs.
|
|
1298
|
+
* @param {string} [params.type] - The type of the logs to retrieve.
|
|
1299
|
+
* @param {number} [params.count] - The maximum number of logs to retrieve.
|
|
1300
|
+
* @param {number} [params.offset] - The offset to retrieve logs from.
|
|
1301
|
+
* @returns {Promise<Log[]>} A Promise that resolves to an array of logs.
|
|
1302
|
+
*/
|
|
1303
|
+
async getLogs(params) {
|
|
1304
|
+
const { entityId, roomId, type, count: count2, offset } = params;
|
|
1305
|
+
return this.withDatabase(async () => {
|
|
1306
|
+
const result = await this.db.select().from(logTable).where(
|
|
1307
|
+
and(
|
|
1308
|
+
eq(logTable.entityId, entityId),
|
|
1309
|
+
roomId ? eq(logTable.roomId, roomId) : void 0,
|
|
1310
|
+
type ? eq(logTable.type, type) : void 0
|
|
1311
|
+
)
|
|
1312
|
+
).orderBy(desc(logTable.createdAt)).limit(count2 ?? 10).offset(offset ?? 0);
|
|
1313
|
+
return result;
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Asynchronously deletes a log from the database based on the provided parameters.
|
|
1318
|
+
* @param {UUID} logId - The ID of the log to delete.
|
|
1319
|
+
* @returns {Promise<void>} A Promise that resolves when the log is deleted.
|
|
1320
|
+
*/
|
|
1321
|
+
async deleteLog(logId) {
|
|
1322
|
+
return this.withDatabase(async () => {
|
|
1323
|
+
await this.db.delete(logTable).where(eq(logTable.id, logId));
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Asynchronously searches for memories in the database based on the provided parameters.
|
|
1328
|
+
* @param {Object} params - The parameters for searching for memories.
|
|
1329
|
+
* @param {string} params.tableName - The name of the table to search for memories in.
|
|
1330
|
+
* @param {number[]} params.embedding - The embedding to search for.
|
|
1331
|
+
* @param {number} [params.match_threshold] - The threshold for the cosine distance.
|
|
1332
|
+
* @param {number} [params.count] - The maximum number of memories to retrieve.
|
|
1333
|
+
* @param {boolean} [params.unique] - Whether to retrieve unique memories only.
|
|
1334
|
+
* @param {string} [params.query] - Optional query string for potential reranking.
|
|
1335
|
+
* @param {UUID} [params.roomId] - Optional room ID to filter by.
|
|
1336
|
+
* @param {UUID} [params.worldId] - Optional world ID to filter by.
|
|
1337
|
+
* @param {UUID} [params.entityId] - Optional entity ID to filter by.
|
|
1338
|
+
* @returns {Promise<Memory[]>} A Promise that resolves to an array of memories.
|
|
1339
|
+
*/
|
|
1340
|
+
async searchMemories(params) {
|
|
1341
|
+
return await this.searchMemoriesByEmbedding(params.embedding, {
|
|
1342
|
+
match_threshold: params.match_threshold,
|
|
1343
|
+
count: params.count,
|
|
1344
|
+
// Pass direct scope fields down
|
|
1345
|
+
roomId: params.roomId,
|
|
1346
|
+
worldId: params.worldId,
|
|
1347
|
+
entityId: params.entityId,
|
|
1348
|
+
unique: params.unique,
|
|
1349
|
+
tableName: params.tableName
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Asynchronously searches for memories in the database based on the provided parameters.
|
|
1354
|
+
* @param {number[]} embedding - The embedding to search for.
|
|
1355
|
+
* @param {Object} params - The parameters for searching for memories.
|
|
1356
|
+
* @param {number} [params.match_threshold] - The threshold for the cosine distance.
|
|
1357
|
+
* @param {number} [params.count] - The maximum number of memories to retrieve.
|
|
1358
|
+
* @param {UUID} [params.roomId] - Optional room ID to filter by.
|
|
1359
|
+
* @param {UUID} [params.worldId] - Optional world ID to filter by.
|
|
1360
|
+
* @param {UUID} [params.entityId] - Optional entity ID to filter by.
|
|
1361
|
+
* @param {boolean} [params.unique] - Whether to retrieve unique memories only.
|
|
1362
|
+
* @param {string} [params.tableName] - The name of the table to search for memories in.
|
|
1363
|
+
* @returns {Promise<Memory[]>} A Promise that resolves to an array of memories.
|
|
1364
|
+
*/
|
|
1365
|
+
async searchMemoriesByEmbedding(embedding, params) {
|
|
1366
|
+
return this.withDatabase(async () => {
|
|
1367
|
+
const cleanVector = embedding.map((n) => Number.isFinite(n) ? Number(n.toFixed(6)) : 0);
|
|
1368
|
+
const similarity = sql12`1 - (${cosineDistance(
|
|
1369
|
+
embeddingTable[this.embeddingDimension],
|
|
1370
|
+
cleanVector
|
|
1371
|
+
)})`;
|
|
1372
|
+
const conditions = [eq(memoryTable.type, params.tableName)];
|
|
1373
|
+
if (params.unique) {
|
|
1374
|
+
conditions.push(eq(memoryTable.unique, true));
|
|
1375
|
+
}
|
|
1376
|
+
conditions.push(eq(memoryTable.agentId, this.agentId));
|
|
1377
|
+
if (params.roomId) {
|
|
1378
|
+
conditions.push(eq(memoryTable.roomId, params.roomId));
|
|
1379
|
+
}
|
|
1380
|
+
if (params.worldId) {
|
|
1381
|
+
conditions.push(eq(memoryTable.worldId, params.worldId));
|
|
1382
|
+
}
|
|
1383
|
+
if (params.entityId) {
|
|
1384
|
+
conditions.push(eq(memoryTable.entityId, params.entityId));
|
|
1385
|
+
}
|
|
1386
|
+
if (params.match_threshold) {
|
|
1387
|
+
conditions.push(gte(similarity, params.match_threshold));
|
|
1388
|
+
}
|
|
1389
|
+
const results = await this.db.select({
|
|
1390
|
+
memory: memoryTable,
|
|
1391
|
+
similarity,
|
|
1392
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
1393
|
+
}).from(embeddingTable).innerJoin(memoryTable, eq(memoryTable.id, embeddingTable.memoryId)).where(and(...conditions)).orderBy(desc(similarity)).limit(params.count ?? 10);
|
|
1394
|
+
return results.map((row) => ({
|
|
1395
|
+
id: row.memory.id,
|
|
1396
|
+
type: row.memory.type,
|
|
1397
|
+
createdAt: row.memory.createdAt,
|
|
1398
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
1399
|
+
entityId: row.memory.entityId,
|
|
1400
|
+
agentId: row.memory.agentId,
|
|
1401
|
+
roomId: row.memory.roomId,
|
|
1402
|
+
worldId: row.memory.worldId,
|
|
1403
|
+
// Include worldId
|
|
1404
|
+
unique: row.memory.unique,
|
|
1405
|
+
metadata: row.memory.metadata,
|
|
1406
|
+
embedding: row.embedding ?? void 0,
|
|
1407
|
+
similarity: row.similarity
|
|
1408
|
+
}));
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Asynchronously creates a new memory in the database.
|
|
1413
|
+
* @param {Memory & { metadata?: MemoryMetadata }} memory - The memory object to create.
|
|
1414
|
+
* @param {string} tableName - The name of the table to create the memory in.
|
|
1415
|
+
* @returns {Promise<UUID>} A Promise that resolves to the ID of the created memory.
|
|
1416
|
+
*/
|
|
1417
|
+
async createMemory(memory, tableName) {
|
|
1418
|
+
logger.debug("DrizzleAdapter createMemory:", {
|
|
1419
|
+
memoryId: memory.id,
|
|
1420
|
+
embeddingLength: memory.embedding?.length,
|
|
1421
|
+
contentLength: memory.content?.text?.length
|
|
1422
|
+
});
|
|
1423
|
+
const memoryId = memory.id ?? v4();
|
|
1424
|
+
const existing = await this.getMemoryById(memoryId);
|
|
1425
|
+
if (existing) {
|
|
1426
|
+
logger.debug("Memory already exists, skipping creation:", {
|
|
1427
|
+
memoryId
|
|
1428
|
+
});
|
|
1429
|
+
return memoryId;
|
|
1430
|
+
}
|
|
1431
|
+
let isUnique = true;
|
|
1432
|
+
if (memory.embedding && Array.isArray(memory.embedding)) {
|
|
1433
|
+
const similarMemories = await this.searchMemoriesByEmbedding(memory.embedding, {
|
|
1434
|
+
tableName,
|
|
1435
|
+
// Use the scope fields from the memory object for similarity check
|
|
1436
|
+
roomId: memory.roomId,
|
|
1437
|
+
worldId: memory.worldId,
|
|
1438
|
+
entityId: memory.entityId,
|
|
1439
|
+
match_threshold: 0.95,
|
|
1440
|
+
count: 1
|
|
1441
|
+
});
|
|
1442
|
+
isUnique = similarMemories.length === 0;
|
|
1443
|
+
}
|
|
1444
|
+
const contentToInsert = typeof memory.content === "string" ? JSON.parse(memory.content) : memory.content;
|
|
1445
|
+
await this.db.transaction(async (tx) => {
|
|
1446
|
+
await tx.insert(memoryTable).values([
|
|
1447
|
+
{
|
|
1448
|
+
id: memoryId,
|
|
1449
|
+
type: tableName,
|
|
1450
|
+
content: sql12`${contentToInsert}::jsonb`,
|
|
1451
|
+
metadata: sql12`${memory.metadata || {}}::jsonb`,
|
|
1452
|
+
entityId: memory.entityId,
|
|
1453
|
+
roomId: memory.roomId,
|
|
1454
|
+
worldId: memory.worldId,
|
|
1455
|
+
// Include worldId
|
|
1456
|
+
agentId: memory.agentId,
|
|
1457
|
+
unique: memory.unique ?? isUnique,
|
|
1458
|
+
createdAt: memory.createdAt
|
|
1459
|
+
}
|
|
1460
|
+
]);
|
|
1461
|
+
if (memory.embedding && Array.isArray(memory.embedding)) {
|
|
1462
|
+
const embeddingValues = {
|
|
1463
|
+
id: v4(),
|
|
1464
|
+
memoryId,
|
|
1465
|
+
createdAt: memory.createdAt
|
|
1466
|
+
};
|
|
1467
|
+
const cleanVector = memory.embedding.map(
|
|
1468
|
+
(n) => Number.isFinite(n) ? Number(n.toFixed(6)) : 0
|
|
1469
|
+
);
|
|
1470
|
+
embeddingValues[this.embeddingDimension] = cleanVector;
|
|
1471
|
+
await tx.insert(embeddingTable).values([embeddingValues]);
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
return memoryId;
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Updates an existing memory in the database.
|
|
1478
|
+
* @param memory The memory object with updated content and optional embedding
|
|
1479
|
+
* @returns Promise resolving to boolean indicating success
|
|
1480
|
+
*/
|
|
1481
|
+
async updateMemory(memory) {
|
|
1482
|
+
return this.withDatabase(async () => {
|
|
1483
|
+
try {
|
|
1484
|
+
logger.debug("Updating memory:", {
|
|
1485
|
+
memoryId: memory.id,
|
|
1486
|
+
hasEmbedding: !!memory.embedding
|
|
1487
|
+
});
|
|
1488
|
+
await this.db.transaction(async (tx) => {
|
|
1489
|
+
if (memory.content) {
|
|
1490
|
+
const contentToUpdate = typeof memory.content === "string" ? JSON.parse(memory.content) : memory.content;
|
|
1491
|
+
await tx.update(memoryTable).set({
|
|
1492
|
+
content: sql12`${contentToUpdate}::jsonb`,
|
|
1493
|
+
...memory.metadata && { metadata: sql12`${memory.metadata}::jsonb` }
|
|
1494
|
+
}).where(eq(memoryTable.id, memory.id));
|
|
1495
|
+
} else if (memory.metadata) {
|
|
1496
|
+
await tx.update(memoryTable).set({
|
|
1497
|
+
metadata: sql12`${memory.metadata}::jsonb`
|
|
1498
|
+
}).where(eq(memoryTable.id, memory.id));
|
|
1499
|
+
}
|
|
1500
|
+
if (memory.embedding && Array.isArray(memory.embedding)) {
|
|
1501
|
+
const cleanVector = memory.embedding.map(
|
|
1502
|
+
(n) => Number.isFinite(n) ? Number(n.toFixed(6)) : 0
|
|
1503
|
+
);
|
|
1504
|
+
const existingEmbedding = await tx.select({ id: embeddingTable.id }).from(embeddingTable).where(eq(embeddingTable.memoryId, memory.id)).limit(1);
|
|
1505
|
+
if (existingEmbedding.length > 0) {
|
|
1506
|
+
const updateValues = {};
|
|
1507
|
+
updateValues[this.embeddingDimension] = cleanVector;
|
|
1508
|
+
await tx.update(embeddingTable).set(updateValues).where(eq(embeddingTable.memoryId, memory.id));
|
|
1509
|
+
} else {
|
|
1510
|
+
const embeddingValues = {
|
|
1511
|
+
id: v4(),
|
|
1512
|
+
memoryId: memory.id,
|
|
1513
|
+
createdAt: Date.now()
|
|
1514
|
+
};
|
|
1515
|
+
embeddingValues[this.embeddingDimension] = cleanVector;
|
|
1516
|
+
await tx.insert(embeddingTable).values([embeddingValues]);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
logger.debug("Memory updated successfully:", {
|
|
1521
|
+
memoryId: memory.id
|
|
1522
|
+
});
|
|
1523
|
+
return true;
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
logger.error("Error updating memory:", {
|
|
1526
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1527
|
+
memoryId: memory.id
|
|
1528
|
+
});
|
|
1529
|
+
return false;
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Asynchronously deletes a memory from the database based on the provided parameters.
|
|
1535
|
+
* @param {UUID} memoryId - The ID of the memory to delete.
|
|
1536
|
+
* @returns {Promise<void>} A Promise that resolves when the memory is deleted.
|
|
1537
|
+
*/
|
|
1538
|
+
async deleteMemory(memoryId) {
|
|
1539
|
+
return this.withDatabase(async () => {
|
|
1540
|
+
await this.db.transaction(async (tx) => {
|
|
1541
|
+
await this.deleteMemoryFragments(tx, memoryId);
|
|
1542
|
+
await tx.delete(embeddingTable).where(eq(embeddingTable.memoryId, memoryId));
|
|
1543
|
+
await tx.delete(memoryTable).where(eq(memoryTable.id, memoryId));
|
|
1544
|
+
});
|
|
1545
|
+
logger.debug("Memory and related fragments removed successfully:", {
|
|
1546
|
+
memoryId
|
|
1547
|
+
});
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Deletes all memory fragments that reference a specific document memory
|
|
1552
|
+
* @param tx The database transaction
|
|
1553
|
+
* @param documentId The UUID of the document memory whose fragments should be deleted
|
|
1554
|
+
* @private
|
|
1555
|
+
*/
|
|
1556
|
+
async deleteMemoryFragments(tx, documentId) {
|
|
1557
|
+
const fragmentsToDelete = await this.getMemoryFragments(tx, documentId);
|
|
1558
|
+
if (fragmentsToDelete.length > 0) {
|
|
1559
|
+
const fragmentIds = fragmentsToDelete.map((f) => f.id);
|
|
1560
|
+
await tx.delete(embeddingTable).where(inArray(embeddingTable.memoryId, fragmentIds));
|
|
1561
|
+
await tx.delete(memoryTable).where(inArray(memoryTable.id, fragmentIds));
|
|
1562
|
+
logger.debug("Deleted related fragments:", {
|
|
1563
|
+
documentId,
|
|
1564
|
+
fragmentCount: fragmentsToDelete.length
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Retrieves all memory fragments that reference a specific document memory
|
|
1570
|
+
* @param tx The database transaction
|
|
1571
|
+
* @param documentId The UUID of the document memory whose fragments should be retrieved
|
|
1572
|
+
* @returns An array of memory fragments
|
|
1573
|
+
* @private
|
|
1574
|
+
*/
|
|
1575
|
+
async getMemoryFragments(tx, documentId) {
|
|
1576
|
+
const fragments = await tx.select({ id: memoryTable.id }).from(memoryTable).where(
|
|
1577
|
+
and(
|
|
1578
|
+
eq(memoryTable.agentId, this.agentId),
|
|
1579
|
+
sql12`${memoryTable.metadata}->>'documentId' = ${documentId}`
|
|
1580
|
+
)
|
|
1581
|
+
);
|
|
1582
|
+
return fragments;
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* Asynchronously deletes all memories from the database based on the provided parameters.
|
|
1586
|
+
* @param {UUID} roomId - The ID of the room to delete memories from.
|
|
1587
|
+
* @param {string} tableName - The name of the table to delete memories from.
|
|
1588
|
+
* @returns {Promise<void>} A Promise that resolves when the memories are deleted.
|
|
1589
|
+
*/
|
|
1590
|
+
async deleteAllMemories(roomId, tableName) {
|
|
1591
|
+
return this.withDatabase(async () => {
|
|
1592
|
+
await this.db.transaction(async (tx) => {
|
|
1593
|
+
const memoryIds = await tx.select({ id: memoryTable.id }).from(memoryTable).where(and(eq(memoryTable.roomId, roomId), eq(memoryTable.type, tableName)));
|
|
1594
|
+
if (memoryIds.length > 0) {
|
|
1595
|
+
await tx.delete(embeddingTable).where(
|
|
1596
|
+
inArray(
|
|
1597
|
+
embeddingTable.memoryId,
|
|
1598
|
+
memoryIds.map((m) => m.id)
|
|
1599
|
+
)
|
|
1600
|
+
);
|
|
1601
|
+
await tx.delete(memoryTable).where(and(eq(memoryTable.roomId, roomId), eq(memoryTable.type, tableName)));
|
|
1602
|
+
}
|
|
1603
|
+
});
|
|
1604
|
+
logger.debug("All memories removed successfully:", {
|
|
1605
|
+
roomId,
|
|
1606
|
+
tableName
|
|
1607
|
+
});
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Asynchronously counts the number of memories in the database based on the provided parameters.
|
|
1612
|
+
* @param {UUID} roomId - The ID of the room to count memories in.
|
|
1613
|
+
* @param {boolean} [unique] - Whether to count unique memories only.
|
|
1614
|
+
* @param {string} [tableName] - The name of the table to count memories in.
|
|
1615
|
+
* @returns {Promise<number>} A Promise that resolves to the number of memories.
|
|
1616
|
+
*/
|
|
1617
|
+
async countMemories(roomId, unique7 = true, tableName = "") {
|
|
1618
|
+
if (!tableName) throw new Error("tableName is required");
|
|
1619
|
+
return this.withDatabase(async () => {
|
|
1620
|
+
const conditions = [eq(memoryTable.roomId, roomId), eq(memoryTable.type, tableName)];
|
|
1621
|
+
if (unique7) {
|
|
1622
|
+
conditions.push(eq(memoryTable.unique, true));
|
|
1623
|
+
}
|
|
1624
|
+
const result = await this.db.select({ count: sql12`count(*)` }).from(memoryTable).where(and(...conditions));
|
|
1625
|
+
return Number(result[0]?.count ?? 0);
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Asynchronously retrieves a room from the database based on the provided parameters.
|
|
1630
|
+
* @param {UUID} roomId - The ID of the room to retrieve.
|
|
1631
|
+
* @returns {Promise<Room | null>} A Promise that resolves to the room if found, null otherwise.
|
|
1632
|
+
*/
|
|
1633
|
+
async getRoom(roomId) {
|
|
1634
|
+
return this.withDatabase(async () => {
|
|
1635
|
+
const result = await this.db.select({
|
|
1636
|
+
id: roomTable.id,
|
|
1637
|
+
channelId: roomTable.channelId,
|
|
1638
|
+
agentId: roomTable.agentId,
|
|
1639
|
+
serverId: roomTable.serverId,
|
|
1640
|
+
worldId: roomTable.worldId,
|
|
1641
|
+
type: roomTable.type,
|
|
1642
|
+
source: roomTable.source
|
|
1643
|
+
}).from(roomTable).where(and(eq(roomTable.id, roomId), eq(roomTable.agentId, this.agentId))).limit(1);
|
|
1644
|
+
if (result.length === 0) return null;
|
|
1645
|
+
return result[0];
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Asynchronously retrieves all rooms from the database based on the provided parameters.
|
|
1650
|
+
* @param {UUID} worldId - The ID of the world to retrieve rooms from.
|
|
1651
|
+
* @returns {Promise<Room[]>} A Promise that resolves to an array of rooms.
|
|
1652
|
+
*/
|
|
1653
|
+
async getRooms(worldId) {
|
|
1654
|
+
return this.withDatabase(async () => {
|
|
1655
|
+
const result = await this.db.select().from(roomTable).where(eq(roomTable.worldId, worldId));
|
|
1656
|
+
return result;
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
/**
|
|
1660
|
+
* Asynchronously updates a room in the database based on the provided parameters.
|
|
1661
|
+
* @param {Room} room - The room object to update.
|
|
1662
|
+
* @returns {Promise<void>} A Promise that resolves when the room is updated.
|
|
1663
|
+
*/
|
|
1664
|
+
async updateRoom(room) {
|
|
1665
|
+
return this.withDatabase(async () => {
|
|
1666
|
+
await this.db.update(roomTable).set({ ...room, agentId: this.agentId }).where(eq(roomTable.id, room.id));
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Asynchronously creates a new room in the database based on the provided parameters.
|
|
1671
|
+
* @param {Room} room - The room object to create.
|
|
1672
|
+
* @returns {Promise<UUID>} A Promise that resolves to the ID of the created room.
|
|
1673
|
+
*/
|
|
1674
|
+
async createRoom({
|
|
1675
|
+
id,
|
|
1676
|
+
name,
|
|
1677
|
+
agentId,
|
|
1678
|
+
source,
|
|
1679
|
+
type,
|
|
1680
|
+
channelId,
|
|
1681
|
+
serverId,
|
|
1682
|
+
worldId,
|
|
1683
|
+
metadata
|
|
1684
|
+
}) {
|
|
1685
|
+
return this.withDatabase(async () => {
|
|
1686
|
+
const newRoomId = id || v4();
|
|
1687
|
+
await this.db.insert(roomTable).values({
|
|
1688
|
+
id: newRoomId,
|
|
1689
|
+
name,
|
|
1690
|
+
agentId,
|
|
1691
|
+
source,
|
|
1692
|
+
type,
|
|
1693
|
+
channelId,
|
|
1694
|
+
serverId,
|
|
1695
|
+
worldId,
|
|
1696
|
+
metadata
|
|
1697
|
+
}).onConflictDoNothing({ target: roomTable.id });
|
|
1698
|
+
return newRoomId;
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* Asynchronously deletes a room from the database based on the provided parameters.
|
|
1703
|
+
* @param {UUID} roomId - The ID of the room to delete.
|
|
1704
|
+
* @returns {Promise<void>} A Promise that resolves when the room is deleted.
|
|
1705
|
+
*/
|
|
1706
|
+
async deleteRoom(roomId) {
|
|
1707
|
+
if (!roomId) throw new Error("Room ID is required");
|
|
1708
|
+
return this.withDatabase(async () => {
|
|
1709
|
+
await this.db.transaction(async (tx) => {
|
|
1710
|
+
await tx.delete(roomTable).where(eq(roomTable.id, roomId));
|
|
1711
|
+
});
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Asynchronously retrieves all rooms for a participant from the database based on the provided parameters.
|
|
1716
|
+
* @param {UUID} entityId - The ID of the entity to retrieve rooms for.
|
|
1717
|
+
* @returns {Promise<UUID[]>} A Promise that resolves to an array of room IDs.
|
|
1718
|
+
*/
|
|
1719
|
+
async getRoomsForParticipant(entityId) {
|
|
1720
|
+
return this.withDatabase(async () => {
|
|
1721
|
+
const result = await this.db.select({ roomId: participantTable.roomId }).from(participantTable).innerJoin(roomTable, eq(participantTable.roomId, roomTable.id)).where(and(eq(participantTable.entityId, entityId), eq(roomTable.agentId, this.agentId)));
|
|
1722
|
+
return result.map((row) => row.roomId);
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* Asynchronously retrieves all rooms for a list of participants from the database based on the provided parameters.
|
|
1727
|
+
* @param {UUID[]} entityIds - The IDs of the entities to retrieve rooms for.
|
|
1728
|
+
* @returns {Promise<UUID[]>} A Promise that resolves to an array of room IDs.
|
|
1729
|
+
*/
|
|
1730
|
+
async getRoomsForParticipants(entityIds) {
|
|
1731
|
+
return this.withDatabase(async () => {
|
|
1732
|
+
const result = await this.db.selectDistinct({ roomId: participantTable.roomId }).from(participantTable).innerJoin(roomTable, eq(participantTable.roomId, roomTable.id)).where(
|
|
1733
|
+
and(inArray(participantTable.entityId, entityIds), eq(roomTable.agentId, this.agentId))
|
|
1734
|
+
);
|
|
1735
|
+
return result.map((row) => row.roomId);
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Asynchronously adds a participant to a room in the database based on the provided parameters.
|
|
1740
|
+
* @param {UUID} entityId - The ID of the entity to add to the room.
|
|
1741
|
+
* @param {UUID} roomId - The ID of the room to add the entity to.
|
|
1742
|
+
* @returns {Promise<boolean>} A Promise that resolves to a boolean indicating whether the participant was added successfully.
|
|
1743
|
+
*/
|
|
1744
|
+
async addParticipant(entityId, roomId) {
|
|
1745
|
+
return this.withDatabase(async () => {
|
|
1746
|
+
try {
|
|
1747
|
+
await this.db.insert(participantTable).values({
|
|
1748
|
+
entityId,
|
|
1749
|
+
roomId,
|
|
1750
|
+
agentId: this.agentId
|
|
1751
|
+
}).onConflictDoNothing();
|
|
1752
|
+
return true;
|
|
1753
|
+
} catch (error) {
|
|
1754
|
+
logger.error("Error adding participant", {
|
|
1755
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1756
|
+
entityId,
|
|
1757
|
+
roomId,
|
|
1758
|
+
agentId: this.agentId
|
|
1759
|
+
});
|
|
1760
|
+
return false;
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Asynchronously removes a participant from a room in the database based on the provided parameters.
|
|
1766
|
+
* @param {UUID} entityId - The ID of the entity to remove from the room.
|
|
1767
|
+
* @param {UUID} roomId - The ID of the room to remove the entity from.
|
|
1768
|
+
* @returns {Promise<boolean>} A Promise that resolves to a boolean indicating whether the participant was removed successfully.
|
|
1769
|
+
*/
|
|
1770
|
+
async removeParticipant(entityId, roomId) {
|
|
1771
|
+
return this.withDatabase(async () => {
|
|
1772
|
+
try {
|
|
1773
|
+
const result = await this.db.transaction(async (tx) => {
|
|
1774
|
+
return await tx.delete(participantTable).where(
|
|
1775
|
+
and(eq(participantTable.entityId, entityId), eq(participantTable.roomId, roomId))
|
|
1776
|
+
).returning();
|
|
1777
|
+
});
|
|
1778
|
+
const removed = result.length > 0;
|
|
1779
|
+
logger.debug(`Participant ${removed ? "removed" : "not found"}:`, {
|
|
1780
|
+
entityId,
|
|
1781
|
+
roomId,
|
|
1782
|
+
removed
|
|
1783
|
+
});
|
|
1784
|
+
return removed;
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
logger.error("Failed to remove participant:", {
|
|
1787
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1788
|
+
entityId,
|
|
1789
|
+
roomId
|
|
1790
|
+
});
|
|
1791
|
+
return false;
|
|
1792
|
+
}
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Asynchronously retrieves all participants for an entity from the database based on the provided parameters.
|
|
1797
|
+
* @param {UUID} entityId - The ID of the entity to retrieve participants for.
|
|
1798
|
+
* @returns {Promise<Participant[]>} A Promise that resolves to an array of participants.
|
|
1799
|
+
*/
|
|
1800
|
+
async getParticipantsForEntity(entityId) {
|
|
1801
|
+
return this.withDatabase(async () => {
|
|
1802
|
+
const result = await this.db.select({
|
|
1803
|
+
id: participantTable.id,
|
|
1804
|
+
entityId: participantTable.entityId,
|
|
1805
|
+
roomId: participantTable.roomId
|
|
1806
|
+
}).from(participantTable).where(eq(participantTable.entityId, entityId));
|
|
1807
|
+
const entity = await this.getEntityById(entityId);
|
|
1808
|
+
if (!entity) {
|
|
1809
|
+
return [];
|
|
1810
|
+
}
|
|
1811
|
+
return result.map((row) => ({
|
|
1812
|
+
id: row.id,
|
|
1813
|
+
entity
|
|
1814
|
+
}));
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Asynchronously retrieves all participants for a room from the database based on the provided parameters.
|
|
1819
|
+
* @param {UUID} roomId - The ID of the room to retrieve participants for.
|
|
1820
|
+
* @returns {Promise<UUID[]>} A Promise that resolves to an array of entity IDs.
|
|
1821
|
+
*/
|
|
1822
|
+
async getParticipantsForRoom(roomId) {
|
|
1823
|
+
return this.withDatabase(async () => {
|
|
1824
|
+
const result = await this.db.select({ entityId: participantTable.entityId }).from(participantTable).where(eq(participantTable.roomId, roomId));
|
|
1825
|
+
return result.map((row) => row.entityId);
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Asynchronously retrieves the user state for a participant in a room from the database based on the provided parameters.
|
|
1830
|
+
* @param {UUID} roomId - The ID of the room to retrieve the participant's user state for.
|
|
1831
|
+
* @param {UUID} entityId - The ID of the entity to retrieve the user state for.
|
|
1832
|
+
* @returns {Promise<"FOLLOWED" | "MUTED" | null>} A Promise that resolves to the participant's user state.
|
|
1833
|
+
*/
|
|
1834
|
+
async getParticipantUserState(roomId, entityId) {
|
|
1835
|
+
return this.withDatabase(async () => {
|
|
1836
|
+
const result = await this.db.select({ roomState: participantTable.roomState }).from(participantTable).where(
|
|
1837
|
+
and(
|
|
1838
|
+
eq(participantTable.roomId, roomId),
|
|
1839
|
+
eq(participantTable.entityId, entityId),
|
|
1840
|
+
eq(participantTable.agentId, this.agentId)
|
|
1841
|
+
)
|
|
1842
|
+
).limit(1);
|
|
1843
|
+
return result[0]?.roomState ?? null;
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Asynchronously sets the user state for a participant in a room in the database based on the provided parameters.
|
|
1848
|
+
* @param {UUID} roomId - The ID of the room to set the participant's user state for.
|
|
1849
|
+
* @param {UUID} entityId - The ID of the entity to set the user state for.
|
|
1850
|
+
* @param {string} state - The state to set the participant's user state to.
|
|
1851
|
+
* @returns {Promise<void>} A Promise that resolves when the participant's user state is set.
|
|
1852
|
+
*/
|
|
1853
|
+
async setParticipantUserState(roomId, entityId, state) {
|
|
1854
|
+
return this.withDatabase(async () => {
|
|
1855
|
+
try {
|
|
1856
|
+
await this.db.transaction(async (tx) => {
|
|
1857
|
+
await tx.update(participantTable).set({ roomState: state }).where(
|
|
1858
|
+
and(
|
|
1859
|
+
eq(participantTable.roomId, roomId),
|
|
1860
|
+
eq(participantTable.entityId, entityId),
|
|
1861
|
+
eq(participantTable.agentId, this.agentId)
|
|
1862
|
+
)
|
|
1863
|
+
);
|
|
1864
|
+
});
|
|
1865
|
+
} catch (error) {
|
|
1866
|
+
logger.error("Failed to set participant user state:", {
|
|
1867
|
+
roomId,
|
|
1868
|
+
entityId,
|
|
1869
|
+
state,
|
|
1870
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1871
|
+
});
|
|
1872
|
+
throw error;
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* Asynchronously creates a new relationship in the database based on the provided parameters.
|
|
1878
|
+
* @param {Object} params - The parameters for creating a new relationship.
|
|
1879
|
+
* @param {UUID} params.sourceEntityId - The ID of the source entity.
|
|
1880
|
+
* @param {UUID} params.targetEntityId - The ID of the target entity.
|
|
1881
|
+
* @param {string[]} [params.tags] - The tags for the relationship.
|
|
1882
|
+
* @param {Object} [params.metadata] - The metadata for the relationship.
|
|
1883
|
+
* @returns {Promise<boolean>} A Promise that resolves to a boolean indicating whether the relationship was created successfully.
|
|
1884
|
+
*/
|
|
1885
|
+
async createRelationship(params) {
|
|
1886
|
+
return this.withDatabase(async () => {
|
|
1887
|
+
const id = v4();
|
|
1888
|
+
const saveParams = {
|
|
1889
|
+
id,
|
|
1890
|
+
sourceEntityId: params.sourceEntityId,
|
|
1891
|
+
targetEntityId: params.targetEntityId,
|
|
1892
|
+
agentId: this.agentId,
|
|
1893
|
+
tags: params.tags || [],
|
|
1894
|
+
metadata: params.metadata || {}
|
|
1895
|
+
};
|
|
1896
|
+
try {
|
|
1897
|
+
await this.db.insert(relationshipTable).values(saveParams);
|
|
1898
|
+
return true;
|
|
1899
|
+
} catch (error) {
|
|
1900
|
+
logger.error("Error creating relationship:", {
|
|
1901
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1902
|
+
saveParams
|
|
1903
|
+
});
|
|
1904
|
+
return false;
|
|
1905
|
+
}
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Asynchronously updates an existing relationship in the database based on the provided parameters.
|
|
1910
|
+
* @param {Relationship} relationship - The relationship object to update.
|
|
1911
|
+
* @returns {Promise<void>} A Promise that resolves when the relationship is updated.
|
|
1912
|
+
*/
|
|
1913
|
+
async updateRelationship(relationship) {
|
|
1914
|
+
return this.withDatabase(async () => {
|
|
1915
|
+
try {
|
|
1916
|
+
await this.db.update(relationshipTable).set({
|
|
1917
|
+
tags: relationship.tags || [],
|
|
1918
|
+
metadata: relationship.metadata || {}
|
|
1919
|
+
}).where(eq(relationshipTable.id, relationship.id));
|
|
1920
|
+
} catch (error) {
|
|
1921
|
+
logger.error("Error updating relationship:", {
|
|
1922
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1923
|
+
relationship
|
|
1924
|
+
});
|
|
1925
|
+
throw error;
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Asynchronously retrieves a relationship from the database based on the provided parameters.
|
|
1931
|
+
* @param {Object} params - The parameters for retrieving a relationship.
|
|
1932
|
+
* @param {UUID} params.sourceEntityId - The ID of the source entity.
|
|
1933
|
+
* @param {UUID} params.targetEntityId - The ID of the target entity.
|
|
1934
|
+
* @returns {Promise<Relationship | null>} A Promise that resolves to the relationship if found, null otherwise.
|
|
1935
|
+
*/
|
|
1936
|
+
async getRelationship(params) {
|
|
1937
|
+
return this.withDatabase(async () => {
|
|
1938
|
+
try {
|
|
1939
|
+
const result = await this.db.select().from(relationshipTable).where(
|
|
1940
|
+
and(
|
|
1941
|
+
eq(relationshipTable.sourceEntityId, params.sourceEntityId),
|
|
1942
|
+
eq(relationshipTable.targetEntityId, params.targetEntityId),
|
|
1943
|
+
eq(relationshipTable.agentId, this.agentId)
|
|
1944
|
+
)
|
|
1945
|
+
).limit(1);
|
|
1946
|
+
if (result.length === 0) {
|
|
1947
|
+
return null;
|
|
1948
|
+
}
|
|
1949
|
+
return {
|
|
1950
|
+
id: result[0].id,
|
|
1951
|
+
sourceEntityId: result[0].sourceEntityId,
|
|
1952
|
+
targetEntityId: result[0].targetEntityId,
|
|
1953
|
+
agentId: result[0].agentId,
|
|
1954
|
+
tags: result[0].tags || [],
|
|
1955
|
+
metadata: result[0].metadata || {},
|
|
1956
|
+
createdAt: result[0].createdAt?.toString()
|
|
1957
|
+
};
|
|
1958
|
+
} catch (error) {
|
|
1959
|
+
logger.error("Error getting relationship:", {
|
|
1960
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1961
|
+
params
|
|
1962
|
+
});
|
|
1963
|
+
return null;
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
/**
|
|
1968
|
+
* Asynchronously retrieves all relationships from the database based on the provided parameters.
|
|
1969
|
+
* @param {Object} params - The parameters for retrieving relationships.
|
|
1970
|
+
* @param {UUID} params.entityId - The ID of the entity to retrieve relationships for.
|
|
1971
|
+
* @param {string[]} [params.tags] - The tags to filter relationships by.
|
|
1972
|
+
* @returns {Promise<Relationship[]>} A Promise that resolves to an array of relationships.
|
|
1973
|
+
*/
|
|
1974
|
+
async getRelationships(params) {
|
|
1975
|
+
return this.withDatabase(async () => {
|
|
1976
|
+
try {
|
|
1977
|
+
let query = this.db.select().from(relationshipTable).where(
|
|
1978
|
+
and(
|
|
1979
|
+
or(
|
|
1980
|
+
eq(relationshipTable.sourceEntityId, params.entityId),
|
|
1981
|
+
eq(relationshipTable.targetEntityId, params.entityId)
|
|
1982
|
+
),
|
|
1983
|
+
eq(relationshipTable.agentId, this.agentId)
|
|
1984
|
+
)
|
|
1985
|
+
);
|
|
1986
|
+
if (params.tags && params.tags.length > 0) {
|
|
1987
|
+
const tagParams = params.tags.map((tag) => `'${tag.replace(/'/g, "''")}'`).join(", ");
|
|
1988
|
+
query = query.where(
|
|
1989
|
+
sql12`${relationshipTable.tags} @> ARRAY[${sql12.raw(tagParams)}]::text[]`
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
const results = await query;
|
|
1993
|
+
return results.map((result) => ({
|
|
1994
|
+
id: result.id,
|
|
1995
|
+
sourceEntityId: result.sourceEntityId,
|
|
1996
|
+
targetEntityId: result.targetEntityId,
|
|
1997
|
+
agentId: result.agentId,
|
|
1998
|
+
tags: result.tags || [],
|
|
1999
|
+
metadata: result.metadata || {},
|
|
2000
|
+
createdAt: result.createdAt?.toString()
|
|
2001
|
+
}));
|
|
2002
|
+
} catch (error) {
|
|
2003
|
+
logger.error("Error getting relationships:", {
|
|
2004
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2005
|
+
params
|
|
2006
|
+
});
|
|
2007
|
+
return [];
|
|
2008
|
+
}
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Asynchronously retrieves a cache value from the database based on the provided key.
|
|
2013
|
+
* @param {string} key - The key to retrieve the cache value for.
|
|
2014
|
+
* @returns {Promise<T | undefined>} A Promise that resolves to the cache value if found, undefined otherwise.
|
|
2015
|
+
*/
|
|
2016
|
+
async getCache(key) {
|
|
2017
|
+
return this.withDatabase(async () => {
|
|
2018
|
+
try {
|
|
2019
|
+
const result = await this.db.select().from(cacheTable).where(and(eq(cacheTable.agentId, this.agentId), eq(cacheTable.key, key)));
|
|
2020
|
+
return result[0]?.value;
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
logger.error("Error fetching cache", {
|
|
2023
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2024
|
+
key,
|
|
2025
|
+
agentId: this.agentId
|
|
2026
|
+
});
|
|
2027
|
+
return void 0;
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Asynchronously sets a cache value in the database based on the provided key and value.
|
|
2033
|
+
* @param {string} key - The key to set the cache value for.
|
|
2034
|
+
* @param {T} value - The value to set in the cache.
|
|
2035
|
+
* @returns {Promise<boolean>} A Promise that resolves to a boolean indicating whether the cache value was set successfully.
|
|
2036
|
+
*/
|
|
2037
|
+
async setCache(key, value) {
|
|
2038
|
+
return this.withDatabase(async () => {
|
|
2039
|
+
try {
|
|
2040
|
+
await this.db.transaction(async (tx) => {
|
|
2041
|
+
await tx.insert(cacheTable).values({
|
|
2042
|
+
key,
|
|
2043
|
+
agentId: this.agentId,
|
|
2044
|
+
value
|
|
2045
|
+
}).onConflictDoUpdate({
|
|
2046
|
+
target: [cacheTable.key, cacheTable.agentId],
|
|
2047
|
+
set: {
|
|
2048
|
+
value
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
2051
|
+
});
|
|
2052
|
+
return true;
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
logger.error("Error setting cache", {
|
|
2055
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2056
|
+
key,
|
|
2057
|
+
agentId: this.agentId
|
|
2058
|
+
});
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* Asynchronously deletes a cache value from the database based on the provided key.
|
|
2065
|
+
* @param {string} key - The key to delete the cache value for.
|
|
2066
|
+
* @returns {Promise<boolean>} A Promise that resolves to a boolean indicating whether the cache value was deleted successfully.
|
|
2067
|
+
*/
|
|
2068
|
+
async deleteCache(key) {
|
|
2069
|
+
return this.withDatabase(async () => {
|
|
2070
|
+
try {
|
|
2071
|
+
await this.db.transaction(async (tx) => {
|
|
2072
|
+
await tx.delete(cacheTable).where(and(eq(cacheTable.agentId, this.agentId), eq(cacheTable.key, key)));
|
|
2073
|
+
});
|
|
2074
|
+
return true;
|
|
2075
|
+
} catch (error) {
|
|
2076
|
+
logger.error("Error deleting cache", {
|
|
2077
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2078
|
+
key,
|
|
2079
|
+
agentId: this.agentId
|
|
2080
|
+
});
|
|
2081
|
+
return false;
|
|
2082
|
+
}
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Asynchronously creates a new world in the database based on the provided parameters.
|
|
2087
|
+
* @param {World} world - The world object to create.
|
|
2088
|
+
* @returns {Promise<UUID>} A Promise that resolves to the ID of the created world.
|
|
2089
|
+
*/
|
|
2090
|
+
async createWorld(world) {
|
|
2091
|
+
return this.withDatabase(async () => {
|
|
2092
|
+
const newWorldId = world.id || v4();
|
|
2093
|
+
await this.db.insert(worldTable).values({
|
|
2094
|
+
...world,
|
|
2095
|
+
id: newWorldId
|
|
2096
|
+
});
|
|
2097
|
+
return newWorldId;
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
/**
|
|
2101
|
+
* Asynchronously retrieves a world from the database based on the provided parameters.
|
|
2102
|
+
* @param {UUID} id - The ID of the world to retrieve.
|
|
2103
|
+
* @returns {Promise<World | null>} A Promise that resolves to the world if found, null otherwise.
|
|
2104
|
+
*/
|
|
2105
|
+
async getWorld(id) {
|
|
2106
|
+
return this.withDatabase(async () => {
|
|
2107
|
+
const result = await this.db.select().from(worldTable).where(eq(worldTable.id, id));
|
|
2108
|
+
return result[0];
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
/**
|
|
2112
|
+
* Asynchronously retrieves all worlds from the database based on the provided parameters.
|
|
2113
|
+
* @returns {Promise<World[]>} A Promise that resolves to an array of worlds.
|
|
2114
|
+
*/
|
|
2115
|
+
async getAllWorlds() {
|
|
2116
|
+
return this.withDatabase(async () => {
|
|
2117
|
+
const result = await this.db.select().from(worldTable).where(eq(worldTable.agentId, this.agentId));
|
|
2118
|
+
return result;
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Asynchronously updates an existing world in the database based on the provided parameters.
|
|
2123
|
+
* @param {World} world - The world object to update.
|
|
2124
|
+
* @returns {Promise<void>} A Promise that resolves when the world is updated.
|
|
2125
|
+
*/
|
|
2126
|
+
async updateWorld(world) {
|
|
2127
|
+
return this.withDatabase(async () => {
|
|
2128
|
+
await this.db.update(worldTable).set(world).where(eq(worldTable.id, world.id));
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Asynchronously removes a world from the database based on the provided parameters.
|
|
2133
|
+
* @param {UUID} id - The ID of the world to remove.
|
|
2134
|
+
* @returns {Promise<void>} A Promise that resolves when the world is removed.
|
|
2135
|
+
*/
|
|
2136
|
+
async removeWorld(id) {
|
|
2137
|
+
return this.withDatabase(async () => {
|
|
2138
|
+
await this.db.delete(worldTable).where(eq(worldTable.id, id));
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Asynchronously creates a new task in the database based on the provided parameters.
|
|
2143
|
+
* @param {Task} task - The task object to create.
|
|
2144
|
+
* @returns {Promise<UUID>} A Promise that resolves to the ID of the created task.
|
|
2145
|
+
*/
|
|
2146
|
+
async createTask(task) {
|
|
2147
|
+
return this.withRetry(async () => {
|
|
2148
|
+
return this.withDatabase(async () => {
|
|
2149
|
+
const now = /* @__PURE__ */ new Date();
|
|
2150
|
+
const metadata = task.metadata || {};
|
|
2151
|
+
const values = {
|
|
2152
|
+
id: task.id,
|
|
2153
|
+
name: task.name,
|
|
2154
|
+
description: task.description,
|
|
2155
|
+
roomId: task.roomId,
|
|
2156
|
+
worldId: task.worldId,
|
|
2157
|
+
tags: task.tags,
|
|
2158
|
+
metadata,
|
|
2159
|
+
createdAt: now,
|
|
2160
|
+
updatedAt: now,
|
|
2161
|
+
agentId: this.agentId
|
|
2162
|
+
};
|
|
2163
|
+
const result = await this.db.insert(taskTable).values(values).returning({ id: taskTable.id });
|
|
2164
|
+
return result[0].id;
|
|
2165
|
+
});
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Asynchronously retrieves tasks based on specified parameters.
|
|
2170
|
+
* @param params Object containing optional roomId and tags to filter tasks
|
|
2171
|
+
* @returns Promise resolving to an array of Task objects
|
|
2172
|
+
*/
|
|
2173
|
+
async getTasks(params) {
|
|
2174
|
+
return this.withRetry(async () => {
|
|
2175
|
+
return this.withDatabase(async () => {
|
|
2176
|
+
let query = this.db.select().from(taskTable).where(eq(taskTable.agentId, this.agentId));
|
|
2177
|
+
if (params.roomId) {
|
|
2178
|
+
query = query.where(eq(taskTable.roomId, params.roomId));
|
|
2179
|
+
}
|
|
2180
|
+
if (params.tags && params.tags.length > 0) {
|
|
2181
|
+
const tagParams = params.tags.map((tag) => `'${tag.replace(/'/g, "''")}'`).join(", ");
|
|
2182
|
+
query = query.where(sql12`${taskTable.tags} @> ARRAY[${sql12.raw(tagParams)}]::text[]`);
|
|
2183
|
+
}
|
|
2184
|
+
const result = await query;
|
|
2185
|
+
return result.map((row) => ({
|
|
2186
|
+
id: row.id,
|
|
2187
|
+
name: row.name,
|
|
2188
|
+
description: row.description,
|
|
2189
|
+
roomId: row.roomId,
|
|
2190
|
+
worldId: row.worldId,
|
|
2191
|
+
tags: row.tags,
|
|
2192
|
+
metadata: row.metadata
|
|
2193
|
+
}));
|
|
2194
|
+
});
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
/**
|
|
2198
|
+
* Asynchronously retrieves a specific task by its name.
|
|
2199
|
+
* @param name The name of the task to retrieve
|
|
2200
|
+
* @returns Promise resolving to the Task object if found, null otherwise
|
|
2201
|
+
*/
|
|
2202
|
+
async getTasksByName(name) {
|
|
2203
|
+
return this.withRetry(async () => {
|
|
2204
|
+
return this.withDatabase(async () => {
|
|
2205
|
+
const result = await this.db.select().from(taskTable).where(and(eq(taskTable.name, name), eq(taskTable.agentId, this.agentId)));
|
|
2206
|
+
return result.map((row) => ({
|
|
2207
|
+
id: row.id,
|
|
2208
|
+
name: row.name,
|
|
2209
|
+
description: row.description,
|
|
2210
|
+
roomId: row.roomId,
|
|
2211
|
+
worldId: row.worldId,
|
|
2212
|
+
tags: row.tags || [],
|
|
2213
|
+
metadata: row.metadata || {}
|
|
2214
|
+
}));
|
|
2215
|
+
});
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Asynchronously retrieves a specific task by its ID.
|
|
2220
|
+
* @param id The UUID of the task to retrieve
|
|
2221
|
+
* @returns Promise resolving to the Task object if found, null otherwise
|
|
2222
|
+
*/
|
|
2223
|
+
async getTask(id) {
|
|
2224
|
+
return this.withRetry(async () => {
|
|
2225
|
+
return this.withDatabase(async () => {
|
|
2226
|
+
const result = await this.db.select().from(taskTable).where(and(eq(taskTable.id, id), eq(taskTable.agentId, this.agentId))).limit(1);
|
|
2227
|
+
if (result.length === 0) {
|
|
2228
|
+
return null;
|
|
2229
|
+
}
|
|
2230
|
+
const row = result[0];
|
|
2231
|
+
return {
|
|
2232
|
+
id: row.id,
|
|
2233
|
+
name: row.name,
|
|
2234
|
+
description: row.description,
|
|
2235
|
+
roomId: row.roomId,
|
|
2236
|
+
worldId: row.worldId,
|
|
2237
|
+
tags: row.tags || [],
|
|
2238
|
+
metadata: row.metadata || {}
|
|
2239
|
+
};
|
|
2240
|
+
});
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* Asynchronously updates an existing task in the database.
|
|
2245
|
+
* @param id The UUID of the task to update
|
|
2246
|
+
* @param task Partial Task object containing the fields to update
|
|
2247
|
+
* @returns Promise resolving when the update is complete
|
|
2248
|
+
*/
|
|
2249
|
+
async updateTask(id, task) {
|
|
2250
|
+
await this.withRetry(async () => {
|
|
2251
|
+
await this.withDatabase(async () => {
|
|
2252
|
+
const updateValues = {};
|
|
2253
|
+
if (task.name !== void 0) updateValues.name = task.name;
|
|
2254
|
+
if (task.description !== void 0) updateValues.description = task.description;
|
|
2255
|
+
if (task.roomId !== void 0) updateValues.roomId = task.roomId;
|
|
2256
|
+
if (task.worldId !== void 0) updateValues.worldId = task.worldId;
|
|
2257
|
+
if (task.tags !== void 0) updateValues.tags = task.tags;
|
|
2258
|
+
task.updatedAt = Date.now();
|
|
2259
|
+
if (task.metadata) {
|
|
2260
|
+
const currentTask = await this.getTask(id);
|
|
2261
|
+
if (currentTask) {
|
|
2262
|
+
const currentMetadata = currentTask.metadata || {};
|
|
2263
|
+
const newMetadata = {
|
|
2264
|
+
...currentMetadata,
|
|
2265
|
+
...task.metadata
|
|
2266
|
+
};
|
|
2267
|
+
updateValues.metadata = newMetadata;
|
|
2268
|
+
} else {
|
|
2269
|
+
updateValues.metadata = {
|
|
2270
|
+
...task.metadata
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
await this.db.update(taskTable).set(updateValues).where(and(eq(taskTable.id, id), eq(taskTable.agentId, this.agentId)));
|
|
2275
|
+
});
|
|
2276
|
+
});
|
|
2277
|
+
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Asynchronously deletes a task from the database.
|
|
2280
|
+
* @param id The UUID of the task to delete
|
|
2281
|
+
* @returns Promise resolving when the deletion is complete
|
|
2282
|
+
*/
|
|
2283
|
+
async deleteTask(id) {
|
|
2284
|
+
await this.withRetry(async () => {
|
|
2285
|
+
await this.withDatabase(async () => {
|
|
2286
|
+
await this.db.delete(taskTable).where(and(eq(taskTable.id, id), eq(taskTable.agentId, this.agentId)));
|
|
2287
|
+
});
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Asynchronously retrieves group chat memories from all rooms under a given server.
|
|
2292
|
+
* It fetches all room IDs associated with the `serverId`, then retrieves memories
|
|
2293
|
+
* from those rooms in descending order (latest to oldest), with an optional count limit.
|
|
2294
|
+
*
|
|
2295
|
+
* @param {Object} params - Parameters for fetching memories.
|
|
2296
|
+
* @param {UUID} params.serverId - The server ID to fetch memories for.
|
|
2297
|
+
* @param {number} [params.count] - The maximum number of memories to retrieve.
|
|
2298
|
+
* @returns {Promise<Memory[]>} - A promise that resolves to an array of memory objects.
|
|
2299
|
+
*/
|
|
2300
|
+
async getMemoriesByServerId(params) {
|
|
2301
|
+
return this.withDatabase(async () => {
|
|
2302
|
+
const roomIdsResult = await this.db.select({ roomId: roomTable.id }).from(roomTable).where(eq(roomTable.serverId, params.serverId));
|
|
2303
|
+
if (roomIdsResult.length === 0) return [];
|
|
2304
|
+
const roomIds = roomIdsResult.map((row) => row.roomId);
|
|
2305
|
+
const query = this.db.select({
|
|
2306
|
+
memory: memoryTable,
|
|
2307
|
+
embedding: embeddingTable[this.embeddingDimension]
|
|
2308
|
+
}).from(memoryTable).leftJoin(embeddingTable, eq(embeddingTable.memoryId, memoryTable.id)).where(inArray(memoryTable.roomId, roomIds)).orderBy(desc(memoryTable.createdAt));
|
|
2309
|
+
const rows = params.count ? await query.limit(params.count) : await query;
|
|
2310
|
+
return rows.map((row) => ({
|
|
2311
|
+
id: row.memory.id,
|
|
2312
|
+
type: row.memory.type,
|
|
2313
|
+
createdAt: row.memory.createdAt,
|
|
2314
|
+
content: typeof row.memory.content === "string" ? JSON.parse(row.memory.content) : row.memory.content,
|
|
2315
|
+
entityId: row.memory.entityId,
|
|
2316
|
+
agentId: row.memory.agentId,
|
|
2317
|
+
roomId: row.memory.roomId,
|
|
2318
|
+
unique: row.memory.unique,
|
|
2319
|
+
embedding: row.embedding ?? void 0
|
|
2320
|
+
}));
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Asynchronously deletes all rooms associated with a specific serverId.
|
|
2325
|
+
* @param {UUID} serverId - The server ID to delete rooms for.
|
|
2326
|
+
* @returns {Promise<void>} A Promise that resolves when the rooms are deleted.
|
|
2327
|
+
*/
|
|
2328
|
+
async deleteRoomsByServerId(serverId) {
|
|
2329
|
+
return this.withDatabase(async () => {
|
|
2330
|
+
await this.db.transaction(async (tx) => {
|
|
2331
|
+
const roomIdsResult = await tx.select({ roomId: roomTable.id }).from(roomTable).where(eq(roomTable.serverId, serverId));
|
|
2332
|
+
if (roomIdsResult.length === 0) return;
|
|
2333
|
+
const roomIds = roomIdsResult.map((row) => row.roomId);
|
|
2334
|
+
await tx.delete(embeddingTable).where(
|
|
2335
|
+
inArray(
|
|
2336
|
+
embeddingTable.memoryId,
|
|
2337
|
+
tx.select({ id: memoryTable.id }).from(memoryTable).where(inArray(memoryTable.roomId, roomIds))
|
|
2338
|
+
)
|
|
2339
|
+
);
|
|
2340
|
+
await tx.delete(memoryTable).where(inArray(memoryTable.roomId, roomIds));
|
|
2341
|
+
await tx.delete(participantTable).where(inArray(participantTable.roomId, roomIds));
|
|
2342
|
+
await tx.delete(logTable).where(inArray(logTable.roomId, roomIds));
|
|
2343
|
+
await tx.delete(roomTable).where(inArray(roomTable.id, roomIds));
|
|
2344
|
+
});
|
|
2345
|
+
logger.debug("Rooms and related logs deleted successfully for server:", {
|
|
2346
|
+
serverId
|
|
2347
|
+
});
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
};
|
|
2351
|
+
|
|
2352
|
+
// src/pglite/adapter.ts
|
|
2353
|
+
var PgliteDatabaseAdapter = class extends BaseDrizzleAdapter {
|
|
2354
|
+
static {
|
|
2355
|
+
__name(this, "PgliteDatabaseAdapter");
|
|
2356
|
+
}
|
|
2357
|
+
manager;
|
|
2358
|
+
embeddingDimension = DIMENSION_MAP[384];
|
|
2359
|
+
/**
|
|
2360
|
+
* Constructor for creating an instance of a class.
|
|
2361
|
+
* @param {UUID} agentId - The unique identifier for the agent.
|
|
2362
|
+
* @param {PGliteClientManager} manager - The manager for the PGlite client.
|
|
2363
|
+
*/
|
|
2364
|
+
constructor(agentId, manager) {
|
|
2365
|
+
super(agentId);
|
|
2366
|
+
this.manager = manager;
|
|
2367
|
+
this.db = drizzle(this.manager.getConnection());
|
|
2368
|
+
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Asynchronously runs the provided database operation while checking if the database manager is currently shutting down.
|
|
2371
|
+
* If the database manager is shutting down, a warning is logged and null is returned.
|
|
2372
|
+
*
|
|
2373
|
+
* @param {Function} operation - The database operation to be performed.
|
|
2374
|
+
* @returns {Promise<T>} A promise that resolves with the result of the database operation.
|
|
2375
|
+
*/
|
|
2376
|
+
async withDatabase(operation) {
|
|
2377
|
+
if (this.manager.isShuttingDown()) {
|
|
2378
|
+
logger2.warn("Database is shutting down");
|
|
2379
|
+
return null;
|
|
2380
|
+
}
|
|
2381
|
+
return operation();
|
|
2382
|
+
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Asynchronously initializes the database by running migrations using the manager.
|
|
2385
|
+
*
|
|
2386
|
+
* @returns {Promise<void>} A Promise that resolves when the database initialization is complete.
|
|
2387
|
+
*/
|
|
2388
|
+
async init() {
|
|
2389
|
+
try {
|
|
2390
|
+
await this.manager.runMigrations();
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
logger2.error("Failed to initialize database:", error);
|
|
2393
|
+
throw error;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Asynchronously closes the manager.
|
|
2398
|
+
*/
|
|
2399
|
+
async close() {
|
|
2400
|
+
await this.manager.close();
|
|
2401
|
+
}
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
// src/pg/adapter.ts
|
|
2405
|
+
import { logger as logger3 } from "@elizaos/core";
|
|
2406
|
+
import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
|
|
2407
|
+
var PgDatabaseAdapter = class extends BaseDrizzleAdapter {
|
|
2408
|
+
/**
|
|
2409
|
+
* Constructor for creating a new instance of a class.
|
|
2410
|
+
* @param {UUID} agentId - The unique identifier for the agent.
|
|
2411
|
+
* @param {PostgresConnectionManager} manager - The Postgres connection manager for the instance.
|
|
2412
|
+
*/
|
|
2413
|
+
constructor(agentId, manager) {
|
|
2414
|
+
super(agentId);
|
|
2415
|
+
this.manager = manager;
|
|
2416
|
+
this.manager = manager;
|
|
2417
|
+
}
|
|
2418
|
+
static {
|
|
2419
|
+
__name(this, "PgDatabaseAdapter");
|
|
2420
|
+
}
|
|
2421
|
+
embeddingDimension = DIMENSION_MAP[384];
|
|
2422
|
+
/**
|
|
2423
|
+
* Executes the provided operation with a database connection.
|
|
2424
|
+
*
|
|
2425
|
+
* @template T
|
|
2426
|
+
* @param {() => Promise<T>} operation - The operation to be executed with the database connection.
|
|
2427
|
+
* @returns {Promise<T>} A promise that resolves with the result of the operation.
|
|
2428
|
+
*/
|
|
2429
|
+
async withDatabase(operation) {
|
|
2430
|
+
return await this.withRetry(async () => {
|
|
2431
|
+
const client = await this.manager.getClient();
|
|
2432
|
+
try {
|
|
2433
|
+
const db = drizzle2(client);
|
|
2434
|
+
this.db = db;
|
|
2435
|
+
return await operation();
|
|
2436
|
+
} finally {
|
|
2437
|
+
client.release();
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Asynchronously initializes the PgDatabaseAdapter by running migrations using the manager.
|
|
2443
|
+
* Logs a success message if initialization is successful, otherwise logs an error message.
|
|
2444
|
+
*
|
|
2445
|
+
* @returns {Promise<void>} A promise that resolves when initialization is complete.
|
|
2446
|
+
*/
|
|
2447
|
+
async init() {
|
|
2448
|
+
try {
|
|
2449
|
+
await this.manager.runMigrations();
|
|
2450
|
+
logger3.debug("PgDatabaseAdapter initialized successfully");
|
|
2451
|
+
} catch (error) {
|
|
2452
|
+
logger3.error("Failed to initialize PgDatabaseAdapter:", error);
|
|
2453
|
+
throw error;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Asynchronously closes the manager associated with this instance.
|
|
2458
|
+
*
|
|
2459
|
+
* @returns A Promise that resolves once the manager is closed.
|
|
2460
|
+
*/
|
|
2461
|
+
async close() {
|
|
2462
|
+
await this.manager.close();
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
|
|
2466
|
+
// src/index.ts
|
|
2467
|
+
var GLOBAL_SINGLETONS = Symbol.for("@elizaos/plugin-sql/global-singletons");
|
|
2468
|
+
var globalSymbols = global;
|
|
2469
|
+
if (!globalSymbols[GLOBAL_SINGLETONS]) {
|
|
2470
|
+
globalSymbols[GLOBAL_SINGLETONS] = {};
|
|
2471
|
+
}
|
|
2472
|
+
var globalSingletons = globalSymbols[GLOBAL_SINGLETONS];
|
|
2473
|
+
function expandTildePath(filepath) {
|
|
2474
|
+
if (filepath && typeof filepath === "string" && filepath.startsWith("~")) {
|
|
2475
|
+
return filepath.replace(/^~/, os.homedir());
|
|
2476
|
+
}
|
|
2477
|
+
return filepath;
|
|
2478
|
+
}
|
|
2479
|
+
__name(expandTildePath, "expandTildePath");
|
|
2480
|
+
function createDatabaseAdapter(config, agentId) {
|
|
2481
|
+
if (config.dataDir) {
|
|
2482
|
+
config.dataDir = expandTildePath(config.dataDir);
|
|
2483
|
+
}
|
|
2484
|
+
if (config.postgresUrl) {
|
|
2485
|
+
if (!globalSingletons.postgresConnectionManager) {
|
|
2486
|
+
globalSingletons.postgresConnectionManager = new PostgresConnectionManager(
|
|
2487
|
+
config.postgresUrl
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
return new PgDatabaseAdapter(agentId, globalSingletons.postgresConnectionManager);
|
|
2491
|
+
}
|
|
2492
|
+
const dataDir = config.dataDir ?? "./elizadb";
|
|
2493
|
+
if (!globalSingletons.pgLiteClientManager) {
|
|
2494
|
+
globalSingletons.pgLiteClientManager = new PGliteClientManager({ dataDir });
|
|
2495
|
+
}
|
|
2496
|
+
return new PgliteDatabaseAdapter(agentId, globalSingletons.pgLiteClientManager);
|
|
2497
|
+
}
|
|
2498
|
+
__name(createDatabaseAdapter, "createDatabaseAdapter");
|
|
2499
|
+
var sqlPlugin = {
|
|
2500
|
+
name: "sql",
|
|
2501
|
+
description: "SQL database adapter plugin using Drizzle ORM",
|
|
2502
|
+
init: /* @__PURE__ */ __name(async (_, runtime) => {
|
|
2503
|
+
const config = {
|
|
2504
|
+
dataDir: runtime.getSetting("PGLITE_DATA_DIR") ?? "./pglite",
|
|
2505
|
+
postgresUrl: runtime.getSetting("POSTGRES_URL")
|
|
2506
|
+
};
|
|
2507
|
+
try {
|
|
2508
|
+
const db = createDatabaseAdapter(config, runtime.agentId);
|
|
2509
|
+
logger4.success("Database connection established successfully");
|
|
2510
|
+
runtime.registerDatabaseAdapter(db);
|
|
2511
|
+
} catch (error) {
|
|
2512
|
+
logger4.error("Failed to initialize database:", error);
|
|
2513
|
+
throw error;
|
|
2514
|
+
}
|
|
2515
|
+
}, "init")
|
|
2516
|
+
};
|
|
2517
|
+
var index_default = sqlPlugin;
|
|
2518
|
+
export {
|
|
2519
|
+
createDatabaseAdapter,
|
|
2520
|
+
index_default as default
|
|
2521
|
+
};
|
|
49
2522
|
//# sourceMappingURL=index.js.map
|