family-ai-agent 1.0.5 → 1.0.7

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.
Files changed (153) hide show
  1. package/.letta/settings.local.json +3 -0
  2. package/dist/cli/index.js +6 -4
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/database/adapters/base-adapter.d.ts +81 -0
  5. package/dist/database/adapters/base-adapter.d.ts.map +1 -0
  6. package/dist/database/adapters/base-adapter.js +105 -0
  7. package/dist/database/adapters/base-adapter.js.map +1 -0
  8. package/dist/database/adapters/index.d.ts +49 -0
  9. package/dist/database/adapters/index.d.ts.map +1 -0
  10. package/dist/database/adapters/index.js +200 -0
  11. package/dist/database/adapters/index.js.map +1 -0
  12. package/dist/database/adapters/postgres-adapter.d.ts +75 -0
  13. package/dist/database/adapters/postgres-adapter.d.ts.map +1 -0
  14. package/dist/database/adapters/postgres-adapter.js +225 -0
  15. package/dist/database/adapters/postgres-adapter.js.map +1 -0
  16. package/dist/database/adapters/sqlite-adapter.d.ts +72 -0
  17. package/dist/database/adapters/sqlite-adapter.d.ts.map +1 -0
  18. package/dist/database/adapters/sqlite-adapter.js +368 -0
  19. package/dist/database/adapters/sqlite-adapter.js.map +1 -0
  20. package/dist/database/cache/cache-keys.d.ts +180 -0
  21. package/dist/database/cache/cache-keys.d.ts.map +1 -0
  22. package/dist/database/cache/cache-keys.js +107 -0
  23. package/dist/database/cache/cache-keys.js.map +1 -0
  24. package/dist/database/cache/index.d.ts +24 -0
  25. package/dist/database/cache/index.d.ts.map +1 -0
  26. package/dist/database/cache/index.js +34 -0
  27. package/dist/database/cache/index.js.map +1 -0
  28. package/dist/database/cache/query-cache.d.ts +67 -0
  29. package/dist/database/cache/query-cache.d.ts.map +1 -0
  30. package/dist/database/cache/query-cache.js +177 -0
  31. package/dist/database/cache/query-cache.js.map +1 -0
  32. package/dist/database/client.d.ts +63 -4
  33. package/dist/database/client.d.ts.map +1 -1
  34. package/dist/database/client.js +147 -59
  35. package/dist/database/client.js.map +1 -1
  36. package/dist/database/db-config.d.ts +104 -0
  37. package/dist/database/db-config.d.ts.map +1 -0
  38. package/dist/database/db-config.js +167 -0
  39. package/dist/database/db-config.js.map +1 -0
  40. package/dist/database/drizzle/index.d.ts +42 -0
  41. package/dist/database/drizzle/index.d.ts.map +1 -0
  42. package/dist/database/drizzle/index.js +48 -0
  43. package/dist/database/drizzle/index.js.map +1 -0
  44. package/dist/database/drizzle/schema/audit.d.ts +533 -0
  45. package/dist/database/drizzle/schema/audit.d.ts.map +1 -0
  46. package/dist/database/drizzle/schema/audit.js +71 -0
  47. package/dist/database/drizzle/schema/audit.js.map +1 -0
  48. package/dist/database/drizzle/schema/checkpoints.d.ts +665 -0
  49. package/dist/database/drizzle/schema/checkpoints.d.ts.map +1 -0
  50. package/dist/database/drizzle/schema/checkpoints.js +110 -0
  51. package/dist/database/drizzle/schema/checkpoints.js.map +1 -0
  52. package/dist/database/drizzle/schema/conversations.d.ts +449 -0
  53. package/dist/database/drizzle/schema/conversations.d.ts.map +1 -0
  54. package/dist/database/drizzle/schema/conversations.js +91 -0
  55. package/dist/database/drizzle/schema/conversations.js.map +1 -0
  56. package/dist/database/drizzle/schema/documents.d.ts +600 -0
  57. package/dist/database/drizzle/schema/documents.d.ts.map +1 -0
  58. package/dist/database/drizzle/schema/documents.js +100 -0
  59. package/dist/database/drizzle/schema/documents.js.map +1 -0
  60. package/dist/database/drizzle/schema/index.d.ts +3084 -0
  61. package/dist/database/drizzle/schema/index.d.ts.map +1 -0
  62. package/dist/database/drizzle/schema/index.js +46 -0
  63. package/dist/database/drizzle/schema/index.js.map +1 -0
  64. package/dist/database/drizzle/schema/memories.d.ts +435 -0
  65. package/dist/database/drizzle/schema/memories.d.ts.map +1 -0
  66. package/dist/database/drizzle/schema/memories.js +73 -0
  67. package/dist/database/drizzle/schema/memories.js.map +1 -0
  68. package/dist/database/drizzle/schema/tasks.d.ts +565 -0
  69. package/dist/database/drizzle/schema/tasks.d.ts.map +1 -0
  70. package/dist/database/drizzle/schema/tasks.js +84 -0
  71. package/dist/database/drizzle/schema/tasks.js.map +1 -0
  72. package/dist/database/health/circuit-breaker.d.ts +81 -0
  73. package/dist/database/health/circuit-breaker.d.ts.map +1 -0
  74. package/dist/database/health/circuit-breaker.js +184 -0
  75. package/dist/database/health/circuit-breaker.js.map +1 -0
  76. package/dist/database/health/health-monitor.d.ts +69 -0
  77. package/dist/database/health/health-monitor.d.ts.map +1 -0
  78. package/dist/database/health/health-monitor.js +174 -0
  79. package/dist/database/health/health-monitor.js.map +1 -0
  80. package/dist/database/health/index.d.ts +27 -0
  81. package/dist/database/health/index.d.ts.map +1 -0
  82. package/dist/database/health/index.js +23 -0
  83. package/dist/database/health/index.js.map +1 -0
  84. package/dist/database/index.d.ts +16 -0
  85. package/dist/database/index.d.ts.map +1 -0
  86. package/dist/database/index.js +41 -0
  87. package/dist/database/index.js.map +1 -0
  88. package/dist/database/migrations/index.d.ts +34 -0
  89. package/dist/database/migrations/index.d.ts.map +1 -0
  90. package/dist/database/migrations/index.js +45 -0
  91. package/dist/database/migrations/index.js.map +1 -0
  92. package/dist/database/migrations/migrator.d.ts +77 -0
  93. package/dist/database/migrations/migrator.d.ts.map +1 -0
  94. package/dist/database/migrations/migrator.js +258 -0
  95. package/dist/database/migrations/migrator.js.map +1 -0
  96. package/dist/database/migrations/versions/001_initial.d.ts +9 -0
  97. package/dist/database/migrations/versions/001_initial.d.ts.map +1 -0
  98. package/dist/database/migrations/versions/001_initial.js +183 -0
  99. package/dist/database/migrations/versions/001_initial.js.map +1 -0
  100. package/dist/database/types.d.ts +255 -0
  101. package/dist/database/types.d.ts.map +1 -0
  102. package/dist/database/types.js +8 -0
  103. package/dist/database/types.js.map +1 -0
  104. package/dist/database/vector/embedding-cache.d.ts +92 -0
  105. package/dist/database/vector/embedding-cache.d.ts.map +1 -0
  106. package/dist/database/vector/embedding-cache.js +185 -0
  107. package/dist/database/vector/embedding-cache.js.map +1 -0
  108. package/dist/database/vector/hnsw-index.d.ts +111 -0
  109. package/dist/database/vector/hnsw-index.d.ts.map +1 -0
  110. package/dist/database/vector/hnsw-index.js +337 -0
  111. package/dist/database/vector/hnsw-index.js.map +1 -0
  112. package/dist/database/vector/index.d.ts +75 -0
  113. package/dist/database/vector/index.d.ts.map +1 -0
  114. package/dist/database/vector/index.js +213 -0
  115. package/dist/database/vector/index.js.map +1 -0
  116. package/dist/database/vector/similarity.d.ts +67 -0
  117. package/dist/database/vector/similarity.d.ts.map +1 -0
  118. package/dist/database/vector/similarity.js +176 -0
  119. package/dist/database/vector/similarity.js.map +1 -0
  120. package/dist/index.d.ts +1 -1
  121. package/dist/index.js +1 -1
  122. package/package.json +6 -3
  123. package/src/cli/index.ts +5 -5
  124. package/src/database/adapters/base-adapter.ts +171 -0
  125. package/src/database/adapters/index.ts +224 -0
  126. package/src/database/adapters/postgres-adapter.ts +285 -0
  127. package/src/database/adapters/sqlite-adapter.ts +420 -0
  128. package/src/database/cache/cache-keys.ts +150 -0
  129. package/src/database/cache/index.ts +44 -0
  130. package/src/database/cache/query-cache.ts +213 -0
  131. package/src/database/client.ts +166 -64
  132. package/src/database/db-config.ts +194 -0
  133. package/src/database/drizzle/index.ts +66 -0
  134. package/src/database/drizzle/schema/audit.ts +127 -0
  135. package/src/database/drizzle/schema/checkpoints.ts +164 -0
  136. package/src/database/drizzle/schema/conversations.ts +138 -0
  137. package/src/database/drizzle/schema/documents.ts +157 -0
  138. package/src/database/drizzle/schema/index.ts +139 -0
  139. package/src/database/drizzle/schema/memories.ts +127 -0
  140. package/src/database/drizzle/schema/tasks.ts +129 -0
  141. package/src/database/health/circuit-breaker.ts +214 -0
  142. package/src/database/health/health-monitor.ts +224 -0
  143. package/src/database/health/index.ts +41 -0
  144. package/src/database/index.ts +157 -0
  145. package/src/database/migrations/index.ts +52 -0
  146. package/src/database/migrations/migrator.ts +325 -0
  147. package/src/database/migrations/versions/001_initial.ts +198 -0
  148. package/src/database/types.ts +324 -0
  149. package/src/database/vector/embedding-cache.ts +234 -0
  150. package/src/database/vector/hnsw-index.ts +452 -0
  151. package/src/database/vector/index.ts +292 -0
  152. package/src/database/vector/similarity.ts +198 -0
  153. package/src/index.ts +1 -1
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Database Adapter Factory
3
+ *
4
+ * Factory for creating database adapters with auto-detection support.
5
+ * Tries PostgreSQL first, falls back to SQLite automatically.
6
+ */
7
+
8
+ import pg from 'pg';
9
+
10
+ import { PostgresAdapter } from './postgres-adapter.js';
11
+ import { SQLiteAdapter } from './sqlite-adapter.js';
12
+ import { BaseAdapter } from './base-adapter.js';
13
+ import { getDatabaseConfig, buildPostgresUrl, getDefaultSqlitePath } from '../db-config.js';
14
+ import type { DatabaseAdapter, DetectionResult, DatabaseConfig } from '../types.js';
15
+ import { createLogger } from '../../utils/logger.js';
16
+
17
+ const { Pool } = pg;
18
+ const logger = createLogger('AdapterFactory');
19
+
20
+ // Singleton adapter instance
21
+ let adapterInstance: DatabaseAdapter | null = null;
22
+ let initPromise: Promise<DatabaseAdapter> | null = null;
23
+
24
+ /**
25
+ * Detect which database to use
26
+ */
27
+ export async function detectDatabase(config: DatabaseConfig): Promise<DetectionResult> {
28
+ // 1. Check for explicit configuration
29
+ if (config.type !== 'auto') {
30
+ if (config.type === 'postgresql') {
31
+ return {
32
+ type: 'postgresql',
33
+ connectionString: config.postgresUrl ?? buildPostgresUrl({
34
+ DATABASE_URL: config.postgresUrl,
35
+ DB_HOST: config.postgresHost,
36
+ DB_PORT: config.postgresPort,
37
+ DB_USER: config.postgresUser,
38
+ DB_PASSWORD: config.postgresPassword,
39
+ DB_NAME: config.postgresDatabase,
40
+ DB_POOL_MAX: config.postgresPoolMax,
41
+ DB_POOL_IDLE_TIMEOUT_MS: config.postgresIdleTimeoutMs,
42
+ DB_POOL_CONNECTION_TIMEOUT_MS: config.postgresConnectionTimeoutMs,
43
+ DATABASE_TYPE: 'postgresql',
44
+ SQLITE_PATH: config.sqlitePath,
45
+ DB_HEALTH_CHECK_INTERVAL_MS: config.healthCheckIntervalMs,
46
+ DB_CIRCUIT_BREAKER_THRESHOLD: config.circuitBreakerThreshold,
47
+ DB_CIRCUIT_BREAKER_TIMEOUT_MS: config.circuitBreakerTimeoutMs,
48
+ DB_CACHE_MAX_SIZE: config.cacheMaxSize,
49
+ DB_CACHE_TTL_MS: config.cacheTtlMs,
50
+ VECTOR_DIMENSION: config.vectorDimension,
51
+ VECTOR_MIN_SIMILARITY: config.vectorMinSimilarity,
52
+ }),
53
+ reason: 'Explicit PostgreSQL configuration',
54
+ };
55
+ }
56
+ return {
57
+ type: 'sqlite',
58
+ sqlitePath: config.sqlitePath ?? getDefaultSqlitePath(),
59
+ reason: 'Explicit SQLite configuration',
60
+ };
61
+ }
62
+
63
+ // 2. Check for DATABASE_URL (usually means PostgreSQL is intended)
64
+ if (config.postgresUrl) {
65
+ return {
66
+ type: 'postgresql',
67
+ connectionString: config.postgresUrl,
68
+ reason: 'DATABASE_URL environment variable set',
69
+ };
70
+ }
71
+
72
+ // 3. Try PostgreSQL connection
73
+ try {
74
+ const connectionString = buildPostgresUrl({
75
+ DATABASE_URL: config.postgresUrl,
76
+ DB_HOST: config.postgresHost,
77
+ DB_PORT: config.postgresPort,
78
+ DB_USER: config.postgresUser,
79
+ DB_PASSWORD: config.postgresPassword,
80
+ DB_NAME: config.postgresDatabase,
81
+ DB_POOL_MAX: config.postgresPoolMax,
82
+ DB_POOL_IDLE_TIMEOUT_MS: config.postgresIdleTimeoutMs,
83
+ DB_POOL_CONNECTION_TIMEOUT_MS: config.postgresConnectionTimeoutMs,
84
+ DATABASE_TYPE: 'auto',
85
+ SQLITE_PATH: config.sqlitePath,
86
+ DB_HEALTH_CHECK_INTERVAL_MS: config.healthCheckIntervalMs,
87
+ DB_CIRCUIT_BREAKER_THRESHOLD: config.circuitBreakerThreshold,
88
+ DB_CIRCUIT_BREAKER_TIMEOUT_MS: config.circuitBreakerTimeoutMs,
89
+ DB_CACHE_MAX_SIZE: config.cacheMaxSize,
90
+ DB_CACHE_TTL_MS: config.cacheTtlMs,
91
+ VECTOR_DIMENSION: config.vectorDimension,
92
+ VECTOR_MIN_SIMILARITY: config.vectorMinSimilarity,
93
+ });
94
+
95
+ const pool = new Pool({
96
+ connectionString,
97
+ connectionTimeoutMillis: 3000, // Quick timeout for detection
98
+ });
99
+
100
+ try {
101
+ const client = await pool.connect();
102
+ await client.query('SELECT 1');
103
+ client.release();
104
+ await pool.end();
105
+
106
+ logger.info('PostgreSQL connection successful');
107
+ return {
108
+ type: 'postgresql',
109
+ connectionString,
110
+ reason: 'PostgreSQL connection successful',
111
+ };
112
+ } finally {
113
+ await pool.end().catch(() => {});
114
+ }
115
+ } catch (error) {
116
+ const message = error instanceof Error ? error.message : 'Unknown error';
117
+ logger.debug('PostgreSQL not available, falling back to SQLite', { error: message });
118
+ }
119
+
120
+ // 4. Fall back to SQLite
121
+ const sqlitePath = config.sqlitePath ?? getDefaultSqlitePath();
122
+ return {
123
+ type: 'sqlite',
124
+ sqlitePath,
125
+ reason: 'PostgreSQL unavailable, using SQLite fallback',
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Create database adapter based on detection result
131
+ */
132
+ export async function createAdapter(detection: DetectionResult, config: DatabaseConfig): Promise<DatabaseAdapter> {
133
+ let adapter: DatabaseAdapter;
134
+
135
+ if (detection.type === 'postgresql') {
136
+ adapter = new PostgresAdapter(detection.connectionString!, config);
137
+ } else {
138
+ adapter = new SQLiteAdapter(detection.sqlitePath!, config);
139
+ }
140
+
141
+ await adapter.initialize();
142
+ return adapter;
143
+ }
144
+
145
+ /**
146
+ * Get or create the database adapter singleton
147
+ */
148
+ export async function getAdapter(): Promise<DatabaseAdapter> {
149
+ if (adapterInstance?.isInitialized) {
150
+ return adapterInstance;
151
+ }
152
+
153
+ if (!initPromise) {
154
+ initPromise = initializeAdapter();
155
+ }
156
+
157
+ return initPromise;
158
+ }
159
+
160
+ /**
161
+ * Initialize the database adapter
162
+ */
163
+ async function initializeAdapter(): Promise<DatabaseAdapter> {
164
+ const config = getDatabaseConfig();
165
+ const detection = await detectDatabase(config);
166
+
167
+ logger.info('Database detected', {
168
+ type: detection.type,
169
+ reason: detection.reason,
170
+ path: detection.sqlitePath,
171
+ });
172
+
173
+ adapterInstance = await createAdapter(detection, config);
174
+ return adapterInstance;
175
+ }
176
+
177
+ /**
178
+ * Close the database adapter
179
+ */
180
+ export async function closeAdapter(): Promise<void> {
181
+ if (adapterInstance) {
182
+ await adapterInstance.close();
183
+ adapterInstance = null;
184
+ initPromise = null;
185
+ logger.info('Database adapter closed');
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Reset adapter (for testing)
191
+ */
192
+ export function resetAdapter(): void {
193
+ adapterInstance = null;
194
+ initPromise = null;
195
+ }
196
+
197
+ /**
198
+ * Check if adapter is initialized
199
+ */
200
+ export function isAdapterInitialized(): boolean {
201
+ return adapterInstance?.isInitialized ?? false;
202
+ }
203
+
204
+ /**
205
+ * Get current adapter type
206
+ */
207
+ export function getAdapterType(): 'postgresql' | 'sqlite' | null {
208
+ return adapterInstance?.type ?? null;
209
+ }
210
+
211
+ // Re-export adapter classes
212
+ export { PostgresAdapter } from './postgres-adapter.js';
213
+ export { SQLiteAdapter } from './sqlite-adapter.js';
214
+ export { BaseAdapter } from './base-adapter.js';
215
+
216
+ export default {
217
+ getAdapter,
218
+ closeAdapter,
219
+ resetAdapter,
220
+ detectDatabase,
221
+ createAdapter,
222
+ isAdapterInitialized,
223
+ getAdapterType,
224
+ };
@@ -0,0 +1,285 @@
1
+ /**
2
+ * PostgreSQL Database Adapter
3
+ *
4
+ * Adapter for PostgreSQL with connection pooling and pgvector support.
5
+ */
6
+
7
+ import pg from 'pg';
8
+ import type { QueryResultRow } from 'pg';
9
+ import { drizzle } from 'drizzle-orm/node-postgres';
10
+ import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
11
+ import { sql } from 'drizzle-orm';
12
+
13
+ import { BaseAdapter } from './base-adapter.js';
14
+ import type {
15
+ QueryResult,
16
+ TransactionClient,
17
+ PoolStats,
18
+ DatabaseConfig,
19
+ } from '../types.js';
20
+ import { createLogger } from '../../utils/logger.js';
21
+ import { MemoryError } from '../../utils/errors.js';
22
+
23
+ import * as schema from '../drizzle/schema/index.js';
24
+
25
+ const { Pool } = pg;
26
+ const logger = createLogger('PostgresAdapter');
27
+
28
+ /**
29
+ * PostgreSQL database adapter with Drizzle ORM
30
+ */
31
+ export class PostgresAdapter extends BaseAdapter {
32
+ readonly type = 'postgresql' as const;
33
+
34
+ private pool: pg.Pool | null = null;
35
+ private drizzleInstance: NodePgDatabase<typeof schema> | null = null;
36
+ private connectionString: string;
37
+ private config: DatabaseConfig;
38
+
39
+ constructor(connectionString: string, config: DatabaseConfig) {
40
+ super();
41
+ this.connectionString = connectionString;
42
+ this.config = config;
43
+ }
44
+
45
+ /**
46
+ * Get Drizzle ORM instance
47
+ */
48
+ get drizzle(): NodePgDatabase<typeof schema> {
49
+ this.ensureInitialized();
50
+ return this.drizzleInstance!;
51
+ }
52
+
53
+ /**
54
+ * Get raw pool (for advanced operations)
55
+ */
56
+ get rawPool(): pg.Pool {
57
+ this.ensureInitialized();
58
+ return this.pool!;
59
+ }
60
+
61
+ /**
62
+ * Initialize PostgreSQL connection pool
63
+ */
64
+ async initialize(): Promise<void> {
65
+ if (this._isInitialized) return;
66
+
67
+ try {
68
+ this.pool = new Pool({
69
+ connectionString: this.connectionString,
70
+ max: this.config.postgresPoolMax,
71
+ idleTimeoutMillis: this.config.postgresIdleTimeoutMs,
72
+ connectionTimeoutMillis: this.config.postgresConnectionTimeoutMs,
73
+ });
74
+
75
+ // Set up event handlers
76
+ this.pool.on('error', (err) => {
77
+ logger.error('Pool error', { error: err.message });
78
+ });
79
+
80
+ this.pool.on('connect', () => {
81
+ logger.debug('New client connected to pool');
82
+ });
83
+
84
+ // Create Drizzle instance
85
+ this.drizzleInstance = drizzle(this.pool, {
86
+ schema,
87
+ logger: process.env.NODE_ENV === 'development',
88
+ });
89
+
90
+ // Test connection and enable pgvector
91
+ await this.testConnection();
92
+ await this.enablePgVector();
93
+
94
+ this._isInitialized = true;
95
+ logger.info('PostgreSQL adapter initialized', {
96
+ host: this.config.postgresHost,
97
+ database: this.config.postgresDatabase,
98
+ });
99
+ } catch (error) {
100
+ const message = error instanceof Error ? error.message : 'Unknown error';
101
+ logger.error('Failed to initialize PostgreSQL', { error: message });
102
+ throw new MemoryError(`PostgreSQL initialization failed: ${message}`);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Test database connection
108
+ */
109
+ private async testConnection(): Promise<void> {
110
+ const client = await this.pool!.connect();
111
+ try {
112
+ await client.query('SELECT 1');
113
+ } finally {
114
+ client.release();
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Enable pgvector extension if not already enabled
120
+ */
121
+ private async enablePgVector(): Promise<void> {
122
+ try {
123
+ await this.pool!.query('CREATE EXTENSION IF NOT EXISTS vector');
124
+ logger.debug('pgvector extension enabled');
125
+ } catch (error) {
126
+ logger.warn('Could not enable pgvector extension', {
127
+ error: error instanceof Error ? error.message : 'Unknown'
128
+ });
129
+ // Don't fail - extension might already exist or user might not have permissions
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Execute a raw SQL query
135
+ */
136
+ async query<T extends QueryResultRow = Record<string, any>>(
137
+ sqlText: string,
138
+ params?: unknown[]
139
+ ): Promise<QueryResult<T>> {
140
+ this.ensureInitialized();
141
+ const start = Date.now();
142
+
143
+ try {
144
+ const result = await this.pool!.query<T>(sqlText, params);
145
+ const duration = Date.now() - start;
146
+ this.logQuery(sqlText, duration, result.rowCount);
147
+
148
+ return {
149
+ rows: result.rows,
150
+ rowCount: result.rowCount,
151
+ };
152
+ } catch (error) {
153
+ this.logError(sqlText, error);
154
+ const message = error instanceof Error ? error.message : 'Unknown error';
155
+ throw new MemoryError(`Query failed: ${message}`);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Execute a transaction
161
+ */
162
+ async transaction<T>(
163
+ callback: (client: TransactionClient) => Promise<T>
164
+ ): Promise<T> {
165
+ this.ensureInitialized();
166
+ const client = await this.pool!.connect();
167
+
168
+ try {
169
+ await client.query('BEGIN');
170
+
171
+ const transactionClient: TransactionClient = {
172
+ query: async <R extends QueryResultRow = Record<string, any>>(
173
+ sqlText: string,
174
+ params?: unknown[]
175
+ ): Promise<QueryResult<R>> => {
176
+ const result = await client.query<R>(sqlText, params);
177
+ return {
178
+ rows: result.rows,
179
+ rowCount: result.rowCount,
180
+ };
181
+ },
182
+ };
183
+
184
+ const result = await callback(transactionClient);
185
+ await client.query('COMMIT');
186
+ return result;
187
+ } catch (error) {
188
+ await client.query('ROLLBACK');
189
+ throw error;
190
+ } finally {
191
+ client.release();
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Check database health
197
+ */
198
+ async healthCheck(): Promise<boolean> {
199
+ try {
200
+ const result = await this.query<{ ok: number }>('SELECT 1 as ok');
201
+ return result.rows.length > 0 && result.rows[0]!.ok === 1;
202
+ } catch {
203
+ return false;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Get connection pool statistics
209
+ */
210
+ getPoolStats(): PoolStats {
211
+ if (!this.pool) {
212
+ return { total: 0, idle: 0, waiting: 0 };
213
+ }
214
+
215
+ return {
216
+ total: this.pool.totalCount,
217
+ idle: this.pool.idleCount,
218
+ waiting: this.pool.waitingCount,
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Close all connections
224
+ */
225
+ async close(): Promise<void> {
226
+ if (this.pool) {
227
+ await this.pool.end();
228
+ this.pool = null;
229
+ this.drizzleInstance = null;
230
+ this._isInitialized = false;
231
+ logger.info('PostgreSQL pool closed');
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Execute vector similarity search
237
+ */
238
+ async vectorSearch<T = Record<string, unknown>>(
239
+ table: string,
240
+ embeddingColumn: string,
241
+ queryEmbedding: number[],
242
+ options: {
243
+ limit?: number;
244
+ minSimilarity?: number;
245
+ filters?: Record<string, unknown>;
246
+ selectColumns?: string[];
247
+ } = {}
248
+ ): Promise<Array<T & { similarity: number }>> {
249
+ const {
250
+ limit = 5,
251
+ minSimilarity = 0.7,
252
+ filters = {},
253
+ selectColumns = ['*'],
254
+ } = options;
255
+
256
+ const embeddingStr = `[${queryEmbedding.join(',')}]`;
257
+
258
+ // Build WHERE clause
259
+ const whereConditions = [`1 - (${embeddingColumn} <=> $1::vector) >= $2`];
260
+ const params: unknown[] = [embeddingStr, minSimilarity];
261
+ let paramIndex = 3;
262
+
263
+ for (const [key, value] of Object.entries(filters)) {
264
+ whereConditions.push(`${key} = $${paramIndex}`);
265
+ params.push(value);
266
+ paramIndex++;
267
+ }
268
+
269
+ params.push(limit);
270
+
271
+ const columns = selectColumns.join(', ');
272
+ const sql = `
273
+ SELECT ${columns}, 1 - (${embeddingColumn} <=> $1::vector) as similarity
274
+ FROM ${table}
275
+ WHERE ${whereConditions.join(' AND ')}
276
+ ORDER BY similarity DESC
277
+ LIMIT $${paramIndex}
278
+ `;
279
+
280
+ const result = await this.query<T & { similarity: number }>(sql, params);
281
+ return result.rows;
282
+ }
283
+ }
284
+
285
+ export default PostgresAdapter;