Module: OllamaChat::Parsing
Overview
A module that provides content parsing functionality for OllamaChat.
The Parsing module encapsulates methods for processing various types of input sources including HTML, XML, CSV, RSS, Atom, PDF, and Postscript documents. It handles content extraction and conversion into standardized text formats suitable for chat interactions. The module supports different document policies for handling imported or embedded content and provides utilities for parsing structured data from multiple source types.
Constant Summary collapse
- DOCUMENT_POLICY_STATES =
An array of valid document policy states that define how document references in user text are handled.
These states control the behavior of the document policy selector:
ignoring: Document references are ignored.embedding: Document references are embedded into the conversation context for RAG.importing: Document references are imported into the conversation.summarizing: Document references are summarized for reference.
%w[ ignoring embedding importing summarizing ]
- HAS_AUDIO =
Matches MIME types for audio and video content, including common container formats. Used by #parse_source and the
get_urltool to route media streams to #parse_audio for transcription. %r{\A(audio/|video/|application/(mp4|ogg|opus|webm|x-matroska)\z)}- CONTENT_REGEXP =
Regular expression to scan content for url/file references
%r{ (https?://\S+) # Match HTTP/HTTPS URLs | # OR (file://(?:[^\s#]+)) # Match file:// URLs | # OR "((?:\.\.|[~.]?)/(?:\\"|\\|[^"\\]+)+)" # Quoted file path with escaped " quotes | # OR ((?:\.\.|[~.]?)/(?:\\\ |\\|[^\\\s]+)+) # File path with escaped spaces }x
Instance Method Summary collapse
-
#epub_read(io) ⇒ String?
Converts an EPUB source to Plaintext via Pandoc and returns the text content.
-
#parse_atom(source_io) ⇒ String
The parse_atom method processes an Atom feed from the provided IO source and converts it into a formatted text representation.
-
#parse_audio(source_io, language: nil) ⇒ String?
Transcribes audio or video content via the ASR service.
-
#parse_content(content, images) ⇒ String
Parses a string for URLs, file refs, and image links, then returns the transformed content.
-
#parse_png(source_io) ⇒ Array<String>?
Extracts embedded metadata from a PNG image, including character profiles, prompts, and workflows.
-
#parse_rss(source_io) ⇒ String
The parse_rss method processes an RSS feed source and converts it into a formatted text representation.
-
#parse_source(source_io, language: nil) ⇒ String?
The parse_source method processes different types of input sources and converts them into a standardized text representation.
-
#pdf_read(io) ⇒ String
The pdf_read method extracts text content from a PDF file by reading all pages.
-
#personalize_character_profile(char, name:) ⇒ String
Personalizes a character profile by replacing placeholders with actual names.
-
#process_document(source_io, source, contents) ⇒ Object
Dispatches a parsed source to the handler matching the active #document_policy.
-
#ps_read(io) ⇒ String?
Reads and processes PDF content using Ghostscript for conversion.
-
#reverse_markdown(html) ⇒ String
The reverse_markdown method converts HTML content into Markdown format.
Methods included from Utils::AnalyzeDirectory
#generate_structure, #recurse_generate_structure, #structure_each_entry
Instance Method Details
#epub_read(io) ⇒ String?
Converts an EPUB source to Plaintext via Pandoc and returns the text content.
203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/ollama_chat/parsing.rb', line 203 def epub_read(io) pandoc = `which pandoc`.chomp if pandoc.present? Tempfile.create(['epub_in', '.epub']) do |tmp| IO.copy_stream(io, tmp) `#{pandoc} -f epub -t plain "#{tmp.path}"` end else feedback("Cannot convert EPUB, pandoc not in path.", type: :warn) nil end end |
#parse_atom(source_io) ⇒ String
The parse_atom method processes an Atom feed from the provided IO source and converts it into a formatted text representation. It extracts the feed title and iterates through each item to build a structured output containing titles, links, and update dates.
The content of each item is converted using reverse_markdown for better readability.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/ollama_chat/parsing.rb', line 168 def parse_atom(source_io) feed = RSS::Parser.parse(source_io, false, false) title = <<~EOT # #{feed.title.content} EOT feed.items.inject(title) do |text, item| text << <<~EOT ## [#{item&.title&.content}](#{item&.link&.href}) updated on #{item&.updated&.content} #{reverse_markdown(item&.content&.content)} EOT end end |
#parse_audio(source_io, language: nil) ⇒ String?
Transcribes audio or video content via the ASR service. Delegates to ASR.transcribe which handles format conversion (e.g. video → WAV via ffmpeg) and the multipart upload to the server.
122 123 124 |
# File 'lib/ollama_chat/parsing.rb', line 122 def parse_audio(source_io, language: nil) OllamaChat::ASR.transcribe(source_io, chat: self, language:) end |
#parse_content(content, images) ⇒ String
Parses a string for URLs, file refs, and image links, then returns
the transformed content. Detects http(s) URLs, file:// paths,
quoted file paths, and collects any image URLs into the supplied
images array.
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'lib/ollama_chat/parsing.rb', line 295 def parse_content(content, images) images.clear contents = [ content ] content.scan(CONTENT_REGEXP).each { |url, file_url, quoted_file, file| if file && Pathname.new(file)..directory? max_depth = config.parsing.directory_structure_max_depth contents << generate_structure(file, max_depth:, include_hidden: true).to_json next end check_exist = false case when url source = url when file_url check_exist = true source = file_url when quoted_file file = quoted_file.gsub('\"', ?") file =~ %r(\A[~./]) or file.prepend('./') check_exist = true source = file when file file = file.gsub('\ ', ' ') file =~ %r(\A[~./]) or file.prepend('./') check_exist = true source = file end fetch_source(source, check_exist:) do |source_io| case source_io&.content_type&.media_type when 'image' add_image(images, source_io, source) if source_io&.content_type&.sub_type == 'png' source_io.rewind if results = parse_png(source_io).full? contents.concat results end end when 'audio', 'video' document_policy.selected == 'ignoring' and next text = parse_audio(source_io) text_io = OllamaChat::Utils::Fetcher::ResponseMetadata.as_text(text) process_document(text_io, source, contents) when 'text', 'application', nil process_document(source_io, source, contents) else feedback( "Cannot fetch #{source.to_s.inspect} with content type " \ "#{source_io&.content_type.inspect}", type: :warn ) end end } contents.select { _1.present? rescue nil }.compact * "\n\n" end |
#parse_png(source_io) ⇒ Array<String>?
Extracts embedded metadata from a PNG image, including character profiles, prompts, and workflows. Character profiles are automatically personalized to replace placeholders with the current user's name.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/ollama_chat/parsing.rb', line 83 def parse_png(source_io) = OllamaChat::Utils::PNGMetadataExtractor.extract_all(source_io) or return results = [] if data = .delete('chara') and (char, char_data = OllamaChat::Utils::PNGMetadataExtractor.decode_character(data)) then name = char_data.dig('data', 'name').full? || Pathname.new(source_io.path).basename.sub_ext('').to_s results << "Character Profile:\n\n#{personalize_character_profile(char, name:)}" end if data = .delete('parameters') and params = OllamaChat::Utils::PNGMetadataExtractor.parse_a1111_parameters(data) then results << "Generation Settings:\n\n#{params.to_json}" end if data = convert_to_utf8(.delete('prompt')) results << "Prompt:\n\n#{data}" end if data = convert_to_utf8(.delete('workflow')) results << "Workflow:\n\n#{data}" end if data = .full? { _1.transform_values { |v| convert_to_utf8(v) } } results << "Metadata:\n\n#{data}" end results.full? end |
#parse_rss(source_io) ⇒ String
The parse_rss method processes an RSS feed source and converts it into a formatted text representation. It extracts the channel title and iterates through each item in the feed to build a structured output. The method uses the RSS parser to handle the source input and formats the title, link, publication date, and description of each item into a readable text format with markdown-style headers and links.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/ollama_chat/parsing.rb', line 138 def parse_rss(source_io) feed = RSS::Parser.parse(source_io, false, false) title = <<~EOT # #{feed&.channel&.title} EOT feed.items.inject(title) do |text, item| text << <<~EOT ## [#{item&.title}](#{item&.link}) updated on #{item&.pubDate} #{reverse_markdown(item&.description)} EOT end end |
#parse_source(source_io, language: nil) ⇒ String?
The parse_source method processes different types of input sources and converts them into a standardized text representation.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/ollama_chat/parsing.rb', line 40 def parse_source(source_io, language: nil) case source_io&.content_type when 'text/html' reverse_markdown(source_io.read) when 'text/xml', 'application/xml' if source_io.read(8192) =~ %r(^\s*<rss\s) source_io.rewind return parse_rss(source_io) end source_io.rewind source_io.read when 'application/rss+xml' parse_rss(source_io) when 'application/atom+xml' parse_atom(source_io) when 'application/postscript' ps_read(source_io) when 'application/pdf' pdf_read(source_io) when 'application/epub+zip' epub_read(source_io) when HAS_AUDIO parse_audio(source_io, language:) when 'image/png' results = parse_png(source_io) and return results.join("\n\n---\n\n") feedback("Could not parse metadata from #{source_io&.content_type} document.", type: :warn) nil when %r(\Aapplication/(json|ld\+json|x-ruby|x-perl|x-gawk|x-python|x-javascript|x-c?sh|x-dosexec|x-shellscript|x-tex|x-latex|x-lyx|x-bibtex)), %r(\Atext/), nil source_io.read else feedback("Cannot parse #{source_io&.content_type} document.", type: :warn) return end end |
#pdf_read(io) ⇒ String
The pdf_read method extracts text content from a PDF file by reading all pages.
192 193 194 195 |
# File 'lib/ollama_chat/parsing.rb', line 192 def pdf_read(io) reader = PDF::Reader.new(io) reader.pages.inject(+'') { |result, page| result << page.text } end |
#personalize_character_profile(char, name:) ⇒ String
Personalizes a character profile by replacing placeholders with actual names.
265 266 267 268 269 270 |
# File 'lib/ollama_chat/parsing.rb', line 265 def personalize_character_profile(char, name:) my_user_name = user_name || 'the user' char = char.gsub(/{{user}}/i, my_user_name) name.present? and char = char.gsub(/{{char}}/i, name) char end |
#process_document(source_io, source, contents) ⇒ Object
Dispatches a parsed source to the handler matching the active #document_policy. This is the shared terminal step for both the audio/video and the text branches of #parse_content, so every policy is applied uniformly regardless of the source media type.
Behaviour per policy:
ignoring— no-op, the source is dropped.importing— the source text is appended tocontents.embedding— the source is embedded into the RAG context; nothing is appended tocontents.summarizing— the source summary is appended tocontents.
367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/ollama_chat/parsing.rb', line 367 def process_document(source_io, source, contents) case document_policy.selected when 'ignoring' when 'importing' contents << import_source(source_io, source) when 'embedding' (source_io, source) when 'summarizing' contents << summarize_source(source_io, source) end end |
#ps_read(io) ⇒ String?
Reads and processes PDF content using Ghostscript for conversion
This method takes an IO object containing PDF data, processes it through Ghostscript's pdfwrite device, and returns the processed PDF content. If Ghostscript is not available in the system path, it outputs an error message.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/ollama_chat/parsing.rb', line 225 def ps_read(io) gs = `which gs`.chomp if gs.present? Tempfile.create do |tmp| IO.popen("#{gs} -q -sDEVICE=pdfwrite -sOutputFile=#{tmp.path} -", 'wb') do |gs_io| IO.copy_stream(io, gs_io) gs_io.close File.open(tmp.path, 'rb') do |pdf| pdf_read(pdf) end end end else feedback("Cannot convert #{io&.content_type} with ghostscript, gs not in path.", type: :warn) end end |
#reverse_markdown(html) ⇒ String
The reverse_markdown method converts HTML content into Markdown format.
This method processes HTML input and transforms it into equivalent Markdown, using specific conversion options to ensure compatibility and formatting.
251 252 253 254 255 256 257 258 |
# File 'lib/ollama_chat/parsing.rb', line 251 def reverse_markdown(html) ReverseMarkdown.convert( html, unknown_tags: :bypass, github_flavored: true, tag_border: '' ) end |