File size: 4,169 Bytes
a980081
49405a0
9c753c0
 
a980081
 
 
 
 
 
 
 
 
49405a0
 
 
a980081
 
 
 
 
 
9c753c0
33bbc44
9c753c0
33bbc44
9c753c0
 
 
63af69d
9c753c0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a980081
 
2fc6631
49405a0
a980081
 
 
9c753c0
20a95f3
 
 
 
 
 
 
a980081
 
 
 
49405a0
 
20a95f3
49405a0
 
 
20a95f3
 
49405a0
 
 
 
 
 
a980081
20a95f3
9c753c0
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
import os
import re
import smolagents
from smolagents import CodeAgent, DuckDuckGoSearchTool, tool

@tool
def calculate_math(expression: str) -> str:
    """Useful for evaluating math expressions or numerical problems safely.
    Args:
        expression: A valid mathematical string to evaluate, like '2 + 2' or '312 * 43'.
    """
    try:
        allowed_chars = "0123456789+-*/(). "
        cleaned = "".join(c for c in expression if c in allowed_chars)
        if cleaned:
            return str(eval(cleaned))
        return "Error: Invalid characters in math expression."
    except Exception as e:
        return f"Error executing calculation: {str(e)}"

class CourseEvaluationAgent:
    def __init__(self):
        print("[Agent System] Initializing Adaptive Bootstrapper...")
        hf_token = os.getenv("HF_TOKEN")
        model_name = "Qwen/Qwen2.5-Coder-32B-Instruct"
        
        # --- DYNAMIC CLASS DETECTION ---
        # We check the library metadata directly to find the right class
        self.model = None
        
        for class_name in ["InferenceClientModel", "HfApiModel", "HfApiEngine", "LiteLLMModel"]:
            if hasattr(smolagents, class_name):
                try:
                    TargetClass = getattr(smolagents, class_name)
                    print(f"[Agent System] Trying class: {class_name}")
                    
                    # Try instantiating with keyword parameters
                    if class_name == "HfApiEngine":
                        self.model = TargetClass(model_id=model_name, token=hf_token)
                    else:
                        self.model = TargetClass(model_id=model_name, token=hf_token)
                        
                    print(f"[Agent System] Successfully bound engine via {class_name}!")
                    break
                except Exception as inner_e:
                    print(f"[Agent System] {class_name} failed setup: {inner_e}")
                    continue

        # Ultimate fallback: If classes fail, see if ApiModel works directly
        if self.model is None and hasattr(smolagents, "ApiModel"):
            try:
                self.model = getattr(smolagents, "ApiModel")(model_id=model_name, token=hf_token)
                print("[Agent System] Bound engine via base ApiModel fallback.")
            except Exception:
                pass

        # If everything failed, we let CodeAgent try raw string fallback
        if self.model is None:
            print("[Agent System] Warning: Forcing direct string fallback mode.")
            self.model = model_name

        # --- BUILD CODE AGENT ---
        # Safely pass our resolved backend configuration to the agent
        self.agent = CodeAgent(
            tools=[DuckDuckGoSearchTool(), calculate_math], 
            model=self.model,
            max_steps=5,
            verbosity_level=1
        )
        
        # Inject styling rules cleanly
        if "system_prompt" in self.agent.prompt_templates:
            self.agent.prompt_templates["system_prompt"] += (
                "\n\nCRITICAL SYSTEM RULE:\n"
                "Your final response after using tools MUST contain ONLY the direct, raw answer text "
                "(e.g., '42', 'Paris', 'John Doe'). Absolutely DO NOT include conversational padding like "
                "'The answer is' or summary sentences. Output just the clean answer string."
            )

    def run_task(self, question: str) -> str:
        try:
            output = self.agent.run(question)
            final_ans = str(output).strip()
            
            # Post-processing to strip out accidental conversational wrappers
            clean_patterns = [
                r"^[Tt]he answer is\s*[:\-]?\s*",
                r"^[Ff]inal [Aa]nswer\s*[:\-]?\s*",
                r"^[\"']", 
                r"[\"']$"
            ]
            for pattern in clean_patterns:
                final_ans = re.sub(pattern, "", final_ans)
                
            return final_ans.strip()
            
        except Exception as e:
            print(f"[CRITICAL AGENT EXCEPTION]: {str(e)}")
            return f"Runtime error: {str(e)[:50]}"