KingNish commited on
Commit
714c652
·
1 Parent(s): e3b02b7
Files changed (2) hide show
  1. app.css +4 -0
  2. app.py +56 -33
app.css CHANGED
@@ -390,3 +390,7 @@ body, .gradio-container {
390
  #chatbot {
391
  z-index: 5;
392
  }
 
 
 
 
 
390
  #chatbot {
391
  z-index: 5;
392
  }
393
+
394
+ .message-wrap {
395
+ margin-bottom: 40px;
396
+ }
app.py CHANGED
@@ -70,59 +70,79 @@ class GradioEvents:
70
 
71
  yield { msg: gr.update(value=""), chatbot: gr.update(value=ctx["history"]), state: gr.update(value=state_value), conv_choice: _conv_choices(state_value), send_btn: gr.update(visible=False), stop_btn: gr.update(visible=True)}
72
 
73
- # Agent will populate ctx["history"] with assistant + tool messages
74
- display_buffer = ""
75
- is_reasoning = False
 
76
  spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
77
  spinner_idx = 0
 
78
 
79
  try:
80
  for ev in agent.stream(ctx["history"]):
81
  t = ev["type"]
82
 
83
  if t == "reasoning":
84
- if not is_reasoning:
85
- is_reasoning = True
86
  spinner_idx += 1
87
- if display_buffer:
88
- temp = [{"role": "assistant", "content": display_buffer}]
 
89
  else:
90
- temp = []
91
- temp.append({
92
- "role": "assistant",
93
- "content": f"<span class=\"thinking-indicator\">{spinner_frames[spinner_idx % len(spinner_frames)]} Thinking...</span>",
94
- })
95
 
96
  elif t == "text":
97
  if is_reasoning:
98
  is_reasoning = False
99
- display_buffer += ev["content"]
100
- temp = [{"role": "assistant", "content": display_buffer}]
 
 
 
 
 
101
 
102
  elif t == "tool_call":
103
- display_buffer += (
104
- f'\n<div class="tool-call">'
105
- f'🔧 <strong>{ev["name"]}</strong>'
106
- f'<pre style="display:inline;font-size:0.85em">({ev["arguments"]})</pre>'
107
- f'</div>'
108
- )
109
- temp = [{"role": "assistant", "content": display_buffer}]
 
 
 
 
 
 
 
 
110
 
111
  elif t == "tool_output":
112
- display_buffer += (
113
- f'\n<div class="tool-output">'
114
- f'📄 <em>({len(ev["content"])} chars)</em>'
115
- f'</div>'
116
- )
117
- temp = [{"role": "assistant", "content": display_buffer}]
 
 
 
 
 
 
 
118
 
119
  elif t == "error":
120
- ctx["history"].append({
121
  "role": "assistant",
122
  "content": f'<span style="color: var(--color-red-500)">{ev["content"]}</span>',
 
123
  })
