@cmdoss/memwal-sdk 0.8.0 → 0.9.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.
Files changed (62) hide show
  1. package/README.md +389 -132
  2. package/dist/client/SimplePDWClient.d.ts +60 -1
  3. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  4. package/dist/client/SimplePDWClient.js +73 -5
  5. package/dist/client/SimplePDWClient.js.map +1 -1
  6. package/dist/client/namespaces/IndexNamespace.d.ts +1 -1
  7. package/dist/client/namespaces/IndexNamespace.d.ts.map +1 -1
  8. package/dist/client/namespaces/IndexNamespace.js +7 -4
  9. package/dist/client/namespaces/IndexNamespace.js.map +1 -1
  10. package/dist/client/namespaces/MemoryNamespace.d.ts +41 -0
  11. package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
  12. package/dist/client/namespaces/MemoryNamespace.js +126 -9
  13. package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
  14. package/dist/client/namespaces/consolidated/AdvancedNamespace.d.ts +215 -0
  15. package/dist/client/namespaces/consolidated/AdvancedNamespace.d.ts.map +1 -0
  16. package/dist/client/namespaces/consolidated/AdvancedNamespace.js +214 -0
  17. package/dist/client/namespaces/consolidated/AdvancedNamespace.js.map +1 -0
  18. package/dist/client/namespaces/consolidated/index.d.ts +1 -0
  19. package/dist/client/namespaces/consolidated/index.d.ts.map +1 -1
  20. package/dist/client/namespaces/consolidated/index.js +1 -0
  21. package/dist/client/namespaces/consolidated/index.js.map +1 -1
  22. package/dist/config/defaults.d.ts.map +1 -1
  23. package/dist/config/defaults.js +9 -4
  24. package/dist/config/defaults.js.map +1 -1
  25. package/dist/core/types/index.d.ts +4 -0
  26. package/dist/core/types/index.d.ts.map +1 -1
  27. package/dist/core/types/index.js.map +1 -1
  28. package/dist/infrastructure/walrus/WalrusStorageService.d.ts +6 -0
  29. package/dist/infrastructure/walrus/WalrusStorageService.d.ts.map +1 -1
  30. package/dist/infrastructure/walrus/WalrusStorageService.js +23 -4
  31. package/dist/infrastructure/walrus/WalrusStorageService.js.map +1 -1
  32. package/dist/services/EmbeddingService.d.ts +9 -0
  33. package/dist/services/EmbeddingService.d.ts.map +1 -1
  34. package/dist/services/EmbeddingService.js +31 -10
  35. package/dist/services/EmbeddingService.js.map +1 -1
  36. package/dist/services/MemoryIndexService.d.ts +2 -0
  37. package/dist/services/MemoryIndexService.d.ts.map +1 -1
  38. package/dist/services/MemoryIndexService.js +11 -4
  39. package/dist/services/MemoryIndexService.js.map +1 -1
  40. package/dist/services/VectorService.js +1 -1
  41. package/dist/services/VectorService.js.map +1 -1
  42. package/dist/vector/BrowserHnswIndexService.js +1 -1
  43. package/dist/vector/BrowserHnswIndexService.js.map +1 -1
  44. package/dist/vector/HnswWasmService.js +1 -1
  45. package/dist/vector/HnswWasmService.js.map +1 -1
  46. package/dist/vector/NodeHnswService.js +1 -1
  47. package/dist/vector/NodeHnswService.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/client/SimplePDWClient.ts +86 -6
  50. package/src/client/namespaces/IndexNamespace.ts +7 -4
  51. package/src/client/namespaces/MemoryNamespace.ts +156 -9
  52. package/src/client/namespaces/consolidated/AdvancedNamespace.ts +264 -0
  53. package/src/client/namespaces/consolidated/index.ts +2 -0
  54. package/src/config/defaults.ts +9 -4
  55. package/src/core/types/index.ts +4 -0
  56. package/src/infrastructure/walrus/WalrusStorageService.ts +29 -4
  57. package/src/services/EmbeddingService.ts +35 -10
  58. package/src/services/MemoryIndexService.ts +11 -5
  59. package/src/services/VectorService.ts +1 -1
  60. package/src/vector/BrowserHnswIndexService.ts +1 -1
  61. package/src/vector/HnswWasmService.ts +1 -1
  62. package/src/vector/NodeHnswService.ts +1 -1
