freeplane-and-yaml 0.1.3__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. freeplane-and-yaml-0.3.0/MANIFEST.in +4 -0
  2. freeplane-and-yaml-0.3.0/PKG-INFO +99 -0
  3. freeplane-and-yaml-0.3.0/README.md +81 -0
  4. {freeplane-and-yaml-0.1.3 → freeplane-and-yaml-0.3.0}/setup.py +11 -5
  5. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/__init__.py +10 -0
  6. {freeplane-and-yaml-0.1.3 → freeplane-and-yaml-0.3.0}/src/freeplane_and_yaml/cli.py +5 -2
  7. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/convert_pdf_to_mindmap.py +75 -0
  8. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/llm_adapter.py +37 -0
  9. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/openai_adapter.py +110 -0
  10. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/pdf2mindmap.py +85 -0
  11. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/schema/mindmap-schema.json +73 -0
  12. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/text_to_mindmap.py +54 -0
  13. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml/yaml_utils.py +26 -0
  14. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml.egg-info/PKG-INFO +99 -0
  15. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml.egg-info/SOURCES.txt +21 -0
  16. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml.egg-info/entry_points.txt +3 -0
  17. freeplane-and-yaml-0.3.0/src/freeplane_and_yaml.egg-info/requires.txt +4 -0
  18. freeplane-and-yaml-0.3.0/src/schema/mindmap-schema.json +73 -0
  19. freeplane-and-yaml-0.1.3/MANIFEST.in +0 -5
  20. freeplane-and-yaml-0.1.3/PKG-INFO +0 -202
  21. freeplane-and-yaml-0.1.3/README.md +0 -184
  22. freeplane-and-yaml-0.1.3/src/freeplane_and_yaml/__init__.py +0 -0
  23. freeplane-and-yaml-0.1.3/src/freeplane_and_yaml.egg-info/PKG-INFO +0 -202
  24. freeplane-and-yaml-0.1.3/src/freeplane_and_yaml.egg-info/SOURCES.txt +0 -13
  25. freeplane-and-yaml-0.1.3/src/freeplane_and_yaml.egg-info/entry_points.txt +0 -3
  26. freeplane-and-yaml-0.1.3/src/freeplane_and_yaml.egg-info/requires.txt +0 -1
  27. {freeplane-and-yaml-0.1.3 → freeplane-and-yaml-0.3.0}/LICENSE +0 -0
  28. {freeplane-and-yaml-0.1.3 → freeplane-and-yaml-0.3.0}/setup.cfg +0 -0
  29. {freeplane-and-yaml-0.1.3 → freeplane-and-yaml-0.3.0}/src/freeplane_and_yaml/convert.py +0 -0
  30. {freeplane-and-yaml-0.1.3 → freeplane-and-yaml-0.3.0}/src/freeplane_and_yaml.egg-info/dependency_links.txt +0 -0
  31. {freeplane-and-yaml-0.1.3 → freeplane-and-yaml-0.3.0}/src/freeplane_and_yaml.egg-info/top_level.txt +0 -0
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ include src/freeplane_and_yaml/schema/mindmap-schema.json
4
+
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.1
2
+ Name: freeplane-and-yaml
3
+ Version: 0.3.0
4
+ Summary: A tool to convert PDF files to Freeplane mind maps using OpenAI
5
+ Home-page: https://github.com/romilly/freeplane-and-yaml
6
+ Author: Romilly Cocking
7
+ Author-email: romilly.cocking@gmail.com
8
+ License: MIT
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+
17
+ # PDF to Freeplane Mind Map Converter
18
+
19
+ A Python tool that converts PDF documents into Freeplane mind maps using OpenAI. This allows you to automatically generate structured mind maps from PDF documents.
20
+
21
+ You can read about how it got written using AI on [medium](https://medium.com/@romillyc/build-your-own-mind-map-tools-with-ai-b193564f2464?sk=b353aa7d16d6412e4aae8f3eab0ec554).
22
+ That's a _friend link_ so you can read it even if you're not a subscriber.
23
+
24
+ ## Installation
25
+
26
+ This project requires Python and should be run in a virtual environment:
27
+
28
+ ```bash
29
+ # Create and activate virtual environment in the directory of your choice
30
+ python -m venv venv
31
+ source venv/bin/activate # On Windows use: venv\Scripts\activate
32
+ pip install freeplane-and-yaml
33
+ ```
34
+
35
+ You'll need to set up your OpenAI API key in a `.env` file or as an environment variable:
36
+
37
+ ```bash
38
+ # Create a .env file in your project directory
39
+ echo "OPENAI_API_KEY=your_api_key_here" > .env
40
+ ```
41
+
42
+
43
+ ## Usage
44
+
45
+ ### Converting PDF Documents to Mind Maps
46
+
47
+ To convert a PDF document to a Freeplane mind map:
48
+
49
+ ```bash
50
+ # Convert a PDF file to a mind map
51
+ pdf2mindmap path/to/document.pdf output_directory
52
+
53
+ # Use a specific OpenAI model (default is gpt-4o-mini)
54
+ pdf2mindmap path/to/document.pdf output_directory --model gpt-4-turbo
55
+
56
+ # Also save the extracted text
57
+ pdf2mindmap path/to/document.pdf output_directory --save-text
58
+ ```
59
+
60
+ The tool performs these steps:
61
+ 1. Extracts text from the PDF using PyMuPDF4LLM (preserving structure as markdown)
62
+ 2. Processes the text with OpenAI to generate a structured mind map in YAML format
63
+ 3. Converts the YAML to a Freeplane mind map (.mm file)
64
+ 4. Saves all files in the output directory
65
+
66
+ This is useful for:
67
+ - Summarizing academic papers
68
+ - Converting product requirements documents (PRDs)
69
+ - Creating structured summaries of technical documentation
70
+ - Organizing research notes or book content
71
+
72
+
73
+ The generated `.mm` file can be opened in Freeplane. When you first open the file, Freeplane will show this warning dialog because the file wasn't created by Freeplane itself:
74
+
75
+ ![Freeplane Warning Dialog](images/warning-dialog.png)
76
+
77
+ This is normal and expected — click OK to load the mind map.
78
+
79
+ Here's an example of how the output looks:
80
+
81
+ ![Example Mind Map](images/Screenshot%20at%202025-02-12%2010-43-23.png)
82
+
83
+ ## Features
84
+
85
+ - Converts PDF documents to Freeplane mind maps using OpenAI
86
+ - Uses PyMuPDF4LLM for high-quality text extraction from PDFs
87
+ - Creates hierarchical mind maps with node titles and notes
88
+ - Automatically alternates between right and left positions for top-level nodes
89
+ - Generates unique IDs for each node
90
+ - Produces clean, well-structured YAML as an intermediate format
91
+ - Supports different OpenAI models (default is gpt-4o-mini)
92
+
93
+ ## License
94
+
95
+ _Apologies to readers from the USA. This README uses UK spelling._
96
+
97
+ This project is licensed under the MIT Licence — see the [LICENCE](LICENSE) file for details.
98
+
99
+
@@ -0,0 +1,81 @@
1
+ # PDF to Freeplane Mind Map Converter
2
+
3
+ A Python tool that converts PDF documents into Freeplane mind maps using OpenAI. This allows you to automatically generate structured mind maps from PDF documents.
4
+
5
+ You can read about how it got written using AI on [medium](https://medium.com/@romillyc/build-your-own-mind-map-tools-with-ai-b193564f2464?sk=b353aa7d16d6412e4aae8f3eab0ec554).
6
+ That's a _friend link_ so you can read it even if you're not a subscriber.
7
+
8
+ ## Installation
9
+
10
+ This project requires Python and should be run in a virtual environment:
11
+
12
+ ```bash
13
+ # Create and activate virtual environment in the directory of your choice
14
+ python -m venv venv
15
+ source venv/bin/activate # On Windows use: venv\Scripts\activate
16
+ pip install freeplane-and-yaml
17
+ ```
18
+
19
+ You'll need to set up your OpenAI API key in a `.env` file or as an environment variable:
20
+
21
+ ```bash
22
+ # Create a .env file in your project directory
23
+ echo "OPENAI_API_KEY=your_api_key_here" > .env
24
+ ```
25
+
26
+
27
+ ## Usage
28
+
29
+ ### Converting PDF Documents to Mind Maps
30
+
31
+ To convert a PDF document to a Freeplane mind map:
32
+
33
+ ```bash
34
+ # Convert a PDF file to a mind map
35
+ pdf2mindmap path/to/document.pdf output_directory
36
+
37
+ # Use a specific OpenAI model (default is gpt-4o-mini)
38
+ pdf2mindmap path/to/document.pdf output_directory --model gpt-4-turbo
39
+
40
+ # Also save the extracted text
41
+ pdf2mindmap path/to/document.pdf output_directory --save-text
42
+ ```
43
+
44
+ The tool performs these steps:
45
+ 1. Extracts text from the PDF using PyMuPDF4LLM (preserving structure as markdown)
46
+ 2. Processes the text with OpenAI to generate a structured mind map in YAML format
47
+ 3. Converts the YAML to a Freeplane mind map (.mm file)
48
+ 4. Saves all files in the output directory
49
+
50
+ This is useful for:
51
+ - Summarizing academic papers
52
+ - Converting product requirements documents (PRDs)
53
+ - Creating structured summaries of technical documentation
54
+ - Organizing research notes or book content
55
+
56
+
57
+ The generated `.mm` file can be opened in Freeplane. When you first open the file, Freeplane will show this warning dialog because the file wasn't created by Freeplane itself:
58
+
59
+ ![Freeplane Warning Dialog](images/warning-dialog.png)
60
+
61
+ This is normal and expected — click OK to load the mind map.
62
+
63
+ Here's an example of how the output looks:
64
+
65
+ ![Example Mind Map](images/Screenshot%20at%202025-02-12%2010-43-23.png)
66
+
67
+ ## Features
68
+
69
+ - Converts PDF documents to Freeplane mind maps using OpenAI
70
+ - Uses PyMuPDF4LLM for high-quality text extraction from PDFs
71
+ - Creates hierarchical mind maps with node titles and notes
72
+ - Automatically alternates between right and left positions for top-level nodes
73
+ - Generates unique IDs for each node
74
+ - Produces clean, well-structured YAML as an intermediate format
75
+ - Supports different OpenAI models (default is gpt-4o-mini)
76
+
77
+ ## License
78
+
79
+ _Apologies to readers from the USA. This README uses UK spelling._
80
+
81
+ This project is licensed under the MIT Licence — see the [LICENCE](LICENSE) file for details.
@@ -2,8 +2,8 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="freeplane-and-yaml", # Your package name (it must be unique on PyPI)
5
- version="0.1.3", # Initial release version
6
- description="A tool to convert YAML files to Freeplane MM format",
5
+ version="0.3.0", # Simplified to focus on PDF-to-mindmap with OpenAI
6
+ description="A tool to convert PDF files to Freeplane mind maps using OpenAI",
7
7
  long_description=open("README.md").read(),
8
8
  long_description_content_type="text/markdown", # Use Markdown for PyPI description
9
9
  url="https://github.com/romilly/freeplane-and-yaml", # Replace with your repository URL
@@ -13,8 +13,14 @@ setup(
13
13
  packages=find_packages(where="src"), # Look for packages in src/
14
14
  package_dir={"": "src"}, # Root of packages is src/
15
15
  include_package_data=True, # Include non-Python files listed in MANIFEST.in
16
+ package_data={
17
+ "freeplane_and_yaml": ["schema/*.json"], # Include schema files
18
+ },
16
19
  install_requires=[
17
- "PyYAML", # Replace or add dependencies here
20
+ "PyYAML",
21
+ "python-dotenv",
22
+ "openai",
23
+ "pymupdf4llm",
18
24
  ],
19
25
  classifiers=[
20
26
  "Programming Language :: Python :: 3",
@@ -22,9 +28,9 @@ setup(
22
28
  "Operating System :: OS Independent",
23
29
  ],
24
30
  python_requires=">=3.9", # Specify minimum Python version
25
- entry_points={
31
+ entry_points={
26
32
  "console_scripts": [
27
- "convert=freeplane_and_yaml.cli:main",
33
+ "pdf2mindmap=freeplane_and_yaml.pdf2mindmap:main",
28
34
  ],
29
35
  },
30
36
 
@@ -0,0 +1,10 @@
1
+ from .convert import convert_yaml_file, converter
2
+ from .llm_adapter import LLMAdapter
3
+ from .text_to_mindmap import TextToMindMap
4
+
5
+ __all__ = [
6
+ "convert_yaml_file",
7
+ "converter",
8
+ "LLMAdapter",
9
+ "TextToMindMap",
10
+ ]
@@ -1,6 +1,6 @@
1
1
  import sys
2
2
  import os
3
- from .convert import convert_yaml_file # Import your conversion logic
3
+ from freeplane_and_yaml.convert import convert_yaml_file # Import your conversion logic
4
4
 
5
5
 
6
6
  def main():
@@ -31,10 +31,13 @@ def main():
31
31
  base_name = os.path.basename(input_file).replace(".yaml", ".mm")
32
32
  output_file = os.path.join(output_dir, base_name)
33
33
 
34
- # Perform the conversion
34
+ # Perform the conversio
35
35
  try:
36
36
  convert_yaml_file(input_file, output_file)
37
37
  print(f"Conversion complete: {output_file}")
38
38
  except Exception as e:
39
39
  print(f"Error during conversion: {e}")
40
40
  sys.exit(1)
41
+
42
+ if __name__ == "__main__":
43
+ main()
@@ -0,0 +1,75 @@
1
+ import os
2
+ import sys
3
+ import tempfile
4
+ import pymupdf4llm
5
+ from .text_to_mindmap import TextToMindMap
6
+ from .openai_adapter import OpenAIAdapter
7
+
8
+ def convert_pdf_to_mindmap(base_name, input_file, mm_output, model, text_output, yaml_output):
9
+ # Step 1: Extract text from the PDF
10
+ print(f"Extracting text from {input_file}...")
11
+ # Ensure the file exists
12
+ if not os.path.isfile(input_file):
13
+ raise FileNotFoundError(f"PDF file not found: {input_file}")
14
+ # Extract text using PyMuPDF4LLM directly
15
+ extracted_text = pymupdf4llm.to_markdown(input_file)
16
+ # Save the extracted text if requested
17
+ if text_output:
18
+ with open(text_output, 'w', encoding='utf-8') as f:
19
+ f.write(extracted_text)
20
+ print(f"Saved extracted text to: {text_output}")
21
+ # If the text is empty, report an error
22
+ if not extracted_text.strip():
23
+ print("Error: No text could be extracted from the PDF", file=sys.stderr)
24
+ sys.exit(1)
25
+ # Step 2: Create the OpenAI adapter
26
+ # Create the adapter with specified model or default
27
+ adapter = OpenAIAdapter(model=model)
28
+ # Step 3: Create a temporary text file for the extracted content
29
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as temp_file:
30
+ temp_file.write(extracted_text)
31
+ temp_file_path = temp_file.name
32
+ try:
33
+ # Step 4: Process the text with the text-to-mindmap service
34
+ print("Converting extracted text to mind map format...")
35
+ service = TextToMindMap(adapter)
36
+
37
+ # First try to get the YAML directly to inspect it
38
+ with open(temp_file_path, 'r', encoding='utf-8') as f:
39
+ text_content = f.read()
40
+
41
+ # Limit the text size to avoid timeouts
42
+ MAX_CHARS = 40000 # Large documents may cause timeouts
43
+ if len(text_content) > MAX_CHARS:
44
+ print(f"Document is large ({len(text_content)} chars). Truncating to {MAX_CHARS} chars to avoid timeouts.")
45
+ text_content = text_content[:MAX_CHARS] + "\n\n[Content truncated due to length...]"
46
+
47
+ try:
48
+ print(f"Sending document for processing...")
49
+ yaml_content = adapter.generate_mind_map_yaml(text_content)
50
+
51
+ # Save raw output for debugging
52
+ output_dir = os.path.dirname(mm_output)
53
+ debug_yaml_path = os.path.join(output_dir, f"{base_name}.raw.yaml")
54
+ with open(debug_yaml_path, 'w', encoding='utf-8') as f:
55
+ f.write(yaml_content)
56
+ print(f"Saved raw YAML for debugging to: {debug_yaml_path}")
57
+
58
+ # Now continue with the regular process
59
+ service.convert_text_file(temp_file_path, yaml_output, mm_output)
60
+ except Exception as e:
61
+ print(f"YAML conversion error: {e}")
62
+ print("Saving debug information and continuing...")
63
+ # Continue with original code path anyway
64
+ service.convert_text_file(temp_file_path, yaml_output, mm_output)
65
+
66
+ print("\nConversion successful!")
67
+ print(f"YAML output: {yaml_output}")
68
+ print(f"Mind map: {mm_output}")
69
+ if text_output:
70
+ print(f"Text output: {text_output}")
71
+
72
+ finally:
73
+ # Clean up the temporary file
74
+ if os.path.exists(temp_file_path):
75
+ os.unlink(temp_file_path)
@@ -0,0 +1,37 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict, Any
3
+ import os
4
+
5
+ # Default schema path - using a path that works both in development and when installed
6
+ # First try the package-relative path (for installed package)
7
+ DEFAULT_SCHEMA_PATH = os.path.join(os.path.dirname(__file__), 'schema', 'mindmap-schema.json')
8
+
9
+ # If that doesn't exist, try the development path
10
+ if not os.path.exists(DEFAULT_SCHEMA_PATH):
11
+ DEFAULT_SCHEMA_PATH = os.path.abspath(
12
+ os.path.join(
13
+ os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
14
+ "src", "schema", "mindmap-schema.json"
15
+ )
16
+ )
17
+
18
+
19
+ class LLMAdapter(ABC):
20
+ """
21
+ Abstract base class for LLM adapters.
22
+ This follows the hexagonal architecture pattern to isolate the LLM service from our core logic.
23
+ """
24
+
25
+ @abstractmethod
26
+ def generate_mind_map_yaml(self, text: str) -> str:
27
+ """
28
+ Generate a mind map in YAML format from the provided text,
29
+ following the schema defined in DEFAULT_SCHEMA_PATH.
30
+
31
+ Args:
32
+ text: The text content to convert to a mind map
33
+
34
+ Returns:
35
+ A string containing the YAML representation of the mind map
36
+ """
37
+ pass
@@ -0,0 +1,110 @@
1
+ import os
2
+ from openai import OpenAI
3
+ from typing import Dict, Any
4
+ from dotenv import load_dotenv
5
+ from .llm_adapter import LLMAdapter, DEFAULT_SCHEMA_PATH
6
+ from .yaml_utils import extract_yaml_from_markdown
7
+
8
+ # Load environment variables from .env file
9
+ load_dotenv()
10
+
11
+ class OpenAIAdapter(LLMAdapter):
12
+ """
13
+ Adapter for OpenAI API.
14
+
15
+ This class provides an implementation of the LLMAdapter for OpenAI models.
16
+ It makes API calls to OpenAI to generate mind maps from text.
17
+ """
18
+
19
+ DEFAULT_MODEL = "gpt-4o" # Default OpenAI model to use
20
+
21
+ def __init__(self, api_key: str = None, model: str = None):
22
+ """
23
+ Initialize the adapter with API credentials.
24
+
25
+ Args:
26
+ api_key: OpenAI API key, defaults to reading from OPENAI_API_KEY environment variable
27
+ model: OpenAI model to use, defaults to DEFAULT_MODEL
28
+ """
29
+ self.api_key = api_key or os.environ.get("OPENAI_API_KEY")
30
+ if not self.api_key:
31
+ raise ValueError(
32
+ "No API key provided. Pass an API key to the constructor or "
33
+ "set the OPENAI_API_KEY environment variable in the .env file."
34
+ )
35
+
36
+ self.model = model or self.DEFAULT_MODEL
37
+ self.client = OpenAI(api_key=self.api_key)
38
+
39
+ def generate_mind_map_yaml(self, text: str) -> str:
40
+ """
41
+ Generate a mind map in YAML format using OpenAI.
42
+
43
+ Args:
44
+ text: The text content to convert to a mind map
45
+
46
+ Returns:
47
+ A string containing the YAML representation of the mind map
48
+ """
49
+ # Read the schema content
50
+ with open(DEFAULT_SCHEMA_PATH, 'r') as f:
51
+ schema = f.read()
52
+
53
+ # Make the API call to OpenAI
54
+ yaml_content = self._call_openai_api(text, schema)
55
+
56
+ return yaml_content
57
+
58
+ def _call_openai_api(self, text: str, schema: str) -> str:
59
+ """
60
+ Call the OpenAI API with the text and schema.
61
+
62
+ Args:
63
+ text: The text content to convert to a mind map
64
+ schema: The JSON schema content
65
+
66
+ Returns:
67
+ The generated YAML content
68
+ """
69
+ try:
70
+ # Build system prompt and user prompt
71
+ system_prompt = "You are an expert at creating structured mind maps in YAML format. Your task is to organize the provided text into a hierarchical structure that follows the schema exactly. Every node must include both a title and a descriptive note providing context or details for that node. Only output valid YAML without any explanations, markdown code blocks, or extra text."
72
+
73
+ user_prompt = f"""I'd like you to summarise a document as a yaml file following a schema.
74
+
75
+ Please respond with ONLY the YAML content that follows the schema, without any additional text or explanations.
76
+ The YAML should have a root node with an appropriate title and organized children nodes that capture
77
+ the key points and structure of the text. Aim to have one node per 20 lines of input text.
78
+
79
+ IMPORTANT: Every node (including the root and ALL child nodes) should include both a title AND a note.
80
+ The note should provide additional context, explanation, or details about that specific node.
81
+
82
+ Here is the schema to follow:
83
+
84
+ {schema}
85
+
86
+ Here is the document to summarize:
87
+
88
+ {text}"""
89
+
90
+ # Call the OpenAI API using the new client interface
91
+ response = self.client.chat.completions.create(
92
+ model=self.model,
93
+ messages=[
94
+ {"role": "system", "content": system_prompt},
95
+ {"role": "user", "content": user_prompt}
96
+ ],
97
+ max_tokens=16000, # Maximum allowed for this model
98
+ temperature=0.2, # Low temperature for more deterministic output
99
+ )
100
+
101
+ # Extract the response content
102
+ content = response.choices[0].message.content
103
+
104
+ # Use the utility function to extract YAML from markdown
105
+ content = extract_yaml_from_markdown(content)
106
+
107
+ return content
108
+
109
+ except Exception as e:
110
+ raise RuntimeError(f"Error calling OpenAI API: {str(e)}") from e
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Convert PDF files to mind maps using PyMuPDF4LLM and AI.
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ import argparse
9
+ import tempfile
10
+ import pymupdf4llm
11
+ from dotenv import load_dotenv
12
+
13
+ from freeplane_and_yaml.convert_pdf_to_mindmap import convert_pdf_to_mindmap
14
+ from .text_to_mindmap import TextToMindMap
15
+ from .openai_adapter import OpenAIAdapter
16
+
17
+ # Load environment variables
18
+ load_dotenv()
19
+
20
+
21
+ def main():
22
+ """Main entry point for the pdf2mindmap command-line tool."""
23
+ parser = argparse.ArgumentParser(
24
+ description="Convert a PDF file to a mind map using PyMuPDF4LLM and OpenAI"
25
+ )
26
+ parser.add_argument(
27
+ "input_file",
28
+ help="Path to the input PDF file"
29
+ )
30
+ parser.add_argument(
31
+ "output_dir",
32
+ help="Directory to save the output files"
33
+ )
34
+ parser.add_argument(
35
+ "--model",
36
+ help="OpenAI model to use (defaults to gpt-4o)"
37
+ )
38
+ parser.add_argument(
39
+ "--save-text",
40
+ action="store_true",
41
+ help="Save the extracted text to a file"
42
+ )
43
+
44
+ args = parser.parse_args()
45
+
46
+ # Validate input file
47
+ if not os.path.isfile(args.input_file):
48
+ print(f"Error: Input PDF file '{args.input_file}' does not exist", file=sys.stderr)
49
+ sys.exit(1)
50
+
51
+ # Make sure it's a PDF file
52
+ if not args.input_file.lower().endswith('.pdf'):
53
+ print(f"Error: Input file must be a PDF file: {args.input_file}", file=sys.stderr)
54
+ sys.exit(1)
55
+
56
+ # Validate output directory
57
+ if not os.path.isdir(args.output_dir):
58
+ try:
59
+ os.makedirs(args.output_dir, exist_ok=True)
60
+ print(f"Created output directory: {args.output_dir}")
61
+ except Exception as e:
62
+ print(f"Error: Could not create output directory '{args.output_dir}': {e}", file=sys.stderr)
63
+ sys.exit(1)
64
+ input_file = args.input_file
65
+ output_dir = args.output_dir
66
+ save_text = args.save_text
67
+ # Generate output file paths
68
+ base_name = os.path.splitext(os.path.basename(input_file))[0]
69
+ yaml_output = os.path.join(output_dir, f"{base_name}.yaml")
70
+ mm_output = os.path.join(output_dir, f"{base_name}.mm")
71
+ text_output = os.path.join(output_dir, f"{base_name}.txt") if save_text else None
72
+ model = args.model
73
+
74
+ try:
75
+ convert_pdf_to_mindmap(base_name, input_file, mm_output, model, text_output, yaml_output)
76
+
77
+ except Exception as e:
78
+ print(f"Error during conversion: {e}", file=sys.stderr)
79
+ sys.exit(1)
80
+
81
+
82
+
83
+
84
+ if __name__ == "__main__":
85
+ main()
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Mind Map Schema",
4
+ "description": "Schema for Freeplane-compatible mind map YAML format",
5
+
6
+ "definitions": {
7
+ "node": {
8
+ "type": "object",
9
+ "required": ["title"],
10
+ "properties": {
11
+ "title": {
12
+ "type": "string",
13
+ "description": "The display text for the node"
14
+ },
15
+ "note": {
16
+ "type": "string",
17
+ "description": "Rich text note attached to the node"
18
+ },
19
+ "children": {
20
+ "type": "object",
21
+ "description": "Child nodes of this node",
22
+ "patternProperties": {
23
+ "^[a-zA-Z0-9_]+$": {
24
+ "$ref": "#/definitions/node"
25
+ }
26
+ },
27
+ "additionalProperties": false
28
+ }
29
+ },
30
+ "additionalProperties": false
31
+ }
32
+ },
33
+
34
+ "type": "object",
35
+ "required": ["root"],
36
+ "properties": {
37
+ "root": {
38
+ "allOf": [
39
+ { "$ref": "#/definitions/node" },
40
+ {
41
+ "required": ["children"],
42
+ "description": "The root node must have at least one child"
43
+ }
44
+ ]
45
+ }
46
+ },
47
+ "additionalProperties": false,
48
+
49
+ "examples": [
50
+ {
51
+ "root": {
52
+ "title": "Example Mind Map",
53
+ "note": "This is the root node",
54
+ "children": {
55
+ "topic1": {
56
+ "title": "First Topic",
57
+ "note": "Note for first topic",
58
+ "children": {
59
+ "subtopic1": {
60
+ "title": "Subtopic 1",
61
+ "note": "Note for subtopic"
62
+ }
63
+ }
64
+ },
65
+ "topic2": {
66
+ "title": "Second Topic",
67
+ "note": "Note for second topic"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ]
73
+ }