File size: 14,510 Bytes
c922f8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
"""
Reasoning tools for the GAIA agent.

This module provides tools for complex reasoning tasks, including:
- Step-by-step reasoning
- Mathematical calculations
- Fact verification

All tools handle errors gracefully and provide detailed error messages.
"""

import logging
import traceback
import re
import math
import json
from typing import Dict, Any, List, Optional, Union, Tuple, Callable
from datetime import datetime

import operator
from sympy import symbols, sympify, solve, simplify
import numpy as np

from src.gaia.agent.config import get_model_config, get_tool_config
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser

logger = logging.getLogger("gaia_agent.tools.reasoning")

class StepByStepReasoner:
    """Tool for performing step-by-step reasoning."""
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        """
        Initialize the step-by-step reasoner.
        
        Args:
            config: Optional configuration dictionary
        """
        self.model_config = config or get_model_config()
        self.model = ChatOpenAI(
            model=self.model_config.get("reasoning_model", "gpt-4o"),
            temperature=self.model_config.get("temperature", 0.1),
            max_tokens=self.model_config.get("max_tokens", 4096)
        )
    
    def reason(self, question: str, context: Optional[str] = None) -> Dict[str, Any]:
        """
        Perform step-by-step reasoning on a question.
        
        Args:
            question: The question to reason about
            context: Optional context information
            
        Returns:
            Dictionary containing the reasoning steps and conclusion
            
        Raises:
            Exception: If an error occurs during reasoning
        """
        
        try:
            prompt_template = """You are an expert at step-by-step reasoning.
            
            Your task is to solve the following problem by breaking it down into clear, logical steps.
            
            {context_text}
            
            Question: {question}
            
            Think through this step-by-step and provide your reasoning in the following JSON format:
            {{
                "steps": [
                    {{
                        "step_number": 1,
                        "reasoning": "First step of reasoning",
                        "intermediate_conclusion": "Conclusion from this step"
                    }},
                    ...
                ],
                "final_conclusion": "The final answer based on all steps",
                "confidence": 0.95  // A number between 0 and 1 indicating your confidence
            }}
            
            JSON Response:"""
            
            context_text = f"Context: {context}" if context else "No additional context provided."
            
            prompt = PromptTemplate.from_template(prompt_template)
            
            chain = prompt | self.model | StrOutputParser()
            
            result = chain.invoke({
                "question": question,
                "context_text": context_text
            })
            
            parsed_result = json.loads(result)
            
            
            return parsed_result
        
        except Exception as e:
            logger.error(f"Error during step-by-step reasoning: {str(e)}")
            logger.error(traceback.format_exc())
            raise Exception(f"Step-by-step reasoning failed: {str(e)}")
    
    def chain_of_thought(self, question: str, context: Optional[str] = None) -> str:
        """
        Perform chain-of-thought reasoning and return a textual response.
        
        Args:
            question: The question to reason about
            context: Optional context information
            
        Returns:
            String containing the reasoning and conclusion
            
        Raises:
            Exception: If an error occurs during reasoning
        """
        
        try:
            prompt_template = """You are an expert at chain-of-thought reasoning.
            
            Your task is to solve the following problem by thinking step-by-step.
            
            {context_text}
            
            Question: {question}
            
            Let's think through this step-by-step:"""
            
            context_text = f"Context: {context}" if context else "No additional context provided."
            
            prompt = PromptTemplate.from_template(prompt_template)
            
            chain = prompt | self.model | StrOutputParser()
            
            result = chain.invoke({
                "question": question,
                "context_text": context_text
            })
            
            
            return result
        
        except Exception as e:
            logger.error(f"Error during chain-of-thought reasoning: {str(e)}")
            logger.error(traceback.format_exc())
            raise Exception(f"Chain-of-thought reasoning failed: {str(e)}")


