@maximem/synap-js-sdk 0.1.4 → 0.2.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/README.md +25 -6
- package/bridge/synap_bridge.py +259 -39
- package/package.json +1 -1
- package/src/bridge-manager.js +2 -1
- package/src/errors.js +187 -0
- package/src/index.js +2 -0
- package/src/setup-typescript.js +20 -0
- package/src/synap-client.js +289 -10
- package/types/index.d.ts +225 -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)
|
|
@@ -169,12 +263,26 @@ async def handle_add_memory(params: dict) -> dict:
|
|
|
169
263
|
|
|
170
264
|
step = time.perf_counter()
|
|
171
265
|
mode = params.get("mode", "long-range")
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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)
|
|
178
286
|
append_step(timings, "memories_create", step)
|
|
179
287
|
|
|
180
288
|
ingestion_id = create_result.ingestion_id
|
|
@@ -186,10 +294,16 @@ async def handle_add_memory(params: dict) -> dict:
|
|
|
186
294
|
if not content:
|
|
187
295
|
continue
|
|
188
296
|
try:
|
|
297
|
+
role = message.get("role", "user")
|
|
189
298
|
await sdk.instance.send_message(
|
|
190
299
|
content=content,
|
|
191
|
-
role=
|
|
300
|
+
role=role,
|
|
301
|
+
conversation_id=conversation_id,
|
|
192
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"),
|
|
193
307
|
)
|
|
194
308
|
except Exception as exc:
|
|
195
309
|
logger.debug("gRPC send_message failed (non-fatal): %s", exc)
|
|
@@ -218,7 +332,7 @@ async def handle_add_memory(params: dict) -> dict:
|
|
|
218
332
|
|
|
219
333
|
memory_ids = [str(memory_id) for memory_id in (final_status.memory_ids or [])]
|
|
220
334
|
if memory_ids:
|
|
221
|
-
user_memory_ids.setdefault(user_id, []).extend(memory_ids)
|
|
335
|
+
user_memory_ids.setdefault(tracking_key(user_id, customer_id), []).extend(memory_ids)
|
|
222
336
|
|
|
223
337
|
return {
|
|
224
338
|
"success": final_status.status.value != "failed",
|
|
@@ -242,38 +356,29 @@ async def handle_search_memory(params: dict) -> dict:
|
|
|
242
356
|
timings: List[dict] = []
|
|
243
357
|
|
|
244
358
|
user_id = params["user_id"]
|
|
359
|
+
customer_id = params.get("customer_id")
|
|
245
360
|
query = params["query"]
|
|
246
361
|
max_results = params.get("max_results", 10)
|
|
247
362
|
mode = params.get("mode", "fast")
|
|
363
|
+
conversation_id = params.get("conversation_id")
|
|
364
|
+
types = params.get("types", ["all"])
|
|
248
365
|
|
|
249
366
|
start = time.perf_counter()
|
|
250
367
|
|
|
251
368
|
step = time.perf_counter()
|
|
252
369
|
context = await sdk.user.context.fetch(
|
|
253
370
|
user_id=user_id,
|
|
371
|
+
customer_id=customer_id,
|
|
372
|
+
conversation_id=conversation_id,
|
|
254
373
|
search_query=[query],
|
|
255
374
|
max_results=max_results,
|
|
256
|
-
types=
|
|
375
|
+
types=types,
|
|
257
376
|
mode=mode,
|
|
258
377
|
)
|
|
259
378
|
append_step(timings, "context_fetch", step)
|
|
260
379
|
|
|
261
380
|
step = time.perf_counter()
|
|
262
|
-
results =
|
|
263
|
-
for fact in context.facts:
|
|
264
|
-
results.append({"id": fact.id, "memory": fact.content, "score": fact.confidence})
|
|
265
|
-
for preference in context.preferences:
|
|
266
|
-
results.append(
|
|
267
|
-
{"id": preference.id, "memory": preference.content, "score": preference.strength}
|
|
268
|
-
)
|
|
269
|
-
for episode in context.episodes:
|
|
270
|
-
results.append(
|
|
271
|
-
{"id": episode.id, "memory": episode.summary, "score": episode.significance}
|
|
272
|
-
)
|
|
273
|
-
for emotion in context.emotions:
|
|
274
|
-
results.append(
|
|
275
|
-
{"id": emotion.id, "memory": emotion.context, "score": emotion.intensity}
|
|
276
|
-
)
|
|
381
|
+
results = flatten_context_items(context)
|
|
277
382
|
append_step(timings, "map_context_results", step)
|
|
278
383
|
|
|
279
384
|
return {
|
|
@@ -295,30 +400,28 @@ async def handle_get_memories(params: dict) -> dict:
|
|
|
295
400
|
timings: List[dict] = []
|
|
296
401
|
|
|
297
402
|
user_id = params["user_id"]
|
|
403
|
+
customer_id = params.get("customer_id")
|
|
298
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"])
|
|
299
408
|
|
|
300
409
|
start = time.perf_counter()
|
|
301
410
|
|
|
302
411
|
step = time.perf_counter()
|
|
303
412
|
context = await sdk.user.context.fetch(
|
|
304
413
|
user_id=user_id,
|
|
414
|
+
customer_id=customer_id,
|
|
415
|
+
conversation_id=conversation_id,
|
|
305
416
|
search_query=[],
|
|
306
|
-
max_results=
|
|
307
|
-
types=
|
|
417
|
+
max_results=max_results,
|
|
418
|
+
types=types,
|
|
308
419
|
mode=mode,
|
|
309
420
|
)
|
|
310
421
|
append_step(timings, "context_fetch_all", step)
|
|
311
422
|
|
|
312
423
|
step = time.perf_counter()
|
|
313
|
-
memories =
|
|
314
|
-
for fact in context.facts:
|
|
315
|
-
memories.append({"id": fact.id, "memory": fact.content})
|
|
316
|
-
for preference in context.preferences:
|
|
317
|
-
memories.append({"id": preference.id, "memory": preference.content})
|
|
318
|
-
for episode in context.episodes:
|
|
319
|
-
memories.append({"id": episode.id, "memory": episode.summary})
|
|
320
|
-
for emotion in context.emotions:
|
|
321
|
-
memories.append({"id": emotion.id, "memory": emotion.context})
|
|
424
|
+
memories = flatten_context_items(context)
|
|
322
425
|
append_step(timings, "map_memories", step)
|
|
323
426
|
|
|
324
427
|
return {
|
|
@@ -326,7 +429,113 @@ async def handle_get_memories(params: dict) -> dict:
|
|
|
326
429
|
"latencyMs": ms_since(start),
|
|
327
430
|
"memories": memories,
|
|
328
431
|
"memoriesCount": len(memories),
|
|
432
|
+
"totalCount": len(memories),
|
|
329
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),
|
|
330
539
|
"bridgeTiming": {
|
|
331
540
|
"python_total_ms": ms_since(handler_start),
|
|
332
541
|
"steps": timings,
|
|
@@ -339,6 +548,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
339
548
|
timings: List[dict] = []
|
|
340
549
|
|
|
341
550
|
user_id = params["user_id"]
|
|
551
|
+
customer_id = params.get("customer_id")
|
|
342
552
|
memory_id = params.get("memory_id")
|
|
343
553
|
|
|
344
554
|
start = time.perf_counter()
|
|
@@ -350,6 +560,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
350
560
|
return {
|
|
351
561
|
"success": True,
|
|
352
562
|
"latencyMs": ms_since(start),
|
|
563
|
+
"deletedCount": 1,
|
|
353
564
|
"rawResponse": {"deleted": 1},
|
|
354
565
|
"bridgeTiming": {
|
|
355
566
|
"python_total_ms": ms_since(handler_start),
|
|
@@ -357,11 +568,15 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
357
568
|
},
|
|
358
569
|
}
|
|
359
570
|
|
|
360
|
-
|
|
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), [])
|
|
361
575
|
if not tracked_ids:
|
|
362
576
|
return {
|
|
363
577
|
"success": True,
|
|
364
578
|
"latencyMs": 0,
|
|
579
|
+
"deletedCount": 0,
|
|
365
580
|
"rawResponse": None,
|
|
366
581
|
"note": "No tracked memory IDs for this user",
|
|
367
582
|
"bridgeTiming": {
|
|
@@ -380,7 +595,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
380
595
|
last_error = str(exc)
|
|
381
596
|
append_step(timings, "delete_tracked_memories", step)
|
|
382
597
|
|
|
383
|
-
user_memory_ids.pop(user_id, None)
|
|
598
|
+
user_memory_ids.pop(tracking_key(user_id, customer_id), None)
|
|
384
599
|
|
|
385
600
|
if last_error:
|
|
386
601
|
return {
|
|
@@ -396,6 +611,7 @@ async def handle_delete_memory(params: dict) -> dict:
|
|
|
396
611
|
return {
|
|
397
612
|
"success": True,
|
|
398
613
|
"latencyMs": ms_since(start),
|
|
614
|
+
"deletedCount": len(tracked_ids),
|
|
399
615
|
"rawResponse": {"deleted": len(tracked_ids)},
|
|
400
616
|
"note": f"Deleted {len(tracked_ids)} memories",
|
|
401
617
|
"bridgeTiming": {
|
|
@@ -434,6 +650,10 @@ HANDLERS = {
|
|
|
434
650
|
"add_memory": handle_add_memory,
|
|
435
651
|
"search_memory": handle_search_memory,
|
|
436
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,
|
|
437
657
|
"delete_memory": handle_delete_memory,
|
|
438
658
|
"shutdown": handle_shutdown,
|
|
439
659
|
}
|
|
@@ -489,7 +709,7 @@ async def main() -> None:
|
|
|
489
709
|
write_response({"id": req_id, "result": result, "error": None})
|
|
490
710
|
except Exception as exc:
|
|
491
711
|
logger.error("Handler error for %s: %s", method, traceback.format_exc())
|
|
492
|
-
write_response({"id": req_id, "result": None, "error": str(exc)})
|
|
712
|
+
write_response({"id": req_id, "result": None, "error": str(exc), "error_type": type(exc).__name__})
|
|
493
713
|
|
|
494
714
|
|
|
495
715
|
if __name__ == "__main__":
|
package/package.json
CHANGED
package/src/bridge-manager.js
CHANGED
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
resolveInstanceId,
|
|
8
8
|
setupPythonRuntime,
|
|
9
9
|
} = require('./runtime');
|
|
10
|
+
const { createSynapError } = require('./errors');
|
|
10
11
|
|
|
11
12
|
class BridgeManager {
|
|
12
13
|
constructor(options = {}) {
|
|
@@ -147,7 +148,7 @@ class BridgeManager {
|
|
|
147
148
|
this.pending.delete(payload.id);
|
|
148
149
|
|
|
149
150
|
if (payload.error) {
|
|
150
|
-
pending.reject(
|
|
151
|
+
pending.reject(createSynapError(payload.error, payload.error_type));
|
|
151
152
|
} else {
|
|
152
153
|
pending.resolve(payload.result);
|
|
153
154
|
}
|