Skip to main content

Static analysis and RAG-optimized chunking for Business Central AL files

Project description

bc-al-chunker

RAG-optimized chunking for Business Central AL files.

bc-al-chunker statically parses .al files and produces semantically-aware chunks optimized for embedding and retrieval-augmented generation (RAG). It understands the structure of every AL object type — tables, pages, codeunits, reports, queries, enums, interfaces, and all extension types — and splits large objects at natural semantic boundaries (sections, procedures, triggers) while keeping small objects whole.

Features

  • Hierarchical chunking — small objects stay whole; large objects split at procedure/trigger/section boundaries
  • Context headers — every sub-chunk gets a synthetic context comment so each chunk is self-contained for embedding
  • All AL object types — table, page, codeunit, report, query, xmlport, enum, interface, permissionset, profile, controladdin, entitlement, and all extension variants
  • Multiple data sources — local filesystem, GitHub API, Azure DevOps API
  • Structured output — Python dataclasses with JSON and JSONL export
  • Zero dependencies for core usage — httpx only needed for remote adapters
  • Fully typed — strict mypy, PEP 561 py.typed marker

Installation

# Core (local filesystem only)
pip install bc-al-chunker

# With GitHub adapter
pip install bc-al-chunker[github]

# With Azure DevOps adapter
pip install bc-al-chunker[azure]

# Everything
pip install bc-al-chunker[all]

Quick Start

from bc_al_chunker import chunk

# Chunk all .al files in a directory
chunks = chunk("/path/to/al-repo")

# Multiple repositories
chunks = chunk(["/repo1", "/repo2"])

# Each chunk has content + rich metadata
for c in chunks:
    print(c.metadata.object_type, c.metadata.object_name, c.metadata.chunk_type)
    print(c.content[:100])
    print(c.token_estimate)

Configuration

from bc_al_chunker import chunk, ChunkingConfig

chunks = chunk(
    "/path/to/repo",
    config=ChunkingConfig(
        max_chunk_chars=2000,      # Max characters per chunk (default: 1500)
        min_chunk_chars=100,       # Min characters per chunk (default: 100)
        include_context_header=True,  # Prepend object context to sub-chunks
        estimate_tokens=True,      # Include token estimate on each chunk
    ),
)

Remote Sources

from bc_al_chunker import chunk_source
from bc_al_chunker.adapters.github import GitHubAdapter
from bc_al_chunker.adapters.azure_devops import AzureDevOpsAdapter

# GitHub
chunks = chunk_source(
    GitHubAdapter("microsoft/BCApps", token="ghp_...", paths=["src/"])
)

# Azure DevOps
chunks = chunk_source(
    AzureDevOpsAdapter("myorg", "myproject", "myrepo", token="pat...")
)

Export

from bc_al_chunker import chunk, chunks_to_json, chunks_to_jsonl, chunks_to_dicts

chunks = chunk("/path/to/repo")

# JSON array
chunks_to_json(chunks, "output.json")

# JSONL (streaming-friendly)
chunks_to_jsonl(chunks, "output.jsonl")

# Python dicts (for programmatic use)
dicts = chunks_to_dicts(chunks)

Chunking Strategy

The chunker uses a hierarchical, AST-aware strategy:

  1. Parse — Each .al file is parsed into an ALObject AST with sections, procedures, triggers, and properties identified
  2. Size check — If the object's source is ≤ max_chunk_chars, it becomes one WholeObject chunk
  3. Split — Large objects are split:
    • Header chunk — object declaration + top-level properties
    • Section chunksfields, keys, layout, actions, views, dataset, etc.
    • Procedure/Trigger chunks — each procedure or trigger as its own chunk
  4. Context injection — Sub-chunks get a context header prepended:
    // Object: codeunit 50100 "Address Management"
    // File: src/Codeunits/AddressManagement.al
    procedure ValidateAddress(var CustAddr: Record "Customer Address")
    begin
        ...
    end;
    

This ensures every chunk is self-contained and produces high-quality embeddings for code search.

Chunk Schema

Each Chunk contains:

  • content — the text to embed
  • token_estimate — approximate token count (chars / 4)
  • metadata:
    • file_path, object_type, object_id, object_name
    • chunk_typewhole_object, header, section, procedure, trigger
    • section_name, procedure_name
    • extends — for extension objects
    • source_table — extracted from page/codeunit properties
    • attributes — e.g., [EventSubscriber(...)]
    • line_start, line_end
    • file_hash — 16-character BLAKE2b hex digest of the source file content; use this to detect whether a file has changed before re-embedding

Detecting file changes with file_hash

Every chunk carries a file_hash computed from the file's source text using BLAKE2b (8-byte digest — the fastest built-in hash algorithm in Python's hashlib). Because the hash is derived from the normalized (BOM-stripped) UTF-8 content, it is stable regardless of encoding variants.

from bc_al_chunker import chunk, hash_source

chunks = chunk("/path/to/al-repo")

# Group chunks by file; all chunks from the same file share the same hash
file_hashes = {c.metadata.file_path: c.metadata.file_hash for c in chunks}

# Later — re-read a file and compare before doing expensive work
with open("MyTable.al", encoding="utf-8-sig") as f:
    current_hash = hash_source(f.read())

if current_hash != file_hashes.get("MyTable.al"):
    # File changed — re-chunk and re-embed
    ...

Development

# Clone and install
git clone https://github.com/andrijantasevski/bc-al-chunker.git
cd bc-al-chunker
uv sync --all-extras --group dev

# Run tests
uv run pytest tests/ -v

# Lint + format
uv run ruff check src/ tests/
uv run ruff format src/ tests/

# Type check
uv run mypy src/

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bc_al_chunker-0.1.3.tar.gz (36.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bc_al_chunker-0.1.3-py3-none-any.whl (26.3 kB view details)

Uploaded Python 3

File details

Details for the file bc_al_chunker-0.1.3.tar.gz.

File metadata

  • Download URL: bc_al_chunker-0.1.3.tar.gz
  • Upload date:
  • Size: 36.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bc_al_chunker-0.1.3.tar.gz
Algorithm Hash digest
SHA256 7ea6b0f0b3541f333ea525793c356a32bfbb3dd8a75fd46a8800c3b6b67d1f23
MD5 f75e9f43913035d7cb326d44cc90b01a
BLAKE2b-256 4a39123455063d055b269046fcd85c764cbc3a62e0e033a217b0bf1ec8718ece

See more details on using hashes here.

File details

Details for the file bc_al_chunker-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: bc_al_chunker-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 26.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bc_al_chunker-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 28c13ff97c59dde7f64b875003acabf4d6a486bfadfdc0620d8d0beaad6259bc
MD5 c7bd57508a55ac8ff5b05239d53558a4
BLAKE2b-256 8ecb648b9638846380d92297b20d04a7fbb99ca00af874e5f125c0f79d55b132

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page