Cascade Bot commited on
Commit
242446f
·
1 Parent(s): 3c2aa2f

Updated TreeOfThoughtsStrategy to use StrategyResult and improved implementation

Browse files
Files changed (1) hide show
  1. reasoning/tree_of_thoughts.py +372 -437
reasoning/tree_of_thoughts.py CHANGED
@@ -7,8 +7,9 @@ from dataclasses import dataclass
7
  from enum import Enum
8
  import heapq
9
  from collections import defaultdict
 
10
 
11
- from .base import ReasoningStrategy
12
 
13
  class NodeType(Enum):
14
  """Types of nodes in the thought tree."""
@@ -32,6 +33,7 @@ class TreeNode:
32
  metadata: Dict[str, Any]
33
  depth: int
34
  evaluation_score: float = 0.0
 
35
 
36
  class TreeOfThoughtsStrategy(ReasoningStrategy):
37
  """
@@ -48,469 +50,402 @@ class TreeOfThoughtsStrategy(ReasoningStrategy):
48
  parallel_threshold: int = 3,
49
  learning_rate: float = 0.1,
50
  strategy_weights: Optional[Dict[str, float]] = None):
 
 
51
  self.min_confidence = min_confidence
52
  self.parallel_threshold = parallel_threshold
53
  self.learning_rate = learning_rate
54
  self.strategy_weights = strategy_weights or {
55
- "LOCAL_LLM": 0.8,
56
- "CHAIN_OF_THOUGHT": 0.6,
57
- "TREE_OF_THOUGHTS": 0.5,
58
- "META_LEARNING": 0.4
 
59
  }
60
- self.node_history: Dict[str, TreeNode] = {}
61
- self.path_patterns: Dict[str, float] = defaultdict(float)
62
 
63
- async def reason(self, query: str, context: Dict[str, Any]) -> Dict[str, Any]:
64
- """Main reasoning method implementing tree of thoughts."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  try:
66
  # Initialize root node
67
- root = await self._create_root_node(query, context)
 
 
 
 
 
 
 
 
 
 
68
 
69
- # Build and explore tree
70
- tree = await self._build_tree(root, context)
71
 
72
- # Find best paths
73
- paths = await self._find_best_paths(tree, context)
74
 
75
- # Synthesize conclusion
76
- conclusion = await self._synthesize_conclusion(paths, context)
77
 
78
- # Update history and patterns
79
- self._update_history(tree)
80
- self._update_patterns(paths)
81
 
82
- return {
83
- "success": True,
84
- "answer": conclusion["answer"],
85
- "confidence": conclusion["confidence"],
86
- "tree": self._tree_to_dict(tree),
87
- "best_paths": [self._path_to_dict(p) for p in paths],
88
- "reasoning_trace": conclusion["trace"],
89
- "meta_insights": conclusion["meta_insights"]
90
- }
91
- except Exception as e:
92
- logging.error(f"Error in tree of thoughts reasoning: {str(e)}")
93
- return {"success": False, "error": str(e)}
94
-
95
- async def _create_root_node(self, query: str, context: Dict[str, Any]) -> TreeNode:
96
- """Create the root node of the thought tree."""
97
- prompt = f"""
98
- Initialize root thought node for query:
99
- Query: {query}
100
- Context: {json.dumps(context)}
101
-
102
- Provide:
103
- 1. Initial problem decomposition
104
- 2. Key aspects to explore
105
- 3. Evaluation criteria
106
- 4. Success metrics
107
-
108
- Format as:
109
- [Root]
110
- Decomposition: ...
111
- Aspects: ...
112
- Criteria: ...
113
- Metrics: ...
114
- """
115
-
116
- response = await context["groq_api"].predict(prompt)
117
- return self._parse_root_node(response["answer"], query)
118
-
119
- async def _build_tree(self, root: TreeNode, context: Dict[str, Any]) -> TreeNode:
120
- """Build and explore the thought tree."""
121
- # Initialize beam with root
122
- beam = [(root.evaluation_score, root)]
123
- visited: Set[str] = set()
124
-
125
- for depth in range(5):
126
- next_beam = []
127
 
