diff --git a/docs/guides/advanced-terminal-usage.mdx b/docs/guides/advanced-terminal-usage.mdx index d8baad05db..0a46dea8e1 100644 --- a/docs/guides/advanced-terminal-usage.mdx +++ b/docs/guides/advanced-terminal-usage.mdx @@ -13,4 +13,5 @@ Magic commands can be used to control the interpreter's behavior in interactive - `%tokens [prompt]`: EXPERIMENTAL: Calculate the tokens used by the next request based on the current conversation's messages and estimate the cost of that request; optionally provide a prompt to also calculate the tokens used by that prompt and the total amount of tokens that will be sent with the next request. - `%info`: Show system and interpreter information. - `%help`: Show this help message. -- `%jupyter`: Export the current session to a Jupyter notebook file (.ipynb) to the Downloads folder. \ No newline at end of file +- `%jupyter`: Export the current session to a Jupyter notebook file (.ipynb) to the Downloads folder. +- `%markdown [path]`: Export the conversation to a specified Markdown path. If no path is provided, it will be saved to the Downloads folder with a generated conversation name. \ No newline at end of file diff --git a/docs/usage/terminal/magic-commands.mdx b/docs/usage/terminal/magic-commands.mdx index 31fb9ab695..98a7fd7b16 100644 --- a/docs/usage/terminal/magic-commands.mdx +++ b/docs/usage/terminal/magic-commands.mdx @@ -13,3 +13,4 @@ Magic commands can be used to control the interpreter's behavior in interactive - `%tokens [prompt]`: EXPERIMENTAL: Calculate the tokens used by the next request based on the current conversation's messages and estimate the cost of that request; optionally provide a prompt to also calculate the tokens used by that prompt and the total amount of tokens that will be sent with the next request. - `%info`: Show system and interpreter information. - `%help`: Show this help message. +- `%markdown [path]`: Export the conversation to a specified Markdown path. If no path is provided, it will be saved to the Downloads folder with a generated conversation name. diff --git a/interpreter/terminal_interface/magic_commands.py b/interpreter/terminal_interface/magic_commands.py index 2801344110..fc1c6a9cae 100644 --- a/interpreter/terminal_interface/magic_commands.py +++ b/interpreter/terminal_interface/magic_commands.py @@ -8,6 +8,7 @@ from ..core.utils.system_debug_info import system_info from .utils.count_tokens import count_messages_tokens from .utils.display_markdown_message import display_markdown_message +from .utils.export_to_markdown import export_to_markdown def handle_undo(self, arguments): @@ -58,6 +59,7 @@ def handle_help(self, arguments): "%help": "Show this help message.", "%info": "Show system and interpreter information", "%jupyter": "Export the conversation to a Jupyter notebook file", + "%markdown [path]": "Export the conversation to a specified Markdown path. If no path is provided, it will be saved to the Downloads folder with a generated conversation name.", } base_message = ["> **Available Commands:**\n\n"] @@ -220,6 +222,9 @@ def get_downloads_path(): else: # For MacOS and Linux downloads = os.path.join(os.path.expanduser("~"), "Downloads") + # For some GNU/Linux distros, there's no '~/Downloads' dir by default + if not os.path.exists(downloads): + os.makedirs(downloads) return downloads @@ -295,6 +300,19 @@ def jupyter(self, arguments): ) +def markdown(self, export_path: str): + # If it's an empty conversations + if len(self.messages) == 0: + print("No messages to export.") + return + + # If user doesn't specify the export path, then save the exported PDF in '~/Downloads' + if not export_path: + export_path = get_downloads_path() + f"/{self.conversation_filename[:-4]}md" + + export_to_markdown(self.messages, export_path) + + def handle_magic_command(self, user_input): # Handle shell if user_input.startswith("%%"): @@ -316,6 +334,7 @@ def handle_magic_command(self, user_input): "tokens": handle_count_tokens, "info": handle_info, "jupyter": jupyter, + "markdown": markdown, } user_input = user_input[1:].strip() # Capture the part after the `%` diff --git a/interpreter/terminal_interface/utils/export_to_markdown.py b/interpreter/terminal_interface/utils/export_to_markdown.py new file mode 100644 index 0000000000..638e39db95 --- /dev/null +++ b/interpreter/terminal_interface/utils/export_to_markdown.py @@ -0,0 +1,37 @@ +def export_to_markdown(messages: list[dict], export_path: str): + markdown = messages_to_markdown(messages) + with open(export_path, 'w') as f: + f.write(markdown) + print(f"Exported current conversation to {export_path}") + + +def messages_to_markdown(messages: list[dict]) -> str: + # Convert interpreter.messages to Markdown text + markdown_content = "" + previous_role = None + for chunk in messages: + current_role = chunk["role"] + if current_role == previous_role: + rendered_chunk = "" + else: + rendered_chunk = f"## {current_role}\n\n" + previous_role = current_role + + # User query message + if chunk["role"] == "user": + rendered_chunk += chunk["content"] + "\n\n" + markdown_content += rendered_chunk + continue + + # Message + if chunk["type"] == "message": + rendered_chunk += chunk["content"] + "\n\n" + + # Code + if chunk["type"] == "code" or chunk["type"] == "console": + code_format = chunk.get("format", "") + rendered_chunk += f"```{code_format}\n{chunk['content']}\n```\n\n" + + markdown_content += rendered_chunk + + return markdown_content