Marylene commited on
Commit
be4fdb8
·
verified ·
1 Parent(s): 1d4a3f4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -66
app.py CHANGED
@@ -1,15 +1,27 @@
1
  # app.py — Formulaire EAN/Libellé + logs (UI & Space) — style "IPC / Insee"
2
- import io, sys, json
3
  import gradio as gr
4
  from contextlib import redirect_stdout, redirect_stderr
5
 
6
- # 👉 adapte le module si ton fichier agent a un autre nom
7
- from quick_deploy_agent import build_agent, parse_result, run_task_with_fallback
8
 
9
 
10
- # --- en haut de app.py, ajoute cet util ---
11
- import re
 
 
 
 
 
 
 
 
 
 
 
12
 
 
13
  def _extract_json(text: str):
14
  """
15
  Essaie de parser un JSON valable depuis une chaîne.
@@ -36,30 +48,11 @@ def _extract_json(text: str):
36
  return None
37
 
38
 
39
- # ---------- util "tee" pour dupliquer les logs ----------
40
- class _Tee(io.TextIOBase):
41
- def __init__(self, *streams): self.streams = streams
42
- def write(self, s):
43
- for st in self.streams:
44
- try: st.write(s)
45
- except Exception: pass
46
- return len(s)
47
- def flush(self):
48
- for st in self.streams:
49
- try: st.flush()
50
- except Exception: pass
51
-
52
-
53
-
54
- # if hasattr(agent, "verbosity_level"):
55
- # agent.verbosity_level = 3 # 0..3 (3 = très verbeux)
56
-
57
  # ---------- prompt interne (l'utilisateur ne tape rien) ----------
58
  TASK_TMPL = """\
59
  Classe ce produit en COICOP:
60
  EAN: {ean}
61
  Libellé: {label}
62
-
63
  Outils autorisés :
64
  - validate_ean
65
  - openfoodfacts_product_by_ean
@@ -71,13 +64,11 @@ Outils autorisés :
71
  - web_search
72
  - web_get
73
  - python_interpreter # autorisé si besoin pour manipuler des listes/dicts
74
-
75
  Règles strictes :
76
  - Utilise python_interpreter uniquement pour manipuler des résultats (listes/dicts, filtrage, fusion).
77
  - N’écris pas de code inutile (pas de fichiers, pas de réseaux hors outils).
78
  - Pour (4) et (5), utilise le libellé utilisateur tel quel.
79
  - Retourne uniquement un JSON valide (objet), sans backticks.
80
-
81
  Pipeline :
82
  1) v = validate_ean(ean)
83
  2) off = openfoodfacts_product_by_ean(ean) # si EAN vide ou invalide, passe à l’étape 4
@@ -86,14 +77,11 @@ Pipeline :
86
  5) sem = coicop_semantic_similarity(text=LIBELLÉ UTILISATEUR, topk=5)
87
  6) merged = merge_candidates(candidates_lists=[offmap, rx, sem], min_k=3, fallback_bias="cheese")
88
  7) res = resolve_coicop_candidates(json_lists=[merged], topn=3)
89
-
90
  → Retourne res tel quel (objet contenant final, alternatives, candidates_top).
91
  """
92
 
93
 
94
-
95
  def classify(label: str, ean: str):
96
- import re
97
  label = (label or "").strip()
98
  ean = (ean or "").strip()
99
 
@@ -107,33 +95,13 @@ def classify(label: str, ean: str):
107
  buf_out, buf_err = io.StringIO(), io.StringIO()
108
  tee_out, tee_err = _Tee(sys.stdout, buf_out), _Tee(sys.stderr, buf_err)
109
 
110
- # Utilitaire local : extraire un vrai JSON d'une chaîne (gère les ```json ... ```)
111
- def _extract_json(text: str):
112
- if isinstance(text, dict):
113
- return text
114
- if not isinstance(text, str):
115
- return None
116
- # 1) direct
117
- try:
118
- return json.loads(text)
119
- except Exception:
120
- pass
121
- # 2) JSON dans des backticks
122
- try:
123
- m = re.search(r"```(?:json)?\s*(\{[\s\S]*\})\s*```", text)
124
- if m:
125
- return json.loads(m.group(1))
126
- except Exception:
127
- pass
128
- return None
129
-
130
  print("\n===== Agent run start =====")
131
  print(task)
132
 
133
  try:
134
- # Exécuter l’agent en dupliquant les logs vers l’UI et le Space
135
  with redirect_stdout(tee_out), redirect_stderr(tee_err):
136
- res = agent.run(task)
137
 
138
  # Parsing robuste du résultat
139
  obj = None
@@ -143,7 +111,7 @@ def classify(label: str, ean: str):
143
  obj = _extract_json(res)
144
  if obj is None:
145
  try:
146
- obj = parse_result(res) # ton parseur existant
147
  except Exception:
148
  obj = None
149
  if obj is None:
@@ -163,19 +131,12 @@ def classify(label: str, ean: str):
163
  print(f"✖ Agent error: {e}")
164
  return json.dumps({"error": str(e)}, ensure_ascii=False, indent=2), logs_ui
165
 
166
- # ---------- instancie l'agent ----------
167
- try:
168
- agent = build_agent()
169
- out = agent.run(task)
170
- except Exception:
171
- from quick_deploy_agent import run_task_with_fallback
172
- out = run_task_with_fallback(task)
173
-
174
 
175
  def fill_example():
176
  # EAN réel OFF (Les p'tits crémeux – Aldi – 216 g)
177
  return "Camembert au lait cru AOP 250g - ALDI", "2006050033638"
178
 
 
179
  # ---------- thème & CSS (style IPC / Insee) ----------
180
  theme = gr.themes.Soft(
181
  primary_hue="blue",
@@ -204,13 +165,11 @@ custom_css = """
204
  }
