@axiom-lattice/pg-stores 1.0.21 → 1.0.23

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.
@@ -10,6 +10,8 @@ import {
10
10
  } from "@axiom-lattice/protocols";
11
11
  import { MigrationManager } from "../migrations/migration";
12
12
  import { createThreadsTable } from "../migrations/thread_migrations";
13
+ import { addThreadTenantId } from "../migrations/thread_tenant_migration";
14
+ import { changeThreadPrimaryKey } from "../migrations/thread_pk_migration";
13
15
 
14
16
  /**
15
17
  * PostgreSQL ThreadStore options
@@ -30,13 +32,16 @@ export interface PostgreSQLThreadStoreOptions {
30
32
 
31
33
  /**
32
34
  * PostgreSQL implementation of ThreadStore
35
+ *
36
+ * Features:
37
+ * - Multi-tenant isolation via tenant_id
33
38
  */
34
39
  export class PostgreSQLThreadStore implements ThreadStore {
35
40
  private pool: Pool;
36
41
  private migrationManager: MigrationManager;
37
42
  private initialized: boolean = false;
38
43
  private ownsPool: boolean = true;
39
- private initPromise: Promise<void> | null = null; // Promise-based lock to prevent concurrent initialization
44
+ private initPromise: Promise<void> | null = null;
40
45
 
41
46
  constructor(options: PostgreSQLThreadStoreOptions) {
42
47
  // Create Pool from config
@@ -48,6 +53,8 @@ export class PostgreSQLThreadStore implements ThreadStore {
48
53
 
49
54
  this.migrationManager = new MigrationManager(this.pool);
50
55
  this.migrationManager.register(createThreadsTable);
56
+ this.migrationManager.register(addThreadTenantId);
57
+ this.migrationManager.register(changeThreadPrimaryKey);
51
58
 
52
59
  // Auto-migrate by default
53
60
  if (options.autoMigrate !== false) {
@@ -73,23 +80,19 @@ export class PostgreSQLThreadStore implements ThreadStore {
73
80
  * Uses a promise-based lock to prevent concurrent initialization
74
81
  */
75
82
  async initialize(): Promise<void> {
76
- // If already initialized, return immediately
77
83
  if (this.initialized) {
78
84
  return;
79
85
  }
80
86
 
81
- // If initialization is in progress, wait for it
82
87
  if (this.initPromise) {
83
88
  return this.initPromise;
84
89
  }
85
90
 
86
- // Start initialization and store the promise
87
91
  this.initPromise = (async () => {
88
92
  try {
89
93
  await this.migrationManager.migrate();
90
94
  this.initialized = true;
91
95
  } finally {
92
- // Clear the promise after completion (success or failure)
93
96
  this.initPromise = null;
94
97
  }
95
98
  })();
@@ -98,52 +101,54 @@ export class PostgreSQLThreadStore implements ThreadStore {
98
101
  }
99
102
 
100
103
  /**
101
- * Get all threads for a specific assistant
104
+ * Get all threads for a specific tenant and assistant
102
105
  */
103
- async getThreadsByAssistantId(assistantId: string): Promise<Thread[]> {
106
+ async getThreadsByAssistantId(tenantId: string, assistantId: string): Promise<Thread[]> {
104
107
  await this.ensureInitialized();
105
108
 
106
109
  const result = await this.pool.query<{
107
110
  id: string;
111
+ tenant_id: string;
108
112
  assistant_id: string;
109
113
  metadata: any;
110
114
  created_at: Date;
111
115
  updated_at: Date;
112
116
  }>(
113
117
  `
114
- SELECT id, assistant_id, metadata, created_at, updated_at
118
+ SELECT id, tenant_id, assistant_id, metadata, created_at, updated_at
115
119
  FROM lattice_threads
116
- WHERE assistant_id = $1
120
+ WHERE tenant_id = $1 AND assistant_id = $2
117
121
  ORDER BY created_at DESC
118
122
  `,
119
- [assistantId]
123
+ [tenantId, assistantId]
120
124
  );
121
125
 
122
126
  return result.rows.map(this.mapRowToThread);
123
127
  }
124
128
 
125
129
  /**
126
- * Get a thread by ID for a specific assistant
130
+ * Get a thread by ID for a specific tenant
127
131
  */
128
132
  async getThreadById(
129
- assistantId: string,
133
+ tenantId: string,
130
134
  threadId: string
131
135
  ): Promise<Thread | undefined> {
132
136
  await this.ensureInitialized();
133
137
 
134
138
  const result = await this.pool.query<{
135
139
  id: string;
140
+ tenant_id: string;
136
141
  assistant_id: string;
137
142
  metadata: any;
138
143
  created_at: Date;
139
144
  updated_at: Date;
140
145
  }>(
141
146
  `
142
- SELECT id, assistant_id, metadata, created_at, updated_at
147
+ SELECT id, tenant_id, assistant_id, metadata, created_at, updated_at
143
148
  FROM lattice_threads
144
- WHERE id = $1 AND assistant_id = $2
149
+ WHERE tenant_id = $1 AND id = $2
145
150
  `,
146
- [threadId, assistantId]
151
+ [tenantId, threadId]
147
152
  );
148
153
 
149
154
  if (result.rows.length === 0) {
@@ -154,9 +159,10 @@ export class PostgreSQLThreadStore implements ThreadStore {
154
159
  }
155
160
 
156
161
  /**
157
- * Create a new thread for an assistant
162
+ * Create a new thread for a tenant and assistant
158
163
  */
159
164
  async createThread(
165
+ tenantId: string,
160
166
  assistantId: string,
161
167
  threadId: string,
162
168
  data: CreateThreadRequest
@@ -168,17 +174,19 @@ export class PostgreSQLThreadStore implements ThreadStore {
168
174
 
169
175
  await this.pool.query(
170
176
  `
171
- INSERT INTO lattice_threads (id, assistant_id, metadata, created_at, updated_at)
172
- VALUES ($1, $2, $3, $4, $5)
173
- ON CONFLICT (id, assistant_id) DO UPDATE SET
177
+ INSERT INTO lattice_threads (id, tenant_id, assistant_id, metadata, created_at, updated_at)
178
+ VALUES ($1, $2, $3, $4, $5, $6)
179
+ ON CONFLICT (id, tenant_id) DO UPDATE SET
180
+ assistant_id = EXCLUDED.assistant_id,
174
181
  metadata = EXCLUDED.metadata,
175
182
  updated_at = EXCLUDED.updated_at
176
183
  `,
177
- [threadId, assistantId, JSON.stringify(metadata), now, now]
184
+ [threadId, tenantId, assistantId, JSON.stringify(metadata), now, now]
178
185
  );
179
186
 
180
187
  return {
181
188
  id: threadId,
189
+ tenantId,
182
190
  assistantId,
183
191
  metadata,
184
192
  createdAt: now,
@@ -190,14 +198,14 @@ export class PostgreSQLThreadStore implements ThreadStore {
190
198
  * Update an existing thread
191
199
  */
192
200
  async updateThread(
193
- assistantId: string,
201
+ tenantId: string,
194
202
  threadId: string,
195
203
  updates: Partial<CreateThreadRequest>
196
204
  ): Promise<Thread | null> {
197
205
  await this.ensureInitialized();
198
206
 
199
207
  // Get existing thread
200
- const existing = await this.getThreadById(assistantId, threadId);
208
+ const existing = await this.getThreadById(tenantId, threadId);
201
209
  if (!existing) {
202
210
  return null;
203
211
  }
@@ -214,9 +222,9 @@ export class PostgreSQLThreadStore implements ThreadStore {
214
222
  `
215
223
  UPDATE lattice_threads
216
224
  SET metadata = $1, updated_at = $2
217
- WHERE id = $3 AND assistant_id = $4
225
+ WHERE tenant_id = $3 AND id = $4
218
226
  `,
219
- [JSON.stringify(updatedMetadata), now, threadId, assistantId]
227
+ [JSON.stringify(updatedMetadata), now, tenantId, threadId]
220
228
  );
221
229
 
222
230
  return {
@@ -229,15 +237,15 @@ export class PostgreSQLThreadStore implements ThreadStore {
229
237
  /**
230
238
  * Delete a thread by ID
231
239
  */
232
- async deleteThread(assistantId: string, threadId: string): Promise<boolean> {
240
+ async deleteThread(tenantId: string, threadId: string): Promise<boolean> {
233
241
  await this.ensureInitialized();
234
242
 
235
243
  const result = await this.pool.query(
236
244
  `
237
245
  DELETE FROM lattice_threads
238
- WHERE id = $1 AND assistant_id = $2
246
+ WHERE tenant_id = $1 AND id = $2
239
247
  `,
240
- [threadId, assistantId]
248
+ [tenantId, threadId]
241
249
  );
242
250
 
243
251
  return result.rowCount !== null && result.rowCount > 0;
@@ -246,16 +254,16 @@ export class PostgreSQLThreadStore implements ThreadStore {
246
254
  /**
247
255
  * Check if thread exists
248
256
  */
249
- async hasThread(assistantId: string, threadId: string): Promise<boolean> {
257
+ async hasThread(tenantId: string, threadId: string): Promise<boolean> {
250
258
  await this.ensureInitialized();
251
259
 
252
260
  const result = await this.pool.query(
253
261
  `
254
262
  SELECT 1 FROM lattice_threads
255
- WHERE id = $1 AND assistant_id = $2
263
+ WHERE tenant_id = $1 AND id = $2
256
264
  LIMIT 1
257
265
  `,
258
- [threadId, assistantId]
266
+ [tenantId, threadId]
259
267
  );
260
268
 
261
269
  return result.rows.length > 0;
@@ -275,6 +283,7 @@ export class PostgreSQLThreadStore implements ThreadStore {
275
283
  */
276
284
  private mapRowToThread(row: {
277
285
  id: string;
286
+ tenant_id: string;
278
287
  assistant_id: string;
279
288
  metadata: any;
280
289
  created_at: Date;
@@ -282,6 +291,7 @@ export class PostgreSQLThreadStore implements ThreadStore {
282
291
  }): Thread {
283
292
  return {
284
293
  id: row.id,
294
+ tenantId: row.tenant_id,
285
295
  assistantId: row.assistant_id,
286
296
  metadata:
287
297
  typeof row.metadata === "string"