@timmeck/brain-core 2.36.36 → 2.36.38

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.
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Structured LLM Output — Typed ContentBlocks statt nur `{ text: string }`
3
+ *
4
+ * Inspiriert von LangChain's structured output parsing.
5
+ * Ermöglicht es, LLM-Antworten in typisierte Blöcke zu zerlegen:
6
+ * Text, Reasoning, Tool Calls, Citations, JSON.
7
+ */
8
+ // ── Parsing ─────────────────────────────────────────────
9
+ /**
10
+ * Parse raw LLM output into structured ContentBlocks.
11
+ *
12
+ * Erkennt:
13
+ * - <thinking>...</thinking> → ReasoningBlock
14
+ * - <tool_call>{"name":"...","args":{...}}</tool_call> → ToolCallBlock
15
+ * - <citation source="...">...</citation> → CitationBlock
16
+ * - ```json ... ``` → JsonBlock
17
+ * - Alles andere → TextBlock
18
+ */
19
+ export function parseStructuredOutput(raw) {
20
+ if (!raw || raw.trim().length === 0)
21
+ return [];
22
+ const blocks = [];
23
+ let remaining = raw;
24
+ // Regex patterns for structured sections
25
+ const patterns = [
26
+ {
27
+ // <thinking>...</thinking>
28
+ regex: /<thinking>([\s\S]*?)<\/thinking>/,
29
+ handler: (m) => ({ type: 'reasoning', content: m[1].trim() }),
30
+ },
31
+ {
32
+ // <tool_call>{"name":"x","args":{...}}</tool_call>
33
+ regex: /<tool_call>([\s\S]*?)<\/tool_call>/,
34
+ handler: (m) => {
35
+ try {
36
+ const parsed = JSON.parse(m[1].trim());
37
+ return {
38
+ type: 'tool_call',
39
+ toolName: parsed.name ?? parsed.tool ?? 'unknown',
40
+ args: parsed.args ?? parsed.arguments ?? parsed.input ?? {},
41
+ };
42
+ }
43
+ catch {
44
+ return { type: 'text', content: m[0] };
45
+ }
46
+ },
47
+ },
48
+ {
49
+ // <citation source="...">...</citation>
50
+ regex: /<citation\s+source="([^"]*)">([\s\S]*?)<\/citation>/,
51
+ handler: (m) => ({ type: 'citation', source: m[1], quote: m[2].trim() }),
52
+ },
53
+ {
54
+ // ```json ... ```
55
+ regex: /```json\s*\n([\s\S]*?)\n```/,
56
+ handler: (m) => {
57
+ try {
58
+ return { type: 'json', data: JSON.parse(m[1].trim()) };
59
+ }
60
+ catch {
61
+ return { type: 'text', content: m[0] };
62
+ }
63
+ },
64
+ },
65
+ ];
66
+ while (remaining.length > 0) {
67
+ // Find the earliest matching pattern
68
+ let earliest = null;
69
+ for (const p of patterns) {
70
+ const match = remaining.match(p.regex);
71
+ if (match && match.index !== undefined) {
72
+ if (!earliest || match.index < earliest.index) {
73
+ earliest = { index: match.index, match, handler: p.handler };
74
+ }
75
+ }
76
+ }
77
+ if (!earliest) {
78
+ // No more patterns found — rest is text
79
+ const trimmed = remaining.trim();
80
+ if (trimmed.length > 0) {
81
+ blocks.push({ type: 'text', content: trimmed });
82
+ }
83
+ break;
84
+ }
85
+ // Text before the match
86
+ const before = remaining.slice(0, earliest.index).trim();
87
+ if (before.length > 0) {
88
+ blocks.push({ type: 'text', content: before });
89
+ }
90
+ // The matched block
91
+ blocks.push(earliest.handler(earliest.match));
92
+ // Continue after the match
93
+ remaining = remaining.slice(earliest.index + earliest.match[0].length);
94
+ }
95
+ return blocks;
96
+ }
97
+ // ── JSON Mode Helper ────────────────────────────────────
98
+ /**
99
+ * Extrahiert JSON aus einer LLM-Antwort.
100
+ * Versucht zuerst die gesamte Antwort als JSON zu parsen,
101
+ * dann sucht es nach ```json Code-Blöcken.
102
+ */
103
+ export function extractJson(raw) {
104
+ // Try full string
105
+ try {
106
+ return JSON.parse(raw.trim());
107
+ }
108
+ catch {
109
+ // Try code block
110
+ const match = raw.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
111
+ if (match) {
112
+ try {
113
+ return JSON.parse(match[1].trim());
114
+ }
115
+ catch {
116
+ return null;
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+ }
122
+ /**
123
+ * Validate parsed JSON against a simple schema (key presence check).
124
+ * Returns the data if valid, null otherwise.
125
+ */
126
+ export function validateJsonSchema(data, requiredKeys) {
127
+ if (!data || typeof data !== 'object')
128
+ return null;
129
+ const obj = data;
130
+ for (const key of requiredKeys) {
131
+ if (!(key in obj))
132
+ return null;
133
+ }
134
+ return data;
135
+ }
136
+ // ── Block Helpers ────────────────────────────────────────
137
+ /** Get all blocks of a specific type */
138
+ export function getBlocks(blocks, type) {
139
+ return blocks.filter((b) => b.type === type);
140
+ }
141
+ /** Get combined text from all TextBlocks */
142
+ export function getTextContent(blocks) {
143
+ return getBlocks(blocks, 'text').map(b => b.content).join('\n\n');
144
+ }
145
+ /** Get all tool calls */
146
+ export function getToolCalls(blocks) {
147
+ return getBlocks(blocks, 'tool_call');
148
+ }
149
+ /** Check if response contains reasoning */
150
+ export function hasReasoning(blocks) {
151
+ return blocks.some(b => b.type === 'reasoning');
152
+ }
153
+ //# sourceMappingURL=structured-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-output.js","sourceRoot":"","sources":["../../src/llm/structured-output.ts"],"names":[],"mappings":"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"}
@@ -118,6 +118,7 @@ export declare class ResearchOrchestrator {
118
118
  private repoAbsorber;
119
119
  private featureRecommender;
120
120
  private contradictionResolver;
121
+ private checkpointManager;
121
122
  private lastAutoMissionTime;
122
123
  private onSuggestionCallback;
123
124
  private db;
@@ -217,6 +218,7 @@ export declare class ResearchOrchestrator {
217
218
  setRepoAbsorber(absorber: RepoAbsorber): void;
218
219
  setFeatureRecommender(recommender: import('../codegen/feature-recommender.js').FeatureRecommender): void;
219
220
  setContradictionResolver(resolver: import('../knowledge-graph/contradiction-resolver.js').ContradictionResolver): void;
221
+ setCheckpointManager(cm: import('../checkpoint/checkpoint-manager.js').CheckpointManager): void;
220
222
  /** Set the LLMService — propagates to all engines that can use LLM. */
221
223
  setLLMService(llm: LLMService): void;
222
224
  /** Get the LLMService instance. */
@@ -69,6 +69,7 @@ export class ResearchOrchestrator {
69
69
  repoAbsorber = null;
70
70
  featureRecommender = null;
71
71
  contradictionResolver = null;
72
+ checkpointManager = null;
72
73
  lastAutoMissionTime = 0;
73
74
  onSuggestionCallback = null;
74
75
  db;
@@ -239,6 +240,7 @@ export class ResearchOrchestrator {
239
240
  setRepoAbsorber(absorber) { this.repoAbsorber = absorber; }
240
241
  setFeatureRecommender(recommender) { this.featureRecommender = recommender; }
241
242
  setContradictionResolver(resolver) { this.contradictionResolver = resolver; }
243
+ setCheckpointManager(cm) { this.checkpointManager = cm; }
242
244
  /** Set the LLMService — propagates to all engines that can use LLM. */
243
245
  setLLMService(llm) {
244
246
  this.llmService = llm;
@@ -1988,6 +1990,13 @@ export class ResearchOrchestrator {
1988
1990
  duration_ms: duration,
1989
1991
  });
1990
1992
  }
1993
+ // Checkpoint: persist cycle state for crash recovery / time-travel
1994
+ if (this.checkpointManager) {
1995
+ try {
1996
+ this.checkpointManager.save(`orchestrator-${this.brainName}`, this.cycleCount, { cycleCount: this.cycleCount, insights: insights.length, anomalies: anomalies.length, durationMs: duration }, { workflowType: 'orchestrator', metadata: { brainName: this.brainName } });
1997
+ }
1998
+ catch { /* checkpoint save should never break the cycle */ }
1999
+ }
1991
2000
  }
1992
2001
  /** Analyze Brain's own state and generate concrete improvement suggestions.
1993
2002
  * Tracks suggestion history — if a suggestion repeats 3+ times without resolution,