@cmdoss/memwal-sdk 0.6.1 → 0.6.2

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 (82) hide show
  1. package/ARCHITECTURE.md +547 -547
  2. package/BENCHMARKS.md +238 -238
  3. package/README.md +181 -181
  4. package/dist/ai-sdk/PDWVectorStore.d.ts.map +1 -1
  5. package/dist/ai-sdk/PDWVectorStore.js +4 -1
  6. package/dist/ai-sdk/PDWVectorStore.js.map +1 -1
  7. package/dist/browser.d.ts +5 -6
  8. package/dist/browser.d.ts.map +1 -1
  9. package/dist/browser.js +7 -6
  10. package/dist/browser.js.map +1 -1
  11. package/dist/client/ClientMemoryManager.d.ts +1 -0
  12. package/dist/client/ClientMemoryManager.d.ts.map +1 -1
  13. package/dist/client/ClientMemoryManager.js +5 -1
  14. package/dist/client/ClientMemoryManager.js.map +1 -1
  15. package/dist/client/PersonalDataWallet.d.ts.map +1 -1
  16. package/dist/client/SimplePDWClient.d.ts +23 -0
  17. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  18. package/dist/client/SimplePDWClient.js +15 -2
  19. package/dist/client/SimplePDWClient.js.map +1 -1
  20. package/dist/client/namespaces/IndexNamespace.d.ts +38 -9
  21. package/dist/client/namespaces/IndexNamespace.d.ts.map +1 -1
  22. package/dist/client/namespaces/IndexNamespace.js +77 -10
  23. package/dist/client/namespaces/IndexNamespace.js.map +1 -1
  24. package/dist/client/namespaces/SearchNamespace.d.ts.map +1 -1
  25. package/dist/client/namespaces/SearchNamespace.js +25 -14
  26. package/dist/client/namespaces/SearchNamespace.js.map +1 -1
  27. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
  28. package/dist/client/namespaces/consolidated/BlockchainNamespace.js +49 -1
  29. package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
  30. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +46 -0
  31. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
  32. package/dist/client/namespaces/consolidated/StorageNamespace.js +34 -0
  33. package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
  34. package/dist/graph/GraphService.js +1 -1
  35. package/dist/permissions/ConsentRepository.browser.d.ts +56 -0
  36. package/dist/permissions/ConsentRepository.browser.d.ts.map +1 -0
  37. package/dist/permissions/ConsentRepository.browser.js +198 -0
  38. package/dist/permissions/ConsentRepository.browser.js.map +1 -0
  39. package/dist/services/GeminiAIService.d.ts.map +1 -1
  40. package/dist/services/GeminiAIService.js +283 -27
  41. package/dist/services/GeminiAIService.js.map +1 -1
  42. package/dist/services/MemoryIndexService.d.ts +31 -2
  43. package/dist/services/MemoryIndexService.d.ts.map +1 -1
  44. package/dist/services/MemoryIndexService.js +75 -3
  45. package/dist/services/MemoryIndexService.js.map +1 -1
  46. package/dist/services/storage/QuiltBatchManager.d.ts +10 -3
  47. package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
  48. package/dist/services/storage/QuiltBatchManager.js +49 -27
  49. package/dist/services/storage/QuiltBatchManager.js.map +1 -1
  50. package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
  51. package/dist/utils/rebuildIndexNode.js +109 -35
  52. package/dist/utils/rebuildIndexNode.js.map +1 -1
  53. package/dist/vector/NodeHnswService.d.ts.map +1 -1
  54. package/dist/vector/NodeHnswService.js +26 -7
  55. package/dist/vector/NodeHnswService.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/access/index.ts +8 -8
  58. package/src/aggregation/index.ts +8 -8
  59. package/src/ai-sdk/PDWVectorStore.ts +4 -1
  60. package/src/browser.ts +15 -10
  61. package/src/client/ClientMemoryManager.ts +6 -1
  62. package/src/client/SimplePDWClient.ts +40 -2
  63. package/src/client/namespaces/IndexNamespace.ts +89 -11
  64. package/src/client/namespaces/SearchNamespace.ts +27 -14
  65. package/src/client/namespaces/consolidated/BlockchainNamespace.ts +55 -1
  66. package/src/client/namespaces/consolidated/StorageNamespace.ts +57 -0
  67. package/src/client/signers/DappKitSigner.ts +207 -207
  68. package/src/generated/pdw/capability.ts +319 -319
  69. package/src/generated/pdw/deps/sui/object.ts +12 -12
  70. package/src/generated/pdw/deps/sui/vec_map.ts +32 -32
  71. package/src/generated/pdw/memory.ts +1087 -1087
  72. package/src/generated/pdw/wallet.ts +123 -123
  73. package/src/generated/utils/index.ts +159 -159
  74. package/src/graph/GraphService.ts +1 -1
  75. package/src/permissions/ConsentRepository.browser.ts +249 -0
  76. package/src/permissions/index.ts +9 -9
  77. package/src/services/GeminiAIService.ts +283 -27
  78. package/src/services/MemoryIndexService.ts +85 -3
  79. package/src/services/storage/QuiltBatchManager.ts +55 -29
  80. package/src/utils/rebuildIndexNode.ts +126 -43
  81. package/src/vector/NodeHnswService.ts +29 -7
  82. package/src/wallet/index.ts +17 -17
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Browser-safe Consent Repository implementations
3
+ *
4
+ * This file contains only browser-compatible implementations.
5
+ * FileSystemConsentRepository is excluded as it requires Node.js fs/promises.
6
+ */
7
+
8
+ import {
9
+ ConsentRequestRecord,
10
+ ConsentStatus,
11
+ } from '../core/types/wallet.js';
12
+ import { normalizeSuiAddress } from '@mysten/sui/utils';
13
+
14
+ export interface ConsentRepository {
15
+ save(request: ConsentRequestRecord): Promise<void>;
16
+ updateStatus(requestId: string, status: ConsentStatus, updatedAt: number): Promise<void>;
17
+ getById(requestId: string): Promise<ConsentRequestRecord | null>;
18
+ listByTarget(targetWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]>;
19
+ listByRequester(requesterWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]>;
20
+ delete(requestId: string): Promise<void>;
21
+ }
22
+
23
+ interface StoredConsentRecord extends ConsentRequestRecord {}
24
+
25
+ function normalizeRecord(record: ConsentRequestRecord): StoredConsentRecord {
26
+ return {
27
+ ...record,
28
+ requesterWallet: normalizeSuiAddress(record.requesterWallet),
29
+ targetWallet: normalizeSuiAddress(record.targetWallet),
30
+ };
31
+ }
32
+
33
+ export class InMemoryConsentRepository implements ConsentRepository {
34
+ private store = new Map<string, StoredConsentRecord>();
35
+
36
+ async save(request: ConsentRequestRecord): Promise<void> {
37
+ const normalized = normalizeRecord(request);
38
+ this.store.set(normalized.requestId, normalized);
39
+ }
40
+
41
+ async updateStatus(requestId: string, status: ConsentStatus, updatedAt: number): Promise<void> {
42
+ const record = this.store.get(requestId);
43
+ if (!record) {
44
+ return;
45
+ }
46
+ this.store.set(requestId, {
47
+ ...record,
48
+ status,
49
+ updatedAt,
50
+ });
51
+ }
52
+
53
+ async getById(requestId: string): Promise<ConsentRequestRecord | null> {
54
+ const record = this.store.get(requestId);
55
+ return record ? { ...record } : null;
56
+ }
57
+
58
+ async listByTarget(targetWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
59
+ const normalizedTarget = normalizeSuiAddress(targetWallet);
60
+ return Array.from(this.store.values())
61
+ .filter((record) => record.targetWallet === normalizedTarget)
62
+ .filter((record) => (status ? record.status === status : true))
63
+ .map((record) => ({ ...record }));
64
+ }
65
+
66
+ async listByRequester(requesterWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
67
+ const normalizedRequester = normalizeSuiAddress(requesterWallet);
68
+ return Array.from(this.store.values())
69
+ .filter((record) => record.requesterWallet === normalizedRequester)
70
+ .filter((record) => (status ? record.status === status : true))
71
+ .map((record) => ({ ...record }));
72
+ }
73
+
74
+ async delete(requestId: string): Promise<void> {
75
+ this.store.delete(requestId);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * IndexedDBConsentRepository - Browser-compatible implementation
81
+ * Uses IndexedDB for persistent storage in browser environments.
82
+ */
83
+ export class IndexedDBConsentRepository implements ConsentRepository {
84
+ private dbName = 'pdw-consent-store';
85
+ private storeName = 'consent-requests';
86
+ private dbVersion = 1;
87
+ private db: IDBDatabase | null = null;
88
+
89
+ constructor(options?: { dbName?: string }) {
90
+ if (options?.dbName) {
91
+ this.dbName = options.dbName;
92
+ }
93
+ }
94
+
95
+ private async getDB(): Promise<IDBDatabase> {
96
+ if (this.db) {
97
+ return this.db;
98
+ }
99
+
100
+ return new Promise((resolve, reject) => {
101
+ const request = indexedDB.open(this.dbName, this.dbVersion);
102
+
103
+ request.onerror = () => reject(request.error);
104
+ request.onsuccess = () => {
105
+ this.db = request.result;
106
+ resolve(request.result);
107
+ };
108
+
109
+ request.onupgradeneeded = (event) => {
110
+ const db = (event.target as IDBOpenDBRequest).result;
111
+ if (!db.objectStoreNames.contains(this.storeName)) {
112
+ const store = db.createObjectStore(this.storeName, { keyPath: 'requestId' });
113
+ store.createIndex('targetWallet', 'targetWallet', { unique: false });
114
+ store.createIndex('requesterWallet', 'requesterWallet', { unique: false });
115
+ store.createIndex('status', 'status', { unique: false });
116
+ }
117
+ };
118
+ });
119
+ }
120
+
121
+ async save(request: ConsentRequestRecord): Promise<void> {
122
+ const db = await this.getDB();
123
+ const normalized = normalizeRecord(request);
124
+
125
+ return new Promise((resolve, reject) => {
126
+ const tx = db.transaction(this.storeName, 'readwrite');
127
+ const store = tx.objectStore(this.storeName);
128
+ const req = store.put(normalized);
129
+
130
+ req.onerror = () => reject(req.error);
131
+ req.onsuccess = () => resolve();
132
+ });
133
+ }
134
+
135
+ async updateStatus(requestId: string, status: ConsentStatus, updatedAt: number): Promise<void> {
136
+ const record = await this.getById(requestId);
137
+ if (!record) {
138
+ return;
139
+ }
140
+
141
+ await this.save({
142
+ ...record,
143
+ status,
144
+ updatedAt,
145
+ });
146
+ }
147
+
148
+ async getById(requestId: string): Promise<ConsentRequestRecord | null> {
149
+ const db = await this.getDB();
150
+
151
+ return new Promise((resolve, reject) => {
152
+ const tx = db.transaction(this.storeName, 'readonly');
153
+ const store = tx.objectStore(this.storeName);
154
+ const req = store.get(requestId);
155
+
156
+ req.onerror = () => reject(req.error);
157
+ req.onsuccess = () => {
158
+ const record = req.result as StoredConsentRecord | undefined;
159
+ resolve(record ? { ...record } : null);
160
+ };
161
+ });
162
+ }
163
+
164
+ async listByTarget(targetWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
165
+ const normalizedTarget = normalizeSuiAddress(targetWallet);
166
+ const db = await this.getDB();
167
+
168
+ return new Promise((resolve, reject) => {
169
+ const tx = db.transaction(this.storeName, 'readonly');
170
+ const store = tx.objectStore(this.storeName);
171
+ const index = store.index('targetWallet');
172
+ const req = index.getAll(normalizedTarget);
173
+
174
+ req.onerror = () => reject(req.error);
175
+ req.onsuccess = () => {
176
+ let records = req.result as StoredConsentRecord[];
177
+ if (status) {
178
+ records = records.filter((r) => r.status === status);
179
+ }
180
+ resolve(records.map((r) => ({ ...r })));
181
+ };
182
+ });
183
+ }
184
+
185
+ async listByRequester(requesterWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
186
+ const normalizedRequester = normalizeSuiAddress(requesterWallet);
187
+ const db = await this.getDB();
188
+
189
+ return new Promise((resolve, reject) => {
190
+ const tx = db.transaction(this.storeName, 'readonly');
191
+ const store = tx.objectStore(this.storeName);
192
+ const index = store.index('requesterWallet');
193
+ const req = index.getAll(normalizedRequester);
194
+
195
+ req.onerror = () => reject(req.error);
196
+ req.onsuccess = () => {
197
+ let records = req.result as StoredConsentRecord[];
198
+ if (status) {
199
+ records = records.filter((r) => r.status === status);
200
+ }
201
+ resolve(records.map((r) => ({ ...r })));
202
+ };
203
+ });
204
+ }
205
+
206
+ async delete(requestId: string): Promise<void> {
207
+ const db = await this.getDB();
208
+
209
+ return new Promise((resolve, reject) => {
210
+ const tx = db.transaction(this.storeName, 'readwrite');
211
+ const store = tx.objectStore(this.storeName);
212
+ const req = store.delete(requestId);
213
+
214
+ req.onerror = () => reject(req.error);
215
+ req.onsuccess = () => resolve();
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Close the database connection
221
+ */
222
+ close(): void {
223
+ if (this.db) {
224
+ this.db.close();
225
+ this.db = null;
226
+ }
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Factory function to create the appropriate ConsentRepository for browser
232
+ */
233
+ export function createBrowserConsentRepository(options?: {
234
+ dbName?: string;
235
+ forceInMemory?: boolean;
236
+ }): ConsentRepository {
237
+ // Force in-memory for testing
238
+ if (options?.forceInMemory) {
239
+ return new InMemoryConsentRepository();
240
+ }
241
+
242
+ // Browser environment - use IndexedDB
243
+ if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') {
244
+ return new IndexedDBConsentRepository({ dbName: options?.dbName });
245
+ }
246
+
247
+ // Fallback to in-memory
248
+ return new InMemoryConsentRepository();
249
+ }
@@ -1,9 +1,9 @@
1
- /**
2
- * Permissions Module
3
- *
4
- * Consent persistence and repository implementations.
5
- */
6
-
7
- export type { ConsentRepository } from './ConsentRepository';
8
- export { FileSystemConsentRepository } from './ConsentRepository';
9
- export { InMemoryConsentRepository } from './ConsentRepository';
1
+ /**
2
+ * Permissions Module
3
+ *
4
+ * Consent persistence and repository implementations.
5
+ */
6
+
7
+ export type { ConsentRepository } from './ConsentRepository';
8
+ export { FileSystemConsentRepository } from './ConsentRepository';
9
+ export { InMemoryConsentRepository } from './ConsentRepository';
@@ -223,35 +223,291 @@ JSON:`;
223
223
  const contextSection = context ? `\nCONTEXT: ${context}\n` : '';
224
224
 
225
225
  return `
226
- Extract meaningful entities and relationships from the following text. Focus on:
227
- - People (names, roles, professions)
228
- - Organizations (companies, institutions, groups)
229
- - Locations (cities, countries, places)
230
- - Concepts (technologies, ideas, skills)
231
- - Events (meetings, projects, activities)
232
- - Objects (products, tools, resources)
233
-
234
- Return a valid JSON response with "entities" and "relationships" arrays.
235
-
236
- For entities:
237
- - "id": unique identifier using snake_case (e.g., "john_doe", "machine_learning")
238
- - "label": human-readable name (e.g., "John Doe", "Machine Learning")
239
- - "type": entity category (person, organization, location, concept, event, object)
240
- - "confidence": confidence score 0.0-1.0
241
- - "properties": optional additional attributes
242
-
243
- For relationships:
244
- - "source": source entity id
245
- - "target": target entity id
246
- - "label": relationship description (e.g., "works_at", "located_in", "uses")
247
- - "confidence": confidence score 0.0-1.0
248
- - "type": optional relationship category
249
-
250
- Only include entities with confidence >= 0.6 and clear, meaningful relationships.
226
+ You are a knowledge graph extraction system for a Personal Data Wallet application. Your task is to extract meaningful entities and relationships from personal memories, notes, and user statements.
227
+
228
+ ## CRITICAL RULE: User Entity
229
+ ALWAYS include a "user" entity to represent the person who wrote this memory:
230
+ {
231
+ "id": "user",
232
+ "label": "User",
233
+ "type": "person",
234
+ "confidence": 1.0
235
+ }
236
+ This "user" entity should be the source or target of relationships describing personal preferences, attributes, or experiences.
237
+
238
+ ## Entity Types (Comprehensive List)
239
+
240
+ ### People & Social
241
+ - **person**: Individual people, including the user themselves
242
+ - Examples: "user", "john_doe", "my_mother", "boss"
243
+ - Properties: name, role, relationship_to_user
244
+
245
+ ### Organizations & Groups
246
+ - **organization**: Companies, institutions, teams, communities
247
+ - Examples: "google", "harvard_university", "local_gym"
248
+ - Properties: industry, size, location
249
+
250
+ ### Locations & Places
251
+ - **location**: Geographic places, addresses, venues
252
+ - Examples: "ho_chi_minh_city", "vietnam", "my_office", "central_park"
253
+ - Properties: type (city/country/venue), coordinates
254
+
255
+ ### Food & Dining
256
+ - **food**: Foods, dishes, cuisines, beverages, ingredients
257
+ - Examples: "spaghetti", "vietnamese_cuisine", "coffee", "chocolate"
258
+ - Properties: cuisine_type, meal_type, dietary_info
259
+ - **restaurant**: Eating establishments
260
+ - Examples: "starbucks", "local_pho_shop"
261
+ - Properties: cuisine, price_range
262
+
263
+ ### Preferences & Interests
264
+ - **preference**: General likes, dislikes, favorites
265
+ - Examples: "blue_color", "morning_routine", "minimalist_style"
266
+ - Properties: sentiment (positive/negative/neutral), intensity (1-10)
267
+ - **hobby**: Recreational activities, pastimes
268
+ - Examples: "playing_guitar", "photography", "hiking", "gaming"
269
+ - Properties: frequency, skill_level
270
+ - **interest**: Topics of curiosity or passion
271
+ - Examples: "artificial_intelligence", "history", "cooking"
272
+ - Properties: depth (casual/moderate/deep)
273
+
274
+ ### Skills & Abilities
275
+ - **skill**: Technical or soft skills, expertise areas
276
+ - Examples: "python_programming", "public_speaking", "cooking"
277
+ - Properties: proficiency (beginner/intermediate/expert)
278
+ - **language**: Languages known or being learned
279
+ - Examples: "english", "vietnamese", "japanese"
280
+ - Properties: proficiency, native (true/false)
281
+
282
+ ### Objects & Possessions
283
+ - **object**: Physical items, products, tools
284
+ - Examples: "macbook_pro", "my_car", "guitar"
285
+ - Properties: brand, model, acquisition_date
286
+ - **digital_product**: Software, apps, digital services
287
+ - Examples: "spotify", "notion", "chatgpt"
288
+ - Properties: category, usage_frequency
289
+
290
+ ### Time & Events
291
+ - **event**: Occasions, milestones, meetings
292
+ - Examples: "birthday_2024", "job_interview", "vacation_trip"
293
+ - Properties: date, duration, importance
294
+ - **routine**: Regular activities or habits
295
+ - Examples: "morning_workout", "weekly_meeting", "daily_meditation"
296
+ - Properties: frequency, time_of_day
297
+
298
+ ### Abstract & Conceptual
299
+ - **concept**: Ideas, topics, abstract things
300
+ - Examples: "work_life_balance", "productivity", "happiness"
301
+ - **goal**: Objectives, aspirations, plans
302
+ - Examples: "learn_japanese", "run_marathon", "save_money"
303
+ - Properties: deadline, priority, status
304
+ - **emotion**: Feelings, moods, emotional states
305
+ - Examples: "happiness", "stress", "excitement"
306
+ - Properties: intensity, trigger
307
+
308
+ ### Health & Wellness
309
+ - **health_condition**: Medical conditions, allergies
310
+ - Examples: "lactose_intolerance", "migraine", "allergy_to_peanuts"
311
+ - **medication**: Medicines, supplements
312
+ - Examples: "vitamin_d", "aspirin"
313
+ - **exercise**: Physical activities for health
314
+ - Examples: "running", "yoga", "weight_training"
315
+
316
+ ### Media & Entertainment
317
+ - **music**: Songs, artists, genres, albums
318
+ - Examples: "jazz_music", "beatles", "classical_piano"
319
+ - **movie**: Films, TV shows, documentaries
320
+ - Examples: "inception", "game_of_thrones"
321
+ - **book**: Books, authors, genres
322
+ - Examples: "atomic_habits", "fiction_genre"
323
+ - **game**: Video games, board games
324
+ - Examples: "chess", "minecraft"
325
+
326
+ ## Relationship Types (Comprehensive List)
327
+
328
+ ### Preference Relationships (source: usually "user")
329
+ - **loves**: Strong positive preference (intensity 9-10)
330
+ - **likes**: Moderate positive preference (intensity 6-8)
331
+ - **enjoys**: Positive experience with something
332
+ - **prefers**: Comparative preference
333
+ - **favorite**: Top choice in a category
334
+ - **interested_in**: Curiosity or engagement
335
+ - **dislikes**: Moderate negative preference
336
+ - **hates**: Strong negative preference (intensity 9-10)
337
+ - **avoids**: Intentionally stays away from
338
+ - **allergic_to**: Medical/physical aversion
339
+
340
+ ### Affiliation Relationships
341
+ - **works_at**: Employment relationship
342
+ - **studies_at**: Educational institution
343
+ - **member_of**: Group membership
344
+ - **belongs_to**: General affiliation
345
+ - **founded**: Created an organization
346
+ - **leads**: Leadership role
347
+
348
+ ### Location Relationships
349
+ - **lives_in**: Current residence
350
+ - **from**: Origin/hometown
351
+ - **located_in**: Physical location
352
+ - **visited**: Past travel
353
+ - **wants_to_visit**: Travel aspiration
354
+
355
+ ### Social Relationships
356
+ - **knows**: Acquaintance
357
+ - **friends_with**: Friendship
358
+ - **family_of**: Family relationship (specify: parent, sibling, child, spouse)
359
+ - **works_with**: Professional relationship
360
+ - **mentored_by**: Learning relationship
361
+
362
+ ### Skill & Knowledge Relationships
363
+ - **has_skill**: Possesses ability
364
+ - **expert_in**: High proficiency
365
+ - **learning**: Currently acquiring
366
+ - **wants_to_learn**: Aspiration to learn
367
+ - **teaches**: Instructing others
368
+ - **certified_in**: Formal qualification
369
+
370
+ ### Possession & Usage
371
+ - **owns**: Ownership
372
+ - **uses**: Regular usage
373
+ - **wants**: Desire to acquire
374
+ - **recommends**: Positive endorsement
375
+
376
+ ### Temporal Relationships
377
+ - **started_on**: Beginning date
378
+ - **ended_on**: Ending date
379
+ - **scheduled_for**: Future event
380
+ - **happens_during**: Temporal context
381
+
382
+ ### Causal & Descriptive
383
+ - **causes**: Causal relationship
384
+ - **related_to**: General association
385
+ - **part_of**: Component relationship
386
+ - **similar_to**: Similarity
387
+ - **opposite_of**: Contrast
388
+
389
+ ## Output Format
390
+
391
+ Return ONLY valid JSON with this structure:
392
+ {
393
+ "entities": [
394
+ {
395
+ "id": "snake_case_identifier",
396
+ "label": "Human Readable Name",
397
+ "type": "entity_type_from_list_above",
398
+ "confidence": 0.0-1.0,
399
+ "properties": { "optional": "attributes" }
400
+ }
401
+ ],
402
+ "relationships": [
403
+ {
404
+ "source": "source_entity_id",
405
+ "target": "target_entity_id",
406
+ "label": "relationship_type_from_list_above",
407
+ "confidence": 0.0-1.0,
408
+ "type": "optional_category"
409
+ }
410
+ ]
411
+ }
412
+
413
+ ## Examples
414
+
415
+ ### Example 1: Food Preference
416
+ Input: "i love spaghetti"
417
+ Output:
418
+ {
419
+ "entities": [
420
+ {"id": "user", "label": "User", "type": "person", "confidence": 1.0},
421
+ {"id": "spaghetti", "label": "Spaghetti", "type": "food", "confidence": 0.95, "properties": {"cuisine": "italian", "meal_type": "main_course"}}
422
+ ],
423
+ "relationships": [
424
+ {"source": "user", "target": "spaghetti", "label": "loves", "confidence": 0.95, "type": "preference"}
425
+ ]
426
+ }
427
+
428
+ ### Example 2: Multiple Preferences
429
+ Input: "i like hamburgers but hate vegetables"
430
+ Output:
431
+ {
432
+ "entities": [
433
+ {"id": "user", "label": "User", "type": "person", "confidence": 1.0},
434
+ {"id": "hamburgers", "label": "Hamburgers", "type": "food", "confidence": 0.95},
435
+ {"id": "vegetables", "label": "Vegetables", "type": "food", "confidence": 0.95}
436
+ ],
437
+ "relationships": [
438
+ {"source": "user", "target": "hamburgers", "label": "likes", "confidence": 0.9, "type": "preference"},
439
+ {"source": "user", "target": "vegetables", "label": "dislikes", "confidence": 0.9, "type": "preference"}
440
+ ]
441
+ }
442
+
443
+ ### Example 3: Work Information
444
+ Input: "i work at Google as a software engineer in Mountain View"
445
+ Output:
446
+ {
447
+ "entities": [
448
+ {"id": "user", "label": "User", "type": "person", "confidence": 1.0, "properties": {"role": "software_engineer"}},
449
+ {"id": "google", "label": "Google", "type": "organization", "confidence": 0.98, "properties": {"industry": "technology"}},
450
+ {"id": "software_engineer", "label": "Software Engineer", "type": "skill", "confidence": 0.9},
451
+ {"id": "mountain_view", "label": "Mountain View", "type": "location", "confidence": 0.95, "properties": {"type": "city"}}
452
+ ],
453
+ "relationships": [
454
+ {"source": "user", "target": "google", "label": "works_at", "confidence": 0.98, "type": "affiliation"},
455
+ {"source": "user", "target": "software_engineer", "label": "has_skill", "confidence": 0.9, "type": "skill"},
456
+ {"source": "google", "target": "mountain_view", "label": "located_in", "confidence": 0.85, "type": "location"}
457
+ ]
458
+ }
459
+
460
+ ### Example 4: Personal Life
461
+ Input: "my name is Aaron and I live in Ho Chi Minh City with my wife"
462
+ Output:
463
+ {
464
+ "entities": [
465
+ {"id": "user", "label": "Aaron", "type": "person", "confidence": 1.0, "properties": {"name": "Aaron"}},
466
+ {"id": "ho_chi_minh_city", "label": "Ho Chi Minh City", "type": "location", "confidence": 0.98, "properties": {"type": "city", "country": "Vietnam"}},
467
+ {"id": "wife", "label": "Wife", "type": "person", "confidence": 0.9, "properties": {"relationship": "spouse"}}
468
+ ],
469
+ "relationships": [
470
+ {"source": "user", "target": "ho_chi_minh_city", "label": "lives_in", "confidence": 0.98, "type": "location"},
471
+ {"source": "user", "target": "wife", "label": "family_of", "confidence": 0.95, "type": "social", "properties": {"relationship_type": "spouse"}}
472
+ ]
473
+ }
474
+
475
+ ### Example 5: Hobbies and Interests
476
+ Input: "i enjoy playing guitar and listening to jazz music on weekends"
477
+ Output:
478
+ {
479
+ "entities": [
480
+ {"id": "user", "label": "User", "type": "person", "confidence": 1.0},
481
+ {"id": "playing_guitar", "label": "Playing Guitar", "type": "hobby", "confidence": 0.95},
482
+ {"id": "guitar", "label": "Guitar", "type": "object", "confidence": 0.9, "properties": {"category": "musical_instrument"}},
483
+ {"id": "jazz_music", "label": "Jazz Music", "type": "music", "confidence": 0.95, "properties": {"genre": "jazz"}},
484
+ {"id": "weekends", "label": "Weekends", "type": "routine", "confidence": 0.8, "properties": {"frequency": "weekly"}}
485
+ ],
486
+ "relationships": [
487
+ {"source": "user", "target": "playing_guitar", "label": "enjoys", "confidence": 0.95, "type": "hobby"},
488
+ {"source": "playing_guitar", "target": "guitar", "label": "uses", "confidence": 0.9, "type": "activity"},
489
+ {"source": "user", "target": "jazz_music", "label": "enjoys", "confidence": 0.9, "type": "preference"},
490
+ {"source": "playing_guitar", "target": "weekends", "label": "happens_during", "confidence": 0.8, "type": "temporal"}
491
+ ]
492
+ }
493
+
494
+ ## Guidelines
495
+
496
+ 1. **Always include "user" entity** for personal statements (first-person: "I", "my", "me")
497
+ 2. **Extract implicit information**: "i'm a doctor" implies medical skill
498
+ 3. **Handle negations properly**: "don't like" = dislikes relationship
499
+ 4. **Capture intensity**: "love" vs "like" vs "enjoy" = different confidence/intensity
500
+ 5. **Include relevant properties**: cuisine type for food, location type for places
501
+ 6. **Create bidirectional relationships when applicable**: if A works_at B, B employs A
502
+ 7. **Minimum confidence threshold**: 0.5 for entities, 0.5 for relationships
503
+ 8. **Be comprehensive**: Extract ALL meaningful entities, even from short texts
504
+ 9. **Handle multilingual content**: Vietnamese, English, etc.
505
+ 10. **Infer entity types from context**: "spaghetti" = food, "Python" = skill/language based on context
251
506
  ${contextSection}
252
- TEXT: ${content}
507
+ ## TEXT TO ANALYZE:
508
+ ${content}
253
509
 
254
- JSON:`;
510
+ ## JSON OUTPUT:`;
255
511
  }
256
512
 
257
513
  private parseExtractionResponse(response: string): { entities: any[]; relationships: any[] } {