holosphere 1.1.19 → 2.0.0-alpha0
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/.env.example +36 -0
- package/.eslintrc.json +16 -0
- package/.prettierrc.json +7 -0
- package/README.md +476 -531
- package/bin/holosphere-activitypub.js +158 -0
- package/cleanup-test-data.js +204 -0
- package/examples/demo.html +1333 -0
- package/examples/example-bot.js +197 -0
- package/package.json +47 -87
- package/scripts/check-bundle-size.js +54 -0
- package/scripts/check-quest-ids.js +77 -0
- package/scripts/import-holons.js +578 -0
- package/scripts/publish-to-relay.js +101 -0
- package/scripts/read-example.js +186 -0
- package/scripts/relay-diagnostic.js +59 -0
- package/scripts/relay-example.js +179 -0
- package/scripts/resync-to-relay.js +245 -0
- package/scripts/revert-import.js +196 -0
- package/scripts/test-hybrid-mode.js +108 -0
- package/scripts/test-local-storage.js +63 -0
- package/scripts/test-nostr-direct.js +55 -0
- package/scripts/test-read-data.js +45 -0
- package/scripts/test-write-read.js +63 -0
- package/scripts/verify-import.js +95 -0
- package/scripts/verify-relay-data.js +139 -0
- package/src/ai/aggregation.js +319 -0
- package/src/ai/breakdown.js +511 -0
- package/src/ai/classifier.js +217 -0
- package/src/ai/council.js +228 -0
- package/src/ai/embeddings.js +279 -0
- package/src/ai/federation-ai.js +324 -0
- package/src/ai/h3-ai.js +955 -0
- package/src/ai/index.js +112 -0
- package/src/ai/json-ops.js +225 -0
- package/src/ai/llm-service.js +205 -0
- package/src/ai/nl-query.js +223 -0
- package/src/ai/relationships.js +353 -0
- package/src/ai/schema-extractor.js +218 -0
- package/src/ai/spatial.js +293 -0
- package/src/ai/tts.js +194 -0
- package/src/content/social-protocols.js +168 -0
- package/src/core/holosphere.js +273 -0
- package/src/crypto/secp256k1.js +259 -0
- package/src/federation/discovery.js +334 -0
- package/src/federation/hologram.js +1042 -0
- package/src/federation/registry.js +386 -0
- package/src/hierarchical/upcast.js +110 -0
- package/src/index.js +2669 -0
- package/src/schema/validator.js +91 -0
- package/src/spatial/h3-operations.js +110 -0
- package/src/storage/backend-factory.js +125 -0
- package/src/storage/backend-interface.js +142 -0
- package/src/storage/backends/activitypub/server.js +653 -0
- package/src/storage/backends/activitypub-backend.js +272 -0
- package/src/storage/backends/gundb-backend.js +233 -0
- package/src/storage/backends/nostr-backend.js +136 -0
- package/src/storage/filesystem-storage-browser.js +41 -0
- package/src/storage/filesystem-storage.js +138 -0
- package/src/storage/global-tables.js +81 -0
- package/src/storage/gun-async.js +281 -0
- package/src/storage/gun-wrapper.js +221 -0
- package/src/storage/indexeddb-storage.js +122 -0
- package/src/storage/key-storage-simple.js +76 -0
- package/src/storage/key-storage.js +136 -0
- package/src/storage/memory-storage.js +59 -0
- package/src/storage/migration.js +338 -0
- package/src/storage/nostr-async.js +811 -0
- package/src/storage/nostr-client.js +939 -0
- package/src/storage/nostr-wrapper.js +211 -0
- package/src/storage/outbox-queue.js +208 -0
- package/src/storage/persistent-storage.js +109 -0
- package/src/storage/sync-service.js +164 -0
- package/src/subscriptions/manager.js +142 -0
- package/test-ai-real-api.js +202 -0
- package/tests/unit/ai/aggregation.test.js +295 -0
- package/tests/unit/ai/breakdown.test.js +446 -0
- package/tests/unit/ai/classifier.test.js +294 -0
- package/tests/unit/ai/council.test.js +262 -0
- package/tests/unit/ai/embeddings.test.js +384 -0
- package/tests/unit/ai/federation-ai.test.js +344 -0
- package/tests/unit/ai/h3-ai.test.js +458 -0
- package/tests/unit/ai/index.test.js +304 -0
- package/tests/unit/ai/json-ops.test.js +307 -0
- package/tests/unit/ai/llm-service.test.js +390 -0
- package/tests/unit/ai/nl-query.test.js +383 -0
- package/tests/unit/ai/relationships.test.js +311 -0
- package/tests/unit/ai/schema-extractor.test.js +384 -0
- package/tests/unit/ai/spatial.test.js +279 -0
- package/tests/unit/ai/tts.test.js +279 -0
- package/tests/unit/content.test.js +332 -0
- package/tests/unit/contract/core.test.js +88 -0
- package/tests/unit/contract/crypto.test.js +198 -0
- package/tests/unit/contract/data.test.js +223 -0
- package/tests/unit/contract/federation.test.js +181 -0
- package/tests/unit/contract/hierarchical.test.js +113 -0
- package/tests/unit/contract/schema.test.js +114 -0
- package/tests/unit/contract/social.test.js +217 -0
- package/tests/unit/contract/spatial.test.js +110 -0
- package/tests/unit/contract/subscriptions.test.js +128 -0
- package/tests/unit/contract/utils.test.js +159 -0
- package/tests/unit/core.test.js +152 -0
- package/tests/unit/crypto.test.js +328 -0
- package/tests/unit/federation.test.js +234 -0
- package/tests/unit/gun-async.test.js +252 -0
- package/tests/unit/hierarchical.test.js +399 -0
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
- package/tests/unit/integration/scenario-02-federation.test.js +76 -0
- package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
- package/tests/unit/integration/scenario-04-validation.test.js +129 -0
- package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
- package/tests/unit/integration/scenario-06-social.test.js +135 -0
- package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
- package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
- package/tests/unit/performance/benchmark.test.js +85 -0
- package/tests/unit/schema.test.js +213 -0
- package/tests/unit/spatial.test.js +158 -0
- package/tests/unit/storage.test.js +195 -0
- package/tests/unit/subscriptions.test.js +328 -0
- package/tests/unit/test-data-permanence-debug.js +197 -0
- package/tests/unit/test-data-permanence.js +340 -0
- package/tests/unit/test-key-persistence-fixed.js +148 -0
- package/tests/unit/test-key-persistence.js +172 -0
- package/tests/unit/test-relay-permanence.js +376 -0
- package/tests/unit/test-second-node.js +95 -0
- package/tests/unit/test-simple-write.js +89 -0
- package/vite.config.js +49 -0
- package/vitest.config.js +20 -0
- package/FEDERATION.md +0 -213
- package/compute.js +0 -298
- package/content.js +0 -1022
- package/federation.js +0 -1234
- package/global.js +0 -736
- package/hexlib.js +0 -335
- package/hologram.js +0 -183
- package/holosphere-bundle.esm.js +0 -34549
- package/holosphere-bundle.js +0 -34580
- package/holosphere-bundle.min.js +0 -49
- package/holosphere.d.ts +0 -604
- package/holosphere.js +0 -739
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embeddings - Vector embedding generation and semantic search
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Embeddings class for vector operations
|
|
7
|
+
*/
|
|
8
|
+
export class Embeddings {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object} openai - OpenAI client instance
|
|
11
|
+
* @param {Object} holosphere - HoloSphere instance for storage
|
|
12
|
+
*/
|
|
13
|
+
constructor(openai, holosphere = null) {
|
|
14
|
+
this.openai = openai;
|
|
15
|
+
this.holosphere = holosphere;
|
|
16
|
+
this.model = 'text-embedding-3-small';
|
|
17
|
+
this.dimensions = 1536;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Set HoloSphere instance for storage
|
|
22
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
23
|
+
*/
|
|
24
|
+
setHoloSphere(holosphere) {
|
|
25
|
+
this.holosphere = holosphere;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate embedding for text
|
|
30
|
+
* @param {string} text - Text to embed
|
|
31
|
+
* @returns {Promise<number[]>} Embedding vector
|
|
32
|
+
*/
|
|
33
|
+
async embed(text) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await this.openai.embeddings.create({
|
|
36
|
+
model: this.model,
|
|
37
|
+
input: text,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return response.data[0].embedding;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(`Embedding generation failed: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate embeddings for multiple texts
|
|
48
|
+
* @param {string[]} texts - Array of texts
|
|
49
|
+
* @returns {Promise<number[][]>} Array of embeddings
|
|
50
|
+
*/
|
|
51
|
+
async embedBatch(texts) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await this.openai.embeddings.create({
|
|
54
|
+
model: this.model,
|
|
55
|
+
input: texts,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return response.data.map(d => d.embedding);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
throw new Error(`Batch embedding failed: ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Calculate cosine similarity between two vectors
|
|
66
|
+
* @param {number[]} vec1 - First vector
|
|
67
|
+
* @param {number[]} vec2 - Second vector
|
|
68
|
+
* @returns {number} Similarity score (0-1)
|
|
69
|
+
*/
|
|
70
|
+
cosineSimilarity(vec1, vec2) {
|
|
71
|
+
if (vec1.length !== vec2.length) {
|
|
72
|
+
throw new Error('Vectors must have same length');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let dotProduct = 0;
|
|
76
|
+
let norm1 = 0;
|
|
77
|
+
let norm2 = 0;
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < vec1.length; i++) {
|
|
80
|
+
dotProduct += vec1[i] * vec2[i];
|
|
81
|
+
norm1 += vec1[i] * vec1[i];
|
|
82
|
+
norm2 += vec2[i] * vec2[i];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Store item with embedding in HoloSphere
|
|
90
|
+
* @param {string} holon - Holon identifier
|
|
91
|
+
* @param {string} lens - Lens name
|
|
92
|
+
* @param {Object} item - Item to store
|
|
93
|
+
* @param {string} textField - Field to embed (default: 'content' or 'text')
|
|
94
|
+
* @returns {Promise<Object>} Stored item with embedding
|
|
95
|
+
*/
|
|
96
|
+
async storeWithEmbedding(holon, lens, item, textField = null) {
|
|
97
|
+
if (!this.holosphere) {
|
|
98
|
+
throw new Error('HoloSphere instance required for storage');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Determine text to embed
|
|
102
|
+
const text = textField
|
|
103
|
+
? item[textField]
|
|
104
|
+
: item.content || item.text || item.description || JSON.stringify(item);
|
|
105
|
+
|
|
106
|
+
if (!text) {
|
|
107
|
+
throw new Error('No text found to embed');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Generate embedding
|
|
111
|
+
const embedding = await this.embed(text);
|
|
112
|
+
|
|
113
|
+
// Store with embedding
|
|
114
|
+
const itemWithEmbedding = {
|
|
115
|
+
...item,
|
|
116
|
+
_embedding: embedding,
|
|
117
|
+
_embeddedField: textField || 'auto',
|
|
118
|
+
_embeddedAt: Date.now(),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
await this.holosphere.put(holon, lens, itemWithEmbedding);
|
|
122
|
+
|
|
123
|
+
return itemWithEmbedding;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Semantic search within a holon/lens
|
|
128
|
+
* @param {string} query - Search query
|
|
129
|
+
* @param {string} holon - Holon identifier
|
|
130
|
+
* @param {string} lens - Lens name
|
|
131
|
+
* @param {Object} options - Search options
|
|
132
|
+
* @returns {Promise<Array<{item: Object, similarity: number}>>} Ranked results
|
|
133
|
+
*/
|
|
134
|
+
async semanticSearch(query, holon, lens, options = {}) {
|
|
135
|
+
if (!this.holosphere) {
|
|
136
|
+
throw new Error('HoloSphere instance required for search');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { limit = 10, threshold = 0.5 } = options;
|
|
140
|
+
|
|
141
|
+
// Get query embedding
|
|
142
|
+
const queryEmbedding = await this.embed(query);
|
|
143
|
+
|
|
144
|
+
// Get all items from holon/lens
|
|
145
|
+
const items = await this.holosphere.getAll(holon, lens);
|
|
146
|
+
|
|
147
|
+
// Calculate similarities
|
|
148
|
+
const results = [];
|
|
149
|
+
|
|
150
|
+
for (const item of items) {
|
|
151
|
+
if (item._embedding) {
|
|
152
|
+
const similarity = this.cosineSimilarity(queryEmbedding, item._embedding);
|
|
153
|
+
|
|
154
|
+
if (similarity >= threshold) {
|
|
155
|
+
results.push({
|
|
156
|
+
item: { ...item, _embedding: undefined }, // Don't return embedding
|
|
157
|
+
similarity,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Sort by similarity and limit
|
|
164
|
+
results.sort((a, b) => b.similarity - a.similarity);
|
|
165
|
+
|
|
166
|
+
return results.slice(0, limit);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Find similar items to a given item
|
|
171
|
+
* @param {Object} item - Item to find similar items for
|
|
172
|
+
* @param {string} holon - Holon identifier
|
|
173
|
+
* @param {string} lens - Lens name
|
|
174
|
+
* @param {Object} options - Search options
|
|
175
|
+
* @returns {Promise<Array>} Similar items
|
|
176
|
+
*/
|
|
177
|
+
async findSimilar(item, holon, lens, options = {}) {
|
|
178
|
+
const text = item.content || item.text || item.description || JSON.stringify(item);
|
|
179
|
+
return this.semanticSearch(text, holon, lens, options);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Cluster items by semantic similarity
|
|
184
|
+
* @param {Object[]} items - Items to cluster
|
|
185
|
+
* @param {number} numClusters - Approximate number of clusters
|
|
186
|
+
* @returns {Promise<Object[][]>} Clustered items
|
|
187
|
+
*/
|
|
188
|
+
async cluster(items, numClusters = 5) {
|
|
189
|
+
// Get embeddings for items that don't have them
|
|
190
|
+
const itemsWithEmbeddings = await Promise.all(
|
|
191
|
+
items.map(async (item) => {
|
|
192
|
+
if (item._embedding) {
|
|
193
|
+
return item;
|
|
194
|
+
}
|
|
195
|
+
const text = item.content || item.text || item.description || JSON.stringify(item);
|
|
196
|
+
const embedding = await this.embed(text);
|
|
197
|
+
return { ...item, _embedding: embedding };
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Simple k-means clustering
|
|
202
|
+
const clusters = this._kMeans(itemsWithEmbeddings, numClusters);
|
|
203
|
+
|
|
204
|
+
// Return clusters without embeddings
|
|
205
|
+
return clusters.map(cluster =>
|
|
206
|
+
cluster.map(item => ({ ...item, _embedding: undefined }))
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Simple k-means clustering
|
|
212
|
+
* @private
|
|
213
|
+
*/
|
|
214
|
+
_kMeans(items, k, maxIterations = 100) {
|
|
215
|
+
if (items.length <= k) {
|
|
216
|
+
return items.map(item => [item]);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Initialize centroids randomly
|
|
220
|
+
const shuffled = [...items].sort(() => Math.random() - 0.5);
|
|
221
|
+
let centroids = shuffled.slice(0, k).map(item => [...item._embedding]);
|
|
222
|
+
|
|
223
|
+
let assignments = new Array(items.length);
|
|
224
|
+
|
|
225
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
226
|
+
// Assign items to nearest centroid
|
|
227
|
+
const newAssignments = items.map((item) => {
|
|
228
|
+
let minDist = Infinity;
|
|
229
|
+
let nearest = 0;
|
|
230
|
+
|
|
231
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
232
|
+
const dist = 1 - this.cosineSimilarity(item._embedding, centroids[c]);
|
|
233
|
+
if (dist < minDist) {
|
|
234
|
+
minDist = dist;
|
|
235
|
+
nearest = c;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return nearest;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Check for convergence
|
|
243
|
+
if (JSON.stringify(newAssignments) === JSON.stringify(assignments)) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
assignments = newAssignments;
|
|
248
|
+
|
|
249
|
+
// Update centroids
|
|
250
|
+
for (let c = 0; c < k; c++) {
|
|
251
|
+
const clusterItems = items.filter((_, i) => assignments[i] === c);
|
|
252
|
+
|
|
253
|
+
if (clusterItems.length > 0) {
|
|
254
|
+
centroids[c] = new Array(this.dimensions).fill(0);
|
|
255
|
+
|
|
256
|
+
for (const item of clusterItems) {
|
|
257
|
+
for (let d = 0; d < this.dimensions; d++) {
|
|
258
|
+
centroids[c][d] += item._embedding[d];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (let d = 0; d < this.dimensions; d++) {
|
|
263
|
+
centroids[c][d] /= clusterItems.length;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Group items by cluster
|
|
270
|
+
const clusters = Array.from({ length: k }, () => []);
|
|
271
|
+
items.forEach((item, i) => {
|
|
272
|
+
clusters[assignments[i]].push(item);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return clusters.filter(c => c.length > 0);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export default Embeddings;
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation Advisor - AI-powered federation suggestions and analysis
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Federation Advisor class
|
|
7
|
+
*/
|
|
8
|
+
export class FederationAdvisor {
|
|
9
|
+
/**
|
|
10
|
+
* @param {LLMService} llmService - LLM service instance
|
|
11
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
12
|
+
* @param {Object} embeddings - Embeddings instance (optional)
|
|
13
|
+
*/
|
|
14
|
+
constructor(llmService, holosphere = null, embeddings = null) {
|
|
15
|
+
this.llm = llmService;
|
|
16
|
+
this.holosphere = holosphere;
|
|
17
|
+
this.embeddings = embeddings;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Set HoloSphere instance
|
|
22
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
23
|
+
*/
|
|
24
|
+
setHoloSphere(holosphere) {
|
|
25
|
+
this.holosphere = holosphere;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Set Embeddings instance
|
|
30
|
+
* @param {Object} embeddings - Embeddings instance
|
|
31
|
+
*/
|
|
32
|
+
setEmbeddings(embeddings) {
|
|
33
|
+
this.embeddings = embeddings;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Suggest federations based on content similarity
|
|
38
|
+
* @param {string} holon - Holon to find federation partners for
|
|
39
|
+
* @param {Object} options - Options
|
|
40
|
+
* @returns {Promise<Array>} Suggested federations
|
|
41
|
+
*/
|
|
42
|
+
async suggestFederations(holon, options = {}) {
|
|
43
|
+
if (!this.holosphere) {
|
|
44
|
+
throw new Error('HoloSphere instance required');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { candidateHolons = [], lens = 'default', maxSuggestions = 5 } = options;
|
|
48
|
+
|
|
49
|
+
// Get data from source holon
|
|
50
|
+
const sourceData = await this.holosphere.getAll(holon, lens);
|
|
51
|
+
|
|
52
|
+
if (sourceData.length === 0) {
|
|
53
|
+
return { holon, suggestions: [], message: 'No data in source holon' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If we have candidate holons, compare with each
|
|
57
|
+
const comparisons = [];
|
|
58
|
+
|
|
59
|
+
for (const candidate of candidateHolons.slice(0, 10)) {
|
|
60
|
+
if (candidate === holon) continue;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const candidateData = await this.holosphere.getAll(candidate, lens);
|
|
64
|
+
if (candidateData.length > 0) {
|
|
65
|
+
comparisons.push({
|
|
66
|
+
holon: candidate,
|
|
67
|
+
dataCount: candidateData.length,
|
|
68
|
+
dataSample: candidateData.slice(0, 10),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Skip unavailable holons
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (comparisons.length === 0) {
|
|
77
|
+
return { holon, suggestions: [], message: 'No candidate holons with data' };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const systemPrompt = `You are a federation advisor. Analyze which regions would benefit from federating (sharing data) with the source region.
|
|
81
|
+
|
|
82
|
+
Source region: ${holon}
|
|
83
|
+
Source data count: ${sourceData.length}
|
|
84
|
+
|
|
85
|
+
Consider:
|
|
86
|
+
1. Content similarity - similar topics benefit from shared visibility
|
|
87
|
+
2. Complementary content - different but related content enables collaboration
|
|
88
|
+
3. Geographic proximity relevance
|
|
89
|
+
4. Potential for knowledge sharing
|
|
90
|
+
|
|
91
|
+
Return JSON array:
|
|
92
|
+
[
|
|
93
|
+
{
|
|
94
|
+
"holon": "id",
|
|
95
|
+
"score": 0.0-1.0,
|
|
96
|
+
"reasoning": "why federate",
|
|
97
|
+
"benefits": ["benefit1"],
|
|
98
|
+
"type": "similarity|complementary|geographic"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
Order by score descending, max ${maxSuggestions} suggestions.`;
|
|
103
|
+
|
|
104
|
+
const userMessage = `Source data sample:
|
|
105
|
+
${JSON.stringify(sourceData.slice(0, 15), null, 2)}
|
|
106
|
+
|
|
107
|
+
Candidate regions:
|
|
108
|
+
${JSON.stringify(comparisons, null, 2)}`;
|
|
109
|
+
|
|
110
|
+
const suggestions = await this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.3 });
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
holon,
|
|
114
|
+
lens,
|
|
115
|
+
suggestions: suggestions.slice(0, maxSuggestions),
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Analyze federation health
|
|
122
|
+
* @param {string} holon - Holon to analyze
|
|
123
|
+
* @returns {Promise<Object>} Federation health analysis
|
|
124
|
+
*/
|
|
125
|
+
async analyzeFederationHealth(holon) {
|
|
126
|
+
if (!this.holosphere) {
|
|
127
|
+
throw new Error('HoloSphere instance required');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Get federation info
|
|
131
|
+
const federation = await this.holosphere.getFederation(holon);
|
|
132
|
+
|
|
133
|
+
if (!federation || !federation.federation || federation.federation.length === 0) {
|
|
134
|
+
return {
|
|
135
|
+
holon,
|
|
136
|
+
health: 'none',
|
|
137
|
+
message: 'No federations configured',
|
|
138
|
+
score: 0,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Analyze each federation
|
|
143
|
+
const federationAnalysis = [];
|
|
144
|
+
|
|
145
|
+
for (const fedHolon of federation.federation.slice(0, 5)) {
|
|
146
|
+
try {
|
|
147
|
+
const fedData = await this.holosphere.getAll(fedHolon, 'default');
|
|
148
|
+
federationAnalysis.push({
|
|
149
|
+
holon: fedHolon,
|
|
150
|
+
active: fedData.length > 0,
|
|
151
|
+
dataCount: fedData.length,
|
|
152
|
+
});
|
|
153
|
+
} catch {
|
|
154
|
+
federationAnalysis.push({
|
|
155
|
+
holon: fedHolon,
|
|
156
|
+
active: false,
|
|
157
|
+
error: true,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const systemPrompt = `You are a federation health analyst. Assess the health of this holon's federation relationships.
|
|
163
|
+
|
|
164
|
+
Source holon: ${holon}
|
|
165
|
+
Federation partners: ${federation.federation.length}
|
|
166
|
+
Active federations: ${federationAnalysis.filter(f => f.active).length}
|
|
167
|
+
|
|
168
|
+
Federation details:
|
|
169
|
+
${JSON.stringify(federationAnalysis, null, 2)}
|
|
170
|
+
|
|
171
|
+
Analyze:
|
|
172
|
+
1. Overall federation health
|
|
173
|
+
2. Active vs inactive relationships
|
|
174
|
+
3. Balance of the federation network
|
|
175
|
+
4. Recommendations
|
|
176
|
+
|
|
177
|
+
Return JSON:
|
|
178
|
+
{
|
|
179
|
+
"health_score": 0.0-1.0,
|
|
180
|
+
"status": "healthy|degraded|unhealthy",
|
|
181
|
+
"active_count": n,
|
|
182
|
+
"inactive_count": n,
|
|
183
|
+
"issues": ["issue1"],
|
|
184
|
+
"recommendations": ["rec1"],
|
|
185
|
+
"summary": "text"
|
|
186
|
+
}`;
|
|
187
|
+
|
|
188
|
+
const analysis = await this.llm.getJSON(systemPrompt, '', { temperature: 0.3 });
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
holon,
|
|
192
|
+
federation: federation.federation,
|
|
193
|
+
analysis,
|
|
194
|
+
timestamp: Date.now(),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Suggest federation optimizations
|
|
200
|
+
* @param {string} holon - Holon to optimize
|
|
201
|
+
* @returns {Promise<Object>} Optimization suggestions
|
|
202
|
+
*/
|
|
203
|
+
async optimizeFederation(holon) {
|
|
204
|
+
const health = await this.analyzeFederationHealth(holon);
|
|
205
|
+
|
|
206
|
+
if (health.health === 'none') {
|
|
207
|
+
return { holon, suggestions: ['Consider establishing initial federations'] };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const systemPrompt = `Based on federation health analysis, suggest optimizations.
|
|
211
|
+
|
|
212
|
+
Health analysis:
|
|
213
|
+
${JSON.stringify(health.analysis, null, 2)}
|
|
214
|
+
|
|
215
|
+
Suggest:
|
|
216
|
+
1. Federations to strengthen
|
|
217
|
+
2. Federations to reconsider
|
|
218
|
+
3. New potential federations
|
|
219
|
+
4. Data flow improvements
|
|
220
|
+
|
|
221
|
+
Return JSON:
|
|
222
|
+
{
|
|
223
|
+
"strengthen": ["holon_id"],
|
|
224
|
+
"reconsider": ["holon_id"],
|
|
225
|
+
"new_potential": ["description"],
|
|
226
|
+
"improvements": ["improvement1"],
|
|
227
|
+
"priority": "high|medium|low"
|
|
228
|
+
}`;
|
|
229
|
+
|
|
230
|
+
return this.llm.getJSON(systemPrompt, '', { temperature: 0.4 });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Analyze data flow in federation network
|
|
235
|
+
* @param {string} holon - Starting holon
|
|
236
|
+
* @returns {Promise<Object>} Data flow analysis
|
|
237
|
+
*/
|
|
238
|
+
async analyzeDataFlow(holon) {
|
|
239
|
+
if (!this.holosphere) {
|
|
240
|
+
throw new Error('HoloSphere instance required');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const federation = await this.holosphere.getFederation(holon);
|
|
244
|
+
|
|
245
|
+
if (!federation) {
|
|
246
|
+
return { holon, message: 'No federation configured' };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const systemPrompt = `Analyze the data flow patterns in a federation network.
|
|
250
|
+
|
|
251
|
+
Source holon: ${holon}
|
|
252
|
+
Federated with: ${federation.federation?.join(', ') || 'none'}
|
|
253
|
+
Notifies: ${federation.notify?.join(', ') || 'none'}
|
|
254
|
+
|
|
255
|
+
Consider:
|
|
256
|
+
1. Bidirectional vs unidirectional relationships
|
|
257
|
+
2. Hub patterns (many connections)
|
|
258
|
+
3. Data propagation paths
|
|
259
|
+
4. Potential bottlenecks
|
|
260
|
+
|
|
261
|
+
Return JSON:
|
|
262
|
+
{
|
|
263
|
+
"flow_type": "bidirectional|unidirectional|mixed",
|
|
264
|
+
"topology": "hub|mesh|chain|star",
|
|
265
|
+
"propagation_depth": n,
|
|
266
|
+
"bottlenecks": ["desc"],
|
|
267
|
+
"recommendations": ["rec1"]
|
|
268
|
+
}`;
|
|
269
|
+
|
|
270
|
+
return this.llm.getJSON(systemPrompt, JSON.stringify(federation, null, 2), { temperature: 0.3 });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Find federation bridges - holons that connect disparate networks
|
|
275
|
+
* @param {string[]} holons - List of holons to analyze
|
|
276
|
+
* @returns {Promise<Object>} Bridge analysis
|
|
277
|
+
*/
|
|
278
|
+
async findBridges(holons) {
|
|
279
|
+
if (!this.holosphere) {
|
|
280
|
+
throw new Error('HoloSphere instance required');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const network = [];
|
|
284
|
+
|
|
285
|
+
for (const holon of holons.slice(0, 20)) {
|
|
286
|
+
try {
|
|
287
|
+
const fed = await this.holosphere.getFederation(holon);
|
|
288
|
+
if (fed) {
|
|
289
|
+
network.push({
|
|
290
|
+
holon,
|
|
291
|
+
federations: fed.federation || [],
|
|
292
|
+
notifies: fed.notify || [],
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
// Skip
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const systemPrompt = `Analyze a federation network to identify bridge holons (connectors between clusters).
|
|
301
|
+
|
|
302
|
+
Network:
|
|
303
|
+
${JSON.stringify(network, null, 2)}
|
|
304
|
+
|
|
305
|
+
Identify:
|
|
306
|
+
1. Bridge holons that connect clusters
|
|
307
|
+
2. Isolated holons
|
|
308
|
+
3. Central hubs
|
|
309
|
+
4. Network clusters
|
|
310
|
+
|
|
311
|
+
Return JSON:
|
|
312
|
+
{
|
|
313
|
+
"bridges": [{"holon": "id", "connects": ["cluster1", "cluster2"]}],
|
|
314
|
+
"isolated": ["holon_id"],
|
|
315
|
+
"hubs": [{"holon": "id", "connections": n}],
|
|
316
|
+
"clusters": [["holon1", "holon2"]],
|
|
317
|
+
"network_health": 0.0-1.0
|
|
318
|
+
}`;
|
|
319
|
+
|
|
320
|
+
return this.llm.getJSON(systemPrompt, '', { temperature: 0.3 });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export default FederationAdvisor;
|