diff --git a/frontends/chatapp_common.py b/frontends/chatapp_common.py index befaf1c8d..c49cbf173 100644 --- a/frontends/chatapp_common.py +++ b/frontends/chatapp_common.py @@ -57,8 +57,26 @@ def clean_reply(text): return re.sub(r"\n{3,}", "\n\n", text).strip() or "..." +_FILE_PLACEHOLDERS = { + "filepath", + "", + "path", + "", + "file_path", + "", + "...", +} + + def extract_files(text): - return re.findall(r"\[FILE:([^\]]+)\]", text or "") + files, seen = [], set() + for fpath in re.findall(r"\[FILE:([^\]]+)\]", text or ""): + fpath = fpath.strip() + if not fpath or fpath.lower() in _FILE_PLACEHOLDERS or fpath in seen: + continue + files.append(fpath) + seen.add(fpath) + return files def strip_files(text): diff --git a/frontends/fsapp.py b/frontends/fsapp.py index 8fa97beed..a1451d515 100644 --- a/frontends/fsapp.py +++ b/frontends/fsapp.py @@ -9,7 +9,7 @@ sys.path.insert(0, PROJECT_ROOT) os.chdir(PROJECT_ROOT) from agentmain import GeneraticAgent -from frontends.chatapp_common import format_restore +from frontends.chatapp_common import extract_files, format_restore from frontends.continue_cmd import handle_frontend_command as handle_continue_frontend, reset_conversation from llmcore import mykeys @@ -116,7 +116,7 @@ def _clean(text): def _extract_files(text): - return re.findall(r"\[FILE:([^\]]+)\]", text or "") + return extract_files(text) def _strip_files(text): diff --git a/tests/test_chatapp_common.py b/tests/test_chatapp_common.py new file mode 100644 index 000000000..009bdbe15 --- /dev/null +++ b/tests/test_chatapp_common.py @@ -0,0 +1,32 @@ +import os +import sys +import unittest + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path[:0] = [ROOT, os.path.join(ROOT, "frontends")] + +from frontends.chatapp_common import extract_files + + +class ExtractFilesTest(unittest.TestCase): + def test_ignores_file_hint_placeholders(self): + text = ( + "If you need to show files to user, use [FILE:filepath] in your response.\n" + "Other placeholders: [FILE:] [FILE:path] [FILE:] " + "[FILE:file_path] [FILE:] [FILE:...]" + ) + + self.assertEqual(extract_files(text), []) + + def test_keeps_real_paths_and_deduplicates(self): + text = ( + "Created [FILE:/tmp/report.txt]\n" + "Again [FILE:/tmp/report.txt]\n" + "Relative [FILE:outputs/chart.png]" + ) + + self.assertEqual(extract_files(text), ["/tmp/report.txt", "outputs/chart.png"]) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file