File size: 4,118 Bytes
c767d20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Real `smolagents` CodeAgent integration for the final project.

This module initializes a `CodeAgent` with standard tools and exposes a 
`run_agent(prompt)` function returning the final answer string.

Notes:
- Requires `smolagents` installed in the environment.
- For serverless inference via Hugging Face, you must be logged in 
  (`huggingface-cli login`) or have `HF_TOKEN` environment variable set,
  and have sufficient provider credits.
"""
from typing import List, Any
import logging
import os

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

from smolagents import (
    CodeAgent,
    InferenceClientModel,
    DuckDuckGoSearchTool,
    FinalAnswerTool,
    VisitWebpageTool,
)

# Get HF token from environment (set by huggingface-cli login or HF_TOKEN env var)
HF_TOKEN = os.environ.get("HF_TOKEN")
if not HF_TOKEN:
    logger.warning(
        "HF_TOKEN not found in environment. Run 'huggingface-cli login' or set HF_TOKEN env var. "
        "CodeAgent initialization will fail without valid credentials."
    )


def make_code_agent(
    max_steps: int = 8,
    verbosity: int = 1,
    model_name: str | None = None,
):
    """Create and return a smolagents CodeAgent configured with standard tools.
    
    Args:
        max_steps: max reasoning steps for the agent.
        verbosity: logging/output verbosity level.
        model_name: HF model ID for serverless inference (e.g., 'allenai/Olmo-3-7B-Instruct').
                   If None, uses the default InferenceClientModel.
    
    Returns:
        CodeAgent instance.
    
    Raises:
        Exception: if InferenceClientModel initialization fails (missing HF login/credits).
    """
    tools: List[Any] = []

    # Standard tools from smolagents
    try:
        tools.append(DuckDuckGoSearchTool())
    except Exception as e:
        logger.debug("DuckDuckGoSearchTool unavailable: %s", e)

    try:
        tools.append(VisitWebpageTool())
    except Exception as e:
        logger.debug("VisitWebpageTool unavailable: %s", e)

    try:
        tools.append(FinalAnswerTool())
    except Exception as e:
        logger.debug("FinalAnswerTool unavailable: %s", e)

    # Initialize serverless inference model
    try:
        if model_name:
            try:
                model = InferenceClientModel(repo_id=model_name, token=HF_TOKEN)
            except Exception:
                try:
                    model = InferenceClientModel(model_id=model_name, token=HF_TOKEN)
                except Exception:
                    model = InferenceClientModel(token=HF_TOKEN)
        else:
            model = InferenceClientModel(token=HF_TOKEN)
        logger.info("InferenceClientModel initialized successfully with HF_TOKEN")
    except Exception as e:
        logger.error(
            "InferenceClientModel initialization failed (ensure HF_TOKEN is set and has credits): %s", e
        )
        raise

    agent = CodeAgent(tools=tools, model=model, max_steps=max_steps, verbosity_level=verbosity)
    return agent


_AGENT_SINGLETON = None


def get_agent():
    """Get or create the singleton CodeAgent instance."""
    global _AGENT_SINGLETON
    if _AGENT_SINGLETON is None:
        _AGENT_SINGLETON = make_code_agent(model_name="allenai/Olmo-3-7B-Think")
    return _AGENT_SINGLETON


def run_agent(prompt: str) -> str:
    """Run the CodeAgent and return the final answer string.
    
    Args:
        prompt: the reasoning task/question for the agent.
    
    Returns:
        The agent's final answer as a string.
    
    Raises:
        Exception: if CodeAgent.run fails (e.g., no HF credentials or credits).
    """
    agent = get_agent()
    res = agent.run(prompt)
    
    # Normalize response to string
    if isinstance(res, dict):
        for key in ("answer", "final_answer", "final"):
            if key in res and isinstance(res[key], str):
                return res[key].strip()
        return str(res)
    
    if isinstance(res, str):
        return res.strip()
    
    return str(res)


if __name__ == "__main__":
    print(run_agent("Give me a short list of 3 fruits."))