@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 +14 -0
- package/dist/api/services/AlchemistService.js +2 -2
- package/dist/api/services/ChatService.js +1 -1
- package/dist/api/services/DeduplicationService.js +1 -1
- package/dist/api/services/EmbeddingService.js +54 -34
- package/dist/api/services/SDKService.js +0 -1
- package/dist/assets/{index-CI2Ob-G8.js → index-Cb2XPDey.js} +27 -27
- package/dist/index.html +1 -1
- package/package.json +3 -3
- package/supabase/migrations/20260127000002_add_vector_storage.sql +67 -0
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
|
|
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
|
|
4
|
-
*
|
|
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
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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);
|