deepadata-edm-sdk 0.6.0-alpha → 0.7.0

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 (42) hide show
  1. package/README.md +40 -10
  2. package/dist/assembler.d.ts +73 -4
  3. package/dist/assembler.d.ts.map +1 -1
  4. package/dist/assembler.js +216 -16
  5. package/dist/assembler.js.map +1 -1
  6. package/dist/extractors/domain-extractors.d.ts.map +1 -1
  7. package/dist/extractors/domain-extractors.js +1 -20
  8. package/dist/extractors/domain-extractors.js.map +1 -1
  9. package/dist/extractors/kimi-extractor.d.ts +1 -1
  10. package/dist/extractors/kimi-extractor.d.ts.map +1 -1
  11. package/dist/extractors/kimi-extractor.js +19 -4
  12. package/dist/extractors/kimi-extractor.js.map +1 -1
  13. package/dist/extractors/llm-extractor.d.ts +35 -12
  14. package/dist/extractors/llm-extractor.d.ts.map +1 -1
  15. package/dist/extractors/llm-extractor.js +182 -167
  16. package/dist/extractors/llm-extractor.js.map +1 -1
  17. package/dist/extractors/openai-extractor.d.ts +1 -1
  18. package/dist/extractors/openai-extractor.js +1 -1
  19. package/dist/extractors/profile-prompts.d.ts +10 -7
  20. package/dist/extractors/profile-prompts.d.ts.map +1 -1
  21. package/dist/extractors/profile-prompts.js +83 -118
  22. package/dist/extractors/profile-prompts.js.map +1 -1
  23. package/dist/index.d.ts +13 -11
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +14 -8
  26. package/dist/index.js.map +1 -1
  27. package/dist/schema/edm-schema.d.ts +486 -294
  28. package/dist/schema/edm-schema.d.ts.map +1 -1
  29. package/dist/schema/edm-schema.js +127 -42
  30. package/dist/schema/edm-schema.js.map +1 -1
  31. package/dist/schema/types.d.ts +6 -7
  32. package/dist/schema/types.d.ts.map +1 -1
  33. package/dist/schema/types.js.map +1 -1
  34. package/dist/stateless.d.ts.map +1 -1
  35. package/dist/stateless.js +0 -2
  36. package/dist/stateless.js.map +1 -1
  37. package/dist/validator.d.ts +38 -3
  38. package/dist/validator.d.ts.map +1 -1
  39. package/dist/validator.js +277 -16
  40. package/dist/validator.js.map +1 -1
  41. package/package.json +59 -59
  42. package/LICENSE +0 -21
