attachments

repository·main·Indexed 18 days ago

https://github.com/maximerivest/attachments

A tool for converting files, URLs, GitHub repositories, and archives into LLM-ready artifacts. It features magic-byte detection for routing files to the correct processor, a decoupled architecture of Unpack Handlers and Processors, and support for formats including PDF, XLSX, DOCX, PPTX, and HTML. It can be used as a CLI tool, a Python library, or an MCP server for AI agents, with options for local processing or remote service fallback.

Tokens
34.9K
Snippets
119
Records
171
Agent score
60%

What's inside attachments

  1. Understand the Attachments module structure

    main

    The attachments package is organized into several functional areas:

    • Public API: src/attachments/__init__.py exports the main entry points.
    • Core Logic: core.py handles the main att() function and routing; _artifacts.py manages the returned artifact containers.
    • Configuration & Dependencies: config.py for global settings, deps.py for dependency detection, and _options.py for DSL option schemas.
    • Input Sources: The _sources/ directory handles where files come from (local files, ZIP/TAR archives, HTTP(S) downloads, and GitHub repositories).
    • Processors: The _processors/ directory contains specialized logic for different file formats (PDF, Excel, Word, HTML, Images, Audio, etc.).
    • Remote Services: service.py is the client for remote APIs, and server.py provides a self-hosted HTTP/WSGI server.
  2. Identify the current version and migration status

    main

    This repository represents attachments 1.0.0, which is the next major version of the attachments PyPI package.

    Version Status:

    • attachments 1.0.0 (This repo): The current active development version. The IR is frozen and enforced by a conformance suite. It features a clean break from the 0.x grammar API.
    • attachments 0.25.x (v1): Currently in maintenance mode (bug fixes only, no new features). It contains the richest corpus of converters but is superseded by the 1.0.0 architecture.

    Note for Users: If you are using pip install attachments, you will receive the stable 0.25.x version. To use the new 1.0.0 features, you must explicitly opt into pre-releases.

  3. What is the Attachments Artifact IR?

    main

    The Artifact is the core, language-neutral data format produced by every converter in the Attachments ecosystem. It is designed to be a lightweight, standardized output that prompt builders, RAG pipelines, and agents can consume regardless of the original file format or source.

    The Artifact schema follows a specific structure:

    {
      "text": "...",
      "images": [],
      "audio": [],
      "video": [],
      "meta": {}
    }

    Key Characteristics:

    • Structural Invariants: While different extraction engines (e.g., different PDF libraries) might produce slightly different text content (whitespace, ligatures), the shape of the Artifact (keys, types, and metadata envelope) is strictly enforced to be portable across implementations.
    • Simplicity: The core model focuses on {text, images}. For advanced RAG use cases, the IR supports optional structural segmentation (e.g., pages, sheets, or sections) with offsets into the text field.
  4. Understand DSL key and value normalization

    main

    When parsing the DSL, the following normalization rules are applied to ensure consistent behavior across different implementations:

    Key Normalization

    • Trimmed of whitespace.
    • Lowercased.
    • Hyphens (-) and spaces are collapsed to underscores (_).
    • Example: Max-Rows becomes max_rows.
    • Duplicate keys: If a key is repeated, the last occurrence wins.

    Value Typing Order

    Values are trimmed and then typed according to this priority order:

    1. Quoted Strings ("..." or '...'): Becomes a string with quotes removed. No further typing is applied.
    2. Boolean: Case-insensitive match for true/false, yes/no, or on/off. Note that 1 and 0 are treated as integers, not booleans.
    3. Integer: Optional leading - followed by digits.
    4. Float: Digits containing exactly one ..
    5. Range: Format N-M where both sides are non-negative integers (parsed as an integer pair).
    6. String: Any other value is treated as a verbatim string.
  5. How routing and service fallback works

    main

    The core routing logic (core.py) manages the transition between local processing and remote service processing.

    Service Fallback Logic

    1. A processor is run locally.
    2. If the resulting Artifact satisfies is_missing_dependency(result) AND an API key is configured, the system attempts to process the file via the remote service.
    3. If the service is used, the resulting Artifact will have meta.via = "service".

    Edge Cases

    • No Processor: If no processor is available for a specific file extension and the result is not text, the system returns an empty Artifact with meta.note = "no processor available". This is treated as a valid state, not an error.
    • Error Propagation: Errors are never raised out of the att() call; they are always returned as Artifact objects containing error metadata.
  6. Understand the HEAVY_FEATURES doctrine and service fallback

    main

    The library distinguishes between light features and HEAVY_FEATURES to manage dependency pain.

    HEAVY_FEATURES currently includes: ocr, audio.

    Service Fallback Logic:

    • Light extras (e.g., pdf, xlsx, docx, html) should only suggest a local pip install remedy.
    • Heavy features (e.g., OCR, transcription, video captioning) that require large dependencies like onnxruntime or whisper weights may suggest the free hosted tier (attachments.dev) as a fallback when local installation is difficult.

    In any message offering both, the local path must always be mentioned first.

  7. How Attachments architecture and service fallback work

    main

    Attachments uses a zero required dependencies architecture designed for minimal installs and graceful degradation.

    When you call att(), the system follows this logic:

    1. Check Local Processor: It checks if a local processor is available (e.g., if pypdf is installed).
    2. Try Local: If available, it attempts to process the file locally.
    3. Service Fallback: If local processing fails or the dependency is missing, and an api_key is configured via configure(), the system automatically attempts to use a remote service.
    4. Return Artifact: The system returns a useful artifact (text, images, etc.) regardless of whether it was processed locally or via the service.
  8. Understand the pricing models and tiers

    main

    The project uses a hybrid pricing model consisting of three tiers. Paying for a tier removes usage limits but does not gate features that were previously available in the free tier.

    1. Free hosted tier: Keyless and requires no signup. It is subject to rate limits and file size constraints.
    2. Credit packs: Prepaid credits purchased via Stripe Checkout. Credits never expire. This is designed for bursty, non-recurring usage.
    3. Subscription: A flat monthly fee for labs and applications. Provides a predictable monthly cost and a pooled key for teams.

    Subscription Details ($29/month):

    • No rate limits.
    • Supports up to 100 MB files.
    • Includes a pooled team key.
    • Includes 10,000 OCR pages and 5,000 transcription minutes monthly (excess usage is billed at pack rates).
  9. Understand the Artifact data structure

    main

    The output of att() is an Artifacts object, which is a list subclass of dictionaries. Each dictionary (an Artifact) follows a universal schema:

    {
        "text": "...",
        "images": [],          // List of ImageItem dicts: {name, mimetype, bytes, page}
        "audio": [],          // Reserved
        "video": [],          // Reserved
        "meta": {
            "source": "filename.ext",
            "kind": "file_type",
            "segments": [
                {"kind": "page", "label": "page 1", "start": 0, "end": 46}
            ],
            "extra": { ... },
            "error": { "code": "...", "message": "..." } // Present only on error
        }
    }

    Key behaviors:

    • Errors: Errors do not raise exceptions. Instead, they return an artifact containing a meta.error dictionary. Common error codes include missing-dependency, parse-error, and unpack-error.
    • Segments: The meta.segments field provides offsets into the text, allowing for structural awareness (e.g., knowing which text belongs to which page).
    # Example of accessing an artifact's content
    artifacts = att("report.pdf")
    first_artifact = artifacts[0]
    
    print(first_artifact["text"])
    print(first_artifact["meta"]["segments"])
    
    # Checking for errors
    if "error" in first_artifact["meta"]:
        print(f"Error: {first_artifact['meta']['error']['message']}")
  10. Configure source option schemas and DSL syntax

    main

    You can define an option schema for your source handler so that users can pass parameters via a DSL-style syntax (e.g., s3://bucket/key[region: us-east-1]).

    Steps to implement options:

    1. Call register_options("scheme://", ...) to define the schema.
    2. Immediately follow this with a call to snapshot_option_defaults() to ensure the schema is not wiped during testing/reset cycles.
    3. Parse the resulting parameters from the input string (which will arrive at your handler as query parameters, e.g., scheme://path?option=value).
    4. If you add an option schema, run uv run python scripts/gen_dsl_assets.py to regenerate the necessary type stubs and documentation assets.
  11. Use DSL syntax for inline options

    main

    You can specify processing options directly within the input string using a Domain Specific Language (DSL) syntax: [key: value, ...].

    Common Examples:

    • PDF: att("report.pdf[pages: 1-4]") or att("report.pdf[pages: 1-10, images: true, dpi: 300]")
    • Excel: att("data.xlsx[sheet: Sales, rows: 100]")
    • Audio: att("meeting.mp3[model: small, language: en]")
    • GitHub: att("github://org/repo[branch: develop]")

    Every DSL option has a keyword-argument twin in the Python function. For example, att("doc.pdf[pages: 1-4]") is equivalent to att("doc.pdf", pages="1-4").

    from attachments import att
    
    # Using DSL syntax
    artifacts = att("report.pdf[pages: 1-4, images: true]")
    
    # Using keyword arguments (equivalent)
    artifacts = att("report.pdf", pages="1-4", images=True)
  12. Identify the target audience for attachments

    main

    The primary audience consists of the scientific-computing and data community (data scientists, researchers, analysts, and domain experts) who work primarily in notebook environments like Jupyter, RStudio/Positron, Quarto, and Colab.

    Key user behaviors to note:

    • They prefer working in notebooks over terminals.
    • They often encounter dependency issues (e.g., onnxruntime) on restricted machines.
    • They value tools that hide file encoding and MIME complexity.

    Secondary users are developers building LLM pipelines who require technical specifications and self-hosting capabilities.