205
  #app-header h1{ margin:0; font-size: 22px; letter-spacing: .3px; }
206
  #app-header p{ margin:6px 0 0; opacity:.92 }
207
-
208
  .card{
209
  background: #fff; border: 1px solid var(--insee-neutral-100); border-radius: var(--radius-lg);
210
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
211
  padding: 18px;
212
  }
213
-
214
  .gr-textbox textarea{
215
  font-size: 16px; line-height: 1.35; padding: 14px 16px;
216
  border: 2px solid #e5e7eb; border-radius: 14px;
@@ -220,7 +179,6 @@ custom_css = """
220
  border-color: var(--insee-primary);
221
  box-shadow: 0 0 0 4px rgba(11,61,145,0.15);
222
  }
223
-
224
  .gr-button{
225
  font-weight: 700; letter-spacing: .2px; border-radius: 14px;
226
  transition: transform .06s ease, box-shadow .06s ease;
@@ -230,7 +188,6 @@ custom_css = """
230
  outline: 3px solid color-mix(in oklab, var(--insee-primary) 30%, white);
231
  box-shadow: 0 0 0 4px rgba(11,61,145,.18);
232
  }
233
-
234
  /* primaire */
235
  button.primary{
236
  background: linear-gradient(180deg, var(--insee-primary), var(--insee-primary-700));
@@ -243,7 +200,6 @@ button.ghost{
243
  button.ghost:hover{
244
  background: color-mix(in oklab, var(--insee-primary) 6%, white);
245
  }
246
-
247
  .gr-code pre, .gr-code code{
248
  font-size: 13.5px; line-height: 1.45; border-radius: 14px;
249
  }
@@ -285,4 +241,5 @@ with gr.Blocks(title="OpenFoodFactsAgent (COICOP)", theme=theme, css=custom_css)
285
  btn_ex.click(fn=fill_example, outputs=[label_in, ean_in])
286
 
287
  if __name__ == "__main__":
288
- demo.launch()
 
 
1
  # app.py — Formulaire EAN/Libellé + logs (UI & Space) — style "IPC / Insee"
2
+ import io, sys, json, re
3
  import gradio as gr
4
  from contextlib import redirect_stdout, redirect_stderr
5
 
6
+ # 👉 imports utilitaires de ton agent (pas besoin de build_agent ici)
7
+ from quick_deploy_agent import parse_result, run_task_with_fallback
8
 
9
 
10
+ # ---------- util "tee" pour dupliquer les logs ----------
11
+ class _Tee(io.TextIOBase):
12
+ def __init__(self, *streams): self.streams = streams
13
+ def write(self, s):
14
+ for st in self.streams:
15
+ try: st.write(s)
16
+ except Exception: pass
17
+ return len(s)
18
+ def flush(self):
19
+ for st in self.streams:
20
+ try: st.flush()
21
+ except Exception: pass
22
+
23
 
24
+ # ---------- helper: extraire un JSON valide d'un texte ----------
25
  def _extract_json(text: str):
26
  """
27
  Essaie de parser un JSON valable depuis une chaîne.
 
48
  return None
49
 
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  # ---------- prompt interne (l'utilisateur ne tape rien) ----------
52
  TASK_TMPL = """\
53
  Classe ce produit en COICOP:
54
  EAN: {ean}
55
  Libellé: {label}
 
56
  Outils autorisés :
57
  - validate_ean
58
  - openfoodfacts_product_by_ean
 
64
  - web_search
65
  - web_get
66
  - python_interpreter # autorisé si besoin pour manipuler des listes/dicts
 
67
  Règles strictes :
68
  - Utilise python_interpreter uniquement pour manipuler des résultats (listes/dicts, filtrage, fusion).
69
  - N’écris pas de code inutile (pas de fichiers, pas de réseaux hors outils).
70
  - Pour (4) et (5), utilise le libellé utilisateur tel quel.
71
  - Retourne uniquement un JSON valide (objet), sans backticks.
 
72
  Pipeline :
73
  1) v = validate_ean(ean)
74
  2) off = openfoodfacts_product_by_ean(ean) # si EAN vide ou invalide, passe à l’étape 4
 
