@maximem/synap-js-sdk 0.1.3 → 0.2.0
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/README.md +25 -6
- package/bridge/synap_bridge.py +263 -40
- package/package.json +1 -1
- package/src/setup-typescript.js +20 -0
- package/src/synap-client.js +299 -19
- package/types/index.d.ts +198 -6
package/README.md
CHANGED
|
@@ -18,10 +18,10 @@ npm install @maximem/synap-js-sdk
|
|
|
18
18
|
Install the Python runtime used by the wrapper:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx synap-js-sdk setup --sdk-version 0.
|
|
21
|
+
npx synap-js-sdk setup --sdk-version 0.2.0
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
If you want the latest Python SDK version, omit `--sdk-version`.
|
|
24
|
+
If you want the latest Python SDK version, omit `--sdk-version` and pass `--upgrade`.
|
|
25
25
|
|
|
26
26
|
## Verify Runtime
|
|
27
27
|
|
|
@@ -65,16 +65,27 @@ async function run() {
|
|
|
65
65
|
|
|
66
66
|
await synap.addMemory({
|
|
67
67
|
userId: 'user-123',
|
|
68
|
+
customerId: 'customer-456',
|
|
69
|
+
conversationId: 'conv-123',
|
|
68
70
|
messages: [{ role: 'user', content: 'My name is Alex and I live in Austin.' }],
|
|
69
71
|
});
|
|
70
72
|
|
|
71
|
-
const
|
|
73
|
+
const context = await synap.fetchUserContext({
|
|
72
74
|
userId: 'user-123',
|
|
73
|
-
|
|
75
|
+
customerId: 'customer-456',
|
|
76
|
+
conversationId: 'conv-123',
|
|
77
|
+
searchQuery: ['Where does the user live?'],
|
|
74
78
|
maxResults: 10,
|
|
75
79
|
});
|
|
76
80
|
|
|
77
|
-
console.log(
|
|
81
|
+
console.log(context.facts);
|
|
82
|
+
|
|
83
|
+
const promptContext = await synap.getContextForPrompt({
|
|
84
|
+
conversationId: 'conv-123',
|
|
85
|
+
style: 'structured',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
console.log(promptContext.formattedContext);
|
|
78
89
|
await synap.shutdown();
|
|
79
90
|
}
|
|
80
91
|
|
|
@@ -97,9 +108,17 @@ This command can:
|
|
|
97
108
|
## Single-Flow Setup (JS + TS)
|
|
98
109
|
|
|
99
110
|
```bash
|
|
100
|
-
npm install @maximem/synap-js-sdk && npx synap-js-sdk setup --sdk-version 0.
|
|
111
|
+
npm install @maximem/synap-js-sdk && npx synap-js-sdk setup --sdk-version 0.2.0 && npx synap-js-sdk setup-ts
|
|
101
112
|
```
|
|
102
113
|
|
|
114
|
+
## API Notes
|
|
115
|
+
|
|
116
|
+
- `addMemory()` now requires `customerId` to match the Python SDK's explicit ingestion scope.
|
|
117
|
+
- `fetchUserContext()`, `fetchCustomerContext()`, and `fetchClientContext()` expose the structured Python `ContextResponse` surface in JS/TS.
|
|
118
|
+
- `getContextForPrompt()` exposes compacted context plus recent un-compacted messages.
|
|
119
|
+
- `searchMemory()` and `getMemories()` remain convenience helpers built on top of user-scoped context fetches.
|
|
120
|
+
- Temporal fields are exposed in JS/TS as `eventDate`, `validUntil`, `temporalCategory`, `temporalConfidence`, plus top-level `temporalEvents`.
|
|
121
|
+
|
|
103
122
|
## CLI Commands
|
|
104
123
|
|
|
105
124
|
```bash
|
package/bridge/synap_bridge.py
CHANGED
|
@@ -5,7 +5,9 @@ Protocol:
|
|
|
5
5
|
stdin -> {"id": 1, "method": "init", "params": {...}}\n
|
|
6
6
|
stdout <- {"id": 1, "result": {...}, "error": null}\n
|
|
7
7
|
Methods:
|
|
8
|
-
init, add_memory, search_memory, get_memories,
|
|
8
|
+
init, add_memory, search_memory, get_memories, fetch_user_context,
|
|
9
|
+
fetch_customer_context, fetch_client_context, get_context_for_prompt,
|
|
10
|
+
delete_memory, shutdown
|
|
9
11
|
"""
|
|
10
12
|
|
|
11
13
|
import asyncio
|
|
@@ -51,6 +53,92 @@ def write_response(obj: dict) -> None:
|
|
|
51
53
|
sys.stdout.flush()
|
|
52
54
|
|
|
53
55
|
|
|
56
|
+
def tracking_key(user_id: str, customer_id: Optional[str]) -> str:
|
|
57
|
+
"""Scope tracked memory IDs by both customer and user."""
|
|
58
|
+
return f"{customer_id or ''}::{user_id}"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def serialize_context_response(context) -> dict:
|
|
62
|
+
"""Serialize a Python ContextResponse for the JS bridge."""
|
|
63
|
+
payload = context.model_dump(mode="json")
|
|
64
|
+
payload["raw_response"] = context.raw if hasattr(context, "raw") else {}
|
|
65
|
+
return payload
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def serialize_context_for_prompt_response(response) -> dict:
|
|
69
|
+
"""Serialize a Python ContextForPromptResponse for the JS bridge."""
|
|
70
|
+
return response.model_dump(mode="json")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def flatten_context_items(context) -> List[dict]:
|
|
74
|
+
"""Convert typed context collections into a flat memory list."""
|
|
75
|
+
items: List[dict] = []
|
|
76
|
+
for fact in context.facts:
|
|
77
|
+
items.append({
|
|
78
|
+
"id": fact.id,
|
|
79
|
+
"memory": fact.content,
|
|
80
|
+
"score": fact.confidence,
|
|
81
|
+
"source": fact.source,
|
|
82
|
+
"metadata": fact.metadata,
|
|
83
|
+
"context_type": "fact",
|
|
84
|
+
"event_date": str(fact.event_date) if getattr(fact, "event_date", None) else None,
|
|
85
|
+
"valid_until": str(fact.valid_until) if getattr(fact, "valid_until", None) else None,
|
|
86
|
+
"temporal_category": getattr(fact, "temporal_category", None),
|
|
87
|
+
"temporal_confidence": getattr(fact, "temporal_confidence", 0.0),
|
|
88
|
+
})
|
|
89
|
+
for preference in context.preferences:
|
|
90
|
+
items.append({
|
|
91
|
+
"id": preference.id,
|
|
92
|
+
"memory": preference.content,
|
|
93
|
+
"score": preference.strength,
|
|
94
|
+
"source": getattr(preference, "source", ""),
|
|
95
|
+
"metadata": preference.metadata,
|
|
96
|
+
"context_type": "preference",
|
|
97
|
+
"event_date": str(preference.event_date) if getattr(preference, "event_date", None) else None,
|
|
98
|
+
"valid_until": str(preference.valid_until) if getattr(preference, "valid_until", None) else None,
|
|
99
|
+
"temporal_category": getattr(preference, "temporal_category", None),
|
|
100
|
+
"temporal_confidence": getattr(preference, "temporal_confidence", 0.0),
|
|
101
|
+
})
|
|
102
|
+
for episode in context.episodes:
|
|
103
|
+
items.append({
|
|
104
|
+
"id": episode.id,
|
|
105
|
+
"memory": episode.summary,
|
|
106
|
+
"score": episode.significance,
|
|
107
|
+
"metadata": episode.metadata,
|
|
108
|
+
"context_type": "episode",
|
|
109
|
+
"event_date": str(episode.event_date) if getattr(episode, "event_date", None) else None,
|
|
110
|
+
"valid_until": str(episode.valid_until) if getattr(episode, "valid_until", None) else None,
|
|
111
|
+
"temporal_category": getattr(episode, "temporal_category", None),
|
|
112
|
+
"temporal_confidence": getattr(episode, "temporal_confidence", 0.0),
|
|
113
|
+
})
|
|
114
|
+
for emotion in context.emotions:
|
|
115
|
+
items.append({
|
|
116
|
+
"id": emotion.id,
|
|
117
|
+
"memory": emotion.context,
|
|
118
|
+
"score": emotion.intensity,
|
|
119
|
+
"metadata": emotion.metadata,
|
|
120
|
+
"context_type": "emotion",
|
|
121
|
+
"event_date": str(emotion.event_date) if getattr(emotion, "event_date", None) else None,
|
|
122
|
+
"valid_until": str(emotion.valid_until) if getattr(emotion, "valid_until", None) else None,
|
|
123
|
+
"temporal_category": getattr(emotion, "temporal_category", None),
|
|
124
|
+
"temporal_confidence": getattr(emotion, "temporal_confidence", 0.0),
|
|
125
|
+
})
|
|
126
|
+
for event in getattr(context, "temporal_events", []):
|
|
127
|
+
items.append({
|
|
128
|
+
"id": event.id,
|
|
129
|
+
"memory": event.content,
|
|
130
|
+
"score": event.temporal_confidence,
|
|
131
|
+
"source": event.source,
|
|
132
|
+
"metadata": event.metadata,
|
|
133
|
+
"context_type": "temporal_event",
|
|
134
|
+
"event_date": str(event.event_date) if event.event_date else None,
|
|
135
|
+
"valid_until": str(event.valid_until) if event.valid_until else None,
|
|
136
|
+
"temporal_category": event.temporal_category,
|
|
137
|
+
"temporal_confidence": event.temporal_confidence,
|
|
138
|
+
})
|
|
139
|
+
return items
|
|
140
|
+
|
|
141
|
+
|
|
54
142
|
def messages_to_text(messages: List[dict]) -> str:
|
|
55
143
|
lines: List[str] = []
|
|
56
144
|
for message in messages:
|
|
@@ -147,7 +235,13 @@ async def handle_add_memory(params: dict) -> dict:
|
|
|
147
235
|
timings: List[dict] = []
|
|
148
236
|
|
|
149
237
|
user_id = params["user_id"]
|
|
238
|
+
customer_id = params.get("customer_id")
|
|
150
239
|
messages = params["messages"]
|
|
240
|
+
conversation_id = params.get("conversation_id")
|
|
241
|
+
session_id = params.get("session_id")
|
|
242
|
+
|
|
243
|
+
if not customer_id:
|
|
244
|
+
raise ValueError("customer_id is required")
|
|
151
245
|
|
|
152
246
|
step = time.perf_counter()
|
|
153
247
|
transcript = messages_to_text(messages)
|
|
@@ -168,12 +262,27 @@ async def handle_add_memory(params: dict) -> dict:
|
|
|
168
262
|
start = time.perf_counter()
|
|
169
263
|
|
|
170
264
|
step = time.perf_counter()
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
265
|
+
mode = params.get("mode", "long-range")
|
|
266
|
+
document_type = params.get("document_type", "ai-chat-conversation")
|
|
267
|
+
document_id = params.get("document_id")
|
|
268
|
+
document_created_at = params.get("document_created_at")
|
|
269
|
+
metadata = params.get("metadata")
|
|
270
|
+
|
|
271
|
+
create_kwargs: dict = {
|
|
272
|
+
"document": transcript,
|
|
273
|
+
"document_type": document_type,
|
|
274
|
+
"user_id": user_id,
|
|
275
|
+
"customer_id": customer_id,
|
|
276
|
+
"mode": mode,
|
|
277
|
+
}
|
|
278
|
+
if document_id is not None:
|
|
279
|
+
create_kwargs["document_id"] = document_id
|
|
280
|
+
if document_created_at is not None:
|
|
281
|
+
create_kwargs["document_created_at"] = document_created_at
|
|
282
|
+
if metadata is not None:
|
|
283
|
+
create_kwargs["metadata"] = metadata
|
|
284
|
+
|
|
285
|
+
create_result = await sdk.memories.create(**create_kwargs)
|
|
177
286
|
append_step(timings, "memories_create", step)
|
|
178
287
|
|
|
179
288
|
ingestion_id = create_result.ingestion_id
|
|
@@ -185,10 +294,16 @@ async def handle_add_memory(params: dict) -> dict:
|
|
|
185
294
|
if not content:
|
|
186
295
|
continue
|
|
187
296
|
try:
|
|
297
|
+
role = message.get("role", "user")
|
|
188
298
|
await sdk.instance.send_message(
|
|
189
299
|
content=content,
|
|
190
|
-
role=
|
|
300
|
+
role=role,
|
|
301
|
+
conversation_id=conversation_id,
|
|
191
302
|
user_id=user_id,
|
|
303
|
+
customer_id=customer_id,
|
|
304
|
+
session_id=session_id,
|
|
305
|
+
event_type="assistant_message" if role == "assistant" else "user_message",
|
|
306
|
+
metadata=message.get("metadata"),
|
|
192
307
|
)
|
|
193
308
|
except Exception as exc:
|
|
194
309
|
logger.debug("gRPC send_message failed (non-fatal): %s", exc)
|
|
@@ -217,7 +332,7 @@ async def handle_add_memory(params: dict) -> dict:
|
|
|
217
332
|
|
|
218
333
|
memory_ids = [str(memory_id) for memory_id in (final_status.memory_ids or [])]
|
|
219
334
|
if memory_ids:
|
|
220
|
-
user_memory_ids.setdefault(user_id, []).extend(memory_ids)
|
|
335
|
+
user_memory_ids.setdefault(tracking_key(user_id, customer_id), []).extend(memory_ids)
|
|
221
336
|
|
|
222
337
|
return {
|
|
223
338
|
"success": final_status.status.value != "failed",
|
|
@@ -241,37 +356,29 @@ async def handle_search_memory(params: dict) -> dict:
|
|
|
241
356
|
timings: List[dict] = []
|
|
242
357
|
|
|
243
358
|
user_id = params["user_id"]
|
|
359
|
+
customer_id = params.get("customer_id")
|
|
244
360
|
query = params["query"]
|
|
245
361
|
max_results = params.get("max_results", 10)
|
|
362
|
+
mode = params.get("mode", "fast")
|
|
363
|
+
conversation_id = params.get("conversation_id")
|
|
364
|
+
types = params.get("types", ["all"])
|
|
246
365
|
|
|
247
366
|
start = time.perf_counter()
|
|
248
367
|
|
|
249
368
|
step = time.perf_counter()
|
|
250
369
|
context = await sdk.user.context.fetch(
|
|
251
370
|
user_id=user_id,
|
|
371
|
+
customer_id=customer_id,
|
|
372
|
+
conversation_id=conversation_id,
|
|
252
373
|
search_query=[query],
|
|
253
374
|
max_results=max_results,
|
|
254
|
-
types=
|
|
255
|
-
mode=
|
|
375
|
+
types=types,
|
|
376
|
+
mode=mode,
|
|
256
377
|
)
|
|
257
378
|
append_step(timings, "context_fetch", step)
|
|
258
379
|
|
|
259
380
|
step = time.perf_counter()
|
|
260
|
-
results =
|
|
261
|
-
for fact in context.facts:
|
|
262
|
-
results.append({"id": fact.id, "memory": fact.content, "score": fact.confidence})
|
|
263
|
-
for preference in context.preferences:
|
|
264
|
-
results.append(
|
|
265
|
-
{"id": preference.id, "memory": preference.content, "score": preference.strength}
|
|
266
|
-
)
|
|
267
|
-
for episode in context.episodes:
|
|
268
|
-
results.append(
|
|
269
|
-
{"id": episode.id, "memory": episode.summary, "score": episode.significance}
|
|
270
|
-
)
|
|
271
|
-
for emotion in context.emotions:
|
|
272
|
-
results.append(
|
|
273
|
-
{"id": emotion.id, "memory": emotion.context, "score": emotion.intensity}
|
|
274
|
-
)
|
|
381
|
+
results = flatten_context_items(context)
|
|
275
382
|
append_step(timings, "map_context_results", step)
|
|
276
383
|
|
|
277
384
|
return {
|
|
@@ -293,29 +400,28 @@ async def handle_get_memories(params: dict) -> dict:
|
|
|
293
400
|
timings: List[dict] = []
|
|
294
401
|
|
|
295
402
|
user_id = params["user_id"]
|
|
403
|
+
customer_id = params.get("customer_id")
|
|
404
|
+
mode = params.get("mode", "fast")
|
|
405
|
+
conversation_id = params.get("conversation_id")
|
|
406
|
+
max_results = params.get("max_results", 100)
|
|
407
|
+
types = params.get("types", ["all"])
|
|
296
408
|
|
|
297
409
|
start = time.perf_counter()
|
|
298
410
|
|
|
299
411
|
step = time.perf_counter()
|
|
300
412
|
context = await sdk.user.context.fetch(
|
|
301
413
|
user_id=user_id,
|
|
414
|
+
customer_id=customer_id,
|
|
415
|
+
conversation_id=conversation_id,
|
|
302
416
|
search_query=[],
|
|
303
|
-
max_results=
|
|
304
|
-
types=
|
|
305
|
-
mode=
|
|
417
|
+
max_results=max_results,
|
|
418
|
+
types=types,
|
|
419
|
+
mode=mode,
|
|
306
420
|
)
|
|
307
421
|
append_step(timings, "context_fetch_all", step)
|
|
308
422
|
|
|
309
423
|
step = time.perf_counter()
|
|
310
|
-
memories =
|
|
311
|
-
for fact in context.facts:
|
|
312
|
-
memories.append({"id": fact.id, "memory": fact.content})
|
|
313
|
-
for preference in context.preferences:
|
|
314
|
-
memories.append({"id": preference.id, "memory": preference.content})
|
|
315
|
-
for episode in context.episodes:
|
|
316
|
-
memories.append({"id": episode.id, "memory": episode.summary})
|
|
317
|
-
for emotion in context.emotions:
|
|
318
|
-
memories.append({"id": emotion.id, "memory": emotion.context})
|
|
424
|
+
memories = flatten_context_items(context)
|
|
319
425
|
append_step(timings, "map_memories", step)
|
|
320
426
|
|
|
321
427
|
return {
|
|
@@ -323,7 +429,113 @@ async def handle_get_memories(params: dict) -> dict:
|
|
|
323
429
|
"latencyMs": ms_since(start),
|
|
324
430
|
"memories": memories,
|
|
325
431
|
"memoriesCount": len(memories),
|
|
432
|
+
"totalCount": len(memories),
|
|
326
433
|
"rawResponse": context.raw if hasattr(context, "raw") else {},
|
|
434
|
+
"source": context.metadata.source if context.metadata else "unknown",
|
|
435
|
+
"bridgeTiming": {
|
|
436
|
+
"python_total_ms": ms_since(handler_start),
|
|
437
|
+
"steps": timings,
|
|
438
|
+
},
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
async def handle_fetch_user_context(params: dict) -> dict:
|
|
443
|
+
handler_start = time.perf_counter()
|
|
444
|
+
timings: List[dict] = []
|
|
445
|
+
start = time.perf_counter()
|
|
446
|
+
|
|
447
|
+
step = time.perf_counter()
|
|
448
|
+
context = await sdk.user.context.fetch(
|
|
449
|
+
user_id=params["user_id"],
|
|
450
|
+
customer_id=params.get("customer_id"),
|
|
451
|
+
conversation_id=params.get("conversation_id"),
|
|
452
|
+
search_query=params.get("search_query"),
|
|
453
|
+
max_results=params.get("max_results", 10),
|
|
454
|
+
types=params.get("types"),
|
|
455
|
+
mode=params.get("mode", "fast"),
|
|
456
|
+
)
|
|
457
|
+
append_step(timings, "context_fetch", step)
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
"success": True,
|
|
461
|
+
"latencyMs": ms_since(start),
|
|
462
|
+
"context": serialize_context_response(context),
|
|
463
|
+
"bridgeTiming": {
|
|
464
|
+
"python_total_ms": ms_since(handler_start),
|
|
465
|
+
"steps": timings,
|
|
466
|
+
},
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
async def handle_fetch_customer_context(params: dict) -> dict:
|
|
471
|
+
handler_start = time.perf_counter()
|
|
472
|
+
timings: List[dict] = []
|
|
473
|
+
start = time.perf_counter()
|
|
474
|
+
|
|
475
|
+
step = time.perf_counter()
|
|
476
|
+
context = await sdk.customer.context.fetch(
|
|
477
|
+
customer_id=params["customer_id"],
|
|
478
|
+
conversation_id=params.get("conversation_id"),
|
|
479
|
+
search_query=params.get("search_query"),
|
|
480
|
+
max_results=params.get("max_results", 10),
|
|
481
|
+
types=params.get("types"),
|
|
482
|
+
mode=params.get("mode", "fast"),
|
|
483
|
+
)
|
|
484
|
+
append_step(timings, "context_fetch", step)
|
|
485
|
+
|
|
486
|
+
return {
|
|
487
|
+
"success": True,
|
|
488
|
+
"latencyMs": ms_since(start),
|
|
489
|
+
"context": serialize_context_response(context),
|
|
490
|
+
"bridgeTiming": {
|
|
491
|
+
"python_total_ms": ms_since(handler_start),
|
|
492
|
+
"steps": timings,
|
|
493
|
+
},
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
async def handle_fetch_client_context(params: dict) -> dict:
|
|
498
|
+
handler_start = time.perf_counter()
|
|
499
|
+
timings: List[dict] = []
|
|
500
|
+
start = time.perf_counter()
|
|
501
|
+
|
|
502
|
+
step = time.perf_counter()
|
|
503
|
+
context = await sdk.client.context.fetch(
|
|
504
|
+
conversation_id=params.get("conversation_id"),
|
|
505
|
+
search_query=params.get("search_query"),
|
|
506
|
+
max_results=params.get("max_results", 10),
|
|
507
|
+
types=params.get("types"),
|
|
508
|
+
mode=params.get("mode", "fast"),
|
|
509
|
+
)
|
|
510
|
+
append_step(timings, "context_fetch", step)
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
"success": True,
|
|
514
|
+
"latencyMs": ms_since(start),
|
|
515
|
+
"context": serialize_context_response(context),
|
|
516
|
+
"bridgeTiming": {
|
|
517
|
+
"python_total_ms": ms_since(handler_start),
|
|
518
|
+
"steps": timings,
|
|
519
|
+
},
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
async def handle_get_context_for_prompt(params: dict) -> dict:
|
|
524
|
+
handler_start = time.perf_counter()
|
|
525
|
+
timings: List[dict] = []
|
|
526
|
+
start = time.perf_counter()
|
|
527
|
+
|
|
528
|
+
step = time.perf_counter()
|
|
529
|
+
response = await sdk.conversation.get_context_for_prompt(
|
|
530
|
+
conversation_id=params["conversation_id"],
|
|
531
|
+
style=params.get("style", "structured"),
|
|
532
|
+
)
|
|
533
|
+
append_step(timings, "get_context_for_prompt", step)
|
|
534
|
+
|
|
535
|
+
return {
|
|
536
|
+
"success": True,
|
|
537
|
+
"latencyMs": ms_since(start),
|
|
538
|
+
"context_for_prompt": serialize_context_for_prompt_response(response),
|
|
327
539
|
"bridgeTiming": {
|
|
328
540
|
"python_total_ms": ms_since(handler_start),
|
|
329
541
|
"steps": timings,
|
|
@@ -336,6 +548,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
336
548
|
timings: List[dict] = []
|
|
337
549
|
|
|
338
550
|
user_id = params["user_id"]
|
|
551
|
+
customer_id = params.get("customer_id")
|
|
339
552
|
memory_id = params.get("memory_id")
|
|
340
553
|
|
|
341
554
|
start = time.perf_counter()
|
|
@@ -347,6 +560,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
347
560
|
return {
|
|
348
561
|
"success": True,
|
|
349
562
|
"latencyMs": ms_since(start),
|
|
563
|
+
"deletedCount": 1,
|
|
350
564
|
"rawResponse": {"deleted": 1},
|
|
351
565
|
"bridgeTiming": {
|
|
352
566
|
"python_total_ms": ms_since(handler_start),
|
|
@@ -354,11 +568,15 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
354
568
|
},
|
|
355
569
|
}
|
|
356
570
|
|
|
357
|
-
|
|
571
|
+
if not customer_id:
|
|
572
|
+
raise ValueError("customer_id is required when memory_id is not provided")
|
|
573
|
+
|
|
574
|
+
tracked_ids = user_memory_ids.get(tracking_key(user_id, customer_id), [])
|
|
358
575
|
if not tracked_ids:
|
|
359
576
|
return {
|
|
360
577
|
"success": True,
|
|
361
578
|
"latencyMs": 0,
|
|
579
|
+
"deletedCount": 0,
|
|
362
580
|
"rawResponse": None,
|
|
363
581
|
"note": "No tracked memory IDs for this user",
|
|
364
582
|
"bridgeTiming": {
|
|
@@ -377,7 +595,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
377
595
|
last_error = str(exc)
|
|
378
596
|
append_step(timings, "delete_tracked_memories", step)
|
|
379
597
|
|
|
380
|
-
user_memory_ids.pop(user_id, None)
|
|
598
|
+
user_memory_ids.pop(tracking_key(user_id, customer_id), None)
|
|
381
599
|
|
|
382
600
|
if last_error:
|
|
383
601
|
return {
|
|
@@ -393,6 +611,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
393
611
|
return {
|
|
394
612
|
"success": True,
|
|
395
613
|
"latencyMs": ms_since(start),
|
|
614
|
+
"deletedCount": len(tracked_ids),
|
|
396
615
|
"rawResponse": {"deleted": len(tracked_ids)},
|
|
397
616
|
"note": f"Deleted {len(tracked_ids)} memories",
|
|
398
617
|
"bridgeTiming": {
|
|
@@ -431,6 +650,10 @@ HANDLERS = {
|
|
|
431
650
|
"add_memory": handle_add_memory,
|
|
432
651
|
"search_memory": handle_search_memory,
|
|
433
652
|
"get_memories": handle_get_memories,
|
|
653
|
+
"fetch_user_context": handle_fetch_user_context,
|
|
654
|
+
"fetch_customer_context": handle_fetch_customer_context,
|
|
655
|
+
"fetch_client_context": handle_fetch_client_context,
|
|
656
|
+
"get_context_for_prompt": handle_get_context_for_prompt,
|
|
434
657
|
"delete_memory": handle_delete_memory,
|
|
435
658
|
"shutdown": handle_shutdown,
|
|
436
659
|
}
|
package/package.json
CHANGED
package/src/setup-typescript.js
CHANGED
|
@@ -64,6 +64,10 @@ function getWrapperTemplate() {
|
|
|
64
64
|
type AddMemoryInput,
|
|
65
65
|
type SearchMemoryInput,
|
|
66
66
|
type GetMemoriesInput,
|
|
67
|
+
type FetchUserContextInput,
|
|
68
|
+
type FetchCustomerContextInput,
|
|
69
|
+
type FetchClientContextInput,
|
|
70
|
+
type GetContextForPromptInput,
|
|
67
71
|
type DeleteMemoryInput,
|
|
68
72
|
} from '@maximem/synap-js-sdk';
|
|
69
73
|
|
|
@@ -90,6 +94,22 @@ export class SynapTsClient {
|
|
|
90
94
|
return this.client.getMemories(input);
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
fetchUserContext(input: FetchUserContextInput) {
|
|
98
|
+
return this.client.fetchUserContext(input);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fetchCustomerContext(input: FetchCustomerContextInput) {
|
|
102
|
+
return this.client.fetchCustomerContext(input);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fetchClientContext(input?: FetchClientContextInput) {
|
|
106
|
+
return this.client.fetchClientContext(input);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getContextForPrompt(input: GetContextForPromptInput) {
|
|
110
|
+
return this.client.getContextForPrompt(input);
|
|
111
|
+
}
|
|
112
|
+
|
|
93
113
|
deleteMemory(input: DeleteMemoryInput) {
|
|
94
114
|
return this.client.deleteMemory(input);
|
|
95
115
|
}
|
package/src/synap-client.js
CHANGED
|
@@ -1,5 +1,200 @@
|
|
|
1
1
|
const { BridgeManager } = require('./bridge-manager');
|
|
2
2
|
|
|
3
|
+
function pickDefined(...values) {
|
|
4
|
+
for (const value of values) {
|
|
5
|
+
if (value !== undefined) return value;
|
|
6
|
+
}
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function normalizeTemporalFields(item = {}) {
|
|
11
|
+
return {
|
|
12
|
+
eventDate: pickDefined(item.eventDate, item.event_date, null),
|
|
13
|
+
validUntil: pickDefined(item.validUntil, item.valid_until, null),
|
|
14
|
+
temporalCategory: pickDefined(item.temporalCategory, item.temporal_category, null),
|
|
15
|
+
temporalConfidence: pickDefined(item.temporalConfidence, item.temporal_confidence, 0),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeMemoryItem(item = {}) {
|
|
20
|
+
return {
|
|
21
|
+
id: item.id || '',
|
|
22
|
+
memory: pickDefined(item.memory, item.content, ''),
|
|
23
|
+
score: item.score,
|
|
24
|
+
source: item.source,
|
|
25
|
+
metadata: item.metadata || {},
|
|
26
|
+
contextType: pickDefined(item.contextType, item.context_type),
|
|
27
|
+
...normalizeTemporalFields(item),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function normalizeContextMetadata(metadata = {}) {
|
|
32
|
+
return {
|
|
33
|
+
correlationId: pickDefined(metadata.correlationId, metadata.correlation_id, ''),
|
|
34
|
+
ttlSeconds: pickDefined(metadata.ttlSeconds, metadata.ttl_seconds, 0),
|
|
35
|
+
source: metadata.source || 'unknown',
|
|
36
|
+
retrievedAt: pickDefined(metadata.retrievedAt, metadata.retrieved_at, null),
|
|
37
|
+
compactionApplied: pickDefined(metadata.compactionApplied, metadata.compaction_applied, null),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeConversationContext(value) {
|
|
42
|
+
if (!value) return null;
|
|
43
|
+
return {
|
|
44
|
+
summary: value.summary || null,
|
|
45
|
+
currentState: pickDefined(value.currentState, value.current_state, {}),
|
|
46
|
+
keyExtractions: pickDefined(value.keyExtractions, value.key_extractions, {}),
|
|
47
|
+
recentTurns: pickDefined(value.recentTurns, value.recent_turns, []),
|
|
48
|
+
compactionId: pickDefined(value.compactionId, value.compaction_id, null),
|
|
49
|
+
compactedAt: pickDefined(value.compactedAt, value.compacted_at, null),
|
|
50
|
+
conversationId: pickDefined(value.conversationId, value.conversation_id, null),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeFact(item = {}) {
|
|
55
|
+
return {
|
|
56
|
+
id: item.id || '',
|
|
57
|
+
content: item.content || '',
|
|
58
|
+
confidence: pickDefined(item.confidence, 0),
|
|
59
|
+
source: item.source || '',
|
|
60
|
+
extractedAt: pickDefined(item.extractedAt, item.extracted_at, null),
|
|
61
|
+
metadata: item.metadata || {},
|
|
62
|
+
...normalizeTemporalFields(item),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function normalizePreference(item = {}) {
|
|
67
|
+
return {
|
|
68
|
+
id: item.id || '',
|
|
69
|
+
category: item.category || '',
|
|
70
|
+
content: item.content || '',
|
|
71
|
+
strength: pickDefined(item.strength, item.confidence, 0),
|
|
72
|
+
source: item.source || '',
|
|
73
|
+
extractedAt: pickDefined(item.extractedAt, item.extracted_at, null),
|
|
74
|
+
metadata: item.metadata || {},
|
|
75
|
+
...normalizeTemporalFields(item),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeEpisode(item = {}) {
|
|
80
|
+
return {
|
|
81
|
+
id: item.id || '',
|
|
82
|
+
summary: pickDefined(item.summary, item.content, ''),
|
|
83
|
+
occurredAt: pickDefined(item.occurredAt, item.occurred_at, null),
|
|
84
|
+
significance: pickDefined(item.significance, item.confidence, 0),
|
|
85
|
+
participants: item.participants || [],
|
|
86
|
+
metadata: item.metadata || {},
|
|
87
|
+
...normalizeTemporalFields(item),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function normalizeEmotion(item = {}) {
|
|
92
|
+
return {
|
|
93
|
+
id: item.id || '',
|
|
94
|
+
emotionType: pickDefined(item.emotionType, item.emotion_type, ''),
|
|
95
|
+
intensity: pickDefined(item.intensity, item.confidence, 0),
|
|
96
|
+
detectedAt: pickDefined(item.detectedAt, item.detected_at, null),
|
|
97
|
+
context: item.context || '',
|
|
98
|
+
metadata: item.metadata || {},
|
|
99
|
+
...normalizeTemporalFields(item),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function normalizeTemporalEvent(item = {}) {
|
|
104
|
+
return {
|
|
105
|
+
id: item.id || '',
|
|
106
|
+
content: item.content || '',
|
|
107
|
+
eventDate: pickDefined(item.eventDate, item.event_date, null),
|
|
108
|
+
validUntil: pickDefined(item.validUntil, item.valid_until, null),
|
|
109
|
+
temporalCategory: pickDefined(item.temporalCategory, item.temporal_category, ''),
|
|
110
|
+
temporalConfidence: pickDefined(item.temporalConfidence, item.temporal_confidence, 0),
|
|
111
|
+
confidence: pickDefined(item.confidence, 0),
|
|
112
|
+
source: item.source || '',
|
|
113
|
+
extractedAt: pickDefined(item.extractedAt, item.extracted_at, null),
|
|
114
|
+
metadata: item.metadata || {},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function normalizeContextResponse(result = {}) {
|
|
119
|
+
const context = result.context || result;
|
|
120
|
+
return {
|
|
121
|
+
facts: (context.facts || []).map(normalizeFact),
|
|
122
|
+
preferences: (context.preferences || []).map(normalizePreference),
|
|
123
|
+
episodes: (context.episodes || []).map(normalizeEpisode),
|
|
124
|
+
emotions: (context.emotions || []).map(normalizeEmotion),
|
|
125
|
+
temporalEvents: (pickDefined(context.temporalEvents, context.temporal_events, []) || []).map(normalizeTemporalEvent),
|
|
126
|
+
conversationContext: normalizeConversationContext(
|
|
127
|
+
pickDefined(context.conversationContext, context.conversation_context, null)
|
|
128
|
+
),
|
|
129
|
+
metadata: normalizeContextMetadata(context.metadata || {}),
|
|
130
|
+
rawResponse: pickDefined(context.rawResponse, context.raw_response, {}),
|
|
131
|
+
bridgeTiming: result.bridgeTiming,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function normalizeRecentMessage(item = {}) {
|
|
136
|
+
return {
|
|
137
|
+
role: item.role || 'user',
|
|
138
|
+
content: item.content || '',
|
|
139
|
+
timestamp: item.timestamp || null,
|
|
140
|
+
messageId: pickDefined(item.messageId, item.message_id, ''),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function normalizeContextForPromptResult(result = {}) {
|
|
145
|
+
const payload = pickDefined(result.contextForPrompt, result.context_for_prompt, result);
|
|
146
|
+
return {
|
|
147
|
+
formattedContext: pickDefined(payload.formattedContext, payload.formatted_context, null),
|
|
148
|
+
available: !!payload.available,
|
|
149
|
+
isStale: !!pickDefined(payload.isStale, payload.is_stale, false),
|
|
150
|
+
compressionRatio: pickDefined(payload.compressionRatio, payload.compression_ratio, null),
|
|
151
|
+
validationScore: pickDefined(payload.validationScore, payload.validation_score, null),
|
|
152
|
+
compactionAgeSeconds: pickDefined(payload.compactionAgeSeconds, payload.compaction_age_seconds, null),
|
|
153
|
+
qualityWarning: !!pickDefined(payload.qualityWarning, payload.quality_warning, false),
|
|
154
|
+
recentMessages: (pickDefined(payload.recentMessages, payload.recent_messages, []) || []).map(normalizeRecentMessage),
|
|
155
|
+
recentMessageCount: pickDefined(payload.recentMessageCount, payload.recent_message_count, 0),
|
|
156
|
+
compactedMessageCount: pickDefined(payload.compactedMessageCount, payload.compacted_message_count, 0),
|
|
157
|
+
totalMessageCount: pickDefined(payload.totalMessageCount, payload.total_message_count, 0),
|
|
158
|
+
bridgeTiming: result.bridgeTiming,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function normalizeSearchMemoryResult(result = {}) {
|
|
163
|
+
return {
|
|
164
|
+
success: !!result.success,
|
|
165
|
+
latencyMs: result.latencyMs || 0,
|
|
166
|
+
results: (result.results || []).map(normalizeMemoryItem),
|
|
167
|
+
resultsCount: pickDefined(result.resultsCount, result.results?.length, 0),
|
|
168
|
+
rawResponse: result.rawResponse || {},
|
|
169
|
+
source: result.source,
|
|
170
|
+
bridgeTiming: result.bridgeTiming,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function normalizeGetMemoriesResult(result = {}) {
|
|
175
|
+
const memories = (result.memories || []).map(normalizeMemoryItem);
|
|
176
|
+
return {
|
|
177
|
+
success: !!result.success,
|
|
178
|
+
latencyMs: result.latencyMs || 0,
|
|
179
|
+
memories,
|
|
180
|
+
totalCount: pickDefined(result.totalCount, result.memoriesCount, memories.length, 0),
|
|
181
|
+
rawResponse: pickDefined(result.rawResponse, null),
|
|
182
|
+
source: result.source,
|
|
183
|
+
bridgeTiming: result.bridgeTiming,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function normalizeDeleteMemoryResult(result = {}) {
|
|
188
|
+
return {
|
|
189
|
+
success: !!result.success,
|
|
190
|
+
latencyMs: result.latencyMs || 0,
|
|
191
|
+
deletedCount: pickDefined(result.deletedCount, result.rawResponse?.deleted, 0),
|
|
192
|
+
rawResponse: pickDefined(result.rawResponse, null),
|
|
193
|
+
note: result.note,
|
|
194
|
+
bridgeTiming: result.bridgeTiming,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
3
198
|
class SynapClient {
|
|
4
199
|
constructor(options = {}) {
|
|
5
200
|
this.bridge = new BridgeManager(options);
|
|
@@ -14,41 +209,122 @@ class SynapClient {
|
|
|
14
209
|
await this.bridge.ensureStarted();
|
|
15
210
|
}
|
|
16
211
|
|
|
17
|
-
async addMemory({
|
|
212
|
+
async addMemory({
|
|
213
|
+
userId,
|
|
214
|
+
customerId,
|
|
215
|
+
conversationId,
|
|
216
|
+
sessionId,
|
|
217
|
+
messages,
|
|
218
|
+
mode,
|
|
219
|
+
documentType,
|
|
220
|
+
documentId,
|
|
221
|
+
documentCreatedAt,
|
|
222
|
+
metadata,
|
|
223
|
+
}) {
|
|
18
224
|
this.#assert(userId, 'userId is required');
|
|
225
|
+
this.#assert(customerId, 'customerId is required');
|
|
19
226
|
this.#assert(Array.isArray(messages), 'messages must be an array');
|
|
20
227
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
);
|
|
228
|
+
const params = { user_id: userId, messages };
|
|
229
|
+
params.customer_id = customerId;
|
|
230
|
+
if (conversationId !== undefined) params.conversation_id = conversationId;
|
|
231
|
+
if (sessionId !== undefined) params.session_id = sessionId;
|
|
232
|
+
if (mode !== undefined) params.mode = mode;
|
|
233
|
+
if (documentType !== undefined) params.document_type = documentType;
|
|
234
|
+
if (documentId !== undefined) params.document_id = documentId;
|
|
235
|
+
if (documentCreatedAt !== undefined) params.document_created_at = documentCreatedAt;
|
|
236
|
+
if (metadata !== undefined) params.metadata = metadata;
|
|
237
|
+
|
|
238
|
+
return this.bridge.call('add_memory', params, this.options.ingestTimeoutMs);
|
|
26
239
|
}
|
|
27
240
|
|
|
28
|
-
async searchMemory({ userId, query, maxResults = 10 }) {
|
|
241
|
+
async searchMemory({ userId, customerId, query, maxResults = 10, mode, conversationId, types }) {
|
|
29
242
|
this.#assert(userId, 'userId is required');
|
|
30
243
|
this.#assert(query, 'query is required');
|
|
244
|
+
this.#assertArray(types, 'types must be an array when provided');
|
|
31
245
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
246
|
+
const params = { user_id: userId, query, max_results: maxResults };
|
|
247
|
+
if (customerId !== undefined) params.customer_id = customerId;
|
|
248
|
+
if (mode !== undefined) params.mode = mode;
|
|
249
|
+
if (conversationId !== undefined) params.conversation_id = conversationId;
|
|
250
|
+
if (types !== undefined) params.types = types;
|
|
251
|
+
|
|
252
|
+
return normalizeSearchMemoryResult(await this.bridge.call('search_memory', params));
|
|
37
253
|
}
|
|
38
254
|
|
|
39
|
-
async getMemories({ userId }) {
|
|
255
|
+
async getMemories({ userId, customerId, mode, conversationId, maxResults, types }) {
|
|
40
256
|
this.#assert(userId, 'userId is required');
|
|
257
|
+
this.#assertArray(types, 'types must be an array when provided');
|
|
258
|
+
|
|
259
|
+
const params = { user_id: userId };
|
|
260
|
+
if (customerId !== undefined) params.customer_id = customerId;
|
|
261
|
+
if (mode !== undefined) params.mode = mode;
|
|
262
|
+
if (conversationId !== undefined) params.conversation_id = conversationId;
|
|
263
|
+
if (maxResults !== undefined) params.max_results = maxResults;
|
|
264
|
+
if (types !== undefined) params.types = types;
|
|
41
265
|
|
|
42
|
-
return this.bridge.call('get_memories',
|
|
266
|
+
return normalizeGetMemoriesResult(await this.bridge.call('get_memories', params));
|
|
43
267
|
}
|
|
44
268
|
|
|
45
|
-
async
|
|
269
|
+
async fetchUserContext({ userId, customerId, conversationId, searchQuery, maxResults = 10, types, mode }) {
|
|
46
270
|
this.#assert(userId, 'userId is required');
|
|
271
|
+
this.#assertArray(searchQuery, 'searchQuery must be an array when provided');
|
|
272
|
+
this.#assertArray(types, 'types must be an array when provided');
|
|
47
273
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
274
|
+
const params = { user_id: userId, max_results: maxResults };
|
|
275
|
+
if (customerId !== undefined) params.customer_id = customerId;
|
|
276
|
+
if (conversationId !== undefined) params.conversation_id = conversationId;
|
|
277
|
+
if (searchQuery !== undefined) params.search_query = searchQuery;
|
|
278
|
+
if (types !== undefined) params.types = types;
|
|
279
|
+
if (mode !== undefined) params.mode = mode;
|
|
280
|
+
|
|
281
|
+
return normalizeContextResponse(await this.bridge.call('fetch_user_context', params));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async fetchCustomerContext({ customerId, conversationId, searchQuery, maxResults = 10, types, mode }) {
|
|
285
|
+
this.#assert(customerId, 'customerId is required');
|
|
286
|
+
this.#assertArray(searchQuery, 'searchQuery must be an array when provided');
|
|
287
|
+
this.#assertArray(types, 'types must be an array when provided');
|
|
288
|
+
|
|
289
|
+
const params = { customer_id: customerId, max_results: maxResults };
|
|
290
|
+
if (conversationId !== undefined) params.conversation_id = conversationId;
|
|
291
|
+
if (searchQuery !== undefined) params.search_query = searchQuery;
|
|
292
|
+
if (types !== undefined) params.types = types;
|
|
293
|
+
if (mode !== undefined) params.mode = mode;
|
|
294
|
+
|
|
295
|
+
return normalizeContextResponse(await this.bridge.call('fetch_customer_context', params));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async fetchClientContext({ conversationId, searchQuery, maxResults = 10, types, mode } = {}) {
|
|
299
|
+
this.#assertArray(searchQuery, 'searchQuery must be an array when provided');
|
|
300
|
+
this.#assertArray(types, 'types must be an array when provided');
|
|
301
|
+
|
|
302
|
+
const params = { max_results: maxResults };
|
|
303
|
+
if (conversationId !== undefined) params.conversation_id = conversationId;
|
|
304
|
+
if (searchQuery !== undefined) params.search_query = searchQuery;
|
|
305
|
+
if (types !== undefined) params.types = types;
|
|
306
|
+
if (mode !== undefined) params.mode = mode;
|
|
307
|
+
|
|
308
|
+
return normalizeContextResponse(await this.bridge.call('fetch_client_context', params));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async getContextForPrompt({ conversationId, style } = {}) {
|
|
312
|
+
this.#assert(conversationId, 'conversationId is required');
|
|
313
|
+
|
|
314
|
+
const params = { conversation_id: conversationId };
|
|
315
|
+
if (style !== undefined) params.style = style;
|
|
316
|
+
|
|
317
|
+
return normalizeContextForPromptResult(await this.bridge.call('get_context_for_prompt', params));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async deleteMemory({ userId, customerId, memoryId = null }) {
|
|
321
|
+
this.#assert(userId, 'userId is required');
|
|
322
|
+
if (memoryId == null) this.#assert(customerId, 'customerId is required when memoryId is not provided');
|
|
323
|
+
|
|
324
|
+
const params = { user_id: userId, memory_id: memoryId };
|
|
325
|
+
if (customerId !== undefined) params.customer_id = customerId;
|
|
326
|
+
|
|
327
|
+
return normalizeDeleteMemoryResult(await this.bridge.call('delete_memory', params));
|
|
52
328
|
}
|
|
53
329
|
|
|
54
330
|
async shutdown() {
|
|
@@ -59,6 +335,10 @@ class SynapClient {
|
|
|
59
335
|
if (!value) throw new Error(message);
|
|
60
336
|
}
|
|
61
337
|
|
|
338
|
+
#assertArray(value, message) {
|
|
339
|
+
if (value !== undefined && !Array.isArray(value)) throw new Error(message);
|
|
340
|
+
}
|
|
341
|
+
|
|
62
342
|
#registerShutdownHooks() {
|
|
63
343
|
const close = async () => {
|
|
64
344
|
try {
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
export type LogLevel = 'debug' | 'error';
|
|
2
|
+
export type RetrievalMode = 'fast' | 'accurate';
|
|
3
|
+
export type IngestMode = 'fast' | 'long-range';
|
|
4
|
+
export type PromptStyle = 'structured' | 'narrative' | 'bullet_points';
|
|
5
|
+
export type ContextType =
|
|
6
|
+
| 'all'
|
|
7
|
+
| 'facts'
|
|
8
|
+
| 'preferences'
|
|
9
|
+
| 'episodes'
|
|
10
|
+
| 'emotions'
|
|
11
|
+
| 'temporal_events';
|
|
12
|
+
export type FlattenedContextType =
|
|
13
|
+
| 'fact'
|
|
14
|
+
| 'preference'
|
|
15
|
+
| 'episode'
|
|
16
|
+
| 'emotion'
|
|
17
|
+
| 'temporal_event';
|
|
18
|
+
export type DocumentType =
|
|
19
|
+
| 'ai-chat-conversation'
|
|
20
|
+
| 'document'
|
|
21
|
+
| 'email'
|
|
22
|
+
| 'pdf'
|
|
23
|
+
| 'image'
|
|
24
|
+
| 'audio'
|
|
25
|
+
| 'meeting-transcript';
|
|
2
26
|
|
|
3
27
|
export interface BridgeLogHandler {
|
|
4
28
|
(level: LogLevel, message: string): void;
|
|
@@ -31,30 +55,81 @@ export interface SynapClientOptions {
|
|
|
31
55
|
}
|
|
32
56
|
|
|
33
57
|
export interface ChatMessage {
|
|
34
|
-
role?:
|
|
58
|
+
role?: 'user' | 'assistant';
|
|
35
59
|
content: string;
|
|
60
|
+
metadata?: Record<string, string>;
|
|
36
61
|
}
|
|
37
62
|
|
|
38
63
|
export interface AddMemoryInput {
|
|
39
64
|
userId: string;
|
|
65
|
+
customerId: string;
|
|
66
|
+
conversationId?: string;
|
|
67
|
+
sessionId?: string;
|
|
40
68
|
messages: ChatMessage[];
|
|
69
|
+
mode?: IngestMode;
|
|
70
|
+
documentType?: DocumentType;
|
|
71
|
+
documentId?: string;
|
|
72
|
+
documentCreatedAt?: string;
|
|
73
|
+
metadata?: Record<string, unknown>;
|
|
41
74
|
}
|
|
42
75
|
|
|
43
76
|
export interface SearchMemoryInput {
|
|
44
77
|
userId: string;
|
|
78
|
+
customerId?: string;
|
|
45
79
|
query: string;
|
|
46
80
|
maxResults?: number;
|
|
81
|
+
mode?: RetrievalMode;
|
|
82
|
+
conversationId?: string;
|
|
83
|
+
types?: ContextType[];
|
|
47
84
|
}
|
|
48
85
|
|
|
49
86
|
export interface GetMemoriesInput {
|
|
50
87
|
userId: string;
|
|
88
|
+
customerId?: string;
|
|
89
|
+
mode?: RetrievalMode;
|
|
90
|
+
conversationId?: string;
|
|
91
|
+
maxResults?: number;
|
|
92
|
+
types?: ContextType[];
|
|
51
93
|
}
|
|
52
94
|
|
|
53
95
|
export interface DeleteMemoryInput {
|
|
54
96
|
userId: string;
|
|
97
|
+
customerId?: string;
|
|
55
98
|
memoryId?: string | null;
|
|
56
99
|
}
|
|
57
100
|
|
|
101
|
+
export interface FetchUserContextInput {
|
|
102
|
+
userId: string;
|
|
103
|
+
customerId?: string;
|
|
104
|
+
conversationId?: string;
|
|
105
|
+
searchQuery?: string[];
|
|
106
|
+
maxResults?: number;
|
|
107
|
+
types?: ContextType[];
|
|
108
|
+
mode?: RetrievalMode;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface FetchCustomerContextInput {
|
|
112
|
+
customerId: string;
|
|
113
|
+
conversationId?: string;
|
|
114
|
+
searchQuery?: string[];
|
|
115
|
+
maxResults?: number;
|
|
116
|
+
types?: ContextType[];
|
|
117
|
+
mode?: RetrievalMode;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface FetchClientContextInput {
|
|
121
|
+
conversationId?: string;
|
|
122
|
+
searchQuery?: string[];
|
|
123
|
+
maxResults?: number;
|
|
124
|
+
types?: ContextType[];
|
|
125
|
+
mode?: RetrievalMode;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface GetContextForPromptInput {
|
|
129
|
+
conversationId: string;
|
|
130
|
+
style?: PromptStyle;
|
|
131
|
+
}
|
|
132
|
+
|
|
58
133
|
export interface BridgeStepTiming {
|
|
59
134
|
step: string;
|
|
60
135
|
ms: number;
|
|
@@ -65,6 +140,115 @@ export interface BridgeTiming {
|
|
|
65
140
|
steps: BridgeStepTiming[];
|
|
66
141
|
}
|
|
67
142
|
|
|
143
|
+
export interface TemporalFields {
|
|
144
|
+
eventDate: string | null;
|
|
145
|
+
validUntil: string | null;
|
|
146
|
+
temporalCategory: string | null;
|
|
147
|
+
temporalConfidence: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface Fact extends TemporalFields {
|
|
151
|
+
id: string;
|
|
152
|
+
content: string;
|
|
153
|
+
confidence: number;
|
|
154
|
+
source: string;
|
|
155
|
+
extractedAt: string | null;
|
|
156
|
+
metadata: Record<string, unknown>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface Preference extends TemporalFields {
|
|
160
|
+
id: string;
|
|
161
|
+
category: string;
|
|
162
|
+
content: string;
|
|
163
|
+
strength: number;
|
|
164
|
+
source: string;
|
|
165
|
+
extractedAt: string | null;
|
|
166
|
+
metadata: Record<string, unknown>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface Episode extends TemporalFields {
|
|
170
|
+
id: string;
|
|
171
|
+
summary: string;
|
|
172
|
+
occurredAt: string | null;
|
|
173
|
+
significance: number;
|
|
174
|
+
participants: string[];
|
|
175
|
+
metadata: Record<string, unknown>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface Emotion extends TemporalFields {
|
|
179
|
+
id: string;
|
|
180
|
+
emotionType: string;
|
|
181
|
+
intensity: number;
|
|
182
|
+
detectedAt: string | null;
|
|
183
|
+
context: string;
|
|
184
|
+
metadata: Record<string, unknown>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface TemporalEvent {
|
|
188
|
+
id: string;
|
|
189
|
+
content: string;
|
|
190
|
+
eventDate: string | null;
|
|
191
|
+
validUntil: string | null;
|
|
192
|
+
temporalCategory: string;
|
|
193
|
+
temporalConfidence: number;
|
|
194
|
+
confidence: number;
|
|
195
|
+
source: string;
|
|
196
|
+
extractedAt: string | null;
|
|
197
|
+
metadata: Record<string, unknown>;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface RecentMessage {
|
|
201
|
+
role: string;
|
|
202
|
+
content: string;
|
|
203
|
+
timestamp: string | null;
|
|
204
|
+
messageId: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface ContextResponseMetadata {
|
|
208
|
+
correlationId: string;
|
|
209
|
+
ttlSeconds: number;
|
|
210
|
+
source: string;
|
|
211
|
+
retrievedAt: string | null;
|
|
212
|
+
compactionApplied: string | null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface ConversationContext {
|
|
216
|
+
summary: string | null;
|
|
217
|
+
currentState: Record<string, unknown>;
|
|
218
|
+
keyExtractions: Record<string, Array<Record<string, unknown>>>;
|
|
219
|
+
recentTurns: Array<Record<string, unknown>>;
|
|
220
|
+
compactionId: string | null;
|
|
221
|
+
compactedAt: string | null;
|
|
222
|
+
conversationId: string | null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface ContextResponse {
|
|
226
|
+
facts: Fact[];
|
|
227
|
+
preferences: Preference[];
|
|
228
|
+
episodes: Episode[];
|
|
229
|
+
emotions: Emotion[];
|
|
230
|
+
temporalEvents: TemporalEvent[];
|
|
231
|
+
conversationContext: ConversationContext | null;
|
|
232
|
+
metadata: ContextResponseMetadata;
|
|
233
|
+
rawResponse: Record<string, unknown>;
|
|
234
|
+
bridgeTiming?: BridgeTiming;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface ContextForPromptResult {
|
|
238
|
+
formattedContext: string | null;
|
|
239
|
+
available: boolean;
|
|
240
|
+
isStale: boolean;
|
|
241
|
+
compressionRatio: number | null;
|
|
242
|
+
validationScore: number | null;
|
|
243
|
+
compactionAgeSeconds: number | null;
|
|
244
|
+
qualityWarning: boolean;
|
|
245
|
+
recentMessages: RecentMessage[];
|
|
246
|
+
recentMessageCount: number;
|
|
247
|
+
compactedMessageCount: number;
|
|
248
|
+
totalMessageCount: number;
|
|
249
|
+
bridgeTiming?: BridgeTiming;
|
|
250
|
+
}
|
|
251
|
+
|
|
68
252
|
export interface AddMemoryResult {
|
|
69
253
|
success: boolean;
|
|
70
254
|
latencyMs: number;
|
|
@@ -73,10 +257,13 @@ export interface AddMemoryResult {
|
|
|
73
257
|
bridgeTiming?: BridgeTiming;
|
|
74
258
|
}
|
|
75
259
|
|
|
76
|
-
export interface SearchMemoryItem {
|
|
260
|
+
export interface SearchMemoryItem extends TemporalFields {
|
|
77
261
|
id: string;
|
|
78
262
|
memory: string;
|
|
79
263
|
score?: number;
|
|
264
|
+
source?: string;
|
|
265
|
+
metadata: Record<string, unknown>;
|
|
266
|
+
contextType?: FlattenedContextType;
|
|
80
267
|
}
|
|
81
268
|
|
|
82
269
|
export interface SearchMemoryResult {
|
|
@@ -89,22 +276,23 @@ export interface SearchMemoryResult {
|
|
|
89
276
|
bridgeTiming?: BridgeTiming;
|
|
90
277
|
}
|
|
91
278
|
|
|
92
|
-
export interface MemoryItem {
|
|
93
|
-
id: string;
|
|
94
|
-
memory: string;
|
|
95
|
-
}
|
|
279
|
+
export interface MemoryItem extends SearchMemoryItem {}
|
|
96
280
|
|
|
97
281
|
export interface GetMemoriesResult {
|
|
98
282
|
success: boolean;
|
|
99
283
|
latencyMs: number;
|
|
100
284
|
memories: MemoryItem[];
|
|
101
285
|
totalCount: number;
|
|
286
|
+
rawResponse: Record<string, unknown> | null;
|
|
287
|
+
source?: string;
|
|
102
288
|
bridgeTiming?: BridgeTiming;
|
|
103
289
|
}
|
|
104
290
|
|
|
105
291
|
export interface DeleteMemoryResult {
|
|
106
292
|
success: boolean;
|
|
293
|
+
latencyMs: number;
|
|
107
294
|
deletedCount: number;
|
|
295
|
+
rawResponse: Record<string, unknown> | null;
|
|
108
296
|
note?: string;
|
|
109
297
|
bridgeTiming?: BridgeTiming;
|
|
110
298
|
}
|
|
@@ -115,6 +303,10 @@ export class SynapClient {
|
|
|
115
303
|
addMemory(input: AddMemoryInput): Promise<AddMemoryResult>;
|
|
116
304
|
searchMemory(input: SearchMemoryInput): Promise<SearchMemoryResult>;
|
|
117
305
|
getMemories(input: GetMemoriesInput): Promise<GetMemoriesResult>;
|
|
306
|
+
fetchUserContext(input: FetchUserContextInput): Promise<ContextResponse>;
|
|
307
|
+
fetchCustomerContext(input: FetchCustomerContextInput): Promise<ContextResponse>;
|
|
308
|
+
fetchClientContext(input?: FetchClientContextInput): Promise<ContextResponse>;
|
|
309
|
+
getContextForPrompt(input: GetContextForPromptInput): Promise<ContextForPromptResult>;
|
|
118
310
|
deleteMemory(input: DeleteMemoryInput): Promise<DeleteMemoryResult>;
|
|
119
311
|
shutdown(): Promise<void>;
|
|
120
312
|
}
|