Skip to content

Commit 9400262

Browse files
authored
Merge pull request #17 from BrunoV21/hf-space-demo
Hf space demo
2 parents bd5624f + 4cd9836 commit 9400262

File tree

6 files changed

+135
-37
lines changed

6 files changed

+135
-37
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,4 @@ examples/hf_demo_space/.chainlit/*
185185
examples/hf_demo_space/chainlit.md
186186

187187
examples/hf_demo_space/public/
188+
.chainlit/

codetide/agents/tide/agent.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
from ...autocomplete import AutoComplete
66
from .models import Steps
77
from .prompts import (
8-
AGENT_TIDE_SYSTEM_PROMPT, GET_CODE_IDENTIFIERS_SYSTEM_PROMPT, STAGED_DIFFS_TEMPLATE, STEPS_SYSTEM_PROMPT, WRITE_PATCH_SYSTEM_PROMPT
8+
AGENT_TIDE_SYSTEM_PROMPT, GET_CODE_IDENTIFIERS_SYSTEM_PROMPT, REJECT_PATCH_FEEDBACK_TEMPLATE,
9+
STAGED_DIFFS_TEMPLATE, STEPS_SYSTEM_PROMPT, WRITE_PATCH_SYSTEM_PROMPT
910
)
10-
from .utils import parse_commit_blocks, parse_patch_blocks, parse_steps_markdown, trim_to_patch_section
11+
from .utils import delete_file, parse_commit_blocks, parse_patch_blocks, parse_steps_markdown, trim_to_patch_section
1112
from .consts import AGENT_TIDE_ASCII_ART
1213

1314
try:
@@ -46,14 +47,37 @@ class AgentTide(BaseModel):
4647
steps :Optional[Steps]=None
4748
session_id :str=Field(default_factory=ulid)
4849
changed_paths :List[str]=Field(default_factory=list)
50+
request_human_confirmation :bool=False
51+
4952
_skip_context_retrieval :bool=False
5053
_last_code_identifers :Optional[Set[str]]=set()
5154
_last_code_context :Optional[str] = None
55+
_has_patch :bool=False
5256

5357
@model_validator(mode="after")
5458
def pass_custom_logger_fn(self)->Self:
5559
self.llm.logger_fn = partial(custom_logger_fn, session_id=self.session_id, filepath=self.patch_path)
5660
return self
61+
62+
def approve(self):
63+
self._has_patch = False
64+
if os.path.exists(self.patch_path):
65+
changed_paths = process_patch(self.patch_path, open_file, write_file, remove_file, file_exists)
66+
self.changed_paths.extend(changed_paths)
67+
68+
previous_response = self.history[-1]
69+
diffPatches = parse_patch_blocks(previous_response, multiple=True)
70+
if diffPatches:
71+
for patch in diffPatches:
72+
# TODO this deletes previouspatches from history to make sure changes are always focused on the latest version of the file
73+
previous_response = previous_response.replace(f"*** Begin Patch\n{patch}*** End Patch", "")
74+
self.history[-1] = previous_response
75+
76+
def reject(self, feedback :str):
77+
self._has_patch = False
78+
self.history.append(REJECT_PATCH_FEEDBACK_TEMPLATE.format(
79+
FEEDBACK=feedback
80+
))
5781

5882
@property
5983
def patch_path(self)->Path:
@@ -105,6 +129,7 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None):
105129
codeContext = self.tide.get(validatedCodeIdentifiers, as_string=True)
106130
self._last_code_context = codeContext
107131

132+
await delete_file(self.patch_path)
108133
response = await self.llm.acomplete(
109134
self.history,
110135
system_prompt=[
@@ -116,9 +141,8 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None):
116141
)
117142

118143
await trim_to_patch_section(self.patch_path)
119-
if os.path.exists(self.patch_path):
120-
changed_paths = process_patch(self.patch_path, open_file, write_file, remove_file, file_exists)
121-
self.changed_paths.extend(changed_paths)
144+
if not self.request_human_confirmation:
145+
self.approve()
122146

123147
commitMessage = parse_commit_blocks(response, multiple=False)
124148
if commitMessage:
@@ -130,9 +154,12 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None):
130154

131155
diffPatches = parse_patch_blocks(response, multiple=True)
132156
if diffPatches:
133-
for patch in diffPatches:
134-
# TODO this deletes previouspatches from history to make sure changes are always focused on the latest version of the file
135-
response = response.replace(f"*** Begin Patch\n{patch}*** End Patch", "")
157+
if self.request_human_confirmation:
158+
self._has_patch = True
159+
else:
160+
for patch in diffPatches:
161+
# TODO this deletes previouspatches from history to make sure changes are always focused on the latest version of the file
162+
response = response.replace(f"*** Begin Patch\n{patch}*** End Patch", "")
136163

137164
self.history.append(response)
138165

codetide/agents/tide/prompts.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,4 +362,14 @@
362362
** The following diffs are currently staged and will be commited once you generate an appropriate description:**
363363
364364
{diffs}
365+
"""
366+
367+
REJECT_PATCH_FEEDBACK_TEMPLATE = """
368+
**PATCH REJECTED** - The patch(es) you proposed in the previous message were **not applied** to the codebase.
369+
370+
**Feedback to address:**
371+
372+
{FEEDBACK}
373+
374+
**Next steps:** Please revise your approach to fulfill the task requirements based on the feedback above.
365375
"""

codetide/agents/tide/ui/app.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ async def on_execute_steps(action :cl.Action):
213213
author="Agent Tide"
214214
).send()
215215

216-
await agent_loop(step_instructions_msg, codeIdentifiers=step.context_identifiers)
216+
await agent_loop(step_instructions_msg, codeIdentifiers=step.context_identifiers, agent_tide_ui=agent_tide_ui)
217217

218218
task_list.status = f"Waiting feedback on step {current_task_idx}"
219219
await task_list.send()
@@ -264,18 +264,20 @@ async def on_inspect_context(action :cl.Action):
264264

265265

266266
@cl.on_message
267-
async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None):
268-
agent_tide_ui = await loadAgentTideUi()
267+
async def agent_loop(message: Optional[cl.Message]=None, codeIdentifiers: Optional[list] = None, agent_tide_ui :Optional[AgentTideUi]=None):
268+
if agent_tide_ui is None:
269+
agent_tide_ui = await loadAgentTideUi()
269270

270271
chat_history = cl.user_session.get("chat_history")
272+
273+
if message is not None:
274+
if message.command:
275+
command_prompt = await agent_tide_ui.get_command_prompt(message.command)
276+
if command_prompt:
277+
message.content = "\n\n---\n\n".join([command_prompt, message.content])
271278

272-
if message.command:
273-
command_prompt = await agent_tide_ui.get_command_prompt(message.command)
274-
if command_prompt:
275-
message.content = "\n\n---\n\n".join([command_prompt, message.content])
276-
277-
chat_history.append({"role": "user", "content": message.content})
278-
await agent_tide_ui.add_to_history(message.content)
279+
chat_history.append({"role": "user", "content": message.content})
280+
await agent_tide_ui.add_to_history(message.content)
279281

280282
msg = cl.Message(content="", author="Agent Tide")
281283
async with cl.Step("ApplyPatch", type="tool") as diff_step:
@@ -347,11 +349,37 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None
347349
)
348350
)
349351

350-
# # Send the final message
351-
await msg.send()
352+
# Send the final message
353+
await msg.send()
354+
355+
chat_history.append({"role": "assistant", "content": msg.content})
356+
await agent_tide_ui.add_to_history(msg.content)
357+
358+
await asyncio.sleep(0.5)
359+
if agent_tide_ui.agent_tide._has_patch:
360+
choice = await cl.AskActionMessage(
361+
content="AgentTide is asking you to review the Patch before applying it.",
362+
actions=[
363+
cl.Action(name="approve_patch", payload={"lgtm": True}, label="✔️ Approve"),
364+
cl.Action(name="reject_patch", payload={"lgtm": False}, label="❌ Reject"),
365+
],
366+
timeout=3600
367+
).send()
368+
369+
if choice:
370+
lgtm = choice.get("payload", []).get("lgtm")
371+
if lgtm:
372+
agent_tide_ui.agent_tide.approve()
373+
else:
374+
response = await cl.AskUserMessage(
375+
content="""Please provide specific feedback explaining why the patch was rejected. Include what's wrong, which parts are problematic, and what needs to change. Avoid vague responses like "doesn't work" - instead be specific like "missing error handling for FileNotFoundError" or "function should return boolean, not None." Your detailed feedback helps generate a better solution.""",
376+
timeout=3600
377+
).send()
352378

353-
chat_history.append({"role": "assistant", "content": msg.content})
354-
await agent_tide_ui.add_to_history(msg.content)
379+
feedback = response.get("output")
380+
agent_tide_ui.agent_tide.reject(feedback)
381+
chat_history.append({"role": "user", "content": feedback})
382+
await agent_loop(agent_tide_ui=agent_tide_ui)
355383

356384
# def generate_temp_password(length=16):
357385
# characters = string.ascii_letters + string.digits + string.punctuation

examples/hf_demo_space/app.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ async def start_chatr():
8181
cl.Action(name="gpt-oss-20b", payload={"model": "openai/gpt-oss-20b:free"}, label="gpt-oss-20b"),
8282
cl.Action(name="custom", payload={"model": "custom"}, label="bring your model")
8383
],
84+
timeout=3600
8485
).send()
8586

8687

@@ -238,7 +239,7 @@ async def on_execute_steps(action :cl.Action):
238239
author="Agent Tide"
239240
).send()
240241

241-
await agent_loop(step_instructions_msg, codeIdentifiers=step.context_identifiers)
242+
await agent_loop(step_instructions_msg, codeIdentifiers=step.context_identifiers, agent_tide_ui=agent_tide_ui)
242243

243244
task_list.status = f"Waiting feedback on step {current_task_idx}"
244245
await task_list.send()
@@ -294,18 +295,21 @@ async def on_inspect_context(action :cl.Action):
294295
await inspect_msg.send()
295296

296297
@cl.on_message
297-
async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None):
298-
agent_tide_ui = cl.user_session.get("AgentTideUi")
298+
async def agent_loop(message: Optional[cl.Message]=None, codeIdentifiers: Optional[list] = None, agent_tide_ui :Optional[AgentTideUi]=None):
299+
if agent_tide_ui is None:
300+
agent_tide_ui = cl.user_session.get("AgentTideUi")
299301

300302
chat_history = cl.user_session.get("chat_history")
303+
304+
if message is not None:
305+
if message.command:
306+
command_prompt = await agent_tide_ui.get_command_prompt(message.command)
307+
if command_prompt:
308+
message.content = "\n\n---\n\n".join([command_prompt, message.content])
301309

302-
if message.command:
303-
command_prompt = await agent_tide_ui.get_command_prompt(message.command)
304-
if command_prompt:
305-
message.content = "\n\n---\n\n".join([command_prompt, message.content])
310+
chat_history.append({"role": "user", "content": message.content})
311+
await agent_tide_ui.add_to_history(message.content)
306312

307-
chat_history.append({"role": "user", "content": message.content})
308-
await agent_tide_ui.add_to_history(message.content)
309313

310314
msg = cl.Message(content="", author="Agent Tide")
311315

@@ -386,11 +390,38 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None
386390
payload={"msg_id": msg.id}
387391
)
388392
)
389-
# # Send the final message
390-
await msg.send()
391393

392-
chat_history.append({"role": "assistant", "content": msg.content})
393-
await agent_tide_ui.add_to_history(msg.content)
394+
# Send the final message
395+
await msg.send()
396+
397+
chat_history.append({"role": "assistant", "content": msg.content})
398+
await agent_tide_ui.add_to_history(msg.content)
399+
400+
await asyncio.sleep(0.5)
401+
if agent_tide_ui.agent_tide._has_patch:
402+
choice = await cl.AskActionMessage(
403+
content="AgentTide is asking you to review the Patch before applying it.",
404+
actions=[
405+
cl.Action(name="approve_patch", payload={"lgtm": True}, label="✔️ Approve"),
406+
cl.Action(name="reject_patch", payload={"lgtm": False}, label="❌ Reject"),
407+
],
408+
timeout=3600
409+
).send()
410+
411+
if choice:
412+
lgtm = choice.get("payload", []).get("lgtm")
413+
if lgtm:
414+
agent_tide_ui.agent_tide.approve()
415+
else:
416+
response = await cl.AskUserMessage(
417+
content="""Please provide specific feedback explaining why the patch was rejected. Include what's wrong, which parts are problematic, and what needs to change. Avoid vague responses like "doesn't work" - instead be specific like "missing error handling for FileNotFoundError" or "function should return boolean, not None." Your detailed feedback helps generate a better solution.""",
418+
timeout=3600
419+
).send()
420+
421+
feedback = response.get("output")
422+
agent_tide_ui.agent_tide.reject(feedback)
423+
chat_history.append({"role": "user", "content": feedback})
424+
await agent_loop(agent_tide_ui=agent_tide_ui)
394425

395426
if __name__ == "__main__":
396427
from dotenv import load_dotenv

examples/hf_demo_space/static/landing_page.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,9 @@
134134
}
135135

136136
.logo {
137-
max-width: 650px;
138-
height: auto;
137+
max-width: 650px; /* won’t exceed 650px on big screens */
138+
width: 100%; /* scales down to fit smaller screens */
139+
height: auto; /* maintains aspect ratio */
139140
margin-bottom: 0.5rem;
140141
filter: drop-shadow(0 4px 8px hsl(var(--foreground) / 0.1));
141142
animation: logoGlow 3s ease-in-out infinite alternate;

0 commit comments

Comments
 (0)