@@ -1 +1 @@
1
- {"version":3,"file":"kimi-extractor.d.ts","sourceRoot":"","sources":["../../src/extractors/kimi-extractor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,KAAK,EAAsB,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE1F,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAmB5B;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,EACtB,KAAK,GAAE,MAA2B,EAClC,OAAO,GAAE,UAAmB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAkF9B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CA2BxD;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAMvC"}
1
+ {"version":3,"file":"kimi-extractor.d.ts","sourceRoot":"","sources":["../../src/extractors/kimi-extractor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,KAAK,EAAsB,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE1F,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAkC5B;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,EACtB,KAAK,GAAE,MAA2B,EAClC,OAAO,GAAE,UAAmB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAmF9B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CA2BxD;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAMvC"}
@@ -1,11 +1,25 @@
1
1
  /**
2
2
  * Kimi K2 Extractor for EDM v0.6.0
3
3
  * Uses MoonshotAI's Kimi K2 model via OpenAI-compatible API
4
- * Supports profile-aware extraction (core/extended/full)
4
+ * Supports profile-aware extraction (essential/extended/full)
5
5
  */
6
6
  import OpenAI from "openai";
7
- import { LlmExtractedFieldsSchema } from "../schema/edm-schema.js";
7
+ import { LlmExtractedFieldsSchema, LlmEssentialFieldsSchema, LlmExtendedFieldsSchema } from "../schema/edm-schema.js";
8
8
  import { EXTRACTION_SYSTEM_PROMPT, } from "./llm-extractor.js";
9
+ /**
10
+ * Get the appropriate schema for profile-specific validation
11
+ */
12
+ function getProfileSchema(profile) {
13
+ switch (profile) {
14
+ case "essential":
15
+ return LlmEssentialFieldsSchema;
16
+ case "extended":
17
+ return LlmExtendedFieldsSchema;
18
+ case "full":
19
+ default:
20
+ return LlmExtractedFieldsSchema;
21
+ }
22
+ }
9
23
  import { getProfilePrompt, calculateProfileConfidence } from "./profile-prompts.js";
10
24
  /**
11
25
  * Default Kimi K2 model identifier
@@ -78,8 +92,9 @@ export async function extractWithKimi(client, input, model = DEFAULT_KIMI_MODEL,
78
92
  catch {
79
93
  throw new Error(`Failed to parse Kimi response as JSON: ${text.slice(0, 200)}...`);
80
94
  }
81
- // Validate against schema
82
- const result = LlmExtractedFieldsSchema.safeParse(parsed);
95
+ // Validate against profile-specific schema
96
+ const schema = getProfileSchema(profile);
97
+ const result = schema.safeParse(parsed);
83
98
  if (!result.success) {
84
99
  const errorDetails = result.error.errors
85
100
  .map((e) => `${e.path.join(".")}: ${e.message}`)
@@ -1 +1 @@
1
- {"version":3,"file":"kimi-extractor.js","sourceRoot":"","sources":["../../src/extractors/kimi-extractor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kimi-extractor.js","sourceRoot":"","sources":["../../src/extractors/kimi-extractor.ts"],"names":[],"mappings":"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"}
@@ -1,23 +1,46 @@
1
1
  /**
2
- * LLM Extractor for EDM v0.6.0
2
+ * LLM Extractor for EDM v0.7.0
3
3
  * Uses Anthropic Claude to extract emotional data from content
4
- * Supports profile-aware extraction (core/extended/full)
4
+ * Supports profile-aware extraction with profile-specific schema validation
5
5
  */
6
6
  import Anthropic from "@anthropic-ai/sdk";
7
7
  import type { LlmExtractedFields, ExtractionInput, EdmProfile } from "../schema/types.js";
8
8
  /**
9
- * System prompt for EDM extraction - Updated for v0.4.0 canonical schema
10
- * Reconciled field names from system-prompt-B.ts:
11
- * - archetype_energy narrative_archetype
12
- * - meaning_inference expressed_insight
13
- * - transcendent_moment transformational_pivot
14
- * - REMOVED: active_motivational_state, media_context, memory_layers, tether_target, moral_valence
15
- * - reentry_score → recurrence_pattern (type changed: number → enum)
16
- * - ADDED: somatic_signature
9
+ * System prompt for EDM extraction - Updated for v0.7.0 canonical schema
10
+ * v0.7.0 changes:
11
+ * - emotion_primary: added disappointment, relief, frustration; accepts free text
12
+ * - narrative_arc: added loss, confrontation; accepts free text
13
+ * - relational_dynamics: accepts free text
14
+ * - arc_type: new field with 12 canonical values
15
+ * - REMOVED: legacy_embed, alignment_delta
16
+ * - emotional_weight calibration anchors added
17
17
  */
18
- export declare const EXTRACTION_SYSTEM_PROMPT = "\nYou classify emotionally rich memories into a JSON object. Input may include text and an image.\n\nRules\n- Fuse text + image. Treat text as primary; use image only to add grounded specifics (place, event, symbols, people).\n- Keep fields to single words or short phrases (1\u20133 words). Only \"narrative\" is multi-sentence (3\u20135).\n- No invention. If not supported by input, use null.\n- Always include every top-level key and sub-key from the schema, even if the value is null or an empty array.\n- Do not omit fields; if unknown, return null.\n- Output JSON only \u2014 no commentary, markdown, or extra text.\n- If motivation is ambiguous, choose the most conservative option (e.g., \"curiosity\" vs \"fear\") or return null.\n\nCRITICAL: Enum Field Constraints\n- Many fields below are STRICT ENUMS with a fixed set of allowed values.\n- You MUST use ONLY the values listed in the enum sets. Do not invent similar values.\n- Cross-contamination warning: Each enum field has its own distinct value set. Do not use values from one field in another.\n Example: \"milestone\" is valid for memory_type but NOT for narrative_arc.\n Example: \"confront\" is valid for both drive_state and coping_style - check which field you're populating.\n- If none of the allowed enum values adequately capture the expressed content, use the closest match. Do not invent alternatives.\n\nNormalization (very important)\n- Emit lowercase for all string fields except proper names in arrays like associated_people.\n- For array fields (emotion_subtone, recall_triggers, retrieval_keys, nearby_themes, resilience_markers, associated_people):\n \u2022 use short tokens/phrases without punctuation;\n \u2022 avoid duplicates;\n \u2022 prefer singular nouns where reasonable (\"tradition\" not \"traditions\").\n- Never put boolean-like strings (\"true\"/\"false\") into fields that are boolean; use real booleans.\n\nSchema\n{\n \"core\": {\n \"anchor\": \"\", // central theme (e.g., \"dad's toolbox\", \"nana's traditions\")\n \"spark\": \"\", // what triggered the memory (e.g., \"finding the cassette\", \"first snow\")\n \"wound\": \"\", // The specific vulnerability, loss, or pain present \u2014 NOT generic labels like 'loss' or 'grief' but what exactly was lost or why it hurts. Examples: 'unlived travel dream', 'war silence never spoken', 'father died before I knew him', 'shame of not fitting in'. If no wound is present, use null.\n \"fuel\": \"\", // what energized the experience (e.g., \"shared laughter\", \"curiosity\")\n \"bridge\": \"\", // connection between past and present (e.g., \"replaying old tape\", \"returning to the porch\")\n \"echo\": \"\", // what still resonates (e.g., \"her laugh\", \"smell of oil\", \"city lights on water\")\n \"narrative\": \"\" // 3\u20135 sentences; include \u22651 sensory detail, \u22651 temporal cue, and a symbolic callback; faithful and concise\n },\n \"constellation\": {\n \"emotion_primary\": \"\", // STRICT ENUM: joy | sadness | fear | anger | wonder | peace | tenderness | reverence | pride | anxiety | gratitude | longing | hope (pick best-fit from these 13 ONLY)\n \"emotion_subtone\": [], // 2\u20134 short words (e.g., bittersweet, grateful) \u2014 free text array\n \"higher_order_emotion\": \"\", // free text: e.g., awe, forgiveness, pride, moral_elevation (or null)\n \"meta_emotional_state\": \"\", // free text: e.g., acceptance, confusion, curiosity (or null)\n \"interpersonal_affect\": \"\", // free text: e.g., warmth, openness, defensiveness (or null)\n \"narrative_arc\": \"\", // STRICT ENUM: overcoming | transformation | connection | reflection | closure (pick ONE or null)\n \"relational_dynamics\": \"\", // STRICT ENUM: parent_child | romantic_partnership | sibling_bond | family | friendship | companionship | mentorship | reunion | community_ritual | grief | self_reflection | professional | therapeutic | service | adversarial (pick ONE)\n \"temporal_context\": \"\", // STRICT ENUM: childhood | early_adulthood | midlife | late_life | recent | future | timeless (pick ONE or null)\n \"memory_type\": \"\", // STRICT ENUM: legacy_artifact | fleeting_moment | milestone | reflection | formative_experience (pick ONE or null)\n \"media_format\": \"\", // photo, video, audio, text, photo_with_story (or null)\n \"narrative_archetype\": \"\", // STRICT ENUM: hero | caregiver | seeker | sage | lover | outlaw | innocent | magician | creator | everyman | jester | ruler | mentor (pick ONE or null; lowercase)\n \"symbolic_anchor\": \"\", // concrete object/place/ritual (or null)\n \"relational_perspective\": \"\", // STRICT ENUM: self | partner | family | friends | community | humanity (pick ONE or null)\n \"temporal_rhythm\": \"\", // STRICT ENUM: still | sudden | rising | fading | recurring | spiraling | dragging | suspended | looping | cyclic (pick ONE or null)\n \"identity_thread\": \"\", // short sentence\n \"expressed_insight\": \"\", // brief insight explicitly stated by subject (extracted, not inferred)\n \"transformational_pivot\": false, // true if subject explicitly identifies this as life-changing\n \"somatic_signature\": \"\" // bodily sensations explicitly described (e.g., \"chest tightness\", \"warmth spreading\") or null\n },\n \"milky_way\": {\n \"event_type\": \"\", // e.g., family gathering, farewell, birthday (or null)\n \"location_context\": \"\", // place from text or image (or null)\n \"associated_people\": [], // names or roles (proper case allowed)\n \"visibility_context\": \"\", // STRICT ENUM: private | family_only | shared_publicly (pick ONE or null)\n \"tone_shift\": \"\" // e.g., loss to gratitude (or null)\n },\n \"gravity\": {\n \"emotional_weight\": 0.0, // 0.0\u20131.0 (felt intensity IN THE MOMENT)\n \"emotional_density\": \"\", // STRICT ENUM: low | medium | high (pick ONE or null)\n \"valence\": \"\", // STRICT ENUM: positive | negative | mixed (pick ONE or null)\n \"viscosity\": \"\", // STRICT ENUM: low | medium | high | enduring | fluid (pick ONE or null)\n \"gravity_type\": \"\", // short phrase (e.g., symbolic resonance)\n \"tether_type\": \"\", // STRICT ENUM: person | symbol | event | place | ritual | object | tradition | identity | self (pick ONE or null)\n \"recall_triggers\": [], // sensory or symbolic cues (lowercase tokens)\n \"retrieval_keys\": [], // compact hooks (3\u20136 tokens recommended)\n \"nearby_themes\": [], // adjacent concepts\n \"legacy_embed\": false,\n \"recurrence_pattern\": \"\", // STRICT ENUM: cyclical | isolated | chronic | emerging (pick ONE or null)\n \"strength_score\": 0.0, // 0.0\u20131.0 (how BOUND/STUCK this memory is)\n \"temporal_decay\": \"\", // STRICT ENUM: fast | moderate | slow (pick ONE or null)\n \"resilience_markers\": [], // 1\u20133 (e.g., acceptance, optimism, continuity)\n \"adaptation_trajectory\": \"\" // STRICT ENUM: improving | stable | declining | integrative | emerging (pick ONE or null)\n },\n \"impulse\": {\n \"primary_energy\": \"\", // free text: e.g., curiosity, fear, compassion (or null; lowercase)\n \"drive_state\": \"\", // STRICT ENUM: explore | approach | avoid | repair | persevere | share | confront | protect | process (pick ONE or null)\n \"motivational_orientation\": \"\", // STRICT ENUM: belonging | safety | mastery | meaning | autonomy (pick ONE or null)\n \"temporal_focus\": \"\", // STRICT ENUM: past | present | future (pick ONE or null)\n \"directionality\": \"\", // STRICT ENUM: inward | outward | transcendent (pick ONE or null)\n \"social_visibility\": \"\", // STRICT ENUM: private | relational | collective (pick ONE or null)\n \"urgency\": \"\", // STRICT ENUM: calm | elevated | pressing | acute (pick ONE or null)\n \"risk_posture\": \"\", // STRICT ENUM: cautious | balanced | bold (pick ONE or null)\n \"agency_level\": \"\", // STRICT ENUM: low | medium | high (pick ONE or null)\n \"regulation_state\": \"\", // STRICT ENUM: regulated | wavering | dysregulated (pick ONE or null)\n \"attachment_style\": \"\", // STRICT ENUM: secure | anxious | avoidant | disorganized (pick ONE or null)\n \"coping_style\": \"\" // STRICT ENUM: reframe_meaning | seek_support | distract | ritualize | confront | detach | process (pick ONE or null)\n }\n\n // Calibration \u2014 Impulse (helps apply the fields consistently)\n // - temporal_focus: past (reminisce), present (here-and-now coping), future (plans/longing).\n // - directionality: inward (self-processing), outward (toward others), transcendent (beyond self).\n // - social_visibility: private (to self or 1:1), relational (friends/family), collective (community-wide).\n // - If uncertain, choose the most conservative option or null.\n\n // CROSS-CONTAMINATION DISAMBIGUATION (read carefully)\n //\n // temporal_rhythm vs urgency:\n // - temporal_rhythm describes the CADENCE or PACE of time in the memory experience\n // (still, sudden, rising, fading, recurring, spiraling, dragging, suspended, looping, cyclic)\n // - urgency describes the INTENSITY of motivational pressure RIGHT NOW\n // (calm, elevated, pressing, acute)\n // - \"pressing\" belongs ONLY in urgency, NEVER in temporal_rhythm\n //\n // temporal_rhythm vs viscosity:\n // - temporal_rhythm is about TIME MOVEMENT in the memory\n // - viscosity is about EMOTIONAL PERSISTENCE over time\n // (low=fleeting, medium=moderate, high=sticky, enduring=long-lasting, fluid=changeable)\n // - \"enduring\" belongs ONLY in viscosity, NEVER in temporal_rhythm\n //\n // relational_dynamics vs relational_perspective:\n // - relational_dynamics: the TYPE of relationship (parent_child, friendship, mentorship, etc.)\n // - relational_perspective: WHOSE viewpoint the narrative is told from (self, partner, family, etc.)\n // - \"family\" can appear in BOTH fields with different meanings\n //\n // drive_state vs coping_style:\n // - drive_state: the MOTIVATIONAL direction (explore, approach, avoid, confront, etc.)\n // - coping_style: the STRATEGY for managing emotions (reframe_meaning, seek_support, confront, etc.)\n // - \"confront\" is valid in BOTH - use drive_state for action impulse, coping_style for emotion management\n //\n // emotion_primary (STRICT ENUM) vs higher_order_emotion (free text):\n // - emotion_primary MUST be one of the 13 listed values ONLY\n // - Do NOT put free-text emotions like \"compassion\", \"reflection\", \"frustration\" in emotion_primary\n // - Use higher_order_emotion for complex emotions not in the primary list\n //\n // narrative_arc (CRITICAL - common error):\n // - Describes the STORY TRAJECTORY (overcoming, transformation, connection, reflection, closure)\n // - \"confrontation\" is NOT a valid arc \u2014 it describes an event/scene, not a trajectory\n // - If the story involves confronting something, use \"overcoming\" (challenge faced and resolved)\n // or \"transformation\" (fundamental change through conflict)\n // - \"confront\" belongs in drive_state or coping_style, NOT in narrative_arc\n //\n // emotional_weight vs strength_score (CRITICAL - different concepts):\n // - emotional_weight: The felt INTENSITY of the experience in the moment.\n // A heated argument = high weight (0.8). A routine check-in = low weight (0.2).\n // - strength_score: How BOUND/STUCK this memory is \u2014 through association, ritual, retelling, or identity.\n // A childhood memory retold for decades = high strength (0.9) even if emotional weight was moderate.\n // A customer complaint = may have high weight (0.8) but low strength (0.3) \u2014 intense but fades quickly.\n // - These should NOT always correlate.\n // Ask: \"How heavy does this feel RIGHT NOW?\" (weight) vs \"How stuck/persistent is this memory?\" (strength)\n //\n // SYNONYM CORRECTIONS (use the canonical form):\n // - drive_state: Use \"process\" NOT \"reflect\". The enum value is \"process\" for internal processing/reflection.\n // - narrative_archetype: Use \"caregiver\" NOT \"caretaker\". The Jungian archetype label is \"caregiver\".\n}\n";
18
+ export declare const EXTRACTION_SYSTEM_PROMPT = "\nYou classify emotionally rich memories into a JSON object. Input may include text and an image.\n\nRules\n- Fuse text + image. Treat text as primary; use image only to add grounded specifics (place, event, symbols, people).\n- Keep fields to single words or short phrases (1\u20133 words). Only \"narrative\" is multi-sentence (3\u20135).\n- No invention. If not supported by input, use null.\n- Always include every top-level key and sub-key from the schema, even if the value is null or an empty array.\n- Do not omit fields; if unknown, return null.\n- Output JSON only \u2014 no commentary, markdown, or extra text.\n- If motivation is ambiguous, choose the most conservative option (e.g., \"curiosity\" vs \"fear\") or return null.\n\nCRITICAL: Enum Field Constraints\n- Many fields below have CANONICAL values \u2014 preferred values for cross-artifact comparability.\n- Use canonical values where they fit. If no canonical value accurately represents the content, use the most accurate free-text term. Accuracy takes precedence over canonical conformance.\n- Cross-contamination warning: Each enum field has its own distinct value set. Do not use values from one field in another.\n Example: \"milestone\" is valid for memory_type but NOT for narrative_arc.\n Example: \"confront\" is valid for both drive_state and coping_style - check which field you're populating.\n- emotion_primary, narrative_arc, relational_dynamics, and arc_type accept free text if no canonical value fits.\n\nNormalization (very important)\n- Emit lowercase for all string fields except proper names in arrays like associated_people.\n- For array fields (emotion_subtone, recall_triggers, retrieval_keys, nearby_themes, resilience_markers, associated_people):\n \u2022 use short tokens/phrases without punctuation;\n \u2022 avoid duplicates;\n \u2022 prefer singular nouns where reasonable (\"tradition\" not \"traditions\").\n- Never put boolean-like strings (\"true\"/\"false\") into fields that are boolean; use real booleans.\n\nSchema\n{\n \"core\": {\n \"anchor\": \"\", // central theme (e.g., \"dad's toolbox\", \"nana's traditions\")\n \"spark\": \"\", // what triggered the memory (e.g., \"finding the cassette\", \"first snow\")\n \"wound\": \"\", // The specific vulnerability, loss, or pain present \u2014 NOT generic labels like 'loss' or 'grief' but what exactly was lost or why it hurts. Examples: 'unlived travel dream', 'war silence never spoken', 'father died before I knew him', 'shame of not fitting in'. If no wound is present, use null.\n \"fuel\": \"\", // what energized the experience (e.g., \"shared laughter\", \"curiosity\")\n \"bridge\": \"\", // connection between past and present (e.g., \"replaying old tape\", \"returning to the porch\")\n \"echo\": \"\", // what still resonates (e.g., \"her laugh\", \"smell of oil\", \"city lights on water\")\n \"narrative\": \"\" // 3\u20135 sentences. REQUIRED: include ALL of the following \u2014 \u22651 concrete sensory detail (sight, sound, smell, texture), \u22651 temporal cue that anchors the memory in time, \u22651 symbolic callback that connects past to present. Write from the subject's perspective. Do not compress or summarise \u2014 give the memory space to breathe. Faithful and specific. Never generic.\n },\n \"constellation\": {\n \"emotion_primary\": \"\", // CANONICAL: joy | sadness | fear | anger | wonder | peace | tenderness | reverence | pride | anxiety | gratitude | longing | hope | shame | disappointment | relief | frustration (free text accepted if none fits)\n \"emotion_subtone\": [], // 2\u20134 short words (e.g., bittersweet, grateful) \u2014 free text array\n \"higher_order_emotion\": \"\", // free text: e.g., awe, forgiveness, pride, moral_elevation (or null)\n \"meta_emotional_state\": \"\", // free text: e.g., acceptance, confusion, curiosity (or null)\n \"interpersonal_affect\": \"\", // free text: e.g., warmth, openness, defensiveness (or null)\n \"narrative_arc\": \"\", // CANONICAL: overcoming | transformation | connection | reflection | closure | loss | confrontation (free text accepted if none fits)\n \"relational_dynamics\": \"\", // CANONICAL: parent_child | grandparent_grandchild | romantic_partnership | couple | sibling_bond | family | friendship | friend | companionship | colleague | mentorship | reunion | community_ritual | grief | self_reflection | professional | therapeutic | service | adversarial (free text accepted if none fits)\n \"temporal_context\": \"\", // STRICT ENUM: childhood | early_adulthood | midlife | late_life | recent | future | timeless (pick ONE or null)\n \"memory_type\": \"\", // STRICT ENUM: legacy_artifact | fleeting_moment | milestone | reflection | formative_experience (pick ONE or null)\n \"media_format\": \"\", // photo, video, audio, text, photo_with_story (or null)\n \"narrative_archetype\": \"\", // STRICT ENUM: hero | caregiver | seeker | sage | lover | outlaw | innocent | orphan | magician | creator | everyman | jester | ruler | mentor (pick ONE or null; lowercase)\n \"symbolic_anchor\": \"\", // concrete object/place/ritual (or null)\n \"relational_perspective\": \"\", // STRICT ENUM: self | partner | family | friends | community | humanity (pick ONE or null)\n \"temporal_rhythm\": \"\", // STRICT ENUM: still | sudden | rising | fading | recurring | spiraling | dragging | suspended | looping | cyclic (pick ONE or null)\n \"identity_thread\": \"\", // short sentence\n \"expressed_insight\": \"\", // brief insight explicitly stated by subject (extracted, not inferred)\n \"transformational_pivot\": false, // true if subject explicitly identifies this as life-changing\n \"somatic_signature\": \"\", // bodily sensations explicitly described (e.g., \"chest tightness\", \"warmth spreading\") or null\n \"arc_type\": \"\" // CANONICAL: betrayal | liberation | grief | discovery | resistance | bond | moral_awakening | transformation | reconciliation | reckoning | threshold | exile (free text accepted if none fits)\n },\n \"milky_way\": {\n \"event_type\": \"\", // e.g., family gathering, farewell, birthday (or null)\n \"location_context\": \"\", // place from text or image (or null)\n \"associated_people\": [], // names or roles (proper case allowed)\n \"visibility_context\": \"\", // STRICT ENUM: private | family_only | shared_publicly (pick ONE or null)\n \"tone_shift\": \"\" // e.g., loss to gratitude (or null)\n },\n \"gravity\": {\n \"emotional_weight\": 0.0, // 0.0\u20131.0 (felt intensity IN THE MOMENT). Calibration: 0.9+ life-altering irreversible moments; 0.7-0.9 significant personal events with strong emotional response; 0.4-0.7 meaningful but routine emotional experiences; 0.1-0.4 mild passing emotional content\n \"emotional_density\": \"\", // STRICT ENUM: low | medium | high (pick ONE or null)\n \"valence\": \"\", // STRICT ENUM: positive | negative | mixed (pick ONE or null)\n \"viscosity\": \"\", // STRICT ENUM: low | medium | high | enduring | fluid (pick ONE or null)\n \"gravity_type\": \"\", // short phrase (e.g., symbolic resonance)\n \"tether_type\": \"\", // STRICT ENUM: person | symbol | event | place | ritual | object | tradition | identity | self (pick ONE or null)\n \"recall_triggers\": [], // sensory or symbolic cues (lowercase tokens)\n \"retrieval_keys\": [], // compact hooks (3\u20136 tokens recommended)\n \"nearby_themes\": [], // adjacent concepts\n \"recurrence_pattern\": \"\", // STRICT ENUM: cyclical | isolated | chronic | emerging (pick ONE or null)\n \"strength_score\": 0.0, // 0.0\u20131.0 (how BOUND/STUCK this memory is)\n \"temporal_decay\": \"\", // STRICT ENUM: fast | moderate | slow (pick ONE or null)\n \"resilience_markers\": [], // 1\u20133 (e.g., acceptance, optimism, continuity)\n \"adaptation_trajectory\": \"\" // STRICT ENUM: improving | stable | declining | integrative | emerging (pick ONE or null)\n },\n \"impulse\": {\n \"primary_energy\": \"\", // free text: e.g., curiosity, fear, compassion (or null; lowercase)\n \"drive_state\": \"\", // STRICT ENUM: explore | approach | avoid | repair | persevere | share | confront | protect | process (pick ONE or null)\n \"motivational_orientation\": \"\", // STRICT ENUM: belonging | safety | mastery | meaning | autonomy (pick ONE or null)\n \"temporal_focus\": \"\", // STRICT ENUM: past | present | future (pick ONE or null)\n \"directionality\": \"\", // STRICT ENUM: inward | outward | transcendent (pick ONE or null)\n \"social_visibility\": \"\", // STRICT ENUM: private | relational | collective (pick ONE or null)\n \"urgency\": \"\", // STRICT ENUM: calm | elevated | pressing | acute (pick ONE or null)\n \"risk_posture\": \"\", // STRICT ENUM: cautious | balanced | bold (pick ONE or null)\n \"agency_level\": \"\", // STRICT ENUM: low | medium | high (pick ONE or null)\n \"regulation_state\": \"\", // STRICT ENUM: regulated | wavering | dysregulated (pick ONE or null)\n \"attachment_style\": \"\", // STRICT ENUM: secure | anxious | avoidant | disorganized (pick ONE or null)\n \"coping_style\": \"\" // STRICT ENUM: reframe_meaning | seek_support | distract | ritualize | confront | detach | process (pick ONE or null)\n }\n\n // Calibration \u2014 Impulse (helps apply the fields consistently)\n // - temporal_focus: past (reminisce), present (here-and-now coping), future (plans/longing).\n // - directionality: inward (self-processing), outward (toward others), transcendent (beyond self).\n // - social_visibility: private (to self or 1:1), relational (friends/family), collective (community-wide).\n // - If uncertain, choose the most conservative option or null.\n\n // CROSS-CONTAMINATION DISAMBIGUATION (read carefully)\n //\n // temporal_rhythm vs urgency:\n // - temporal_rhythm describes the CADENCE or PACE of time in the memory experience\n // (still, sudden, rising, fading, recurring, spiraling, dragging, suspended, looping, cyclic)\n // - urgency describes the INTENSITY of motivational pressure RIGHT NOW\n // (calm, elevated, pressing, acute)\n // - \"pressing\" belongs ONLY in urgency, NEVER in temporal_rhythm\n //\n // temporal_rhythm vs viscosity:\n // - temporal_rhythm is about TIME MOVEMENT in the memory\n // - viscosity is about EMOTIONAL PERSISTENCE over time\n // (low=fleeting, medium=moderate, high=sticky, enduring=long-lasting, fluid=changeable)\n // - \"enduring\" belongs ONLY in viscosity, NEVER in temporal_rhythm\n //\n // relational_dynamics vs relational_perspective:\n // - relational_dynamics: the TYPE of relationship (parent_child, friendship, mentorship, etc.)\n // - relational_perspective: WHOSE viewpoint the narrative is told from (self, partner, family, etc.)\n // - \"family\" can appear in BOTH fields with different meanings\n //\n // drive_state vs coping_style:\n // - drive_state: the MOTIVATIONAL direction (explore, approach, avoid, confront, etc.)\n // - coping_style: the STRATEGY for managing emotions (reframe_meaning, seek_support, confront, etc.)\n // - \"confront\" is valid in BOTH - use drive_state for action impulse, coping_style for emotion management\n //\n // emotion_primary (STRICT ENUM) vs higher_order_emotion (free text):\n // - emotion_primary MUST be one of the 14 listed values ONLY\n // - Do NOT put free-text emotions like \"compassion\", \"reflection\", \"frustration\" in emotion_primary\n // - Use higher_order_emotion for complex emotions not in the primary list\n //\n // narrative_arc (CRITICAL - common error):\n // - Describes the STORY TRAJECTORY (overcoming, transformation, connection, reflection, closure)\n // - \"confrontation\" is NOT a valid arc \u2014 it describes an event/scene, not a trajectory\n // - If the story involves confronting something, use \"overcoming\" (challenge faced and resolved)\n // or \"transformation\" (fundamental change through conflict)\n // - \"confront\" belongs in drive_state or coping_style, NOT in narrative_arc\n //\n // emotional_weight vs strength_score (CRITICAL - different concepts):\n // - emotional_weight: The felt INTENSITY of the experience in the moment.\n // A heated argument = high weight (0.8). A routine check-in = low weight (0.2).\n // - strength_score: How BOUND/STUCK this memory is \u2014 through association, ritual, retelling, or identity.\n // A childhood memory retold for decades = high strength (0.9) even if emotional weight was moderate.\n // A customer complaint = may have high weight (0.8) but low strength (0.3) \u2014 intense but fades quickly.\n // - These should NOT always correlate.\n // Ask: \"How heavy does this feel RIGHT NOW?\" (weight) vs \"How stuck/persistent is this memory?\" (strength)\n //\n // SYNONYM CORRECTIONS (use the canonical form):\n // - drive_state: Use \"process\" NOT \"reflect\". The enum value is \"process\" for internal processing/reflection.\n // - narrative_archetype: Use \"caregiver\" NOT \"caretaker\". The Jungian archetype label is \"caregiver\".\n}\n";
19
+ /**
20
+ * Profile-specific extracted fields types
21
+ */
22
+ export interface LlmEssentialExtracted {
23
+ core: LlmExtractedFields["core"];
24
+ constellation: {
25
+ emotion_primary: string | null;
26
+ emotion_subtone: string[];
27
+ narrative_arc: string | null;
28
+ };
29
+ }
30
+ export interface LlmExtendedExtracted {
31
+ core: LlmExtractedFields["core"];
32
+ constellation: LlmExtractedFields["constellation"];
33
+ milky_way: LlmExtractedFields["milky_way"];
34
+ gravity: {
35
+ emotional_weight: number;
36
+ valence: string | null;
37
+ tether_type: string | null;
38
+ recurrence_pattern: string | null;
39
+ strength_score: number;
40
+ };
41
+ }
19
42
  export interface LlmExtractionResult {
20
- extracted: LlmExtractedFields;
43
+ extracted: LlmExtractedFields | LlmEssentialExtracted | LlmExtendedExtracted;
21
44
  confidence: number;
22
45
  model: string;
23
46
  profile: EdmProfile;
@@ -1 +1 @@
1
- {"version":3,"file":"llm-extractor.d.ts","sourceRoot":"","sources":["../../src/extractors/llm-extractor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAI1F;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,ijZA0JpC,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,eAAe,EACtB,KAAK,GAAE,MAAmC,EAC1C,OAAO,GAAE,UAAmB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAgF9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,kBAAkB,GAAG,MAAM,CA6CzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAIhE"}
1
+ {"version":3,"file":"llm-extractor.d.ts","sourceRoot":"","sources":["../../src/extractors/llm-extractor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAQ1F;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,w/aA0JpC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,EAAE;QACb,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACnD,SAAS,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAC3C,OAAO,EAAE;QACP,gBAAgB,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CAEH;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,kBAAkB,GAAG,qBAAqB,GAAG,oBAAoB,CAAC;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAiBD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,eAAe,EACtB,KAAK,GAAE,MAAmC,EAC1C,OAAO,GAAE,UAAmB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAiF9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,kBAAkB,GAAG,MAAM,CA6CzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAIhE"}
@@ -1,176 +1,190 @@
1
1
  /**
2
- * LLM Extractor for EDM v0.6.0
2
+ * LLM Extractor for EDM v0.7.0
3
3
  * Uses Anthropic Claude to extract emotional data from content
4
- * Supports profile-aware extraction (core/extended/full)
4
+ * Supports profile-aware extraction with profile-specific schema validation
5
5
  */
6
6
  import Anthropic from "@anthropic-ai/sdk";
7
- import { LlmExtractedFieldsSchema } from "../schema/edm-schema.js";
7
+ import { LlmExtractedFieldsSchema, LlmEssentialFieldsSchema, LlmExtendedFieldsSchema, } from "../schema/edm-schema.js";
8
8
  import { getProfilePrompt, calculateProfileConfidence } from "./profile-prompts.js";
9
9
  /**
10
- * System prompt for EDM extraction - Updated for v0.4.0 canonical schema
11
- * Reconciled field names from system-prompt-B.ts:
12
- * - archetype_energy narrative_archetype
13
- * - meaning_inference expressed_insight
14
- * - transcendent_moment transformational_pivot
15
- * - REMOVED: active_motivational_state, media_context, memory_layers, tether_target, moral_valence
16
- * - reentry_score → recurrence_pattern (type changed: number → enum)
17
- * - ADDED: somatic_signature
10
+ * System prompt for EDM extraction - Updated for v0.7.0 canonical schema
11
+ * v0.7.0 changes:
12
+ * - emotion_primary: added disappointment, relief, frustration; accepts free text
13
+ * - narrative_arc: added loss, confrontation; accepts free text
14
+ * - relational_dynamics: accepts free text
15
+ * - arc_type: new field with 12 canonical values
16
+ * - REMOVED: legacy_embed, alignment_delta
17
+ * - emotional_weight calibration anchors added
18
18
  */
19
- export const EXTRACTION_SYSTEM_PROMPT = `
20
- You classify emotionally rich memories into a JSON object. Input may include text and an image.
21
-
22
- Rules
23
- - Fuse text + image. Treat text as primary; use image only to add grounded specifics (place, event, symbols, people).
24
- - Keep fields to single words or short phrases (1–3 words). Only "narrative" is multi-sentence (3–5).
25
- - No invention. If not supported by input, use null.
26
- - Always include every top-level key and sub-key from the schema, even if the value is null or an empty array.
27
- - Do not omit fields; if unknown, return null.
28
- - Output JSON only — no commentary, markdown, or extra text.
29
- - If motivation is ambiguous, choose the most conservative option (e.g., "curiosity" vs "fear") or return null.
30
-
31
- CRITICAL: Enum Field Constraints
32
- - Many fields below are STRICT ENUMS with a fixed set of allowed values.
33
- - You MUST use ONLY the values listed in the enum sets. Do not invent similar values.
34
- - Cross-contamination warning: Each enum field has its own distinct value set. Do not use values from one field in another.
35
- Example: "milestone" is valid for memory_type but NOT for narrative_arc.
36
- Example: "confront" is valid for both drive_state and coping_style - check which field you're populating.
37
- - If none of the allowed enum values adequately capture the expressed content, use the closest match. Do not invent alternatives.
38
-
39
- Normalization (very important)
40
- - Emit lowercase for all string fields except proper names in arrays like associated_people.
41
- - For array fields (emotion_subtone, recall_triggers, retrieval_keys, nearby_themes, resilience_markers, associated_people):
42
- • use short tokens/phrases without punctuation;
43
- • avoid duplicates;
44
- • prefer singular nouns where reasonable ("tradition" not "traditions").
45
- - Never put boolean-like strings ("true"/"false") into fields that are boolean; use real booleans.
46
-
47
- Schema
48
- {
49
- "core": {
50
- "anchor": "", // central theme (e.g., "dad's toolbox", "nana's traditions")
51
- "spark": "", // what triggered the memory (e.g., "finding the cassette", "first snow")
52
- "wound": "", // The specific vulnerability, loss, or pain present — NOT generic labels like 'loss' or 'grief' but what exactly was lost or why it hurts. Examples: 'unlived travel dream', 'war silence never spoken', 'father died before I knew him', 'shame of not fitting in'. If no wound is present, use null.
53
- "fuel": "", // what energized the experience (e.g., "shared laughter", "curiosity")
54
- "bridge": "", // connection between past and present (e.g., "replaying old tape", "returning to the porch")
55
- "echo": "", // what still resonates (e.g., "her laugh", "smell of oil", "city lights on water")
56
- "narrative": "" // 3–5 sentences; include ≥1 sensory detail, ≥1 temporal cue, and a symbolic callback; faithful and concise
57
- },
58
- "constellation": {
59
- "emotion_primary": "", // STRICT ENUM: joy | sadness | fear | anger | wonder | peace | tenderness | reverence | pride | anxiety | gratitude | longing | hope (pick best-fit from these 13 ONLY)
60
- "emotion_subtone": [], // 2–4 short words (e.g., bittersweet, grateful) — free text array
61
- "higher_order_emotion": "", // free text: e.g., awe, forgiveness, pride, moral_elevation (or null)
62
- "meta_emotional_state": "", // free text: e.g., acceptance, confusion, curiosity (or null)
63
- "interpersonal_affect": "", // free text: e.g., warmth, openness, defensiveness (or null)
64
- "narrative_arc": "", // STRICT ENUM: overcoming | transformation | connection | reflection | closure (pick ONE or null)
65
- "relational_dynamics": "", // STRICT ENUM: parent_child | romantic_partnership | sibling_bond | family | friendship | companionship | mentorship | reunion | community_ritual | grief | self_reflection | professional | therapeutic | service | adversarial (pick ONE)
66
- "temporal_context": "", // STRICT ENUM: childhood | early_adulthood | midlife | late_life | recent | future | timeless (pick ONE or null)
67
- "memory_type": "", // STRICT ENUM: legacy_artifact | fleeting_moment | milestone | reflection | formative_experience (pick ONE or null)
68
- "media_format": "", // photo, video, audio, text, photo_with_story (or null)
69
- "narrative_archetype": "", // STRICT ENUM: hero | caregiver | seeker | sage | lover | outlaw | innocent | magician | creator | everyman | jester | ruler | mentor (pick ONE or null; lowercase)
70
- "symbolic_anchor": "", // concrete object/place/ritual (or null)
71
- "relational_perspective": "", // STRICT ENUM: self | partner | family | friends | community | humanity (pick ONE or null)
72
- "temporal_rhythm": "", // STRICT ENUM: still | sudden | rising | fading | recurring | spiraling | dragging | suspended | looping | cyclic (pick ONE or null)
73
- "identity_thread": "", // short sentence
74
- "expressed_insight": "", // brief insight explicitly stated by subject (extracted, not inferred)
75
- "transformational_pivot": false, // true if subject explicitly identifies this as life-changing
76
- "somatic_signature": "" // bodily sensations explicitly described (e.g., "chest tightness", "warmth spreading") or null
77
- },
78
- "milky_way": {
79
- "event_type": "", // e.g., family gathering, farewell, birthday (or null)
80
- "location_context": "", // place from text or image (or null)
81
- "associated_people": [], // names or roles (proper case allowed)
82
- "visibility_context": "", // STRICT ENUM: private | family_only | shared_publicly (pick ONE or null)
83
- "tone_shift": "" // e.g., loss to gratitude (or null)
84
- },
85
- "gravity": {
86
- "emotional_weight": 0.0, // 0.0–1.0 (felt intensity IN THE MOMENT)
87
- "emotional_density": "", // STRICT ENUM: low | medium | high (pick ONE or null)
88
- "valence": "", // STRICT ENUM: positive | negative | mixed (pick ONE or null)
89
- "viscosity": "", // STRICT ENUM: low | medium | high | enduring | fluid (pick ONE or null)
90
- "gravity_type": "", // short phrase (e.g., symbolic resonance)
91
- "tether_type": "", // STRICT ENUM: person | symbol | event | place | ritual | object | tradition | identity | self (pick ONE or null)
92
- "recall_triggers": [], // sensory or symbolic cues (lowercase tokens)
93
- "retrieval_keys": [], // compact hooks (3–6 tokens recommended)
94
- "nearby_themes": [], // adjacent concepts
95
- "legacy_embed": false,
96
- "recurrence_pattern": "", // STRICT ENUM: cyclical | isolated | chronic | emerging (pick ONE or null)
97
- "strength_score": 0.0, // 0.0–1.0 (how BOUND/STUCK this memory is)
98
- "temporal_decay": "", // STRICT ENUM: fast | moderate | slow (pick ONE or null)
99
- "resilience_markers": [], // 1–3 (e.g., acceptance, optimism, continuity)
100
- "adaptation_trajectory": "" // STRICT ENUM: improving | stable | declining | integrative | emerging (pick ONE or null)
101
- },
102
- "impulse": {
103
- "primary_energy": "", // free text: e.g., curiosity, fear, compassion (or null; lowercase)
104
- "drive_state": "", // STRICT ENUM: explore | approach | avoid | repair | persevere | share | confront | protect | process (pick ONE or null)
105
- "motivational_orientation": "", // STRICT ENUM: belonging | safety | mastery | meaning | autonomy (pick ONE or null)
106
- "temporal_focus": "", // STRICT ENUM: past | present | future (pick ONE or null)
107
- "directionality": "", // STRICT ENUM: inward | outward | transcendent (pick ONE or null)
108
- "social_visibility": "", // STRICT ENUM: private | relational | collective (pick ONE or null)
109
- "urgency": "", // STRICT ENUM: calm | elevated | pressing | acute (pick ONE or null)
110
- "risk_posture": "", // STRICT ENUM: cautious | balanced | bold (pick ONE or null)
111
- "agency_level": "", // STRICT ENUM: low | medium | high (pick ONE or null)
112
- "regulation_state": "", // STRICT ENUM: regulated | wavering | dysregulated (pick ONE or null)
113
- "attachment_style": "", // STRICT ENUM: secure | anxious | avoidant | disorganized (pick ONE or null)
114
- "coping_style": "" // STRICT ENUM: reframe_meaning | seek_support | distract | ritualize | confront | detach | process (pick ONE or null)
115
- }
116
-
117
- // Calibration — Impulse (helps apply the fields consistently)
118
- // - temporal_focus: past (reminisce), present (here-and-now coping), future (plans/longing).
119
- // - directionality: inward (self-processing), outward (toward others), transcendent (beyond self).
120
- // - social_visibility: private (to self or 1:1), relational (friends/family), collective (community-wide).
121
- // - If uncertain, choose the most conservative option or null.
122
-
123
- // CROSS-CONTAMINATION DISAMBIGUATION (read carefully)
124
- //
125
- // temporal_rhythm vs urgency:
126
- // - temporal_rhythm describes the CADENCE or PACE of time in the memory experience
127
- // (still, sudden, rising, fading, recurring, spiraling, dragging, suspended, looping, cyclic)
128
- // - urgency describes the INTENSITY of motivational pressure RIGHT NOW
129
- // (calm, elevated, pressing, acute)
130
- // - "pressing" belongs ONLY in urgency, NEVER in temporal_rhythm
131
- //
132
- // temporal_rhythm vs viscosity:
133
- // - temporal_rhythm is about TIME MOVEMENT in the memory
134
- // - viscosity is about EMOTIONAL PERSISTENCE over time
135
- // (low=fleeting, medium=moderate, high=sticky, enduring=long-lasting, fluid=changeable)
136
- // - "enduring" belongs ONLY in viscosity, NEVER in temporal_rhythm
137
- //
138
- // relational_dynamics vs relational_perspective:
139
- // - relational_dynamics: the TYPE of relationship (parent_child, friendship, mentorship, etc.)
140
- // - relational_perspective: WHOSE viewpoint the narrative is told from (self, partner, family, etc.)
141
- // - "family" can appear in BOTH fields with different meanings
142
- //
143
- // drive_state vs coping_style:
144
- // - drive_state: the MOTIVATIONAL direction (explore, approach, avoid, confront, etc.)
145
- // - coping_style: the STRATEGY for managing emotions (reframe_meaning, seek_support, confront, etc.)
146
- // - "confront" is valid in BOTH - use drive_state for action impulse, coping_style for emotion management
147
- //
148
- // emotion_primary (STRICT ENUM) vs higher_order_emotion (free text):
149
- // - emotion_primary MUST be one of the 13 listed values ONLY
150
- // - Do NOT put free-text emotions like "compassion", "reflection", "frustration" in emotion_primary
151
- // - Use higher_order_emotion for complex emotions not in the primary list
152
- //
153
- // narrative_arc (CRITICAL - common error):
154
- // - Describes the STORY TRAJECTORY (overcoming, transformation, connection, reflection, closure)
155
- // - "confrontation" is NOT a valid arc — it describes an event/scene, not a trajectory
156
- // - If the story involves confronting something, use "overcoming" (challenge faced and resolved)
157
- // or "transformation" (fundamental change through conflict)
158
- // - "confront" belongs in drive_state or coping_style, NOT in narrative_arc
159
- //
160
- // emotional_weight vs strength_score (CRITICAL - different concepts):
161
- // - emotional_weight: The felt INTENSITY of the experience in the moment.
162
- // A heated argument = high weight (0.8). A routine check-in = low weight (0.2).
163
- // - strength_score: How BOUND/STUCK this memory is — through association, ritual, retelling, or identity.
164
- // A childhood memory retold for decades = high strength (0.9) even if emotional weight was moderate.
165
- // A customer complaint = may have high weight (0.8) but low strength (0.3) — intense but fades quickly.
166
- // - These should NOT always correlate.
167
- // Ask: "How heavy does this feel RIGHT NOW?" (weight) vs "How stuck/persistent is this memory?" (strength)
168
- //
169
- // SYNONYM CORRECTIONS (use the canonical form):
170
- // - drive_state: Use "process" NOT "reflect". The enum value is "process" for internal processing/reflection.
171
- // - narrative_archetype: Use "caregiver" NOT "caretaker". The Jungian archetype label is "caregiver".
172
- }
19
+ export const EXTRACTION_SYSTEM_PROMPT = `
20
+ You classify emotionally rich memories into a JSON object. Input may include text and an image.
21
+
22
+ Rules
23
+ - Fuse text + image. Treat text as primary; use image only to add grounded specifics (place, event, symbols, people).
24
+ - Keep fields to single words or short phrases (1–3 words). Only "narrative" is multi-sentence (3–5).
25
+ - No invention. If not supported by input, use null.
26
+ - Always include every top-level key and sub-key from the schema, even if the value is null or an empty array.
27
+ - Do not omit fields; if unknown, return null.
28
+ - Output JSON only — no commentary, markdown, or extra text.
29
+ - If motivation is ambiguous, choose the most conservative option (e.g., "curiosity" vs "fear") or return null.
30
+
31
+ CRITICAL: Enum Field Constraints
32
+ - Many fields below have CANONICAL values preferred values for cross-artifact comparability.
33
+ - Use canonical values where they fit. If no canonical value accurately represents the content, use the most accurate free-text term. Accuracy takes precedence over canonical conformance.
34
+ - Cross-contamination warning: Each enum field has its own distinct value set. Do not use values from one field in another.
35
+ Example: "milestone" is valid for memory_type but NOT for narrative_arc.
36
+ Example: "confront" is valid for both drive_state and coping_style - check which field you're populating.
37
+ - emotion_primary, narrative_arc, relational_dynamics, and arc_type accept free text if no canonical value fits.
38
+
39
+ Normalization (very important)
40
+ - Emit lowercase for all string fields except proper names in arrays like associated_people.
41
+ - For array fields (emotion_subtone, recall_triggers, retrieval_keys, nearby_themes, resilience_markers, associated_people):
42
+ • use short tokens/phrases without punctuation;
43
+ • avoid duplicates;
44
+ • prefer singular nouns where reasonable ("tradition" not "traditions").
45
+ - Never put boolean-like strings ("true"/"false") into fields that are boolean; use real booleans.
46
+
47
+ Schema
48
+ {
49
+ "core": {
50
+ "anchor": "", // central theme (e.g., "dad's toolbox", "nana's traditions")
51
+ "spark": "", // what triggered the memory (e.g., "finding the cassette", "first snow")
52
+ "wound": "", // The specific vulnerability, loss, or pain present — NOT generic labels like 'loss' or 'grief' but what exactly was lost or why it hurts. Examples: 'unlived travel dream', 'war silence never spoken', 'father died before I knew him', 'shame of not fitting in'. If no wound is present, use null.
53
+ "fuel": "", // what energized the experience (e.g., "shared laughter", "curiosity")
54
+ "bridge": "", // connection between past and present (e.g., "replaying old tape", "returning to the porch")
55
+ "echo": "", // what still resonates (e.g., "her laugh", "smell of oil", "city lights on water")
56
+ "narrative": "" // 3–5 sentences. REQUIRED: include ALL of the following — ≥1 concrete sensory detail (sight, sound, smell, texture), ≥1 temporal cue that anchors the memory in time, ≥1 symbolic callback that connects past to present. Write from the subject's perspective. Do not compress or summarise — give the memory space to breathe. Faithful and specific. Never generic.
57
+ },
58
+ "constellation": {
59
+ "emotion_primary": "", // CANONICAL: joy | sadness | fear | anger | wonder | peace | tenderness | reverence | pride | anxiety | gratitude | longing | hope | shame | disappointment | relief | frustration (free text accepted if none fits)
60
+ "emotion_subtone": [], // 2–4 short words (e.g., bittersweet, grateful) — free text array
61
+ "higher_order_emotion": "", // free text: e.g., awe, forgiveness, pride, moral_elevation (or null)
62
+ "meta_emotional_state": "", // free text: e.g., acceptance, confusion, curiosity (or null)
63
+ "interpersonal_affect": "", // free text: e.g., warmth, openness, defensiveness (or null)
64
+ "narrative_arc": "", // CANONICAL: overcoming | transformation | connection | reflection | closure | loss | confrontation (free text accepted if none fits)
65
+ "relational_dynamics": "", // CANONICAL: parent_child | grandparent_grandchild | romantic_partnership | couple | sibling_bond | family | friendship | friend | companionship | colleague | mentorship | reunion | community_ritual | grief | self_reflection | professional | therapeutic | service | adversarial (free text accepted if none fits)
66
+ "temporal_context": "", // STRICT ENUM: childhood | early_adulthood | midlife | late_life | recent | future | timeless (pick ONE or null)
67
+ "memory_type": "", // STRICT ENUM: legacy_artifact | fleeting_moment | milestone | reflection | formative_experience (pick ONE or null)
68
+ "media_format": "", // photo, video, audio, text, photo_with_story (or null)
69
+ "narrative_archetype": "", // STRICT ENUM: hero | caregiver | seeker | sage | lover | outlaw | innocent | orphan | magician | creator | everyman | jester | ruler | mentor (pick ONE or null; lowercase)
70
+ "symbolic_anchor": "", // concrete object/place/ritual (or null)
71
+ "relational_perspective": "", // STRICT ENUM: self | partner | family | friends | community | humanity (pick ONE or null)
72
+ "temporal_rhythm": "", // STRICT ENUM: still | sudden | rising | fading | recurring | spiraling | dragging | suspended | looping | cyclic (pick ONE or null)
73
+ "identity_thread": "", // short sentence
74
+ "expressed_insight": "", // brief insight explicitly stated by subject (extracted, not inferred)
75
+ "transformational_pivot": false, // true if subject explicitly identifies this as life-changing
76
+ "somatic_signature": "", // bodily sensations explicitly described (e.g., "chest tightness", "warmth spreading") or null
77
+ "arc_type": "" // CANONICAL: betrayal | liberation | grief | discovery | resistance | bond | moral_awakening | transformation | reconciliation | reckoning | threshold | exile (free text accepted if none fits)
78
+ },
79
+ "milky_way": {
80
+ "event_type": "", // e.g., family gathering, farewell, birthday (or null)
81
+ "location_context": "", // place from text or image (or null)
82
+ "associated_people": [], // names or roles (proper case allowed)
83
+ "visibility_context": "", // STRICT ENUM: private | family_only | shared_publicly (pick ONE or null)
84
+ "tone_shift": "" // e.g., loss to gratitude (or null)
85
+ },
86
+ "gravity": {
87
+ "emotional_weight": 0.0, // 0.0–1.0 (felt intensity IN THE MOMENT). Calibration: 0.9+ life-altering irreversible moments; 0.7-0.9 significant personal events with strong emotional response; 0.4-0.7 meaningful but routine emotional experiences; 0.1-0.4 mild passing emotional content
88
+ "emotional_density": "", // STRICT ENUM: low | medium | high (pick ONE or null)
89
+ "valence": "", // STRICT ENUM: positive | negative | mixed (pick ONE or null)
90
+ "viscosity": "", // STRICT ENUM: low | medium | high | enduring | fluid (pick ONE or null)
91
+ "gravity_type": "", // short phrase (e.g., symbolic resonance)
92
+ "tether_type": "", // STRICT ENUM: person | symbol | event | place | ritual | object | tradition | identity | self (pick ONE or null)
93
+ "recall_triggers": [], // sensory or symbolic cues (lowercase tokens)
94
+ "retrieval_keys": [], // compact hooks (3–6 tokens recommended)
95
+ "nearby_themes": [], // adjacent concepts
96
+ "recurrence_pattern": "", // STRICT ENUM: cyclical | isolated | chronic | emerging (pick ONE or null)
97
+ "strength_score": 0.0, // 0.0–1.0 (how BOUND/STUCK this memory is)
98
+ "temporal_decay": "", // STRICT ENUM: fast | moderate | slow (pick ONE or null)
99
+ "resilience_markers": [], // 1–3 (e.g., acceptance, optimism, continuity)
100
+ "adaptation_trajectory": "" // STRICT ENUM: improving | stable | declining | integrative | emerging (pick ONE or null)
101
+ },
102
+ "impulse": {
103
+ "primary_energy": "", // free text: e.g., curiosity, fear, compassion (or null; lowercase)
104
+ "drive_state": "", // STRICT ENUM: explore | approach | avoid | repair | persevere | share | confront | protect | process (pick ONE or null)
105
+ "motivational_orientation": "", // STRICT ENUM: belonging | safety | mastery | meaning | autonomy (pick ONE or null)
106
+ "temporal_focus": "", // STRICT ENUM: past | present | future (pick ONE or null)
107
+ "directionality": "", // STRICT ENUM: inward | outward | transcendent (pick ONE or null)
108
+ "social_visibility": "", // STRICT ENUM: private | relational | collective (pick ONE or null)
109
+ "urgency": "", // STRICT ENUM: calm | elevated | pressing | acute (pick ONE or null)
110
+ "risk_posture": "", // STRICT ENUM: cautious | balanced | bold (pick ONE or null)
111
+ "agency_level": "", // STRICT ENUM: low | medium | high (pick ONE or null)
112
+ "regulation_state": "", // STRICT ENUM: regulated | wavering | dysregulated (pick ONE or null)
113
+ "attachment_style": "", // STRICT ENUM: secure | anxious | avoidant | disorganized (pick ONE or null)
114
+ "coping_style": "" // STRICT ENUM: reframe_meaning | seek_support | distract | ritualize | confront | detach | process (pick ONE or null)
115
+ }
116
+
117
+ // Calibration — Impulse (helps apply the fields consistently)
118
+ // - temporal_focus: past (reminisce), present (here-and-now coping), future (plans/longing).
119
+ // - directionality: inward (self-processing), outward (toward others), transcendent (beyond self).
120
+ // - social_visibility: private (to self or 1:1), relational (friends/family), collective (community-wide).
121
+ // - If uncertain, choose the most conservative option or null.
122
+
123
+ // CROSS-CONTAMINATION DISAMBIGUATION (read carefully)
124
+ //
125
+ // temporal_rhythm vs urgency:
126
+ // - temporal_rhythm describes the CADENCE or PACE of time in the memory experience
127
+ // (still, sudden, rising, fading, recurring, spiraling, dragging, suspended, looping, cyclic)
128
+ // - urgency describes the INTENSITY of motivational pressure RIGHT NOW
129
+ // (calm, elevated, pressing, acute)
130
+ // - "pressing" belongs ONLY in urgency, NEVER in temporal_rhythm
131
+ //
132
+ // temporal_rhythm vs viscosity:
133
+ // - temporal_rhythm is about TIME MOVEMENT in the memory
134
+ // - viscosity is about EMOTIONAL PERSISTENCE over time
135
+ // (low=fleeting, medium=moderate, high=sticky, enduring=long-lasting, fluid=changeable)
136
+ // - "enduring" belongs ONLY in viscosity, NEVER in temporal_rhythm
137
+ //
138
+ // relational_dynamics vs relational_perspective:
139
+ // - relational_dynamics: the TYPE of relationship (parent_child, friendship, mentorship, etc.)
140
+ // - relational_perspective: WHOSE viewpoint the narrative is told from (self, partner, family, etc.)
141
+ // - "family" can appear in BOTH fields with different meanings
142
+ //
143
+ // drive_state vs coping_style:
144
+ // - drive_state: the MOTIVATIONAL direction (explore, approach, avoid, confront, etc.)
145
+ // - coping_style: the STRATEGY for managing emotions (reframe_meaning, seek_support, confront, etc.)
146
+ // - "confront" is valid in BOTH - use drive_state for action impulse, coping_style for emotion management
147
+ //
148
+ // emotion_primary (STRICT ENUM) vs higher_order_emotion (free text):
149
+ // - emotion_primary MUST be one of the 14 listed values ONLY
150
+ // - Do NOT put free-text emotions like "compassion", "reflection", "frustration" in emotion_primary
151
+ // - Use higher_order_emotion for complex emotions not in the primary list
152
+ //
153
+ // narrative_arc (CRITICAL - common error):
154
+ // - Describes the STORY TRAJECTORY (overcoming, transformation, connection, reflection, closure)
155
+ // - "confrontation" is NOT a valid arc — it describes an event/scene, not a trajectory
156
+ // - If the story involves confronting something, use "overcoming" (challenge faced and resolved)
157
+ // or "transformation" (fundamental change through conflict)
158
+ // - "confront" belongs in drive_state or coping_style, NOT in narrative_arc
159
+ //
160
+ // emotional_weight vs strength_score (CRITICAL - different concepts):
161
+ // - emotional_weight: The felt INTENSITY of the experience in the moment.
162
+ // A heated argument = high weight (0.8). A routine check-in = low weight (0.2).
163
+ // - strength_score: How BOUND/STUCK this memory is — through association, ritual, retelling, or identity.
164
+ // A childhood memory retold for decades = high strength (0.9) even if emotional weight was moderate.
165
+ // A customer complaint = may have high weight (0.8) but low strength (0.3) — intense but fades quickly.
166
+ // - These should NOT always correlate.
167
+ // Ask: "How heavy does this feel RIGHT NOW?" (weight) vs "How stuck/persistent is this memory?" (strength)
168
+ //
169
+ // SYNONYM CORRECTIONS (use the canonical form):
170
+ // - drive_state: Use "process" NOT "reflect". The enum value is "process" for internal processing/reflection.
171
+ // - narrative_archetype: Use "caregiver" NOT "caretaker". The Jungian archetype label is "caregiver".
172
+ }
173
173
  `;
174
+ /**
175
+ * Get the appropriate schema for profile-specific validation
176
+ */
177
+ function getProfileSchema(profile) {
178
+ switch (profile) {
179
+ case "essential":
180
+ return LlmEssentialFieldsSchema;
181
+ case "extended":
182
+ return LlmExtendedFieldsSchema;
183
+ case "full":
184
+ default:
185
+ return LlmExtractedFieldsSchema;
186
+ }
187
+ }
174
188
  /**
175
189
  * Extract EDM fields from content using Anthropic Claude
176
190
  *
@@ -231,8 +245,9 @@ export async function extractWithLlm(client, input, model = "claude-sonnet-4-202
231
245
  catch {
232
246
  throw new Error(`Failed to parse LLM response as JSON: ${textBlock.text.slice(0, 200)}...`);
233
247
  }
234
- // Validate against schema
235
- const result = LlmExtractedFieldsSchema.safeParse(parsed);
248
+ // Validate against profile-specific schema
249
+ const schema = getProfileSchema(profile);
250
+ const result = schema.safeParse(parsed);
236
251
  if (!result.success) {
237
252
  const errorDetails = result.error.errors
238
253
  .map((e) => `${e.path.join(".")}: ${e.message}`)