File size: 8,626 Bytes
3545685
9b5b26a
 
 
c19d193
e636c57
8fe38ae
9b5b26a
5df72d6
9b5b26a
7110895
 
8fe38ae
7110895
9b5b26a
7110895
 
 
 
 
8fe38ae
 
9b5b26a
7110895
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8fe38ae
7110895
 
 
8fe38ae
7110895
 
 
8fe38ae
7110895
 
8fe38ae
7110895
8fe38ae
7110895
 
 
 
8fe38ae
7110895
8fe38ae
 
 
 
 
 
 
 
7110895
8fe38ae
7110895
8fe38ae
7110895
9b5b26a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c01ffb
 
6aae614
ae7a494
 
 
 
3545685
 
 
 
 
13d500a
8c01ffb
 
9b5b26a
 
8c01ffb
861422e
 
9b5b26a
8c01ffb
8fe992b
1feba8c
8c01ffb
 
 
 
8fe992b
 
9b5b26a
e636c57
4977da9
 
 
 
 
 
b9a86b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4977da9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
997d15e
 
 
 
 
 
 
 
 
4977da9
e636c57
 
8fe38ae
 
 
 
 
 
 
 
 
 
 
 
 
e636c57
 
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
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
import gradio as gr
from tools.final_answer import FinalAnswerTool

# Below is an example of a tool that does nothing. Amaze us with your creativity !
@tool
def my_custom_tool(arg1: str, arg2: int) -> str:  # it's important to specify the return type
    # Keep this format for the description / args / args description but feel free to modify the tool
    """Fetch and nicely format top headlines from popular news sources for today.

    Args:
        arg1: Comma-separated list of sources to include (options: "bbc", "nyt", "guardian", "hn", "all").
              Use "all" or empty string to fetch from all supported sources.
        arg2: Maximum number of headlines per source (must be > 0).

    This tool scrapes public RSS feeds (no API key needed) and returns a
    markdown-formatted string grouped by source, with each headline on its own line
    and linked to the original article when available.
    """
    import xml.etree.ElementTree as ET

    if arg2 <= 0:
        return "Please provide a positive integer number of headlines per source for arg2."

    # Normalize requested sources
    requested = [s.strip().lower() for s in arg1.split(",")] if arg1 else []
    if not requested or "all" in requested:
        requested = ["bbc", "nyt", "guardian", "hn"]

    # Supported sources and their RSS URLs
    sources = {
        "bbc": {
            "name": "BBC News",
            "url": "https://feeds.bbci.co.uk/news/rss.xml",
        },
        "nyt": {
            "name": "The New York Times",
            "url": "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml",
        },
        "guardian": {
            "name": "The Guardian",
            "url": "https://www.theguardian.com/world/rss",
        },
        "hn": {
            "name": "Hacker News (Top)",
            "url": "https://hnrss.org/frontpage",
        },
    }

    picked_keys = [k for k in requested if k in sources]
    if not picked_keys:
        return (
            "No valid news sources selected. Valid options: bbc, nyt, guardian, hn, all."
        )

    output_sections = []

    for key in picked_keys:
        meta = sources[key]
        name = meta["name"]
        url = meta["url"]

        try:
            resp = requests.get(url, timeout=8)
            resp.raise_for_status()
            root = ET.fromstring(resp.content)

            # RSS structure: channel/item/title/link
            items = []
            for item in root.findall(".//item"):
                title_el = item.find("title")
                link_el = item.find("link")
                if title_el is not None and title_el.text:
                    title = title_el.text.strip()
                    link = link_el.text.strip() if link_el is not None and link_el.text else ""
                    if title:
                        items.append((title, link))
                if len(items) >= arg2:
                    break

            if not items:
                output_sections.append(f"### {name}\n\n_(no headlines found)_")
            else:
                lines = []
                for idx, (title, link) in enumerate(items, start=1):
                    if link:
                        lines.append(f"{idx}. [{title}]({link})")
                    else:
                        lines.append(f"{idx}. {title}")
                joined = "\n".join(lines)
                output_sections.append(f"### {name} (top {len(items)})\n\n{joined}")
        except Exception as e:
            output_sections.append(f"### {name}\n\n_Error fetching headlines: {e}_")

    header = "## Top headlines from popular news sources\n"
    return header + "\n\n" + "\n\n".join(output_sections)

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """A tool that fetches the current local time in a specified timezone.
    Args:
        timezone: A string representing a valid timezone (e.g., 'America/New_York').
    """
    try:
        # Create timezone object
        tz = pytz.timezone(timezone)
        # Get current time in that timezone
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"The current local time in {timezone} is: {local_time}"
    except Exception as e:
        return f"Error fetching time for timezone '{timezone}': {str(e)}"


