@peopl-health/nexus 6.0.0-dev.466 → 6.0.0-dev.611
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/lib/adapters/messageAdapters/TwilioMessageAdapter.js +17 -3
- package/lib/clinical/AssistantProcessor.js +3 -0
- package/lib/clinical/config/divergenceConfig.js +63 -0
- package/lib/clinical/helpers/divergenceHelper.js +100 -0
- package/lib/clinical/helpers/seedHelper.js +62 -0
- package/lib/clinical/index.js +7 -0
- package/lib/clinical/memory/DefaultMemoryManager.js +13 -1
- package/lib/clinical/models/divergenceModel.js +41 -0
- package/lib/clinical/models/turnTraceModel.js +11 -0
- package/lib/clinical/providers/BaseLLMProvider.js +16 -0
- package/lib/clinical/services/clinicalAgentService.js +18 -0
- package/lib/clinical/services/divergenceService.js +74 -0
- package/lib/clinical/services/promptComposerService.js +2 -1
- package/lib/clinical/services/seedService.js +21 -0
- package/lib/clinical/services/shadowMessageService.js +58 -0
- package/lib/clinical/services/shadowService.js +11 -2
- package/lib/clinical/stores/CtcaeCatalog.js +67 -0
- package/lib/controllers/conversationController.js +3 -1
- package/lib/controllers/templateController.js +27 -12
- package/lib/core/BatchingManager.js +2 -2
- package/lib/eval/EvalProvider.js +4 -3
- package/lib/fhir/config/catalogConfig.js +46 -0
- package/lib/fhir/config/connection.js +11 -0
- package/lib/fhir/config/fhirConfig.js +41 -0
- package/lib/fhir/constants/projectionSlugs.js +3 -0
- package/lib/fhir/helpers/elementHelper.js +33 -0
- package/lib/fhir/index.js +21 -0
- package/lib/fhir/models/envelopeModel.js +129 -0
- package/lib/fhir/models/historyModel.js +19 -0
- package/lib/fhir/projections/clinicalMentionProjection.js +65 -0
- package/lib/fhir/projections/registerProjectors.js +14 -0
- package/lib/fhir/resources/Appointment.js +28 -0
- package/lib/fhir/resources/Condition.js +34 -0
- package/lib/fhir/resources/MedicationStatement.js +42 -0
- package/lib/fhir/resources/Observation.js +79 -0
- package/lib/fhir/resources/Procedure.js +30 -0
- package/lib/fhir/resources/Provenance.js +28 -0
- package/lib/fhir/services/clinicalMentionService.js +18 -0
- package/lib/fhir/services/fhirService.js +31 -0
- package/lib/fhir/stores/fhirStore.js +187 -0
- package/lib/helpers/deliveryAttemptHelper.js +5 -13
- package/lib/helpers/projectorHelper.js +18 -0
- package/lib/helpers/twilioMediaHelper.js +25 -1
- package/lib/index.js +14 -0
- package/lib/services/airtableService.js +40 -14
- package/lib/shared/dtos/BaseDto.js +13 -0
- package/lib/shared/dtos/ClinicalFact.js +85 -0
- package/lib/shared/dtos/ClinicalMention.js +53 -0
- package/lib/shared/dtos/ClusterHistoryRecord.js +21 -0
- package/lib/shared/dtos/ClusterImpression.js +40 -0
- package/lib/shared/dtos/ContingencySafetyNet.js +47 -0
- package/lib/shared/dtos/DispatchedEscalation.js +44 -0
- package/lib/shared/dtos/Intervention.js +29 -0
- package/lib/shared/dtos/ManagedSymptom.js +75 -0
- package/lib/shared/dtos/MonitoringEpisode.js +25 -0
- package/lib/shared/dtos/PatientContext.js +22 -0
- package/lib/shared/dtos/PatientRiskAssessment.js +46 -0
- package/lib/shared/dtos/Reminder.js +32 -0
- package/lib/shared/dtos/RoutingDecision.js +68 -0
- package/lib/shared/dtos/SymptomEpisode.js +41 -0
- package/lib/shared/dtos/TreatmentEpisode.js +31 -0
- package/lib/utils/jsonUtils.js +34 -1
- package/lib/utils/vcardParser.js +41 -0
- package/package.json +7 -2
- package/lib/clinical/config/fhirConfig.js +0 -49
- package/lib/clinical/services/fhirService.js +0 -24
- /package/lib/{clinical → fhir}/helpers/fhirHelper.js +0 -0
package/lib/utils/jsonUtils.js
CHANGED
|
@@ -9,6 +9,12 @@ function jsonStringifyWithUnicodeEscapes(obj) {
|
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function isPlainObject(value) {
|
|
13
|
+
if (value === null || typeof value !== 'object') return false;
|
|
14
|
+
const proto = Object.getPrototypeOf(value);
|
|
15
|
+
return proto === Object.prototype || proto === null;
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
function safeParse(value, fallback = {}) {
|
|
13
19
|
try {
|
|
14
20
|
return JSON.parse(value || JSON.stringify(fallback));
|
|
@@ -17,4 +23,31 @@ function safeParse(value, fallback = {}) {
|
|
|
17
23
|
}
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
function safeParseArray(value, fallback = null) {
|
|
27
|
+
const parsed = safeParse(value, fallback);
|
|
28
|
+
return Array.isArray(parsed) ? parsed : fallback;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function cleanForJson(value) {
|
|
32
|
+
if (value === null || value === undefined) return value;
|
|
33
|
+
if (value instanceof Date) return value.toISOString();
|
|
34
|
+
if (typeof value === 'object' && typeof value.toHexString === 'function') return value.toHexString();
|
|
35
|
+
if (Array.isArray(value)) return value.map(cleanForJson);
|
|
36
|
+
if (typeof value === 'object') {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [key, val] of Object.entries(value)) {
|
|
39
|
+
if (key === '_id' || key === '__v') continue;
|
|
40
|
+
out[key] = cleanForJson(val);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
jsonStringifyWithUnicodeEscapes,
|
|
49
|
+
safeParse,
|
|
50
|
+
safeParseArray,
|
|
51
|
+
cleanForJson,
|
|
52
|
+
isPlainObject,
|
|
53
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
function unescapeValue(value) {
|
|
2
|
+
return value.replace(/\\n/gi, '\n').replace(/\\([;,\\])/g, '$1').trim();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function parseVCard(text) {
|
|
6
|
+
const result = { name: null, phones: [] };
|
|
7
|
+
if (!text || typeof text !== 'string') return result;
|
|
8
|
+
|
|
9
|
+
const lines = text.replace(/\r\n[ \t]/g, '').split(/\r?\n/);
|
|
10
|
+
const seen = new Set();
|
|
11
|
+
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
const sep = line.indexOf(':');
|
|
14
|
+
if (sep === -1) continue;
|
|
15
|
+
const name = line.slice(0, sep).split(';')[0].trim().split('.').pop().toUpperCase();
|
|
16
|
+
const value = unescapeValue(line.slice(sep + 1));
|
|
17
|
+
if (!value) continue;
|
|
18
|
+
|
|
19
|
+
if (name === 'FN' && !result.name) {
|
|
20
|
+
result.name = value;
|
|
21
|
+
} else if (name === 'TEL' && !seen.has(value)) {
|
|
22
|
+
seen.add(value);
|
|
23
|
+
result.phones.push(value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function extractContact(text) {
|
|
31
|
+
const { name, phones } = parseVCard(text);
|
|
32
|
+
if (!name && !phones.length) return null;
|
|
33
|
+
const lines = ['*Contacto Compartido*'];
|
|
34
|
+
if (name) lines.push(`Nombre: ${name}`);
|
|
35
|
+
if (phones.length) lines.push(`Número: ${phones.join(', ')}`);
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
extractContact
|
|
41
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peopl-health/nexus",
|
|
3
|
-
"version": "6.0.0-dev.
|
|
3
|
+
"version": "6.0.0-dev.611",
|
|
4
4
|
"description": "Core messaging and assistant library for WhatsApp communication platforms",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"whatsapp",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"./observability": {
|
|
31
31
|
"import": "./lib/observability/index.js",
|
|
32
32
|
"require": "./lib/observability/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./fhir": {
|
|
35
|
+
"import": "./lib/fhir/index.js",
|
|
36
|
+
"require": "./lib/fhir/index.js"
|
|
33
37
|
}
|
|
34
38
|
},
|
|
35
39
|
"main": "lib/index.js",
|
|
@@ -78,7 +82,8 @@
|
|
|
78
82
|
"pino": "10.0.0",
|
|
79
83
|
"pino-pretty": "^10.2.0",
|
|
80
84
|
"sharp": "0.32.6",
|
|
81
|
-
"uuid": "^9.0.0"
|
|
85
|
+
"uuid": "^9.0.0",
|
|
86
|
+
"zod": "^4.4.3"
|
|
82
87
|
},
|
|
83
88
|
"devDependencies": {
|
|
84
89
|
"@types/node": "^20.5.0",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
const { Config_ID } = require('../../config/airtableConfig');
|
|
2
|
-
|
|
3
|
-
const { getRecords } = require('../../services/airtableService');
|
|
4
|
-
|
|
5
|
-
const MapCache = require('../../utils/MapCache');
|
|
6
|
-
const { safeParse } = require('../../utils/jsonUtils');
|
|
7
|
-
|
|
8
|
-
const FHIR_CONFIG_TABLE = 'fhir';
|
|
9
|
-
const CACHE_TTL = 5 * 60 * 1000;
|
|
10
|
-
const CACHE_KEY = 'fhirConfig';
|
|
11
|
-
const REQUIRED_KEYS = ['IDENT_BASE', 'EXT_BASE', 'SYSTEMS'];
|
|
12
|
-
|
|
13
|
-
const cache = new MapCache({ maxSize: 1, ttl: CACHE_TTL });
|
|
14
|
-
|
|
15
|
-
async function load() {
|
|
16
|
-
const cached = cache.get(CACHE_KEY);
|
|
17
|
-
if (cached) return cached;
|
|
18
|
-
|
|
19
|
-
const records = await getRecords(Config_ID, FHIR_CONFIG_TABLE);
|
|
20
|
-
const byKey = Object.fromEntries((records || []).map((r) => [r.config, r.value]));
|
|
21
|
-
|
|
22
|
-
const missing = REQUIRED_KEYS.filter((k) => !byKey[k]);
|
|
23
|
-
if (missing.length) throw new Error(`fhir config missing from Config base: ${missing.join(', ')}`);
|
|
24
|
-
|
|
25
|
-
const systems = safeParse(byKey.SYSTEMS, null);
|
|
26
|
-
if (!systems) throw new Error('fhir config SYSTEMS is not valid JSON');
|
|
27
|
-
|
|
28
|
-
const config = { IDENT_BASE: byKey.IDENT_BASE, EXT_BASE: byKey.EXT_BASE, SYSTEMS: systems };
|
|
29
|
-
cache.set(CACHE_KEY, config);
|
|
30
|
-
return config;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function current() {
|
|
34
|
-
const cached = cache.get(CACHE_KEY);
|
|
35
|
-
if (!cached) throw new Error('fhir config not loaded; call load() first');
|
|
36
|
-
return cached;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function clearCache() {
|
|
40
|
-
cache.clear();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = {
|
|
44
|
-
load,
|
|
45
|
-
clearCache,
|
|
46
|
-
getIdentBase: () => current().IDENT_BASE,
|
|
47
|
-
getExtBase: () => current().EXT_BASE,
|
|
48
|
-
getSystems: () => current().SYSTEMS,
|
|
49
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const { load } = require('../config/fhirConfig');
|
|
2
|
-
|
|
3
|
-
const projectors = new Map();
|
|
4
|
-
|
|
5
|
-
function registerProjector(name, fn) {
|
|
6
|
-
projectors.set(name, fn);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
async function project(items) {
|
|
10
|
-
await load();
|
|
11
|
-
const entry = items
|
|
12
|
-
.flatMap(({ name, aggregate }) => {
|
|
13
|
-
const fn = projectors.get(name);
|
|
14
|
-
if (!fn) throw new Error(`no FHIR projector registered for '${name}'`);
|
|
15
|
-
return (fn(aggregate) || []).flat();
|
|
16
|
-
})
|
|
17
|
-
.map((resource) => ({ resource }));
|
|
18
|
-
return { resourceType: 'Bundle', type: 'collection', entry };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = {
|
|
22
|
-
registerProjector,
|
|
23
|
-
project,
|
|
24
|
-
};
|
|
File without changes
|