@realtimex/realtimex-alchemy 1.0.49 → 1.0.51

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/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.51] - 2026-01-27
9
+
10
+ ### Added
11
+ - **Vector Storage**: Migrated embedding storage from local SDK to Supabase `pgvector`. This enables cloud-native similarity search and persistent memory across devices.
12
+ - **Database**: Added `alchemy_vectors` table and HNSW indexes for high-performance semantic retrieval.
13
+
14
+ ### Improved
15
+ - **Performance**: Implemented server-side similarity search via `match_vectors` RPC function, reducing latency for RAG and deduplication.
16
+
17
+ ## [1.0.50] - 2026-01-26
18
+
19
+ ### Improved
20
+ - **Setup Wizard**: Added a smart check to automatically skip the migration step if the database is already initialized (`init_state` detected), streamlining the experience for existing projects.
21
+
8
22
  ## [1.0.49] - 2026-01-26
9
23
 
10
24
  ### Added
@@ -410,14 +410,14 @@ export class AlchemistService {
410
410
  .eq('id', signal.id);
411
411
  return;
412
412
  }
413
- // Store embedding in RealTimeX vector storage
413
+ // Store embedding in Supabase pgvector storage
414
414
  await embeddingService.storeSignalEmbedding(signal.id, embedding, {
415
415
  title: signal.title,
416
416
  summary: signal.summary,
417
417
  url: signal.url,
418
418
  category: signal.category,
419
419
  userId
420
- });
420
+ }, supabase);
421
421
  // Update signal metadata
422
422
  await supabase
423
423
  .from('signals')