124
  yield {
125
- chatbot: gr.update(value=ctx["history"]),
126
  state: gr.update(value=state_value),
127
  send_btn: gr.update(visible=True),
128
  stop_btn: gr.update(visible=False),
@@ -130,14 +150,16 @@ class GradioEvents:
130
  return
131
 
132
  elif t == "done":
133
- # Agent has fully populated ctx["history"]
134
  break
135
 
136
  yield {
137
- chatbot: gr.update(value=ctx["history"] + temp),
138
  state: gr.update(value=state_value),
139
  }
140
 
 
 
 
141
  yield {
142
  chatbot: gr.update(value=ctx["history"]),
143
  state: gr.update(value=state_value),
@@ -146,12 +168,13 @@ class GradioEvents:
146
  }
147
 
148
  except Exception as exc:
149
- ctx["history"].append({
150
  "role": "assistant",
151
  "content": f'<span style="color: var(--color-red-500)">{exc}</span>',
 
152
  })
153
  yield {
154
- chatbot: gr.update(value=ctx["history"]),
155
  state: gr.update(value=state_value),
156
  send_btn: gr.update(visible=True),
157
  stop_btn: gr.update(visible=False),
 
70
 
71
  yield { msg: gr.update(value=""), chatbot: gr.update(value=ctx["history"]), state: gr.update(value=state_value), conv_choice: _conv_choices(state_value), send_btn: gr.update(visible=False), stop_btn: gr.update(visible=True)}
72
 
73
+ # Build display as separate titled messages (smolagents style)
74
+ display_messages: list[dict] = list(ctx["history"])
75
+ text_msg_idx: int | None = None
76
+ tool_call_idx: int | None = None
77
  spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
78
  spinner_idx = 0
79
+ is_reasoning = False
80
 
81
  try:
82
  for ev in agent.stream(ctx["history"]):
83
  t = ev["type"]
84
 
85
  if t == "reasoning":
86
+ is_reasoning = True
 
87
  spinner_idx += 1
88
+ content = f"<span class=\"thinking-indicator\">{spinner_frames[spinner_idx % len(spinner_frames)]} Thinking...</span>"
89
+ if text_msg_idx is not None:
90
+ display_messages[text_msg_idx]["content"] = content
91
  else:
92
+ display_messages.append({"role": "assistant", "content": content, "metadata": {}})
93
+ text_msg_idx = len(display_messages) - 1
 
 
 
94
 
95
  elif t == "text":
96
  if is_reasoning:
97
  is_reasoning = False
98
+ if text_msg_idx is not None:
99
+ display_messages[text_msg_idx]["content"] = ""
100
+ if text_msg_idx is not None:
101
+ display_messages[text_msg_idx]["content"] += ev["content"]
102
+ else:
103
+ display_messages.append({"role": "assistant", "content": ev["content"], "metadata": {}})
104
+ text_msg_idx = len(display_messages) - 1
105
 
106
  elif t == "tool_call":
107
+ # Finalize any in-flight text message (keep if it has real content)
108
+ if text_msg_idx is not None:
109
+ c = display_messages[text_msg_idx].get("content", "").strip()
110
+ if not c or c.startswith("<span"):
111
+ display_messages.pop(text_msg_idx)
112
+ text_msg_idx = None
113
+ tool_call_idx = None
114
+
115
+ tool_name = ev["name"]
116
+ display_messages.append({
117
+ "role": "assistant",
118
+ "content": f"```\n{tool_name}({ev['arguments']})\n```\n⏳ Running...",
119
+ "metadata": {"title": f"🛠️ Used tool {tool_name}"},
120
+ })
121
+ tool_call_idx = len(display_messages) - 1
122
 
123
  elif t == "tool_output":
124
+ if tool_call_idx is not None:
125
+ snippet = ev["content"][:500]
126
+ cc = snippet if len(ev["content"]) <= 500 else snippet + "\n..."
127
+ # Grab the tool name from the existing message
128
+ tool_name = display_messages[tool_call_idx]["metadata"]["title"].split("Used tool ")[-1]
129
+ display_messages[tool_call_idx]["content"] = (
130
+ f"```\n{tool_name}(...)\n```\n\n"
131
+ f"**Output:**\n```\n{cc}\n```"
132
+ )
133
+ display_messages[tool_call_idx]["metadata"] = {
134
+ "title": f"🛠️ {tool_name} — {len(ev['content'])} chars",
135
+ }
136
+ tool_call_idx = None
137
 
138
  elif t == "error":
139
+ display_messages.append({
140
  "role": "assistant",
141
  "content": f'<span style="color: var(--color-red-500)">{ev["content"]}</span>',
142
+ "metadata": {"title": "💥 Error"},
143
  })
144
  yield {
145
+ chatbot: gr.update(value=display_messages),
146
  state: gr.update(value=state_value),
147
  send_btn: gr.update(visible=True),
148
  stop_btn: gr.update(visible=False),
 
150
  return
151
 
152
  elif t == "done":
 
153
  break
154
 
155
  yield {
156
+ chatbot: gr.update(value=display_messages),
157
  state: gr.update(value=state_value),
158
  }
159
 
160
+ # Sync history with display format so saves remember tool call bubbles
161
+ ctx["history"] = display_messages
162
+
163
  yield {
164
  chatbot: gr.update(value=ctx["history"]),
165
  state: gr.update(value=state_value),
 
168
  }
169
 
170
  except Exception as exc:
171
+ display_messages.append({
172
  "role": "assistant",
173
  "content": f'<span style="color: var(--color-red-500)">{exc}</span>',
174
+ "metadata": {"title": "💥 Error"},
175
  })
176
  yield {
177
+ chatbot: gr.update(value=display_messages),
178
  state: gr.update(value=state_value),
179
  send_btn: gr.update(visible=True),
180
  stop_btn: gr.update(visible=False),