@molroo-io/sdk 0.6.2 → 0.7.1
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/README.md +100 -194
- package/dist/cjs/api-client.d.ts +2 -12
- package/dist/cjs/api-client.d.ts.map +1 -1
- package/dist/cjs/api-client.js +4 -42
- package/dist/cjs/errors.d.ts +1 -16
- package/dist/cjs/errors.d.ts.map +1 -1
- package/dist/cjs/errors.js +2 -18
- package/dist/cjs/events/types.d.ts +14 -21
- package/dist/cjs/events/types.d.ts.map +1 -1
- package/dist/cjs/events/types.js +0 -11
- package/dist/cjs/index.d.ts +26 -48
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +29 -52
- package/dist/cjs/llm/resolve.d.ts +4 -22
- package/dist/cjs/llm/resolve.d.ts.map +1 -1
- package/dist/cjs/llm/resolve.js +19 -7
- package/dist/cjs/llm/vercel-ai/adapter.d.ts +4 -10
- package/dist/cjs/llm/vercel-ai/adapter.d.ts.map +1 -1
- package/dist/cjs/llm/vercel-ai/adapter.js +6 -152
- package/dist/cjs/llm/vercel-ai/config.d.ts +8 -5
- package/dist/cjs/llm/vercel-ai/config.d.ts.map +1 -1
- package/dist/cjs/memory/in-memory.d.ts +14 -37
- package/dist/cjs/memory/in-memory.d.ts.map +1 -1
- package/dist/cjs/memory/in-memory.js +22 -85
- package/dist/cjs/memory/recall.d.ts +10 -21
- package/dist/cjs/memory/recall.d.ts.map +1 -1
- package/dist/cjs/memory/recall.js +12 -91
- package/dist/cjs/memory/types.d.ts +46 -186
- package/dist/cjs/memory/types.d.ts.map +1 -1
- package/dist/cjs/memory/types.js +0 -10
- package/dist/cjs/persona/chat-orchestrator.d.ts +46 -0
- package/dist/cjs/persona/chat-orchestrator.d.ts.map +1 -0
- package/dist/cjs/persona/chat-orchestrator.js +240 -0
- package/dist/cjs/persona/event-emitter.d.ts +7 -0
- package/dist/cjs/persona/event-emitter.d.ts.map +1 -0
- package/dist/cjs/persona/event-emitter.js +53 -0
- package/dist/cjs/persona/memory-pipeline.d.ts +26 -0
- package/dist/cjs/persona/memory-pipeline.d.ts.map +1 -0
- package/dist/cjs/persona/memory-pipeline.js +69 -0
- package/dist/cjs/persona.d.ts +56 -187
- package/dist/cjs/persona.d.ts.map +1 -1
- package/dist/cjs/persona.js +62 -638
- package/dist/cjs/shared/appraisal.d.ts +26 -0
- package/dist/cjs/shared/appraisal.d.ts.map +1 -0
- package/dist/cjs/shared/appraisal.js +45 -0
- package/dist/cjs/shared/client-factory.d.ts +23 -0
- package/dist/cjs/shared/client-factory.d.ts.map +1 -0
- package/dist/cjs/shared/client-factory.js +48 -0
- package/dist/cjs/shared/errors.d.ts +21 -0
- package/dist/cjs/shared/errors.d.ts.map +1 -0
- package/dist/cjs/shared/errors.js +29 -0
- package/dist/cjs/world/client.d.ts +2 -9
- package/dist/cjs/world/client.d.ts.map +1 -1
- package/dist/cjs/world/client.js +7 -34
- package/dist/cjs/world/errors.d.ts +1 -8
- package/dist/cjs/world/errors.d.ts.map +1 -1
- package/dist/cjs/world/errors.js +2 -12
- package/dist/cjs/world/index.d.ts +2 -2
- package/dist/cjs/world/index.d.ts.map +1 -1
- package/dist/cjs/world/types.d.ts +32 -5
- package/dist/cjs/world/types.d.ts.map +1 -1
- package/dist/cjs/world/world-domain.d.ts.map +1 -1
- package/dist/cjs/world/world-domain.js +4 -32
- package/dist/cjs/world/world-persona.d.ts +17 -12
- package/dist/cjs/world/world-persona.d.ts.map +1 -1
- package/dist/cjs/world/world-persona.js +6 -18
- package/dist/cjs/world/world.d.ts +28 -5
- package/dist/cjs/world/world.d.ts.map +1 -1
- package/dist/cjs/world/world.js +13 -11
- package/dist/esm/api-client.d.ts +2 -12
- package/dist/esm/api-client.d.ts.map +1 -1
- package/dist/esm/api-client.js +3 -38
- package/dist/esm/errors.d.ts +1 -16
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +1 -17
- package/dist/esm/events/types.d.ts +14 -21
- package/dist/esm/events/types.d.ts.map +1 -1
- package/dist/esm/events/types.js +0 -11
- package/dist/esm/index.d.ts +26 -48
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +25 -38
- package/dist/esm/llm/resolve.d.ts +4 -22
- package/dist/esm/llm/resolve.d.ts.map +1 -1
- package/dist/esm/llm/resolve.js +20 -8
- package/dist/esm/llm/vercel-ai/adapter.d.ts +4 -10
- package/dist/esm/llm/vercel-ai/adapter.d.ts.map +1 -1
- package/dist/esm/llm/vercel-ai/adapter.js +6 -119
- package/dist/esm/llm/vercel-ai/config.d.ts +8 -5
- package/dist/esm/llm/vercel-ai/config.d.ts.map +1 -1
- package/dist/esm/memory/in-memory.d.ts +14 -37
- package/dist/esm/memory/in-memory.d.ts.map +1 -1
- package/dist/esm/memory/in-memory.js +20 -83
- package/dist/esm/memory/recall.d.ts +10 -21
- package/dist/esm/memory/recall.d.ts.map +1 -1
- package/dist/esm/memory/recall.js +12 -91
- package/dist/esm/memory/types.d.ts +46 -186
- package/dist/esm/memory/types.d.ts.map +1 -1
- package/dist/esm/memory/types.js +1 -9
- package/dist/esm/persona/chat-orchestrator.d.ts +46 -0
- package/dist/esm/persona/chat-orchestrator.d.ts.map +1 -0
- package/dist/esm/persona/chat-orchestrator.js +204 -0
- package/dist/esm/persona/event-emitter.d.ts +7 -0
- package/dist/esm/persona/event-emitter.d.ts.map +1 -0
- package/dist/esm/persona/event-emitter.js +50 -0
- package/dist/esm/persona/memory-pipeline.d.ts +26 -0
- package/dist/esm/persona/memory-pipeline.d.ts.map +1 -0
- package/dist/esm/persona/memory-pipeline.js +65 -0
- package/dist/esm/persona.d.ts +56 -187
- package/dist/esm/persona.d.ts.map +1 -1
- package/dist/esm/persona.js +62 -638
- package/dist/esm/shared/appraisal.d.ts +26 -0
- package/dist/esm/shared/appraisal.d.ts.map +1 -0
- package/dist/esm/shared/appraisal.js +40 -0
- package/dist/esm/shared/client-factory.d.ts +23 -0
- package/dist/esm/shared/client-factory.d.ts.map +1 -0
- package/dist/esm/shared/client-factory.js +41 -0
- package/dist/esm/shared/errors.d.ts +21 -0
- package/dist/esm/shared/errors.d.ts.map +1 -0
- package/dist/esm/shared/errors.js +24 -0
- package/dist/esm/world/client.d.ts +2 -9
- package/dist/esm/world/client.d.ts.map +1 -1
- package/dist/esm/world/client.js +6 -30
- package/dist/esm/world/errors.d.ts +1 -8
- package/dist/esm/world/errors.d.ts.map +1 -1
- package/dist/esm/world/errors.js +1 -11
- package/dist/esm/world/index.d.ts +2 -2
- package/dist/esm/world/index.d.ts.map +1 -1
- package/dist/esm/world/types.d.ts +32 -5
- package/dist/esm/world/types.d.ts.map +1 -1
- package/dist/esm/world/world-domain.d.ts.map +1 -1
- package/dist/esm/world/world-domain.js +4 -32
- package/dist/esm/world/world-persona.d.ts +17 -12
- package/dist/esm/world/world-persona.d.ts.map +1 -1
- package/dist/esm/world/world-persona.js +6 -18
- package/dist/esm/world/world.d.ts +28 -5
- package/dist/esm/world/world.d.ts.map +1 -1
- package/dist/esm/world/world.js +13 -11
- package/package.json +4 -101
- package/dist/cjs/embedding/cloudflare.d.ts +0 -15
- package/dist/cjs/embedding/cloudflare.d.ts.map +0 -1
- package/dist/cjs/embedding/cloudflare.js +0 -16
- package/dist/cjs/embedding/cohere.d.ts +0 -8
- package/dist/cjs/embedding/cohere.d.ts.map +0 -1
- package/dist/cjs/embedding/cohere.js +0 -31
- package/dist/cjs/embedding/index.d.ts +0 -9
- package/dist/cjs/embedding/index.d.ts.map +0 -1
- package/dist/cjs/embedding/index.js +0 -11
- package/dist/cjs/embedding/local.d.ts +0 -6
- package/dist/cjs/embedding/local.d.ts.map +0 -1
- package/dist/cjs/embedding/local.js +0 -28
- package/dist/cjs/embedding/openai.d.ts +0 -9
- package/dist/cjs/embedding/openai.d.ts.map +0 -1
- package/dist/cjs/embedding/openai.js +0 -26
- package/dist/cjs/events/console.d.ts +0 -25
- package/dist/cjs/events/console.d.ts.map +0 -1
- package/dist/cjs/events/console.js +0 -41
- package/dist/cjs/events/webhook.d.ts +0 -30
- package/dist/cjs/events/webhook.d.ts.map +0 -1
- package/dist/cjs/events/webhook.js +0 -79
- package/dist/cjs/memory/cloudflare/index.d.ts +0 -3
- package/dist/cjs/memory/cloudflare/index.d.ts.map +0 -1
- package/dist/cjs/memory/cloudflare/index.js +0 -5
- package/dist/cjs/memory/cloudflare/vectorize.d.ts +0 -62
- package/dist/cjs/memory/cloudflare/vectorize.d.ts.map +0 -1
- package/dist/cjs/memory/cloudflare/vectorize.js +0 -55
- package/dist/cjs/memory/in-memory-semantic.d.ts +0 -16
- package/dist/cjs/memory/in-memory-semantic.d.ts.map +0 -1
- package/dist/cjs/memory/in-memory-semantic.js +0 -57
- package/dist/cjs/memory/pinecone/index.d.ts +0 -7
- package/dist/cjs/memory/pinecone/index.d.ts.map +0 -1
- package/dist/cjs/memory/pinecone/index.js +0 -8
- package/dist/cjs/memory/pinecone/memory-adapter.d.ts +0 -62
- package/dist/cjs/memory/pinecone/memory-adapter.d.ts.map +0 -1
- package/dist/cjs/memory/pinecone/memory-adapter.js +0 -220
- package/dist/cjs/memory/pinecone/semantic.d.ts +0 -44
- package/dist/cjs/memory/pinecone/semantic.d.ts.map +0 -1
- package/dist/cjs/memory/pinecone/semantic.js +0 -90
- package/dist/cjs/memory/sqlite/index.d.ts +0 -3
- package/dist/cjs/memory/sqlite/index.d.ts.map +0 -1
- package/dist/cjs/memory/sqlite/index.js +0 -5
- package/dist/cjs/memory/sqlite/memory-adapter.d.ts +0 -58
- package/dist/cjs/memory/sqlite/memory-adapter.d.ts.map +0 -1
- package/dist/cjs/memory/sqlite/memory-adapter.js +0 -336
- package/dist/cjs/memory/sqlite/schema.d.ts +0 -4
- package/dist/cjs/memory/sqlite/schema.d.ts.map +0 -1
- package/dist/cjs/memory/sqlite/schema.js +0 -91
- package/dist/cjs/memory/supabase/index.d.ts +0 -7
- package/dist/cjs/memory/supabase/index.d.ts.map +0 -1
- package/dist/cjs/memory/supabase/index.js +0 -8
- package/dist/cjs/memory/supabase/memory-adapter.d.ts +0 -67
- package/dist/cjs/memory/supabase/memory-adapter.d.ts.map +0 -1
- package/dist/cjs/memory/supabase/memory-adapter.js +0 -335
- package/dist/cjs/memory/supabase/semantic.d.ts +0 -44
- package/dist/cjs/memory/supabase/semantic.d.ts.map +0 -1
- package/dist/cjs/memory/supabase/semantic.js +0 -72
- package/dist/esm/embedding/cloudflare.d.ts +0 -15
- package/dist/esm/embedding/cloudflare.d.ts.map +0 -1
- package/dist/esm/embedding/cloudflare.js +0 -13
- package/dist/esm/embedding/cohere.d.ts +0 -8
- package/dist/esm/embedding/cohere.d.ts.map +0 -1
- package/dist/esm/embedding/cohere.js +0 -28
- package/dist/esm/embedding/index.d.ts +0 -9
- package/dist/esm/embedding/index.d.ts.map +0 -1
- package/dist/esm/embedding/index.js +0 -4
- package/dist/esm/embedding/local.d.ts +0 -6
- package/dist/esm/embedding/local.d.ts.map +0 -1
- package/dist/esm/embedding/local.js +0 -25
- package/dist/esm/embedding/openai.d.ts +0 -9
- package/dist/esm/embedding/openai.d.ts.map +0 -1
- package/dist/esm/embedding/openai.js +0 -23
- package/dist/esm/events/console.d.ts +0 -25
- package/dist/esm/events/console.d.ts.map +0 -1
- package/dist/esm/events/console.js +0 -37
- package/dist/esm/events/webhook.d.ts +0 -30
- package/dist/esm/events/webhook.d.ts.map +0 -1
- package/dist/esm/events/webhook.js +0 -75
- package/dist/esm/memory/cloudflare/index.d.ts +0 -3
- package/dist/esm/memory/cloudflare/index.d.ts.map +0 -1
- package/dist/esm/memory/cloudflare/index.js +0 -1
- package/dist/esm/memory/cloudflare/vectorize.d.ts +0 -62
- package/dist/esm/memory/cloudflare/vectorize.d.ts.map +0 -1
- package/dist/esm/memory/cloudflare/vectorize.js +0 -51
- package/dist/esm/memory/in-memory-semantic.d.ts +0 -16
- package/dist/esm/memory/in-memory-semantic.d.ts.map +0 -1
- package/dist/esm/memory/in-memory-semantic.js +0 -53
- package/dist/esm/memory/pinecone/index.d.ts +0 -7
- package/dist/esm/memory/pinecone/index.d.ts.map +0 -1
- package/dist/esm/memory/pinecone/index.js +0 -3
- package/dist/esm/memory/pinecone/memory-adapter.d.ts +0 -62
- package/dist/esm/memory/pinecone/memory-adapter.d.ts.map +0 -1
- package/dist/esm/memory/pinecone/memory-adapter.js +0 -216
- package/dist/esm/memory/pinecone/semantic.d.ts +0 -44
- package/dist/esm/memory/pinecone/semantic.d.ts.map +0 -1
- package/dist/esm/memory/pinecone/semantic.js +0 -86
- package/dist/esm/memory/sqlite/index.d.ts +0 -3
- package/dist/esm/memory/sqlite/index.d.ts.map +0 -1
- package/dist/esm/memory/sqlite/index.js +0 -1
- package/dist/esm/memory/sqlite/memory-adapter.d.ts +0 -58
- package/dist/esm/memory/sqlite/memory-adapter.d.ts.map +0 -1
- package/dist/esm/memory/sqlite/memory-adapter.js +0 -296
- package/dist/esm/memory/sqlite/schema.d.ts +0 -4
- package/dist/esm/memory/sqlite/schema.d.ts.map +0 -1
- package/dist/esm/memory/sqlite/schema.js +0 -86
- package/dist/esm/memory/supabase/index.d.ts +0 -7
- package/dist/esm/memory/supabase/index.d.ts.map +0 -1
- package/dist/esm/memory/supabase/index.js +0 -3
- package/dist/esm/memory/supabase/memory-adapter.d.ts +0 -67
- package/dist/esm/memory/supabase/memory-adapter.d.ts.map +0 -1
- package/dist/esm/memory/supabase/memory-adapter.js +0 -331
- package/dist/esm/memory/supabase/semantic.d.ts +0 -44
- package/dist/esm/memory/supabase/semantic.d.ts.map +0 -1
- package/dist/esm/memory/supabase/semantic.js +0 -68
package/dist/esm/persona.js
CHANGED
|
@@ -1,114 +1,56 @@
|
|
|
1
1
|
import { resolveLLM } from './llm/resolve';
|
|
2
|
-
import { isMemoryConfig } from './memory/types';
|
|
3
|
-
import { recallMemories, buildMemoryBlock, stripMetaTags } from './memory/recall';
|
|
4
2
|
import { MolrooApiError } from './errors';
|
|
5
3
|
import { createApiClient, unwrap } from './api-client';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
goal_relevance: c(a.goal_relevance, -1, 1),
|
|
11
|
-
goal_congruence: c(a.goal_congruence, -1, 1),
|
|
12
|
-
expectedness: c(a.expectedness, 0, 1),
|
|
13
|
-
controllability: c(a.controllability, 0, 1),
|
|
14
|
-
agency: c(a.agency, -1, 1),
|
|
15
|
-
norm_compatibility: c(a.norm_compatibility, -1, 1),
|
|
16
|
-
internal_standards: c(a.internal_standards ?? 0, -1, 1),
|
|
17
|
-
adjustment_potential: c(a.adjustment_potential ?? 0.5, 0, 1),
|
|
18
|
-
urgency: c(a.urgency ?? 0.5, 0, 1),
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
/** Build a system prompt section describing the interlocutor. */
|
|
22
|
-
function buildInterlocutorBlock(ctx) {
|
|
23
|
-
const parts = [`## About ${ctx.name}`];
|
|
24
|
-
if (ctx.description)
|
|
25
|
-
parts.push(ctx.description);
|
|
26
|
-
if (ctx.extensions) {
|
|
27
|
-
for (const [title, content] of Object.entries(ctx.extensions)) {
|
|
28
|
-
parts.push(`### ${title}`);
|
|
29
|
-
parts.push(content);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return parts.join('\n');
|
|
33
|
-
}
|
|
4
|
+
import { postPerceive } from './persona/memory-pipeline';
|
|
5
|
+
import { chat as chatOrchestrator } from './persona/chat-orchestrator';
|
|
6
|
+
// ── SDK Persona Types ──
|
|
7
|
+
const DEFAULT_BASE_URL = 'https://api.molroo.io';
|
|
34
8
|
// ── MolrooPersona ──
|
|
35
9
|
/**
|
|
36
10
|
* SDK client for interacting with a standalone molroo persona instance.
|
|
37
11
|
*
|
|
38
|
-
* @
|
|
39
|
-
* ```typescript
|
|
40
|
-
* // Single adapter (recommended)
|
|
41
|
-
* const persona = await MolrooPersona.create(
|
|
42
|
-
* { baseUrl: 'https://api.molroo.io', apiKey: 'key', llm,
|
|
43
|
-
* memory: new SqliteMemoryAdapter({ dbPath: './memory.db' }) },
|
|
44
|
-
* { identity: { name: 'Sera' }, personality: { O: 0.8, C: 0.6, E: 0.7 } },
|
|
45
|
-
* );
|
|
46
|
-
*
|
|
47
|
-
* // Split adapters (advanced)
|
|
48
|
-
* const persona = await MolrooPersona.create(
|
|
49
|
-
* { baseUrl: 'https://api.molroo.io', apiKey: 'key', llm,
|
|
50
|
-
* memory: { episodes: episodeStore, semantic: vectorStore, embedding: embedProvider } },
|
|
51
|
-
* { identity: { name: 'Sera' }, personality: { O: 0.8, C: 0.6, E: 0.7 } },
|
|
52
|
-
* );
|
|
53
|
-
* ```
|
|
12
|
+
* @deprecated Use {@link Molroo} from './world/world' instead.
|
|
54
13
|
*/
|
|
55
14
|
export class MolrooPersona {
|
|
56
15
|
constructor(config) {
|
|
57
|
-
|
|
16
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
17
|
+
this.client = createApiClient(baseUrl, config.apiKey);
|
|
58
18
|
this._personaId = config.personaId || '';
|
|
59
19
|
this.llm = config.llm ?? null;
|
|
60
20
|
this.engineLlm = config.engineLlm ?? null;
|
|
61
21
|
this.events = config.events ?? null;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (isMemoryConfig(config.memory)) {
|
|
65
|
-
// Split adapter path (MemoryConfig)
|
|
66
|
-
this.memoryAdapter = null;
|
|
67
|
-
this.episodeStore = config.memory.episodes;
|
|
68
|
-
this.semanticStore = config.memory.semantic ?? null;
|
|
69
|
-
this.embeddingProvider = config.memory.embedding ?? null;
|
|
70
|
-
this.memoryRecallConfig = config.memory.recall;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
// Single adapter path (MemoryAdapter)
|
|
74
|
-
this.memoryAdapter = config.memory;
|
|
75
|
-
this.episodeStore = null;
|
|
76
|
-
this.semanticStore = null;
|
|
77
|
-
this.embeddingProvider = null;
|
|
78
|
-
this.memoryRecallConfig = config.recall;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
this.memoryAdapter = null;
|
|
83
|
-
this.episodeStore = null;
|
|
84
|
-
this.semanticStore = null;
|
|
85
|
-
this.embeddingProvider = null;
|
|
86
|
-
this.memoryRecallConfig = undefined;
|
|
87
|
-
}
|
|
22
|
+
this.memoryAdapter = config.memory ?? null;
|
|
23
|
+
this.memoryRecallConfig = config.recall;
|
|
88
24
|
}
|
|
89
25
|
// ── Properties ──
|
|
90
|
-
/** Unique identifier for this persona instance. */
|
|
91
26
|
get id() {
|
|
92
27
|
return this._personaId;
|
|
93
28
|
}
|
|
94
|
-
/** Unique identifier for this persona instance (alias for {@link id}). */
|
|
95
29
|
get personaId() {
|
|
96
30
|
return this._personaId;
|
|
97
31
|
}
|
|
98
|
-
|
|
32
|
+
static async create(config, input) {
|
|
33
|
+
// Handle description string (auto-generate via LLM)
|
|
34
|
+
if (typeof input === 'string') {
|
|
35
|
+
if (!config.llm) {
|
|
36
|
+
throw new MolrooApiError('LLM adapter is required when using description string. ' +
|
|
37
|
+
'Provide llm option or use explicit PersonaConfigData.', 'LLM_REQUIRED', 400);
|
|
38
|
+
}
|
|
39
|
+
const { generatePersona } = await import('./generate/persona');
|
|
40
|
+
const llm = await resolveLLM(config.llm);
|
|
41
|
+
const personaConfig = await generatePersona(llm, input);
|
|
42
|
+
return MolrooPersona.createWithConfig(config, personaConfig);
|
|
43
|
+
}
|
|
44
|
+
// Handle explicit PersonaConfigData
|
|
45
|
+
return MolrooPersona.createWithConfig(config, input);
|
|
46
|
+
}
|
|
99
47
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* @param config - API connection config and optional LLM adapter.
|
|
103
|
-
* Accepts a full {@link LLMAdapter} or a shorthand config object:
|
|
104
|
-
* ```typescript
|
|
105
|
-
* llm: { provider: 'openai', apiKey: '...' }
|
|
106
|
-
* ```
|
|
107
|
-
* @param personaConfig - Persona identity, personality, and goals.
|
|
108
|
-
* @returns A connected MolrooPersona instance.
|
|
48
|
+
* Internal implementation for creating persona with resolved config.
|
|
49
|
+
* @internal
|
|
109
50
|
*/
|
|
110
|
-
static async
|
|
111
|
-
const
|
|
51
|
+
static async createWithConfig(config, personaConfig) {
|
|
52
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
53
|
+
const client = createApiClient(baseUrl, config.apiKey);
|
|
112
54
|
const { data } = await client.POST('/personas', {
|
|
113
55
|
body: { config: personaConfig },
|
|
114
56
|
headers: { 'Idempotency-Key': crypto.randomUUID() },
|
|
@@ -118,75 +60,34 @@ export class MolrooPersona {
|
|
|
118
60
|
config.engineLlm ? resolveLLM(config.engineLlm) : undefined,
|
|
119
61
|
]);
|
|
120
62
|
const result = unwrap(data);
|
|
121
|
-
|
|
122
|
-
return persona;
|
|
63
|
+
return new MolrooPersona({ ...config, llm, engineLlm, personaId: result.personaId });
|
|
123
64
|
}
|
|
124
65
|
/**
|
|
125
|
-
* Generate a persona from
|
|
126
|
-
*
|
|
127
|
-
* Combines {@link generatePersona} + {@link MolrooPersona.create} in one step.
|
|
128
|
-
*
|
|
129
|
-
* @param config - API connection config and optional LLM adapter.
|
|
130
|
-
* @param description - Natural-language description of the persona to generate.
|
|
131
|
-
* @returns A connected MolrooPersona instance with a generated config.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```typescript
|
|
135
|
-
* const sera = await MolrooPersona.generate(
|
|
136
|
-
* { baseUrl: 'https://api.molroo.io', apiKey: 'mk_live_...', llm: { provider: 'openai', apiKey: '...' } },
|
|
137
|
-
* 'A warm and friendly barista named Sera who loves coffee and people.',
|
|
138
|
-
* );
|
|
139
|
-
* ```
|
|
66
|
+
* Generate a persona from description and create it.
|
|
67
|
+
* @deprecated Use `create(config, description)` instead.
|
|
140
68
|
*/
|
|
141
69
|
static async generate(config, description) {
|
|
142
|
-
|
|
143
|
-
const llm = await resolveLLM(config.llm);
|
|
144
|
-
const personaConfig = await generatePersona(llm, description);
|
|
145
|
-
return MolrooPersona.create(config, personaConfig);
|
|
70
|
+
return MolrooPersona.create(config, description);
|
|
146
71
|
}
|
|
147
|
-
/**
|
|
148
|
-
* Connect to an existing persona by ID.
|
|
149
|
-
*
|
|
150
|
-
* Verifies the persona exists and fetches its configuration.
|
|
151
|
-
*
|
|
152
|
-
* @param config - API connection config and optional LLM adapter.
|
|
153
|
-
* Accepts a full {@link LLMAdapter} or a shorthand config object:
|
|
154
|
-
* ```typescript
|
|
155
|
-
* llm: { provider: 'openai', apiKey: '...' }
|
|
156
|
-
* ```
|
|
157
|
-
* @param personaId - The ID of the persona to connect to.
|
|
158
|
-
* @returns A connected MolrooPersona instance.
|
|
159
|
-
*/
|
|
160
72
|
static async connect(config, personaId) {
|
|
161
73
|
const [llm, engineLlm] = await Promise.all([
|
|
162
74
|
config.llm ? resolveLLM(config.llm) : undefined,
|
|
163
75
|
config.engineLlm ? resolveLLM(config.engineLlm) : undefined,
|
|
164
76
|
]);
|
|
77
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
165
78
|
const persona = new MolrooPersona({ ...config, llm, engineLlm, personaId });
|
|
166
|
-
// Verify persona exists
|
|
167
79
|
await persona.client.GET('/personas/{id}', {
|
|
168
80
|
params: { path: { id: personaId } },
|
|
169
81
|
});
|
|
170
82
|
return persona;
|
|
171
83
|
}
|
|
172
|
-
/**
|
|
173
|
-
* List all personas for the authenticated tenant.
|
|
174
|
-
*
|
|
175
|
-
* @param config - API connection config (baseUrl + apiKey).
|
|
176
|
-
* @returns List of persona summaries with optional pagination cursor.
|
|
177
|
-
*/
|
|
178
84
|
static async listPersonas(config) {
|
|
179
|
-
const
|
|
85
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
86
|
+
const client = createApiClient(baseUrl, config.apiKey);
|
|
180
87
|
const { data } = await client.GET('/personas');
|
|
181
88
|
return unwrap(data);
|
|
182
89
|
}
|
|
183
90
|
// ── Runtime ──
|
|
184
|
-
/**
|
|
185
|
-
* Send a message to the persona and get an emotion-processed response.
|
|
186
|
-
*
|
|
187
|
-
* Internally converts the SDK-friendly options into the API wire format
|
|
188
|
-
* (`{ event: PerceiveEvent, context?: PerceiveContext }`).
|
|
189
|
-
*/
|
|
190
91
|
async perceive(message, options) {
|
|
191
92
|
const eventType = options?.type ?? 'chat_message';
|
|
192
93
|
const sourceName = typeof options?.from === 'string'
|
|
@@ -211,196 +112,32 @@ export class MolrooPersona {
|
|
|
211
112
|
body: { event, context },
|
|
212
113
|
});
|
|
213
114
|
const response = unwrap(data);
|
|
214
|
-
|
|
215
|
-
if (!options?.skipMemory && response.memoryEpisode) {
|
|
216
|
-
// Tag episode with event type so recall can distinguish chat vs events
|
|
115
|
+
if (!options?.skipMemory && response.memoryEpisode && this.memoryAdapter) {
|
|
217
116
|
if (!response.memoryEpisode.type) {
|
|
218
117
|
response.memoryEpisode.type = eventType;
|
|
219
118
|
}
|
|
220
|
-
this.
|
|
119
|
+
postPerceive(this.memoryPipelineDeps, response);
|
|
221
120
|
}
|
|
222
121
|
return response;
|
|
223
122
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Fire a non-chat event and process through the emotion engine.
|
|
226
|
-
*
|
|
227
|
-
* Unlike chat(), this does not involve LLM generation — it directly
|
|
228
|
-
* sends an event to the perceive endpoint with a required appraisal vector.
|
|
229
|
-
* The resulting episode is saved to memory and can be recalled during chat().
|
|
230
|
-
*
|
|
231
|
-
* @param type - Event type identifier (e.g. 'attack', 'gift', 'rest').
|
|
232
|
-
* @param description - Human-readable event description (used as message context).
|
|
233
|
-
* @param options - Must include `appraisal`. Optionally `from`, `stimulus`, `payload`.
|
|
234
|
-
* @returns Emotion engine response.
|
|
235
|
-
*
|
|
236
|
-
* @example
|
|
237
|
-
* ```typescript
|
|
238
|
-
* await persona.event('attack', 'goblin attacks with sword', {
|
|
239
|
-
* from: 'goblin',
|
|
240
|
-
* appraisal: {
|
|
241
|
-
* goal_relevance: 0.8,
|
|
242
|
-
* goal_congruence: -0.9,
|
|
243
|
-
* expectedness: 0.3,
|
|
244
|
-
* controllability: 0.4,
|
|
245
|
-
* agency: -0.6,
|
|
246
|
-
* norm_compatibility: -0.5,
|
|
247
|
-
* },
|
|
248
|
-
* });
|
|
249
|
-
* ```
|
|
250
|
-
*/
|
|
251
123
|
async event(type, description, options) {
|
|
252
124
|
return this.perceive(description, { ...options, type });
|
|
253
125
|
}
|
|
254
|
-
/**
|
|
255
|
-
* High-level chat: getState → LLM generate → perceive with appraisal.
|
|
256
|
-
*
|
|
257
|
-
* 1. Calls `getPromptContext()` for the server-assembled system prompt
|
|
258
|
-
* 2. Recalls episodic + semantic memories from client-side stores
|
|
259
|
-
* 3. Sends to LLM adapter for text generation + appraisal
|
|
260
|
-
* 4. Sends the appraisal to the API via `perceive()` for emotion computation
|
|
261
|
-
* 5. Runs post-chat pipeline (episode save, reflection, events)
|
|
262
|
-
*
|
|
263
|
-
* Requires {@link LLMAdapter}. Without one, use {@link perceive} directly
|
|
264
|
-
* for emotion-only interaction.
|
|
265
|
-
*/
|
|
266
126
|
async chat(message, options) {
|
|
267
|
-
const fromOption = options?.from ?? 'user';
|
|
268
|
-
const from = typeof fromOption === 'string' ? fromOption : fromOption.name;
|
|
269
|
-
const interlocutor = typeof fromOption === 'object' ? fromOption : null;
|
|
270
127
|
const llm = this.requireLLM();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// 2. Recall memories (episodic + semantic + reflections)
|
|
276
|
-
const recalled = await recallMemories(message, {
|
|
128
|
+
const deps = {
|
|
129
|
+
personaId: this._personaId,
|
|
130
|
+
llm,
|
|
131
|
+
engineLlm: this.engineLlm,
|
|
277
132
|
memoryAdapter: this.memoryAdapter,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (interlocutor) {
|
|
286
|
-
systemPrompt += '\n\n' + buildInterlocutorBlock(interlocutor);
|
|
287
|
-
}
|
|
288
|
-
const memoryBlock = buildMemoryBlock(recalled.episodic, recalled.semantic, recalled.reflections);
|
|
289
|
-
if (memoryBlock) {
|
|
290
|
-
systemPrompt += '\n\n' + memoryBlock;
|
|
291
|
-
}
|
|
292
|
-
// 3. LLM Generate (SDK side, user's API key)
|
|
293
|
-
const messages = [];
|
|
294
|
-
if (options?.history) {
|
|
295
|
-
messages.push(...options.history.map(h => ({ role: h.role, content: h.content })));
|
|
296
|
-
}
|
|
297
|
-
messages.push({ role: 'user', content: message });
|
|
298
|
-
let responseText;
|
|
299
|
-
let appraisal;
|
|
300
|
-
let earlyPerceiveResponse;
|
|
301
|
-
// Split mode: engineLlm handles appraisal, primary llm handles response text
|
|
302
|
-
if (this.engineLlm && this.engineLlm !== llm) {
|
|
303
|
-
const { AppraisalVectorSchema } = await import('./llm/schema');
|
|
304
|
-
const appraisalInstruction = [
|
|
305
|
-
'',
|
|
306
|
-
'## Appraisal Task',
|
|
307
|
-
"Evaluate how RECEIVING the user's message affects this persona emotionally.",
|
|
308
|
-
'The event you are appraising is the message itself arriving — not the described situation.',
|
|
309
|
-
"When the user shares their own experience (e.g. loss, success), appraise through the persona's relational goals: caring about the speaker makes their suffering relevant and incongruent.",
|
|
310
|
-
"Rate each dimension from the persona's subjective perspective.",
|
|
311
|
-
'An insult should produce negative goal_congruence and norm_compatibility.',
|
|
312
|
-
'A compliment should produce positive values. Neutral small talk should be near zero.',
|
|
313
|
-
].join('\n');
|
|
314
|
-
// 3a. engineLlm → appraisal (based on prior emotional state)
|
|
315
|
-
// Only send last few messages for appraisal context (full history is wasteful)
|
|
316
|
-
const appraisalMessages = messages.length <= 5 ? messages : messages.slice(-5);
|
|
317
|
-
const { object: appraisalResult } = await this.engineLlm.generateObject({
|
|
318
|
-
system: systemPrompt + appraisalInstruction,
|
|
319
|
-
messages: appraisalMessages,
|
|
320
|
-
schema: AppraisalVectorSchema,
|
|
321
|
-
});
|
|
322
|
-
appraisal = clampAppraisal(appraisalResult);
|
|
323
|
-
// 3b. perceive with user message → engine updates emotion before response generation
|
|
324
|
-
earlyPerceiveResponse = await this.perceive(message, {
|
|
325
|
-
from,
|
|
326
|
-
appraisal,
|
|
327
|
-
priorEpisodes: recalled.episodic.length > 0 ? recalled.episodic : undefined,
|
|
328
|
-
skipMemory: true, // chat() runs its own postChatPipeline
|
|
329
|
-
});
|
|
330
|
-
// 3c. Fetch updated prompt reflecting new emotional state
|
|
331
|
-
let updatedPrompt = systemPrompt;
|
|
332
|
-
try {
|
|
333
|
-
const updatedCtx = await this.getPromptContext(options?.consumerSuffix, from);
|
|
334
|
-
updatedPrompt = updatedCtx.systemPrompt;
|
|
335
|
-
if (interlocutor) {
|
|
336
|
-
updatedPrompt += '\n\n' + buildInterlocutorBlock(interlocutor);
|
|
337
|
-
}
|
|
338
|
-
if (memoryBlock) {
|
|
339
|
-
updatedPrompt += '\n\n' + memoryBlock;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
catch {
|
|
343
|
-
// Use original prompt if updated fetch fails
|
|
344
|
-
}
|
|
345
|
-
// 3d. primary llm → response (with updated emotional state in prompt)
|
|
346
|
-
const { text } = await llm.generateText({
|
|
347
|
-
system: updatedPrompt,
|
|
348
|
-
messages,
|
|
349
|
-
});
|
|
350
|
-
responseText = text;
|
|
351
|
-
}
|
|
352
|
-
else if (hasTools) {
|
|
353
|
-
// Combined mode with tool-use: LLM can request memory searches
|
|
354
|
-
const result = await this.generateWithToolLoop(llm, systemPrompt, messages, options?.onToolCall);
|
|
355
|
-
responseText = result.text;
|
|
356
|
-
appraisal = clampAppraisal(result.appraisal);
|
|
357
|
-
}
|
|
358
|
-
else {
|
|
359
|
-
// Combined mode (default): single LLM call for response + appraisal
|
|
360
|
-
const { LLMResponseSchema } = await import('./llm/schema');
|
|
361
|
-
const { object: llmResult } = await llm.generateObject({
|
|
362
|
-
system: systemPrompt,
|
|
363
|
-
messages,
|
|
364
|
-
schema: LLMResponseSchema,
|
|
365
|
-
});
|
|
366
|
-
responseText = llmResult.response;
|
|
367
|
-
appraisal = clampAppraisal(llmResult.appraisal ?? {
|
|
368
|
-
goal_relevance: 0,
|
|
369
|
-
goal_congruence: 0,
|
|
370
|
-
expectedness: 0.5,
|
|
371
|
-
controllability: 0.5,
|
|
372
|
-
agency: 0,
|
|
373
|
-
norm_compatibility: 0,
|
|
374
|
-
internal_standards: 0,
|
|
375
|
-
adjustment_potential: 0.5,
|
|
376
|
-
urgency: 0.5,
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
// 4. Send to API for emotion processing (skip if already done in split mode)
|
|
380
|
-
let response;
|
|
381
|
-
if (earlyPerceiveResponse) {
|
|
382
|
-
response = earlyPerceiveResponse;
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
response = await this.perceive(responseText, {
|
|
386
|
-
from,
|
|
387
|
-
appraisal,
|
|
388
|
-
priorEpisodes: recalled.episodic.length > 0 ? recalled.episodic : undefined,
|
|
389
|
-
skipMemory: true, // chat() runs its own postChatPipeline
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
// Inject LLM-generated text
|
|
393
|
-
response.text = responseText;
|
|
394
|
-
// 5. Post-chat pipeline (fire-and-forget: episode save, reflection, events)
|
|
395
|
-
this.postChatPipeline(response);
|
|
396
|
-
return { text: responseText, response };
|
|
133
|
+
memoryRecallConfig: this.memoryRecallConfig,
|
|
134
|
+
events: this.events,
|
|
135
|
+
getPromptContext: (suffix, source) => this.getPromptContext(suffix, source),
|
|
136
|
+
perceive: (msg, opts) => this.perceive(msg, opts),
|
|
137
|
+
searchMemory: (query) => this.searchMemory(query),
|
|
138
|
+
};
|
|
139
|
+
return chatOrchestrator(deps, message, options);
|
|
397
140
|
}
|
|
398
|
-
/**
|
|
399
|
-
* Advance persona time by the specified number of seconds.
|
|
400
|
-
*
|
|
401
|
-
* @param seconds - Number of seconds to advance.
|
|
402
|
-
* @returns Any pending events that were processed.
|
|
403
|
-
*/
|
|
404
141
|
async tick(seconds) {
|
|
405
142
|
const { data } = await this.client.POST('/personas/{id}/tick', {
|
|
406
143
|
params: { path: { id: this._personaId } },
|
|
@@ -408,11 +145,6 @@ export class MolrooPersona {
|
|
|
408
145
|
});
|
|
409
146
|
return unwrap(data);
|
|
410
147
|
}
|
|
411
|
-
/**
|
|
412
|
-
* Directly set the persona's emotion state in VAD space.
|
|
413
|
-
*
|
|
414
|
-
* @param vad - Partial VAD values to set (V: -1..1, A: 0..1, D: -1..1).
|
|
415
|
-
*/
|
|
416
148
|
async setEmotion(vad) {
|
|
417
149
|
await this.client.POST('/personas/{id}/emotion', {
|
|
418
150
|
params: { path: { id: this._personaId } },
|
|
@@ -420,33 +152,18 @@ export class MolrooPersona {
|
|
|
420
152
|
});
|
|
421
153
|
}
|
|
422
154
|
// ── State ──
|
|
423
|
-
/**
|
|
424
|
-
* Get the current emotional and psychological state of the persona.
|
|
425
|
-
*
|
|
426
|
-
* @returns Current emotion, mood, somatic, and narrative state.
|
|
427
|
-
*/
|
|
428
155
|
async getState() {
|
|
429
156
|
const { data } = await this.client.GET('/personas/{id}/state', {
|
|
430
157
|
params: { path: { id: this._personaId } },
|
|
431
158
|
});
|
|
432
159
|
return unwrap(data);
|
|
433
160
|
}
|
|
434
|
-
/**
|
|
435
|
-
* Get a full snapshot of the persona's internal state.
|
|
436
|
-
*
|
|
437
|
-
* @returns Complete persona snapshot for backup/restore.
|
|
438
|
-
*/
|
|
439
161
|
async getSnapshot() {
|
|
440
162
|
const { data } = await this.client.GET('/personas/{id}/snapshot', {
|
|
441
163
|
params: { path: { id: this._personaId } },
|
|
442
164
|
});
|
|
443
165
|
return unwrap(data);
|
|
444
166
|
}
|
|
445
|
-
/**
|
|
446
|
-
* Restore the persona's internal state from a snapshot.
|
|
447
|
-
*
|
|
448
|
-
* @param snapshot - The snapshot to restore.
|
|
449
|
-
*/
|
|
450
167
|
async putSnapshot(snapshot) {
|
|
451
168
|
await this.client.PUT('/personas/{id}/snapshot', {
|
|
452
169
|
params: { path: { id: this._personaId } },
|
|
@@ -454,11 +171,6 @@ export class MolrooPersona {
|
|
|
454
171
|
});
|
|
455
172
|
}
|
|
456
173
|
// ── Config ──
|
|
457
|
-
/**
|
|
458
|
-
* Patch the persona's configuration (identity, personality, goals).
|
|
459
|
-
*
|
|
460
|
-
* @param updates - Configuration updates to apply.
|
|
461
|
-
*/
|
|
462
174
|
async patch(updates) {
|
|
463
175
|
await this.client.PATCH('/personas/{id}', {
|
|
464
176
|
params: { path: { id: this._personaId } },
|
|
@@ -466,7 +178,6 @@ export class MolrooPersona {
|
|
|
466
178
|
});
|
|
467
179
|
}
|
|
468
180
|
// ── Lifecycle ──
|
|
469
|
-
/** Soft-delete this persona. Can be restored with {@link restore}. */
|
|
470
181
|
async destroy() {
|
|
471
182
|
if (this._personaId) {
|
|
472
183
|
await this.client.DELETE('/personas/{id}', {
|
|
@@ -474,7 +185,6 @@ export class MolrooPersona {
|
|
|
474
185
|
});
|
|
475
186
|
}
|
|
476
187
|
}
|
|
477
|
-
/** Restore a previously soft-deleted persona. */
|
|
478
188
|
async restore() {
|
|
479
189
|
if (this._personaId) {
|
|
480
190
|
await this.client.POST('/personas/{id}/restore', {
|
|
@@ -482,216 +192,7 @@ export class MolrooPersona {
|
|
|
482
192
|
});
|
|
483
193
|
}
|
|
484
194
|
}
|
|
485
|
-
// ──
|
|
486
|
-
/** Whether memory is available (either single adapter or split stores). */
|
|
487
|
-
get hasMemory() {
|
|
488
|
-
return this.memoryAdapter !== null || this.episodeStore !== null;
|
|
489
|
-
}
|
|
490
|
-
// ── Post-Chat Pipeline (fire-and-forget) ──
|
|
491
|
-
/**
|
|
492
|
-
* Lightweight pipeline for perceive()-only interactions (non-chat events).
|
|
493
|
-
* Saves the episode and emits events, but skips LLM reflection.
|
|
494
|
-
*/
|
|
495
|
-
postPerceivePipeline(response) {
|
|
496
|
-
if (!response.memoryEpisode)
|
|
497
|
-
return;
|
|
498
|
-
if (this.memoryAdapter) {
|
|
499
|
-
// Single adapter path — adapter handles semantic indexing internally
|
|
500
|
-
this.memoryAdapter.saveEpisode(response.memoryEpisode).catch(() => { });
|
|
501
|
-
}
|
|
502
|
-
else if (this.episodeStore) {
|
|
503
|
-
// Split adapter path
|
|
504
|
-
this.episodeStore.saveEpisode(response.memoryEpisode).catch(() => { });
|
|
505
|
-
if (this.semanticStore && this.embeddingProvider && response.memoryEpisode.context) {
|
|
506
|
-
const ep = response.memoryEpisode;
|
|
507
|
-
this.embeddingProvider
|
|
508
|
-
.embed(stripMetaTags(ep.context))
|
|
509
|
-
.then((embedding) => {
|
|
510
|
-
this.semanticStore.index({
|
|
511
|
-
id: ep.id,
|
|
512
|
-
embedding,
|
|
513
|
-
metadata: {
|
|
514
|
-
type: 'episode',
|
|
515
|
-
sourceEntity: ep.sourceEntity,
|
|
516
|
-
timestamp: ep.timestamp,
|
|
517
|
-
importance: ep.importance,
|
|
518
|
-
episodeType: ep.type,
|
|
519
|
-
},
|
|
520
|
-
});
|
|
521
|
-
})
|
|
522
|
-
.catch(() => { });
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
// Event emission
|
|
526
|
-
if (this.events) {
|
|
527
|
-
this.emitResponseEvents(response, Date.now()).catch(() => { });
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
postChatPipeline(response) {
|
|
531
|
-
const now = Date.now();
|
|
532
|
-
if (this.memoryAdapter) {
|
|
533
|
-
// Single adapter path — adapter handles semantic indexing internally
|
|
534
|
-
if (response.memoryEpisode) {
|
|
535
|
-
this.memoryAdapter.saveEpisode(response.memoryEpisode).catch(() => { });
|
|
536
|
-
}
|
|
537
|
-
// Reflection generation
|
|
538
|
-
if ((this.engineLlm || this.llm) && response.reflectionPrompt) {
|
|
539
|
-
this.handleReflection(response.reflectionPrompt, response.emotion.vad).catch(() => { });
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
// Split adapter path
|
|
544
|
-
// 1. Episode storage + semantic indexing
|
|
545
|
-
if (this.episodeStore && response.memoryEpisode) {
|
|
546
|
-
this.episodeStore.saveEpisode(response.memoryEpisode).catch(() => { });
|
|
547
|
-
if (this.semanticStore && this.embeddingProvider && response.memoryEpisode.context) {
|
|
548
|
-
const ep = response.memoryEpisode;
|
|
549
|
-
this.embeddingProvider
|
|
550
|
-
.embed(stripMetaTags(ep.context))
|
|
551
|
-
.then((embedding) => {
|
|
552
|
-
this.semanticStore.index({
|
|
553
|
-
id: ep.id,
|
|
554
|
-
embedding,
|
|
555
|
-
metadata: {
|
|
556
|
-
type: 'episode',
|
|
557
|
-
sourceEntity: ep.sourceEntity,
|
|
558
|
-
timestamp: ep.timestamp,
|
|
559
|
-
importance: ep.importance,
|
|
560
|
-
episodeType: ep.type,
|
|
561
|
-
},
|
|
562
|
-
});
|
|
563
|
-
})
|
|
564
|
-
.catch(() => { });
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
// 2. Reflection generation (requires LLM + episodeStore)
|
|
568
|
-
if (this.episodeStore && (this.engineLlm || this.llm) && response.reflectionPrompt) {
|
|
569
|
-
this.handleReflection(response.reflectionPrompt, response.emotion.vad).catch(() => { });
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
// 3. Event emission
|
|
573
|
-
if (this.events) {
|
|
574
|
-
this.emitResponseEvents(response, now).catch(() => { });
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
async handleReflection(prompt, emotionVad) {
|
|
578
|
-
const reflectionLlm = this.engineLlm ?? this.llm;
|
|
579
|
-
if (!reflectionLlm)
|
|
580
|
-
return;
|
|
581
|
-
if (!this.memoryAdapter && !this.episodeStore)
|
|
582
|
-
return;
|
|
583
|
-
const { text } = await reflectionLlm.generateText({
|
|
584
|
-
system: prompt.system,
|
|
585
|
-
messages: [{ role: 'user', content: prompt.user }],
|
|
586
|
-
});
|
|
587
|
-
const reflection = {
|
|
588
|
-
id: `ref-${Date.now()}`,
|
|
589
|
-
timestamp: Date.now(),
|
|
590
|
-
sourceEntity: this._personaId,
|
|
591
|
-
content: text,
|
|
592
|
-
trigger: 'interaction',
|
|
593
|
-
emotionSnapshot: emotionVad ?? { V: 0, A: 0, D: 0 },
|
|
594
|
-
};
|
|
595
|
-
if (this.memoryAdapter) {
|
|
596
|
-
// Single adapter path — save reflection if supported
|
|
597
|
-
if (this.memoryAdapter.saveReflection) {
|
|
598
|
-
await this.memoryAdapter.saveReflection(reflection);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
else if (this.episodeStore) {
|
|
602
|
-
// Split adapter path
|
|
603
|
-
await this.episodeStore.saveReflection(reflection);
|
|
604
|
-
if (this.semanticStore && this.embeddingProvider) {
|
|
605
|
-
try {
|
|
606
|
-
const embedding = await this.embeddingProvider.embed(text);
|
|
607
|
-
await this.semanticStore.index({
|
|
608
|
-
id: reflection.id,
|
|
609
|
-
embedding,
|
|
610
|
-
metadata: {
|
|
611
|
-
type: 'reflection',
|
|
612
|
-
sourceEntity: this._personaId,
|
|
613
|
-
timestamp: reflection.timestamp,
|
|
614
|
-
},
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
catch {
|
|
618
|
-
// semantic indexing is optional
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
await this.events?.emit({
|
|
623
|
-
type: 'reflection_generated',
|
|
624
|
-
personaId: this._personaId,
|
|
625
|
-
timestamp: Date.now(),
|
|
626
|
-
payload: { trigger: 'interaction' },
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
async emitResponseEvents(response, now) {
|
|
630
|
-
if (!this.events)
|
|
631
|
-
return;
|
|
632
|
-
const batch = [];
|
|
633
|
-
if (response.emotion) {
|
|
634
|
-
batch.push({
|
|
635
|
-
type: 'emotion_changed',
|
|
636
|
-
personaId: this._personaId,
|
|
637
|
-
timestamp: now,
|
|
638
|
-
payload: {
|
|
639
|
-
vad: response.emotion.vad,
|
|
640
|
-
primary: response.emotion.discrete.primary,
|
|
641
|
-
intensity: response.emotion.discrete.intensity,
|
|
642
|
-
},
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
if (response.socialUpdates?.length) {
|
|
646
|
-
batch.push({
|
|
647
|
-
type: 'relationship_changed',
|
|
648
|
-
personaId: this._personaId,
|
|
649
|
-
timestamp: now,
|
|
650
|
-
payload: { updates: response.socialUpdates },
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
if (response.memoryEpisode) {
|
|
654
|
-
batch.push({
|
|
655
|
-
type: 'memory_consolidated',
|
|
656
|
-
personaId: this._personaId,
|
|
657
|
-
timestamp: now,
|
|
658
|
-
payload: { episodeId: response.memoryEpisode.id },
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
if (response.stageTransition) {
|
|
662
|
-
batch.push({ type: 'stage_transition', personaId: this._personaId, timestamp: now, payload: {} });
|
|
663
|
-
}
|
|
664
|
-
if (response.maskExposure) {
|
|
665
|
-
batch.push({ type: 'mask_exposure', personaId: this._personaId, timestamp: now, payload: response.maskExposure });
|
|
666
|
-
}
|
|
667
|
-
if (response.goalChanges) {
|
|
668
|
-
batch.push({ type: 'goal_changed', personaId: this._personaId, timestamp: now, payload: response.goalChanges });
|
|
669
|
-
}
|
|
670
|
-
if (batch.length === 0)
|
|
671
|
-
return;
|
|
672
|
-
if (this.events.emitBatch) {
|
|
673
|
-
await this.events.emitBatch(batch);
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
for (const event of batch) {
|
|
677
|
-
await this.events.emit(event);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
buildAppraisalHint(appraisal) {
|
|
682
|
-
return [
|
|
683
|
-
'## Emotional Evaluation (already computed — embody this, do not describe it)',
|
|
684
|
-
`Goal relevance: ${appraisal.goal_relevance.toFixed(2)}, congruence: ${appraisal.goal_congruence.toFixed(2)}`,
|
|
685
|
-
`Expectedness: ${appraisal.expectedness.toFixed(2)}, controllability: ${appraisal.controllability.toFixed(2)}`,
|
|
686
|
-
`Agency: ${appraisal.agency.toFixed(2)}, norm compatibility: ${appraisal.norm_compatibility.toFixed(2)}`,
|
|
687
|
-
].join('\n');
|
|
688
|
-
}
|
|
689
|
-
requireLLM() {
|
|
690
|
-
if (!this.llm) {
|
|
691
|
-
throw new MolrooApiError('LLM adapter is required for chat(). Provide llm option, or use perceive() directly.', 'LLM_NOT_CONFIGURED', 400);
|
|
692
|
-
}
|
|
693
|
-
return this.llm;
|
|
694
|
-
}
|
|
195
|
+
// ── Prompt / Memory ──
|
|
695
196
|
async getPromptContext(consumerSuffix, sourceEntity) {
|
|
696
197
|
const { data } = await this.client.POST('/personas/{id}/prompt-context', {
|
|
697
198
|
params: { path: { id: this._personaId } },
|
|
@@ -702,10 +203,6 @@ export class MolrooPersona {
|
|
|
702
203
|
});
|
|
703
204
|
return unwrap(data);
|
|
704
205
|
}
|
|
705
|
-
/**
|
|
706
|
-
* Search persona's episodic memory via the API.
|
|
707
|
-
* Used internally by the tool-use loop.
|
|
708
|
-
*/
|
|
709
206
|
async searchMemory(query, options) {
|
|
710
207
|
const { data } = await this.client.POST('/personas/{id}/memory/search', {
|
|
711
208
|
params: { path: { id: this._personaId } },
|
|
@@ -718,93 +215,20 @@ export class MolrooPersona {
|
|
|
718
215
|
const result = unwrap(data);
|
|
719
216
|
return result.episodes;
|
|
720
217
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
let currentSystem = system;
|
|
730
|
-
for (let iteration = 0; iteration < MAX_TOOL_ITERATIONS; iteration++) {
|
|
731
|
-
const { object: llmResult } = await llm.generateObject({
|
|
732
|
-
system: currentSystem,
|
|
733
|
-
messages,
|
|
734
|
-
schema: LLMResponseWithToolsSchema,
|
|
735
|
-
});
|
|
736
|
-
// If no tool call requested, return the response
|
|
737
|
-
if (!llmResult.search_memory) {
|
|
738
|
-
const text = llmResult.response;
|
|
739
|
-
const appraisal = llmResult.appraisal ?? {
|
|
740
|
-
goal_relevance: 0,
|
|
741
|
-
goal_congruence: 0,
|
|
742
|
-
expectedness: 0.5,
|
|
743
|
-
controllability: 0.5,
|
|
744
|
-
agency: 0,
|
|
745
|
-
norm_compatibility: 0,
|
|
746
|
-
internal_standards: 0,
|
|
747
|
-
adjustment_potential: 0.5,
|
|
748
|
-
urgency: 0.5,
|
|
749
|
-
};
|
|
750
|
-
return { text, appraisal };
|
|
751
|
-
}
|
|
752
|
-
// Execute memory search tool call
|
|
753
|
-
const query = llmResult.search_memory;
|
|
754
|
-
let episodes = [];
|
|
755
|
-
try {
|
|
756
|
-
episodes = await this.searchMemory(query);
|
|
757
|
-
}
|
|
758
|
-
catch {
|
|
759
|
-
// Memory search failed — continue without results
|
|
760
|
-
}
|
|
761
|
-
// Notify callback if provided
|
|
762
|
-
if (onToolCall) {
|
|
763
|
-
onToolCall({
|
|
764
|
-
name: 'search_memory',
|
|
765
|
-
args: { query },
|
|
766
|
-
result: episodes,
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
// Inject memory search results into system prompt for next iteration
|
|
770
|
-
const resultBlock = episodes.length > 0
|
|
771
|
-
? `\n\n## Memory Search Results (query: "${query}")\n${episodes.map(ep => {
|
|
772
|
-
const ts = ep.timestamp ? new Date(ep.timestamp).toISOString() : 'unknown';
|
|
773
|
-
const source = ep.sourceEntity ?? 'unknown';
|
|
774
|
-
const context = ep.context ?? 'no context';
|
|
775
|
-
return `- [${ts}] ${source}: ${context}`;
|
|
776
|
-
}).join('\n')}`
|
|
777
|
-
: `\n\n## Memory Search Results (query: "${query}")\nNo matching memories found.`;
|
|
778
|
-
currentSystem = currentSystem + resultBlock;
|
|
779
|
-
}
|
|
780
|
-
// Exhausted tool iterations — make a final call without tool schema
|
|
781
|
-
const { object: finalResult } = await llm.generateObject({
|
|
782
|
-
system: currentSystem,
|
|
783
|
-
messages,
|
|
784
|
-
schema: LLMResponseSchema,
|
|
785
|
-
});
|
|
786
|
-
const text = finalResult.response;
|
|
787
|
-
const appraisal = finalResult.appraisal ?? {
|
|
788
|
-
goal_relevance: 0,
|
|
789
|
-
goal_congruence: 0,
|
|
790
|
-
expectedness: 0.5,
|
|
791
|
-
controllability: 0.5,
|
|
792
|
-
agency: 0,
|
|
793
|
-
norm_compatibility: 0,
|
|
794
|
-
internal_standards: 0,
|
|
795
|
-
adjustment_potential: 0.5,
|
|
796
|
-
urgency: 0.5,
|
|
218
|
+
// ── Private ──
|
|
219
|
+
get memoryPipelineDeps() {
|
|
220
|
+
return {
|
|
221
|
+
personaId: this._personaId,
|
|
222
|
+
memoryAdapter: this.memoryAdapter,
|
|
223
|
+
events: this.events,
|
|
224
|
+
llm: this.llm,
|
|
225
|
+
engineLlm: this.engineLlm,
|
|
797
226
|
};
|
|
798
|
-
return { text, appraisal };
|
|
799
227
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
parts.push(`Speaking with: ${from}.`);
|
|
804
|
-
}
|
|
805
|
-
if (state.narrative) {
|
|
806
|
-
parts.push(`Narrative tone: ${state.narrative.tone.toFixed(2)}, agency: ${state.narrative.agency.toFixed(2)}`);
|
|
228
|
+
requireLLM() {
|
|
229
|
+
if (!this.llm) {
|
|
230
|
+
throw new MolrooApiError('LLM adapter is required for chat(). Provide llm option, or use perceive() directly.', 'LLM_NOT_CONFIGURED', 400);
|
|
807
231
|
}
|
|
808
|
-
return
|
|
232
|
+
return this.llm;
|
|
809
233
|
}
|
|
810
234
|
}
|