holosphere 1.1.20 → 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 +483 -367
- 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 -980
- 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 -33256
- package/holosphere-bundle.js +0 -33287
- package/holosphere-bundle.min.js +0 -39
- package/holosphere.d.ts +0 -601
- package/holosphere.js +0 -719
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Natural Language Query - Query HoloSphere data using natural language
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* NL Query class for natural language to structured query conversion
|
|
7
|
+
*/
|
|
8
|
+
export class NLQuery {
|
|
9
|
+
/**
|
|
10
|
+
* @param {LLMService} llmService - LLM service instance
|
|
11
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
12
|
+
*/
|
|
13
|
+
constructor(llmService, holosphere = null) {
|
|
14
|
+
this.llm = llmService;
|
|
15
|
+
this.holosphere = holosphere;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Set HoloSphere instance
|
|
20
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
21
|
+
*/
|
|
22
|
+
setHoloSphere(holosphere) {
|
|
23
|
+
this.holosphere = holosphere;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parse natural language query into structured format
|
|
28
|
+
* @param {string} query - Natural language query
|
|
29
|
+
* @param {Object} context - Context (available holons, lenses, etc.)
|
|
30
|
+
* @returns {Promise<Object>} Structured query
|
|
31
|
+
*/
|
|
32
|
+
async parse(query, context = {}) {
|
|
33
|
+
const systemPrompt = `You are a query parser. Convert natural language queries into structured JSON filters for a geospatial database.
|
|
34
|
+
|
|
35
|
+
Available holons: ${context.holons?.join(', ') || 'any'}
|
|
36
|
+
Available lenses: ${context.lenses?.join(', ') || 'any'}
|
|
37
|
+
|
|
38
|
+
Return a JSON object with:
|
|
39
|
+
{
|
|
40
|
+
"holon": "holon_id or null",
|
|
41
|
+
"lens": "lens_name or null",
|
|
42
|
+
"filters": {
|
|
43
|
+
"field_name": { "op": "eq|ne|gt|gte|lt|lte|contains|in", "value": value }
|
|
44
|
+
},
|
|
45
|
+
"sort": { "field": "field_name", "order": "asc|desc" } or null,
|
|
46
|
+
"limit": number or null,
|
|
47
|
+
"spatial": { "near": "location_name", "radius": "distance" } or null,
|
|
48
|
+
"temporal": { "after": "date", "before": "date" } or null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
- "show projects near Rome" -> { "lens": "projects", "spatial": { "near": "Rome" } }
|
|
53
|
+
- "find quests with more than 10 participants" -> { "lens": "quests", "filters": { "participants": { "op": "gt", "value": 10 } } }`;
|
|
54
|
+
|
|
55
|
+
const result = await this.llm.getJSON(systemPrompt, query, { temperature: 0.2 });
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Execute natural language query
|
|
61
|
+
* @param {string} query - Natural language query
|
|
62
|
+
* @param {Object} options - Query options
|
|
63
|
+
* @returns {Promise<Array>} Query results
|
|
64
|
+
*/
|
|
65
|
+
async execute(query, options = {}) {
|
|
66
|
+
if (!this.holosphere) {
|
|
67
|
+
throw new Error('HoloSphere instance required for query execution');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Parse the query
|
|
71
|
+
const parsed = await this.parse(query, options.context);
|
|
72
|
+
|
|
73
|
+
// Get data from HoloSphere
|
|
74
|
+
let results = [];
|
|
75
|
+
|
|
76
|
+
if (parsed.holon && parsed.lens) {
|
|
77
|
+
results = await this.holosphere.getAll(parsed.holon, parsed.lens);
|
|
78
|
+
} else if (parsed.lens) {
|
|
79
|
+
// Search across all holons for this lens (would need holon list)
|
|
80
|
+
if (options.holons) {
|
|
81
|
+
for (const holon of options.holons) {
|
|
82
|
+
const data = await this.holosphere.getAll(holon, parsed.lens);
|
|
83
|
+
results.push(...data.map(item => ({ ...item, _holon: holon })));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Apply filters
|
|
89
|
+
if (parsed.filters) {
|
|
90
|
+
results = this._applyFilters(results, parsed.filters);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Apply sorting
|
|
94
|
+
if (parsed.sort) {
|
|
95
|
+
results = this._applySort(results, parsed.sort);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Apply limit
|
|
99
|
+
if (parsed.limit) {
|
|
100
|
+
results = results.slice(0, parsed.limit);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
query,
|
|
105
|
+
parsed,
|
|
106
|
+
results,
|
|
107
|
+
count: results.length,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Apply filters to results
|
|
113
|
+
* @private
|
|
114
|
+
*/
|
|
115
|
+
_applyFilters(results, filters) {
|
|
116
|
+
return results.filter(item => {
|
|
117
|
+
for (const [field, condition] of Object.entries(filters)) {
|
|
118
|
+
const value = this._getNestedValue(item, field);
|
|
119
|
+
|
|
120
|
+
if (!this._matchCondition(value, condition)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get nested value from object
|
|
130
|
+
* @private
|
|
131
|
+
*/
|
|
132
|
+
_getNestedValue(obj, path) {
|
|
133
|
+
return path.split('.').reduce((acc, key) => acc?.[key], obj);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if value matches condition
|
|
138
|
+
* @private
|
|
139
|
+
*/
|
|
140
|
+
_matchCondition(value, condition) {
|
|
141
|
+
const { op, value: targetValue } = condition;
|
|
142
|
+
|
|
143
|
+
switch (op) {
|
|
144
|
+
case 'eq':
|
|
145
|
+
return value === targetValue;
|
|
146
|
+
case 'ne':
|
|
147
|
+
return value !== targetValue;
|
|
148
|
+
case 'gt':
|
|
149
|
+
return value > targetValue;
|
|
150
|
+
case 'gte':
|
|
151
|
+
return value >= targetValue;
|
|
152
|
+
case 'lt':
|
|
153
|
+
return value < targetValue;
|
|
154
|
+
case 'lte':
|
|
155
|
+
return value <= targetValue;
|
|
156
|
+
case 'contains':
|
|
157
|
+
return String(value).toLowerCase().includes(String(targetValue).toLowerCase());
|
|
158
|
+
case 'in':
|
|
159
|
+
return Array.isArray(targetValue) && targetValue.includes(value);
|
|
160
|
+
default:
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Apply sorting to results
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
_applySort(results, sort) {
|
|
170
|
+
const { field, order } = sort;
|
|
171
|
+
const multiplier = order === 'desc' ? -1 : 1;
|
|
172
|
+
|
|
173
|
+
return [...results].sort((a, b) => {
|
|
174
|
+
const aVal = this._getNestedValue(a, field);
|
|
175
|
+
const bVal = this._getNestedValue(b, field);
|
|
176
|
+
|
|
177
|
+
if (aVal < bVal) return -1 * multiplier;
|
|
178
|
+
if (aVal > bVal) return 1 * multiplier;
|
|
179
|
+
return 0;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Suggest queries based on context
|
|
185
|
+
* @param {Object} context - Current context
|
|
186
|
+
* @returns {Promise<string[]>} Suggested queries
|
|
187
|
+
*/
|
|
188
|
+
async suggest(context = {}) {
|
|
189
|
+
const systemPrompt = `Suggest 5 useful natural language queries for exploring geospatial data.
|
|
190
|
+
|
|
191
|
+
Available holons: ${context.holons?.join(', ') || 'geographic areas'}
|
|
192
|
+
Available lenses: ${context.lenses?.join(', ') || 'projects, quests, events, resources'}
|
|
193
|
+
|
|
194
|
+
Return ONLY a JSON array of query strings.`;
|
|
195
|
+
|
|
196
|
+
return this.llm.getJSON(systemPrompt, 'Generate query suggestions', { temperature: 0.7 });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Explain query results in natural language
|
|
201
|
+
* @param {string} query - Original query
|
|
202
|
+
* @param {Array} results - Query results
|
|
203
|
+
* @returns {Promise<string>} Natural language explanation
|
|
204
|
+
*/
|
|
205
|
+
async explain(query, results) {
|
|
206
|
+
const systemPrompt = `You are a helpful data analyst. Explain the results of a query in natural language.
|
|
207
|
+
|
|
208
|
+
Original query: ${query}
|
|
209
|
+
Number of results: ${results.length}
|
|
210
|
+
|
|
211
|
+
Provide a brief, helpful summary of what was found.`;
|
|
212
|
+
|
|
213
|
+
const sampleResults = results.slice(0, 5);
|
|
214
|
+
|
|
215
|
+
return this.llm.sendMessage(
|
|
216
|
+
systemPrompt,
|
|
217
|
+
`Results sample: ${JSON.stringify(sampleResults, null, 2)}`,
|
|
218
|
+
{ temperature: 0.5 }
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export default NLQuery;
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relationship Discovery - Find hidden connections across data
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Relationship Discovery class
|
|
7
|
+
*/
|
|
8
|
+
export class RelationshipDiscovery {
|
|
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
|
+
* Discover relationships in holon data
|
|
38
|
+
* @param {string} holon - Holon identifier
|
|
39
|
+
* @param {string} lens - Lens to analyze (optional)
|
|
40
|
+
* @returns {Promise<Object>} Discovered relationships
|
|
41
|
+
*/
|
|
42
|
+
async discoverRelationships(holon, lens = null) {
|
|
43
|
+
if (!this.holosphere) {
|
|
44
|
+
throw new Error('HoloSphere instance required');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get data from holon
|
|
48
|
+
let data = [];
|
|
49
|
+
const lenses = lens ? [lens] : ['projects', 'quests', 'events', 'resources', 'default'];
|
|
50
|
+
|
|
51
|
+
for (const l of lenses) {
|
|
52
|
+
try {
|
|
53
|
+
const lensData = await this.holosphere.getAll(holon, l);
|
|
54
|
+
data.push(...lensData.map(item => ({ ...item, _lens: l })));
|
|
55
|
+
} catch {
|
|
56
|
+
// Skip unavailable lenses
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (data.length < 2) {
|
|
61
|
+
return { holon, relationships: [], message: 'Not enough data for relationship discovery' };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const systemPrompt = `You are a relationship discovery expert. Find hidden connections between items in this dataset.
|
|
65
|
+
|
|
66
|
+
Region: ${holon}
|
|
67
|
+
Items: ${data.length}
|
|
68
|
+
|
|
69
|
+
Look for:
|
|
70
|
+
1. Thematic connections (shared topics, goals)
|
|
71
|
+
2. Entity connections (shared people, organizations)
|
|
72
|
+
3. Temporal connections (same timeframes)
|
|
73
|
+
4. Causal connections (one enables/requires another)
|
|
74
|
+
5. Geographic connections (same sub-regions)
|
|
75
|
+
|
|
76
|
+
Return JSON:
|
|
77
|
+
{
|
|
78
|
+
"relationships": [
|
|
79
|
+
{
|
|
80
|
+
"item1": {"id": "id1", "title": "title"},
|
|
81
|
+
"item2": {"id": "id2", "title": "title"},
|
|
82
|
+
"type": "thematic|entity|temporal|causal|geographic",
|
|
83
|
+
"strength": 0.0-1.0,
|
|
84
|
+
"description": "relationship description"
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"clusters": [
|
|
88
|
+
{
|
|
89
|
+
"theme": "cluster theme",
|
|
90
|
+
"items": ["id1", "id2"]
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
"key_entities": ["entity1", "entity2"],
|
|
94
|
+
"summary": "overview of relationship network"
|
|
95
|
+
}`;
|
|
96
|
+
|
|
97
|
+
const relationships = await this.llm.getJSON(
|
|
98
|
+
systemPrompt,
|
|
99
|
+
JSON.stringify(data.slice(0, 40), null, 2),
|
|
100
|
+
{ temperature: 0.3 }
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
holon,
|
|
105
|
+
lens,
|
|
106
|
+
itemCount: data.length,
|
|
107
|
+
relationships,
|
|
108
|
+
timestamp: Date.now(),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Find items similar to a given item
|
|
114
|
+
* @param {Object} item - Item to find similar items for
|
|
115
|
+
* @param {string} holon - Holon to search in (optional)
|
|
116
|
+
* @param {string} lens - Lens to search in (optional)
|
|
117
|
+
* @param {Object} options - Search options
|
|
118
|
+
* @returns {Promise<Array>} Similar items
|
|
119
|
+
*/
|
|
120
|
+
async findSimilar(item, holon = null, lens = null, options = {}) {
|
|
121
|
+
if (!this.holosphere) {
|
|
122
|
+
throw new Error('HoloSphere instance required');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const { limit = 10, threshold = 0.5, useEmbeddings = true } = options;
|
|
126
|
+
|
|
127
|
+
// If embeddings available, use semantic search
|
|
128
|
+
if (useEmbeddings && this.embeddings && holon && lens) {
|
|
129
|
+
return this.embeddings.findSimilar(item, holon, lens, { limit, threshold });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Fall back to LLM-based similarity
|
|
133
|
+
if (!holon) {
|
|
134
|
+
throw new Error('Holon required for LLM-based similarity');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const searchLenses = lens ? [lens] : ['projects', 'quests', 'events', 'resources', 'default'];
|
|
138
|
+
let candidates = [];
|
|
139
|
+
|
|
140
|
+
for (const l of searchLenses) {
|
|
141
|
+
try {
|
|
142
|
+
const data = await this.holosphere.getAll(holon, l);
|
|
143
|
+
candidates.push(...data.map(d => ({ ...d, _lens: l })));
|
|
144
|
+
} catch {
|
|
145
|
+
// Skip
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (candidates.length === 0) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const systemPrompt = `Find items most similar to the reference item.
|
|
154
|
+
|
|
155
|
+
Reference item:
|
|
156
|
+
${JSON.stringify(item, null, 2)}
|
|
157
|
+
|
|
158
|
+
Consider:
|
|
159
|
+
1. Topic/theme similarity
|
|
160
|
+
2. Goal alignment
|
|
161
|
+
3. Participant overlap
|
|
162
|
+
4. Resource requirements
|
|
163
|
+
5. Timeline compatibility
|
|
164
|
+
|
|
165
|
+
Return JSON array of similar items with similarity scores:
|
|
166
|
+
[
|
|
167
|
+
{
|
|
168
|
+
"item": {item object},
|
|
169
|
+
"similarity": 0.0-1.0,
|
|
170
|
+
"reasons": ["reason1"]
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
Order by similarity descending. Max ${limit} items with similarity >= ${threshold}.`;
|
|
175
|
+
|
|
176
|
+
const results = await this.llm.getJSON(
|
|
177
|
+
systemPrompt,
|
|
178
|
+
JSON.stringify(candidates.slice(0, 50), null, 2),
|
|
179
|
+
{ temperature: 0.2 }
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return results.slice(0, limit).filter(r => r.similarity >= threshold);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build relationship graph
|
|
187
|
+
* @param {string} holon - Holon
|
|
188
|
+
* @param {string} lens - Lens
|
|
189
|
+
* @returns {Promise<Object>} Graph structure
|
|
190
|
+
*/
|
|
191
|
+
async buildGraph(holon, lens) {
|
|
192
|
+
const discovered = await this.discoverRelationships(holon, lens);
|
|
193
|
+
|
|
194
|
+
const nodes = new Map();
|
|
195
|
+
const edges = [];
|
|
196
|
+
|
|
197
|
+
// Build nodes from relationships
|
|
198
|
+
for (const rel of discovered.relationships?.relationships || []) {
|
|
199
|
+
if (!nodes.has(rel.item1.id)) {
|
|
200
|
+
nodes.set(rel.item1.id, { id: rel.item1.id, label: rel.item1.title });
|
|
201
|
+
}
|
|
202
|
+
if (!nodes.has(rel.item2.id)) {
|
|
203
|
+
nodes.set(rel.item2.id, { id: rel.item2.id, label: rel.item2.title });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
edges.push({
|
|
207
|
+
source: rel.item1.id,
|
|
208
|
+
target: rel.item2.id,
|
|
209
|
+
type: rel.type,
|
|
210
|
+
weight: rel.strength,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
nodes: Array.from(nodes.values()),
|
|
216
|
+
edges,
|
|
217
|
+
clusters: discovered.relationships?.clusters || [],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Find cross-holon relationships
|
|
223
|
+
* @param {string[]} holons - Holons to analyze
|
|
224
|
+
* @param {string} lens - Lens
|
|
225
|
+
* @returns {Promise<Object>} Cross-holon relationships
|
|
226
|
+
*/
|
|
227
|
+
async findCrossHolonRelationships(holons, lens) {
|
|
228
|
+
if (!this.holosphere) {
|
|
229
|
+
throw new Error('HoloSphere instance required');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const allData = {};
|
|
233
|
+
|
|
234
|
+
for (const holon of holons.slice(0, 5)) {
|
|
235
|
+
try {
|
|
236
|
+
const data = await this.holosphere.getAll(holon, lens);
|
|
237
|
+
if (data.length > 0) {
|
|
238
|
+
allData[holon] = data.slice(0, 15);
|
|
239
|
+
}
|
|
240
|
+
} catch {
|
|
241
|
+
// Skip
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (Object.keys(allData).length < 2) {
|
|
246
|
+
return { message: 'Need at least 2 holons with data' };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const systemPrompt = `Find relationships between items across different geographic regions.
|
|
250
|
+
|
|
251
|
+
Regions and their data:
|
|
252
|
+
${JSON.stringify(allData, null, 2)}
|
|
253
|
+
|
|
254
|
+
Identify:
|
|
255
|
+
1. Cross-region collaborations
|
|
256
|
+
2. Shared themes across regions
|
|
257
|
+
3. Potential synergies
|
|
258
|
+
4. Knowledge transfer opportunities
|
|
259
|
+
|
|
260
|
+
Return JSON:
|
|
261
|
+
{
|
|
262
|
+
"cross_relationships": [
|
|
263
|
+
{
|
|
264
|
+
"holon1": "id",
|
|
265
|
+
"item1": "item_id",
|
|
266
|
+
"holon2": "id",
|
|
267
|
+
"item2": "item_id",
|
|
268
|
+
"type": "type",
|
|
269
|
+
"potential": "collaboration|knowledge_share|synergy"
|
|
270
|
+
}
|
|
271
|
+
],
|
|
272
|
+
"shared_themes": [{"theme": "theme", "holons": ["id1", "id2"]}],
|
|
273
|
+
"opportunities": ["opp1"]
|
|
274
|
+
}`;
|
|
275
|
+
|
|
276
|
+
return this.llm.getJSON(systemPrompt, '', { temperature: 0.4 });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Suggest connections for an item
|
|
281
|
+
* @param {Object} item - Item to find connections for
|
|
282
|
+
* @param {string} holon - Holon
|
|
283
|
+
* @param {string} lens - Lens
|
|
284
|
+
* @returns {Promise<Object>} Suggested connections
|
|
285
|
+
*/
|
|
286
|
+
async suggestConnections(item, holon, lens) {
|
|
287
|
+
const similar = await this.findSimilar(item, holon, lens, { limit: 10 });
|
|
288
|
+
|
|
289
|
+
const systemPrompt = `Based on similar items found, suggest specific actions to create connections.
|
|
290
|
+
|
|
291
|
+
Reference item:
|
|
292
|
+
${JSON.stringify(item, null, 2)}
|
|
293
|
+
|
|
294
|
+
Similar items found:
|
|
295
|
+
${JSON.stringify(similar, null, 2)}
|
|
296
|
+
|
|
297
|
+
Suggest:
|
|
298
|
+
1. Collaboration opportunities
|
|
299
|
+
2. Resource sharing possibilities
|
|
300
|
+
3. Joint initiatives
|
|
301
|
+
4. Knowledge exchange
|
|
302
|
+
|
|
303
|
+
Return JSON:
|
|
304
|
+
{
|
|
305
|
+
"collaborations": [{"with": "item_id", "action": "suggested action"}],
|
|
306
|
+
"resource_sharing": [{"items": ["id1", "id2"], "resource": "what to share"}],
|
|
307
|
+
"initiatives": [{"description": "joint initiative", "participants": ["id1"]}],
|
|
308
|
+
"knowledge_exchange": [{"from": "id", "to": "id", "topic": "topic"}]
|
|
309
|
+
}`;
|
|
310
|
+
|
|
311
|
+
return this.llm.getJSON(systemPrompt, '', { temperature: 0.5 });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Detect relationship patterns
|
|
316
|
+
* @param {string} holon - Holon
|
|
317
|
+
* @param {string} lens - Lens
|
|
318
|
+
* @returns {Promise<Object>} Detected patterns
|
|
319
|
+
*/
|
|
320
|
+
async detectPatterns(holon, lens) {
|
|
321
|
+
const relationships = await this.discoverRelationships(holon, lens);
|
|
322
|
+
|
|
323
|
+
const systemPrompt = `Analyze relationships and detect higher-level patterns.
|
|
324
|
+
|
|
325
|
+
Relationships:
|
|
326
|
+
${JSON.stringify(relationships.relationships, null, 2)}
|
|
327
|
+
|
|
328
|
+
Detect:
|
|
329
|
+
1. Hub-spoke patterns (central connectors)
|
|
330
|
+
2. Cluster patterns (tight groups)
|
|
331
|
+
3. Bridge patterns (connecting different groups)
|
|
332
|
+
4. Chain patterns (sequential relationships)
|
|
333
|
+
5. Cyclical patterns (mutual dependencies)
|
|
334
|
+
|
|
335
|
+
Return JSON:
|
|
336
|
+
{
|
|
337
|
+
"patterns": [
|
|
338
|
+
{
|
|
339
|
+
"type": "hub|cluster|bridge|chain|cycle",
|
|
340
|
+
"description": "pattern description",
|
|
341
|
+
"items": ["id1", "id2"],
|
|
342
|
+
"significance": "why this matters"
|
|
343
|
+
}
|
|
344
|
+
],
|
|
345
|
+
"network_structure": "description of overall structure",
|
|
346
|
+
"recommendations": ["rec1"]
|
|
347
|
+
}`;
|
|
348
|
+
|
|
349
|
+
return this.llm.getJSON(systemPrompt, '', { temperature: 0.3 });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export default RelationshipDiscovery;
|