deepadata-edm-sdk 0.6.0-alpha
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/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/assembler.d.ts +37 -0
- package/dist/assembler.d.ts.map +1 -0
- package/dist/assembler.js +204 -0
- package/dist/assembler.js.map +1 -0
- package/dist/extractors/domain-extractors.d.ts +8 -0
- package/dist/extractors/domain-extractors.d.ts.map +1 -0
- package/dist/extractors/domain-extractors.js +173 -0
- package/dist/extractors/domain-extractors.js.map +1 -0
- package/dist/extractors/image-analyzer.d.ts +38 -0
- package/dist/extractors/image-analyzer.d.ts.map +1 -0
- package/dist/extractors/image-analyzer.js +101 -0
- package/dist/extractors/image-analyzer.js.map +1 -0
- package/dist/extractors/kimi-extractor.d.ts +22 -0
- package/dist/extractors/kimi-extractor.d.ts.map +1 -0
- package/dist/extractors/kimi-extractor.js +137 -0
- package/dist/extractors/kimi-extractor.js.map +1 -0
- package/dist/extractors/llm-extractor.d.ts +43 -0
- package/dist/extractors/llm-extractor.d.ts.map +1 -0
- package/dist/extractors/llm-extractor.js +295 -0
- package/dist/extractors/llm-extractor.js.map +1 -0
- package/dist/extractors/openai-extractor.d.ts +17 -0
- package/dist/extractors/openai-extractor.d.ts.map +1 -0
- package/dist/extractors/openai-extractor.js +97 -0
- package/dist/extractors/openai-extractor.js.map +1 -0
- package/dist/extractors/profile-prompts.d.ts +32 -0
- package/dist/extractors/profile-prompts.d.ts.map +1 -0
- package/dist/extractors/profile-prompts.js +283 -0
- package/dist/extractors/profile-prompts.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/schema/edm-schema.d.ts +1645 -0
- package/dist/schema/edm-schema.d.ts.map +1 -0
- package/dist/schema/edm-schema.js +345 -0
- package/dist/schema/edm-schema.js.map +1 -0
- package/dist/schema/types.d.ts +94 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +79 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/stateless.d.ts +28 -0
- package/dist/stateless.d.ts.map +1 -0
- package/dist/stateless.js +100 -0
- package/dist/stateless.js.map +1 -0
- package/dist/validator.d.ts +24 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +154 -0
- package/dist/validator.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Extractors for EDM v0.6.0
|
|
3
|
+
* Populates domains not handled by LLM extraction
|
|
4
|
+
* Supports profile-aware artifact assembly
|
|
5
|
+
*/
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// META Domain
|
|
9
|
+
// =============================================================================
|
|
10
|
+
export function createMeta(metadata, sourceType, profile = "full") {
|
|
11
|
+
return {
|
|
12
|
+
id: randomUUID(),
|
|
13
|
+
version: "0.6.0-alpha",
|
|
14
|
+
profile,
|
|
15
|
+
created_at: new Date().toISOString(),
|
|
16
|
+
updated_at: null,
|
|
17
|
+
locale: metadata.locale ?? null,
|
|
18
|
+
owner_user_id: metadata.subjectId ?? null,
|
|
19
|
+
parent_id: metadata.parentId ?? null,
|
|
20
|
+
visibility: metadata.visibility ?? "private",
|
|
21
|
+
pii_tier: metadata.piiTier ?? "moderate",
|
|
22
|
+
source_type: sourceType,
|
|
23
|
+
source_context: null,
|
|
24
|
+
consent_basis: metadata.consentBasis,
|
|
25
|
+
consent_scope: null,
|
|
26
|
+
consent_revoked_at: null,
|
|
27
|
+
tags: metadata.tags ?? [],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// GOVERNANCE Domain
|
|
32
|
+
// =============================================================================
|
|
33
|
+
export function createGovernance(metadata) {
|
|
34
|
+
// Default subject rights based on jurisdiction
|
|
35
|
+
const defaultRights = getDefaultSubjectRights(metadata.jurisdiction ?? null);
|
|
36
|
+
return {
|
|
37
|
+
jurisdiction: metadata.jurisdiction ?? null,
|
|
38
|
+
retention_policy: {
|
|
39
|
+
basis: "user_defined",
|
|
40
|
+
ttl_days: null, // No automatic expiry by default
|
|
41
|
+
on_expiry: "soft_delete",
|
|
42
|
+
},
|
|
43
|
+
subject_rights: defaultRights,
|
|
44
|
+
exportability: "allowed",
|
|
45
|
+
k_anonymity: {
|
|
46
|
+
k: null,
|
|
47
|
+
groups: [],
|
|
48
|
+
},
|
|
49
|
+
policy_labels: determinePolicyLabels(metadata.piiTier),
|
|
50
|
+
masking_rules: [],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function getDefaultSubjectRights(jurisdiction) {
|
|
54
|
+
// GDPR and similar provide strong rights
|
|
55
|
+
if (jurisdiction === "GDPR" || jurisdiction === "LGPD") {
|
|
56
|
+
return {
|
|
57
|
+
portable: true,
|
|
58
|
+
erasable: true,
|
|
59
|
+
explainable: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// CCPA provides most rights
|
|
63
|
+
if (jurisdiction === "CCPA") {
|
|
64
|
+
return {
|
|
65
|
+
portable: true,
|
|
66
|
+
erasable: true,
|
|
67
|
+
explainable: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// HIPAA is strict on health data
|
|
71
|
+
if (jurisdiction === "HIPAA") {
|
|
72
|
+
return {
|
|
73
|
+
portable: true,
|
|
74
|
+
erasable: true,
|
|
75
|
+
explainable: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Default: provide basic rights
|
|
79
|
+
return {
|
|
80
|
+
portable: true,
|
|
81
|
+
erasable: true,
|
|
82
|
+
explainable: false,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function determinePolicyLabels(piiTier) {
|
|
86
|
+
if (!piiTier || piiTier === "none") {
|
|
87
|
+
return ["none"];
|
|
88
|
+
}
|
|
89
|
+
if (piiTier === "extreme" || piiTier === "high") {
|
|
90
|
+
return ["sensitive"];
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
// =============================================================================
|
|
95
|
+
// TELEMETRY Domain
|
|
96
|
+
// =============================================================================
|
|
97
|
+
export function createTelemetry(confidence, model, notes, provider) {
|
|
98
|
+
return {
|
|
99
|
+
entry_confidence: confidence,
|
|
100
|
+
extraction_model: model,
|
|
101
|
+
extraction_provider: provider ?? null,
|
|
102
|
+
extraction_notes: notes,
|
|
103
|
+
alignment_delta: null, // Populated by downstream systems
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// =============================================================================
|
|
107
|
+
// SYSTEM Domain
|
|
108
|
+
// =============================================================================
|
|
109
|
+
export function createSystem() {
|
|
110
|
+
// System domain is populated by downstream systems (embedding, indexing)
|
|
111
|
+
// SDK creates empty structure
|
|
112
|
+
return {
|
|
113
|
+
embeddings: [],
|
|
114
|
+
indices: {
|
|
115
|
+
waypoint_ids: [],
|
|
116
|
+
sector_weights: {
|
|
117
|
+
episodic: 0,
|
|
118
|
+
semantic: 0,
|
|
119
|
+
procedural: 0,
|
|
120
|
+
emotional: 0,
|
|
121
|
+
reflective: 0,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// =============================================================================
|
|
127
|
+
// CROSSWALKS Domain
|
|
128
|
+
// =============================================================================
|
|
129
|
+
export function createCrosswalks(extracted) {
|
|
130
|
+
// Map emotion_primary to Plutchik
|
|
131
|
+
const plutchikMapping = {
|
|
132
|
+
joy: "joy",
|
|
133
|
+
sadness: "sadness",
|
|
134
|
+
fear: "fear",
|
|
135
|
+
anger: "anger",
|
|
136
|
+
wonder: "surprise", // Plutchik uses surprise
|
|
137
|
+
peace: "trust", // Closest Plutchik equivalent
|
|
138
|
+
tenderness: "trust",
|
|
139
|
+
reverence: "trust",
|
|
140
|
+
};
|
|
141
|
+
const emotionPrimary = extracted.constellation.emotion_primary;
|
|
142
|
+
const plutchikPrimary = emotionPrimary ? (plutchikMapping[emotionPrimary] ?? null) : null;
|
|
143
|
+
// Map memory_type to HMD_v2
|
|
144
|
+
const hmdMapping = {
|
|
145
|
+
legacy_artifact: "autobiographical",
|
|
146
|
+
fleeting_moment: "episodic",
|
|
147
|
+
milestone: "flashbulb",
|
|
148
|
+
reflection: "semantic",
|
|
149
|
+
formative_experience: "autobiographical",
|
|
150
|
+
};
|
|
151
|
+
const memoryType = extracted.constellation.memory_type;
|
|
152
|
+
const hmdType = memoryType ? (hmdMapping[memoryType] ?? null) : null;
|
|
153
|
+
return {
|
|
154
|
+
plutchik_primary: plutchikPrimary,
|
|
155
|
+
geneva_emotion_wheel: null, // Requires more complex mapping
|
|
156
|
+
DSM5_specifiers: null, // Should not be auto-populated
|
|
157
|
+
HMD_v2_memory_type: hmdType,
|
|
158
|
+
ISO_27557_labels: null, // Future standard
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// Source Type Detection
|
|
163
|
+
// =============================================================================
|
|
164
|
+
export function detectSourceType(hasText, hasImage) {
|
|
165
|
+
if (hasText && hasImage) {
|
|
166
|
+
return "mixed";
|
|
167
|
+
}
|
|
168
|
+
if (hasImage) {
|
|
169
|
+
return "image";
|
|
170
|
+
}
|
|
171
|
+
return "text";
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=domain-extractors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-extractors.js","sourceRoot":"","sources":["../../src/extractors/domain-extractors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Context Analyzer for EDM v0.4.0
|
|
3
|
+
* Extracts grounding context from images to supplement text extraction
|
|
4
|
+
*/
|
|
5
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
6
|
+
export interface ImageContext {
|
|
7
|
+
/** Detected location or setting */
|
|
8
|
+
location: string | null;
|
|
9
|
+
/** Identified people or roles */
|
|
10
|
+
people: string[];
|
|
11
|
+
/** Symbolic objects or elements */
|
|
12
|
+
symbols: string[];
|
|
13
|
+
/** Event type detected */
|
|
14
|
+
eventType: string | null;
|
|
15
|
+
/** Temporal cues (era, season, time of day) */
|
|
16
|
+
temporalCues: string | null;
|
|
17
|
+
/** Overall emotional tone */
|
|
18
|
+
emotionalTone: string | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Analyze an image to extract grounding context
|
|
22
|
+
*/
|
|
23
|
+
export declare function analyzeImage(client: Anthropic, imageBase64: string, mediaType?: "image/jpeg" | "image/png" | "image/gif" | "image/webp", model?: string): Promise<ImageContext>;
|
|
24
|
+
/**
|
|
25
|
+
* Merge image context into extracted fields
|
|
26
|
+
* Image provides grounding, text takes priority for interpretation
|
|
27
|
+
*/
|
|
28
|
+
export declare function mergeImageContext(extracted: {
|
|
29
|
+
milky_way: {
|
|
30
|
+
location_context: string | null;
|
|
31
|
+
associated_people: string[];
|
|
32
|
+
event_type: string | null;
|
|
33
|
+
};
|
|
34
|
+
constellation: {
|
|
35
|
+
symbolic_anchor: string | null;
|
|
36
|
+
};
|
|
37
|
+
}, imageContext: ImageContext): void;
|
|
38
|
+
//# sourceMappingURL=image-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-analyzer.d.ts","sourceRoot":"","sources":["../../src/extractors/image-analyzer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iCAAiC;IACjC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mCAAmC;IACnC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6BAA6B;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAsBD;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,YAA2B,EACjF,KAAK,GAAE,MAAmC,GACzC,OAAO,CAAC,YAAY,CAAC,CAmDvB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE;IACT,SAAS,EAAE;QAAE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACvG,aAAa,EAAE;QAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACnD,EACD,YAAY,EAAE,YAAY,GACzB,IAAI,CAyBN"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const IMAGE_ANALYSIS_PROMPT = `
|
|
2
|
+
Analyze this image to extract context for an emotional memory. Return ONLY a JSON object with these fields:
|
|
3
|
+
|
|
4
|
+
{
|
|
5
|
+
"location": "place or setting visible (or null)",
|
|
6
|
+
"people": ["list of people/roles visible"],
|
|
7
|
+
"symbols": ["symbolic objects, artifacts, or meaningful elements"],
|
|
8
|
+
"eventType": "type of event if discernible (or null)",
|
|
9
|
+
"temporalCues": "era, season, time indicators (or null)",
|
|
10
|
+
"emotionalTone": "overall mood conveyed by the image (or null)"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Rules:
|
|
14
|
+
- Only extract what is visually evident
|
|
15
|
+
- Do not invent or assume
|
|
16
|
+
- Use lowercase except for proper names
|
|
17
|
+
- Keep descriptions brief (1-3 words each)
|
|
18
|
+
- Return valid JSON only, no commentary
|
|
19
|
+
`;
|
|
20
|
+
/**
|
|
21
|
+
* Analyze an image to extract grounding context
|
|
22
|
+
*/
|
|
23
|
+
export async function analyzeImage(client, imageBase64, mediaType = "image/jpeg", model = "claude-sonnet-4-20250514") {
|
|
24
|
+
const response = await client.messages.create({
|
|
25
|
+
model,
|
|
26
|
+
max_tokens: 1024,
|
|
27
|
+
messages: [
|
|
28
|
+
{
|
|
29
|
+
role: "user",
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: "image",
|
|
33
|
+
source: {
|
|
34
|
+
type: "base64",
|
|
35
|
+
media_type: mediaType,
|
|
36
|
+
data: imageBase64,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: "text",
|
|
41
|
+
text: IMAGE_ANALYSIS_PROMPT,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
48
|
+
if (!textBlock || textBlock.type !== "text") {
|
|
49
|
+
throw new Error("No text response from image analysis");
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(textBlock.text);
|
|
53
|
+
return {
|
|
54
|
+
location: parsed.location ?? null,
|
|
55
|
+
people: Array.isArray(parsed.people) ? parsed.people : [],
|
|
56
|
+
symbols: Array.isArray(parsed.symbols) ? parsed.symbols : [],
|
|
57
|
+
eventType: parsed.eventType ?? null,
|
|
58
|
+
temporalCues: parsed.temporalCues ?? null,
|
|
59
|
+
emotionalTone: parsed.emotionalTone ?? null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Return empty context on parse failure
|
|
64
|
+
return {
|
|
65
|
+
location: null,
|
|
66
|
+
people: [],
|
|
67
|
+
symbols: [],
|
|
68
|
+
eventType: null,
|
|
69
|
+
temporalCues: null,
|
|
70
|
+
emotionalTone: null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Merge image context into extracted fields
|
|
76
|
+
* Image provides grounding, text takes priority for interpretation
|
|
77
|
+
*/
|
|
78
|
+
export function mergeImageContext(extracted, imageContext) {
|
|
79
|
+
// Only fill null fields - text takes priority
|
|
80
|
+
if (!extracted.milky_way.location_context && imageContext.location) {
|
|
81
|
+
extracted.milky_way.location_context = imageContext.location;
|
|
82
|
+
}
|
|
83
|
+
// Merge people lists
|
|
84
|
+
if (imageContext.people.length > 0) {
|
|
85
|
+
const existingLower = extracted.milky_way.associated_people.map((p) => p.toLowerCase());
|
|
86
|
+
for (const person of imageContext.people) {
|
|
87
|
+
if (!existingLower.includes(person.toLowerCase())) {
|
|
88
|
+
extracted.milky_way.associated_people.push(person);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Event type from image if not in text
|
|
93
|
+
if (!extracted.milky_way.event_type && imageContext.eventType) {
|
|
94
|
+
extracted.milky_way.event_type = imageContext.eventType;
|
|
95
|
+
}
|
|
96
|
+
// Symbolic anchor from image symbols
|
|
97
|
+
if (!extracted.constellation.symbolic_anchor && imageContext.symbols.length > 0) {
|
|
98
|
+
extracted.constellation.symbolic_anchor = imageContext.symbols[0] ?? null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=image-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-analyzer.js","sourceRoot":"","sources":["../../src/extractors/image-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi K2 Extractor for EDM v0.6.0
|
|
3
|
+
* Uses MoonshotAI's Kimi K2 model via OpenAI-compatible API
|
|
4
|
+
* Supports profile-aware extraction (core/extended/full)
|
|
5
|
+
*/
|
|
6
|
+
import OpenAI from "openai";
|
|
7
|
+
import type { ExtractionInput, EdmProfile } from "../schema/types.js";
|
|
8
|
+
import { type LlmExtractionResult } from "./llm-extractor.js";
|
|
9
|
+
/**
|
|
10
|
+
* Extract EDM fields from content using Kimi K2
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractWithKimi(client: OpenAI, input: ExtractionInput, model?: string, profile?: EdmProfile): Promise<LlmExtractionResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Create a Kimi client using MoonshotAI's direct API
|
|
15
|
+
* Falls back to OpenRouter if direct API key is not available
|
|
16
|
+
*/
|
|
17
|
+
export declare function createKimiClient(apiKey?: string): OpenAI;
|
|
18
|
+
/**
|
|
19
|
+
* Get the appropriate model ID based on which client is being used
|
|
20
|
+
*/
|
|
21
|
+
export declare function getKimiModelId(): string;
|
|
22
|
+
//# sourceMappingURL=kimi-extractor.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi K2 Extractor for EDM v0.6.0
|
|
3
|
+
* Uses MoonshotAI's Kimi K2 model via OpenAI-compatible API
|
|
4
|
+
* Supports profile-aware extraction (core/extended/full)
|
|
5
|
+
*/
|
|
6
|
+
import OpenAI from "openai";
|
|
7
|
+
import { LlmExtractedFieldsSchema } from "../schema/edm-schema.js";
|
|
8
|
+
import { EXTRACTION_SYSTEM_PROMPT, } from "./llm-extractor.js";
|
|
9
|
+
import { getProfilePrompt, calculateProfileConfidence } from "./profile-prompts.js";
|
|
10
|
+
/**
|
|
11
|
+
* Default Kimi K2 model identifier
|
|
12
|
+
* MoonshotAI exposes this via their OpenAI-compatible endpoint
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_KIMI_MODEL = "kimi-k2-0711-preview";
|
|
15
|
+
/**
|
|
16
|
+
* Kimi API base URLs
|
|
17
|
+
* - Direct: api.moonshot.cn or api.moonshot.ai (requires MOONSHOT_API_KEY or KIMI_API_KEY)
|
|
18
|
+
* - OpenRouter: openrouter.ai (requires OPENROUTER_API_KEY)
|
|
19
|
+
* Set MOONSHOT_BASE_URL env var to override the default
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_KIMI_BASE_URL = "https://api.moonshot.cn/v1";
|
|
22
|
+
const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
23
|
+
const OPENROUTER_KIMI_MODEL = "moonshotai/kimi-k2";
|
|
24
|
+
/**
|
|
25
|
+
* Extract EDM fields from content using Kimi K2
|
|
26
|
+
*/
|
|
27
|
+
export async function extractWithKimi(client, input, model = DEFAULT_KIMI_MODEL, profile = "full") {
|
|
28
|
+
const userContent = [];
|
|
29
|
+
// Add text content
|
|
30
|
+
if (input.text) {
|
|
31
|
+
userContent.push({
|
|
32
|
+
type: "text",
|
|
33
|
+
text: input.text,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Add image if provided (OpenAI-compatible format)
|
|
37
|
+
if (input.image) {
|
|
38
|
+
const mediaType = input.imageMediaType ?? "image/jpeg";
|
|
39
|
+
userContent.push({
|
|
40
|
+
type: "image_url",
|
|
41
|
+
image_url: {
|
|
42
|
+
url: `data:${mediaType};base64,${input.image}`,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// Select profile-specific prompt or use full extraction prompt
|
|
47
|
+
const profilePrompt = getProfilePrompt(profile);
|
|
48
|
+
const systemPrompt = profilePrompt || EXTRACTION_SYSTEM_PROMPT;
|
|
49
|
+
const response = await client.chat.completions.create({
|
|
50
|
+
model,
|
|
51
|
+
max_tokens: 4096,
|
|
52
|
+
messages: [
|
|
53
|
+
{
|
|
54
|
+
role: "system",
|
|
55
|
+
content: systemPrompt,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
role: "user",
|
|
59
|
+
content: userContent,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
// Extract text response
|
|
64
|
+
const text = response.choices[0]?.message?.content;
|
|
65
|
+
if (!text) {
|
|
66
|
+
throw new Error("No text response from Kimi K2");
|
|
67
|
+
}
|
|
68
|
+
// Parse JSON response (strip markdown code fences if present)
|
|
69
|
+
let jsonText = text.trim();
|
|
70
|
+
const fenceMatch = jsonText.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?\s*```$/);
|
|
71
|
+
if (fenceMatch?.[1]) {
|
|
72
|
+
jsonText = fenceMatch[1].trim();
|
|
73
|
+
}
|
|
74
|
+
let parsed;
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(jsonText);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new Error(`Failed to parse Kimi response as JSON: ${text.slice(0, 200)}...`);
|
|
80
|
+
}
|
|
81
|
+
// Validate against schema
|
|
82
|
+
const result = LlmExtractedFieldsSchema.safeParse(parsed);
|
|
83
|
+
if (!result.success) {
|
|
84
|
+
const errorDetails = result.error.errors
|
|
85
|
+
.map((e) => `${e.path.join(".")}: ${e.message}`)
|
|
86
|
+
.join("; ");
|
|
87
|
+
throw new Error(`Kimi response failed schema validation: ${errorDetails}`);
|
|
88
|
+
}
|
|
89
|
+
// Calculate profile-aware confidence
|
|
90
|
+
const confidence = calculateProfileConfidence(result.data, profile);
|
|
91
|
+
return {
|
|
92
|
+
extracted: result.data,
|
|
93
|
+
confidence,
|
|
94
|
+
model,
|
|
95
|
+
profile,
|
|
96
|
+
notes: null,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Create a Kimi client using MoonshotAI's direct API
|
|
101
|
+
* Falls back to OpenRouter if direct API key is not available
|
|
102
|
+
*/
|
|
103
|
+
export function createKimiClient(apiKey) {
|
|
104
|
+
// Try direct MoonshotAI API first
|
|
105
|
+
const directKey = apiKey ?? process.env["MOONSHOT_API_KEY"] ?? process.env["KIMI_API_KEY"];
|
|
106
|
+
if (directKey) {
|
|
107
|
+
const baseURL = process.env["MOONSHOT_BASE_URL"] ?? DEFAULT_KIMI_BASE_URL;
|
|
108
|
+
return new OpenAI({
|
|
109
|
+
apiKey: directKey,
|
|
110
|
+
baseURL,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Fall back to OpenRouter
|
|
114
|
+
const openRouterKey = process.env["OPENROUTER_API_KEY"];
|
|
115
|
+
if (openRouterKey) {
|
|
116
|
+
return new OpenAI({
|
|
117
|
+
apiKey: openRouterKey,
|
|
118
|
+
baseURL: OPENROUTER_BASE_URL,
|
|
119
|
+
defaultHeaders: {
|
|
120
|
+
"HTTP-Referer": "https://deepadata.com",
|
|
121
|
+
"X-Title": "DeepaData EDM Extraction",
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
throw new Error("Kimi API key is required. Set MOONSHOT_API_KEY, KIMI_API_KEY, or OPENROUTER_API_KEY environment variable.");
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get the appropriate model ID based on which client is being used
|
|
129
|
+
*/
|
|
130
|
+
export function getKimiModelId() {
|
|
131
|
+
// If using OpenRouter, use their model identifier
|
|
132
|
+
if (process.env["OPENROUTER_API_KEY"] && !process.env["MOONSHOT_API_KEY"] && !process.env["KIMI_API_KEY"]) {
|
|
133
|
+
return OPENROUTER_KIMI_MODEL;
|
|
134
|
+
}
|
|
135
|
+
return DEFAULT_KIMI_MODEL;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=kimi-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kimi-extractor.js","sourceRoot":"","sources":["../../src/extractors/kimi-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Extractor for EDM v0.6.0
|
|
3
|
+
* Uses Anthropic Claude to extract emotional data from content
|
|
4
|
+
* Supports profile-aware extraction (core/extended/full)
|
|
5
|
+
*/
|
|
6
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
7
|
+
import type { LlmExtractedFields, ExtractionInput, EdmProfile } from "../schema/types.js";
|
|
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
|
|
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";
|
|
19
|
+
export interface LlmExtractionResult {
|
|
20
|
+
extracted: LlmExtractedFields;
|
|
21
|
+
confidence: number;
|
|
22
|
+
model: string;
|
|
23
|
+
profile: EdmProfile;
|
|
24
|
+
notes: string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Extract EDM fields from content using Anthropic Claude
|
|
28
|
+
*
|
|
29
|
+
* @param client - Anthropic client
|
|
30
|
+
* @param input - Content to extract from
|
|
31
|
+
* @param model - Model to use (default: claude-sonnet-4-20250514)
|
|
32
|
+
* @param profile - EDM profile (default: 'full')
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractWithLlm(client: Anthropic, input: ExtractionInput, model?: string, profile?: EdmProfile): Promise<LlmExtractionResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Calculate extraction confidence based on field population
|
|
37
|
+
*/
|
|
38
|
+
export declare function calculateConfidence(extracted: LlmExtractedFields): number;
|
|
39
|
+
/**
|
|
40
|
+
* Create an Anthropic client
|
|
41
|
+
*/
|
|
42
|
+
export declare function createAnthropicClient(apiKey?: string): Anthropic;
|
|
43
|
+
//# sourceMappingURL=llm-extractor.d.ts.map
|
|
@@ -0,0 +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"}
|