praisonai 1.2.0 → 1.2.2
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/cache/index.d.ts +78 -0
- package/dist/cache/index.js +235 -0
- package/dist/db/postgres.d.ts +84 -0
- package/dist/db/postgres.js +266 -0
- package/dist/db/redis.d.ts +109 -0
- package/dist/db/redis.js +307 -0
- package/dist/db/sqlite.d.ts +66 -0
- package/dist/db/sqlite.js +339 -0
- package/dist/events/index.d.ts +84 -0
- package/dist/events/index.js +153 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +88 -2
- package/dist/integrations/index.d.ts +7 -0
- package/dist/integrations/index.js +26 -0
- package/dist/integrations/observability/base.d.ts +123 -0
- package/dist/integrations/observability/base.js +183 -0
- package/dist/integrations/observability/index.d.ts +8 -0
- package/dist/integrations/observability/index.js +29 -0
- package/dist/integrations/observability/langfuse.d.ts +32 -0
- package/dist/integrations/observability/langfuse.js +174 -0
- package/dist/integrations/vector/base.d.ts +110 -0
- package/dist/integrations/vector/base.js +158 -0
- package/dist/integrations/vector/chroma.d.ts +25 -0
- package/dist/integrations/vector/chroma.js +143 -0
- package/dist/integrations/vector/index.d.ts +14 -0
- package/dist/integrations/vector/index.js +37 -0
- package/dist/integrations/vector/pinecone.d.ts +28 -0
- package/dist/integrations/vector/pinecone.js +172 -0
- package/dist/integrations/vector/qdrant.d.ts +25 -0
- package/dist/integrations/vector/qdrant.js +146 -0
- package/dist/integrations/vector/weaviate.d.ts +30 -0
- package/dist/integrations/vector/weaviate.js +206 -0
- package/dist/integrations/voice/base.d.ts +76 -0
- package/dist/integrations/voice/base.js +168 -0
- package/dist/integrations/voice/index.d.ts +6 -0
- package/dist/integrations/voice/index.js +26 -0
- package/dist/knowledge/graph-rag.d.ts +125 -0
- package/dist/knowledge/graph-rag.js +289 -0
- package/dist/knowledge/index.d.ts +2 -0
- package/dist/knowledge/index.js +18 -0
- package/dist/knowledge/reranker.d.ts +86 -0
- package/dist/knowledge/reranker.js +196 -0
- package/dist/tools/arxivTools.d.ts +19 -6
- package/dist/tools/arxivTools.js +13 -7
- package/dist/tools/base.d.ts +97 -0
- package/dist/tools/base.js +147 -0
- package/dist/tools/index.d.ts +1 -11
- package/dist/tools/index.js +7 -11
- package/dist/tools/mcpSse.d.ts +5 -3
- package/dist/tools/mcpSse.js +6 -4
- package/dist/workflows/yaml-parser.d.ts +48 -0
- package/dist/workflows/yaml-parser.js +304 -0
- package/package.json +1 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache System - In-memory and persistent caching for agents
|
|
3
|
+
* Inspired by mastra's cache module
|
|
4
|
+
*/
|
|
5
|
+
export interface CacheConfig {
|
|
6
|
+
name?: string;
|
|
7
|
+
ttl?: number;
|
|
8
|
+
maxSize?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface CacheEntry<T = any> {
|
|
11
|
+
value: T;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
expiresAt?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Abstract base class for cache implementations
|
|
17
|
+
*/
|
|
18
|
+
export declare abstract class BaseCache {
|
|
19
|
+
readonly name: string;
|
|
20
|
+
constructor(config?: CacheConfig);
|
|
21
|
+
abstract get<T = any>(key: string): Promise<T | undefined>;
|
|
22
|
+
abstract set<T = any>(key: string, value: T, ttl?: number): Promise<void>;
|
|
23
|
+
abstract delete(key: string): Promise<boolean>;
|
|
24
|
+
abstract clear(): Promise<void>;
|
|
25
|
+
abstract has(key: string): Promise<boolean>;
|
|
26
|
+
abstract keys(): Promise<string[]>;
|
|
27
|
+
abstract size(): Promise<number>;
|
|
28
|
+
abstract listPush<T = any>(key: string, value: T): Promise<void>;
|
|
29
|
+
abstract listLength(key: string): Promise<number>;
|
|
30
|
+
abstract listRange<T = any>(key: string, start: number, end?: number): Promise<T[]>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* In-memory cache implementation
|
|
34
|
+
*/
|
|
35
|
+
export declare class MemoryCache extends BaseCache {
|
|
36
|
+
private cache;
|
|
37
|
+
private lists;
|
|
38
|
+
private ttl?;
|
|
39
|
+
private maxSize?;
|
|
40
|
+
constructor(config?: CacheConfig);
|
|
41
|
+
get<T = any>(key: string): Promise<T | undefined>;
|
|
42
|
+
set<T = any>(key: string, value: T, ttl?: number): Promise<void>;
|
|
43
|
+
delete(key: string): Promise<boolean>;
|
|
44
|
+
clear(): Promise<void>;
|
|
45
|
+
has(key: string): Promise<boolean>;
|
|
46
|
+
keys(): Promise<string[]>;
|
|
47
|
+
size(): Promise<number>;
|
|
48
|
+
listPush<T = any>(key: string, value: T): Promise<void>;
|
|
49
|
+
listLength(key: string): Promise<number>;
|
|
50
|
+
listRange<T = any>(key: string, start: number, end?: number): Promise<T[]>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* File-based cache implementation
|
|
54
|
+
*/
|
|
55
|
+
export declare class FileCache extends BaseCache {
|
|
56
|
+
private cacheDir;
|
|
57
|
+
private fs;
|
|
58
|
+
private path;
|
|
59
|
+
constructor(config?: CacheConfig & {
|
|
60
|
+
cacheDir?: string;
|
|
61
|
+
});
|
|
62
|
+
private ensureDir;
|
|
63
|
+
private getFilePath;
|
|
64
|
+
get<T = any>(key: string): Promise<T | undefined>;
|
|
65
|
+
set<T = any>(key: string, value: T, ttl?: number): Promise<void>;
|
|
66
|
+
delete(key: string): Promise<boolean>;
|
|
67
|
+
clear(): Promise<void>;
|
|
68
|
+
has(key: string): Promise<boolean>;
|
|
69
|
+
keys(): Promise<string[]>;
|
|
70
|
+
size(): Promise<number>;
|
|
71
|
+
listPush<T = any>(key: string, value: T): Promise<void>;
|
|
72
|
+
listLength(key: string): Promise<number>;
|
|
73
|
+
listRange<T = any>(key: string, start: number, end?: number): Promise<T[]>;
|
|
74
|
+
}
|
|
75
|
+
export declare function createMemoryCache(config?: CacheConfig): MemoryCache;
|
|
76
|
+
export declare function createFileCache(config?: CacheConfig & {
|
|
77
|
+
cacheDir?: string;
|
|
78
|
+
}): FileCache;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cache System - In-memory and persistent caching for agents
|
|
4
|
+
* Inspired by mastra's cache module
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.FileCache = exports.MemoryCache = exports.BaseCache = void 0;
|
|
41
|
+
exports.createMemoryCache = createMemoryCache;
|
|
42
|
+
exports.createFileCache = createFileCache;
|
|
43
|
+
/**
|
|
44
|
+
* Abstract base class for cache implementations
|
|
45
|
+
*/
|
|
46
|
+
class BaseCache {
|
|
47
|
+
constructor(config = {}) {
|
|
48
|
+
this.name = config.name || 'cache';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.BaseCache = BaseCache;
|
|
52
|
+
/**
|
|
53
|
+
* In-memory cache implementation
|
|
54
|
+
*/
|
|
55
|
+
class MemoryCache extends BaseCache {
|
|
56
|
+
constructor(config = {}) {
|
|
57
|
+
super(config);
|
|
58
|
+
this.cache = new Map();
|
|
59
|
+
this.lists = new Map();
|
|
60
|
+
this.ttl = config.ttl;
|
|
61
|
+
this.maxSize = config.maxSize;
|
|
62
|
+
}
|
|
63
|
+
async get(key) {
|
|
64
|
+
const entry = this.cache.get(key);
|
|
65
|
+
if (!entry)
|
|
66
|
+
return undefined;
|
|
67
|
+
// Check expiration
|
|
68
|
+
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
69
|
+
this.cache.delete(key);
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return entry.value;
|
|
73
|
+
}
|
|
74
|
+
async set(key, value, ttl) {
|
|
75
|
+
// Enforce max size
|
|
76
|
+
if (this.maxSize && this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
77
|
+
// Remove oldest entry
|
|
78
|
+
const firstKey = this.cache.keys().next().value;
|
|
79
|
+
if (firstKey)
|
|
80
|
+
this.cache.delete(firstKey);
|
|
81
|
+
}
|
|
82
|
+
const effectiveTtl = ttl ?? this.ttl;
|
|
83
|
+
const entry = {
|
|
84
|
+
value,
|
|
85
|
+
createdAt: Date.now(),
|
|
86
|
+
expiresAt: effectiveTtl ? Date.now() + effectiveTtl : undefined
|
|
87
|
+
};
|
|
88
|
+
this.cache.set(key, entry);
|
|
89
|
+
}
|
|
90
|
+
async delete(key) {
|
|
91
|
+
const existed = this.cache.has(key);
|
|
92
|
+
this.cache.delete(key);
|
|
93
|
+
this.lists.delete(key);
|
|
94
|
+
return existed;
|
|
95
|
+
}
|
|
96
|
+
async clear() {
|
|
97
|
+
this.cache.clear();
|
|
98
|
+
this.lists.clear();
|
|
99
|
+
}
|
|
100
|
+
async has(key) {
|
|
101
|
+
const entry = this.cache.get(key);
|
|
102
|
+
if (!entry)
|
|
103
|
+
return false;
|
|
104
|
+
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
105
|
+
this.cache.delete(key);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
async keys() {
|
|
111
|
+
return Array.from(this.cache.keys());
|
|
112
|
+
}
|
|
113
|
+
async size() {
|
|
114
|
+
return this.cache.size;
|
|
115
|
+
}
|
|
116
|
+
async listPush(key, value) {
|
|
117
|
+
if (!this.lists.has(key)) {
|
|
118
|
+
this.lists.set(key, []);
|
|
119
|
+
}
|
|
120
|
+
this.lists.get(key).push(value);
|
|
121
|
+
}
|
|
122
|
+
async listLength(key) {
|
|
123
|
+
return this.lists.get(key)?.length ?? 0;
|
|
124
|
+
}
|
|
125
|
+
async listRange(key, start, end) {
|
|
126
|
+
const list = this.lists.get(key) ?? [];
|
|
127
|
+
return list.slice(start, end);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.MemoryCache = MemoryCache;
|
|
131
|
+
/**
|
|
132
|
+
* File-based cache implementation
|
|
133
|
+
*/
|
|
134
|
+
class FileCache extends BaseCache {
|
|
135
|
+
constructor(config = {}) {
|
|
136
|
+
super(config);
|
|
137
|
+
this.cacheDir = config.cacheDir || '.cache';
|
|
138
|
+
}
|
|
139
|
+
async ensureDir() {
|
|
140
|
+
if (!this.fs) {
|
|
141
|
+
this.fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
142
|
+
this.path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
await this.fs.mkdir(this.cacheDir, { recursive: true });
|
|
146
|
+
}
|
|
147
|
+
catch { }
|
|
148
|
+
}
|
|
149
|
+
getFilePath(key) {
|
|
150
|
+
const safeKey = key.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
151
|
+
return this.path.join(this.cacheDir, `${safeKey}.json`);
|
|
152
|
+
}
|
|
153
|
+
async get(key) {
|
|
154
|
+
await this.ensureDir();
|
|
155
|
+
try {
|
|
156
|
+
const data = await this.fs.readFile(this.getFilePath(key), 'utf-8');
|
|
157
|
+
const entry = JSON.parse(data);
|
|
158
|
+
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
159
|
+
await this.delete(key);
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
return entry.value;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async set(key, value, ttl) {
|
|
169
|
+
await this.ensureDir();
|
|
170
|
+
const entry = {
|
|
171
|
+
value,
|
|
172
|
+
createdAt: Date.now(),
|
|
173
|
+
expiresAt: ttl ? Date.now() + ttl : undefined
|
|
174
|
+
};
|
|
175
|
+
await this.fs.writeFile(this.getFilePath(key), JSON.stringify(entry));
|
|
176
|
+
}
|
|
177
|
+
async delete(key) {
|
|
178
|
+
await this.ensureDir();
|
|
179
|
+
try {
|
|
180
|
+
await this.fs.unlink(this.getFilePath(key));
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async clear() {
|
|
188
|
+
await this.ensureDir();
|
|
189
|
+
try {
|
|
190
|
+
const files = await this.fs.readdir(this.cacheDir);
|
|
191
|
+
await Promise.all(files.filter((f) => f.endsWith('.json'))
|
|
192
|
+
.map((f) => this.fs.unlink(this.path.join(this.cacheDir, f))));
|
|
193
|
+
}
|
|
194
|
+
catch { }
|
|
195
|
+
}
|
|
196
|
+
async has(key) {
|
|
197
|
+
return (await this.get(key)) !== undefined;
|
|
198
|
+
}
|
|
199
|
+
async keys() {
|
|
200
|
+
await this.ensureDir();
|
|
201
|
+
try {
|
|
202
|
+
const files = await this.fs.readdir(this.cacheDir);
|
|
203
|
+
return files
|
|
204
|
+
.filter((f) => f.endsWith('.json'))
|
|
205
|
+
.map((f) => f.replace('.json', ''));
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async size() {
|
|
212
|
+
return (await this.keys()).length;
|
|
213
|
+
}
|
|
214
|
+
async listPush(key, value) {
|
|
215
|
+
const list = (await this.get(`list_${key}`)) || [];
|
|
216
|
+
list.push(value);
|
|
217
|
+
await this.set(`list_${key}`, list);
|
|
218
|
+
}
|
|
219
|
+
async listLength(key) {
|
|
220
|
+
const list = (await this.get(`list_${key}`)) || [];
|
|
221
|
+
return list.length;
|
|
222
|
+
}
|
|
223
|
+
async listRange(key, start, end) {
|
|
224
|
+
const list = (await this.get(`list_${key}`)) || [];
|
|
225
|
+
return list.slice(start, end);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
exports.FileCache = FileCache;
|
|
229
|
+
// Factory functions
|
|
230
|
+
function createMemoryCache(config) {
|
|
231
|
+
return new MemoryCache(config);
|
|
232
|
+
}
|
|
233
|
+
function createFileCache(config) {
|
|
234
|
+
return new FileCache(config);
|
|
235
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL Database Adapter
|
|
3
|
+
* For persistent storage of sessions, messages, and runs
|
|
4
|
+
*/
|
|
5
|
+
export interface PostgresConfig {
|
|
6
|
+
connectionString?: string;
|
|
7
|
+
host?: string;
|
|
8
|
+
port?: number;
|
|
9
|
+
database?: string;
|
|
10
|
+
user?: string;
|
|
11
|
+
password?: string;
|
|
12
|
+
ssl?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface PostgresAdapter {
|
|
15
|
+
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
|
16
|
+
execute(sql: string, params?: any[]): Promise<{
|
|
17
|
+
rowCount: number;
|
|
18
|
+
}>;
|
|
19
|
+
transaction<T>(fn: (client: PostgresAdapter) => Promise<T>): Promise<T>;
|
|
20
|
+
connect(): Promise<void>;
|
|
21
|
+
disconnect(): Promise<void>;
|
|
22
|
+
isConnected(): boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* PostgreSQL Adapter using pg-compatible REST APIs (like Neon, Supabase)
|
|
26
|
+
*/
|
|
27
|
+
export declare class NeonPostgresAdapter implements PostgresAdapter {
|
|
28
|
+
private connectionString;
|
|
29
|
+
private connected;
|
|
30
|
+
constructor(config: PostgresConfig);
|
|
31
|
+
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
|
32
|
+
execute(sql: string, params?: any[]): Promise<{
|
|
33
|
+
rowCount: number;
|
|
34
|
+
}>;
|
|
35
|
+
transaction<T>(fn: (client: PostgresAdapter) => Promise<T>): Promise<T>;
|
|
36
|
+
connect(): Promise<void>;
|
|
37
|
+
disconnect(): Promise<void>;
|
|
38
|
+
isConnected(): boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* In-memory PostgreSQL-like adapter for testing
|
|
42
|
+
*/
|
|
43
|
+
export declare class MemoryPostgresAdapter implements PostgresAdapter {
|
|
44
|
+
private tables;
|
|
45
|
+
private connected;
|
|
46
|
+
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
|
47
|
+
private handleSelect;
|
|
48
|
+
private handleInsert;
|
|
49
|
+
private handleUpdate;
|
|
50
|
+
private handleDelete;
|
|
51
|
+
private handleCreateTable;
|
|
52
|
+
execute(sql: string, params?: any[]): Promise<{
|
|
53
|
+
rowCount: number;
|
|
54
|
+
}>;
|
|
55
|
+
transaction<T>(fn: (client: PostgresAdapter) => Promise<T>): Promise<T>;
|
|
56
|
+
connect(): Promise<void>;
|
|
57
|
+
disconnect(): Promise<void>;
|
|
58
|
+
isConnected(): boolean;
|
|
59
|
+
getTable(name: string): any[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* PostgreSQL Session Storage
|
|
63
|
+
*/
|
|
64
|
+
export declare class PostgresSessionStorage {
|
|
65
|
+
private db;
|
|
66
|
+
private tableName;
|
|
67
|
+
private initialized;
|
|
68
|
+
constructor(db: PostgresAdapter, tableName?: string);
|
|
69
|
+
init(): Promise<void>;
|
|
70
|
+
createSession(id: string, data?: Record<string, any>): Promise<void>;
|
|
71
|
+
getSession(id: string): Promise<Record<string, any> | null>;
|
|
72
|
+
updateSession(id: string, data: Record<string, any>): Promise<void>;
|
|
73
|
+
deleteSession(id: string): Promise<void>;
|
|
74
|
+
addMessage(sessionId: string, message: {
|
|
75
|
+
id: string;
|
|
76
|
+
role: string;
|
|
77
|
+
content: string;
|
|
78
|
+
toolCalls?: any;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
getMessages(sessionId: string, limit?: number): Promise<any[]>;
|
|
81
|
+
}
|
|
82
|
+
export declare function createNeonPostgres(config: PostgresConfig): NeonPostgresAdapter;
|
|
83
|
+
export declare function createMemoryPostgres(): MemoryPostgresAdapter;
|
|
84
|
+
export declare function createPostgresSessionStorage(db: PostgresAdapter, tableName?: string): PostgresSessionStorage;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PostgreSQL Database Adapter
|
|
4
|
+
* For persistent storage of sessions, messages, and runs
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.PostgresSessionStorage = exports.MemoryPostgresAdapter = exports.NeonPostgresAdapter = void 0;
|
|
8
|
+
exports.createNeonPostgres = createNeonPostgres;
|
|
9
|
+
exports.createMemoryPostgres = createMemoryPostgres;
|
|
10
|
+
exports.createPostgresSessionStorage = createPostgresSessionStorage;
|
|
11
|
+
/**
|
|
12
|
+
* PostgreSQL Adapter using pg-compatible REST APIs (like Neon, Supabase)
|
|
13
|
+
*/
|
|
14
|
+
class NeonPostgresAdapter {
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.connected = false;
|
|
17
|
+
if (config.connectionString) {
|
|
18
|
+
this.connectionString = config.connectionString;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const { host, port, database, user, password, ssl } = config;
|
|
22
|
+
this.connectionString = `postgres://${user}:${password}@${host}:${port || 5432}/${database}${ssl ? '?sslmode=require' : ''}`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async query(sql, params) {
|
|
26
|
+
// Use Neon serverless driver pattern
|
|
27
|
+
const response = await fetch(this.connectionString.replace('postgres://', 'https://').split('/')[0] + '/sql', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'Neon-Connection-String': this.connectionString
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
query: sql,
|
|
35
|
+
params: params || []
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const error = await response.text();
|
|
40
|
+
throw new Error(`PostgreSQL error: ${response.status} - ${error}`);
|
|
41
|
+
}
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
return data.rows || [];
|
|
44
|
+
}
|
|
45
|
+
async execute(sql, params) {
|
|
46
|
+
const rows = await this.query(sql, params);
|
|
47
|
+
return { rowCount: rows.length };
|
|
48
|
+
}
|
|
49
|
+
async transaction(fn) {
|
|
50
|
+
await this.execute('BEGIN');
|
|
51
|
+
try {
|
|
52
|
+
const result = await fn(this);
|
|
53
|
+
await this.execute('COMMIT');
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
await this.execute('ROLLBACK');
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async connect() {
|
|
62
|
+
// Test connection
|
|
63
|
+
await this.query('SELECT 1');
|
|
64
|
+
this.connected = true;
|
|
65
|
+
}
|
|
66
|
+
async disconnect() {
|
|
67
|
+
this.connected = false;
|
|
68
|
+
}
|
|
69
|
+
isConnected() {
|
|
70
|
+
return this.connected;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.NeonPostgresAdapter = NeonPostgresAdapter;
|
|
74
|
+
/**
|
|
75
|
+
* In-memory PostgreSQL-like adapter for testing
|
|
76
|
+
*/
|
|
77
|
+
class MemoryPostgresAdapter {
|
|
78
|
+
constructor() {
|
|
79
|
+
this.tables = new Map();
|
|
80
|
+
this.connected = false;
|
|
81
|
+
}
|
|
82
|
+
async query(sql, params) {
|
|
83
|
+
// Very basic SQL parsing for testing
|
|
84
|
+
const normalizedSql = sql.toLowerCase().trim();
|
|
85
|
+
if (normalizedSql.startsWith('select')) {
|
|
86
|
+
return this.handleSelect(sql, params);
|
|
87
|
+
}
|
|
88
|
+
else if (normalizedSql.startsWith('insert')) {
|
|
89
|
+
return this.handleInsert(sql, params);
|
|
90
|
+
}
|
|
91
|
+
else if (normalizedSql.startsWith('update')) {
|
|
92
|
+
return this.handleUpdate(sql, params);
|
|
93
|
+
}
|
|
94
|
+
else if (normalizedSql.startsWith('delete')) {
|
|
95
|
+
return this.handleDelete(sql, params);
|
|
96
|
+
}
|
|
97
|
+
else if (normalizedSql.startsWith('create table')) {
|
|
98
|
+
this.handleCreateTable(sql);
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
handleSelect(sql, params) {
|
|
104
|
+
const match = sql.match(/from\s+(\w+)/i);
|
|
105
|
+
if (!match)
|
|
106
|
+
return [];
|
|
107
|
+
const tableName = match[1];
|
|
108
|
+
const rows = this.tables.get(tableName) || [];
|
|
109
|
+
// Handle WHERE clause
|
|
110
|
+
const whereMatch = sql.match(/where\s+(\w+)\s*=\s*\$(\d+)/i);
|
|
111
|
+
if (whereMatch && params) {
|
|
112
|
+
const field = whereMatch[1];
|
|
113
|
+
const paramIndex = parseInt(whereMatch[2]) - 1;
|
|
114
|
+
const value = params[paramIndex];
|
|
115
|
+
return rows.filter(row => row[field] === value);
|
|
116
|
+
}
|
|
117
|
+
return rows;
|
|
118
|
+
}
|
|
119
|
+
handleInsert(sql, params) {
|
|
120
|
+
const match = sql.match(/insert\s+into\s+(\w+)\s*\(([^)]+)\)/i);
|
|
121
|
+
if (!match || !params)
|
|
122
|
+
return [];
|
|
123
|
+
const tableName = match[1];
|
|
124
|
+
const columns = match[2].split(',').map(c => c.trim());
|
|
125
|
+
if (!this.tables.has(tableName)) {
|
|
126
|
+
this.tables.set(tableName, []);
|
|
127
|
+
}
|
|
128
|
+
const row = {};
|
|
129
|
+
columns.forEach((col, i) => {
|
|
130
|
+
row[col] = params[i];
|
|
131
|
+
});
|
|
132
|
+
this.tables.get(tableName).push(row);
|
|
133
|
+
return [row];
|
|
134
|
+
}
|
|
135
|
+
handleUpdate(sql, params) {
|
|
136
|
+
const match = sql.match(/update\s+(\w+)\s+set/i);
|
|
137
|
+
if (!match || !params)
|
|
138
|
+
return [];
|
|
139
|
+
const tableName = match[1];
|
|
140
|
+
const rows = this.tables.get(tableName) || [];
|
|
141
|
+
// Simple update - just return affected count
|
|
142
|
+
return [{ count: rows.length }];
|
|
143
|
+
}
|
|
144
|
+
handleDelete(sql, params) {
|
|
145
|
+
const match = sql.match(/delete\s+from\s+(\w+)/i);
|
|
146
|
+
if (!match)
|
|
147
|
+
return [];
|
|
148
|
+
const tableName = match[1];
|
|
149
|
+
const whereMatch = sql.match(/where\s+(\w+)\s*=\s*\$(\d+)/i);
|
|
150
|
+
if (whereMatch && params) {
|
|
151
|
+
const field = whereMatch[1];
|
|
152
|
+
const paramIndex = parseInt(whereMatch[2]) - 1;
|
|
153
|
+
const value = params[paramIndex];
|
|
154
|
+
const rows = this.tables.get(tableName) || [];
|
|
155
|
+
const filtered = rows.filter(row => row[field] !== value);
|
|
156
|
+
this.tables.set(tableName, filtered);
|
|
157
|
+
return [{ count: rows.length - filtered.length }];
|
|
158
|
+
}
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
handleCreateTable(sql) {
|
|
162
|
+
const match = sql.match(/create\s+table\s+(?:if\s+not\s+exists\s+)?(\w+)/i);
|
|
163
|
+
if (match) {
|
|
164
|
+
const tableName = match[1];
|
|
165
|
+
if (!this.tables.has(tableName)) {
|
|
166
|
+
this.tables.set(tableName, []);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async execute(sql, params) {
|
|
171
|
+
const rows = await this.query(sql, params);
|
|
172
|
+
return { rowCount: rows.length };
|
|
173
|
+
}
|
|
174
|
+
async transaction(fn) {
|
|
175
|
+
return fn(this);
|
|
176
|
+
}
|
|
177
|
+
async connect() {
|
|
178
|
+
this.connected = true;
|
|
179
|
+
}
|
|
180
|
+
async disconnect() {
|
|
181
|
+
this.connected = false;
|
|
182
|
+
this.tables.clear();
|
|
183
|
+
}
|
|
184
|
+
isConnected() {
|
|
185
|
+
return this.connected;
|
|
186
|
+
}
|
|
187
|
+
// Helper for testing
|
|
188
|
+
getTable(name) {
|
|
189
|
+
return this.tables.get(name) || [];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.MemoryPostgresAdapter = MemoryPostgresAdapter;
|
|
193
|
+
/**
|
|
194
|
+
* PostgreSQL Session Storage
|
|
195
|
+
*/
|
|
196
|
+
class PostgresSessionStorage {
|
|
197
|
+
constructor(db, tableName = 'sessions') {
|
|
198
|
+
this.initialized = false;
|
|
199
|
+
this.db = db;
|
|
200
|
+
this.tableName = tableName;
|
|
201
|
+
}
|
|
202
|
+
async init() {
|
|
203
|
+
if (this.initialized)
|
|
204
|
+
return;
|
|
205
|
+
await this.db.execute(`
|
|
206
|
+
CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
207
|
+
id TEXT PRIMARY KEY,
|
|
208
|
+
data JSONB NOT NULL,
|
|
209
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
210
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
211
|
+
)
|
|
212
|
+
`);
|
|
213
|
+
await this.db.execute(`
|
|
214
|
+
CREATE TABLE IF NOT EXISTS ${this.tableName}_messages (
|
|
215
|
+
id TEXT PRIMARY KEY,
|
|
216
|
+
session_id TEXT NOT NULL REFERENCES ${this.tableName}(id),
|
|
217
|
+
role TEXT NOT NULL,
|
|
218
|
+
content TEXT NOT NULL,
|
|
219
|
+
tool_calls JSONB,
|
|
220
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
221
|
+
)
|
|
222
|
+
`);
|
|
223
|
+
this.initialized = true;
|
|
224
|
+
}
|
|
225
|
+
async createSession(id, data = {}) {
|
|
226
|
+
await this.init();
|
|
227
|
+
await this.db.execute(`INSERT INTO ${this.tableName} (id, data) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET data = $2, updated_at = CURRENT_TIMESTAMP`, [id, JSON.stringify(data)]);
|
|
228
|
+
}
|
|
229
|
+
async getSession(id) {
|
|
230
|
+
await this.init();
|
|
231
|
+
const rows = await this.db.query(`SELECT data FROM ${this.tableName} WHERE id = $1`, [id]);
|
|
232
|
+
return rows[0]?.data || null;
|
|
233
|
+
}
|
|
234
|
+
async updateSession(id, data) {
|
|
235
|
+
await this.init();
|
|
236
|
+
await this.db.execute(`UPDATE ${this.tableName} SET data = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2`, [JSON.stringify(data), id]);
|
|
237
|
+
}
|
|
238
|
+
async deleteSession(id) {
|
|
239
|
+
await this.init();
|
|
240
|
+
await this.db.execute(`DELETE FROM ${this.tableName}_messages WHERE session_id = $1`, [id]);
|
|
241
|
+
await this.db.execute(`DELETE FROM ${this.tableName} WHERE id = $1`, [id]);
|
|
242
|
+
}
|
|
243
|
+
async addMessage(sessionId, message) {
|
|
244
|
+
await this.init();
|
|
245
|
+
await this.db.execute(`INSERT INTO ${this.tableName}_messages (id, session_id, role, content, tool_calls) VALUES ($1, $2, $3, $4, $5)`, [message.id, sessionId, message.role, message.content, message.toolCalls ? JSON.stringify(message.toolCalls) : null]);
|
|
246
|
+
}
|
|
247
|
+
async getMessages(sessionId, limit) {
|
|
248
|
+
await this.init();
|
|
249
|
+
let sql = `SELECT * FROM ${this.tableName}_messages WHERE session_id = $1 ORDER BY created_at ASC`;
|
|
250
|
+
if (limit) {
|
|
251
|
+
sql += ` LIMIT ${limit}`;
|
|
252
|
+
}
|
|
253
|
+
return this.db.query(sql, [sessionId]);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
exports.PostgresSessionStorage = PostgresSessionStorage;
|
|
257
|
+
// Factory functions
|
|
258
|
+
function createNeonPostgres(config) {
|
|
259
|
+
return new NeonPostgresAdapter(config);
|
|
260
|
+
}
|
|
261
|
+
function createMemoryPostgres() {
|
|
262
|
+
return new MemoryPostgresAdapter();
|
|
263
|
+
}
|
|
264
|
+
function createPostgresSessionStorage(db, tableName) {
|
|
265
|
+
return new PostgresSessionStorage(db, tableName);
|
|
266
|
+
}
|