@juspay/hippocampus 0.1.0 → 0.1.1
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/client.d.ts +37 -67
- package/dist/client.js +241 -0
- package/dist/errors.js +19 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +6 -318
- package/dist/logger.js +61 -0
- package/dist/storage/redis.d.ts +14 -0
- package/dist/storage/redis.js +98 -0
- package/dist/storage/s3.d.ts +13 -0
- package/dist/storage/s3.js +108 -0
- package/dist/storage/sqlite.d.ts +11 -0
- package/dist/storage/sqlite.js +102 -0
- package/dist/types.d.ts +44 -159
- package/package.json +32 -17
package/dist/client.d.ts
CHANGED
|
@@ -1,69 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { HippocampusConfig } from './types';
|
|
2
2
|
export declare class Hippocampus {
|
|
3
|
-
private
|
|
4
|
-
private
|
|
5
|
-
private
|
|
6
|
-
private
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}>;
|
|
40
|
-
getTimeline(ownerId: string, entity: string): Promise<{
|
|
41
|
-
chronicles: Chronicle[];
|
|
42
|
-
}>;
|
|
43
|
-
getChronicle(id: string): Promise<{
|
|
44
|
-
chronicle: Chronicle;
|
|
45
|
-
}>;
|
|
46
|
-
updateChronicle(id: string, input: ChronicleUpdateInput): Promise<{
|
|
47
|
-
chronicle: Chronicle;
|
|
48
|
-
}>;
|
|
49
|
-
expireChronicle(id: string): Promise<void>;
|
|
50
|
-
createNexus(input: NexusCreateInput): Promise<{
|
|
51
|
-
nexus: Nexus;
|
|
52
|
-
}>;
|
|
53
|
-
getRelatedChronicles(chronicleId: string): Promise<{
|
|
54
|
-
related: {
|
|
55
|
-
nexus: Nexus;
|
|
56
|
-
chronicle: Chronicle;
|
|
57
|
-
}[];
|
|
58
|
-
}>;
|
|
59
|
-
health(): Promise<HealthResponse>;
|
|
60
|
-
status(): Promise<StatusResponse>;
|
|
61
|
-
runDecay(ownerId: string): Promise<{
|
|
62
|
-
affected: number;
|
|
63
|
-
}>;
|
|
64
|
-
private request;
|
|
65
|
-
private get;
|
|
66
|
-
private post;
|
|
67
|
-
private patch;
|
|
68
|
-
private del;
|
|
3
|
+
private storage;
|
|
4
|
+
private storageConfig;
|
|
5
|
+
private prompt;
|
|
6
|
+
private maxWords;
|
|
7
|
+
private neurolink;
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config?: HippocampusConfig);
|
|
10
|
+
private ensureStorage;
|
|
11
|
+
private ensureNeurolink;
|
|
12
|
+
/**
|
|
13
|
+
* Add/update memory for an owner.
|
|
14
|
+
*
|
|
15
|
+
* Fetches existing memory, merges with new content via LLM, stores result.
|
|
16
|
+
*
|
|
17
|
+
* @param ownerId - Unique identifier (user ID, session ID, etc.)
|
|
18
|
+
* @param content - New conversation content to incorporate
|
|
19
|
+
* @returns The condensed memory string that was stored, or empty string on failure
|
|
20
|
+
*/
|
|
21
|
+
add(ownerId: string, content: string): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Get the stored memory for an owner.
|
|
24
|
+
*
|
|
25
|
+
* @param ownerId - Unique identifier
|
|
26
|
+
* @returns The condensed memory string, or null if none exists or on failure
|
|
27
|
+
*/
|
|
28
|
+
get(ownerId: string): Promise<string | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Delete memory for an owner.
|
|
31
|
+
*
|
|
32
|
+
* @param ownerId - Unique identifier
|
|
33
|
+
*/
|
|
34
|
+
delete(ownerId: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Close storage connections and clean up resources.
|
|
37
|
+
*/
|
|
38
|
+
close(): Promise<void>;
|
|
69
39
|
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { logger } from './logger.js';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_PROMPT = `You are a memory condensation engine. You receive:
|
|
4
|
+
1. OLD_MEMORY: the user's existing memory summary (may be empty)
|
|
5
|
+
2. NEW_CONTENT: new conversation content
|
|
6
|
+
|
|
7
|
+
Your job: merge the old memory with relevant new information into a single condensed summary.
|
|
8
|
+
|
|
9
|
+
Rules:
|
|
10
|
+
- Output ONLY the condensed memory text, nothing else
|
|
11
|
+
- Maximum {{MAX_WORDS}} words
|
|
12
|
+
- Preserve important facts: names, preferences, goals, decisions, context
|
|
13
|
+
- Drop greetings, filler, redundant information
|
|
14
|
+
- If NEW_CONTENT has nothing worth remembering, return OLD_MEMORY unchanged
|
|
15
|
+
- If OLD_MEMORY is empty and NEW_CONTENT has nothing worth remembering, return empty string
|
|
16
|
+
|
|
17
|
+
OLD_MEMORY:
|
|
18
|
+
{{OLD_MEMORY}}
|
|
19
|
+
|
|
20
|
+
NEW_CONTENT:
|
|
21
|
+
{{NEW_CONTENT}}
|
|
22
|
+
|
|
23
|
+
Condensed memory:`;
|
|
24
|
+
class Hippocampus {
|
|
25
|
+
storage = null;
|
|
26
|
+
storageConfig;
|
|
27
|
+
prompt;
|
|
28
|
+
maxWords;
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
neurolink = null;
|
|
31
|
+
config;
|
|
32
|
+
constructor(config = {}) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.storageConfig = config.storage || { type: 'sqlite' };
|
|
35
|
+
this.prompt = config.prompt || process.env.HC_CONDENSATION_PROMPT || DEFAULT_PROMPT;
|
|
36
|
+
this.maxWords = config.maxWords || 50;
|
|
37
|
+
logger.info('Hippocampus initialized', {
|
|
38
|
+
storage: this.storageConfig.type,
|
|
39
|
+
maxWords: this.maxWords,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async ensureStorage() {
|
|
43
|
+
if (this.storage) {
|
|
44
|
+
return this.storage;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
switch (this.storageConfig.type) {
|
|
48
|
+
case 'sqlite': {
|
|
49
|
+
const { SqliteStorage } = await import('./storage/sqlite.js');
|
|
50
|
+
this.storage = new SqliteStorage(this.storageConfig);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case 'redis': {
|
|
54
|
+
const { RedisStorage } = await import('./storage/redis.js');
|
|
55
|
+
this.storage = new RedisStorage(this.storageConfig);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case 's3': {
|
|
59
|
+
const { S3Storage } = await import('./storage/s3.js');
|
|
60
|
+
this.storage = new S3Storage(this.storageConfig);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
default:
|
|
64
|
+
logger.error('Unknown storage type', {
|
|
65
|
+
type: this.storageConfig.type,
|
|
66
|
+
});
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
logger.error('Failed to initialize storage backend', {
|
|
72
|
+
type: this.storageConfig.type,
|
|
73
|
+
error: error instanceof Error ? error.message : String(error),
|
|
74
|
+
});
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return this.storage;
|
|
78
|
+
}
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
async ensureNeurolink() {
|
|
81
|
+
if (this.neurolink) {
|
|
82
|
+
return this.neurolink;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const { NeuroLink } = await import('@juspay/neurolink');
|
|
86
|
+
this.neurolink = new NeuroLink();
|
|
87
|
+
logger.info('NeuroLink instance created for Hippocampus condensation');
|
|
88
|
+
return this.neurolink;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
logger.error('Failed to initialize NeuroLink for condensation', {
|
|
92
|
+
error: error instanceof Error ? error.message : String(error),
|
|
93
|
+
});
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Add/update memory for an owner.
|
|
99
|
+
*
|
|
100
|
+
* Fetches existing memory, merges with new content via LLM, stores result.
|
|
101
|
+
*
|
|
102
|
+
* @param ownerId - Unique identifier (user ID, session ID, etc.)
|
|
103
|
+
* @param content - New conversation content to incorporate
|
|
104
|
+
* @returns The condensed memory string that was stored, or empty string on failure
|
|
105
|
+
*/
|
|
106
|
+
async add(ownerId, content) {
|
|
107
|
+
try {
|
|
108
|
+
const storage = await this.ensureStorage();
|
|
109
|
+
if (!storage) {
|
|
110
|
+
return '';
|
|
111
|
+
}
|
|
112
|
+
const neurolink = await this.ensureNeurolink();
|
|
113
|
+
if (!neurolink) {
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
let oldMemory = '';
|
|
117
|
+
try {
|
|
118
|
+
oldMemory = (await storage.get(ownerId)) || '';
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
logger.warn('Failed to fetch existing memory, proceeding without it', {
|
|
122
|
+
ownerId,
|
|
123
|
+
error: error instanceof Error ? error.message : String(error),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const filledPrompt = this.prompt
|
|
127
|
+
.replaceAll('{{OLD_MEMORY}}', oldMemory || '(none)')
|
|
128
|
+
.replaceAll('{{NEW_CONTENT}}', content)
|
|
129
|
+
.replaceAll('{{MAX_WORDS}}', String(this.maxWords));
|
|
130
|
+
logger.debug('Condensing memory', {
|
|
131
|
+
ownerId,
|
|
132
|
+
oldMemoryLength: oldMemory.length,
|
|
133
|
+
newContentLength: content.length,
|
|
134
|
+
});
|
|
135
|
+
let condensed = '';
|
|
136
|
+
try {
|
|
137
|
+
const result = await neurolink.generate({
|
|
138
|
+
input: { text: filledPrompt },
|
|
139
|
+
provider: this.config.neurolink?.provider,
|
|
140
|
+
model: this.config.neurolink?.model,
|
|
141
|
+
temperature: this.config.neurolink?.temperature ?? 0.1,
|
|
142
|
+
disableTools: true,
|
|
143
|
+
});
|
|
144
|
+
condensed = (result?.content || '').trim();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
logger.error('LLM condensation call failed', {
|
|
148
|
+
ownerId,
|
|
149
|
+
error: error instanceof Error ? error.message : String(error),
|
|
150
|
+
});
|
|
151
|
+
return oldMemory;
|
|
152
|
+
}
|
|
153
|
+
if (condensed) {
|
|
154
|
+
try {
|
|
155
|
+
await storage.set(ownerId, condensed);
|
|
156
|
+
logger.info('Memory updated', { ownerId, words: condensed.split(/\s+/).length });
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
logger.error('Failed to persist condensed memory', {
|
|
160
|
+
ownerId,
|
|
161
|
+
error: error instanceof Error ? error.message : String(error),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else if (oldMemory) {
|
|
166
|
+
logger.debug('No new memory extracted, keeping existing', { ownerId });
|
|
167
|
+
}
|
|
168
|
+
return condensed || oldMemory;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger.error('Unexpected error in add()', {
|
|
172
|
+
ownerId,
|
|
173
|
+
error: error instanceof Error ? error.message : String(error),
|
|
174
|
+
});
|
|
175
|
+
return '';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get the stored memory for an owner.
|
|
180
|
+
*
|
|
181
|
+
* @param ownerId - Unique identifier
|
|
182
|
+
* @returns The condensed memory string, or null if none exists or on failure
|
|
183
|
+
*/
|
|
184
|
+
async get(ownerId) {
|
|
185
|
+
try {
|
|
186
|
+
const storage = await this.ensureStorage();
|
|
187
|
+
if (!storage) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return await storage.get(ownerId);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
logger.error('Failed to get memory', {
|
|
194
|
+
ownerId,
|
|
195
|
+
error: error instanceof Error ? error.message : String(error),
|
|
196
|
+
});
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Delete memory for an owner.
|
|
202
|
+
*
|
|
203
|
+
* @param ownerId - Unique identifier
|
|
204
|
+
*/
|
|
205
|
+
async delete(ownerId) {
|
|
206
|
+
try {
|
|
207
|
+
const storage = await this.ensureStorage();
|
|
208
|
+
if (!storage) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
await storage.delete(ownerId);
|
|
212
|
+
logger.info('Memory deleted', { ownerId });
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
logger.error('Failed to delete memory', {
|
|
216
|
+
ownerId,
|
|
217
|
+
error: error instanceof Error ? error.message : String(error),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Close storage connections and clean up resources.
|
|
223
|
+
*/
|
|
224
|
+
async close() {
|
|
225
|
+
try {
|
|
226
|
+
if (this.storage) {
|
|
227
|
+
await this.storage.close();
|
|
228
|
+
this.storage = null;
|
|
229
|
+
}
|
|
230
|
+
logger.info('Hippocampus closed');
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
logger.error('Failed to close Hippocampus', {
|
|
234
|
+
error: error instanceof Error ? error.message : String(error),
|
|
235
|
+
});
|
|
236
|
+
this.storage = null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { Hippocampus };
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class HippocampusError extends Error {
|
|
2
|
+
statusCode;
|
|
3
|
+
details;
|
|
4
|
+
constructor(statusCode, message, details) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.details = details;
|
|
8
|
+
this.name = 'HippocampusError';
|
|
9
|
+
}
|
|
10
|
+
static fromResponse(status, body) {
|
|
11
|
+
if (body && typeof body === 'object' && 'error' in body) {
|
|
12
|
+
const err = body.error;
|
|
13
|
+
return new HippocampusError(status, err.message || 'Unknown error', err.details);
|
|
14
|
+
}
|
|
15
|
+
return new HippocampusError(status, `HTTP ${status}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { HippocampusError };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ export { Hippocampus } from './client';
|
|
|
2
2
|
export { HippocampusError } from './errors';
|
|
3
3
|
export { logger } from './logger';
|
|
4
4
|
export type { LogLevel } from './logger';
|
|
5
|
-
export type {
|
|
5
|
+
export type { StorageType, StorageBackend, StorageConfig, SqliteStorageConfig, RedisStorageConfig, S3StorageConfig, HippocampusConfig, } from './types';
|
|
6
|
+
export { SqliteStorage } from './storage/sqlite';
|
|
7
|
+
export { RedisStorage } from './storage/redis';
|
|
8
|
+
export { S3Storage } from './storage/s3';
|