@realtimex/sdk 1.0.9 → 1.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/README.md +152 -0
- package/dist/index.d.mts +293 -1
- package/dist/index.d.ts +293 -1
- package/dist/index.js +387 -0
- package/dist/index.mjs +383 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -429,6 +429,384 @@ var PortModule = class {
|
|
|
429
429
|
}
|
|
430
430
|
};
|
|
431
431
|
|
|
432
|
+
// src/modules/llm.ts
|
|
433
|
+
var LLMPermissionError = class extends Error {
|
|
434
|
+
constructor(permission, code = "PERMISSION_REQUIRED") {
|
|
435
|
+
super(`Permission required: ${permission}`);
|
|
436
|
+
this.permission = permission;
|
|
437
|
+
this.code = code;
|
|
438
|
+
this.name = "LLMPermissionError";
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
var LLMProviderError = class extends Error {
|
|
442
|
+
constructor(message, code = "LLM_ERROR") {
|
|
443
|
+
super(message);
|
|
444
|
+
this.code = code;
|
|
445
|
+
this.name = "LLMProviderError";
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
var VectorStore = class {
|
|
449
|
+
constructor(baseUrl, appId) {
|
|
450
|
+
this.baseUrl = baseUrl;
|
|
451
|
+
this.appId = appId;
|
|
452
|
+
}
|
|
453
|
+
get headers() {
|
|
454
|
+
return {
|
|
455
|
+
"Content-Type": "application/json",
|
|
456
|
+
"x-app-id": this.appId
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Upsert (insert or update) vectors into storage
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```ts
|
|
464
|
+
* await sdk.llm.vectors.upsert([
|
|
465
|
+
* { id: 'chunk-1', vector: embeddings[0], metadata: { text: 'Hello', documentId: 'doc-1' } }
|
|
466
|
+
* ], { workspaceId: 'ws-123' });
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
async upsert(vectors, options = {}) {
|
|
470
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/upsert`, {
|
|
471
|
+
method: "POST",
|
|
472
|
+
headers: this.headers,
|
|
473
|
+
body: JSON.stringify({
|
|
474
|
+
vectors,
|
|
475
|
+
workspaceId: options.workspaceId
|
|
476
|
+
})
|
|
477
|
+
});
|
|
478
|
+
const data = await response.json();
|
|
479
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
480
|
+
throw new LLMPermissionError(data.permission || "vectors.write");
|
|
481
|
+
}
|
|
482
|
+
return data;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Query similar vectors by embedding
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* ```ts
|
|
489
|
+
* const results = await sdk.llm.vectors.query(queryVector, {
|
|
490
|
+
* topK: 5,
|
|
491
|
+
* filter: { documentId: 'doc-1' },
|
|
492
|
+
* workspaceId: 'ws-123'
|
|
493
|
+
* });
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
async query(vector, options = {}) {
|
|
497
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/query`, {
|
|
498
|
+
method: "POST",
|
|
499
|
+
headers: this.headers,
|
|
500
|
+
body: JSON.stringify({
|
|
501
|
+
vector,
|
|
502
|
+
topK: options.topK ?? 5,
|
|
503
|
+
filter: options.filter,
|
|
504
|
+
workspaceId: options.workspaceId
|
|
505
|
+
})
|
|
506
|
+
});
|
|
507
|
+
const data = await response.json();
|
|
508
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
509
|
+
throw new LLMPermissionError(data.permission || "vectors.read");
|
|
510
|
+
}
|
|
511
|
+
return data;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Delete vectors from storage
|
|
515
|
+
*
|
|
516
|
+
* Note: Currently only supports deleteAll: true
|
|
517
|
+
* Use workspaceId to scope deletion to a specific workspace
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```ts
|
|
521
|
+
* await sdk.llm.vectors.delete({ deleteAll: true, workspaceId: 'ws-123' });
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
524
|
+
async delete(options) {
|
|
525
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/delete`, {
|
|
526
|
+
method: "POST",
|
|
527
|
+
headers: this.headers,
|
|
528
|
+
body: JSON.stringify(options)
|
|
529
|
+
});
|
|
530
|
+
const data = await response.json();
|
|
531
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
532
|
+
throw new LLMPermissionError(data.permission || "vectors.write");
|
|
533
|
+
}
|
|
534
|
+
return data;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* List all available workspaces (namespaces) for this app
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```ts
|
|
541
|
+
* const { workspaces } = await sdk.llm.vectors.listWorkspaces();
|
|
542
|
+
* console.log('Workspaces:', workspaces);
|
|
543
|
+
* ```
|
|
544
|
+
*/
|
|
545
|
+
async listWorkspaces() {
|
|
546
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/workspaces`, {
|
|
547
|
+
method: "GET",
|
|
548
|
+
headers: this.headers
|
|
549
|
+
});
|
|
550
|
+
const data = await response.json();
|
|
551
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
552
|
+
throw new LLMPermissionError(data.permission || "vectors.read");
|
|
553
|
+
}
|
|
554
|
+
return data;
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
var LLMModule = class {
|
|
558
|
+
constructor(baseUrl, appId) {
|
|
559
|
+
this.baseUrl = baseUrl;
|
|
560
|
+
this.appId = appId;
|
|
561
|
+
this.vectors = new VectorStore(baseUrl, appId);
|
|
562
|
+
}
|
|
563
|
+
get headers() {
|
|
564
|
+
return {
|
|
565
|
+
"Content-Type": "application/json",
|
|
566
|
+
"x-app-id": this.appId
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Get available LLM and embedding providers/models
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```ts
|
|
574
|
+
* const { llm, embedding } = await sdk.llm.getProviders();
|
|
575
|
+
* console.log('Available LLM models:', llm[0].models);
|
|
576
|
+
* ```
|
|
577
|
+
*/
|
|
578
|
+
async getProviders() {
|
|
579
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/providers`, {
|
|
580
|
+
method: "GET",
|
|
581
|
+
headers: this.headers
|
|
582
|
+
});
|
|
583
|
+
const data = await response.json();
|
|
584
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
585
|
+
throw new LLMPermissionError(data.permission || "llm.providers");
|
|
586
|
+
}
|
|
587
|
+
return data;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Send a chat completion request (synchronous)
|
|
591
|
+
*
|
|
592
|
+
* @example
|
|
593
|
+
* ```ts
|
|
594
|
+
* const response = await sdk.llm.chat([
|
|
595
|
+
* { role: 'system', content: 'You are a helpful assistant.' },
|
|
596
|
+
* { role: 'user', content: 'Hello!' }
|
|
597
|
+
* ], { model: 'gpt-4o', temperature: 0.7 });
|
|
598
|
+
*
|
|
599
|
+
* console.log(response.response?.content);
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
async chat(messages, options = {}) {
|
|
603
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/chat`, {
|
|
604
|
+
method: "POST",
|
|
605
|
+
headers: this.headers,
|
|
606
|
+
body: JSON.stringify({
|
|
607
|
+
messages,
|
|
608
|
+
model: options.model,
|
|
609
|
+
provider: options.provider,
|
|
610
|
+
temperature: options.temperature ?? 0.7,
|
|
611
|
+
max_tokens: options.max_tokens ?? 1e3
|
|
612
|
+
})
|
|
613
|
+
});
|
|
614
|
+
const data = await response.json();
|
|
615
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
616
|
+
throw new LLMPermissionError(data.permission || "llm.chat");
|
|
617
|
+
}
|
|
618
|
+
if (data.code === "LLM_ERROR") {
|
|
619
|
+
throw new LLMProviderError(data.error || "LLM request failed");
|
|
620
|
+
}
|
|
621
|
+
return data;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Send a streaming chat completion request (SSE)
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```ts
|
|
628
|
+
* for await (const chunk of sdk.llm.chatStream([
|
|
629
|
+
* { role: 'user', content: 'Tell me a story' }
|
|
630
|
+
* ])) {
|
|
631
|
+
* process.stdout.write(chunk.textResponse || '');
|
|
632
|
+
* }
|
|
633
|
+
* ```
|
|
634
|
+
*/
|
|
635
|
+
async *chatStream(messages, options = {}) {
|
|
636
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/chat/stream`, {
|
|
637
|
+
method: "POST",
|
|
638
|
+
headers: {
|
|
639
|
+
...this.headers,
|
|
640
|
+
"Accept": "text/event-stream"
|
|
641
|
+
},
|
|
642
|
+
body: JSON.stringify({
|
|
643
|
+
messages,
|
|
644
|
+
model: options.model,
|
|
645
|
+
provider: options.provider,
|
|
646
|
+
temperature: options.temperature ?? 0.7,
|
|
647
|
+
max_tokens: options.max_tokens ?? 1e3
|
|
648
|
+
})
|
|
649
|
+
});
|
|
650
|
+
if (!response.ok) {
|
|
651
|
+
const errorData = await response.json();
|
|
652
|
+
if (errorData.code === "PERMISSION_REQUIRED") {
|
|
653
|
+
throw new LLMPermissionError(errorData.permission || "llm.chat");
|
|
654
|
+
}
|
|
655
|
+
throw new LLMProviderError(errorData.error || "Stream request failed");
|
|
656
|
+
}
|
|
657
|
+
const reader = response.body?.getReader();
|
|
658
|
+
if (!reader) {
|
|
659
|
+
throw new LLMProviderError("Response body is not readable");
|
|
660
|
+
}
|
|
661
|
+
const decoder = new TextDecoder();
|
|
662
|
+
let buffer = "";
|
|
663
|
+
let isErrorEvent = false;
|
|
664
|
+
try {
|
|
665
|
+
while (true) {
|
|
666
|
+
const { done, value } = await reader.read();
|
|
667
|
+
if (done) break;
|
|
668
|
+
buffer += decoder.decode(value, { stream: true });
|
|
669
|
+
const lines = buffer.split("\n");
|
|
670
|
+
buffer = lines.pop() || "";
|
|
671
|
+
for (const line of lines) {
|
|
672
|
+
const trimmedLine = line.trim();
|
|
673
|
+
if (!trimmedLine || trimmedLine.startsWith(":")) continue;
|
|
674
|
+
if (trimmedLine.startsWith("event: error")) {
|
|
675
|
+
isErrorEvent = true;
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
if (trimmedLine.startsWith("data: ")) {
|
|
679
|
+
const jsonStr = trimmedLine.slice(6);
|
|
680
|
+
if (jsonStr === "[DONE]") {
|
|
681
|
+
isErrorEvent = false;
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
const data = JSON.parse(jsonStr);
|
|
686
|
+
if (isErrorEvent) {
|
|
687
|
+
isErrorEvent = false;
|
|
688
|
+
throw new LLMProviderError(
|
|
689
|
+
data.error || "Stream error",
|
|
690
|
+
data.code || "LLM_STREAM_ERROR"
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
const chunk = data;
|
|
694
|
+
if (chunk.error) {
|
|
695
|
+
throw new LLMProviderError(
|
|
696
|
+
chunk.message || "Stream error"
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
yield chunk;
|
|
700
|
+
} catch (parseError) {
|
|
701
|
+
isErrorEvent = false;
|
|
702
|
+
if (jsonStr !== "[DONE]") {
|
|
703
|
+
console.warn("[LLM Stream] Parse error:", jsonStr);
|
|
704
|
+
}
|
|
705
|
+
if (parseError instanceof LLMProviderError) throw parseError;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
} finally {
|
|
711
|
+
reader.releaseLock();
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Generate vector embeddings from text
|
|
716
|
+
*
|
|
717
|
+
* @example
|
|
718
|
+
* ```ts
|
|
719
|
+
* // Single text
|
|
720
|
+
* const { embeddings } = await sdk.llm.embed('Hello world');
|
|
721
|
+
*
|
|
722
|
+
* // Multiple texts
|
|
723
|
+
* const { embeddings } = await sdk.llm.embed(['Hello', 'World']);
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
async embed(input, options = {}) {
|
|
727
|
+
const inputArray = Array.isArray(input) ? input : [input];
|
|
728
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/embed`, {
|
|
729
|
+
method: "POST",
|
|
730
|
+
headers: this.headers,
|
|
731
|
+
body: JSON.stringify({
|
|
732
|
+
input: inputArray,
|
|
733
|
+
provider: options.provider,
|
|
734
|
+
model: options.model
|
|
735
|
+
})
|
|
736
|
+
});
|
|
737
|
+
const data = await response.json();
|
|
738
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
739
|
+
throw new LLMPermissionError(data.permission || "llm.embed");
|
|
740
|
+
}
|
|
741
|
+
if (data.code === "PROVIDER_UNAVAILABLE") {
|
|
742
|
+
throw new LLMProviderError(data.error || "Embedding provider not available");
|
|
743
|
+
}
|
|
744
|
+
return data;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Helper: Embed text and store as vectors in one call
|
|
748
|
+
*
|
|
749
|
+
* @example
|
|
750
|
+
* ```ts
|
|
751
|
+
* await sdk.llm.embedAndStore({
|
|
752
|
+
* texts: ['Hello world', 'Goodbye world'],
|
|
753
|
+
* documentId: 'doc-123',
|
|
754
|
+
* workspaceId: 'ws-456'
|
|
755
|
+
* });
|
|
756
|
+
* ```
|
|
757
|
+
*/
|
|
758
|
+
async embedAndStore(params) {
|
|
759
|
+
const { texts, documentId, workspaceId, idPrefix = "chunk", provider, model } = params;
|
|
760
|
+
const embedResult = await this.embed(texts, { provider, model });
|
|
761
|
+
if (!embedResult.success || !embedResult.embeddings) {
|
|
762
|
+
return {
|
|
763
|
+
success: false,
|
|
764
|
+
error: embedResult.error || "Embedding failed",
|
|
765
|
+
code: embedResult.code
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
const vectors = texts.map((text, i) => ({
|
|
769
|
+
id: `${idPrefix}_${i}`,
|
|
770
|
+
vector: embedResult.embeddings[i],
|
|
771
|
+
metadata: {
|
|
772
|
+
text,
|
|
773
|
+
documentId,
|
|
774
|
+
workspaceId
|
|
775
|
+
}
|
|
776
|
+
}));
|
|
777
|
+
return this.vectors.upsert(vectors, { workspaceId });
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Helper: Search similar documents by text query
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```ts
|
|
784
|
+
* const results = await sdk.llm.search('What is RealtimeX?', {
|
|
785
|
+
* topK: 5,
|
|
786
|
+
* workspaceId: 'ws-123'
|
|
787
|
+
* });
|
|
788
|
+
*
|
|
789
|
+
* for (const result of results) {
|
|
790
|
+
* console.log(result.metadata?.text, result.score);
|
|
791
|
+
* }
|
|
792
|
+
* ```
|
|
793
|
+
*/
|
|
794
|
+
async search(query, options = {}) {
|
|
795
|
+
const embedResult = await this.embed(query, {
|
|
796
|
+
provider: options.provider,
|
|
797
|
+
model: options.model
|
|
798
|
+
});
|
|
799
|
+
if (!embedResult.success || !embedResult.embeddings?.[0]) {
|
|
800
|
+
throw new LLMProviderError("Failed to embed query");
|
|
801
|
+
}
|
|
802
|
+
const queryResult = await this.vectors.query(embedResult.embeddings[0], options);
|
|
803
|
+
if (!queryResult.success) {
|
|
804
|
+
throw new LLMProviderError(queryResult.error || "Vector search failed");
|
|
805
|
+
}
|
|
806
|
+
return queryResult.results || [];
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
|
|
432
810
|
// src/index.ts
|
|
433
811
|
var _RealtimeXSDK = class _RealtimeXSDK {
|
|
434
812
|
constructor(config = {}) {
|
|
@@ -443,6 +821,7 @@ var _RealtimeXSDK = class _RealtimeXSDK {
|
|
|
443
821
|
this.api = new ApiModule(this.realtimexUrl, this.appId, this.appName);
|
|
444
822
|
this.task = new TaskModule(this.realtimexUrl, this.appName, this.appId);
|
|
445
823
|
this.port = new PortModule(config.defaultPort);
|
|
824
|
+
this.llm = new LLMModule(this.realtimexUrl, this.appId);
|
|
446
825
|
if (this.permissions.length > 0) {
|
|
447
826
|
this.register().catch((err) => {
|
|
448
827
|
console.error("[RealtimeX SDK] Auto-registration failed:", err.message);
|
|
@@ -493,10 +872,14 @@ var RealtimeXSDK = _RealtimeXSDK;
|
|
|
493
872
|
export {
|
|
494
873
|
ActivitiesModule,
|
|
495
874
|
ApiModule,
|
|
875
|
+
LLMModule,
|
|
876
|
+
LLMPermissionError,
|
|
877
|
+
LLMProviderError,
|
|
496
878
|
PermissionDeniedError,
|
|
497
879
|
PermissionRequiredError,
|
|
498
880
|
PortModule,
|
|
499
881
|
RealtimeXSDK,
|
|
500
882
|
TaskModule,
|
|
883
|
+
VectorStore,
|
|
501
884
|
WebhookModule
|
|
502
885
|
};
|