@@ -39,6 +39,8 @@ export interface CreateMemoryOptions {
39
39
  importance?: number; // 1-10
40
40
  topic?: string;
41
41
  metadata?: Record<string, any>;
42
+ /** Pre-generated embedding (if provided, skips embedding generation) */
43
+ embedding?: number[];
42
44
  onProgress?: (stage: string, percent: number) => void;
43
45
  }
44
46
 
@@ -75,6 +77,20 @@ export interface ListMemoryOptions {
75
77
  order?: 'asc' | 'desc';
76
78
  }
77
79
 
80
+ /**
81
+ * Options for searching memories
82
+ */
83
+ export interface SearchMemoryOptions {
84
+ /** Maximum number of results (default: 10) */
85
+ limit?: number;
86
+ /** Minimum similarity threshold 0-1 (default: 0.5) */
87
+ threshold?: number;
88
+ /** Filter by category */
89
+ category?: string;
90
+ /** Include full content in results (default: true) */
91
+ includeContent?: boolean;
92
+ }
93
+
78
94
  /**
79
95
  * Memory context with related memories
80
96
  */
@@ -132,7 +148,7 @@ export class MemoryNamespace {
132
148
  * ```
133
149
  */
134
150
  async create(content: string, options: CreateMemoryOptions = {}): Promise<Memory> {
135
- const { importance = 5, topic, metadata, onProgress } = options;
151
+ const { importance = 5, topic, metadata, onProgress, embedding: preGeneratedEmbedding } = options;
136
152
  let category: string = options.category || 'general';
137
153
 
138
154
  try {
@@ -160,17 +176,17 @@ export class MemoryNamespace {
160
176
  }
161
177
  }
162
178
 
163
- // 2. Generate embedding
164
- let embedding: number[] | undefined;
165
- if (this.services.embedding) {
179
+ // 2. Use pre-generated embedding or generate new one
180
+ let embedding: number[] | undefined = preGeneratedEmbedding;
181
+ if (embedding && embedding.length > 0) {
182
+ console.log(`✅ Using pre-generated embedding: ${embedding.length}D`);
183
+ } else if (this.services.embedding) {
166
184
  onProgress?.('generating embedding', 20);
167
- const embResult = await this.services.embedding.embedText({
168
- text: content
169
- });
185
+ const embResult = await this.services.embedding.embedText({ text: content });
170
186
  embedding = embResult.vector;
171
- console.log(`✅ Embedding generated: ${embedding?.length || 0}D vector`);
187
+ console.log(`✅ Embedding generated: ${embedding?.length || 0}D`);
172
188
  } else {
173
- console.warn('⚠️ EmbeddingService not available - no embedding will be stored!');
189
+ console.warn('⚠️ No embedding available');
174
190
  }
175
191
 
176
192
  // 3. Encrypt (if enabled) - Use capability-based encryption (v2.2)
@@ -703,6 +719,137 @@ export class MemoryNamespace {
703
719
  }
704
720
  }
705
721
 
722
+ /**
723
+ * Search memories using natural language query
724
+ *
725
+ * Unified search that:
726
+ * 1. Converts query text to embedding
727
+ * 2. Performs vector similarity search
728
+ * 3. Returns ranked results with content
729
+ *
730
+ * @param query - Natural language search query
731
+ * @param options - Search options (limit, threshold, category)
732
+ * @returns Array of matching memories sorted by relevance
733
+ *
734
+ * @example
735
+ * ```typescript
736
+ * // Simple search
737
+ * const results = await pdw.memory.search('meeting notes');
738
+ *
739
+ * // With options
740
+ * const results = await pdw.memory.search('TypeScript tips', {
741
+ * limit: 5,
742
+ * category: 'note',
743
+ * threshold: 0.7
744
+ * });
745
+ * ```
746
+ */
747
+ async search(query: string, options: SearchMemoryOptions = {}): Promise<Memory[]> {
748
+ const {
749
+ limit = 10,
750
+ threshold = 0.5,
751
+ category,
752
+ includeContent = true
753
+ } = options;
754
+
755
+ try {
756
+ // Validate query
757
+ if (!query || query.trim().length === 0) {
758
+ return [];
759
+ }
760
+
761
+ // Step 1: Generate embedding for query
762
+ if (!this.services.embedding) {
763
+ throw new Error('Embedding service not available - cannot perform semantic search');
764
+ }
765
+
766
+ const embResult = await this.services.embedding.embedText({ text: query });
767
+ const queryEmbedding = embResult.vector;
768
+
769
+ // Step 2: Perform vector search
770
+ let searchResults: any[] = [];
771
+
772
+ // Try local index first (faster)
773
+ if (this.services.vector) {
774
+ try {
775
+ const spaceId = this.services.config.userAddress;
776
+ const searchResult = await this.services.vector.searchVectors(spaceId, queryEmbedding, { k: limit * 2 });
777
+ searchResults = searchResult.results.map((r: any) => ({
778
+ id: r.metadata?.memoryObjectId || r.memoryId || String(r.vectorId),
779
+ vectorId: r.vectorId,
780
+ content: r.metadata?.content || '',
781
+ category: r.metadata?.category,
782
+ importance: r.metadata?.importance,
783
+ blobId: r.metadata?.blobId || r.memoryId,
784
+ metadata: r.metadata,
785
+ similarity: r.similarity || 0,
786
+ encrypted: r.metadata?.isEncrypted || false,
787
+ createdAt: r.metadata?.timestamp || Date.now()
788
+ }));
789
+ } catch (indexError) {
790
+ console.warn('Local index search failed:', indexError);
791
+ }
792
+ }
793
+
794
+ // Fallback to memory index service if available
795
+ if (searchResults.length === 0 && this.services.memoryIndex) {
796
+ try {
797
+ const results = await this.services.memoryIndex.searchMemories({
798
+ userAddress: this.services.config.userAddress,
799
+ vector: queryEmbedding,
800
+ k: limit * 2
801
+ });
802
+ searchResults = results.map((r: any) => ({
803
+ id: r.memoryObjectId || r.id,
804
+ content: r.content || '',
805
+ category: r.category,
806
+ importance: r.importance || r.metadata?.importance,
807
+ blobId: r.blobId || r.id,
808
+ metadata: r.metadata,
809
+ similarity: r.score || r.similarity || 0,
810
+ encrypted: r.isEncrypted || false,
811
+ createdAt: r.timestamp || Date.now()
812
+ }));
813
+ } catch (memIndexError) {
814
+ console.warn('Memory index search failed:', memIndexError);
815
+ }
816
+ }
817
+
818
+ // Step 3: Filter by threshold and category
819
+ let filtered = searchResults.filter(r => r.similarity >= threshold);
820
+
821
+ if (category) {
822
+ filtered = filtered.filter(r => r.category === category);
823
+ }
824
+
825
+ // Step 4: Limit results
826
+ const limited = filtered.slice(0, limit);
827
+
828
+ // Step 5: Optionally fetch full content for encrypted memories
829
+ if (includeContent) {
830
+ const withContent = await Promise.all(
831
+ limited.map(async (r) => {
832
+ // If content is missing or encrypted, try to fetch from storage
833
+ if (!r.content && r.blobId) {
834
+ try {
835
+ const memory = await this.get(r.blobId);
836
+ return { ...r, content: memory.content };
837
+ } catch {
838
+ return r;
839
+ }
840
+ }
841
+ return r;
842
+ })
843
+ );
844
+ return withContent as Memory[];
845
+ }
846
+
847
+ return limited as Memory[];
848
+ } catch (error) {
849
+ throw new Error(`Failed to search memories: ${error instanceof Error ? error.message : String(error)}`);
850
+ }
851
+ }
852
+
706
853
  /**
707
854
  * Create multiple memories in batch using Walrus Quilt
708
855
  *
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Advanced Namespace - Power User Features
3
+ *
4
+ * Groups specialized features for advanced use cases:
5
+ * - Knowledge graph operations
6
+ * - Memory analytics and insights
7
+ * - Manual encryption/decryption
8
+ * - Access control and permissions
9
+ * - Transaction building
10
+ * - Processing pipelines
11
+ *
12
+ * Most users won't need these - use pdw.memory for common operations.
13
+ *
14
+ * @module client/namespaces/consolidated
15
+ */
16
+
17
+ import type { ServiceContainer } from '../../SimplePDWClient';
18
+ import type { GraphNamespace } from '../GraphNamespace';
19
+ import type { AnalyticsNamespace } from '../AnalyticsNamespace';
20
+ import type { EncryptionNamespace } from '../EncryptionNamespace';
21
+ import type { PermissionsNamespace } from '../PermissionsNamespace';
22
+ import type { TxNamespace } from '../TxNamespace';
23
+ import type { PipelineNamespace } from '../PipelineNamespace';
24
+ import type { CapabilityNamespace } from '../CapabilityNamespace';
25
+ import type { ContextNamespace } from '../ContextNamespace';
26
+ import type { BatchNamespace } from '../BatchNamespace';
27
+ import type { CacheNamespace } from '../CacheNamespace';
28
+ import type { SearchNamespace } from '../SearchNamespace';
29
+
30
+ /**
31
+ * Advanced Namespace
32
+ *
33
+ * Access specialized features through sub-namespaces:
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Knowledge graph
38
+ * const entities = await pdw.advanced.graph.getEntities();
39
+ *
40
+ * // Analytics
41
+ * const insights = await pdw.advanced.analytics.generate();
42
+ *
43
+ * // Manual encryption
44
+ * const encrypted = await pdw.advanced.encryption.encrypt(data);
45
+ *
46
+ * // Permissions
47
+ * await pdw.advanced.permissions.grant(userId, memoryId);
48
+ *
49
+ * // Blockchain transactions
50
+ * const tx = pdw.advanced.blockchain.buildCreateMemory(params);
51
+ * ```
52
+ */
53
+ export class AdvancedNamespace {
54
+ private _graph?: GraphNamespace;
55
+ private _analytics?: AnalyticsNamespace;
56
+ private _encryption?: EncryptionNamespace;
57
+ private _permissions?: PermissionsNamespace;
58
+ private _blockchain?: TxNamespace;
59
+ private _pipeline?: PipelineNamespace;
60
+ private _capability?: CapabilityNamespace;
61
+ private _context?: ContextNamespace;
62
+ private _batch?: BatchNamespace;
63
+ private _cache?: CacheNamespace;
64
+ private _search?: SearchNamespace;
65
+
66
+ constructor(
67
+ private services: ServiceContainer,
68
+ namespaces: {
69
+ graph?: GraphNamespace;
70
+ analytics?: AnalyticsNamespace;
71
+ encryption?: EncryptionNamespace;
72
+ permissions?: PermissionsNamespace;
73
+ blockchain?: TxNamespace;
74
+ pipeline?: PipelineNamespace;
75
+ capability?: CapabilityNamespace;
76
+ context?: ContextNamespace;
77
+ batch?: BatchNamespace;
78
+ cache?: CacheNamespace;
79
+ search?: SearchNamespace;
80
+ }
81
+ ) {
82
+ this._graph = namespaces.graph;
83
+ this._analytics = namespaces.analytics;
84
+ this._encryption = namespaces.encryption;
85
+ this._permissions = namespaces.permissions;
86
+ this._blockchain = namespaces.blockchain;
87
+ this._pipeline = namespaces.pipeline;
88
+ this._capability = namespaces.capability;
89
+ this._context = namespaces.context;
90
+ this._batch = namespaces.batch;
91
+ this._cache = namespaces.cache;
92
+ this._search = namespaces.search;
93
+ }
94
+
95
+ /**
96
+ * Knowledge Graph Operations
97
+ *
98
+ * Extract entities and relationships from memories,
99
+ * traverse the graph, and find connections.
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const entities = await pdw.advanced.graph.getEntities();
104
+ * const related = await pdw.advanced.graph.traverse(entityId);
105
+ * ```
106
+ */
107
+ get graph(): GraphNamespace | undefined {
108
+ return this._graph;
109
+ }
110
+
111
+ /**
112
+ * Memory Analytics
113
+ *
114
+ * Generate insights, trends, and clustering from memories.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const insights = await pdw.advanced.analytics.generate();
119
+ * const trends = await pdw.advanced.analytics.trends();
120
+ * ```
121
+ */
122
+ get analytics(): AnalyticsNamespace | undefined {
123
+ return this._analytics;
124
+ }
125
+
126
+ /**
127
+ * Manual Encryption/Decryption
128
+ *
129
+ * Direct access to SEAL encryption for custom use cases.
130
+ * Note: pdw.memory.create() handles encryption automatically.
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const encrypted = await pdw.advanced.encryption.encrypt(data, keyId);
135
+ * const decrypted = await pdw.advanced.encryption.decrypt(options);
136
+ * ```
137
+ */
138
+ get encryption(): EncryptionNamespace | undefined {
139
+ return this._encryption;
140
+ }
141
+
142
+ /**
143
+ * Access Control & Permissions
144
+ *
145
+ * Grant, revoke, and manage access to memories.
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * await pdw.advanced.permissions.grant(userId, memoryId);
150
+ * await pdw.advanced.permissions.revoke(userId, memoryId);
151
+ * const perms = await pdw.advanced.permissions.list(memoryId);
152
+ * ```
153
+ */
154
+ get permissions(): PermissionsNamespace | undefined {
155
+ return this._permissions;
156
+ }
157
+
158
+ /**
159
+ * Blockchain Transaction Building
160
+ *
161
+ * Build custom Sui transactions for advanced operations.
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const tx = pdw.advanced.blockchain.buildCreateMemory(params);
166
+ * const result = await pdw.advanced.blockchain.execute(tx);
167
+ * ```
168
+ */
169
+ get blockchain(): TxNamespace | undefined {
170
+ return this._blockchain;
171
+ }
172
+
173
+ /**
174
+ * Processing Pipelines
175
+ *
176
+ * Create and manage memory processing pipelines.
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * const pipeline = await pdw.advanced.pipeline.create(config);
181
+ * await pdw.advanced.pipeline.execute(pipeline, data);
182
+ * ```
183
+ */
184
+ get pipeline(): PipelineNamespace | undefined {
185
+ return this._pipeline;
186
+ }
187
+
188
+ /**
189
+ * Capability Management
190
+ *
191
+ * Low-level MemoryCap object management for SEAL encryption.
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * const cap = await pdw.advanced.capability.getOrCreate(appId);
196
+ * const keyId = pdw.advanced.capability.computeKeyId(cap);
197
+ * ```
198
+ */
199
+ get capability(): CapabilityNamespace | undefined {
200
+ return this._capability;
201
+ }
202
+
203
+ /**
204
+ * App Context Management
205
+ *
206
+ * Manage application contexts for multi-app memory access.
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const ctx = await pdw.advanced.context.getOrCreate(appId);
211
+ * await pdw.advanced.context.transfer(ctxId, newOwner);
212
+ * ```
213
+ */
214
+ get context(): ContextNamespace | undefined {
215
+ return this._context;
216
+ }
217
+
218
+ /**
219
+ * Batch Processing Utilities
220
+ *
221
+ * Advanced batch operations beyond createBatch.
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * const stats = await pdw.advanced.batch.stats();
226
+ * const progress = await pdw.advanced.batch.progress(batchId);
227
+ * ```
228
+ */
229
+ get batch(): BatchNamespace | undefined {
230
+ return this._batch;
231
+ }
232
+
233
+ /**
234
+ * Cache Management
235
+ *
236
+ * Direct access to in-memory LRU cache.
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * const cached = pdw.advanced.cache.get(key);
241
+ * pdw.advanced.cache.set(key, value);
242
+ * pdw.advanced.cache.clear();
243
+ * ```
244
+ */
245
+ get cache(): CacheNamespace | undefined {
246
+ return this._cache;
247
+ }
248
+
249
+ /**
250
+ * Advanced Search
251
+ *
252
+ * Low-level vector and semantic search operations.
253
+ * Note: pdw.memory.search() is simpler for most use cases.
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * const results = await pdw.advanced.search.vector(embedding, k);
258
+ * const results = await pdw.advanced.search.hybrid(query, filters);
259
+ * ```
260
+ */
261
+ get search(): SearchNamespace | undefined {
262
+ return this._search;
263
+ }
264
+ }
@@ -37,3 +37,5 @@ export type {
37
37
  MemoryPackage,
38
38
  UploadOptions
39
39
  } from './StorageNamespace';
40
+
41
+ export { AdvancedNamespace } from './AdvancedNamespace';
@@ -22,11 +22,15 @@ export function createDefaultConfig(): PDWConfig {
22
22
  cacheEnabled: true,
23
23
  encryptionEnabled: true,
24
24
  },
25
- // Walrus Storage Configuration
25
+ // Walrus Storage Configuration (Testnet defaults)
26
+ // Publisher: Direct blob uploads (server-side, full control)
26
27
  walrusPublisherUrl: 'https://publisher.walrus-testnet.walrus.space',
28
+ // Upload Relay: Optimized for browser/mobile (fewer connections, faster)
29
+ walrusUploadRelayUrl: 'https://upload-relay.testnet.walrus.space',
30
+ // Aggregator: Blob retrieval
27
31
  walrusAggregatorUrl: 'https://aggregator.walrus-testnet.walrus.space',
28
- walrusMaxFileSize: 1024 * 1024 * 1024, // 1GB
29
- walrusTimeout: 30000, // 30 seconds
32
+ walrusMaxFileSize: 10 * 1024 * 1024, // 10MB (public service limit)
33
+ walrusTimeout: 60000, // 60 seconds for upload relay
30
34
  };
