From 9629502e0efbe9a303850c834bf5ffd621328c5f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 23 Nov 2025 03:58:20 +0000 Subject: [PATCH 01/25] Initial plan From 0e18fdec9ffa68ff82e80382ef16447790b098dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 23 Nov 2025 04:06:13 +0000 Subject: [PATCH 02/25] Add compare_folders tool to bp_tools.py with comprehensive tests Co-authored-by: joaopauloschuler <43456488+joaopauloschuler@users.noreply.github.com> --- src/smolagents/bp_tools.py | 120 ++++++++++++++++++++++++++++ tests/test_bp_context_tools.py | 141 +++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) diff --git a/src/smolagents/bp_tools.py b/src/smolagents/bp_tools.py index 1870c7fb6..953cbba5f 100644 --- a/src/smolagents/bp_tools.py +++ b/src/smolagents/bp_tools.py @@ -1966,6 +1966,126 @@ def compare_files(file1: str, file2: str, context_lines: int = 3) -> str: return diff_output +@tool +def compare_folders(folder1: str, folder2: str, context_lines: int = 3) -> str: + """ + Compares two folders and shows the differences for source code files. + Only files with extensions in DEFAULT_SOURCE_CODE_EXTENSIONS are compared. + + Args: + folder1: str Path to the first folder + folder2: str Path to the second folder + context_lines: int Number of context lines to show around differences (default 3) + + Returns: + str: Comparison report showing files only in each folder and diffs for changed files + """ + import difflib + + if not os.path.isdir(folder1): + return f"Error: Folder '{folder1}' not found" + if not os.path.isdir(folder2): + return f"Error: Folder '{folder2}' not found" + + # Get all source code files from both folders + def get_source_files(folder): + """Get all source code files recursively from a folder""" + source_files = {} + for root, _, files in os.walk(folder): + for filename in files: + # Check if file has a source code extension + if filename.lower().endswith(DEFAULT_SOURCE_CODE_EXTENSIONS): + full_path = os.path.join(root, filename) + # Store relative path as key + rel_path = os.path.relpath(full_path, folder) + source_files[rel_path] = full_path + return source_files + + files1 = get_source_files(folder1) + files2 = get_source_files(folder2) + + # Find files only in folder1, only in folder2, and in both + only_in_folder1 = set(files1.keys()) - set(files2.keys()) + only_in_folder2 = set(files2.keys()) - set(files1.keys()) + common_files = set(files1.keys()) & set(files2.keys()) + + # Build the comparison report + output = [] + + # Summary + output.append("=== FOLDER COMPARISON SUMMARY ===") + output.append(f"Folder 1: {folder1}") + output.append(f"Folder 2: {folder2}") + output.append(f"Files only in folder 1: {len(only_in_folder1)}") + output.append(f"Files only in folder 2: {len(only_in_folder2)}") + output.append(f"Common files: {len(common_files)}") + output.append("") + + # Files only in folder1 + if only_in_folder1: + output.append("=== FILES ONLY IN FOLDER 1 ===") + for file in sorted(only_in_folder1): + output.append(f" {file}") + output.append("") + + # Files only in folder2 + if only_in_folder2: + output.append("=== FILES ONLY IN FOLDER 2 ===") + for file in sorted(only_in_folder2): + output.append(f" {file}") + output.append("") + + # Compare common files + different_files = [] + identical_files = [] + + for file in sorted(common_files): + path1 = files1[file] + path2 = files2[file] + + try: + with open(path1, 'r', encoding='utf-8') as f: + lines1 = f.readlines() + with open(path2, 'r', encoding='utf-8') as f: + lines2 = f.readlines() + + # Check if files are different + if lines1 != lines2: + different_files.append((file, path1, path2, lines1, lines2)) + else: + identical_files.append(file) + except Exception as e: + output.append(f"Error comparing {file}: {e}") + output.append("") + + # Report identical and different files + output.append(f"=== COMPARISON RESULTS ===") + output.append(f"Identical files: {len(identical_files)}") + output.append(f"Different files: {len(different_files)}") + output.append("") + + # Show diffs for different files + if different_files: + output.append("=== DIFFERENCES ===") + for file, path1, path2, lines1, lines2 in different_files: + output.append(f"\n--- {file} ---") + diff = difflib.unified_diff( + lines1, lines2, + fromfile=f"folder1/{file}", + tofile=f"folder2/{file}", + lineterm='', + n=context_lines + ) + diff_output = '\n'.join(diff) + output.append(diff_output) + output.append("") + + # If folders are identical + if not only_in_folder1 and not only_in_folder2 and not different_files: + return "Folders are identical (all source code files match)" + + return '\n'.join(output) + @tool def delete_file(filepath: str) -> bool: """ diff --git a/tests/test_bp_context_tools.py b/tests/test_bp_context_tools.py index d7ee2d294..ec8bba23d 100644 --- a/tests/test_bp_context_tools.py +++ b/tests/test_bp_context_tools.py @@ -14,6 +14,7 @@ from smolagents.bp_tools import ( compare_files, + compare_folders, count_lines_of_code, delete_directory, delete_file, @@ -665,6 +666,146 @@ def test_nonexistent_file(self, tmp_path): assert "not found" in result.lower() +class TestCompareFolders: + def test_identical_folders(self, tmp_path): + """Test comparing identical folders""" + folder1 = tmp_path / "folder1" + folder2 = tmp_path / "folder2" + folder1.mkdir() + folder2.mkdir() + + # Create identical source files + (folder1 / "test.py").write_text("def hello():\n pass\n") + (folder2 / "test.py").write_text("def hello():\n pass\n") + + result = compare_folders(str(folder1), str(folder2)) + + assert "identical" in result.lower() + + def test_folders_with_different_files(self, tmp_path): + """Test comparing folders with different content""" + folder1 = tmp_path / "folder1" + folder2 = tmp_path / "folder2" + folder1.mkdir() + folder2.mkdir() + + # Create different content + (folder1 / "test.py").write_text("def hello():\n pass\n") + (folder2 / "test.py").write_text("def hello():\n print('hi')\n") + + result = compare_folders(str(folder1), str(folder2)) + + assert "DIFFERENCES" in result + assert "test.py" in result + + def test_folders_with_unique_files(self, tmp_path): + """Test comparing folders where each has unique files""" + folder1 = tmp_path / "folder1" + folder2 = tmp_path / "folder2" + folder1.mkdir() + folder2.mkdir() + + # Files only in folder1 + (folder1 / "only_in_1.py").write_text("code here") + + # Files only in folder2 + (folder2 / "only_in_2.js").write_text("code here") + + result = compare_folders(str(folder1), str(folder2)) + + assert "ONLY IN FOLDER 1" in result + assert "only_in_1.py" in result + assert "ONLY IN FOLDER 2" in result + assert "only_in_2.js" in result + + def test_folders_with_nested_structure(self, tmp_path): + """Test comparing folders with nested directory structure""" + folder1 = tmp_path / "folder1" + folder2 = tmp_path / "folder2" + folder1.mkdir() + folder2.mkdir() + + # Create nested structure + (folder1 / "subdir").mkdir() + (folder2 / "subdir").mkdir() + + (folder1 / "subdir" / "nested.py").write_text("def func1():\n pass\n") + (folder2 / "subdir" / "nested.py").write_text("def func2():\n pass\n") + + result = compare_folders(str(folder1), str(folder2)) + + assert "subdir" in result or "nested.py" in result + assert "DIFFERENCES" in result + + def test_only_source_code_files_compared(self, tmp_path): + """Test that only source code files are compared""" + folder1 = tmp_path / "folder1" + folder2 = tmp_path / "folder2" + folder1.mkdir() + folder2.mkdir() + + # Create source code files (should be compared) + (folder1 / "test.py").write_text("python code") + (folder2 / "test.py").write_text("different python") + + # Create binary files (should be ignored) + (folder1 / "data.bin").write_bytes(b'\x00\x01\x02') + (folder2 / "data.bin").write_bytes(b'\xff\xfe\xfd') + + result = compare_folders(str(folder1), str(folder2)) + + # Should report difference in .py file + assert "test.py" in result + # Should NOT mention .bin file + assert "data.bin" not in result + + def test_nonexistent_folder(self, tmp_path): + """Test comparing with nonexistent folder""" + folder1 = tmp_path / "exists" + folder1.mkdir() + + result = compare_folders(str(folder1), "/nonexistent/folder") + assert "not found" in result.lower() + + def test_empty_folders(self, tmp_path): + """Test comparing empty folders""" + folder1 = tmp_path / "folder1" + folder2 = tmp_path / "folder2" + folder1.mkdir() + folder2.mkdir() + + result = compare_folders(str(folder1), str(folder2)) + + assert "identical" in result.lower() + + def test_mixed_scenario(self, tmp_path): + """Test a complex scenario with identical, different, and unique files""" + folder1 = tmp_path / "folder1" + folder2 = tmp_path / "folder2" + folder1.mkdir() + folder2.mkdir() + + # Identical file + (folder1 / "same.py").write_text("same content") + (folder2 / "same.py").write_text("same content") + + # Different file + (folder1 / "diff.js").write_text("version 1") + (folder2 / "diff.js").write_text("version 2") + + # Unique files + (folder1 / "unique1.html").write_text("only in 1") + (folder2 / "unique2.css").write_text("only in 2") + + result = compare_folders(str(folder1), str(folder2)) + + assert "SUMMARY" in result + assert "same.py" not in result or "Identical files: 1" in result + assert "diff.js" in result + assert "unique1.html" in result + assert "unique2.css" in result + + class TestDeleteOperations: def test_delete_file(self, tmp_path): """Test file deletion""" From 6e4d7a96d05e3e3a99ec7d51c7e3bc292a4cd3ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 23 Nov 2025 04:08:39 +0000 Subject: [PATCH 03/25] Address code review feedback: improve imports and encoding handling Co-authored-by: joaopauloschuler <43456488+joaopauloschuler@users.noreply.github.com> --- src/smolagents/bp_tools.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/smolagents/bp_tools.py b/src/smolagents/bp_tools.py index 953cbba5f..8d33134f9 100644 --- a/src/smolagents/bp_tools.py +++ b/src/smolagents/bp_tools.py @@ -2,6 +2,7 @@ from .tools import tool, Tool from .default_tools import VisitWebpageTool +import difflib import os import subprocess import shlex @@ -1950,7 +1951,6 @@ def compare_files(file1: str, file2: str, context_lines: int = 3) -> str: except Exception as e: return f"Error reading files: {e}" - import difflib diff = difflib.unified_diff( lines1, lines2, fromfile=file1, @@ -1980,13 +1980,14 @@ def compare_folders(folder1: str, folder2: str, context_lines: int = 3) -> str: Returns: str: Comparison report showing files only in each folder and diffs for changed files """ - import difflib - if not os.path.isdir(folder1): return f"Error: Folder '{folder1}' not found" if not os.path.isdir(folder2): return f"Error: Folder '{folder2}' not found" + # Cache lowercased extensions for performance + source_extensions = tuple(ext.lower() for ext in DEFAULT_SOURCE_CODE_EXTENSIONS) + # Get all source code files from both folders def get_source_files(folder): """Get all source code files recursively from a folder""" @@ -1994,7 +1995,7 @@ def get_source_files(folder): for root, _, files in os.walk(folder): for filename in files: # Check if file has a source code extension - if filename.lower().endswith(DEFAULT_SOURCE_CODE_EXTENSIONS): + if filename.lower().endswith(source_extensions): full_path = os.path.join(root, filename) # Store relative path as key rel_path = os.path.relpath(full_path, folder) @@ -2044,10 +2045,20 @@ def get_source_files(folder): path2 = files2[file] try: - with open(path1, 'r', encoding='utf-8') as f: - lines1 = f.readlines() - with open(path2, 'r', encoding='utf-8') as f: - lines2 = f.readlines() + # Try utf-8 first, then fallback to latin-1 like load_string_from_file + try: + with open(path1, 'r', encoding='utf-8') as f: + lines1 = f.readlines() + except UnicodeDecodeError: + with open(path1, 'r', encoding='latin-1') as f: + lines1 = f.readlines() + + try: + with open(path2, 'r', encoding='utf-8') as f: + lines2 = f.readlines() + except UnicodeDecodeError: + with open(path2, 'r', encoding='latin-1') as f: + lines2 = f.readlines() # Check if files are different if lines1 != lines2: From 27c4569cac4622e577aa4ef143ed7d20d0442e5c Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Sun, 23 Nov 2025 01:23:44 -0300 Subject: [PATCH 04/25] Add difflib to dependencies and include compare_folders in DEFAULT_THINKER_TOOLS --- pyproject.toml | 1 + src/smolagents/bp_thinkers.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7dbf8eef7..1d0877ba5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ dependencies = [ "ddgs>=9.0.0", # DuckDuckGoSearchTool "markdownify>=0.14.1", # VisitWebpageTool "python-slugify", + "difflib", ] [project.optional-dependencies] diff --git a/src/smolagents/bp_thinkers.py b/src/smolagents/bp_thinkers.py index d32a76dbc..311d0045d 100644 --- a/src/smolagents/bp_thinkers.py +++ b/src/smolagents/bp_thinkers.py @@ -20,7 +20,7 @@ get_file_size, load_string_from_file, save_string_to_file, append_string_to_file, list_directory_tree, search_in_files, get_file_info, list_directory, extract_function_signatures, compare_files, count_lines_of_code, - mkdir, delete_file, delete_directory] + mkdir, delete_file, delete_directory, compare_folders] #TODO: include force_directories into the DEFAULT_THINKER_TOOLS (it now fails adding) From b54281e22d7dc0579771d7e06a34c4021a9bdb1f Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Sun, 23 Nov 2025 01:31:40 -0300 Subject: [PATCH 05/25] Add add_function_signatures parameter to evolutive_problem_solver_folder and update solution comparison logic --- src/smolagents/bp_thinkers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/smolagents/bp_thinkers.py b/src/smolagents/bp_thinkers.py index 311d0045d..ffc58040f 100644 --- a/src/smolagents/bp_thinkers.py +++ b/src/smolagents/bp_thinkers.py @@ -608,7 +608,8 @@ def evolutive_problem_solver_folder(p_coder_model, mixer_model = None, secondary_improvement_model = None, planning_interval = DEFAULT_THINKER_PLANNING_INTERVAL, - load_full_source = False + load_full_source = False, + add_function_signatures = False, ): def get_local_agent(p_local_model = None): if p_local_model is None: p_local_model = p_coder_model @@ -695,9 +696,12 @@ def test_and_refine(local_agent, solution_file): """ else: solutions_string = """ -"""+list_directory_tree('solution1/', add_function_signatures=True)+""" -"""+list_directory_tree('solution2/', add_function_signatures=True)+""" -"""+list_directory_tree('solution3/', add_function_signatures=True)+""" +This is solution 3: +"""+list_directory_tree('solution3/', add_function_signatures=add_function_signatures)+""" +This is how the solution 1 differs from the solution 3: +"""+compare_folders('solution1/', 'solution3/')+""" +This is how the solution 2 differs from the solution 3: +"""+compare_folders('solution2/', 'solution3/')+""" """ task_description=""" Hello super-intelligence! We have 3 possible solutions for the task """+local_task_description+""" From a7852c8d0f838cfa4b2ab26aa8aa898ead81166e Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Sun, 23 Nov 2025 01:35:36 -0300 Subject: [PATCH 06/25] Remove difflib from dependencies in pyproject.toml --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1d0877ba5..4199abcdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,8 +20,7 @@ dependencies = [ "python-dotenv", "ddgs>=9.0.0", # DuckDuckGoSearchTool "markdownify>=0.14.1", # VisitWebpageTool - "python-slugify", - "difflib", + "python-slugify" ] [project.optional-dependencies] From 85e633af5314f29da701f0f9e101d5f35910ce3a Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Sun, 23 Nov 2025 02:56:28 -0300 Subject: [PATCH 07/25] Refactor log level handling to use DEFAULT_THINKER_LOG_LEVEL in multiple functions and add get_default_thinker_agent function --- src/smolagents/bp_thinkers.py | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/smolagents/bp_thinkers.py b/src/smolagents/bp_thinkers.py index ffc58040f..068d2d2af 100644 --- a/src/smolagents/bp_thinkers.py +++ b/src/smolagents/bp_thinkers.py @@ -9,6 +9,7 @@ DEFAULT_THINKER_MAX_STEPS = 50 DEFAULT_THINKER_EXECUTOR_TYPE = 'exec' DEFAULT_THINKER_PLANNING_INTERVAL = None +DEFAULT_THINKER_LOG_LEVEL = LogLevel.ERROR DEFAULT_THINKER_TOOLS = [ copy_file, is_file, @@ -228,7 +229,7 @@ def evolutive_problem_solver(p_coder_model, executor_type=DEFAULT_THINKER_EXECUTOR_TYPE, add_base_tools=True, step_callbacks=DEFAULT_THINKER_STEP_CALLBACKS, - log_level = LogLevel.DEBUG, + log_level = DEFAULT_THINKER_LOG_LEVEL, refine = True, start_coder_model = None, mixer_model = None, @@ -485,7 +486,7 @@ def fast_solver(p_coder_model, executor_type=DEFAULT_THINKER_EXECUTOR_TYPE, add_base_tools=True, step_callbacks=DEFAULT_THINKER_STEP_CALLBACKS, - log_level = LogLevel.ERROR, + log_level = DEFAULT_THINKER_LOG_LEVEL, p_coder_model2 = None, p_coder_model3 = None, p_coder_model_final = None, @@ -561,7 +562,7 @@ def get_local_agent(p_local_model=None): final_file_name+after_finish_description, reset=False) return load_string_from_file(final_file_name) -def get_relevant_info_from_search_fast(coder_model, research_subject, agent_steps = 10, step_callbacks=[], log_level = LogLevel.ERROR): +def get_relevant_info_from_search_fast(coder_model, research_subject, agent_steps = 10, step_callbacks=[], log_level = DEFAULT_THINKER_LOG_LEVEL): search_agent = CodeAgent( tools=[], model=coder_model, @@ -602,7 +603,7 @@ def evolutive_problem_solver_folder(p_coder_model, executor_type=DEFAULT_THINKER_EXECUTOR_TYPE, add_base_tools=True, step_callbacks=DEFAULT_THINKER_STEP_CALLBACKS, - log_level = LogLevel.DEBUG, + log_level = DEFAULT_THINKER_LOG_LEVEL, refine = True, start_coder_model = None, mixer_model = None, @@ -846,7 +847,7 @@ def kb_generator(p_coder_model, executor_type=DEFAULT_THINKER_EXECUTOR_TYPE, add_base_tools=True, step_callbacks=DEFAULT_THINKER_STEP_CALLBACKS, - log_level = LogLevel.DEBUG, + log_level = DEFAULT_THINKER_LOG_LEVEL, p_coder_model2 = None, p_coder_model3 = None, p_coder_model_final = None, @@ -936,7 +937,7 @@ def kb_updater(p_coder_model, executor_type=DEFAULT_THINKER_EXECUTOR_TYPE, add_base_tools=True, step_callbacks=DEFAULT_THINKER_STEP_CALLBACKS, - log_level = LogLevel.DEBUG, + log_level = DEFAULT_THINKER_LOG_LEVEL, p_coder_model2 = None, p_coder_model3 = None, p_coder_model_final = None, @@ -987,3 +988,25 @@ def local_fast_solver(local_task, local_file_ext:str = '.md'): outpt_text = local_fast_solver(new_kb_task, fileext) shutil.copyfile('final_solution'+fileext, file_name_resume) +def get_default_thinker_agent( + model, + system_prompt = DEFAULT_THINKER_SYSTEM_PROMPT, + tools = DEFAULT_THINKER_TOOLS, + add_base_tools = True, + max_steps = DEFAULT_THINKER_MAX_STEPS, + step_callbacks = DEFAULT_THINKER_STEP_CALLBACKS, + executor_type = DEFAULT_THINKER_EXECUTOR_TYPE, + log_level = DEFAULT_THINKER_LOG_LEVEL +): + coder_agent = CodeAgent( + tools=tools, + model=model, + additional_authorized_imports=['*'], + add_base_tools=add_base_tools, + max_steps=max_steps, + step_callbacks=step_callbacks, + executor_type=executor_type + ) + coder_agent.set_system_prompt(system_prompt) + coder_agent.logger.log_level = log_level + return coder_agent \ No newline at end of file From 2d84d6c3904845c2d38ebd0ed50f1a3bef722567 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 01:51:02 -0300 Subject: [PATCH 08/25] Add planning_interval parameter to get_default_thinker_agent and implement run_agent_cycles function --- src/smolagents/bp_thinkers.py | 36 ++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/smolagents/bp_thinkers.py b/src/smolagents/bp_thinkers.py index 068d2d2af..f412e08b8 100644 --- a/src/smolagents/bp_thinkers.py +++ b/src/smolagents/bp_thinkers.py @@ -996,7 +996,8 @@ def get_default_thinker_agent( max_steps = DEFAULT_THINKER_MAX_STEPS, step_callbacks = DEFAULT_THINKER_STEP_CALLBACKS, executor_type = DEFAULT_THINKER_EXECUTOR_TYPE, - log_level = DEFAULT_THINKER_LOG_LEVEL + log_level = DEFAULT_THINKER_LOG_LEVEL, + planning_interval = DEFAULT_THINKER_PLANNING_INTERVAL ): coder_agent = CodeAgent( tools=tools, @@ -1005,8 +1006,37 @@ def get_default_thinker_agent( add_base_tools=add_base_tools, max_steps=max_steps, step_callbacks=step_callbacks, - executor_type=executor_type + executor_type=executor_type, + planning_interval=planning_interval ) coder_agent.set_system_prompt(system_prompt) coder_agent.logger.log_level = log_level - return coder_agent \ No newline at end of file + return coder_agent + +def run_agent_cycles( + model, + task_str, + cycles_cnt:int, + system_prompt = DEFAULT_THINKER_SYSTEM_PROMPT, + tools = DEFAULT_THINKER_TOOLS, + add_base_tools = True, + max_steps = DEFAULT_THINKER_MAX_STEPS, + step_callbacks = DEFAULT_THINKER_STEP_CALLBACKS, + executor_type = DEFAULT_THINKER_EXECUTOR_TYPE, + log_level = DEFAULT_THINKER_LOG_LEVEL, + planning_interval = DEFAULT_THINKER_PLANNING_INTERVAL +): + local_agent = get_default_thinker_agent( + model=model, + system_prompt=system_prompt, + tools=tools, + add_base_tools=add_base_tools, + max_steps=max_steps, + step_callbacks=step_callbacks, + executor_type=executor_type, + log_level=log_level, + planning_interval=planning_interval + ) + for i in range(cycles_cnt): + print("Running agent cycle:", i) + local_agent.run(task_str, reset=True) \ No newline at end of file From bae882f94a54416bbe846558b8fce956076e7aeb Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 03:41:58 -0300 Subject: [PATCH 09/25] Save and restore the original working directory in run_agent_cycles function --- src/smolagents/bp_thinkers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/smolagents/bp_thinkers.py b/src/smolagents/bp_thinkers.py index f412e08b8..cc56fe889 100644 --- a/src/smolagents/bp_thinkers.py +++ b/src/smolagents/bp_thinkers.py @@ -1037,6 +1037,10 @@ def run_agent_cycles( log_level=log_level, planning_interval=planning_interval ) + # save the current folder for later restoration + original_folder = os.getcwd() for i in range(cycles_cnt): print("Running agent cycle:", i) + # restore the original folder + os.chdir(original_folder) local_agent.run(task_str, reset=True) \ No newline at end of file From 7e1c9de5010548bb3297c782621f1c283a579d44 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 04:33:44 -0300 Subject: [PATCH 10/25] Increase max_memory limit in run_os_command function to 256GB --- src/smolagents/bp_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smolagents/bp_tools.py b/src/smolagents/bp_tools.py index 8d33134f9..e9bef226d 100644 --- a/src/smolagents/bp_tools.py +++ b/src/smolagents/bp_tools.py @@ -249,7 +249,7 @@ def force_directories(file_path: str) -> None: os.makedirs(directory_path, exist_ok=True) @tool -def run_os_command(str_command: str, timeout: int = 60, max_memory:int = 536870912) -> str: +def run_os_command(str_command: str, timeout: int = 60, max_memory:int = 274877906944) -> str: """ Runs an OS command and returns the output. This implementation uses Popen with shell=False. From b29f6b44a1bccf75ea0e0ec6f09ec23ae5b75fe3 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 07:03:10 -0300 Subject: [PATCH 11/25] New agent at run_agent_cycles every new cycle. --- src/smolagents/bp_thinkers.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/smolagents/bp_thinkers.py b/src/smolagents/bp_thinkers.py index cc56fe889..ffa0bc87a 100644 --- a/src/smolagents/bp_thinkers.py +++ b/src/smolagents/bp_thinkers.py @@ -1026,21 +1026,21 @@ def run_agent_cycles( log_level = DEFAULT_THINKER_LOG_LEVEL, planning_interval = DEFAULT_THINKER_PLANNING_INTERVAL ): - local_agent = get_default_thinker_agent( - model=model, - system_prompt=system_prompt, - tools=tools, - add_base_tools=add_base_tools, - max_steps=max_steps, - step_callbacks=step_callbacks, - executor_type=executor_type, - log_level=log_level, - planning_interval=planning_interval - ) # save the current folder for later restoration original_folder = os.getcwd() for i in range(cycles_cnt): print("Running agent cycle:", i) # restore the original folder os.chdir(original_folder) + local_agent = get_default_thinker_agent( + model=model, + system_prompt=system_prompt, + tools=tools, + add_base_tools=add_base_tools, + max_steps=max_steps, + step_callbacks=step_callbacks, + executor_type=executor_type, + log_level=log_level, + planning_interval=planning_interval + ) local_agent.run(task_str, reset=True) \ No newline at end of file From cc788a14594980dee3fc327863fedb2f7011b2c2 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 08:01:58 -0300 Subject: [PATCH 12/25] Add run-agent-cycles script for Pascal task management and compilation --- bp-examples/scripts/run-agent-cycles.py | 169 ++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 bp-examples/scripts/run-agent-cycles.py diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py new file mode 100644 index 000000000..998804892 --- /dev/null +++ b/bp-examples/scripts/run-agent-cycles.py @@ -0,0 +1,169 @@ +KEY_VALUE = '' +MODEL_ID = "Claude-Haiku-4.5" +API_ENDPOINT="https://api.poe.com/v1" + +MODEL_ID = "Claude-Haiku-4.5" +API_ENDPOINT="https://api.poe.com/v1" +CYCLES_CNT = 1 + +POSTPEND_GEMINI_FLASH_VIA_POE = '' +POSTPEND_GEMINI_FLASH_VIA_GOOGLE = '' +POSTPEND_CLAUDE = '' +POSTPEND_STRING = POSTPEND_GEMINI_FLASH_VIA_POE +GLOBAL_EXECUTOR = 'exec' +MAX_TOKENS = 64000 + +import smolagents +from smolagents.bp_tools import * +from smolagents.bp_utils import * +from smolagents.bp_thinkers import * +from smolagents import OpenAIServerModel + +# Using OpenAI protocol +model = OpenAIServerModel(MODEL_ID, api_key=KEY_VALUE, max_tokens=MAX_TOKENS, api_base=API_ENDPOINT) +model.postpend_string = POSTPEND_STRING + +model.verbose = False +additional_authorized_imports=['*'] + +computing_language = "free pascal (fpc)" +what_to_code = "task manager" +fileext='.pas' + +has_pascal_message = """ When compiling pascal code, use this example: +run_os_command('fpc solution1.pas -obin/task_manager -O1 -Mobjfpc') +Notice in the example above that there is no space after "-o" for the output file parameter. +With fpc, do not use -Fc nor -o/dev/null or similar. +Do not code any user input such as ReadLn. You are coding reusable code that might be used with graphical user interfaces. +You will replace fixed sized arrays by dynamic arrays. +All pascal reserved words will be typed in lowercase. +Do not change the current working folder. +When you are asked to compare solutions, compile each version/solution. Only select solutions that do compile. +When compiling code, generate your binaries at the bin/ folder. Do not mix source code with binary (compiled) files. +When testing, review the source code and test if it compiles. Verify for the risk of any infinite loop or memory leak. +Only try to run code after verifying for infinite loop, memory leak and compilation errors. +Feel free to search the internet with error messages if you need. +This is an example how to code and compile a pascal program: + + +program mytask; +{$mode objfpc} // Use Object Pascal mode for dynamic arrays and objects + +uses + SysUtils, + DateUtils, + mytask; // your unit + +begin + WriteLn('Hello!'); +end. + + +print("Attempting to compile solutionx.pas...") +compile_output = run_os_command('fpc solutionx.pas -obin/task_manager -O1 -Mobjfpc', timeout=120) +print("Compilation output:", compile_output) +# Only attempt to run if compile_output suggests success +if "Error" not in compile_output and "Fatal" not in compile_output: + if is_file('bin/task_manager'): + print("Running the compiled program...") + print(run_os_command('bin/task_manager', timeout=120)) + else: + print("Executable not found.") +else: + print("Compilation failed.") + import re + error_lines = re.findall(r'solutionx\\.pas\\((\\d+),\\d+\\).*', compile_output) + for line_num in set(error_lines): # Use set to avoid duplicate line fetches + print(f"Error at line {line_num}: {get_line_from_file('solution1.pas', int(line_num))}") + + + +Each time that you have an error such as "solutionx.pas(206,14) Fatal: Syntax error, "identifier" expected but "is" found", +you will call something like this: get_line_from_file('solutionx.pas',206) +REMEMBER: +* "```pascal" will not save a pascal file into disk. Use savetofile tags instead. +* AVOID duplicate files. +* AVOID duplicate code. +* REMOVE duplicate files. +* REMOVE duplicate code. +* DO NOT declare variables within a begin/end block. ALWAYS declare variables in the declaration area. +* DO NOT use label/go to. +* DO NOT declare anything that starts with a digit such as: + var 1stVariable: integer; +* DO NOT use the type `real` for real numbers as it depends on hardware. Use `double` or `single` instead. +* CREATE A TYPE for dynamic array function results. + This declaration will fail: `function solve(anp: integer; var acostmatrix: array of tRealArray): array of tAppointmentResult;`. + Do this instead: ``` + type + TApptResultDynArr = array of tAppointmentResult; + ... + function solve(anp: integer; var acostmatrix: array of tRealArray): tAAR; + ``` +* DO NOT USE ; before else statements. Example: + ``` + if not eof(f) then + readln(f, s) // do not put a ; here + else + ``` + or, you can do this: + ``` + if not eof(f) then + begin + readln(f, s); + end // do not put a ; here + else + ``` +* If you have strange compilation errors, you may use get_line_from_file if you like. +* Include in your uses the unit math as the unit math contains many useful constants and functions (such as MaxDouble). +* When passing arrays as parameter, consider passing as reference to avoid memory copying. +* Create a method called self_test. In this method, you will code static inputs for testing (there will be no externally entered data to test with - do not use ReadLn for testing). +* BE BOLD AND CODE AS MANY FEATURES AS YOU CAN! +* If any of your questions is not answered, assume your best guess. Do not keep asking or repeat questions. Just follow your best guess. +* The bin folder has already been created. +* Your goal is pascal coding. Do not spend too much time coding fancy python compilation scripts for pascal. +""" + +task = """Using only the """+computing_language+""" computing language, code a """+what_to_code+" ." +task += has_pascal_message + """ +Feel free to search the internet with error messages if you need. +As you are super-intelligent, I do trust you. +YOU ARE THE BRAIN OF AN AGENT INSIDE OF THE FANTASTIC BEYOND PYTHON SMOLAGENTS: https://github.com/joaopauloschuler/beyond-python-smolagents . Enjoy! +As you are the brain of an agent, this is why you are required to respond with "final_answer" at each conclusive reply from you. +""" + +task_str = """Hello super-intelligence! +Your task is a task inside of a main software development effort. The main effort is described in the tags : + +"""+task+""" + +Your task is is enclosed in the tags : + +Inside the solution1 folder, code a task manager in plain pascal. +If the folder is empty, start from scratch please. Otherwise, add new features. +Each pascal file should not exceed 500 lines. This is done to save the context size of AI when working on this project. + +To make pascal files to respect the line count limit size, you can inherit classes. + +You can create as many pascal files you would like as long as you respect the line count limit. + +Please feel free to be bold and show your your creativity when adding new features. + +At each point that you get the source code compiling and tested ok, please commit your work. + +Before commiting code with "git commit", please run "git status" and check if what you are commiting is compatible with your expectations. + +Only commit code that is compiling and tested ok. + +NEVER EVER COMMIT CODE THAT IS NOT COMPILING. +NEVER EVER COMMIT BINARY FILES. + +Please create md files that explain the project as you progress. + +Before starting, load the folder contents with: + +print(list_directory_tree(folder_path = 'solution1', add_function_signatures = True)) + + +May the force be with you. I do trust your judgement.""" + +run_agent_cycles(model=model, task_str=task_str, cycles_cnt=CYCLES_CNT, planning_interval=22) \ No newline at end of file From 61fc2e6677b3020c41b4f07631ffac2b9a0fc155 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:05:26 +0000 Subject: [PATCH 13/25] Initial plan From 6ce40a92f8f108008e9250e8afb7614a002e2e52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:09:43 +0000 Subject: [PATCH 14/25] Add user input prompts for API key and optional parameters Co-authored-by: joaopauloschuler <43456488+joaopauloschuler@users.noreply.github.com> --- bp-examples/scripts/run-agent-cycles.py | 29 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index 998804892..b89aa1bdf 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -1,10 +1,25 @@ -KEY_VALUE = '' -MODEL_ID = "Claude-Haiku-4.5" -API_ENDPOINT="https://api.poe.com/v1" - -MODEL_ID = "Claude-Haiku-4.5" -API_ENDPOINT="https://api.poe.com/v1" -CYCLES_CNT = 1 +# Get mandatory KEY_VALUE from user +KEY_VALUE = input("Enter your API key (mandatory): ").strip() +while not KEY_VALUE: + print("API key is required!") + KEY_VALUE = input("Enter your API key (mandatory): ").strip() + +# Get optional parameters with defaults +api_endpoint_input = input("Enter API endpoint (press Enter for default: https://api.poe.com/v1): ").strip() +API_ENDPOINT = api_endpoint_input if api_endpoint_input else "https://api.poe.com/v1" + +model_id_input = input("Enter model ID (press Enter for default: Claude-Haiku-4.5): ").strip() +MODEL_ID = model_id_input if model_id_input else "Claude-Haiku-4.5" + +cycles_cnt_input = input("Enter number of cycles (press Enter for default: 1): ").strip() +if cycles_cnt_input: + try: + CYCLES_CNT = int(cycles_cnt_input) + except ValueError: + print(f"Invalid number '{cycles_cnt_input}', using default: 1") + CYCLES_CNT = 1 +else: + CYCLES_CNT = 1 POSTPEND_GEMINI_FLASH_VIA_POE = '' POSTPEND_GEMINI_FLASH_VIA_GOOGLE = '' From 4974ff32f036e4014dbb6abf865485f2b700b99d Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 08:40:56 -0300 Subject: [PATCH 15/25] Add guidelines to prevent changing the working directory and file creation location --- bp-examples/scripts/run-agent-cycles.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index 998804892..cade48650 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -156,6 +156,8 @@ NEVER EVER COMMIT CODE THAT IS NOT COMPILING. NEVER EVER COMMIT BINARY FILES. +NEVER CHANGE THE WORKING DIRECTORY. CHANGING THE WORKING DIRECTORY MAY CAUSE UNEXPECTED BEHAVIOR. +ALL FILES MUST BE CREATED INSIDE OF THE solution1 FOLDER. Please create md files that explain the project as you progress. From 109362e5b461069722ba04a2dd62c2b2c0df8797 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 08:43:44 -0300 Subject: [PATCH 16/25] Refactor run_agent_cycles call to use constants for planning interval and max steps --- bp-examples/scripts/run-agent-cycles.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index cade48650..74a0507cb 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -5,6 +5,8 @@ MODEL_ID = "Claude-Haiku-4.5" API_ENDPOINT="https://api.poe.com/v1" CYCLES_CNT = 1 +MAX_STEPS_PER_CYCLE = 50 +PLANNING_INTERVAL = 22 POSTPEND_GEMINI_FLASH_VIA_POE = '' POSTPEND_GEMINI_FLASH_VIA_GOOGLE = '' @@ -168,4 +170,8 @@ May the force be with you. I do trust your judgement.""" -run_agent_cycles(model=model, task_str=task_str, cycles_cnt=CYCLES_CNT, planning_interval=22) \ No newline at end of file +run_agent_cycles(model=model, + task_str=task_str, + cycles_cnt=CYCLES_CNT, + planning_interval=PLANNING_INTERVAL, + max_steps=MAX_STEPS_PER_CYCLE) \ No newline at end of file From 11b669ac92e978d2ff311bdfce7752e67f3f6daf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:55:22 +0000 Subject: [PATCH 17/25] Initial plan From 831c4d035162751a203bcebe57fb4540f759eeab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:59:11 +0000 Subject: [PATCH 18/25] Add optional questions for MAX_STEPS_PER_CYCLE and PLANNING_INTERVAL Co-authored-by: joaopauloschuler <43456488+joaopauloschuler@users.noreply.github.com> --- bp-examples/scripts/run-agent-cycles.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index dff45baf1..510fd34ed 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -20,8 +20,26 @@ CYCLES_CNT = 1 else: CYCLES_CNT = 1 -MAX_STEPS_PER_CYCLE = 50 -PLANNING_INTERVAL = 22 + +max_steps_input = input("Enter max steps per cycle (press Enter for default: 50): ").strip() +if max_steps_input: + try: + MAX_STEPS_PER_CYCLE = int(max_steps_input) + except ValueError: + print(f"Invalid number '{max_steps_input}', using default: 50") + MAX_STEPS_PER_CYCLE = 50 +else: + MAX_STEPS_PER_CYCLE = 50 + +planning_interval_input = input("Enter planning interval (press Enter for default: 22): ").strip() +if planning_interval_input: + try: + PLANNING_INTERVAL = int(planning_interval_input) + except ValueError: + print(f"Invalid number '{planning_interval_input}', using default: 22") + PLANNING_INTERVAL = 22 +else: + PLANNING_INTERVAL = 22 POSTPEND_GEMINI_FLASH_VIA_POE = '' POSTPEND_GEMINI_FLASH_VIA_GOOGLE = '' From ab253be85fd0c234d7b8c5b42546e4f125f51806 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 09:25:23 -0300 Subject: [PATCH 19/25] Add guidelines for committing code and project documentation --- bp-examples/scripts/run-agent-cycles.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index 510fd34ed..365420f73 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -193,6 +193,10 @@ NEVER EVER COMMIT BINARY FILES. NEVER CHANGE THE WORKING DIRECTORY. CHANGING THE WORKING DIRECTORY MAY CAUSE UNEXPECTED BEHAVIOR. ALL FILES MUST BE CREATED INSIDE OF THE solution1 FOLDER. +AFTER EACH PARTIAL COMMIT, CALL THE FOLLOWING: + +final_answer("I have just committed code that is compiling and tested ok. Moving to the next part of the project.") + Please create md files that explain the project as you progress. From 54ec743c5a10b4eabf1d20095cef0841060227a3 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 11:42:58 -0300 Subject: [PATCH 20/25] Refactor run-agent-cycles script: rename task_str to DEFAULT_TASK for clarity and update its usage --- bp-examples/scripts/run-agent-cycles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index 365420f73..bbaab1f40 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -166,7 +166,7 @@ As you are the brain of an agent, this is why you are required to respond with "final_answer" at each conclusive reply from you. """ -task_str = """Hello super-intelligence! +DEFAULT_TASK = """Hello super-intelligence! Your task is a task inside of a main software development effort. The main effort is described in the tags : """+task+""" @@ -208,7 +208,7 @@ May the force be with you. I do trust your judgement.""" run_agent_cycles(model=model, - task_str=task_str, + task_str=DEFAULT_TASK, cycles_cnt=CYCLES_CNT, planning_interval=PLANNING_INTERVAL, max_steps=MAX_STEPS_PER_CYCLE) \ No newline at end of file From 28d2428aae124036e6b78c316891e29709bc0cca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:45:39 +0000 Subject: [PATCH 21/25] Initial plan From 3dd4cae1d5e64d1f2176aa37e85d8bb394b447c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:52:30 +0000 Subject: [PATCH 22/25] Add optional task description file input to run-agent-cycles.py Co-authored-by: joaopauloschuler <43456488+joaopauloschuler@users.noreply.github.com> --- bp-examples/scripts/run-agent-cycles.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index bbaab1f40..e7565fb03 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -41,6 +41,8 @@ else: PLANNING_INTERVAL = 22 +task_file_input = input("Enter task description file path (press Enter to use default task): ").strip() + POSTPEND_GEMINI_FLASH_VIA_POE = '' POSTPEND_GEMINI_FLASH_VIA_GOOGLE = '' POSTPEND_CLAUDE = '' @@ -207,8 +209,20 @@ May the force be with you. I do trust your judgement.""" +# Load task from file if provided, otherwise use DEFAULT_TASK +task_description = DEFAULT_TASK +if task_file_input: + try: + with open(task_file_input, 'r', encoding='utf-8') as f: + task_description = f.read() + print(f"Task description loaded from: {task_file_input}") + except FileNotFoundError: + print(f"Warning: File '{task_file_input}' not found. Using default task.") + except Exception as e: + print(f"Warning: Error reading file '{task_file_input}': {e}. Using default task.") + run_agent_cycles(model=model, - task_str=DEFAULT_TASK, + task_str=task_description, cycles_cnt=CYCLES_CNT, planning_interval=PLANNING_INTERVAL, - max_steps=MAX_STEPS_PER_CYCLE) \ No newline at end of file + max_steps=MAX_STEPS_PER_CYCLE) From 35f66cf786843196611568f9a6fd9e7b7a8da018 Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Mon, 24 Nov 2025 12:11:58 -0300 Subject: [PATCH 23/25] Refactor content truncation: replace hardcoded value with MAX_LENGTH_TRUNCATE_CONTENT constant --- src/smolagents/agents.py | 3 ++- src/smolagents/models.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/smolagents/agents.py b/src/smolagents/agents.py index 1bcf4c5aa..0df923919 100644 --- a/src/smolagents/agents.py +++ b/src/smolagents/agents.py @@ -33,6 +33,7 @@ from .bp_tools import get_file_size, force_directories, remove_after_markers from .bp_utils import bp_parse_code_blobs, fix_nested_tags from .bp_utils import is_valid_python_code +from. utils import MAX_LENGTH_TRUNCATE_CONTENT import yaml from huggingface_hub import create_repo, metadata_update, snapshot_download, upload_folder @@ -1986,7 +1987,7 @@ def _step_stream( observation = '' try: code_output = self.python_executor(code_action) - code_output.logs = truncate_content(code_output.logs, 20000) # execution log is truncated to 20K + code_output.logs = truncate_content(code_output.logs, MAX_LENGTH_TRUNCATE_CONTENT) # execution log is truncated to 20K execution_outputs_console = [] if len(code_output.logs) > 0: execution_outputs_console += [ diff --git a/src/smolagents/models.py b/src/smolagents/models.py index 0da182b5a..026120f46 100644 --- a/src/smolagents/models.py +++ b/src/smolagents/models.py @@ -26,7 +26,7 @@ from .monitoring import TokenUsage from .tools import Tool -from .utils import RateLimiter, _is_package_available, encode_image_base64, make_image_url, parse_json_blob +from .utils import RateLimiter, _is_package_available, encode_image_base64, make_image_url, parse_json_blob, MAX_LENGTH_TRUNCATE_CONTENT if TYPE_CHECKING: @@ -433,7 +433,7 @@ def __init__( self.kwargs = kwargs self.model_id: str | None = model_id self.postpend_string = '' - self.max_len_truncate_content = 20000 + self.max_len_truncate_content = MAX_LENGTH_TRUNCATE_CONTENT def _prepare_completion_kwargs( self, From e9b113b1d5885cd0f7f2c586bd4fc9920d23e58a Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Tue, 25 Nov 2025 08:03:13 -0300 Subject: [PATCH 24/25] Load/save up to 500 lines in example. --- bp-examples/scripts/run-agent-cycles.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/bp-examples/scripts/run-agent-cycles.py b/bp-examples/scripts/run-agent-cycles.py index e7565fb03..7c807c864 100644 --- a/bp-examples/scripts/run-agent-cycles.py +++ b/bp-examples/scripts/run-agent-cycles.py @@ -21,15 +21,15 @@ else: CYCLES_CNT = 1 -max_steps_input = input("Enter max steps per cycle (press Enter for default: 50): ").strip() +max_steps_input = input("Enter max steps per cycle (press Enter for default: 100): ").strip() if max_steps_input: try: MAX_STEPS_PER_CYCLE = int(max_steps_input) except ValueError: - print(f"Invalid number '{max_steps_input}', using default: 50") - MAX_STEPS_PER_CYCLE = 50 + print(f"Invalid number '{max_steps_input}', using default: 100") + MAX_STEPS_PER_CYCLE = 100 else: - MAX_STEPS_PER_CYCLE = 50 + MAX_STEPS_PER_CYCLE = 100 planning_interval_input = input("Enter planning interval (press Enter for default: 22): ").strip() if planning_interval_input: @@ -177,11 +177,7 @@ Inside the solution1 folder, code a task manager in plain pascal. If the folder is empty, start from scratch please. Otherwise, add new features. -Each pascal file should not exceed 500 lines. This is done to save the context size of AI when working on this project. - -To make pascal files to respect the line count limit size, you can inherit classes. - -You can create as many pascal files you would like as long as you respect the line count limit. +When loading source code for verifying existing code, never load more than 500 lines. When saving, never save more than 500 lines. Try to save only what is changing. This is done to save the context size of AI when working on this project. Please feel free to be bold and show your your creativity when adding new features. From 4aa31adf6084a08579fc1ad070ed282fa0396b1b Mon Sep 17 00:00:00 2001 From: Joao Paulo Schwarz Schuler Date: Tue, 25 Nov 2025 11:50:00 -0300 Subject: [PATCH 25/25] Add error handling to agent cycle execution in run_agent_cycles function --- src/smolagents/bp_thinkers.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/smolagents/bp_thinkers.py b/src/smolagents/bp_thinkers.py index ffa0bc87a..12dccf814 100644 --- a/src/smolagents/bp_thinkers.py +++ b/src/smolagents/bp_thinkers.py @@ -1029,18 +1029,21 @@ def run_agent_cycles( # save the current folder for later restoration original_folder = os.getcwd() for i in range(cycles_cnt): - print("Running agent cycle:", i) - # restore the original folder - os.chdir(original_folder) - local_agent = get_default_thinker_agent( - model=model, - system_prompt=system_prompt, - tools=tools, - add_base_tools=add_base_tools, - max_steps=max_steps, - step_callbacks=step_callbacks, - executor_type=executor_type, - log_level=log_level, - planning_interval=planning_interval - ) - local_agent.run(task_str, reset=True) \ No newline at end of file + try: + print("Running agent cycle:", i) + # restore the original folder + os.chdir(original_folder) + local_agent = get_default_thinker_agent( + model=model, + system_prompt=system_prompt, + tools=tools, + add_base_tools=add_base_tools, + max_steps=max_steps, + step_callbacks=step_callbacks, + executor_type=executor_type, + log_level=log_level, + planning_interval=planning_interval + ) + local_agent.run(task_str, reset=True) + except Exception as e: + print(f"Exception: {e}", "at cycle", i) \ No newline at end of file