MasterOfHugs commited on
Commit
a60f4fe
·
verified ·
1 Parent(s): 28104df

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +20 -257
app.py CHANGED
@@ -1,273 +1,36 @@
1
- #!/usr/bin/env python3
2
- # coding: utf-8
3
- """
4
- Minimal, robust app.py for the agent Space.
5
- - Uses smolagents CodeAgent + HfApiModel (free model google/flan-t5-small)
6
- - Exposes a few safe tools: web_search, visit_webpage, calculator, get_current_time_in_timezone, final_answer
7
- - Loads prompts.yaml and normalizes final_answer templates
8
- - Launches the existing Gradio_UI (Gradio_UI.py)
9
- """
10
-
11
- import json
12
- import re
13
- import datetime
14
- import pytz
15
  import yaml
16
- import traceback
17
- from typing import Any
18
-
19
- from smolagents import CodeAgent, HfApiModel, tool
20
-
21
- # ---- Tools (each tool must have clear docstring describing args) ----
22
-
23
- @tool
24
- def final_answer(answer: str) -> str:
25
- """
26
- Receive the final answer and return it.
27
-
28
- Args:
29
- answer (str): The final answer string produced by the agent.
30
-
31
- Returns:
32
- str: The same answer (the framework captures it as the final result).
33
- """
34
- return str(answer)
35
-
36
-
37
- @tool
38
- def calculator(expr: str) -> str:
39
- """
40
- Safely evaluate a simple arithmetic expression.
41
-
42
- Args:
43
- expr (str): A math expression like "2 + 3 * (4 - 1)".
44
-
45
- Returns:
46
- str: JSON string: {"expression": expr, "result": value} or {"error": "..."}.
47
- """
48
- try:
49
- # whitelist of AST nodes for safety
50
- import ast, operator
51
-
52
- allowed = {
53
- ast.Add: operator.add,
54
- ast.Sub: operator.sub,
55
- ast.Mult: operator.mul,
56
- ast.Div: operator.truediv,
57
- ast.Pow: operator.pow,
58
- ast.Mod: operator.mod,
59
- ast.USub: operator.neg,
60
- }
61
-
62
- def _eval(node):
63
- if isinstance(node, ast.Constant):
64
- return node.value
65
- if isinstance(node, ast.Num):
66
- return node.n
67
- if isinstance(node, ast.UnaryOp) and type(node.op) in allowed:
68
- return allowed[type(node.op)](_eval(node.operand))
69
- if isinstance(node, ast.BinOp) and type(node.op) in allowed:
70
- return allowed[type(node.op)](_eval(node.left), _eval(node.right))
71
- raise ValueError("Unsupported expression element: %s" % type(node))
72
-
73
- tree = ast.parse(expr, mode="eval")
74
- val = _eval(tree.body)
75
- return json.dumps({"expression": expr, "result": float(val)})
76
- except Exception as e:
77
- return json.dumps({"error": f"Calc error: {e}"})
78
-
79
-
80
- @tool
81
- def get_current_time_in_timezone(timezone: str) -> str:
82
- """
83
- Get current local time for a timezone.
84
-
85
- Args:
86
- timezone (str): IANA timezone string (e.g. "Europe/Paris").
87
-
88
- Returns:
89
- str: JSON string {"timezone": tz, "local_time": "..."} or {"error":"..."}.
90
- """
91
- try:
92
- tz = pytz.timezone(timezone)
93
- local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
94
- return json.dumps({"timezone": timezone, "local_time": local_time}, ensure_ascii=False)
95
- except Exception as e:
96
- return json.dumps({"error": f"Timezone error: {e}"})
97
-
98
-
99
- @tool
100
- def web_search(query: str, max_results: int = 5) -> str:
101
- """
102
- Search the web using duckduckgo_search (best-effort).
103
-
104
- Args:
105
- query (str): Search query string.
106
- max_results (int): Number of results to return (default 5).
107
-
108
- Returns:
109
- str: JSON string list of results or {"error": "..."}.
110
- """
111
- try:
112
- # local import so app can start even if ddg lib is missing
113
- from duckduckgo_search import DDGS
114
-
115
- with DDGS() as ddgs:
116
- results = list(ddgs.text(query, max_results=max_results))
117
- return json.dumps(results, ensure_ascii=False)
118
- except Exception as e:
119
- return json.dumps({"error": f"web_search failed: {str(e)}"})
120
-
121
 
 
122
  @tool
123
  def visit_webpage(url: str) -> str:
124
  """
125
- Download a webpage HTML using requests.
126
 
127
  Args:
128
- url (str): The page URL to fetch.
129
 
130
  Returns:
131
- str: The page HTML (string) or an error message.
132
  """
133
- try:
134
- import requests
135
-
136
- headers = {
137
- "User-Agent": "Mozilla/5.0 (compatible; InfoAgent/1.0; +https://example.com)"
138
- }
139
- r = requests.get(url, headers=headers, timeout=12)
140
- r.raise_for_status()
141
- return r.text
142
- except Exception as e:
143
- return json.dumps({"error": f"visit_webpage failed: {str(e)}"})
144
-
145
-
146
- # ---- load prompts.yaml and normalize final_answer template ----
147
-
148
- DEFAULT_PROMPTS = {
149
- "system_prompt": (
150
- "You are an assistant that solves tasks by producing short 'Thought:' then a 'Code:' block "
151
- "wrapped as:\nCode:\n```py\n# python code\n```\n<end_code>\n"
152
- "When finished call final_answer(...) with the result.\n"
153
- ),
154
- "final_answer": {
155
- "pre_messages": "You are done. Produce a concise final answer.",
156
- "post_messages": ""
157
- },
158
- }
159
-
160
- def load_and_normalize_prompts(path: str = "prompts.yaml") -> dict:
161
- try:
162
- with open(path, "r", encoding="utf-8") as fh:
163
- raw = yaml.safe_load(fh) or {}
164
- except FileNotFoundError:
165
- raw = {}
166
-
167
- # extract system_prompt if present
168
- system_prompt = raw.get("system_prompt", DEFAULT_PROMPTS["system_prompt"])
169
 
