chat und agent Mode verbesserung

This commit is contained in:
Livio Meuli 2026-04-10 15:07:28 +02:00
parent cd233714bf
commit df7f34abbf
2 changed files with 97 additions and 55 deletions

View File

@ -463,6 +463,24 @@ class CodingAgent:
"is_done": False, "is_done": False,
} }
def follow_up(self, question: str) -> None:
"""Continue a completed task with a follow-up question.
Resets the done-flag and appends the user's question to the existing
conversation, so the agent retains full context of what was already done.
Call propose_next_action() afterwards to continue the loop.
"""
self.is_done = False
self.messages.append({
"role": "user",
"content": (
f"<human_message>{question}</human_message>\n"
"<replan>The user has a follow-up question or correction regarding "
"the task you just completed. Review what you already did and "
"address their question accordingly.</replan>"
),
})
def reject(self, feedback: str) -> None: def reject(self, feedback: str) -> None:
"""Reject the pending action and inject user feedback. """Reject the pending action and inject user feedback.

View File

@ -49,6 +49,15 @@ def _reject_action(feedback: str):
st.session_state.agent_status = "waiting_approval" st.session_state.agent_status = "waiting_approval"
def _followup_agent(question: str):
"""Inject a follow-up question into the finished agent and resume the loop."""
agent = st.session_state.coding_agent
agent.follow_up(question)
action = agent.propose_next_action()
st.session_state.agent_pending_action = action
st.session_state.agent_status = "waiting_approval"
def _reset_agent(): def _reset_agent():
"""Reset all agent state back to idle.""" """Reset all agent state back to idle."""
st.session_state.coding_agent = None st.session_state.coding_agent = None
@ -70,19 +79,18 @@ def render_agent_mode():
if agent_log: if agent_log:
with st.expander(f"Agent Log — {len(agent_log)} step(s) completed", expanded=False): with st.expander(f"Agent Log — {len(agent_log)} step(s) completed", expanded=False):
for i, step in enumerate(agent_log): for i, step in enumerate(agent_log):
st.markdown(f"**Step {i + 1} — `{step['tool']}`**") with st.chat_message("assistant"):
st.caption(f"Thought: {step['thought']}") st.markdown(f"**Step {i + 1} — `{step['tool']}`**")
if step.get("arguments"): st.caption(f"Thought: {step['thought']}")
with st.container(): if step.get("arguments"):
st.json(step["arguments"]) st.json(step["arguments"])
result_text = step.get("result", "") result_text = step.get("result", "")
if result_text.startswith("ERROR") or result_text.startswith("SYNTAX ERROR"): if result_text.startswith("ERROR") or result_text.startswith("SYNTAX ERROR"):
st.error(result_text) st.error(result_text)
elif result_text.startswith("OK") or result_text.startswith("DONE"): elif result_text.startswith("OK") or result_text.startswith("DONE"):
st.success(result_text) st.success(result_text)
else: else:
st.code(result_text, language=None) st.code(result_text, language=None)
st.divider()
# ── Idle: task input ────────────────────────────────────────────────────── # ── Idle: task input ──────────────────────────────────────────────────────
if agent_status == "idle": if agent_status == "idle":
@ -104,17 +112,12 @@ def render_agent_mode():
elif agent_status == "waiting_approval": elif agent_status == "waiting_approval":
pending = st.session_state.get("agent_pending_action", {}) pending = st.session_state.get("agent_pending_action", {})
st.markdown("**The agent wants to execute the following step:**") with st.status("Agent proposes the following step:", expanded=True):
st.markdown(f"**Thought:** {pending.get('thought', '—')}")
with st.container(border=True):
st.markdown("**Thought**")
st.markdown(pending.get("thought", "—"))
st.markdown(f"**Tool:** `{pending.get('tool', '—')}`") st.markdown(f"**Tool:** `{pending.get('tool', '—')}`")
args = pending.get("arguments", {}) args = pending.get("arguments", {})
if args: if args:
st.markdown("**Arguments:**")
if "content" in args: if "content" in args:
display_args = {k: v for k, v in args.items() if k != "content"} display_args = {k: v for k, v in args.items() if k != "content"}
if display_args: if display_args:
@ -129,7 +132,7 @@ def render_agent_mode():
placeholder="e.g. Use a different approach...", placeholder="e.g. Use a different approach...",
) )
col1, col2, col3 = st.columns([2, 2, 3]) col1, col2, col3 = st.columns([3, 2, 2])
with col1: with col1:
if st.button("Approve", type="primary", use_container_width=True): if st.button("Approve", type="primary", use_container_width=True):
with st.spinner("Executing and planning next step..."): with st.spinner("Executing and planning next step..."):
@ -149,52 +152,73 @@ def render_agent_mode():
elif agent_status == "done": elif agent_status == "done":
last_result = agent_log[-1]["result"] if agent_log else "" last_result = agent_log[-1]["result"] if agent_log else ""
st.success(f"Task completed! {last_result}") st.success(f"Task completed! {last_result}")
if st.button("New Task", use_container_width=True):
_reset_agent() st.divider()
st.rerun()
followup = st.text_area(
"Follow-up question or correction:",
key="agent_followup_input",
height=80,
placeholder="e.g. The output is wrong — it should sort descending. Can you fix that?",
)
col1, col2 = st.columns(2)
with col1:
if st.button("Ask Follow-up", type="primary", use_container_width=True):
if followup.strip():
with st.spinner("Agent is thinking..."):
_followup_agent(followup.strip())
st.rerun()
else:
st.warning("Please enter a follow-up question first.")
with col2:
if st.button("New Task", use_container_width=True):
_reset_agent()
st.rerun()
# ── Normal Chat ─────────────────────────────────────────────────────────────── # ── Normal Chat ───────────────────────────────────────────────────────────────
def render_normal_chat(): def render_normal_chat():
chat_section = st.container() # Chat history as bubbles
setup_section = st.container() for message in st.session_state.chat_history:
role = message["role"]
if role == "system":
continue
with st.chat_message(role):
st.markdown(message["content"])
with chat_section: # Chat input — Enter to send, no extra button needed
if st.session_state.chat_history: user_input = st.chat_input("Type your message here...")
for message in st.session_state.chat_history: if user_input:
st.markdown(f"**{message['role'].capitalize()}:** {message['content']}") chat_manager = st.session_state.chat_manager
st.toggle("Agent Mode", key="agent_mode") # Inject system prompt on the first message
if not chat_manager.get_history():
system_prompt = SystemPrompter.generate_prompt()
chat_manager.add_message("system", system_prompt)
# Clear the input field before the widget is rendered (Streamlit requirement) # Show user message immediately without waiting for response
if st.session_state.get("_clear_chat_input"): with st.chat_message("user"):
st.session_state.chat_input = "" st.markdown(user_input)
st.session_state._clear_chat_input = False
user_input = st.text_input("Type your message here:", key="chat_input") # Show response with spinner while API is called
with st.chat_message("assistant"):
with st.spinner("Thinking..."):
try:
ai_response = chat_manager.send_message(user_input)
except Exception as e:
ai_response = f"Error: {e}"
st.markdown(ai_response)
if st.button("Send", key="send_button") and user_input: st.session_state.chat_history.append({"role": "user", "content": user_input})
chat_manager = st.session_state.chat_manager st.session_state.chat_history.append({"role": "assistant", "content": ai_response})
st.rerun()
# Inject system prompt on the first message # Rendered in the normal flow; JS above clones them to fixed positions
if not chat_manager.get_history(): # and hides these originals.
system_prompt = SystemPrompter.generate_prompt() st.toggle("Agent Mode", key="agent_mode")
chat_manager.add_message("system", system_prompt) with st.expander("Settings", expanded=False):
st.session_state.chat_history.append({"role": "user", "content": user_input})
try:
ai_response = chat_manager.send_message(user_input)
except Exception as e:
ai_response = f"Error: {e}"
st.session_state.chat_history.append({"role": "assistant", "content": ai_response})
st.session_state._clear_chat_input = True
st.rerun()
with setup_section:
st.info("This is where you can set up your AI assistant. For now, this section is just a placeholder.")
st.toggle("Use debug system prompt", key="use_system_prompt", value=True) st.toggle("Use debug system prompt", key="use_system_prompt", value=True)