77
  5) sem = coicop_semantic_similarity(text=LIBELLÉ UTILISATEUR, topk=5)
78
  6) merged = merge_candidates(candidates_lists=[offmap, rx, sem], min_k=3, fallback_bias="cheese")
79
  7) res = resolve_coicop_candidates(json_lists=[merged], topn=3)
 
80
  → Retourne res tel quel (objet contenant final, alternatives, candidates_top).
81
  """
82
 
83
 
 
84
  def classify(label: str, ean: str):
 
85
  label = (label or "").strip()
86
  ean = (ean or "").strip()
87
 
 
95
  buf_out, buf_err = io.StringIO(), io.StringIO()
96
  tee_out, tee_err = _Tee(sys.stdout, buf_out), _Tee(sys.stderr, buf_err)
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  print("\n===== Agent run start =====")
99
  print(task)
100
 
101
  try:
102
+ # Exécuter l’agent via la fonction tolérante aux timeouts et modèles de repli
103
  with redirect_stdout(tee_out), redirect_stderr(tee_err):
104
+ res = run_task_with_fallback(task)
105
 
106
  # Parsing robuste du résultat
107
  obj = None
 
111
  obj = _extract_json(res)
112
  if obj is None:
113
  try:
114
+ obj = parse_result(res) # parseur du projet
115
  except Exception:
116
  obj = None
117
  if obj is None:
 
131
  print(f"✖ Agent error: {e}")
132
  return json.dumps({"error": str(e)}, ensure_ascii=False, indent=2), logs_ui
133
 
 
 
 
 
 
 
 
 
134
 
135
  def fill_example():
136
  # EAN réel OFF (Les p'tits crémeux – Aldi – 216 g)
137
  return "Camembert au lait cru AOP 250g - ALDI", "2006050033638"
138
 
139
+
140
  # ---------- thème & CSS (style IPC / Insee) ----------
141
  theme = gr.themes.Soft(
142
  primary_hue="blue",
 
165
  }
166
  #app-header h1{ margin:0; font-size: 22px; letter-spacing: .3px; }
167
  #app-header p{ margin:6px 0 0; opacity:.92 }
 
168
  .card{
169
  background: #fff; border: 1px solid var(--insee-neutral-100); border-radius: var(--radius-lg);
170
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
171
  padding: 18px;
172
  }
 
173
  .gr-textbox textarea{
174
  font-size: 16px; line-height: 1.35; padding: 14px 16px;
175
  border: 2px solid #e5e7eb; border-radius: 14px;
 
179
  border-color: var(--insee-primary);
180
  box-shadow: 0 0 0 4px rgba(11,61,145,0.15);
181
  }
 
182
  .gr-button{
183
  font-weight: 700; letter-spacing: .2px; border-radius: 14px;
184
  transition: transform .06s ease, box-shadow .06s ease;
 
188
  outline: 3px solid color-mix(in oklab, var(--insee-primary) 30%, white);
189
  box-shadow: 0 0 0 4px rgba(11,61,145,.18);
190
  }
 
191
  /* primaire */
192
  button.primary{
193
  background: linear-gradient(180deg, var(--insee-primary), var(--insee-primary-700));
 
200
  button.ghost:hover{
201
  background: color-mix(in oklab, var(--insee-primary) 6%, white);
202
  }
 
203
  .gr-code pre, .gr-code code{
204
  font-size: 13.5px; line-height: 1.45; border-radius: 14px;
205
  }
 
241
  btn_ex.click(fn=fill_example, outputs=[label_in, ean_in])
242
 
243
  if __name__ == "__main__":
244
+ # SSR est activé par défaut sur Spaces; ajuste si besoin:
245
+ demo.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=True)