@maximem/synap-js-sdk 0.2.4 → 0.3.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/bridge/synap_bridge.py +63 -3
- package/package.json +6 -2
- package/src/errors.js +17 -0
- package/src/synap-client.js +101 -0
- package/types/index.d.ts +91 -0
package/bridge/synap_bridge.py
CHANGED
|
@@ -5,9 +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,
|
|
9
|
-
fetch_customer_context, fetch_client_context,
|
|
10
|
-
delete_memory, shutdown
|
|
8
|
+
init, add_memory, create_memory, record_message, search_memory, get_memories,
|
|
9
|
+
fetch_user_context, fetch_customer_context, fetch_client_context,
|
|
10
|
+
get_context_for_prompt, delete_memory, shutdown
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import asyncio
|
|
@@ -637,9 +637,69 @@ async def handle_shutdown(_params: dict) -> dict:
|
|
|
637
637
|
}
|
|
638
638
|
|
|
639
639
|
|
|
640
|
+
async def handle_record_message(params: dict) -> dict:
|
|
641
|
+
"""Record a single conversation turn (registers the conversation_id).
|
|
642
|
+
|
|
643
|
+
Mirrors sdk.conversation.record_message(...) so the JS client can expose
|
|
644
|
+
the same namespaced surface the integration packages duck-type against.
|
|
645
|
+
"""
|
|
646
|
+
handler_start = time.perf_counter()
|
|
647
|
+
timings: List[dict] = []
|
|
648
|
+
start = time.perf_counter()
|
|
649
|
+
step = time.perf_counter()
|
|
650
|
+
result = await sdk.conversation.record_message(
|
|
651
|
+
conversation_id=params["conversation_id"],
|
|
652
|
+
role=params["role"],
|
|
653
|
+
content=params["content"],
|
|
654
|
+
user_id=params["user_id"],
|
|
655
|
+
customer_id=params["customer_id"],
|
|
656
|
+
session_id=params.get("session_id"),
|
|
657
|
+
metadata=params.get("metadata"),
|
|
658
|
+
)
|
|
659
|
+
append_step(timings, "record_message", step)
|
|
660
|
+
return {
|
|
661
|
+
"success": True,
|
|
662
|
+
"latencyMs": ms_since(start),
|
|
663
|
+
"result": result,
|
|
664
|
+
"bridgeTiming": {"python_total_ms": ms_since(handler_start), "steps": timings},
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
async def handle_create_memory(params: dict) -> dict:
|
|
669
|
+
"""Create a durable memory from a single document string.
|
|
670
|
+
|
|
671
|
+
Mirrors sdk.memories.create(document=...). Distinct from add_memory,
|
|
672
|
+
which builds a transcript from a messages array.
|
|
673
|
+
"""
|
|
674
|
+
handler_start = time.perf_counter()
|
|
675
|
+
timings: List[dict] = []
|
|
676
|
+
start = time.perf_counter()
|
|
677
|
+
create_kwargs: dict = {"document": params["document"]}
|
|
678
|
+
for key in ("user_id", "customer_id", "document_type", "document_id",
|
|
679
|
+
"document_created_at", "mode", "metadata"):
|
|
680
|
+
if params.get(key) is not None:
|
|
681
|
+
create_kwargs[key] = params[key]
|
|
682
|
+
step = time.perf_counter()
|
|
683
|
+
result = await sdk.memories.create(**create_kwargs)
|
|
684
|
+
append_step(timings, "memories_create", step)
|
|
685
|
+
status = result.status.value if hasattr(result.status, "value") else result.status
|
|
686
|
+
return {
|
|
687
|
+
"success": True,
|
|
688
|
+
"latencyMs": ms_since(start),
|
|
689
|
+
"result": {
|
|
690
|
+
"ingestion_id": str(result.ingestion_id),
|
|
691
|
+
"document_id": result.document_id,
|
|
692
|
+
"status": status,
|
|
693
|
+
},
|
|
694
|
+
"bridgeTiming": {"python_total_ms": ms_since(handler_start), "steps": timings},
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
|
|
640
698
|
HANDLERS = {
|
|
641
699
|
"init": handle_init,
|
|
642
700
|
"add_memory": handle_add_memory,
|
|
701
|
+
"create_memory": handle_create_memory,
|
|
702
|
+
"record_message": handle_record_message,
|
|
643
703
|
"search_memory": handle_search_memory,
|
|
644
704
|
"get_memories": handle_get_memories,
|
|
645
705
|
"fetch_user_context": handle_fetch_user_context,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maximem/synap-js-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "JavaScript wrapper around the Synap Python SDK",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"setup": "node bin/synap-js-sdk.js setup",
|
|
21
21
|
"setup:ts": "node bin/synap-js-sdk.js setup-ts",
|
|
22
|
-
"check": "node -e \"const sdk=require('./src'); console.log(Object.keys(sdk));\""
|
|
22
|
+
"check": "node -e \"const sdk=require('./src'); console.log(Object.keys(sdk));\"",
|
|
23
|
+
"test": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"vitest": "^2.1.9"
|
|
23
27
|
},
|
|
24
28
|
"engines": {
|
|
25
29
|
"node": ">=18"
|
package/src/errors.js
CHANGED
|
@@ -79,6 +79,21 @@ class AuthenticationError extends SynapPermanentError {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
class InsufficientCreditsError extends SynapPermanentError {
|
|
83
|
+
constructor(message, details, correlationId) {
|
|
84
|
+
super(message, correlationId);
|
|
85
|
+
this.name = 'InsufficientCreditsError';
|
|
86
|
+
// Structured details from the server's 402 response body:
|
|
87
|
+
// { balance_credits, minimum_required_credits, recovery_url, redeem_url }
|
|
88
|
+
this.balanceCredits = details && details.balance_credits != null
|
|
89
|
+
? Number(details.balance_credits) : null;
|
|
90
|
+
this.minimumRequiredCredits = details && details.minimum_required_credits != null
|
|
91
|
+
? Number(details.minimum_required_credits) : null;
|
|
92
|
+
this.recoveryUrl = details && details.recovery_url ? details.recovery_url : null;
|
|
93
|
+
this.redeemUrl = details && details.redeem_url ? details.redeem_url : null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
82
97
|
class ContextNotFoundError extends SynapPermanentError {
|
|
83
98
|
constructor(message, correlationId) {
|
|
84
99
|
super(message, correlationId);
|
|
@@ -119,6 +134,7 @@ const ERROR_MAP = {
|
|
|
119
134
|
InvalidInstanceIdError,
|
|
120
135
|
InvalidConversationIdError,
|
|
121
136
|
AuthenticationError,
|
|
137
|
+
InsufficientCreditsError,
|
|
122
138
|
ContextNotFoundError,
|
|
123
139
|
SessionExpiredError,
|
|
124
140
|
ListeningAlreadyActiveError,
|
|
@@ -143,6 +159,7 @@ module.exports = {
|
|
|
143
159
|
InvalidInstanceIdError,
|
|
144
160
|
InvalidConversationIdError,
|
|
145
161
|
AuthenticationError,
|
|
162
|
+
InsufficientCreditsError,
|
|
146
163
|
ContextNotFoundError,
|
|
147
164
|
SessionExpiredError,
|
|
148
165
|
ListeningAlreadyActiveError,
|
package/src/synap-client.js
CHANGED
|
@@ -203,6 +203,7 @@ class SynapClient {
|
|
|
203
203
|
};
|
|
204
204
|
|
|
205
205
|
this.#registerShutdownHooks();
|
|
206
|
+
this.#installNamespacedApi();
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
async init() {
|
|
@@ -358,6 +359,106 @@ class SynapClient {
|
|
|
358
359
|
process.exit(0);
|
|
359
360
|
});
|
|
360
361
|
}
|
|
362
|
+
|
|
363
|
+
#installNamespacedApi() {
|
|
364
|
+
const buildParams = (args = {}) => {
|
|
365
|
+
const params = {};
|
|
366
|
+
const conv = pickDefined(args.conversation_id, args.conversationId);
|
|
367
|
+
const sq = pickDefined(args.search_query, args.searchQuery);
|
|
368
|
+
const mr = pickDefined(args.max_results, args.maxResults);
|
|
369
|
+
if (conv !== undefined && conv !== null) params.conversation_id = conv;
|
|
370
|
+
if (sq !== undefined && sq !== null) params.search_query = sq;
|
|
371
|
+
if (mr !== undefined) params.max_results = mr;
|
|
372
|
+
if (args.types !== undefined) params.types = args.types;
|
|
373
|
+
if (args.mode !== undefined) params.mode = args.mode;
|
|
374
|
+
return params;
|
|
375
|
+
};
|
|
376
|
+
const fetch = async (args = {}) => {
|
|
377
|
+
const userId = pickDefined(args.user_id, args.userId);
|
|
378
|
+
const customerId = pickDefined(args.customer_id, args.customerId);
|
|
379
|
+
const params = buildParams(args);
|
|
380
|
+
let command;
|
|
381
|
+
if (userId !== undefined && userId !== null) {
|
|
382
|
+
command = 'fetch_user_context';
|
|
383
|
+
params.user_id = userId;
|
|
384
|
+
if (customerId !== undefined && customerId !== null) params.customer_id = customerId;
|
|
385
|
+
} else if (customerId !== undefined && customerId !== null) {
|
|
386
|
+
command = 'fetch_customer_context';
|
|
387
|
+
params.customer_id = customerId;
|
|
388
|
+
} else {
|
|
389
|
+
command = 'fetch_client_context';
|
|
390
|
+
}
|
|
391
|
+
const result = await this.bridge.call(command, params);
|
|
392
|
+
return result.context || {};
|
|
393
|
+
};
|
|
394
|
+
const fetchScope = (command, idKeys) => async (args = {}) => {
|
|
395
|
+
const params = buildParams(args);
|
|
396
|
+
if (idKeys.includes('user_id')) {
|
|
397
|
+
const userId = pickDefined(args.user_id, args.userId);
|
|
398
|
+
if (userId !== undefined && userId !== null) params.user_id = userId;
|
|
399
|
+
}
|
|
400
|
+
if (idKeys.includes('customer_id')) {
|
|
401
|
+
const customerId = pickDefined(args.customer_id, args.customerId);
|
|
402
|
+
if (customerId !== undefined && customerId !== null) params.customer_id = customerId;
|
|
403
|
+
}
|
|
404
|
+
const result = await this.bridge.call(command, params);
|
|
405
|
+
return result.context || {};
|
|
406
|
+
};
|
|
407
|
+
this.fetch = fetch;
|
|
408
|
+
this.conversation = {
|
|
409
|
+
record_message: async (args = {}) => {
|
|
410
|
+
const conversationId = pickDefined(args.conversation_id, args.conversationId);
|
|
411
|
+
const userId = pickDefined(args.user_id, args.userId);
|
|
412
|
+
const customerId = pickDefined(args.customer_id, args.customerId);
|
|
413
|
+
this.#assert(conversationId, 'conversation_id is required');
|
|
414
|
+
this.#assert(userId, 'user_id is required');
|
|
415
|
+
this.#assert(customerId, 'customer_id is required');
|
|
416
|
+
const params = {
|
|
417
|
+
conversation_id: conversationId, role: args.role, content: args.content,
|
|
418
|
+
user_id: userId, customer_id: customerId,
|
|
419
|
+
};
|
|
420
|
+
const sessionId = pickDefined(args.session_id, args.sessionId);
|
|
421
|
+
if (sessionId !== undefined) params.session_id = sessionId;
|
|
422
|
+
if (args.metadata !== undefined) params.metadata = args.metadata;
|
|
423
|
+
const result = await this.bridge.call('record_message', params);
|
|
424
|
+
return pickDefined(result.result, result);
|
|
425
|
+
},
|
|
426
|
+
context: {
|
|
427
|
+
get_context_for_prompt: async (args = {}) => {
|
|
428
|
+
const conversationId = pickDefined(args.conversation_id, args.conversationId);
|
|
429
|
+
this.#assert(conversationId, 'conversation_id is required');
|
|
430
|
+
const params = { conversation_id: conversationId };
|
|
431
|
+
if (args.style !== undefined) params.style = args.style;
|
|
432
|
+
const result = await this.bridge.call('get_context_for_prompt', params);
|
|
433
|
+
return pickDefined(result.context_for_prompt, result.contextForPrompt, {});
|
|
434
|
+
},
|
|
435
|
+
fetch,
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
this.memories = {
|
|
439
|
+
create: async (args = {}) => {
|
|
440
|
+
this.#assert(args.document, 'document is required');
|
|
441
|
+
const params = { document: args.document };
|
|
442
|
+
const userId = pickDefined(args.user_id, args.userId);
|
|
443
|
+
const customerId = pickDefined(args.customer_id, args.customerId);
|
|
444
|
+
const documentType = pickDefined(args.document_type, args.documentType);
|
|
445
|
+
const documentId = pickDefined(args.document_id, args.documentId);
|
|
446
|
+
const documentCreatedAt = pickDefined(args.document_created_at, args.documentCreatedAt);
|
|
447
|
+
if (userId !== undefined) params.user_id = userId;
|
|
448
|
+
if (customerId !== undefined) params.customer_id = customerId;
|
|
449
|
+
if (documentType !== undefined) params.document_type = documentType;
|
|
450
|
+
if (documentId !== undefined) params.document_id = documentId;
|
|
451
|
+
if (documentCreatedAt !== undefined) params.document_created_at = documentCreatedAt;
|
|
452
|
+
if (args.mode !== undefined) params.mode = args.mode;
|
|
453
|
+
if (args.metadata !== undefined) params.metadata = args.metadata;
|
|
454
|
+
const result = await this.bridge.call('create_memory', params, this.options.ingestTimeoutMs);
|
|
455
|
+
return pickDefined(result.result, result);
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
this.user = { context: { fetch: fetchScope('fetch_user_context', ['user_id', 'customer_id']) } };
|
|
459
|
+
this.customer = { context: { fetch: fetchScope('fetch_customer_context', ['customer_id']) } };
|
|
460
|
+
this.client = { context: { fetch: fetchScope('fetch_client_context', []) } };
|
|
461
|
+
}
|
|
361
462
|
}
|
|
362
463
|
|
|
363
464
|
module.exports = { SynapClient };
|
package/types/index.d.ts
CHANGED
|
@@ -296,6 +296,75 @@ export interface DeleteMemoryResult {
|
|
|
296
296
|
bridgeTiming?: BridgeTiming;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
export interface SynapNamespacedFetchInput {
|
|
300
|
+
userId?: string | null;
|
|
301
|
+
user_id?: string | null;
|
|
302
|
+
customerId?: string | null;
|
|
303
|
+
customer_id?: string | null;
|
|
304
|
+
conversationId?: string | null;
|
|
305
|
+
conversation_id?: string | null;
|
|
306
|
+
searchQuery?: string[] | null;
|
|
307
|
+
search_query?: string[] | null;
|
|
308
|
+
maxResults?: number;
|
|
309
|
+
max_results?: number;
|
|
310
|
+
types?: string[];
|
|
311
|
+
mode?: string;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export interface SynapRecordMessageInput {
|
|
315
|
+
conversationId?: string | null;
|
|
316
|
+
conversation_id?: string | null;
|
|
317
|
+
role: string;
|
|
318
|
+
content: string;
|
|
319
|
+
userId?: string | null;
|
|
320
|
+
user_id?: string | null;
|
|
321
|
+
customerId?: string | null;
|
|
322
|
+
customer_id?: string | null;
|
|
323
|
+
sessionId?: string | null;
|
|
324
|
+
session_id?: string | null;
|
|
325
|
+
metadata?: Record<string, unknown>;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export interface SynapCreateMemoryInput {
|
|
329
|
+
document: string;
|
|
330
|
+
userId?: string | null;
|
|
331
|
+
user_id?: string | null;
|
|
332
|
+
customerId?: string | null;
|
|
333
|
+
customer_id?: string | null;
|
|
334
|
+
documentType?: string | null;
|
|
335
|
+
document_type?: string | null;
|
|
336
|
+
documentId?: string | null;
|
|
337
|
+
document_id?: string | null;
|
|
338
|
+
documentCreatedAt?: string | null;
|
|
339
|
+
document_created_at?: string | null;
|
|
340
|
+
mode?: string | null;
|
|
341
|
+
metadata?: Record<string, unknown>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export interface SynapGetContextForPromptInput {
|
|
345
|
+
conversationId?: string;
|
|
346
|
+
conversation_id?: string;
|
|
347
|
+
style?: string;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface SynapScopeContextApi {
|
|
351
|
+
context: {
|
|
352
|
+
fetch(input?: SynapNamespacedFetchInput): Promise<any>;
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface SynapConversationApi {
|
|
357
|
+
record_message(input: SynapRecordMessageInput): Promise<any>;
|
|
358
|
+
context: {
|
|
359
|
+
get_context_for_prompt(input: SynapGetContextForPromptInput): Promise<any>;
|
|
360
|
+
fetch(input?: SynapNamespacedFetchInput): Promise<any>;
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface SynapMemoriesApi {
|
|
365
|
+
create(input: SynapCreateMemoryInput): Promise<any>;
|
|
366
|
+
}
|
|
367
|
+
|
|
299
368
|
export class SynapClient {
|
|
300
369
|
constructor(options?: SynapClientOptions);
|
|
301
370
|
init(): Promise<void>;
|
|
@@ -308,6 +377,12 @@ export class SynapClient {
|
|
|
308
377
|
getContextForPrompt(input: GetContextForPromptInput): Promise<ContextForPromptResult>;
|
|
309
378
|
deleteMemory(input: DeleteMemoryInput): Promise<DeleteMemoryResult>;
|
|
310
379
|
shutdown(): Promise<void>;
|
|
380
|
+
fetch(input?: SynapNamespacedFetchInput): Promise<any>;
|
|
381
|
+
readonly conversation: SynapConversationApi;
|
|
382
|
+
readonly memories: SynapMemoriesApi;
|
|
383
|
+
readonly user: SynapScopeContextApi;
|
|
384
|
+
readonly customer: SynapScopeContextApi;
|
|
385
|
+
readonly client: SynapScopeContextApi;
|
|
311
386
|
}
|
|
312
387
|
|
|
313
388
|
export interface SetupPythonRuntimeOptions {
|
|
@@ -369,6 +444,22 @@ export class InvalidInputError extends SynapPermanentError {}
|
|
|
369
444
|
export class InvalidInstanceIdError extends InvalidInputError {}
|
|
370
445
|
export class InvalidConversationIdError extends InvalidInputError {}
|
|
371
446
|
export class AuthenticationError extends SynapPermanentError {}
|
|
447
|
+
export class InsufficientCreditsError extends SynapPermanentError {
|
|
448
|
+
balanceCredits: number | null;
|
|
449
|
+
minimumRequiredCredits: number | null;
|
|
450
|
+
recoveryUrl: string | null;
|
|
451
|
+
redeemUrl: string | null;
|
|
452
|
+
constructor(
|
|
453
|
+
message: string,
|
|
454
|
+
details?: {
|
|
455
|
+
balance_credits?: number | null;
|
|
456
|
+
minimum_required_credits?: number | null;
|
|
457
|
+
recovery_url?: string | null;
|
|
458
|
+
redeem_url?: string | null;
|
|
459
|
+
},
|
|
460
|
+
correlationId?: string,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
372
463
|
export class ContextNotFoundError extends SynapPermanentError {}
|
|
373
464
|
export class SessionExpiredError extends SynapPermanentError {}
|
|
374
465
|
export class ListeningAlreadyActiveError extends SynapPermanentError {}
|