No edit summary
(Replaced content with "Kotoba")
Tag: Replaced
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
from __future__ import annotations
Kotoba
import re
from typing import Any, Dict, List, Tuple, TypedDict, Union
from langchain_core.documents import Document
from langchain_text_splitters.base import Language
from langchain_text_splitters.character import RecursiveCharacterTextSplitter
 
class LineType(TypedDict):
    """Line type as typed dict."""
    metadata: Dict[str, str]
    content: str
 
class HeaderType(TypedDict):
    """Header type as typed dict."""
    level: int
    name: str
    data: str
 
class IdentifyHeaders:
    """Compute data for identifying header text."""
    def __init__(self,pdf_doc: str,page = None,body_limit: float = 10):
        """Read all text and make a dictionary of fontsizes.
        Args:
            body_limit: consider text with larger font size as some header
        """
        mydoc = fitz.open(pdf_doc)
        fontsizes = {}
        pages = range(mydoc.page_count)
        for pno in pages:
            page = mydoc.load_page(pno)
            blocks = page.get_text("dict", flags=fitz.TEXTFLAGS_TEXT)["blocks"]
            for span in [  # look at all non-empty horizontal spans
                s
                for b in blocks
                for l in b["lines"]
                for s in l["spans"]
                if not is_white(s["text"])
            ]:
                fontsz = round(span["size"])
                count = fontsizes.get(fontsz, 0) + len(span["text"].strip())
                fontsizes[fontsz] = count
 
        mydoc.close()
        self.header_id = {}
        temp = sorted([(k, v) for k, v in fontsizes.items()],key=lambda i: i[1],reverse=True,)
        b_limit = temp[0][0]
        sizes = sorted([f for f in fontsizes.keys() if f > b_limit],reverse=True,)[:8]
        for i, size in enumerate(sizes):
            self.header_id[size] = "#" * (i + 1) + " "
 
    def get_header_id(self, span: dict, page=None) -> str:
        """Return appropriate markdown header prefix.
        Given a text span from a "dict"/"rawdict" extraction, determine the
        markdown header prefix string of 0 to n concatenated '#' characters.
        """
        fontsize = round(span["size"])  # compute fontsize
        hdr_id = self.header_id.get(fontsize, "")
        return hdr_id
 
def aggregate_lines_to_chunks(lines: List[LineType]) -> List[Document]:
    """Combine lines with common metadata into chunks
        Args:
            lines: Line of text / associated header metadata
    """
   
def split_text(text: str,headers_split: List[Tuple[str, str]]) -> List[Document]:
    """Split markdown file
        Args:
            text: Markdown file"""
    lines = text.split("\n")
    lines_with_metadata: List[LineType] = []
    current_content: List[str] = []
    current_metadata: Dict[str, str] = {}
    current_metadata['type'] = 'text'
    header_stack: List[HeaderType] = []
    initial_metadata: Dict[str, str] = {}
    in_code_block = False
    opening_fence = ""
    for line in lines:
        stripped_line = line.strip()
        stripped_line = "".join(filter(str.isprintable, stripped_line))
        if stripped_line == '':
            continue
        current_header_level = 0
        if stripped_line.startswith("-"):
            continue
        elif stripped_line.startswith("```") or stripped_line.startswith("[[Special:Contributions/84.185.107.48|84.185.107.48]]"):
            initial_metadata['type'] = 'code'
            in_code_block = True
            opening_fence = "```"
        elif stripped_line.startswith("|"):
            initial_metadata['type'] = 'table'
        elif not in_code_block:
            initial_metadata['type'] = 'text'
        if in_code_block:
            if stripped_line.startswith(opening_fence):
                in_code_block = False
                opening_fence = ""
 
        for sep, name in headers_split: #if header create index
            if stripped_line.startswith(sep) and (len(stripped_line) == len(sep) or stripped_line[len(sep)] == " "):
                current_header_level = sep.count("#")
                while (header_stack and header_stack[-1]["level"] >= current_header_level):
                    popped_header = header_stack.pop()
                    if popped_header["name"] in initial_metadata:
                        initial_metadata.pop(popped_header["name"])
 
                header: HeaderType = {"level": current_header_level,"name": name,"data": stripped_line[len(sep):].strip()}
                header_stack.append(header)
                initial_metadata[name] = header["data"]
 
        if current_metadata['type'] != initial_metadata['type']:
            lines_with_metadata.append({"content":"\n".join(current_content),"metadata":current_metadata.copy()})
            current_content.clear()           
        current_metadata = initial_metadata.copy()
        if current_header_level == 0:
            current_content.append(stripped_line)
        else:
            lines_with_metadata.append({"content":"\n".join(current_content),"metadata":current_metadata.copy()})
            current_content.clear()
 
    lines_with_metadata.append({"content":"\n".join(current_content),"metadata":current_metadata.copy()})
    #lines_with_metadata.append({"content":"\n".join(current_content),"metadata":current_metadata})
    aggregated_chunks = [x for x in lines_with_metadata if x['content'] != '']
    # aggregated_chunks: List[LineType] = []
    # for line in lines_with_metadata:
    #    if (aggregated_chunks and aggregated_chunks[-1]["metadata"] == line["metadata"]):
    #        aggregated_chunks[-1]["content"] += "  \n" + line["content"]
    #    elif (aggregated_chunks
    #          and aggregated_chunks[-1]["metadata"] != line["metadata"]
    #          and len(aggregated_chunks[-1]["metadata"]) < len(line["metadata"])
    #          and aggregated_chunks[-1]["content"].split("\n")[-1][0] == "#"
    #          and False
    #        ):
    #        aggregated_chunks[-1]["content"] += "  \n" + line["content"]
    #        aggregated_chunks[-1]["metadata"] = line["metadata"]
    #    else:
    #        aggregated_chunks.append(line)
 
    return [
        Document(page_content=chunk["content"], metadata=chunk["metadata"])
        for chunk in aggregated_chunks
    ]

Latest revision as of 12:11, 6 November 2024

Kotoba