128
- for _, node in beam:
129
- if node.id in visited:
130
- continue
131
-
132
- visited.add(node.id)
133
-
134
- # Generate child nodes
135
- children = await self._generate_children(node, context)
136
-
137
- # Evaluate and filter children
138
- evaluated_children = await self._evaluate_nodes(children, context)
139
-
140
- # Add to beam
141
- for child in evaluated_children:
142
- if child.evaluation_score > 0.4:
143
- next_beam.append((child.evaluation_score, child))
144
- node.children.append(child)
145
 
146
- # Select best nodes for next iteration
147
- beam = heapq.nlargest(3, next_beam, key=lambda x: x[0])
148
 
149
- if not beam:
150
- break
151
-
152
- return root
153
-
154
- async def _generate_children(self, parent: TreeNode, context: Dict[str, Any]) -> List[TreeNode]:
155
- """Generate child nodes for a given parent."""
156
- prompt = f"""
157
- Generate child thoughts for node:
158
- Parent: {json.dumps(self._node_to_dict(parent))}
159
- Context: {json.dumps(context)}
160
-
161
- For each child provide:
162
- 1. [Type]: {" | ".join([t.value for t in NodeType if t != NodeType.ROOT])}
163
- 2. [Content]: Main thought
164
- 3. [Confidence]: 0-1 score
165
- 4. [Rationale]: Why this follows from parent
166
- 5. [Potential]: Future exploration potential
167
-
168
- Format as:
169
- [C1]
170
- Type: ...
171
- Content: ...
172
- Confidence: ...
173
- Rationale: ...
174
- Potential: ...
175
- """
176
-
177
- response = await context["groq_api"].predict(prompt)
178
- return self._parse_child_nodes(response["answer"], parent)
179
-
180
- async def _evaluate_nodes(self, nodes: List[TreeNode], context: Dict[str, Any]) -> List[TreeNode]:
181
- """Evaluate a list of nodes."""
182
- prompt = f"""
183
- Evaluate thought nodes:
184
- Nodes: {json.dumps([self._node_to_dict(n) for n in nodes])}
185
- Context: {json.dumps(context)}
186
-
187
- For each node evaluate:
188
- 1. Logical coherence
189
- 2. Evidence support
190
- 3. Novelty value
191
- 4. Exploration potential
192
-
193
- Format as:
194
- [N1]
195
- Coherence: 0-1
196
- Evidence: 0-1
197
- Novelty: 0-1
198
- Potential: 0-1
199
- Overall: 0-1
200
- """
201
-
202
- response = await context["groq_api"].predict(prompt)
203
- return self._apply_evaluations(nodes, response["answer"])
204
-
205
- async def _find_best_paths(self, root: TreeNode, context: Dict[str, Any]) -> List[List[TreeNode]]:
206
- """Find the best paths through the tree."""
207
- paths = []
208
- current_path = [root]
209
-
210
- def dfs(node: TreeNode, path: List[TreeNode]):
211
- if not node.children:
212
- paths.append(path[:])
213
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
- # Sort children by score
216
- sorted_children = sorted(node.children, key=lambda x: x.evaluation_score, reverse=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
 
218
- # Explore top paths
219
- for child in sorted_children[:3]:
220
- path.append(child)
221
- dfs(child, path)
222
- path.pop()
223
-
224
- dfs(root, current_path)
225
-
226
- # Evaluate complete paths
227
- evaluated_paths = await self._evaluate_paths(paths, context)
228
-
229
- # Return top paths
230
- return sorted(evaluated_paths, key=lambda p: sum(n.evaluation_score for n in p), reverse=True)[:3]
231
-
232
- async def _synthesize_conclusion(self, paths: List[List[TreeNode]], context: Dict[str, Any]) -> Dict[str, Any]:
233
- """Synthesize final conclusion from best paths."""
234
- prompt = f"""
235
- Synthesize conclusion from thought paths:
236
- Paths: {json.dumps([[self._node_to_dict(n) for n in path] for path in paths])}
237
- Context: {json.dumps(context)}
238
-
239
- Provide:
240
- 1. Main conclusion
241
- 2. Confidence level
242
- 3. Reasoning trace
243
- 4. Supporting evidence
244
- 5. Alternative perspectives
245
- 6. Meta-insights
246
-
247
- Format as:
248
- [Conclusion]
249
- Answer: ...
250
- Confidence: ...
251
- Trace: ...
252
- Evidence: ...
253
- Alternatives: ...
254
-
255
- [Meta]
256
- Insights: ...
257
- Patterns: ...
258
- """
259
-
260
- response = await context["groq_api"].predict(prompt)
261
- return self._parse_conclusion(response["answer"])
262
-
263
- def _parse_root_node(self, response: str, query: str) -> TreeNode:
264
- """Parse root node from response."""
265
- root = TreeNode(
266
- id="root",
267
- type=NodeType.ROOT,
268
- content=query,
269
- confidence=1.0,
 
 
 
270
  children=[],
271
- parent=None,
272
- metadata={},
273
- depth=0
274
  )
 
275
 
276
- for line in response.split('\n'):
277
- line = line.strip()
278
- if line.startswith('Decomposition:'):
279
- root.metadata["decomposition"] = line[14:].strip()
280
- elif line.startswith('Aspects:'):
281
- root.metadata["aspects"] = [a.strip() for a in line[8:].split(',')]
282
- elif line.startswith('Criteria:'):
283
- root.metadata["criteria"] = [c.strip() for c in line[9:].split(',')]
284
- elif line.startswith('Metrics:'):
285
- root.metadata["metrics"] = [m.strip() for m in line[8:].split(',')]
286
-
287
- return root
288
-
289
- def _parse_child_nodes(self, response: str, parent: TreeNode) -> List[TreeNode]:
290
- """Parse child nodes from response."""
291
- children = []
292
- current = None
293
-
294
- for line in response.split('\n'):
295
- line = line.strip()
296
- if not line:
297
- continue
298
-
299
- if line.startswith('[C'):
300
- if current:
301
- children.append(current)
302
- current = None
303
- elif line.startswith('Type:'):
304
- type_str = line[5:].strip()
305
- try:
306
- node_type = NodeType(type_str.lower())
307
- current = TreeNode(
308
- id=f"{parent.id}_{len(children)}",
309
- type=node_type,
310
- content="",
311
- confidence=0.0,
312
- children=[],
313
- parent=parent,
314
- metadata={},
315
- depth=parent.depth + 1
316
- )
317
- except ValueError:
318
- logging.warning(f"Invalid node type: {type_str}")
319
- elif current:
320
- if line.startswith('Content:'):
321
- current.content = line[8:].strip()
322
- elif line.startswith('Confidence:'):
323
- try:
324
- current.confidence = float(line[11:].strip())
325
- except:
326
- current.confidence = 0.5
327
- elif line.startswith('Rationale:'):
328
- current.metadata["rationale"] = line[10:].strip()
329
- elif line.startswith('Potential:'):
330
- current.metadata["potential"] = line[10:].strip()
331
-
332
- if current:
333
- children.append(current)
334
-
335
- return children
336
-
337
- def _apply_evaluations(self, nodes: List[TreeNode], response: str) -> List[TreeNode]:
338
- """Apply evaluation scores to nodes."""
339
- current_node_idx = 0
340
- current_scores = {}
341
-
342
- for line in response.split('\n'):
343
- line = line.strip()
344
- if not line:
345
- continue
346
-
347
- if line.startswith('[N'):
348
- if current_scores and current_node_idx < len(nodes):
349
- nodes[current_node_idx].evaluation_score = current_scores.get("Overall", 0.0)
350
- nodes[current_node_idx].metadata.update(current_scores)
351
- current_node_idx += 1
352
- current_scores = {}
353
- elif ':' in line:
354
- key, value = line.split(':')
355
- try:
356
- current_scores[key.strip()] = float(value.strip())
357
- except:
358
- pass
359
-
360
- if current_scores and current_node_idx < len(nodes):
361
- nodes[current_node_idx].evaluation_score = current_scores.get("Overall", 0.0)
362
- nodes[current_node_idx].metadata.update(current_scores)
363
-
364
- return nodes
365
-
366
- async def _evaluate_paths(self, paths: List[List[TreeNode]], context: Dict[str, Any]) -> List[List[TreeNode]]:
367
- """Evaluate complete reasoning paths."""
368
- prompt = f"""
369
- Evaluate complete reasoning paths:
370
- Paths: {json.dumps([[self._node_to_dict(n) for n in path] for path in paths])}
371
- Context: {json.dumps(context)}
372
-
373
- For each path evaluate:
374
- 1. Coherence of progression
375
- 2. Evidence support
376
- 3. Conclusion strength
377
- 4. Novel insights
378
-
379
- Format as:
380
- [P1]
381
- Coherence: 0-1
382
- Evidence: 0-1
383
- Conclusion: 0-1
384
- Insights: 0-1
385
- Overall: 0-1
386
- """
387
-
388
- response = await context["groq_api"].predict(prompt)
389
- scores = self._parse_path_scores(response["answer"])
390
-
391
- # Apply scores to paths
392
- for i, path in enumerate(paths):
393
- if i < len(scores):
394
- for node in path:
395
- node.evaluation_score *= scores[i]
396
 
397
- return paths
398
-
399
- def _parse_path_scores(self, response: str) -> List[float]:
400
- """Parse path evaluation scores."""
401
- scores = []
402
- current_score = None
403
 
404
- for line in response.split('\n'):
405
- line = line.strip()
406
- if not line:
407
- continue
408
-
409
- if line.startswith('[P'):
410
- if current_score is not None:
411
- scores.append(current_score)
412
- current_score = None
413
- elif line.startswith('Overall:'):
414
- try:
415
- current_score = float(line[8:].strip())
416
- except:
417
- current_score = 0.5
418
 
419
- if current_score is not None:
420
- scores.append(current_score)
 
 
 
421
 
422
- return scores
423
-
424
- def _parse_conclusion(self, response: str) -> Dict[str, Any]:
425
- """Parse final conclusion."""
426
- conclusion = {
427
- "answer": "",
428
- "confidence": 0.0,
429
- "trace": [],
430
- "evidence": [],
431
- "alternatives": [],
432
- "meta_insights": []
433
- }
434
 
435
- section = None
436
- for line in response.split('\n'):
437
- line = line.strip()
438
- if not line:
439
- continue
440
-
441
- if line.startswith('[Conclusion]'):
442
- section = "conclusion"
443
- elif line.startswith('[Meta]'):
444
- section = "meta"
445
- elif section == "conclusion":
446
- if line.startswith('Answer:'):
447
- conclusion["answer"] = line[7:].strip()
448
- elif line.startswith('Confidence:'):
449
- try:
450
- conclusion["confidence"] = float(line[11:].strip())
451
- except:
452
- conclusion["confidence"] = 0.5
453
- elif line.startswith('Trace:'):
454
- conclusion["trace"] = [t.strip() for t in line[6:].split(',')]
455
- elif line.startswith('Evidence:'):
456
- conclusion["evidence"] = [e.strip() for e in line[9:].split(',')]
457
- elif line.startswith('Alternatives:'):
458
- conclusion["alternatives"] = [a.strip() for a in line[13:].split(',')]
459
- elif section == "meta":
460
- if line.startswith('Insights:'):
461
- conclusion["meta_insights"].extend([i.strip() for i in line[9:].split(',')])
462
 
463
- return conclusion
464
-
465
- def _node_to_dict(self, node: TreeNode) -> Dict[str, Any]:
466
- """Convert node to dictionary for serialization."""
467
- return {
468
- "id": node.id,
469
- "type": node.type.value,
470
- "content": node.content,
471
- "confidence": node.confidence,
472
- "evaluation_score": node.evaluation_score,
473
- "metadata": node.metadata,
474
- "depth": node.depth
475
- }
476
-
477
- def _tree_to_dict(self, root: TreeNode) -> Dict[str, Any]:
478
- """Convert entire tree to dictionary."""
479
- def convert_node(node: TreeNode) -> Dict[str, Any]:
480
- node_dict = self._node_to_dict(node)
481
- node_dict["children"] = [convert_node(c) for c in node.children]
482
- return node_dict
483
 
484
- return convert_node(root)
485
-
486
- def _path_to_dict(self, path: List[TreeNode]) -> List[Dict[str, Any]]:
487
- """Convert path to dictionary."""
488
- return [self._node_to_dict(n) for n in path]
489
-
490
- def _update_history(self, root: TreeNode):
491
- """Update node history."""
492
- def add_to_history(node: TreeNode):
493
- self.node_history[node.id] = node
494
  for child in node.children:
495
- add_to_history(child)
496
 
497
- add_to_history(root)
498
-
499
- def _update_patterns(self, paths: List[List[TreeNode]]):
500
- """Update path patterns."""
501
- for path in paths:
502
- pattern = "->".join(n.type.value for n in path)
503
- self.path_patterns[pattern] += path[-1].evaluation_score
504
-
505
- def get_node_history(self) -> Dict[str, Dict[str, Any]]:
506
- """Get history of all nodes."""
507
- return {k: self._node_to_dict(v) for k, v in self.node_history.items()}
508
-
509
- def get_successful_patterns(self) -> Dict[str, float]:
510
- """Get successful reasoning patterns."""
511
- return dict(sorted(self.path_patterns.items(), key=lambda x: x[1], reverse=True))
512
-
513
- def clear_history(self):
514
- """Clear node history and patterns."""
515
- self.node_history.clear()
516
- self.path_patterns.clear()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  from enum import Enum
8
  import heapq
9
  from collections import defaultdict
10
+ from datetime import datetime
11
 
12
+ from .base import ReasoningStrategy, StrategyResult
13
 
14
  class NodeType(Enum):
15
  """Types of nodes in the thought tree."""
 
33
  metadata: Dict[str, Any]
34
  depth: int
35
  evaluation_score: float = 0.0
36
+ timestamp: str = datetime.now().isoformat()
37
 
38
  class TreeOfThoughtsStrategy(ReasoningStrategy):
39
  """
 
50
  parallel_threshold: int = 3,
51
  learning_rate: float = 0.1,
52
  strategy_weights: Optional[Dict[str, float]] = None):
53
+ """Initialize Tree of Thoughts reasoning."""
54
+ super().__init__()
55
  self.min_confidence = min_confidence
56
  self.parallel_threshold = parallel_threshold
57
  self.learning_rate = learning_rate
58
  self.strategy_weights = strategy_weights or {
59
+ 'hypothesis': 0.3,
60
+ 'evidence': 0.2,
61
+ 'analysis': 0.2,
62
+ 'synthesis': 0.15,
63
+ 'evaluation': 0.15
64
  }
 
 
65
 
66
+ # Initialize tree
67
+ self.root: Optional[TreeNode] = None
68
+ self.current_node: Optional[TreeNode] = None
69
+
70
+ # Performance tracking
71
+ self.performance_metrics = {
72
+ 'tree_depth': 0,
73
+ 'num_nodes': 0,
74
+ 'branching_factor': 0.0,
75
+ 'avg_confidence': 0.0,
76
+ 'pruned_nodes': 0
77
+ }
78
+
79
+ async def reason(
80
+ self,
81
+ query: str,
82
+ context: Dict[str, Any]
83
+ ) -> StrategyResult:
84
+ """
85
+ Apply Tree of Thoughts reasoning to analyze the query.
86
+
87
+ Args:
88
+ query: The input query to reason about
89
+ context: Additional context and parameters
90
+
91
+ Returns:
92
+ StrategyResult containing the reasoning tree and confidence
93
+ """
94
  try:
95
  # Initialize root node
96
+ self.root = TreeNode(
97
+ id="root",
98
+ type=NodeType.ROOT,
99
+ content=query,
100
+ confidence=1.0,
101
+ children=[],
102
+ parent=None,
103
+ metadata={"query": query},
104
+ depth=0
105
+ )
106
+ self.current_node = self.root
107
 
108
+ # Generate initial hypotheses
109
+ await self._generate_hypotheses(context)
110
 
111
+ # Gather evidence
112
+ await self._gather_evidence(context)
113
 
114
+ # Analyze evidence
115
+ await self._analyze_evidence(context)
116
 
117
+ # Synthesize findings
118
+ await self._synthesize_findings(context)
 
119
 
120
+ # Evaluate paths
121
+ await self._evaluate_paths(context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
+ # Find best path
124
+ best_path = self._find_best_path()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
+ # Generate conclusion
127
+ conclusion = await self._generate_conclusion(best_path, context)
128
 
129
+ # Update performance metrics
130
+ self._update_metrics()
131
+
132
+ return StrategyResult(
133
+ strategy_type="tree_of_thoughts",
134
+ success=True,
135
+ answer=conclusion.content,
136
+ confidence=conclusion.confidence,
137
+ reasoning_trace=[{
138
+ "step": str(node.type.value),
139
+ "content": node.content,
140
+ "confidence": node.confidence,
141
+ "depth": node.depth,
142
+ "score": node.evaluation_score,
143
+ "metadata": node.metadata,
144
+ "timestamp": node.timestamp
145
+ } for node in self._traverse_tree()],
146
+ metadata={
147
+ "tree_depth": self.performance_metrics['tree_depth'],
148
+ "num_nodes": self.performance_metrics['num_nodes'],
149
+ "branching_factor": self.performance_metrics['branching_factor']
150
+ },
151
+ performance_metrics=self.performance_metrics
152
+ )
153
+
154
+ except Exception as e:
155
+ logging.error(f"Tree of Thoughts reasoning error: {str(e)}")
156
+ return StrategyResult(
157
+ strategy_type="tree_of_thoughts",
158
+ success=False,
159
+ answer=None,
160
+ confidence=0.0,
161
+ reasoning_trace=[{
162
+ "step": "error",
163
+ "error": str(e),
164
+ "timestamp": datetime.now().isoformat()
165
+ }],
166
+ metadata={"error": str(e)},
167
+ performance_metrics=self.performance_metrics
168
+ )
169
+
170
+ async def _generate_hypotheses(self, context: Dict[str, Any]) -> None:
171
+ """Generate initial hypotheses as child nodes."""
172
+ hypotheses = self._extract_hypotheses(self.root.content, context)
173
+
174
+ for h_content in hypotheses:
175
+ node = TreeNode(
176
+ id=f"h{len(self.root.children)}",
177
+ type=NodeType.HYPOTHESIS,
178
+ content=h_content,
179
+ confidence=self._calculate_confidence(h_content, context),
180
+ children=[],
181
+ parent=self.root,
182
+ metadata={"type": "hypothesis"},
183
+ depth=1
184
+ )
185
+ self.root.children.append(node)
186
+
187
+ async def _gather_evidence(self, context: Dict[str, Any]) -> None:
188
+ """Gather evidence for each hypothesis."""
189
+ for hypothesis in self.root.children:
190
+ evidence = self._find_evidence(hypothesis.content, context)
191
+
192
+ for e_content in evidence:
193
+ node = TreeNode(
194
+ id=f"{hypothesis.id}_e{len(hypothesis.children)}",
195
+ type=NodeType.EVIDENCE,
196
+ content=e_content,
197
+ confidence=self._calculate_confidence(e_content, context),
198
+ children=[],
199
+ parent=hypothesis,
200
+ metadata={"type": "evidence"},
201
+ depth=hypothesis.depth + 1
202
+ )
203
+ hypothesis.children.append(node)
204
+
205
+ async def _analyze_evidence(self, context: Dict[str, Any]) -> None:
206
+ """Analyze gathered evidence."""
207
+ for hypothesis in self.root.children:
208
+ for evidence in hypothesis.children:
209
+ analysis = self._analyze_node(evidence, context)
210
 
211
+ node = TreeNode(
212
+ id=f"{evidence.id}_a",
213
+ type=NodeType.ANALYSIS,
214
+ content=analysis,
215
+ confidence=self._calculate_confidence(analysis, context),
216
+ children=[],
217
+ parent=evidence,
218
+ metadata={"type": "analysis"},
219
+ depth=evidence.depth + 1
220
+ )
221
+ evidence.children.append(node)
222
+
223
+ async def _synthesize_findings(self, context: Dict[str, Any]) -> None:
224
+ """Synthesize findings from analysis."""
225
+ for hypothesis in self.root.children:
226
+ synthesis = self._synthesize_branch(hypothesis, context)
227
 
228
+ node = TreeNode(
229
+ id=f"{hypothesis.id}_s",
230
+ type=NodeType.SYNTHESIS,
231
+ content=synthesis,
232
+ confidence=self._calculate_confidence(synthesis, context),
233
+ children=[],
234
+ parent=hypothesis,
235
+ metadata={"type": "synthesis"},
236
+ depth=hypothesis.depth + 1
237
+ )
238
+ hypothesis.children.append(node)
239
+
240
+ async def _evaluate_paths(self, context: Dict[str, Any]) -> None:
241
+ """Evaluate different reasoning paths."""
242
+ for hypothesis in self.root.children:
243
+ evaluation = self._evaluate_branch(hypothesis, context)
244
+
245
+ node = TreeNode(
246
+ id=f"{hypothesis.id}_e",
247
+ type=NodeType.EVALUATION,
248
+ content=evaluation,
249
+ confidence=self._calculate_confidence(evaluation, context),
250
+ children=[],
251
+ parent=hypothesis,
252
+ metadata={"type": "evaluation"},
253
+ depth=hypothesis.depth + 1
254
+ )
255
+ hypothesis.children.append(node)
256
+
257
+ def _find_best_path(self) -> List[TreeNode]:
258
+ """Find the path with highest confidence."""
259
+ best_path = []
260
+ best_score = 0.0
261
+
262
+ for hypothesis in self.root.children:
263
+ path_score = self._calculate_path_score(hypothesis)
264
+ if path_score > best_score:
265
+ best_score = path_score
266
+ best_path = self._get_path(hypothesis)
267
+
268
+ return best_path
269
+
270
+ async def _generate_conclusion(
271
+ self,
272
+ path: List[TreeNode],
273
+ context: Dict[str, Any]
274
+ ) -> TreeNode:
275
+ """Generate final conclusion from best path."""
276
+ conclusion_content = self._synthesize_path(path, context)
277
+
278
+ node = TreeNode(
279
+ id="conclusion",
280
+ type=NodeType.CONCLUSION,
281
+ content=conclusion_content,
282
+ confidence=self._calculate_path_confidence(path),
283
  children=[],
284
+ parent=self.root,
285
+ metadata={"type": "conclusion", "path_length": len(path)},
286
+ depth=max(n.depth for n in path) + 1
287
  )
288
+ self.root.children.append(node)
289
 
290
+ return node
291
+
292
+ def _calculate_confidence(
293
+ self,
294
+ content: str,
295
+ context: Dict[str, Any]
296
+ ) -> float:
297
+ """Calculate confidence score for content."""
298
+ # Base confidence
299
+ confidence = 0.5
300
+
301
+ # Adjust based on content length
302
+ words = content.split()
303
+ if len(words) > 50:
304
+ confidence += 0.1
305
+ if len(words) > 100:
306
+ confidence += 0.1
307
+
308
+ # Adjust based on context match
309
+ if context.get('keywords'):
310
+ matches = sum(1 for k in context['keywords'] if k in content.lower())
311
+ confidence += min(0.3, matches * 0.1)
312
+
313
+ return min(1.0, confidence)
314
+
315
+ def _calculate_path_score(self, node: TreeNode) -> float:
316
+ """Calculate score for a path in the tree."""
317
+ score = node.confidence
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
+ # Consider child nodes
320
+ if node.children:
321
+ child_scores = [self._calculate_path_score(c) for c in node.children]
322
+ score += max(child_scores) * 0.8 # Decay factor
 
 
323
 
324
+ return score
325
+
326
+ def _calculate_path_confidence(self, path: List[TreeNode]) -> float:
327
+ """Calculate overall confidence for a path."""
328
+ if not path:
329
+ return 0.0
330
+
331
+ # Weight confidences by node type
332
+ weighted_sum = sum(
333
+ node.confidence * self.strategy_weights.get(node.type.value, 0.1)
334
+ for node in path
335
+ )
 
 
336
 
337
+ # Normalize by weights
338
+ total_weight = sum(
339
+ self.strategy_weights.get(node.type.value, 0.1)
340
+ for node in path
341
+ )
342
 
343
+ return weighted_sum / total_weight if total_weight > 0 else 0.0
344
+
345
+ def _get_path(self, node: TreeNode) -> List[TreeNode]:
346
+ """Get path from root to node."""
347
+ path = []
348
+ current = node
 
 
 
 
 
 
349
 
350
+ while current:
351
+ path.append(current)
352
+ current = current.parent
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
+ return list(reversed(path))
355
+
356
+ def _traverse_tree(self) -> List[TreeNode]:
357
+ """Traverse tree in pre-order."""
358
+ nodes = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
+ def traverse(node: TreeNode):
361
+ nodes.append(node)
 
 
 
 
 
 
 
 
362
  for child in node.children:
363
+ traverse(child)
364
 
365
+ if self.root:
366
+ traverse(self.root)
367
+
368
+ return nodes
369
+
370
+ def _extract_hypotheses(
371
+ self,
372
+ content: str,
373
+ context: Dict[str, Any]
374
+ ) -> List[str]:
375
+ """Extract potential hypotheses from content."""
376
+ # Simple extraction based on keywords
377
+ # Could be enhanced with NLP
378
+ hypotheses = []
379
+
380
+ keywords = context.get('keywords', [])
381
+ sentences = content.split('.')
382
+
383
+ for sentence in sentences:
384
+ if any(k in sentence.lower() for k in keywords):
385
+ hypotheses.append(sentence.strip())
386
+
387
+ return hypotheses or ["Default hypothesis"]
388
+
389
+ def _find_evidence(
390
+ self,
391
+ hypothesis: str,
392
+ context: Dict[str, Any]
393
+ ) -> List[str]:
394
+ """Find evidence supporting hypothesis."""
395
+ evidence = []
396
+
397
+ if 'evidence' in context:
398
+ for e in context['evidence']:
399
+ if any(term in e.lower() for term in hypothesis.lower().split()):
400
+ evidence.append(e)
401
+
402
+ return evidence or ["No direct evidence found"]
403
+
404
+ def _analyze_node(
405
+ self,
406
+ node: TreeNode,
407
+ context: Dict[str, Any]
408
+ ) -> str:
409
+ """Analyze a node's content."""
410
+ return f"Analysis of {node.content}"
411
+
412
+ def _synthesize_branch(
413
+ self,
414
+ node: TreeNode,
415
+ context: Dict[str, Any]
416
+ ) -> str:
417
+ """Synthesize findings from a branch."""
418
+ return f"Synthesis of branch {node.id}"
419
+
420
+ def _evaluate_branch(
421
+ self,
422
+ node: TreeNode,
423
+ context: Dict[str, Any]
424
+ ) -> str:
425
+ """Evaluate a branch of the tree."""
426
+ return f"Evaluation of branch {node.id}"
427
+
428
+ def _synthesize_path(
429
+ self,
430
+ path: List[TreeNode],
431
+ context: Dict[str, Any]
432
+ ) -> str:
433
+ """Synthesize conclusion from path."""
434
+ return "Conclusion: " + " -> ".join(n.content for n in path)
435
+
436
+ def _update_metrics(self) -> None:
437
+ """Update performance metrics."""
438
+ if self.root:
439
+ nodes = self._traverse_tree()
440
+ depths = [n.depth for n in nodes]
441
+
442
+ # Count nodes with children
443
+ internal_nodes = sum(1 for n in nodes if n.children)
444
+
445
+ self.performance_metrics.update({
446
+ 'tree_depth': max(depths),
447
+ 'num_nodes': len(nodes),
448
+ 'branching_factor': len(nodes) / max(1, internal_nodes),
449
+ 'avg_confidence': sum(n.confidence for n in nodes) / len(nodes),
450
+ 'pruned_nodes': self.performance_metrics['pruned_nodes']
451
+ })