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
package/src/ai/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HoloSphere AI Module
|
|
3
|
+
* Comprehensive AI functionality using OpenAI chat completions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import all modules for local use and re-export
|
|
7
|
+
import { LLMService, default as LLM } from './llm-service.js';
|
|
8
|
+
import { SchemaExtractor } from './schema-extractor.js';
|
|
9
|
+
import { JSONOps } from './json-ops.js';
|
|
10
|
+
import { Embeddings } from './embeddings.js';
|
|
11
|
+
import { Council } from './council.js';
|
|
12
|
+
import { TTS, VOICES, MODELS } from './tts.js';
|
|
13
|
+
import { NLQuery } from './nl-query.js';
|
|
14
|
+
import { Classifier } from './classifier.js';
|
|
15
|
+
import { SpatialAnalysis } from './spatial.js';
|
|
16
|
+
import { SmartAggregation } from './aggregation.js';
|
|
17
|
+
import { FederationAdvisor } from './federation-ai.js';
|
|
18
|
+
import { RelationshipDiscovery } from './relationships.js';
|
|
19
|
+
import { TaskBreakdown } from './breakdown.js';
|
|
20
|
+
import { H3AI } from './h3-ai.js';
|
|
21
|
+
|
|
22
|
+
// Re-export all modules
|
|
23
|
+
export {
|
|
24
|
+
LLMService,
|
|
25
|
+
LLM,
|
|
26
|
+
SchemaExtractor,
|
|
27
|
+
JSONOps,
|
|
28
|
+
Embeddings,
|
|
29
|
+
Council,
|
|
30
|
+
TTS,
|
|
31
|
+
VOICES,
|
|
32
|
+
MODELS,
|
|
33
|
+
NLQuery,
|
|
34
|
+
Classifier,
|
|
35
|
+
SpatialAnalysis,
|
|
36
|
+
SmartAggregation,
|
|
37
|
+
FederationAdvisor,
|
|
38
|
+
RelationshipDiscovery,
|
|
39
|
+
TaskBreakdown,
|
|
40
|
+
H3AI,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create all AI services with shared LLM instance
|
|
45
|
+
* @param {string} apiKey - OpenAI API key
|
|
46
|
+
* @param {Object} holosphere - HoloSphere instance (optional)
|
|
47
|
+
* @param {Object} options - Configuration options
|
|
48
|
+
* @param {Object} openaiClient - Optional pre-created OpenAI client
|
|
49
|
+
* @returns {Object} AI services
|
|
50
|
+
*/
|
|
51
|
+
export function createAIServices(apiKey, holosphere = null, options = {}, openaiClient = null) {
|
|
52
|
+
// Create shared LLM service
|
|
53
|
+
const llm = new LLMService(apiKey, options.llm);
|
|
54
|
+
|
|
55
|
+
// Use provided OpenAI client or create a placeholder
|
|
56
|
+
// Note: For embeddings/TTS, caller should provide openaiClient or use individual services
|
|
57
|
+
const openai = openaiClient;
|
|
58
|
+
|
|
59
|
+
// Create all services
|
|
60
|
+
const embeddings = new Embeddings(openai, holosphere);
|
|
61
|
+
const schemaExtractor = new SchemaExtractor(llm);
|
|
62
|
+
const jsonOps = new JSONOps(llm);
|
|
63
|
+
const council = new Council(llm);
|
|
64
|
+
const tts = new TTS(openai);
|
|
65
|
+
const nlQuery = new NLQuery(llm, holosphere);
|
|
66
|
+
const classifier = new Classifier(llm, holosphere);
|
|
67
|
+
const spatial = new SpatialAnalysis(llm, holosphere);
|
|
68
|
+
const aggregation = new SmartAggregation(llm, holosphere);
|
|
69
|
+
const federationAdvisor = new FederationAdvisor(llm, holosphere, embeddings);
|
|
70
|
+
const relationships = new RelationshipDiscovery(llm, holosphere, embeddings);
|
|
71
|
+
const taskBreakdown = new TaskBreakdown(llm, holosphere);
|
|
72
|
+
const h3ai = new H3AI(llm, holosphere);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
llm,
|
|
76
|
+
openai,
|
|
77
|
+
embeddings,
|
|
78
|
+
schemaExtractor,
|
|
79
|
+
jsonOps,
|
|
80
|
+
council,
|
|
81
|
+
tts,
|
|
82
|
+
nlQuery,
|
|
83
|
+
classifier,
|
|
84
|
+
spatial,
|
|
85
|
+
aggregation,
|
|
86
|
+
federationAdvisor,
|
|
87
|
+
relationships,
|
|
88
|
+
taskBreakdown,
|
|
89
|
+
h3ai,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Default export
|
|
94
|
+
export default {
|
|
95
|
+
LLMService,
|
|
96
|
+
SchemaExtractor,
|
|
97
|
+
JSONOps,
|
|
98
|
+
Embeddings,
|
|
99
|
+
Council,
|
|
100
|
+
TTS,
|
|
101
|
+
VOICES,
|
|
102
|
+
MODELS,
|
|
103
|
+
NLQuery,
|
|
104
|
+
Classifier,
|
|
105
|
+
SpatialAnalysis,
|
|
106
|
+
SmartAggregation,
|
|
107
|
+
FederationAdvisor,
|
|
108
|
+
RelationshipDiscovery,
|
|
109
|
+
TaskBreakdown,
|
|
110
|
+
H3AI,
|
|
111
|
+
createAIServices,
|
|
112
|
+
};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy JSON Operations - AI-assisted semantic operations on JSON structures
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* JSON Operations class for semantic JSON manipulation
|
|
7
|
+
*/
|
|
8
|
+
export class JSONOps {
|
|
9
|
+
/**
|
|
10
|
+
* @param {LLMService} llmService - LLM service instance
|
|
11
|
+
*/
|
|
12
|
+
constructor(llmService) {
|
|
13
|
+
this.llm = llmService;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Semantically merge two JSON objects
|
|
18
|
+
* @param {Object} obj1 - First object
|
|
19
|
+
* @param {Object} obj2 - Second object
|
|
20
|
+
* @param {Object} options - Merge options
|
|
21
|
+
* @returns {Promise<Object>} Merged object
|
|
22
|
+
*/
|
|
23
|
+
async add(obj1, obj2, options = {}) {
|
|
24
|
+
const systemPrompt = `You are a JSON merge expert. Semantically merge these two JSON objects.
|
|
25
|
+
|
|
26
|
+
Rules:
|
|
27
|
+
1. Combine information from both objects intelligently
|
|
28
|
+
2. For conflicting values, prefer the more complete/detailed one${options.preferSecond ? ', or prefer the second object' : ''}
|
|
29
|
+
3. Merge arrays by combining items, removing duplicates by semantic similarity
|
|
30
|
+
4. Preserve all unique information from both objects
|
|
31
|
+
5. Return ONLY valid JSON`;
|
|
32
|
+
|
|
33
|
+
const userMessage = `Object 1:
|
|
34
|
+
${JSON.stringify(obj1, null, 2)}
|
|
35
|
+
|
|
36
|
+
Object 2:
|
|
37
|
+
${JSON.stringify(obj2, null, 2)}`;
|
|
38
|
+
|
|
39
|
+
return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.2 });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Remove concepts from obj1 based on obj2
|
|
44
|
+
* @param {Object} obj1 - Base object
|
|
45
|
+
* @param {Object} obj2 - Object containing concepts to remove
|
|
46
|
+
* @returns {Promise<Object>} Result with concepts removed
|
|
47
|
+
*/
|
|
48
|
+
async subtract(obj1, obj2) {
|
|
49
|
+
const systemPrompt = `You are a JSON manipulation expert. Remove concepts from the first object that are semantically present in the second object.
|
|
50
|
+
|
|
51
|
+
Rules:
|
|
52
|
+
1. Remove fields from obj1 that have semantic equivalents in obj2
|
|
53
|
+
2. For arrays, remove items that are semantically similar to items in obj2
|
|
54
|
+
3. Preserve fields/items in obj1 that have no semantic match in obj2
|
|
55
|
+
4. Remove nested content that matches semantically
|
|
56
|
+
5. Return ONLY valid JSON`;
|
|
57
|
+
|
|
58
|
+
const userMessage = `Base object (to modify):
|
|
59
|
+
${JSON.stringify(obj1, null, 2)}
|
|
60
|
+
|
|
61
|
+
Concepts to remove:
|
|
62
|
+
${JSON.stringify(obj2, null, 2)}`;
|
|
63
|
+
|
|
64
|
+
return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.2 });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Combine objects with semantic deduplication
|
|
69
|
+
* @param {Object} obj1 - First object
|
|
70
|
+
* @param {Object} obj2 - Second object
|
|
71
|
+
* @returns {Promise<Object>} Union of objects
|
|
72
|
+
*/
|
|
73
|
+
async union(obj1, obj2) {
|
|
74
|
+
const systemPrompt = `You are a JSON merge expert. Create a semantic union of these two JSON objects.
|
|
75
|
+
|
|
76
|
+
Rules:
|
|
77
|
+
1. Include all unique information from both objects
|
|
78
|
+
2. For duplicate concepts, keep only one (the more complete version)
|
|
79
|
+
3. Deduplicate array items by semantic similarity, not just exact match
|
|
80
|
+
4. Merge nested objects recursively with the same rules
|
|
81
|
+
5. Return ONLY valid JSON`;
|
|
82
|
+
|
|
83
|
+
const userMessage = `Object 1:
|
|
84
|
+
${JSON.stringify(obj1, null, 2)}
|
|
85
|
+
|
|
86
|
+
Object 2:
|
|
87
|
+
${JSON.stringify(obj2, null, 2)}`;
|
|
88
|
+
|
|
89
|
+
return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.2 });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Find differences between two objects
|
|
94
|
+
* @param {Object} obj1 - First object
|
|
95
|
+
* @param {Object} obj2 - Second object
|
|
96
|
+
* @returns {Promise<{added: Object, removed: Object, changed: Object}>} Differences
|
|
97
|
+
*/
|
|
98
|
+
async difference(obj1, obj2) {
|
|
99
|
+
const systemPrompt = `You are a JSON comparison expert. Find the semantic differences between these two JSON objects.
|
|
100
|
+
|
|
101
|
+
Return a JSON object with:
|
|
102
|
+
- "added": fields/values present in obj2 but not in obj1
|
|
103
|
+
- "removed": fields/values present in obj1 but not in obj2
|
|
104
|
+
- "changed": fields where the value changed (show both old and new)
|
|
105
|
+
|
|
106
|
+
Consider semantic similarity - fields with different names but same meaning should be considered the same.
|
|
107
|
+
Return ONLY valid JSON with the structure: {"added": {}, "removed": {}, "changed": {}}`;
|
|
108
|
+
|
|
109
|
+
const userMessage = `Object 1 (original):
|
|
110
|
+
${JSON.stringify(obj1, null, 2)}
|
|
111
|
+
|
|
112
|
+
Object 2 (new):
|
|
113
|
+
${JSON.stringify(obj2, null, 2)}`;
|
|
114
|
+
|
|
115
|
+
return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.2 });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Concatenate arrays and text fields semantically
|
|
120
|
+
* @param {Object} obj1 - First object
|
|
121
|
+
* @param {Object} obj2 - Second object
|
|
122
|
+
* @returns {Promise<Object>} Concatenated object
|
|
123
|
+
*/
|
|
124
|
+
async concatenate(obj1, obj2) {
|
|
125
|
+
const systemPrompt = `You are a JSON concatenation expert. Concatenate these two JSON objects.
|
|
126
|
+
|
|
127
|
+
Rules:
|
|
128
|
+
1. For string fields: append obj2 values to obj1 values with appropriate separators
|
|
129
|
+
2. For array fields: append obj2 arrays to obj1 arrays
|
|
130
|
+
3. For number fields: sum the values
|
|
131
|
+
4. For object fields: recursively concatenate
|
|
132
|
+
5. For boolean fields: use logical OR
|
|
133
|
+
6. Preserve the structure of obj1
|
|
134
|
+
7. Return ONLY valid JSON`;
|
|
135
|
+
|
|
136
|
+
const userMessage = `Object 1:
|
|
137
|
+
${JSON.stringify(obj1, null, 2)}
|
|
138
|
+
|
|
139
|
+
Object 2:
|
|
140
|
+
${JSON.stringify(obj2, null, 2)}`;
|
|
141
|
+
|
|
142
|
+
return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.2 });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Find intersection - common concepts between objects
|
|
147
|
+
* @param {Object} obj1 - First object
|
|
148
|
+
* @param {Object} obj2 - Second object
|
|
149
|
+
* @returns {Promise<Object>} Common elements
|
|
150
|
+
*/
|
|
151
|
+
async intersection(obj1, obj2) {
|
|
152
|
+
const systemPrompt = `You are a JSON comparison expert. Find the semantic intersection of these two JSON objects.
|
|
153
|
+
|
|
154
|
+
Rules:
|
|
155
|
+
1. Return only fields/values that are semantically present in BOTH objects
|
|
156
|
+
2. For arrays, return only items that have semantic matches in both
|
|
157
|
+
3. Consider semantic similarity, not just exact matches
|
|
158
|
+
4. Return ONLY valid JSON`;
|
|
159
|
+
|
|
160
|
+
const userMessage = `Object 1:
|
|
161
|
+
${JSON.stringify(obj1, null, 2)}
|
|
162
|
+
|
|
163
|
+
Object 2:
|
|
164
|
+
${JSON.stringify(obj2, null, 2)}`;
|
|
165
|
+
|
|
166
|
+
return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.2 });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Transform object structure
|
|
171
|
+
* @param {Object} obj - Object to transform
|
|
172
|
+
* @param {string} targetStructure - Description of target structure
|
|
173
|
+
* @returns {Promise<Object>} Transformed object
|
|
174
|
+
*/
|
|
175
|
+
async transform(obj, targetStructure) {
|
|
176
|
+
const systemPrompt = `You are a JSON transformation expert. Transform this JSON object according to the target structure description.
|
|
177
|
+
|
|
178
|
+
Target structure: ${targetStructure}
|
|
179
|
+
|
|
180
|
+
Rules:
|
|
181
|
+
1. Map existing fields to the new structure
|
|
182
|
+
2. Preserve all information, reorganizing as needed
|
|
183
|
+
3. Use appropriate type conversions
|
|
184
|
+
4. Return ONLY valid JSON`;
|
|
185
|
+
|
|
186
|
+
return this.llm.getJSON(systemPrompt, JSON.stringify(obj, null, 2), { temperature: 0.3 });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Simplify/flatten nested object
|
|
191
|
+
* @param {Object} obj - Object to simplify
|
|
192
|
+
* @param {Object} options - Options
|
|
193
|
+
* @returns {Promise<Object>} Simplified object
|
|
194
|
+
*/
|
|
195
|
+
async simplify(obj, options = {}) {
|
|
196
|
+
const systemPrompt = `You are a JSON simplification expert. Simplify this nested JSON object.
|
|
197
|
+
|
|
198
|
+
Rules:
|
|
199
|
+
1. Flatten deeply nested structures where appropriate
|
|
200
|
+
2. Combine redundant fields
|
|
201
|
+
3. Remove null/empty values${options.keepEmpty ? ' (unless specified to keep)' : ''}
|
|
202
|
+
4. Use clear, concise field names
|
|
203
|
+
5. Return ONLY valid JSON`;
|
|
204
|
+
|
|
205
|
+
return this.llm.getJSON(systemPrompt, JSON.stringify(obj, null, 2), { temperature: 0.3 });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Apply a natural language operation to JSON
|
|
210
|
+
* @param {Object} obj - Object to modify
|
|
211
|
+
* @param {string} operation - Natural language description of operation
|
|
212
|
+
* @returns {Promise<Object>} Modified object
|
|
213
|
+
*/
|
|
214
|
+
async apply(obj, operation) {
|
|
215
|
+
const systemPrompt = `You are a JSON manipulation expert. Apply the following operation to the JSON object:
|
|
216
|
+
|
|
217
|
+
Operation: ${operation}
|
|
218
|
+
|
|
219
|
+
Return ONLY valid JSON with the operation applied.`;
|
|
220
|
+
|
|
221
|
+
return this.llm.getJSON(systemPrompt, JSON.stringify(obj, null, 2), { temperature: 0.3 });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export default JSONOps;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Service - Core OpenAI wrapper for HoloSphere
|
|
3
|
+
* Provides simple chat completions without Assistants API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import OpenAI from 'openai';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* LLM Service class for interacting with OpenAI
|
|
10
|
+
*/
|
|
11
|
+
export class LLMService {
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} apiKey - OpenAI API key
|
|
14
|
+
* @param {Object} options - Configuration options
|
|
15
|
+
* @param {string} options.model - Model to use (default: 'gpt-4o-mini')
|
|
16
|
+
* @param {number} options.maxTokens - Max tokens (default: 2000)
|
|
17
|
+
* @param {number} options.temperature - Temperature (default: 0.7)
|
|
18
|
+
*/
|
|
19
|
+
constructor(apiKey, options = {}) {
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
throw new Error('OpenAI API key is required');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.openai = new OpenAI({ apiKey });
|
|
25
|
+
this.model = options.model || 'gpt-4o-mini';
|
|
26
|
+
this.maxTokens = options.maxTokens || 2000;
|
|
27
|
+
this.temperature = options.temperature || 0.7;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Send a message to the LLM
|
|
32
|
+
* @param {string} systemPrompt - System prompt
|
|
33
|
+
* @param {string} userMessage - User message
|
|
34
|
+
* @param {Object} options - Override options
|
|
35
|
+
* @returns {Promise<string>} Response content
|
|
36
|
+
*/
|
|
37
|
+
async sendMessage(systemPrompt, userMessage, options = {}) {
|
|
38
|
+
try {
|
|
39
|
+
const response = await this.openai.chat.completions.create({
|
|
40
|
+
model: options.model || this.model,
|
|
41
|
+
messages: [
|
|
42
|
+
{ role: 'system', content: systemPrompt },
|
|
43
|
+
{ role: 'user', content: userMessage }
|
|
44
|
+
],
|
|
45
|
+
max_tokens: options.maxTokens || this.maxTokens,
|
|
46
|
+
temperature: options.temperature ?? this.temperature,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return response.choices[0]?.message?.content?.trim() || '';
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error(`LLM request failed: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Send multiple messages (conversation)
|
|
57
|
+
* @param {Array<{role: string, content: string}>} messages - Messages array
|
|
58
|
+
* @param {Object} options - Override options
|
|
59
|
+
* @returns {Promise<string>} Response content
|
|
60
|
+
*/
|
|
61
|
+
async chat(messages, options = {}) {
|
|
62
|
+
try {
|
|
63
|
+
const response = await this.openai.chat.completions.create({
|
|
64
|
+
model: options.model || this.model,
|
|
65
|
+
messages,
|
|
66
|
+
max_tokens: options.maxTokens || this.maxTokens,
|
|
67
|
+
temperature: options.temperature ?? this.temperature,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return response.choices[0]?.message?.content?.trim() || '';
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error(`LLM chat failed: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Summarize text
|
|
78
|
+
* @param {string} text - Text to summarize
|
|
79
|
+
* @param {Object} options - Options (maxLength, style)
|
|
80
|
+
* @returns {Promise<string>} Summary
|
|
81
|
+
*/
|
|
82
|
+
async summarize(text, options = {}) {
|
|
83
|
+
const systemPrompt = `You are a helpful assistant that summarizes text concisely while preserving key information. Keep summaries clear and focused.${
|
|
84
|
+
options.maxLength ? ` Keep the summary under ${options.maxLength} words.` : ''
|
|
85
|
+
}${options.style ? ` Style: ${options.style}.` : ''}`;
|
|
86
|
+
|
|
87
|
+
return this.sendMessage(systemPrompt, text, { temperature: 0.5 });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Analyze text from a specific perspective
|
|
92
|
+
* @param {string} text - Text to analyze
|
|
93
|
+
* @param {string} aspect - Aspect to analyze (sentiment, themes, tone, etc.)
|
|
94
|
+
* @returns {Promise<string>} Analysis
|
|
95
|
+
*/
|
|
96
|
+
async analyze(text, aspect) {
|
|
97
|
+
const systemPrompt = `You are an expert analyst. Analyze the following text from the perspective of "${aspect}". Provide a clear, structured analysis.`;
|
|
98
|
+
return this.sendMessage(systemPrompt, text, { temperature: 0.3 });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Extract keywords from text
|
|
103
|
+
* @param {string} text - Text to extract keywords from
|
|
104
|
+
* @param {number} maxKeywords - Maximum keywords (default: 10)
|
|
105
|
+
* @returns {Promise<string[]>} Array of keywords
|
|
106
|
+
*/
|
|
107
|
+
async extractKeywords(text, maxKeywords = 10) {
|
|
108
|
+
const systemPrompt = `Extract the ${maxKeywords} most important keywords or key phrases from the text. Return ONLY a JSON array of strings, nothing else. Example: ["keyword1", "keyword2"]`;
|
|
109
|
+
const response = await this.sendMessage(systemPrompt, text, { temperature: 0.2 });
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(response);
|
|
113
|
+
} catch {
|
|
114
|
+
// Try to extract array from response if JSON parsing fails
|
|
115
|
+
const match = response.match(/\[[\s\S]*\]/);
|
|
116
|
+
if (match) {
|
|
117
|
+
return JSON.parse(match[0]);
|
|
118
|
+
}
|
|
119
|
+
return response.split(',').map(k => k.trim().replace(/["\[\]]/g, ''));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Categorize text into given categories
|
|
125
|
+
* @param {string} text - Text to categorize
|
|
126
|
+
* @param {string[]} categories - Available categories
|
|
127
|
+
* @returns {Promise<{category: string, confidence: number, reasoning: string}>}
|
|
128
|
+
*/
|
|
129
|
+
async categorize(text, categories) {
|
|
130
|
+
const systemPrompt = `Categorize the following text into one of these categories: ${categories.join(', ')}.
|
|
131
|
+
Return ONLY a JSON object with: {"category": "chosen_category", "confidence": 0.0-1.0, "reasoning": "brief explanation"}`;
|
|
132
|
+
|
|
133
|
+
const response = await this.sendMessage(systemPrompt, text, { temperature: 0.2 });
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
return JSON.parse(response);
|
|
137
|
+
} catch {
|
|
138
|
+
const match = response.match(/\{[\s\S]*\}/);
|
|
139
|
+
if (match) {
|
|
140
|
+
return JSON.parse(match[0]);
|
|
141
|
+
}
|
|
142
|
+
return { category: categories[0], confidence: 0.5, reasoning: response };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Translate text to target language
|
|
148
|
+
* @param {string} text - Text to translate
|
|
149
|
+
* @param {string} targetLanguage - Target language
|
|
150
|
+
* @returns {Promise<string>} Translated text
|
|
151
|
+
*/
|
|
152
|
+
async translate(text, targetLanguage) {
|
|
153
|
+
const systemPrompt = `Translate the following text to ${targetLanguage}. Return ONLY the translation, nothing else.`;
|
|
154
|
+
return this.sendMessage(systemPrompt, text, { temperature: 0.3 });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Generate questions about content
|
|
159
|
+
* @param {string} text - Text to generate questions about
|
|
160
|
+
* @param {number} count - Number of questions (default: 5)
|
|
161
|
+
* @returns {Promise<string[]>} Array of questions
|
|
162
|
+
*/
|
|
163
|
+
async generateQuestions(text, count = 5) {
|
|
164
|
+
const systemPrompt = `Generate ${count} insightful questions about the following text. Return ONLY a JSON array of question strings, nothing else. Example: ["Question 1?", "Question 2?"]`;
|
|
165
|
+
const response = await this.sendMessage(systemPrompt, text, { temperature: 0.7 });
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
return JSON.parse(response);
|
|
169
|
+
} catch {
|
|
170
|
+
const match = response.match(/\[[\s\S]*\]/);
|
|
171
|
+
if (match) {
|
|
172
|
+
return JSON.parse(match[0]);
|
|
173
|
+
}
|
|
174
|
+
return response.split('\n').filter(q => q.trim().endsWith('?'));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get JSON response from LLM
|
|
180
|
+
* @param {string} systemPrompt - System prompt
|
|
181
|
+
* @param {string} userMessage - User message
|
|
182
|
+
* @param {Object} options - Options
|
|
183
|
+
* @returns {Promise<Object>} Parsed JSON response
|
|
184
|
+
*/
|
|
185
|
+
async getJSON(systemPrompt, userMessage, options = {}) {
|
|
186
|
+
const response = await this.sendMessage(
|
|
187
|
+
systemPrompt + '\n\nReturn ONLY valid JSON, no additional text.',
|
|
188
|
+
userMessage,
|
|
189
|
+
{ ...options, temperature: options.temperature ?? 0.2 }
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
return JSON.parse(response);
|
|
194
|
+
} catch {
|
|
195
|
+
// Try to extract JSON from response
|
|
196
|
+
const match = response.match(/[\[{][\s\S]*[\]}]/);
|
|
197
|
+
if (match) {
|
|
198
|
+
return JSON.parse(match[0]);
|
|
199
|
+
}
|
|
200
|
+
throw new Error(`Failed to parse JSON response: ${response.substring(0, 100)}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default LLMService;
|