170
- # prepare prompt_templates dict: everything except system_prompt
171
- templates = {k: v for k, v in raw.items() if k != "system_prompt"}
172
-
173
- # ensure final_answer template exists and both fields are strings (Jinja expects strings)
174
- fa = templates.get("final_answer")
175
- if not isinstance(fa, dict):
176
- fa = {}
177
- pre = fa.get("pre_messages", DEFAULT_PROMPTS["final_answer"]["pre_messages"])
178
- post = fa.get("post_messages", DEFAULT_PROMPTS["final_answer"]["post_messages"])
179
- # If pre/post are lists, join them
180
- if isinstance(pre, list):
181
- pre = "\n".join(str(x) for x in pre)
182
- if isinstance(post, list):
183
- post = "\n".join(str(x) for x in post)
184
- templates["final_answer"] = {"pre_messages": str(pre), "post_messages": str(post)}
185
-
186
- # keep system_prompt included at top-level (CodeAgent expects system_prompt argument separately)
187
- templates["_system_prompt"] = system_prompt
188
- return templates
189
-
190
-
191
- prompt_templates_raw = load_and_normalize_prompts("prompts.yaml")
192
- system_prompt = prompt_templates_raw.pop("_system_prompt", DEFAULT_PROMPTS["system_prompt"])
193
- prompt_templates = prompt_templates_raw
194
-
195
-
196
- # ---- create model (HfApiModel) with fallback ----
197
-
198
- def create_model(preferred: str = "google/flan-t5-small"):
199
- try:
200
- model = HfApiModel(model_id=preferred, max_tokens=1024, temperature=0.0)
201
- # ensure numeric token counters exist
202
- try:
203
- if getattr(model, "last_input_token_count", None) is None:
204
- model.last_input_token_count = 0
205
- except Exception:
206
- pass
207
- try:
208
- if getattr(model, "last_output_token_count", None) is None:
209
- model.last_output_token_count = 0
210
- except Exception:
211
- pass
212
- return model
213
- except Exception:
214
- # fallback to a tiny local object implementing run()
215
- class Fallback:
216
- def __init__(self):
217
- self.model_id = "fallback"
218
- self.last_input_token_count = 0
219
- self.last_output_token_count = 0
220
-
221
- def run(self, prompt: str, *args, **kwargs):
222
- self.last_input_token_count = len(prompt.split()) if prompt else 0
223
- reply = (
224
- "FALLBACK: remote model unavailable. I can still use tools: web_search, visit_webpage, "
225
- "calculator, get_current_time_in_timezone. Ask a concrete question."
226
- )
227
- self.last_output_token_count = len(reply.split())
228
- return reply
229
-
230
- def __call__(self, *args, **kwargs):
231
- return self.run(args[0] if args else kwargs.get("prompt", ""))
232
-
233
- return Fallback()
234
-
235
-
236
- model = create_model("google/flan-t5-small")
237
-
238
- # ensure numeric counters not None (protect Gradio_UI)
239
- try:
240
- if getattr(model, "last_input_token_count", None) is None:
241
- model.last_input_token_count = 0
242
- except Exception:
243
- pass
244
- try:
245
- if getattr(model, "last_output_token_count", None) is None:
246
- model.last_output_token_count = 0
247
- except Exception:
248
- pass
249
-
250
- # ---- create agent ----
251
-
252
- tools_list = [final_answer, calculator, get_current_time_in_timezone, web_search, visit_webpage]
253
 
 
254
  agent = CodeAgent(
255
- model=model,
256
- tools=tools_list,
257
- max_steps=6,
258
- verbosity_level=1,
259
- system_prompt=system_prompt,
260
- prompt_templates=prompt_templates,
261
- name="MinimalInfoAgent",
262
- description="Minimal, robust agent with small set of tools",
263
  )
264
 
265
- # ---- launch UI (uses Gradio_UI.py present in project) ----
266
  if __name__ == "__main__":
267
- try:
268
- from Gradio_UI import GradioUI
269
-
270
- GradioUI(agent).launch()
271
- except Exception as exc:
272
- print("Failed to launch Gradio UI:", exc)
273
- traceback.print_exc()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import yaml
2
+ from smolagents import CodeAgent, tool
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ # Définition d’un outil simple
5
  @tool
6
  def visit_webpage(url: str) -> str:
7
  """
8
+ Visit a webpage and return its content.
9
 
10
  Args:
11
+ url (str): The URL to visit.
12
 
13
  Returns:
14
+ str: The full HTML content of the page.
15
  """
16
+ import requests
17
+ response = requests.get(url)
18
+ response.raise_for_status()
19
+ return response.text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ # Charger les prompts depuis prompts.yaml
22
+ with open("prompts.yaml", "r", encoding="utf-8") as f:
23
+ prompts = yaml.safe_load(f)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ # Créer l’agent avec le system_prompt depuis le yaml
26
  agent = CodeAgent(
27
+ tools=[visit_webpage],
28
+ system_prompt=prompts["system_prompt"]
 
 
 
 
 
 
29
  )
30
 
 
31
  if __name__ == "__main__":
32
+ print("===== Application Startup =====")
33
+ task = "Visit https://example.com and summarize the page."
34
+ result = agent.run(task)
35
+ print("===== Final Answer =====")
36
+ print(result)