@@ -64,7 +64,7 @@ export class ChatService {
64
64
  let sources = [];
65
65
  // 3. Retrieve Context (if embedding checks out)
66
66
  if (queryEmbedding) {
67
- const similar = await embeddingService.findSimilarSignals(queryEmbedding, userId, 0.55, // Lowered threshold for better recall
67
+ const similar = await embeddingService.findSimilarSignals(queryEmbedding, userId, supabase, 0.55, // Lowered threshold for better recall
68
68
  10 // Increased Top K
69
69
  );
70
70
  console.log(`[ChatService] RAG Retrieval: Found ${similar.length} signals for query: "${content}"`);
@@ -18,7 +18,7 @@ export class DeduplicationService {
18
18
  try {
19
19
  // 1. Semantic Check (if embedding exists)
20
20
  if (embedding && embedding.length > 0) {
21
- const similar = await embeddingService.findSimilarSignals(embedding, userId, this.SIMILARITY_THRESHOLD, 5 // Check top 5 matches
21
+ const similar = await embeddingService.findSimilarSignals(embedding, userId, supabase, this.SIMILARITY_THRESHOLD, 5 // Check top 5 matches
22
22
  );
23
23
  if (similar.length > 0) {
24
24
  const bestMatch = similar[0];
@@ -1,11 +1,11 @@
1
1
  import { SDKService } from './SDKService.js';
2
2
  /**
3
- * Embedding Service using RealTimeX SDK
4
- * Provides simplified interface for embedding generation and vector operations
3
+ * Embedding Service
4
+ * Uses RealTimeX SDK for embedding generation
5
+ * Uses Supabase pgvector for vector storage and similarity search
5
6
  * Gracefully degrades if SDK is not available
6
7
  */
7
8
  export class EmbeddingService {
8
- WORKSPACE_ID = 'alchemy-signals';
9
9
  SIMILARITY_THRESHOLD = 0.85;
10
10
  /**
11
11
  * Generate embedding for a single text
@@ -60,22 +60,29 @@ export class EmbeddingService {
60
60
  }
61
61
  }
62
62
  /**
63
- * Store signal embedding in RealTimeX vector storage
63
+ * Store signal embedding in Supabase pgvector storage
64
64
  * @param signalId - Unique signal ID
65
65
  * @param embedding - Embedding vector
66
66
  * @param metadata - Signal metadata
67
+ * @param supabase - Supabase client
67
68
  */
68
- async storeSignalEmbedding(signalId, embedding, metadata) {
69
+ async storeSignalEmbedding(signalId, embedding, metadata, supabase) {
69
70
  try {
70
- const sdk = SDKService.getSDK();
71
- if (!sdk) {
72
- throw new Error('SDK not available');
71
+ // Format embedding as pgvector string
72
+ const embeddingStr = `[${embedding.join(',')}]`;
73
+ const { error } = await supabase
74
+ .from('alchemy_vectors')
75
+ .upsert({
76
+ signal_id: signalId,
77
+ user_id: metadata.userId,
78
+ embedding: embeddingStr,
79
+ model: 'text-embedding-3-small'
80
+ }, {
81
+ onConflict: 'signal_id,model'
82
+ });
83
+ if (error) {
84
+ throw error;
73
85
  }
74
- await sdk.llm.vectors.upsert([{
75
- id: signalId,
76
- vector: embedding,
77
- metadata
78
- }], { workspaceId: this.WORKSPACE_ID });
79
86
  console.log('[EmbeddingService] Stored embedding for signal:', signalId);
80
87
  }
81
88
  catch (error) {
@@ -84,32 +91,39 @@ export class EmbeddingService {
84
91
  }
85
92
  }
86
93
  /**
87
- * Find similar signals using semantic search
94
+ * Find similar signals using semantic search via Supabase pgvector
88
95
  * @param queryEmbedding - Query embedding vector
89
96
  * @param userId - User ID for filtering
97
+ * @param supabase - Supabase client
90
98
  * @param threshold - Similarity threshold (0-1)
91
99
  * @param limit - Max results
92
100
  * @returns Array of similar signals
93
101
  */
94
- async findSimilarSignals(queryEmbedding, userId, threshold = this.SIMILARITY_THRESHOLD, limit = 10) {
102
+ async findSimilarSignals(queryEmbedding, userId, supabase, threshold = this.SIMILARITY_THRESHOLD, limit = 10) {
95
103
  try {
96
- const sdk = SDKService.getSDK();
97
- if (!sdk) {
104
+ // Format embedding as pgvector string
105
+ const embeddingStr = `[${queryEmbedding.join(',')}]`;
106
+ const { data, error } = await supabase.rpc('match_vectors', {
107
+ query_embedding: embeddingStr,
108
+ match_threshold: threshold,
109
+ match_count: limit,
110
+ target_user_id: userId
111
+ });
112
+ if (error) {
113
+ console.error('[EmbeddingService] Similarity search RPC error:', error.message);
98
114
  return [];
99
115
  }
100
- const response = await sdk.llm.vectors.query(queryEmbedding, {
101
- topK: limit,
102
- workspaceId: this.WORKSPACE_ID
103
- });
104
- // Filter by similarity threshold and user
105
- const results = response.results || [];
106
- return results
107
- .filter((r) => r.metadata?.userId === userId)
108
- .filter((r) => r.score >= threshold)
109
- .map((r) => ({
110
- id: r.id,
111
- score: r.score,
112
- metadata: r.metadata || {}
116
+ // Map results to expected format
117
+ return (data || []).map((r) => ({
118
+ id: r.signal_id,
119
+ score: r.similarity,
120
+ metadata: {
121
+ title: r.title,
122
+ summary: r.summary,
123
+ url: r.url,
124
+ category: r.category,
125
+ userId
126
+ }
113
127
  }));
114
128
  }
115
129
  catch (error) {
@@ -120,12 +134,18 @@ export class EmbeddingService {
120
134
  /**
121
135
  * Delete all embeddings for a user
122
136
  * @param userId - User ID
137
+ * @param supabase - Supabase client
123
138
  */
124
- async deleteUserEmbeddings(userId) {
139
+ async deleteUserEmbeddings(userId, supabase) {
125
140
  try {
126
- // Note: Current SDK only supports deleteAll
127
- // In future, we may need user-specific workspaces
128
- console.warn('[EmbeddingService] User-specific deletion not yet supported');
141
+ const { error } = await supabase
142
+ .from('alchemy_vectors')
143
+ .delete()
144
+ .eq('user_id', userId);
145
+ if (error) {
146
+ throw error;
147
+ }
148
+ console.log('[EmbeddingService] Deleted all embeddings for user:', userId);
129
149
  }
130
150
  catch (error) {
131
151
  console.error('[EmbeddingService] Deletion failed:', error.message);
@@ -147,7 +147,6 @@ export class SDKService {
147
147
  throw new Error('RealTimeX SDK not linked. Cannot get app data dir.');
148
148
  }
149
149
  try {
150
- // @ts-ignore - SDK method for getting app data directory
151
150
  return await sdk.getAppDataDir();
152
151
  }
153
152
  catch (error) {