PD03 commited on
Commit
ddcbcce
Β·
verified Β·
1 Parent(s): c5dd741

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -75
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- import json, os, time, uuid
3
  from smolagents import CodeAgent, tool
4
  from smolagents.models import LiteLLMModel
5
  try:
@@ -7,15 +7,68 @@ try:
7
  except Exception:
8
  HfApiModel = None
9
 
10
- # --------- Shared model factory ---------
11
- def get_model():
12
- if os.getenv("OPENAI_API_KEY"):
13
- return LiteLLMModel(model_id=os.getenv("OPENAI_MODEL","gpt-5"),
14
- api_key=os.getenv("OPENAI_API_KEY"))
15
- elif HfApiModel is not None:
16
- return HfApiModel(os.getenv("HF_MODEL","Qwen/Qwen2.5-7B-Instruct"))
17
- else:
18
- raise RuntimeError("No model available. Set OPENAI_API_KEY or install HF Inference support.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  # =======================================
21
  # Exercise 1: Hello Tools (add + multiply)
@@ -23,7 +76,6 @@ def get_model():
23
  @tool
24
  def add(a: float, b: float) -> float:
25
  """Add two numbers.
26
-
27
  Args:
28
  a (float): First addend.
29
  b (float): Second addend.
@@ -35,7 +87,6 @@ def add(a: float, b: float) -> float:
35
  @tool
36
  def mul(a: float, b: float) -> float:
37
  """Multiply two numbers.
38
-
39
  Args:
40
  a (float): First factor.
41
  b (float): Second factor.
@@ -45,44 +96,50 @@ def mul(a: float, b: float) -> float:
45
  return a * b
46
 
47
  def run_ex1():
48
- system = (
49
- "You are a careful math assistant. Use the available tools to compute the answer.\n"
50
- "Finally, PRINT ONLY a JSON object like {\"result\": <number>}."
51
- )
52
- agent = CodeAgent(
53
- tools=[add, mul],
54
- model=get_model(),
55
- system_prompt=system,
56
- add_base_tools=False,
57
- verbosity_level=1,
58
- temperature=0.0,
59
- )
60
- goal = "Compute 2 * (3 + 4) and return {\"result\": 14}."
61
- out = agent.run(goal)
62
- return str(out)
 
 
 
63
 
64
  # =======================================
65
  # Exercise 2: Guardrails + fixed plan
66
  # =======================================
67
  def run_ex2():
68
- system = (
69
- "Follow this fixed plan exactly:\n"
70
- "1) Call add(a=3, b=4) and store the result in x.\n"
71
- "2) Call mul(a=2, b=x) to compute y.\n"
72
- "3) PRINT ONLY this JSON: {\"result\": y}\n"
73
- "Never print explanations."
74
- )
75
- agent = CodeAgent(
76
- tools=[add, mul],
77
- model=get_model(),
78
- system_prompt=system,
79
- add_base_tools=False,
80
- verbosity_level=1,
81
- temperature=0.0,
82
- )
83
- goal = "Please give me the product of two and the sum of three and four β€” as JSON."
84
- out = agent.run(goal)
85
- return str(out)
 
 
 
86
 
87
  # =======================================
88
  # Exercise 3: Mini PR -> PO
@@ -90,22 +147,20 @@ def run_ex2():
90
  @tool
91
  def validate_pr(pr: dict) -> dict:
92
  """Validate basic PR fields and structure.
93
-
94
  Args:
95
- pr (dict): Purchase Requisition object with keys: pr_id, requester, cost_center, currency, items.
96
  Returns:
97
- dict: {{"ok": bool, "errors": list[str]}}
98
  """
99
- req = ["pr_id","requester","cost_center","currency","items"]
100
  errors = [f"Missing {k}" for k in req if k not in pr]
101
  if not isinstance(pr.get("items", []), list) or not pr["items"]:
102
  errors.append("Items must be a non-empty list")
103
- return {"ok": len(errors)==0, "errors": errors}
104
 
105
  @tool
106
  def create_po(pr: dict) -> dict:
107
  """Create a simple Purchase Order (PO) from a PR.
108
-
109
  Args:
110
  pr (dict): Validated PR dict with items[{sku, quantity, unit_price}] and currency.
111
  Returns:
@@ -115,18 +170,18 @@ def create_po(pr: dict) -> dict:
115
  items_out = []
116
  for it in pr.get("items", []):
117
  line = float(it["quantity"]) * float(it["unit_price"])
118
- items_out.append({"sku": it.get("sku","UNKNOWN"), "line_total": round(line,2)})
119
  subtotal += line
120
  tax = round(subtotal * 0.08, 2)
121
  total = round(subtotal + tax, 2)
122
  return {
123
  "po_id": f"PO-{int(time.time())}-{uuid.uuid4().hex[:6].upper()}",
124
- "currency": pr.get("currency","SGD"),
125
  "items": items_out,
126
- "subtotal": round(subtotal,2),
127
  "tax": tax,
128
  "total": total,
129
- "source_pr_id": pr.get("pr_id")
130
  }
131
 
132
  DEFAULT_PR = json.dumps({
@@ -135,8 +190,8 @@ DEFAULT_PR = json.dumps({
135
  "cost_center": "CC-SG-OPS",
136
  "currency": "SGD",
137
  "items": [
138
- {"sku":"PAPER-A4-80G","quantity":20,"unit_price":6.8},
139
- {"sku":"STAPLER-01","quantity":5,"unit_price":18.5}
140
  ]
141
  }, indent=2)
142
 
@@ -145,35 +200,47 @@ def run_ex3(pr_text):
145
  pr = json.loads(pr_text)
146
  except Exception as e:
147
  return f"Invalid JSON: {e}"
148
- system = (
149
- "You are a procurement agent. Use the tools as follows:\n"
150
- "1) validate_pr(pr). If ok==false, PRINT ONLY {\"error\": errors}.\n"
151
- "2) If ok==true, call create_po(pr).\n"
152
- "3) PRINT ONLY the resulting PO JSON. No extra text."
153
- )
154
- agent = CodeAgent(
155
- tools=[validate_pr, create_po],
156
- model=get_model(),
157
- system_prompt=system,
158
- add_base_tools=False,
159
- verbosity_level=1,
160
- temperature=0.0,
161
- )
162
- goal = "Convert this PR to a PO (strict JSON). PR is in additional_context['pr']."
163
- out = agent.run(goal, additional_context={"pr": pr})
164
- return str(out)
 
 
 
165
 
 
166
  with gr.Blocks(title="Smolagents Beginner Lab (Online)") as demo:
167
  gr.Markdown("# Smolagents Beginner Lab (Online)")
168
- gr.Markdown("Click the buttons to run the exercises in the cloud. Set `OPENAI_API_KEY` in Space secrets for best results.")
 
 
 
 
 
 
169
  with gr.Tab("1) Hello Tools"):
170
  btn1 = gr.Button("Run Exercise 1")
171
  out1 = gr.Textbox(label="Output", lines=6)
172
  btn1.click(lambda: run_ex1(), inputs=None, outputs=out1)
 
173
  with gr.Tab("2) Guardrails (deterministic)"):
174
  btn2 = gr.Button("Run Exercise 2")
175
  out2 = gr.Textbox(label="Output", lines=6)
176
  btn2.click(lambda: run_ex2(), inputs=None, outputs=out2)
 
177
  with gr.Tab("3) Mini PR β†’ PO"):
178
  pr_input = gr.Textbox(label="PR JSON", value=DEFAULT_PR, lines=16)
179
  btn3 = gr.Button("Run Exercise 3")
 
1
  import gradio as gr
2
+ import json, os, time, uuid, traceback
3
  from smolagents import CodeAgent, tool
4
  from smolagents.models import LiteLLMModel
5
  try:
 
7
  except Exception:
8
  HfApiModel = None
9
 
10
+ # --------- Shared model factory with fallback + verbose errors ---------
11
+ def get_model(prefer_openai=True):
12
+ """
13
+ Picks an LLM for the agent. Prefers OpenAI via LiteLLM if OPENAI_API_KEY is set,
14
+ falls back to Hugging Face Inference (HF_MODEL) otherwise.
15
+ """
16
+ last_error = None
17
+ if prefer_openai and os.getenv("OPENAI_API_KEY"):
18
+ model_id = os.getenv("OPENAI_MODEL", "gpt-5") # change if your account lacks access
19
+ try:
20
+ return LiteLLMModel(
21
+ model_id=model_id,
22
+ api_key=os.getenv("OPENAI_API_KEY"),
23
+ base_url=os.getenv("OPENAI_BASE_URL")
24
+ )
25
+ except Exception as e:
26
+ last_error = f"OpenAI model init failed for '{model_id}': {e}"
27
+ # Try a widely available fallback
28
+ try:
29
+ return LiteLLMModel(
30
+ model_id="gpt-4o-mini",
31
+ api_key=os.getenv("OPENAI_API_KEY"),
32
+ base_url=os.getenv("OPENAI_BASE_URL")
33
+ )
34
+ except Exception as e2:
35
+ last_error += f" | Fallback gpt-4o-mini failed: {e2}"
36
+
37
+ if HfApiModel is not None:
38
+ try:
39
+ return HfApiModel(os.getenv("HF_MODEL", "Qwen/Qwen2.5-7B-Instruct"))
40
+ except Exception as e:
41
+ last_error = f"HF model init failed: {e}"
42
+
43
+ raise RuntimeError(last_error or "No model available. Set OPENAI_API_KEY or HF_MODEL in Space secrets.")
44
+
45
+ def diag_openai_ping(prompt="Say 'pong' and nothing else."):
46
+ """Quick connectivity test to your OpenAI key/model."""
47
+ try:
48
+ model = get_model(prefer_openai=True) # force OpenAI path if possible
49
+
50
+ @tool
51
+ def echo_tool(x: str) -> str:
52
+ """Echo the provided text.
53
+ Args:
54
+ x (str): Text to echo back.
55
+ Returns:
56
+ str: The same text.
57
+ """
58
+ return x
59
+
60
+ agent = CodeAgent(
61
+ tools=[echo_tool],
62
+ model=model,
63
+ system_prompt="Reply with the user message verbatim. No extra words.",
64
+ add_base_tools=False,
65
+ verbosity_level=1,
66
+ temperature=0.0,
67
+ )
68
+ out = agent.run(prompt)
69
+ return f"βœ… OpenAI ping ok.\nModel: {getattr(model, 'model_id', 'unknown')}\nOutput: {out}"
70
+ except Exception as e:
71
+ return "❌ OpenAI ping failed:\n" + "".join(traceback.format_exception_only(type(e), e))
72
 
73
  # =======================================
74
  # Exercise 1: Hello Tools (add + multiply)
 
76
  @tool
77
  def add(a: float, b: float) -> float:
78
  """Add two numbers.
 
79
  Args:
80
  a (float): First addend.
81
  b (float): Second addend.
 
87
  @tool
88
  def mul(a: float, b: float) -> float:
89
  """Multiply two numbers.
 
90
  Args:
91
  a (float): First factor.
92
  b (float): Second factor.
 
96
  return a * b
97
 
98
  def run_ex1():
99
+ try:
100
+ system = (
101
+ "You are a careful math assistant. Use the available tools to compute the answer.\n"
102
+ "Finally, PRINT ONLY a JSON object like {\"result\": <number>}."
103
+ )
104
+ agent = CodeAgent(
105
+ tools=[add, mul],
106
+ model=get_model(),
107
+ system_prompt=system,
108
+ add_base_tools=False,
109
+ verbosity_level=1,
110
+ temperature=0.0,
111
+ )
112
+ goal = "Compute 2 * (3 + 4) and return {\"result\": 14}."
113
+ out = agent.run(goal)
114
+ return str(out)
115
+ except Exception:
116
+ return "❌ Exercise 1 error:\n" + traceback.format_exc()
117
 
118
  # =======================================
119
  # Exercise 2: Guardrails + fixed plan
120
  # =======================================
121
  def run_ex2():
122
+ try:
123
+ system = (
124
+ "Follow this fixed plan exactly:\n"
125
+ "1) Call add(a=3, b=4) and store the result in x.\n"
126
+ "2) Call mul(a=2, b=x) to compute y.\n"
127
+ "3) PRINT ONLY this JSON: {\"result\": y}\n"
128
+ "Never print explanations."
129
+ )
130
+ agent = CodeAgent(
131
+ tools=[add, mul],
132
+ model=get_model(),
133
+ system_prompt=system,
134
+ add_base_tools=False,
135
+ verbosity_level=1,
136
+ temperature=0.0,
137
+ )
138
+ goal = "Please give me the product of two and the sum of three and four β€” as JSON."
139
+ out = agent.run(goal)
140
+ return str(out)
141
+ except Exception:
142
+ return "❌ Exercise 2 error:\n" + traceback.format_exc()
143
 
144
  # =======================================
145
  # Exercise 3: Mini PR -> PO
 
147
  @tool
148
  def validate_pr(pr: dict) -> dict:
149
  """Validate basic PR fields and structure.
 
150
  Args:
151
+ pr (dict): Purchase Requisition with keys: pr_id, requester, cost_center, currency, items.
152
  Returns:
153
+ dict: {"ok": bool, "errors": list[str]}
154
  """
155
+ req = ["pr_id", "requester", "cost_center", "currency", "items"]
156
  errors = [f"Missing {k}" for k in req if k not in pr]
157
  if not isinstance(pr.get("items", []), list) or not pr["items"]:
158
  errors.append("Items must be a non-empty list")
159
+ return {"ok": len(errors) == 0, "errors": errors}
160
 
161
  @tool
162
  def create_po(pr: dict) -> dict:
163
  """Create a simple Purchase Order (PO) from a PR.
 
164
  Args:
165
  pr (dict): Validated PR dict with items[{sku, quantity, unit_price}] and currency.
166
  Returns:
 
170
  items_out = []
171
  for it in pr.get("items", []):
172
  line = float(it["quantity"]) * float(it["unit_price"])
173
+ items_out.append({"sku": it.get("sku", "UNKNOWN"), "line_total": round(line, 2)})
174
  subtotal += line
175
  tax = round(subtotal * 0.08, 2)
176
  total = round(subtotal + tax, 2)
177
  return {
178
  "po_id": f"PO-{int(time.time())}-{uuid.uuid4().hex[:6].upper()}",
179
+ "currency": pr.get("currency", "SGD"),
180
  "items": items_out,
181
+ "subtotal": round(subtotal, 2),
182
  "tax": tax,
183
  "total": total,
184
+ "source_pr_id": pr.get("pr_id"),
185
  }
186
 
187
  DEFAULT_PR = json.dumps({
 
190
  "cost_center": "CC-SG-OPS",
191
  "currency": "SGD",
192
  "items": [
193
+ {"sku": "PAPER-A4-80G", "quantity": 20, "unit_price": 6.8},
194
+ {"sku": "STAPLER-01", "quantity": 5, "unit_price": 18.5}
195
  ]
196
  }, indent=2)
197
 
 
200
  pr = json.loads(pr_text)
201
  except Exception as e:
202
  return f"Invalid JSON: {e}"
203
+ try:
204
+ system = (
205
+ "You are a procurement agent. Use the tools as follows:\n"
206
+ "1) validate_pr(pr). If ok==false, PRINT ONLY {\"error\": errors}.\n"
207
+ "2) If ok==true, call create_po(pr).\n"
208
+ "3) PRINT ONLY the resulting PO JSON. No extra text."
209
+ )
210
+ agent = CodeAgent(
211
+ tools=[validate_pr, create_po],
212
+ model=get_model(),
213
+ system_prompt=system,
214
+ add_base_tools=False,
215
+ verbosity_level=1,
216
+ temperature=0.0,
217
+ )
218
+ goal = "Convert this PR to a PO (strict JSON). PR is in additional_context['pr']."
219
+ out = agent.run(goal, additional_context={"pr": pr})
220
+ return str(out)
221
+ except Exception:
222
+ return "❌ Exercise 3 error:\n" + traceback.format_exc()
223
 
224
+ # --------------------- Gradio UI ---------------------
225
  with gr.Blocks(title="Smolagents Beginner Lab (Online)") as demo:
226
  gr.Markdown("# Smolagents Beginner Lab (Online)")
227
+ gr.Markdown("Set `OPENAI_API_KEY` in Space secrets for best results. Optional: `OPENAI_MODEL` (e.g., gpt-5 or gpt-4o-mini).")
228
+
229
+ with gr.Tab("Diagnostics"):
230
+ ping_btn = gr.Button("Run OpenAI ping test")
231
+ ping_out = gr.Textbox(label="Diagnostics Output", lines=8)
232
+ ping_btn.click(lambda: diag_openai_ping(), inputs=None, outputs=ping_out)
233
+
234
  with gr.Tab("1) Hello Tools"):
235
  btn1 = gr.Button("Run Exercise 1")
236
  out1 = gr.Textbox(label="Output", lines=6)
237
  btn1.click(lambda: run_ex1(), inputs=None, outputs=out1)
238
+
239
  with gr.Tab("2) Guardrails (deterministic)"):
240
  btn2 = gr.Button("Run Exercise 2")
241
  out2 = gr.Textbox(label="Output", lines=6)
242
  btn2.click(lambda: run_ex2(), inputs=None, outputs=out2)
243
+
244
  with gr.Tab("3) Mini PR β†’ PO"):
245
  pr_input = gr.Textbox(label="PR JSON", value=DEFAULT_PR, lines=16)
246
  btn3 = gr.Button("Run Exercise 3")