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.
Files changed (146) hide show
  1. package/.env.example +36 -0
  2. package/.eslintrc.json +16 -0
  3. package/.prettierrc.json +7 -0
  4. package/README.md +483 -367
  5. package/bin/holosphere-activitypub.js +158 -0
  6. package/cleanup-test-data.js +204 -0
  7. package/examples/demo.html +1333 -0
  8. package/examples/example-bot.js +197 -0
  9. package/package.json +47 -87
  10. package/scripts/check-bundle-size.js +54 -0
  11. package/scripts/check-quest-ids.js +77 -0
  12. package/scripts/import-holons.js +578 -0
  13. package/scripts/publish-to-relay.js +101 -0
  14. package/scripts/read-example.js +186 -0
  15. package/scripts/relay-diagnostic.js +59 -0
  16. package/scripts/relay-example.js +179 -0
  17. package/scripts/resync-to-relay.js +245 -0
  18. package/scripts/revert-import.js +196 -0
  19. package/scripts/test-hybrid-mode.js +108 -0
  20. package/scripts/test-local-storage.js +63 -0
  21. package/scripts/test-nostr-direct.js +55 -0
  22. package/scripts/test-read-data.js +45 -0
  23. package/scripts/test-write-read.js +63 -0
  24. package/scripts/verify-import.js +95 -0
  25. package/scripts/verify-relay-data.js +139 -0
  26. package/src/ai/aggregation.js +319 -0
  27. package/src/ai/breakdown.js +511 -0
  28. package/src/ai/classifier.js +217 -0
  29. package/src/ai/council.js +228 -0
  30. package/src/ai/embeddings.js +279 -0
  31. package/src/ai/federation-ai.js +324 -0
  32. package/src/ai/h3-ai.js +955 -0
  33. package/src/ai/index.js +112 -0
  34. package/src/ai/json-ops.js +225 -0
  35. package/src/ai/llm-service.js +205 -0
  36. package/src/ai/nl-query.js +223 -0
  37. package/src/ai/relationships.js +353 -0
  38. package/src/ai/schema-extractor.js +218 -0
  39. package/src/ai/spatial.js +293 -0
  40. package/src/ai/tts.js +194 -0
  41. package/src/content/social-protocols.js +168 -0
  42. package/src/core/holosphere.js +273 -0
  43. package/src/crypto/secp256k1.js +259 -0
  44. package/src/federation/discovery.js +334 -0
  45. package/src/federation/hologram.js +1042 -0
  46. package/src/federation/registry.js +386 -0
  47. package/src/hierarchical/upcast.js +110 -0
  48. package/src/index.js +2669 -0
  49. package/src/schema/validator.js +91 -0
  50. package/src/spatial/h3-operations.js +110 -0
  51. package/src/storage/backend-factory.js +125 -0
  52. package/src/storage/backend-interface.js +142 -0
  53. package/src/storage/backends/activitypub/server.js +653 -0
  54. package/src/storage/backends/activitypub-backend.js +272 -0
  55. package/src/storage/backends/gundb-backend.js +233 -0
  56. package/src/storage/backends/nostr-backend.js +136 -0
  57. package/src/storage/filesystem-storage-browser.js +41 -0
  58. package/src/storage/filesystem-storage.js +138 -0
  59. package/src/storage/global-tables.js +81 -0
  60. package/src/storage/gun-async.js +281 -0
  61. package/src/storage/gun-wrapper.js +221 -0
  62. package/src/storage/indexeddb-storage.js +122 -0
  63. package/src/storage/key-storage-simple.js +76 -0
  64. package/src/storage/key-storage.js +136 -0
  65. package/src/storage/memory-storage.js +59 -0
  66. package/src/storage/migration.js +338 -0
  67. package/src/storage/nostr-async.js +811 -0
  68. package/src/storage/nostr-client.js +939 -0
  69. package/src/storage/nostr-wrapper.js +211 -0
  70. package/src/storage/outbox-queue.js +208 -0
  71. package/src/storage/persistent-storage.js +109 -0
  72. package/src/storage/sync-service.js +164 -0
  73. package/src/subscriptions/manager.js +142 -0
  74. package/test-ai-real-api.js +202 -0
  75. package/tests/unit/ai/aggregation.test.js +295 -0
  76. package/tests/unit/ai/breakdown.test.js +446 -0
  77. package/tests/unit/ai/classifier.test.js +294 -0
  78. package/tests/unit/ai/council.test.js +262 -0
  79. package/tests/unit/ai/embeddings.test.js +384 -0
  80. package/tests/unit/ai/federation-ai.test.js +344 -0
  81. package/tests/unit/ai/h3-ai.test.js +458 -0
  82. package/tests/unit/ai/index.test.js +304 -0
  83. package/tests/unit/ai/json-ops.test.js +307 -0
  84. package/tests/unit/ai/llm-service.test.js +390 -0
  85. package/tests/unit/ai/nl-query.test.js +383 -0
  86. package/tests/unit/ai/relationships.test.js +311 -0
  87. package/tests/unit/ai/schema-extractor.test.js +384 -0
  88. package/tests/unit/ai/spatial.test.js +279 -0
  89. package/tests/unit/ai/tts.test.js +279 -0
  90. package/tests/unit/content.test.js +332 -0
  91. package/tests/unit/contract/core.test.js +88 -0
  92. package/tests/unit/contract/crypto.test.js +198 -0
  93. package/tests/unit/contract/data.test.js +223 -0
  94. package/tests/unit/contract/federation.test.js +181 -0
  95. package/tests/unit/contract/hierarchical.test.js +113 -0
  96. package/tests/unit/contract/schema.test.js +114 -0
  97. package/tests/unit/contract/social.test.js +217 -0
  98. package/tests/unit/contract/spatial.test.js +110 -0
  99. package/tests/unit/contract/subscriptions.test.js +128 -0
  100. package/tests/unit/contract/utils.test.js +159 -0
  101. package/tests/unit/core.test.js +152 -0
  102. package/tests/unit/crypto.test.js +328 -0
  103. package/tests/unit/federation.test.js +234 -0
  104. package/tests/unit/gun-async.test.js +252 -0
  105. package/tests/unit/hierarchical.test.js +399 -0
  106. package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
  107. package/tests/unit/integration/scenario-02-federation.test.js +76 -0
  108. package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
  109. package/tests/unit/integration/scenario-04-validation.test.js +129 -0
  110. package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
  111. package/tests/unit/integration/scenario-06-social.test.js +135 -0
  112. package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
  113. package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
  114. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
  115. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
  116. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
  117. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
  118. package/tests/unit/performance/benchmark.test.js +85 -0
  119. package/tests/unit/schema.test.js +213 -0
  120. package/tests/unit/spatial.test.js +158 -0
  121. package/tests/unit/storage.test.js +195 -0
  122. package/tests/unit/subscriptions.test.js +328 -0
  123. package/tests/unit/test-data-permanence-debug.js +197 -0
  124. package/tests/unit/test-data-permanence.js +340 -0
  125. package/tests/unit/test-key-persistence-fixed.js +148 -0
  126. package/tests/unit/test-key-persistence.js +172 -0
  127. package/tests/unit/test-relay-permanence.js +376 -0
  128. package/tests/unit/test-second-node.js +95 -0
  129. package/tests/unit/test-simple-write.js +89 -0
  130. package/vite.config.js +49 -0
  131. package/vitest.config.js +20 -0
  132. package/FEDERATION.md +0 -213
  133. package/compute.js +0 -298
  134. package/content.js +0 -980
  135. package/federation.js +0 -1234
  136. package/global.js +0 -736
  137. package/hexlib.js +0 -335
  138. package/hologram.js +0 -183
  139. package/holosphere-bundle.esm.js +0 -33256
  140. package/holosphere-bundle.js +0 -33287
  141. package/holosphere-bundle.min.js +0 -39
  142. package/holosphere.d.ts +0 -601
  143. package/holosphere.js +0 -719
  144. package/node.js +0 -246
  145. package/schema.js +0 -139
  146. package/utils.js +0 -302
@@ -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;