@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/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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realtimex/sdk",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "SDK for building Local Apps that integrate with RealtimeX",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",