Class: Ace::LLM::Molecules::FileIoHandler
- Inherits:
-
Object
- Object
- Ace::LLM::Molecules::FileIoHandler
- Defined in:
- lib/ace/llm/molecules/file_io_handler.rb
Overview
FileIoHandler provides file I/O utilities for LLM query commands This is a molecule - it handles specific file operations with validation
Constant Summary collapse
- FORMAT_EXTENSIONS =
File extensions that indicate different output formats
{ ".json" => "json", ".md" => "markdown", ".markdown" => "markdown", ".txt" => "text", ".text" => "text" }.freeze
- MAX_FILE_SIZE =
Maximum file size to read (10MB)
10 * 1024 * 1024
Instance Method Summary collapse
-
#file_path?(input) ⇒ Boolean
Detect if input is a file path or inline content.
-
#infer_format(file_path) ⇒ String
Infer output format from file extension.
-
#initialize(**options) ⇒ FileIoHandler
constructor
Initialize file I/O handler.
-
#read_content(input, auto_detect: true) ⇒ String
Read content from file or return inline content.
-
#read_file_content(file_path) ⇒ String
Read content from a file with size validation.
-
#safe_path?(path) ⇒ Boolean
Check if a path is safe to write to.
-
#validate_inline_content(content) ⇒ String
Validate inline content.
-
#write_content(content, file_path, format: nil, force: false) ⇒ String
Write content to file with format handling.
Constructor Details
#initialize(**options) ⇒ FileIoHandler
Initialize file I/O handler
27 28 29 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 27 def initialize(**) @max_file_size = .fetch(:max_file_size, MAX_FILE_SIZE) end |
Instance Method Details
#file_path?(input) ⇒ Boolean
Detect if input is a file path or inline content
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 34 def file_path?(input) return false if input.nil? || input.strip.empty? # File paths must be single line strings input_str = input.strip return false if input_str.include?("\n") || input_str.include?("\r") # Only consider it a file path if the file actually exists begin path = Pathname.new(input_str) File.exist?(path.to_s) rescue ArgumentError, SystemCallError # Invalid path characters or other path-related errors false end end |
#infer_format(file_path) ⇒ String
Infer output format from file extension
128 129 130 131 132 133 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 128 def infer_format(file_path) return "text" if file_path.nil? || file_path.empty? ext = File.extname(file_path).downcase FORMAT_EXTENSIONS.fetch(ext, "text") end |
#read_content(input, auto_detect: true) ⇒ String
Read content from file or return inline content
56 57 58 59 60 61 62 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 56 def read_content(input, auto_detect: true) if auto_detect && file_path?(input) read_file_content(input.strip) else validate_inline_content(input) end end |
#read_file_content(file_path) ⇒ String
Read content from a file with size validation
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 68 def read_file_content(file_path) path = Pathname.new(file_path). # Check file size file_size = File.size(path) if file_size > @max_file_size raise Ace::LLM::Error, "File too large: #{file_size} bytes (max: #{@max_file_size} bytes)" end # Read file content File.read(path) rescue Errno::ENOENT raise Ace::LLM::Error, "File not found: #{file_path}" rescue Errno::EACCES raise Ace::LLM::Error, "Permission denied reading file: #{file_path}" rescue SystemCallError => e raise Ace::LLM::Error, "Error reading file: #{e.}" end |
#safe_path?(path) ⇒ Boolean
Check if a path is safe to write to
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 138 def safe_path?(path) return false if path.nil? || path.empty? # Reject paths with null bytes return false if path.include?("\0") # Reject paths trying to traverse up directories return false if path.include?("..") begin = Pathname.new(path). # Must be absolute after expansion .absolute? rescue false end end |
#validate_inline_content(content) ⇒ String
Validate inline content
91 92 93 94 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 91 def validate_inline_content(content) raise Ace::LLM::Error, "Content cannot be nil or empty" if content.nil? || content.strip.empty? content end |
#write_content(content, file_path, format: nil, force: false) ⇒ String
Write content to file with format handling
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/ace/llm/molecules/file_io_handler.rb', line 103 def write_content(content, file_path, format: nil, force: false) path = Pathname.new(file_path). # Check if file exists and handle overwrite if !force && File.exist?(path) raise Ace::LLM::Error, "File already exists: #{file_path}. Use --force to overwrite." end # Ensure parent directory exists FileUtils.mkdir_p(path.dirname) # Write content File.write(path, content) # Return format (inferred from extension or specified) format || infer_format(file_path) rescue Errno::EACCES raise Ace::LLM::Error, "Permission denied writing to: #{file_path}" rescue SystemCallError => e raise Ace::LLM::Error, "Error writing file: #{e.}" end |