final_answer = FinalAnswerTool()

# If the agent does not answer, the model is overloaded, please use another model or the following Hugging Face Endpoint that also contains qwen2.5 coder:
# model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud' 

model = InferenceClientModel(
    max_tokens=2096,
    temperature=0.5,
    model_id='Qwen/Qwen2.5-Coder-32B-Instruct',  # it is possible that this model may be overloaded
    custom_role_conversions=None,
)


# Import tool from Hub
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)
    
agent = CodeAgent(
    model=model,
    tools=[final_answer, my_custom_tool, get_current_time_in_timezone, image_generation_tool],  # don't remove final_answer
    max_steps=6,
    verbosity_level=1,
    name=None,
    description=None,
)


def chat_fn(message, history):
    """Chat handler for Gradio.

    If the user is asking for headlines, we bypass the agent and call the
    headlines tool directly so the nicely formatted markdown is returned
    as-is. For other queries, we fall back to the CodeAgent.
    """
    text = (message or "").lower().strip()

    # Special handling for capability / "what can you do" questions
    if any(phrase in text for phrase in ["what can you do", "what you can do", "help me", "how can you help"]):
        return (
            "I’m your **Daily Headlines Assistant**.\n\n"
            "- I fetch today’s top headlines from **BBC**, **The New York Times**, "
            "**The Guardian**, and **Hacker News** using live RSS feeds.\n"
            "- I format them into a clean, grouped markdown view with **clickable links**.\n"
            "- I can also tell you the **current time in any timezone** and generate images from text prompts.\n\n"
            "Try asking things like:\n"
            "- \"Show me today’s top 3 headlines from BBC, NYT and Hacker News.\"\n"
            "- \"Give me the top 5 headlines from BBC only.\"\n"
            "- \"What time is it now in America/New_York?\"\n"
        )
    wants_news = any(
        kw in text
        for kw in ["headline", "headlines", "bbc", "nyt", "new york times", "guardian", "hacker news", "hn"]
    )

    if wants_news:
        # Simple heuristic: default to all sources and 3 headlines if user
        # does not specify numbers; otherwise, try to extract a small integer.
        import re

        match = re.search(r"\b(\d{1,2})\b", text)
        count = int(match.group(1)) if match else 3
        # Map some common names to our source keys
        sources = []
        if "bbc" in text:
            sources.append("bbc")
        if "nyt" in text or "new york times" in text:
            sources.append("nyt")
        if "guardian" in text:
            sources.append("guardian")
        if "hacker news" in text or "hn" in text:
            sources.append("hn")
        # If nothing specific mentioned, use all
        arg1 = ",".join(sources) if sources else "all"

        try:
            return my_custom_tool(arg1=arg1, arg2=count)
        except Exception as e:
            return f"Error while fetching headlines: {e}"

    # Fallback: use the full agent for non-news tasks
    try:
        result = agent.run(task=message)
    except Exception as e:
        result = f"Error while running the agent: {e}"
    if result is None:
        result = (
            "The agent did not produce a final answer (the upstream model may have "
            "returned an error like 502). Please try again or with a simpler request."
        )
    return str(result)


demo = gr.ChatInterface(
    fn=chat_fn,
    title="Daily Headlines Assistant",
    description=(
        "Ask for today's top headlines from BBC, The New York Times, The Guardian, "
        "and Hacker News. Results include clickable links and are grouped by source."
    ),
    examples=[
        "Show me today’s top 3 headlines from BBC, NYT and Hacker News.",
        "Give me the top 5 headlines from BBC only.",
        "Fetch today’s main stories from The Guardian and Hacker News.",
    ],
)

demo.launch()