AlgoCore commited on
Commit
e693966
·
1 Parent(s): eb581d5

Redesign UI with radio buttons for category

Browse files
Files changed (1) hide show
  1. gradio_ui.py +71 -143
gradio_ui.py CHANGED
@@ -1,13 +1,3 @@
1
- """
2
- gradio_ui.py — Interactive Gradio web interface for the Support Ticket Environment.
3
-
4
- Allows human exploration and debugging without writing code.
5
- Launched automatically when ENABLE_WEB_INTERFACE=true or run directly.
6
-
7
- Usage:
8
- python support_ticket_env/gradio_ui.py
9
- """
10
-
11
  import json
12
  import sys
13
  import os
@@ -20,71 +10,29 @@ sys.path.insert(0, ROOT)
20
  try:
21
  import gradio as gr
22
  except ImportError:
23
- print("gradio not installed. Run: pip install gradio")
24
  sys.exit(1)
25
 
26
  from support_ticket_env.server.support_environment import SupportTicketEnvironment
27
  from support_ticket_env.models import SupportAction
28
 
29
- # ─── shared env instance ────────────────────────────────────────
30
  _env = SupportTicketEnvironment()
31
- _history: list[dict] = []
32
  _current_obs = None
 
33
 
34
-
35
- # ─── helpers ────────────────────────────────────────────────────
36
-
37
- def _format_history() -> str:
38
- if not _history:
39
- return "_No actions yet._"
40
- lines = []
41
- for i, h in enumerate(_history, 1):
42
- reward_str = f"{h['reward']:+.3f}" if h["reward"] is not None else "—"
43
- lines.append(
44
- f"**Step {i}** | `{h['action']}` → reward `{reward_str}`\n"
45
- f"> {h['feedback']}"
46
- )
47
- return "\n\n".join(lines)
48
-
49
-
50
- def _obs_to_display(obs) -> tuple[str, str, str]:
51
- """Return (ticket_box, status_box, score_box)."""
52
- ticket = f"**[{obs.ticket_id}]** {obs.ticket_text}"
53
- status = (
54
- f"Task **{obs.task_id}** | Step **{obs.step_count}** | "
55
- f"Category: `{obs.current_category or 'unknown'}` | "
56
- f"Resolved: {'✅' if obs.resolved else '⬜'}"
57
- )
58
- score = f"Last step score: **{obs.score:.3f}** | reward: **{obs.reward or 0.0:+.3f}**"
59
- return ticket, status, score
60
-
61
-
62
- # ─── UI callbacks ────────────────────────────────────────────────
63
-
64
- def do_reset(task_id: int, seed: int):
65
- global _history, _current_obs
66
  _history = []
67
- obs = _env.reset(task_id=task_id, seed=seed)
68
  _current_obs = obs
69
- ticket, status, score = _obs_to_display(obs)
70
- return (
71
- ticket, status, score,
72
- _format_history(),
73
- gr.update(interactive=True),
74
- obs.feedback,
75
- gr.update(value=False), # done flag
76
- )
77
-
78
 
79
- def do_step(action_type: str, category: str, reply_text: str, reason: str):
80
  global _current_obs
81
  if _current_obs is None:
82
- return (
83
- "⚠️ Please reset the environment first.",
84
- "", "", _format_history(), "", gr.update(value=False),
85
- )
86
-
87
- # Build action
88
  kwargs = {"action_type": action_type}
89
  if action_type == "classify" and category:
90
  kwargs["category"] = category
@@ -92,120 +40,100 @@ def do_step(action_type: str, category: str, reply_text: str, reason: str):
92
  kwargs["reply_text"] = reply_text
93
  if reason:
94
  kwargs["reason"] = reason
95
-
96
  try:
97
  action = SupportAction(**kwargs)
 
 
 
 
 
 
 
98
  except Exception as e:
99
- return (
100
- _current_obs.ticket_text,
101
- f"❌ Invalid action: {e}", "",
102
- _format_history(), "", gr.update(value=False),
103
- )
104
-
105
- obs = _env.step(action)
106
- _current_obs = obs
107
-
108
- _history.append({
109
- "action": f"{action_type}" + (f"/{category}" if category and action_type == "classify" else ""),
110
- "reward": obs.reward,
111
- "feedback": obs.feedback,
112
- })
113
-
114
- ticket, status, score = _obs_to_display(obs)
115
- done_msg = "🏁 Episode finished!" if obs.done else ""
116
- return (
117
- ticket, status, score,
118
- _format_history(),
119
- obs.feedback,
120
- gr.update(value=obs.done),
121
- )
122
-
123
 
