-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat: Dynamic File Editing #3063
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @Saedbhati , left some comments below, we also need to add test code for new feature, one PR here for some quick enhancement: #3088
camel/toolkits/file_write_toolkit.py
Outdated
except ImportError: | ||
return ( | ||
"Error reading YAML file: PyYAML package not installed. " | ||
"Please install with: pip install PyYAML" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use decorator would be more tidy and unified: @dependencies_required('yaml')
, same for other parts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got error below when i run the example code, I think in previous tasks we didn't mention that the created python file should be named as simple_flask_server.py
2025-08-31 02:38:07,434 - camel.camel.agents.chat_agent - WARNING - Error executing tool 'read_file': Execution of function read_file failed with arguments () and {'file_path': 'simple_flask_server.py'}. Error: [Errno 2] No such file or directory: '/Users/enrei/Desktop/repos/camel0829/camel/file_write_outputs/simple_flask_server.py' with result: Tool execution failed: Error executing tool 'read_file': Execution of function read_file failed with arguments () and {'file_path': 'simple_flask_server.py'}. Error: [Errno 2] No such file or directory: '/Users/enrei/Desktop/repos/camel0829/camel/file_write_outputs/simple_flask_server.py'
camel/toolkits/file_write_toolkit.py
Outdated
@@ -1072,4 +1445,7 @@ def get_tools(self) -> List[FunctionTool]: | |||
""" | |||
return [ | |||
FunctionTool(self.write_to_file), | |||
FunctionTool(self.read_file), | |||
FunctionTool(self.edit_file), | |||
FunctionTool(self.insert_at_line), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tried run this function, got
2025-08-31 02:52:04,460 - camel.camel.toolkits.file_write_toolkit - ERROR - Error inserting content in file /Users/enrei/Desktop/repos/camel0829/camel/examples/toolkits/mock_test_file.pdf: 'utf-8' codec can't decode byte 0x93 in position 10: invalid start byte
with test file: https://chatgpt.com/share/68b3489b-3ed4-8013-86e3-ad255bd018d9
and task: Please insert a new route '/contact' that returns 'Contact us at contact@example.com' to the /Users/enrei/Desktop/repos/camel0829/camel/examples/toolkits/mock_test_file.pdf file, you must use insert_at_line function
seems now we don't support binary files that can't be decoded as text?
camel/toolkits/file_write_toolkit.py
Outdated
logger.error(error_msg) | ||
return error_msg | ||
|
||
def insert_at_line( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the design of this function is confused
- why we need
insert_at_line
as we already haveedit_file
, what case is this function designed for, we need to specify this in docstring for the agent to understand better - how could the agent know which line the content should insert? there's no function or design to provide the line number to the agent
camel/toolkits/file_write_toolkit.py
Outdated
if extension in [".doc", ".docx"]: | ||
self._edit_docx_file(resolved_path, old_content, new_content) | ||
elif extension == ".json": | ||
self._edit_json_file(resolved_path, old_content, new_content) | ||
elif extension in [".yaml", ".yml"]: | ||
self._edit_yaml_file(resolved_path, old_content, new_content) | ||
else: | ||
# For text files, use simple text replacement | ||
return self._perform_text_replacement( | ||
resolved_path, old_content, new_content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the supported file format is limited, at least we also need support types like .pdf as we supported in write_to_file
camel/toolkits/file_write_toolkit.py
Outdated
except ImportError as e: | ||
raise ImportError( | ||
"The python-docx package is required for DOCX file operations." | ||
"Please install it with: pip install camel-ai[document_tools]" | ||
) from e |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not necessary, we could require the dependency to remove unnecessary except case to make code more tidy, same for other parts
camel/toolkits/file_write_toolkit.py
Outdated
except ImportError: | ||
try: | ||
import pypdf | ||
|
||
with open(file_path, 'rb') as file: | ||
pdf_reader = pypdf.PdfReader(file) # type: ignore[assignment] | ||
text = "" | ||
for page in pdf_reader.pages: | ||
text += page.extract_text() + "\n" | ||
return text | ||
except ImportError as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not necessary, we could require the dependency to remove unnecessary except case to make code more tidy, same for other parts
camel/toolkits/file_write_toolkit.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we update the class naming and the description since now we not only support file write, but also file editing?
@@ -1072,4 +1445,7 @@ def get_tools(self) -> List[FunctionTool]: | |||
""" | |||
return [ | |||
FunctionTool(self.write_to_file), | |||
FunctionTool(self.read_file), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for read_file
, we can use loaders like markitdown which has better performance, maybe later support user config the BaseLoader (after #2819 merged) when they initialize FileWriteToolkit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @Saedbhati great integration!
I included some comments on function design that may be helpful to consider, otherwise awesome work!
camel/toolkits/file_write_toolkit.py
Outdated
# Simple string replacement | ||
json_str = json.dumps(data, ensure_ascii=False) | ||
if old_content in json_str: | ||
updated_str = json_str.replace(old_content, new_content) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using str.replace()
can cause some issues since we are altering json's
-
Replaces ALL occurrences of the search text, so
{"message": "Hello", "user_message": "Hi"}
becomes{"greeting": "Hello", "user_greeting": "Hi"}
when searching for"message"
-
When
str.replace()
modifies JSON content, it can break the fundamental syntax rules. For example, if you accidentally replace a quote mark, bracket, or comma, the resulting string may no longer be valid JSON. -
Replacing " with ' in
{"name": "John"}
creates{'name': 'John'}
(invalid JSON - single quotes not allowed) -
Replacing , with ; in
{"a": 1, "b": 2}
creates{"a": 1; "b": 2}
(invalid JSON - semicolons not valid separators) -
Replacing { with [ in
{"data": {"nested": "value"}}
creates["data": {"nested": "value"}}
(invalid JSON - mismatched brackets)
camel/toolkits/file_write_toolkit.py
Outdated
# Replace entire document content | ||
for paragraph in doc.paragraphs[:]: | ||
p = paragraph._element | ||
p.getparent().remove(p) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p.getparent().remove(p)
removes paragraph element from XML tree which will delete any font information, styling, alignment, indentation. This will result in new text additions being added with the default font settings, which may cause text replacements to look strange in the existing file
@Saedbhati thanks for your PR, may I know what issues you are facing? In what conditions would you think that its ready for production. Perhaps we can open an issue for contribution. |
@a7m-1st i think there are better approach than this like patching or using regex to replace text. Most AI code editor use Patching method |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test code need to be updated
hey @Saedbhati @a7m-1st , I think adding optional regex/patch modes would make it more powerful for advanced scenarios, let's create another issue and PR for this |
thanks @Saedbhati , @a7m-1st @waleedalzarooni ! added enhance PR: #3109 |
Description
Describe your changes in detail (optional if the linked issue already contains a detailed description of the changes).
Fixes #2981
Checklist
Go over all the following points, and put an
x
in all the boxes that apply.Fixes #issue-number
in the PR description (required)pyproject.toml
anduv lock
If you are unsure about any of these, don't hesitate to ask. We are here to help!