class MathCalculator:
    """Tool for performing mathematical calculations."""
    
    def __init__(self):
        """Initialize the math calculator."""
        self.operations = {
            '+': operator.add,
            '-': operator.sub,
            '*': operator.mul,
            '/': operator.truediv,
            '^': operator.pow,
            '**': operator.pow,
            '%': operator.mod,
            '//': operator.floordiv
        }
    
    def calculate(self, expression: str) -> Dict[str, Any]:
        """
        Evaluate a mathematical expression.
        
        Args:
            expression: The mathematical expression to evaluate
            
        Returns:
            Dictionary containing the result and steps
            
        Raises:
            Exception: If an error occurs during calculation
        """
        
        try:
            cleaned_expr = self._clean_expression(expression)
            
            result = float(sympify(cleaned_expr).evalf())
            
            if result.is_integer():
                result = int(result)
            
            return {
                "expression": expression,
                "result": result,
                "steps": [f"Evaluated expression: {cleaned_expr}", f"Result: {result}"]
            }
        
        except Exception as e:
            logger.error(f"Error calculating expression: {str(e)}")
            logger.error(traceback.format_exc())
            raise Exception(f"Mathematical calculation failed: {str(e)}")
    
    def solve_equation(self, equation: str, variable: str = 'x') -> Dict[str, Any]:
        """
        Solve a mathematical equation for a variable.
        
        Args:
            equation: The equation to solve (e.g., "x + 5 = 10")
            variable: The variable to solve for (default: 'x')
            
        Returns:
            Dictionary containing the solution and steps
            
        Raises:
            Exception: If an error occurs during solving
        """
        
        try:
            if '=' in equation:
                left_side, right_side = equation.split('=', 1)
                equation_sympy = f"({left_side.strip()}) - ({right_side.strip()})"
            else:
                equation_sympy = equation
            
            var = symbols(variable)
            
            expr = sympify(equation_sympy)
            solutions = solve(expr, var)
            
            formatted_solutions = []
            for sol in solutions:
                if sol.is_real:
                    try:
                        float_sol = float(sol)
                        if float_sol.is_integer():
                            formatted_solutions.append(int(float_sol))
                        else:
                            formatted_solutions.append(float_sol)
                    except:
                        formatted_solutions.append(str(sol))
                else:
                    formatted_solutions.append(str(sol))
            
            return {
                "equation": equation,
                "variable": variable,
                "solutions": formatted_solutions,
                "steps": [
                    f"Parsed equation: {equation}",
                    f"Rearranged to: {equation_sympy} = 0",
                    f"Solved for {variable}",
                    f"Solutions: {', '.join(map(str, formatted_solutions))}"
                ]
            }
        
        except Exception as e:
            logger.error(f"Error solving equation: {str(e)}")
            logger.error(traceback.format_exc())
            raise Exception(f"Equation solving failed: {str(e)}")
    
    def _clean_expression(self, expression: str) -> str:
        """
        Clean a mathematical expression for evaluation.
        
        Args:
            expression: The expression to clean
            
        Returns:
            Cleaned expression
        """
        expression = expression.strip()
        
        replacements = {
            'sin': 'math.sin',
            'cos': 'math.cos',
            'tan': 'math.tan',
            'log': 'math.log',
            'exp': 'math.exp',
            'sqrt': 'math.sqrt',
            'pi': 'math.pi',
            'e': 'math.e'
        }
        
        for old, new in replacements.items():
            expression = re.sub(r'\b' + old + r'\b', new, expression)
        
        return expression


class FactVerifier:
    """Tool for verifying facts and claims."""
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        """
        Initialize the fact verifier.
        
        Args:
            config: Optional configuration dictionary
        """
        self.model_config = config or get_model_config()
        self.model = ChatOpenAI(
            model=self.model_config.get("reasoning_model", "gpt-4o"),
            temperature=self.model_config.get("temperature", 0.1),
            max_tokens=self.model_config.get("max_tokens", 4096)
        )
    
    def verify(self, claim: str, evidence: Optional[str] = None) -> Dict[str, Any]:
        """
        Verify a claim against evidence.
        
        Args:
            claim: The claim to verify
            evidence: Optional evidence to use for verification
            
        Returns:
            Dictionary containing verification results
            
        Raises:
            Exception: If an error occurs during verification
        """
        
        try:
            prompt_template = """You are an expert fact checker.
            
            Your task is to verify the following claim based on the provided evidence.
            
            Claim: {claim}
            
            Evidence: {evidence}
            
            Analyze the claim and evidence carefully. Provide your verification in the following JSON format:
            {{
                "is_verifiable": true/false,  // Whether the claim can be verified with the given evidence
                "verification_result": "supported"/"contradicted"/"insufficient_evidence",
                "confidence": 0.95,  // A number between 0 and 1 indicating your confidence
                "reasoning": "Your step-by-step reasoning process",
                "missing_information": "What additional information would help verify this claim"
            }}
            
            JSON Response:"""
            
            evidence_text = evidence if evidence else "No evidence provided."
            
            prompt = PromptTemplate.from_template(prompt_template)
            
            chain = prompt | self.model | StrOutputParser()
            
            result = chain.invoke({
                "claim": claim,
                "evidence": evidence_text
            })
            
            parsed_result = json.loads(result)
            
            
            return parsed_result
        
        except Exception as e:
            logger.error(f"Error during fact verification: {str(e)}")
            logger.error(traceback.format_exc())
            raise Exception(f"Fact verification failed: {str(e)}")
    
    def compare_claims(self, claim1: str, claim2: str) -> Dict[str, Any]:
        """
        Compare two claims for consistency.
        
        Args:
            claim1: The first claim
            claim2: The second claim
            
        Returns:
            Dictionary containing comparison results
            
        Raises:
            Exception: If an error occurs during comparison
        """
        
        try:
            prompt_template = """You are an expert at analyzing logical consistency.
            
            Your task is to compare the following two claims and determine if they are consistent, contradictory, or unrelated.
            
            Claim 1: {claim1}
            
            Claim 2: {claim2}
            
            Analyze the claims carefully. Provide your analysis in the following JSON format:
            {{
                "relationship": "consistent"/"contradictory"/"unrelated",
                "confidence": 0.95,  // A number between 0 and 1 indicating your confidence
                "reasoning": "Your step-by-step reasoning process",
                "implications": "What the relationship between these claims implies"
            }}
            
            JSON Response:"""
            
            prompt = PromptTemplate.from_template(prompt_template)
            
            chain = prompt | self.model | StrOutputParser()
            
            result = chain.invoke({
                "claim1": claim1,
                "claim2": claim2
            })
            
            parsed_result = json.loads(result)
            
            
            return parsed_result
        
        except Exception as e:
            logger.error(f"Error during claim comparison: {str(e)}")
            logger.error(traceback.format_exc())
            raise Exception(f"Claim comparison failed: {str(e)}")


def create_step_by_step_reasoner() -> StepByStepReasoner:
    """Create a step-by-step reasoner instance."""
    return StepByStepReasoner()

def create_math_calculator() -> MathCalculator:
    """Create a math calculator instance."""
    return MathCalculator()

def create_fact_verifier() -> FactVerifier:
    """Create a fact verifier instance."""
    return FactVerifier()