124
  def do_state():
125
  state = _env.state
126
  return json.dumps({
127
- "episode_id": state.episode_id,
128
- "step_count": state.step_count,
129
  "task_id": state.task_id,
130
  "ticket_id": state.ticket_id,
131
  "correct_category": state.correct_category,
132
  "correct_action": state.correct_action,
133
  "classified": state.classified,
134
  "resolved": state.resolved,
 
135
  "total_reward": state.total_reward,
136
- "tickets_resolved": state.tickets_resolved,
137
- "tickets_total": state.tickets_total,
138
  }, indent=2)
139
 
140
-
141
- # ─── UI layout ──────────────────────────────────────────────────
142
-
143
- DESCRIPTION = """
144
- # 🎫 Customer Support Ticket Resolution Environment
145
-
146
- An **OpenEnv** environment for training AI agents to handle customer support tickets.
147
-
148
- **Tasks:** 1 = Classify · 2 = Classify + Action · 3 = Full Queue Resolution
149
- """
150
-
151
- with gr.Blocks(title="Support Ticket Env") as demo:
152
- gr.Markdown(DESCRIPTION)
153
 
154
  with gr.Row():
155
- # ── Left panel: controls ────────────────────────────────
156
  with gr.Column(scale=1):
157
- gr.Markdown("### ⚙️ Episode Setup")
158
- task_slider = gr.Slider(1, 3, value=1, step=1, label="Task ID")
159
- seed_input = gr.Number(value=42, label="Seed", precision=0)
160
- reset_btn = gr.Button("🔄 Reset Episode", variant="primary")
161
-
162
- gr.Markdown("### 🎬 Take Action")
163
- action_type = gr.Radio(
164
- ["classify", "reply", "escalate", "close"],
165
- value="classify", label="Action Type",
 
 
 
 
 
166
  )
167
- category_dd = gr.Radio(
168
- choices=["billing", "technical", "account", "general", "refund"],
169
- label="Category (for classify)",
170
  value=None,
 
171
  )
172
- reply_box = gr.Textbox(label="Reply Text (for reply)", lines=3)
173
  reason_box = gr.Textbox(label="Reason (optional)")
174
- step_btn = gr.Button("▶️ Step", variant="secondary")
175
- state_btn = gr.Button("🔍 Show State")
176
 
177
- # ── Right panel: observation ────────────────────────────
178
  with gr.Column(scale=2):
179
- gr.Markdown("### 📬 Current Ticket")
180
- ticket_display = gr.Markdown("_Reset to start._")
181
- status_display = gr.Markdown("")
182
- score_display = gr.Markdown("")
183
- feedback_box = gr.Textbox(label="Last Feedback", interactive=False)
184
- done_checkbox = gr.Checkbox(label="Episode Done", interactive=False)
185
-
186
- gr.Markdown("### 📜 Action History")
187
- history_display = gr.Markdown("_No actions yet._")
188
-
189
- gr.Markdown("### 🗂️ Raw State (JSON)")
190
- state_output = gr.Code(language="json", label="state()")
 
 
 
 
 
 
 
 
 
191
 
192
- # ── wire up ─────────────────────────────────────────────────
193
  reset_btn.click(
194
  do_reset,
195
- inputs=[task_slider, seed_input],
196
- outputs=[ticket_display, status_display, score_display,
197
- history_display, step_btn, feedback_box, done_checkbox],
198
  )
199
  step_btn.click(
200
  do_step,
201
- inputs=[action_type, category_dd, reply_box, reason_box],
202
- outputs=[ticket_display, status_display, score_display,
203
- history_display, feedback_box, done_checkbox],
204
  )
205
  state_btn.click(
206
- do_state, inputs=[], outputs=[state_output],
 
 
207
  )
208
 
209
-
210
  if __name__ == "__main__":
211
- demo.launch(server_name="0.0.0.0", server_port=7861, share=False)
 
 
 
 
 
 
 
 
 
 
 
1
  import json
2
  import sys
3
  import os
 
10
  try:
11
  import gradio as gr
12
  except ImportError:
13
+ print("gradio not installed.")
14
  sys.exit(1)
15
 
16
  from support_ticket_env.server.support_environment import SupportTicketEnvironment
17
  from support_ticket_env.models import SupportAction
18
 
 
19
  _env = SupportTicketEnvironment()
 
20
  _current_obs = None
21
+ _history = []
22
 
23
+ def do_reset(task_id, seed):
24
+ global _current_obs, _history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  _history = []
26
+ obs = _env.reset(task_id=int(task_id), seed=int(seed))
27
  _current_obs = obs