31
35
  }
32
36
 
@@ -44,8 +48,9 @@ export function createTestnetConfig(overrides: Partial<PDWConfig> = {}): PDWConf
44
48
  threshold: 2,
45
49
  },
46
50
  },
47
- // Testnet Walrus endpoints (same as default for now)
51
+ // Testnet Walrus endpoints
48
52
  walrusPublisherUrl: 'https://publisher.walrus-testnet.walrus.space',
53
+ walrusUploadRelayUrl: 'https://upload-relay.testnet.walrus.space',
49
54
  walrusAggregatorUrl: 'https://aggregator.walrus-testnet.walrus.space',
50
55
  ...overrides,
51
56
  };
@@ -37,7 +37,11 @@ export interface PDWConfig {
37
37
  /** Storage configuration */
38
38
  storageConfig?: StorageConfig;
39
39
  /** Walrus storage configuration */
40
+ /** Publisher URL for direct blob uploads (server-side) */
40
41
  walrusPublisherUrl?: string;
42
+ /** Upload Relay URL for browser/mobile uploads (fewer connections, faster) */
43
+ walrusUploadRelayUrl?: string;
44
+ /** Aggregator URL for blob retrieval */
41
45
  walrusAggregatorUrl?: string;
42
46
  walrusMaxFileSize?: number;
43
47
  walrusTimeout?: number;
@@ -17,8 +17,14 @@ export interface WalrusConfig {
17
17
  network?: 'testnet' | 'mainnet';
18
18
  adminAddress?: string;
19
19
  storageEpochs?: number;
20
+ /** Publisher URL for direct blob uploads (server-side) */
21
+ publisherHost?: string;
22
+ /** Upload Relay URL for browser/mobile uploads (fewer connections, faster) */
20
23
  uploadRelayHost?: string;
24
+ /** Aggregator URL for blob retrieval */
21
25
  aggregatorHost?: string;
26
+ /** Use upload relay instead of publisher (default: true for browser, false for server) */
27
+ useUploadRelay?: boolean;
22
28
  retryAttempts?: number;
23
29
  timeoutMs?: number;
24
30
  sealService?: SealService;
@@ -123,13 +129,21 @@ export class WalrusStorageService {
123
129
 
124
130
  constructor(config: Partial<WalrusConfig> = {}) {
125
131
  const network = config.network || 'testnet';
132
+ // Detect browser environment for default useUploadRelay
133
+ const isBrowser = typeof window !== 'undefined';
126
134
 
127
135
  this.config = {
128
136
  network,
129
137
  adminAddress: config.adminAddress || '',
130
138
  storageEpochs: config.storageEpochs || 12,
139
+ // Publisher: direct uploads (server-side)
140
+ publisherHost: config.publisherHost || 'https://publisher.walrus-testnet.walrus.space',
141
+ // Upload Relay: optimized for browser/mobile (fewer connections)
131
142
  uploadRelayHost: config.uploadRelayHost || 'https://upload-relay.testnet.walrus.space',
143
+ // Aggregator: blob retrieval
132
144
  aggregatorHost: config.aggregatorHost || 'https://aggregator.walrus-testnet.walrus.space',
145
+ // Use upload relay by default in browser, publisher on server
146
+ useUploadRelay: config.useUploadRelay ?? isBrowser,
133
147
  retryAttempts: config.retryAttempts || 3,
134
148
  timeoutMs: config.timeoutMs || 60000,
135
149
  sealService: config.sealService,
@@ -519,9 +533,20 @@ export class WalrusStorageService {
519
533
  }
520
534
  }
521
535
 
522
- // Fallback to REST API if no signer (read-only mode or relay upload)
536
+ // Fallback to REST API based on useUploadRelay config
523
537
  try {
524
- const response = await fetch(`${this.config.uploadRelayHost}/v1/store?epochs=${this.config.storageEpochs}`, {
538
+ let url: string;
539
+ if (this.config.useUploadRelay) {
540
+ // Upload Relay: optimized for browser/mobile (fewer network connections)
541
+ // POST to /v1/blobs endpoint
542
+ url = `${this.config.uploadRelayHost}/v1/blobs?epochs=${this.config.storageEpochs}`;
543
+ } else {
544
+ // Publisher: direct upload (server-side)
545
+ // PUT to /v1/blobs endpoint
546
+ url = `${this.config.publisherHost}/v1/blobs?epochs=${this.config.storageEpochs}`;
547
+ }
548
+
549
+ const response = await fetch(url, {
525
550
  method: 'PUT',
526
551
  headers: {
527
552
  'Content-Type': 'application/octet-stream'
@@ -568,9 +593,9 @@ export class WalrusStorageService {
568
593
  }
569
594
  }
570
595
 
571
- // Fallback to REST API
596
+ // Fallback to REST API - Aggregator endpoint: GET /v1/blobs/<blob-id>
572
597
  try {
573
- const response = await fetch(`${this.config.aggregatorHost}/v1/${blobId}`);
598
+ const response = await fetch(`${this.config.aggregatorHost}/v1/blobs/${blobId}`);
574
599
 
575
600
  if (!response.ok) {
576
601
  throw new Error(`Retrieval failed: ${response.status} ${response.statusText}`);
@@ -175,7 +175,7 @@ export class EmbeddingService {
175
175
  // New behavior: Direct EmbeddingModel from ai-sdk
176
176
  this.embeddingModel = config.model;
177
177
  this.modelName = 'custom';
178
- this.dimensions = config.dimensions || 3072;
178
+ this.dimensions = config.dimensions || 768; // Default 768 for speed (was 3072)
179
179
  this.provider = 'custom';
180
180
  console.log('✅ EmbeddingService initialized with custom ai-sdk model');
181
181
  return;
@@ -246,19 +246,26 @@ export class EmbeddingService {
246
246
 
247
247
  /**
248
248
  * Get default dimensions for provider
249
+ *
250
+ * Default is now 768 for faster performance:
251
+ * - 4x smaller vectors = faster indexing & search
252
+ * - ~4x less storage space
253
+ * - Minimal quality loss for most use cases
254
+ *
255
+ * Users can override via config.dimensions
249
256
  */
250
257
  private getDefaultDimensions(provider: string): number {
251
258
  switch (provider) {
252
259
  case 'google':
253
- return 3072;
260
+ return 768; // text-embedding-004 supports output_dimensionality
254
261
  case 'openai':
255
- return 1536; // text-embedding-3-small default
262
+ return 768; // text-embedding-3-small supports dimensions param
256
263
  case 'openrouter':
257
- return 3072; // google/gemini-embedding-001 returns 3072 dimensions
264
+ return 768; // Most models support dimension truncation
258
265
  case 'cohere':
259
- return 1024;
266
+ return 768; // embed-english-v3.0 supports dimensions
260
267
  default:
261
- return 3072;
268
+ return 768; // Default to 768 for speed
262
269
  }
263
270
  }
264
271
 
@@ -362,16 +369,25 @@ export class EmbeddingService {
362
369
  /**
363
370
  * Generate embedding using OpenRouter SDK
364
371
  * Uses official @openrouter/sdk for embeddings
372
+ * Passes dimensions parameter to truncate output vectors
365
373
  */
366
374
  private async embedTextOpenRouter(text: string, startTime: number): Promise<EmbeddingResult> {
367
375
  if (!this.openRouterClient) {
368
376
  throw new Error('OpenRouter client not initialized');
369
377
  }
370
378
 
371
- const result = await this.openRouterClient.embeddings.generate({
379
+ // Build request with optional dimensions parameter
380
+ const request: any = {
372
381
  model: this.modelName,
373
382
  input: text
374
- });
383
+ };
384
+
385
+ // Add dimensions if configured (enables output truncation)
386
+ if (this.dimensions && this.dimensions < 3072) {
387
+ request.dimensions = this.dimensions;
388
+ }
389
+
390
+ const result = await this.openRouterClient.embeddings.generate(request);
375
391
 
376
392
  // Handle union type - result can be string or object
377
393
  if (typeof result === 'string') {
@@ -453,16 +469,25 @@ export class EmbeddingService {
453
469
 
454
470
  /**
455
471
  * Generate batch embeddings using OpenRouter SDK
472
+ * Passes dimensions parameter to truncate output vectors
456
473
  */
457
474
  private async embedBatchOpenRouter(texts: string[], startTime: number): Promise<BatchEmbeddingResult> {
458
475
  if (!this.openRouterClient) {
459
476
  throw new Error('OpenRouter client not initialized');
460
477
  }
461
478
 
462
- const result = await this.openRouterClient.embeddings.generate({
479
+ // Build request with optional dimensions parameter
480
+ const request: any = {
463
481
  model: this.modelName,
464
482
  input: texts
465
- });
483
+ };
484
+
485
+ // Add dimensions if configured (enables output truncation)
486
+ if (this.dimensions && this.dimensions < 3072) {
487
+ request.dimensions = this.dimensions;
488
+ }
489
+
490
+ const result = await this.openRouterClient.embeddings.generate(request);
466
491
 
467
492
  // Handle union type - result can be string or object
468
493
  if (typeof result === 'string') {