holosphere 1.1.20 → 2.0.0-alpha1
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/LICENSE +162 -38
- 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,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classifier - Auto-classification of content to appropriate lenses
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Classifier class for content categorization
|
|
7
|
+
*/
|
|
8
|
+
export class Classifier {
|
|
9
|
+
/**
|
|
10
|
+
* @param {LLMService} llmService - LLM service instance
|
|
11
|
+
* @param {Object} holosphere - HoloSphere instance (optional)
|
|
12
|
+
*/
|
|
13
|
+
constructor(llmService, holosphere = null) {
|
|
14
|
+
this.llm = llmService;
|
|
15
|
+
this.holosphere = holosphere;
|
|
16
|
+
this.lensDescriptions = new Map();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Set HoloSphere instance
|
|
21
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
22
|
+
*/
|
|
23
|
+
setHoloSphere(holosphere) {
|
|
24
|
+
this.holosphere = holosphere;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Register a lens with its description
|
|
29
|
+
* @param {string} lensName - Lens name
|
|
30
|
+
* @param {string} description - Description of what goes in this lens
|
|
31
|
+
* @param {Object} schema - Optional JSON schema for the lens
|
|
32
|
+
*/
|
|
33
|
+
registerLens(lensName, description, schema = null) {
|
|
34
|
+
this.lensDescriptions.set(lensName, { description, schema });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Register multiple lenses
|
|
39
|
+
* @param {Object<string, {description: string, schema?: Object}>} lenses
|
|
40
|
+
*/
|
|
41
|
+
registerLenses(lenses) {
|
|
42
|
+
for (const [name, config] of Object.entries(lenses)) {
|
|
43
|
+
this.registerLens(name, config.description, config.schema);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Classify content into the best lens
|
|
49
|
+
* @param {Object|string} content - Content to classify
|
|
50
|
+
* @returns {Promise<{lens: string, confidence: number, reasoning: string}>}
|
|
51
|
+
*/
|
|
52
|
+
async classifyToLens(content) {
|
|
53
|
+
const lenses = Array.from(this.lensDescriptions.entries());
|
|
54
|
+
|
|
55
|
+
if (lenses.length === 0) {
|
|
56
|
+
throw new Error('No lenses registered. Use registerLens() first.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const lensDescriptions = lenses
|
|
60
|
+
.map(([name, config]) => `- ${name}: ${config.description}`)
|
|
61
|
+
.join('\n');
|
|
62
|
+
|
|
63
|
+
const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
64
|
+
|
|
65
|
+
const systemPrompt = `You are a content classifier. Classify the following content into the most appropriate lens.
|
|
66
|
+
|
|
67
|
+
Available lenses:
|
|
68
|
+
${lensDescriptions}
|
|
69
|
+
|
|
70
|
+
Return JSON: {"lens": "lens_name", "confidence": 0.0-1.0, "reasoning": "brief explanation"}`;
|
|
71
|
+
|
|
72
|
+
return this.llm.getJSON(systemPrompt, contentStr, { temperature: 0.2 });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Classify content into multiple possible lenses
|
|
77
|
+
* @param {Object|string} content - Content to classify
|
|
78
|
+
* @param {number} maxLenses - Maximum lenses to return
|
|
79
|
+
* @returns {Promise<Array<{lens: string, confidence: number}>>}
|
|
80
|
+
*/
|
|
81
|
+
async classifyMultiple(content, maxLenses = 3) {
|
|
82
|
+
const lenses = Array.from(this.lensDescriptions.entries());
|
|
83
|
+
|
|
84
|
+
if (lenses.length === 0) {
|
|
85
|
+
throw new Error('No lenses registered');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const lensDescriptions = lenses
|
|
89
|
+
.map(([name, config]) => `- ${name}: ${config.description}`)
|
|
90
|
+
.join('\n');
|
|
91
|
+
|
|
92
|
+
const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
93
|
+
|
|
94
|
+
const systemPrompt = `You are a content classifier. Classify the content into up to ${maxLenses} appropriate lenses, ranked by fit.
|
|
95
|
+
|
|
96
|
+
Available lenses:
|
|
97
|
+
${lensDescriptions}
|
|
98
|
+
|
|
99
|
+
Return JSON array: [{"lens": "name", "confidence": 0.0-1.0}, ...]
|
|
100
|
+
Order by confidence descending.`;
|
|
101
|
+
|
|
102
|
+
return this.llm.getJSON(systemPrompt, contentStr, { temperature: 0.2 });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Auto-store content in the best lens
|
|
107
|
+
* @param {string} holon - Holon identifier
|
|
108
|
+
* @param {Object} content - Content to store
|
|
109
|
+
* @returns {Promise<{lens: string, stored: boolean, result: Object}>}
|
|
110
|
+
*/
|
|
111
|
+
async autoStore(holon, content) {
|
|
112
|
+
if (!this.holosphere) {
|
|
113
|
+
throw new Error('HoloSphere instance required for storage');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const classification = await this.classifyToLens(content);
|
|
117
|
+
|
|
118
|
+
// Store in classified lens
|
|
119
|
+
const result = await this.holosphere.put(holon, classification.lens, content);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
lens: classification.lens,
|
|
123
|
+
confidence: classification.confidence,
|
|
124
|
+
reasoning: classification.reasoning,
|
|
125
|
+
stored: true,
|
|
126
|
+
result,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Suggest lens for new content type
|
|
132
|
+
* @param {string} contentDescription - Description of the new content type
|
|
133
|
+
* @returns {Promise<{name: string, description: string}>}
|
|
134
|
+
*/
|
|
135
|
+
async suggestNewLens(contentDescription) {
|
|
136
|
+
const existingLenses = Array.from(this.lensDescriptions.keys());
|
|
137
|
+
|
|
138
|
+
const systemPrompt = `You are helping design a data organization system. Suggest a new lens (category) for this type of content.
|
|
139
|
+
|
|
140
|
+
Existing lenses: ${existingLenses.join(', ')}
|
|
141
|
+
|
|
142
|
+
Return JSON: {"name": "lens_name", "description": "what this lens contains"}
|
|
143
|
+
Use lowercase_snake_case for names.`;
|
|
144
|
+
|
|
145
|
+
return this.llm.getJSON(systemPrompt, contentDescription, { temperature: 0.5 });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Analyze content and extract fields for schema
|
|
150
|
+
* @param {Object} content - Sample content
|
|
151
|
+
* @returns {Promise<Object>} Suggested JSON schema
|
|
152
|
+
*/
|
|
153
|
+
async suggestSchema(content) {
|
|
154
|
+
const contentStr = JSON.stringify(content, null, 2);
|
|
155
|
+
|
|
156
|
+
const systemPrompt = `You are a data modeling expert. Analyze this content and suggest a JSON schema for validating similar content.
|
|
157
|
+
|
|
158
|
+
Return a valid JSON schema with:
|
|
159
|
+
- Type definitions
|
|
160
|
+
- Required fields
|
|
161
|
+
- Field descriptions
|
|
162
|
+
- Reasonable constraints`;
|
|
163
|
+
|
|
164
|
+
return this.llm.getJSON(systemPrompt, contentStr, { temperature: 0.3 });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Validate content against lens schema
|
|
169
|
+
* @param {Object} content - Content to validate
|
|
170
|
+
* @param {string} lensName - Lens to validate against
|
|
171
|
+
* @returns {Promise<{valid: boolean, issues: string[]}>}
|
|
172
|
+
*/
|
|
173
|
+
async validateForLens(content, lensName) {
|
|
174
|
+
const lensConfig = this.lensDescriptions.get(lensName);
|
|
175
|
+
|
|
176
|
+
if (!lensConfig) {
|
|
177
|
+
throw new Error(`Lens not found: ${lensName}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!lensConfig.schema) {
|
|
181
|
+
// If no schema, use AI to validate
|
|
182
|
+
const systemPrompt = `Validate if this content is appropriate for the "${lensName}" lens.
|
|
183
|
+
Lens description: ${lensConfig.description}
|
|
184
|
+
|
|
185
|
+
Return JSON: {"valid": true/false, "issues": ["issue1", "issue2"] or []}`;
|
|
186
|
+
|
|
187
|
+
return this.llm.getJSON(systemPrompt, JSON.stringify(content, null, 2), { temperature: 0.2 });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Use schema validation (would integrate with schema validator)
|
|
191
|
+
return { valid: true, issues: [] };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get classification stats for a collection
|
|
196
|
+
* @param {Object[]} items - Items to analyze
|
|
197
|
+
* @returns {Promise<Object>} Classification statistics
|
|
198
|
+
*/
|
|
199
|
+
async analyzeCollection(items) {
|
|
200
|
+
const classifications = await Promise.all(
|
|
201
|
+
items.map(item => this.classifyToLens(item))
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const stats = {};
|
|
205
|
+
for (const c of classifications) {
|
|
206
|
+
stats[c.lens] = (stats[c.lens] || 0) + 1;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
total: items.length,
|
|
211
|
+
byLens: stats,
|
|
212
|
+
classifications,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export default Classifier;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Council - Multi-perspective wisdom from different viewpoints
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Default 12 perspectives based on HolonsBot
|
|
6
|
+
const DEFAULT_PERSPECTIVES = [
|
|
7
|
+
{ name: 'Values & Worldview', prompt: 'Answer from the embodied perspective of Values and Worldview - considering ethics, beliefs, culture, and meaning.' },
|
|
8
|
+
{ name: 'Health & Wellbeing', prompt: 'Answer from the embodied perspective of Health & Wellbeing - considering physical, mental, and social health.' },
|
|
9
|
+
{ name: 'Food & Agriculture', prompt: 'Answer from the embodied perspective of Food & Agriculture - considering food systems, nutrition, and farming.' },
|
|
10
|
+
{ name: 'Business & Trade', prompt: 'Answer from the embodied perspective of Business & Trade - considering commerce, markets, and economic exchange.' },
|
|
11
|
+
{ name: 'Energy & Resources', prompt: 'Answer from the embodied perspective of Energy & Resources - considering power, materials, and sustainability.' },
|
|
12
|
+
{ name: 'Climate & Environment', prompt: 'Answer from the embodied perspective of Climate Change - considering environmental impact and adaptation.' },
|
|
13
|
+
{ name: 'Ecosystems & Biosphere', prompt: 'Answer from the embodied perspective of Ecosystems & Biosphere - considering biodiversity and natural systems.' },
|
|
14
|
+
{ name: 'Water Availability', prompt: 'Answer from the embodied perspective of Water Availability - considering water access, quality, and management.' },
|
|
15
|
+
{ name: 'Habitat & Infrastructure', prompt: 'Answer from the embodied perspective of Habitat & Infrastructure - considering built environment and housing.' },
|
|
16
|
+
{ name: 'Economy & Wealth', prompt: 'Answer from the embodied perspective of Economy & Wealth - considering prosperity, inequality, and resources.' },
|
|
17
|
+
{ name: 'Governance & Institutions', prompt: 'Answer from the embodied perspective of Governance & Institutions - considering power, policy, and organization.' },
|
|
18
|
+
{ name: 'Community & Resilience', prompt: 'Answer from the embodied perspective of Community & Resilience - considering social bonds and adaptability.' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Council class for multi-perspective answers
|
|
23
|
+
*/
|
|
24
|
+
export class Council {
|
|
25
|
+
/**
|
|
26
|
+
* @param {LLMService} llmService - LLM service instance
|
|
27
|
+
* @param {Array} perspectives - Custom perspectives (optional)
|
|
28
|
+
*/
|
|
29
|
+
constructor(llmService, perspectives = null) {
|
|
30
|
+
this.llm = llmService;
|
|
31
|
+
this.perspectives = perspectives || DEFAULT_PERSPECTIVES;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Set custom perspectives
|
|
36
|
+
* @param {Array<{name: string, prompt: string}>} perspectives
|
|
37
|
+
*/
|
|
38
|
+
setPerspectives(perspectives) {
|
|
39
|
+
this.perspectives = perspectives;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get default perspectives
|
|
44
|
+
* @returns {Array} Default perspectives
|
|
45
|
+
*/
|
|
46
|
+
static getDefaultPerspectives() {
|
|
47
|
+
return DEFAULT_PERSPECTIVES;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Ask a question to the council
|
|
52
|
+
* @param {string} question - Question to ask
|
|
53
|
+
* @param {Object} options - Options
|
|
54
|
+
* @returns {Promise<{perspectives: Array, summary: string}>}
|
|
55
|
+
*/
|
|
56
|
+
async ask(question, options = {}) {
|
|
57
|
+
const { parallel = true, includeSummary = true } = options;
|
|
58
|
+
const perspectives = options.perspectives || this.perspectives;
|
|
59
|
+
|
|
60
|
+
// Get answers from all perspectives
|
|
61
|
+
let answers;
|
|
62
|
+
|
|
63
|
+
if (parallel) {
|
|
64
|
+
answers = await Promise.all(
|
|
65
|
+
perspectives.map(async (perspective) => {
|
|
66
|
+
const answer = await this._askPerspective(question, perspective);
|
|
67
|
+
return {
|
|
68
|
+
perspective: perspective.name,
|
|
69
|
+
answer,
|
|
70
|
+
};
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
} else {
|
|
74
|
+
answers = [];
|
|
75
|
+
for (const perspective of perspectives) {
|
|
76
|
+
const answer = await this._askPerspective(question, perspective);
|
|
77
|
+
answers.push({
|
|
78
|
+
perspective: perspective.name,
|
|
79
|
+
answer,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Generate summary
|
|
85
|
+
let summary = null;
|
|
86
|
+
if (includeSummary) {
|
|
87
|
+
summary = await this._summarize(question, answers);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
question,
|
|
92
|
+
perspectives: answers,
|
|
93
|
+
summary,
|
|
94
|
+
timestamp: Date.now(),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Ask a question from specific perspective
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
async _askPerspective(question, perspective) {
|
|
103
|
+
const systemPrompt = `You are a wise council member representing a specific perspective.
|
|
104
|
+
|
|
105
|
+
${perspective.prompt}
|
|
106
|
+
|
|
107
|
+
Provide thoughtful, nuanced answers that honor your unique perspective while being constructive and helpful. Be concise but insightful.`;
|
|
108
|
+
|
|
109
|
+
return this.llm.sendMessage(systemPrompt, question, { temperature: 0.7 });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Summarize all perspectives
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
async _summarize(question, answers) {
|
|
117
|
+
const perspectiveTexts = answers
|
|
118
|
+
.map(a => `**${a.perspective}**: ${a.answer}`)
|
|
119
|
+
.join('\n\n');
|
|
120
|
+
|
|
121
|
+
const systemPrompt = `You are a wise facilitator. Synthesize the following perspectives into a balanced summary that:
|
|
122
|
+
1. Identifies common themes and agreements
|
|
123
|
+
2. Notes important tensions or tradeoffs
|
|
124
|
+
3. Provides actionable wisdom
|
|
125
|
+
4. Remains neutral and balanced
|
|
126
|
+
|
|
127
|
+
Be concise but comprehensive.`;
|
|
128
|
+
|
|
129
|
+
const userMessage = `Question: ${question}
|
|
130
|
+
|
|
131
|
+
Perspectives:
|
|
132
|
+
${perspectiveTexts}`;
|
|
133
|
+
|
|
134
|
+
return this.llm.sendMessage(systemPrompt, userMessage, { temperature: 0.5 });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Create custom perspectives from a list of names
|
|
139
|
+
* @param {string[]} names - Perspective names
|
|
140
|
+
* @returns {Array} Custom perspectives
|
|
141
|
+
*/
|
|
142
|
+
static createPerspectives(names) {
|
|
143
|
+
return names.map(name => ({
|
|
144
|
+
name,
|
|
145
|
+
prompt: `Answer from the embodied perspective of ${name} - considering all aspects related to ${name.toLowerCase()}.`,
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Ask with custom perspectives (convenience method)
|
|
151
|
+
* @param {string} question - Question
|
|
152
|
+
* @param {string[]} perspectiveNames - List of perspective names
|
|
153
|
+
* @param {Object} options - Options
|
|
154
|
+
* @returns {Promise<Object>} Council response
|
|
155
|
+
*/
|
|
156
|
+
async askCustom(question, perspectiveNames, options = {}) {
|
|
157
|
+
const perspectives = Council.createPerspectives(perspectiveNames);
|
|
158
|
+
return this.ask(question, { ...options, perspectives });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get a single perspective answer
|
|
163
|
+
* @param {string} question - Question
|
|
164
|
+
* @param {string} perspectiveName - Perspective name
|
|
165
|
+
* @returns {Promise<string>} Answer
|
|
166
|
+
*/
|
|
167
|
+
async askSingle(question, perspectiveName) {
|
|
168
|
+
const perspective = this.perspectives.find(p => p.name === perspectiveName)
|
|
169
|
+
|| { name: perspectiveName, prompt: `Answer from the perspective of ${perspectiveName}.` };
|
|
170
|
+
|
|
171
|
+
return this._askPerspective(question, perspective);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Debate between perspectives
|
|
176
|
+
* @param {string} topic - Topic to debate
|
|
177
|
+
* @param {string[]} perspectiveNames - Two perspectives to debate
|
|
178
|
+
* @param {number} rounds - Number of exchange rounds
|
|
179
|
+
* @returns {Promise<Object>} Debate result
|
|
180
|
+
*/
|
|
181
|
+
async debate(topic, perspectiveNames, rounds = 3) {
|
|
182
|
+
if (perspectiveNames.length !== 2) {
|
|
183
|
+
throw new Error('Debate requires exactly 2 perspectives');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const perspectives = perspectiveNames.map(name =>
|
|
187
|
+
this.perspectives.find(p => p.name === name)
|
|
188
|
+
|| { name, prompt: `Argue from the perspective of ${name}.` }
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const exchanges = [];
|
|
192
|
+
let context = `Topic: ${topic}`;
|
|
193
|
+
|
|
194
|
+
for (let i = 0; i < rounds; i++) {
|
|
195
|
+
for (const perspective of perspectives) {
|
|
196
|
+
const systemPrompt = `You are debating from the perspective of ${perspective.name}.
|
|
197
|
+
${perspective.prompt}
|
|
198
|
+
Respond to the previous arguments constructively, acknowledging good points but advocating for your perspective.`;
|
|
199
|
+
|
|
200
|
+
const response = await this.llm.sendMessage(systemPrompt, context, { temperature: 0.7 });
|
|
201
|
+
|
|
202
|
+
exchanges.push({
|
|
203
|
+
perspective: perspective.name,
|
|
204
|
+
round: i + 1,
|
|
205
|
+
response,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
context += `\n\n${perspective.name}: ${response}`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Generate conclusion
|
|
213
|
+
const conclusion = await this.llm.sendMessage(
|
|
214
|
+
'As a neutral moderator, summarize the key insights from this debate and identify areas of agreement and disagreement.',
|
|
215
|
+
context,
|
|
216
|
+
{ temperature: 0.5 }
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
topic,
|
|
221
|
+
perspectives: perspectiveNames,
|
|
222
|
+
exchanges,
|
|
223
|
+
conclusion,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export default Council;
|