28
+ state = _env.state
29
+ info = f"TICKET: {obs.ticket_text}\n\nFeedback: {obs.feedback}"
30
+ return info, "Episode started! Now classify the ticket.", ""
 
 
 
 
 
 
31
 
32
+ def do_step(action_type, category, reply_text, reason):
33
  global _current_obs
34
  if _current_obs is None:
35
+ return "Please click Reset first!", "", ""
 
 
 
 
 
36
  kwargs = {"action_type": action_type}
37
  if action_type == "classify" and category:
38
  kwargs["category"] = category
 
40
  kwargs["reply_text"] = reply_text
41
  if reason:
42
  kwargs["reason"] = reason
 
43
  try:
44
  action = SupportAction(**kwargs)
45
+ obs = _env.step(action)
46
+ _current_obs = obs
47
+ _history.append(f"Step: {action_type}/{category or ''} -> Reward: {obs.reward:.2f}")
48
+ result = f"Feedback: {obs.feedback}\nReward: {obs.reward:.2f}\nDone: {obs.done}"
49
+ history = "\n".join(_history)
50
+ ticket = f"TICKET: {obs.ticket_text}"
51
+ return ticket, result, history
52
  except Exception as e:
53
+ return _current_obs.ticket_text, f"Error: {e}", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  def do_state():
56
  state = _env.state
57
  return json.dumps({
 
 
58
  "task_id": state.task_id,
59
  "ticket_id": state.ticket_id,
60
  "correct_category": state.correct_category,
61
  "correct_action": state.correct_action,
62
  "classified": state.classified,
63
  "resolved": state.resolved,
64
+ "step_count": state.step_count,
65
  "total_reward": state.total_reward,
 
 
66
  }, indent=2)
67
 
68
+ with gr.Blocks(title="Support Ticket Environment") as demo:
69
+ gr.Markdown("# Customer Support Ticket Resolution Environment")
70
+ gr.Markdown("Train AI agents to handle customer support tickets.")
 
 
 
 
 
 
 
 
 
 
71
 
72
  with gr.Row():
 
73
  with gr.Column(scale=1):
74
+ gr.Markdown("## Setup")
75
+ task_radio = gr.Radio(
76
+ choices=[1, 2, 3],
77
+ value=1,
78
+ label="Task (1=Easy, 2=Medium, 3=Hard)"
79
+ )
80
+ seed_box = gr.Number(value=42, label="Seed")
81
+ reset_btn = gr.Button("RESET", variant="primary")
82
+
83
+ gr.Markdown("## Action")
84
+ action_radio = gr.Radio(
85
+ choices=["classify", "reply", "escalate", "close"],
86
+ value="classify",
87
+ label="Action Type"
88
  )
89
+ category_radio = gr.Radio(
90
+ choices=["billing", "technical", "account", "general", "refund"],
 
91
  value=None,
92
+ label="Category (select for classify)"
93
  )
94
+ reply_box = gr.Textbox(label="Reply Text (for reply action)", lines=2)
95
  reason_box = gr.Textbox(label="Reason (optional)")
96
+ step_btn = gr.Button("STEP", variant="secondary")
97
+ state_btn = gr.Button("GET STATE")
98
 
 
99
  with gr.Column(scale=2):
100
+ gr.Markdown("## Current Ticket")
101
+ ticket_box = gr.Textbox(
102
+ label="Ticket",
103
+ value="Click RESET to start",
104
+ lines=3,
105
+ interactive=False
106
+ )
107
+ gr.Markdown("## Result")
108
+ result_box = gr.Textbox(
109
+ label="Last Step Result",
110
+ lines=4,
111
+ interactive=False
112
+ )
113
+ gr.Markdown("## History")
114
+ history_box = gr.Textbox(
115
+ label="Action History",
116
+ lines=5,
117
+ interactive=False
118
+ )
119
+ gr.Markdown("## State")
120
+ state_box = gr.Code(language="json", label="Environment State")
121
 
 
122
  reset_btn.click(
123
  do_reset,
124
+ inputs=[task_radio, seed_box],
125
+ outputs=[ticket_box, result_box, history_box]
 
126
  )
127
  step_btn.click(
128
  do_step,
129
+ inputs=[action_radio, category_radio, reply_box, reason_box],
130
+ outputs=[ticket_box, result_box, history_box]
 
131
  )
132
  state_btn.click(
133
+ do_state,
134
+ inputs=[],
135
+ outputs=[state_box]
136
  )
137
 
 
138
  if __name__ == "__main__":
139
+ demo.launch(server_name="0.0.0.0", server_port=7861, share=False)