eyecite

repository·main·Indexed 42 days ago

https://github.com/freelawproject/eyecite

An open source tool for extracting and resolving legal citations from text strings, supporting full cases, statutory, and journal citations. Version 2.7.8 provides a high-level API to find, clean, and annotate citations using functions like get_citations(), annotate_citations(), and resolve_citations(). It includes utilities for text normalization, HTML/XML cleaning, and a comprehensive model system for handling various citation types such as FullCaseCitation, ShortCaseCitation, and IdCitation.

Tokens
10.3K
Snippets
32
Records
42
Agent score
55%

What's inside eyecite

  1. Configure tokenizers for citation extraction

    main

    Eyecite uses tokenizers to convert source text into a list of tokens (e.g., CitationToken, IdToken, StopWordToken) before scanning them. You can substitute the default tokenizer by passing a tokenizer instance to get_citations(text, tokenizer=...).

    Available Tokenizers

    1. AhocorasickTokenizer (Default): Uses the pyahocorasick library to filter regexes and the built-in re library for extraction. It is suitable for most use cases.
    2. HyperscanTokenizer: Compiles all extraction regexes into a hyperscan database for single-pass extraction. This is significantly faster but requires the hyperscan dependency.

    To install the hyperscan dependency:

    pip install hyperscan

    Because compiling the hyperscan database is slow, it is recommended to provide a cache_dir to HyperscanTokenizer to store the database.

    from eyecite.tokenizers import HyperscanTokenizer
    
    hyperscan_tokenizer = HyperscanTokenizer(cache_dir='.hyperscan')
    cites = get_citations(text, tokenizer=hyperscan_tokenizer)
  2. Annotate citations in original source text with HTML

    main

    If you have cleaned text (e.g., stripped HTML tags) but want to apply annotations back to the original source text (e.g., a string containing HTML), use the source_text parameter in annotate_citations. This uses a diffing algorithm to map the citation spans from the cleaned text back to the correct locations in the original source.

    Example workflow:

    1. Clean the source text using clean_text.
    2. Extract citations from the cleaned text using get_citations.
    3. Apply annotations to the original source using annotate_citations(..., source_text=source_text).
    from eyecite import get_citations, annotate_citations, clean_text
    
    source_text = '<p>bob lissner v. <i>test   1 U.S.</i> 12,   347-348 (4th Cir. 1982)</p>'
    plain_text = clean_text(source_text, ['html', 'inline_whitespace'])
    citations = get_citations(plain_text)
    linked_text = annotate_citations(plain_text, [[c.span(), "<aRelay>", "</a>"] for c in citations], source_text=source_text)
    
    # returns: '<p>bob lissner v. <i>test   <aRelay>1 U.S.</i> 12</aRelay>,   347-348 (4th Cir. 1982)</p>'
  3. Run eyecite tests

    main

    Eyecite includes a test suite. You can run it using unittest:

    python3 -m unittest discover -s tests -p 'test_*.py'

    To assist with writing local tests, you can use the following factories from eyecite.test_factories to create mock citation objects:

    • case_citation
    • id_citation
    • supra_citation
    • unknown_citation
    from eyecite.test_factories import (
        case_citation,
        id_citation,
        supra_citation,
        unknown_citation,
    )
  4. Resolve reference citations using an external database

    main

    Eyecite supports a two-step process to resolve reference citations (e.g., "Theatre Enterprises at 552") that lack a full case name. This requires an external database or heuristic to resolve a short case name before the second extraction pass.

    1. Extract full citations: Use get_citations() to find explicit citations.
    2. Resolve case name: Manually or via database, assign a resolved short name to the metadata.resolved_case_name_short field of a citation.
    3. Extract references: Use extract_reference_citations() with the resolved citation and the original text.
    4. Filter and merge: Use filter_citations() to combine the original and newly found citations.
    from eyecite import get_citations
    from eyecite.find import extract_reference_citations
    from eyecite.helpers import filter_citations
    
    plain_text = (
        "quoting Theatre Enterprises, Inc. v. Paramount Film Distributing Corp., 346 U. S. 537, 541 (1954); "
        "alterations in original. Thus, the District Court understood that allegations of "
        "parallel business conduct, taken alone, do not state a claim under § 1; "
        "plaintiffs must allege additional facts that “ten to exclude independent "
        "self-interested conduct as an As Theatre Enterprises at 552 held, parallel"
    )
    
    # Step 1: Extract full citations
    citations = get_citations(plain_text)
    
    # Step 2: Resolve the case name from an external database or prior knowledge
    citations[0].metadata.resolved_case_name_short = "Theatre Enterprises"
    
    # Step 3: Extract reference citations using the resolved name
    references = extract_reference_citations(citations[0], plain_text)
    
    # Step 4: Filter and merge citations
    new_citations = filter_citations(citations + references)
  5. Extract reference citations following a full citation

    main

    When a FullCaseCitation is found, eyecite can automatically look for subsequent ReferenceCitation objects that follow a specific name-pincite pattern (e.g., using the plaintiff or defendant's name).

    This is handled internally by get_citations, but the logic relies on:

    1. Name-Pincite Pattern: Using regexes generated from the metadata of the preceding FullCaseCitation.
    2. Markup Detection: If markup_text is provided, eyecite uses find_reference_citations_from_markup to find party names wrapped in <em> or <i> tags.
  6. How different citation types are identified

    main

    The get_citations function categorizes tokens into several types to determine how to extract the citation:

    Token TypeDescriptionExtraction Logic
    CitationTokenA reporter, law journal, or law.Attempts to extract a FullCitation (e.g., FullCaseCitation) first; if that fails, attempts a ShortCaseCitation (e.g., Adarand, 515 U.S., at 241).
    IdTokenAn "Id." or "Ibid." reference.Extracts an IdCitation. Note: Resolution to the actual antecedent is left to the user for safety.
    SupraTokenA "supra" reference.Extracts a SupraCitation. Resolution to the antecedent is also left to the user.
    SectionTokenA section marker.Recorded as an UnknownCitation to maintain an accurate list of possible antecedents for later resolution.
  7. Understand the CitationBase and its subclasses

    main

    The CitationBase is the fundamental class for all citation objects returned by eyecite.find.get_citations. It tracks the token (the source text), the index in the token list, and various span offsets.

    Key subclasses include:

    • ResourceCitation: A base for citations that identify a specific resource (case, law, or journal). It includes exact_editions, variation_editions, and an edition_guess.
    • FullCitation: A ResourceCitation that fully identifies a resource.
      • FullLawCitation: Citation to a source from reporters_db/laws.json.
      • FullJournalCitation: Citation to a source from reporters_db/journals.json.
    • CaseCitation: A convenience class for case citations. It includes volume, reporter, and page in its groups dictionary.
      • FullCaseCitation: A standard, fully named citation (e.g., includes plaintiff and defendant).
      • ShortCaseCitation: A short-form citation (e.g., "Adarand, 515 U.S., at 241").
    • SupraCitation: Represents a 'supra' citation (e.g., "Adarand, supra, at 240").
    • IdCitation: Represents an 'id' or 'ibid' citation.
    • ReferenceCitation: A citation referring to a case by name and pincite alone (e.g., "Roe at 240").
    • UnknownCitation: A catch-all for unrecognized citations.
  8. Handle unbalanced HTML tags during annotation

    main

    When annotating text that contains HTML, you may encounter mismatched tags (e.g., an annotation wrapping only part of an <i> tag). Control this behavior using the unbalanced_tags parameter in annotate_citations:

    • unbalanced_tags="skip": Annotations that would result in unbalanced tags are not inserted. A simple correction for style tags is attempted.
    • unbalanced_tags="wrap": Unbalanced tags are wrapped. For example, <aRelay>1 U.S.</aRelay></i><aRelay> 12</aRelay>.

    Warning: unbalanced_tags="wrap" uses regular expressions and is intended for regularly formatted documents (like court-published case text). It may produce unpredictable results for complex inputs like partial HTML comments or <pre> tags.

  9. Annotate text with citation links using annotate_citations()

    main

    To insert HTML or other markup (like links) back into a text based on extracted citations, use annotate_citations().

    An annotation is a tuple of ((start_offset, end_offset), before_text, after_text). You can obtain the offsets by calling the .span() method on a Citation object.

    Important: If you extracted citations from a cleaned version of the text, you must pass the original, uncleaned text to the source_text parameter. annotate_citations() will use the diff-match-patch library to intelligently adjust the positional offsets so the annotations land correctly in the original text.

    from eyecite import annotate_citations
    
    # 1. Prepare annotations: ( (start, end), prefix, suffix )
    annotations = []
    for resource, cites in resolutions.items():
        if isinstance(resource, Resource):
            url = f"/api?cite={resource.citation.matched_text()}"
            for citation in cites:
                annotations.append((citation.span(), f"<a href='{url}'>", "</a>"))
    
    # 2. Apply annotations to the original text
    # Use source_text to map offsets from cleaned_text back to opinion_text
    annotated_text = annotate_citations(
        cleaned_text, 
        annotations, 
        source_text=opinion_text
    )
  10. Extract citations using HyperscanTokenizer

    main

    For high-performance citation extraction, use the HyperscanTokenizer. It uses a regular expression database that is pre-compiled and cached.

    Note: The first use of the tokenizer is slow due to the one-time pre-compilation stage. To ensure subsequent calls are fast, provide a cache_dir so the compiled database can be persisted.

    To use it, you must have the hyperscan Python package installed (pip install hyperscan).

    from eyecite.tokenizers import HyperscanTokenizer
    from eyecite import get_citations
    
    # Initialize with a cache directory for performance
    tokenizer = HyperscanTokenizer(cache_dir=".test_cache")
    
    # Extract citations
    citations = get_citations(cleaned_text, tokenizer=tokenizer)