Skip to content
edujbarriosPublic

About

Low-power, offline retrieval (RAG) for disaster and infrastructure-failure guidance.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

11 stars

Watchers

0 watching

Forks

Repository files navigation

LastLight

PyPI version Latest GitHub release Python versions

A stdlib-only Python library for low-power, offline retrieval in disaster and infrastructure-failure scenarios.

Most modern AI systems assume that connectivity, cloud compute, large models and abundant power are available. LastLight explores the reverse case: how much useful, auditable assistance can remain available when the infrastructure itself is unreliable?

LastLight retrieves practical knowledge from local Markdown/ZIP packs, exposes source passages and ranking metadata, adapts retrieval strategy to resource policy, and refuses when the available evidence is too weak to support an answer.

No cloud API. No embeddings. No vector database. No telemetry. No runtime dependencies outside the Python standard library.

Install

The current stable release is 0.3.1, available on PyPI. The PyPI and GitHub badges above resolve dynamically to the latest release.

python -m pip install --upgrade lastlight==0.3.1

Python 3.10+ is supported.

For development from a checkout:

python -m pip install -e .

Try it in 30 seconds

Download the small demo knowledge pack used by the examples:

curl -L \
  https://raw.githubusercontent.com/edujbarrios/lastlight/main/examplepack/lastlight-example-en.zip \
  -o lastlight-example-en.zip

Ask LastLight a normal question, much like you would ask an offline assistant:

from lastlight import LastLight

engine = LastLight("lastlight-example-en.zip")

question = (
    "The power has been out for several hours. "
    "How long will food stay safe in my refrigerator if I keep the door closed?"
)

result = engine.query(question)

if result.accepted:
    print(result.passage)
else:
    print("I don't have enough reliable local knowledge to answer that.")

Observed output:

Keep refrigerator and freezer doors closed as much as possible. As a reference, an unopened refrigerator keeps food cold for about 4 hours.

The interaction is intentionally assistant-like, but the answer is retrieved from local knowledge, not generated. The source remains available for inspection:

source = result.sources[0]

print(f"Source: {source.title}")
print(f"Path: {source.path}")
print(f"Confidence: {result.confidence}")
Source: Food safety during a power outage
Path: en/blackout/food-safety.md
Confidence: HIGH

Source paths are pack-relative and stable: the same document keeps the same logical path whether the pack is a directory, a ZIP file, or the ZIP is renamed. Pack identity metadata (pack_name, pack_version, pack_source, and pack_path) is preserved consistently whether one pack or several packs are mounted. Once the package and knowledge pack are local, querying does not require a network connection.

0.3.x: hybrid retrieval and configurable evidence calibration

The hybrid retrieval and calibrated decision features introduced in LastLight 0.3.0 remain supported in 0.3.1. This maintenance release also adds read-only external benchmark-v4 readiness checks and controlled version-comparison tooling in the source repository. The separate corpus-v4 results have not yet been reproduced, so 0.3.1 makes no new retrieval-performance claims. See the 0.3.1 release notes.

from lastlight import LastLight, CalibrationConfig, StrategyCalibration, SectionConfig

policy = CalibrationConfig(
    bm25=StrategyCalibration(
        min_strength=0.48,
        min_evidence_coverage=0.32,
        min_passage_coverage=0.28,
        min_decision_score=0.47,
        ambiguity_ratio=1.08,
    ),
)
engine = LastLight(
    "lastlight-example-en.zip",
    strategy="hybrid",            # lexical + BM25 reciprocal-rank fusion
    calibration=policy,
    sections=SectionConfig(min_chars=1500, max_chars=1800, overlap_chars=250),
)
result = engine.query("How long can a refrigerator keep food cold during an outage?")
print(result.accepted, result.confidence)
print(result.retrieval.strategy, result.decision.calibrated_score)
print(result.decision.refusal_code, result.decision.explanation)
for source in result.sources:
    print(source.path, source.section, source.backend_scores)

Hybrid scores are RRF scores, not BM25 or lexical scores. A recovered source still needs explicit passage evidence before acceptance. Public decision thresholds and weights are documented, and the optional section configuration preserves small-document behavior by default. Structured rejection codes complement, rather than replace, the legacy refusal_reason values. See the 0.2.2 → 0.3.0 migration guide.

Confidence-aware refusal

LastLight does not turn every weak match into an answer. The public result makes that decision explicit:

from lastlight import LastLight

engine = LastLight("lastlight-example-en.zip")
result = engine.query("How do I repair a diesel engine that will not start?")

print(result.accepted)
print(result.confidence)
print(result.passage)
print(result.refusal_reason)
False
None
None
no_matching_knowledge

Callers should use result.accepted as the answer boundary rather than treating every retrieval candidate as an answer.

Public Python API

The library is designed around a small public surface:

from lastlight import (
    LastLight,
    QueryResult,
    RetrievalMetadata,
    SourceResult,
    PackInfo,
    PackValidation,
    PackProvenance,
)

The main operations are:

engine.query(text)            # structured QueryResult
engine.search(text)           # ranked SourceResult values
engine.answer(text)           # formatted text response
engine.plan(text)             # adaptive retrieval metadata
engine.packs()                # mounted pack metadata
engine.validate_packs()       # structured validation reports
engine.verify_provenance()    # integrity/freshness reports

These package-root contracts are the intended integration boundary for UIs, benchmarks and other companion repositories. Explicit knowledge sources are validated at this public boundary, so missing, empty, or unsupported paths fail fast instead of looking like empty retrieval results. Public retrieval methods also require top_k to be an integer greater than or equal to 1. See Python API.

Compare retrieval strategies

LastLight exposes lexical, BM25, and deterministic hybrid retrieval plus an adaptive planner. The examples below are checked against the built wheel in CI.

from lastlight import LastLight

query = (
    "The power has been out for several hours. "
    "How long will food stay safe in my refrigerator if I keep the door closed?"
)

for strategy in ("lexical", "bm25"):  # raw-score comparisons; hybrid uses RRF instead
    result = LastLight(
        "lastlight-example-en.zip",
        strategy=strategy,
    ).query(query)

    source = result.sources[0]
    print(strategy, source.title, f"score={source.score:.3f}", source.confidence)
    print(result.passage)

Observed output:

lexical Food safety during a power outage score=4.918 HIGH
Keep refrigerator and freezer doors closed as much as possible. As a reference, an unopened refrigerator keeps food cold for about 4 hours.

bm25 Food safety during a power outage score=11.475 HIGH
Keep refrigerator and freezer doors closed as much as possible. As a reference, an unopened refrigerator keeps food cold for about 4 hours.

Score scales are strategy-specific, so lexical and BM25 numeric scores should not be compared directly.

Adaptive strategy selection

Adaptive mode does not blend lexical and BM25 scores. It deterministically chooses a retrieval strategy from query risk, operating mode and resource policy.

for mode in ("survival", "balanced", "accuracy"):
    plan = LastLight(
        "lastlight-example-en.zip",
        strategy="adaptive",
        mode=mode,
    ).plan(query)

    print(mode, plan.strategy, plan.effective_top_k, plan.risk, plan.reason)

Observed decisions:

survival lexical 2 normal survival mode caps retrieval cost
balanced bm25 3 normal balanced mode with sufficient detected resources
accuracy bm25 3 normal accuracy mode with no active resource constraint

Explicit resource budgets can change the plan:

plan = LastLight(
    "lastlight-example-en.zip",
    strategy="adaptive",
    mode="balanced",
    energy_budget_mwh=0.4,
).plan(query)

print(plan.strategy)
print(plan.effective_top_k)
print(plan.reason)
lexical
2
energy budget is at or below 0.5 mWh/query

See Adaptive Retrieval for the decision order and policy thresholds.

Use multiple knowledge packs

from lastlight import LastLight

engine = LastLight.from_packs(
    [
        "packs/water-en.zip",
        "packs/first-aid-en.zip",
        "packs/blackout-en.zip",
    ],
    strategy="adaptive",
    mode="balanced",
)

result = engine.query("Someone is bleeding heavily. What guidance is available?")

for source in result.sources:
    print(source.pack_name, source.path, source.confidence, source.score)

This is the intended integration point for projects such as lastlight-ui and lastlight-bench: import the library instead of spawning and parsing the CLI.

Large Markdown documents

Large manuals and field notes can remain as readable Markdown files instead of being manually split into many artificial documents. For documents of at least 4,000 characters, LastLight builds bounded internal ranking units for both lexical and BM25 retrieval while keeping the original file as the public source.

Markdown headings are used first when available:

# First Aid Field Guide

## Burns
...

## Severe bleeding

### Tourniquet use
...

A query about tourniquet use inherits the local First Aid Field Guide > Severe bleeding > Tourniquet use heading path. Oversized sections are then split into paragraph-aware ranking chunks of at most 1,800 characters with a small complete-paragraph overlap, so relevant evidence near a chunk boundary is not lost.

The same bounded ranking path now applies to a long document with no headings and to a guide that contains only one very large section. The final passage is selected from the winning internal unit, but the public source still points to the original Markdown file with the same path, body, pack metadata, and provenance.

Prepared internal units are reused across repeated queries while the corpus remains equivalent. This avoids re-parsing and re-chunking large manuals on every lexical or BM25 search; changing the document content or ranking metadata invalidates the cached preparation automatically.

The repository layer also reuses parsed Markdown documents and pack metadata while the mounted source is unchanged. ZIP packs normally reduce repeated queries to lightweight file-metadata checks instead of reopening, decompressing, hashing, and reparsing every Markdown body. Directory packs still perform a lightweight file metadata scan so edits, additions, removals, and renames are noticed automatically, but unchanged document bodies are not reread. Multi-pack composition is cached as well, so unchanged source documents are not repeatedly copied just to attach the same pack identity fields.

Short documents continue to use the original document-level ranking path, so existing small packs retain their established behavior and score characteristics.

Knowledge Pack Format v1

Distributable LastLight packs use an explicit versioned manifest contract. A typical pack looks like:

water-en.zip
├── lastlight-pack.json
└── en/
    └── water/
        ├── purification.md
        └── storage.md

A minimal manifest starts with:

{
  "format_version": 1,
  "name": "Emergency Water EN",
  "version": "1.0.0",
  "languages": ["en"],
  "license": "CC-BY-4.0",
  "source": "https://example.org/water"
}

format_version identifies the LastLight pack schema; version identifies the knowledge content release. Validation checks the schema version, required field types, semantic content version, language codes, provenance entries and optional SHA-256 fingerprints.

Pack loading rejects unsafe ZIP paths and duplicate normalized archive members, limits uncompressed Markdown sizes/counts, and applies a manifest size limit in both ZIP and directory packs. Directory manifests and Markdown documents may not escape the pack root through symlinks. Directory and ZIP packs also use matching Markdown-discovery rules, including case-insensitive .md extensions and ignoring hidden/__MACOSX metadata paths. Malformed pack data is reported through the public PackError hierarchy.

See Knowledge Packs and Knowledge Pack Provenance.

Core evaluation gate

LastLight ships a small deterministic evaluation suite inside the installed package. It covers answerable queries and expected refusals against the example corpus. CI runs the same suite on Python 3.10, 3.11 and 3.12 and blocks regressions below the core thresholds for top-1 accuracy, answer precision, refusal recall and answerable recall.

The larger stress, hardware and energy benchmark suites belong in lastlight-bench; the core suite is deliberately small and release-oriented.

From a checkout you can also run:

lastlight --knowledge examplepack/lastlight-example-en.zip --eval

Language behavior

engine = LastLight(
    "pack.zip",
    language="es",
)

An explicit language always wins. Without one, LastLight adopts a monolingual corpus language automatically and conservatively routes clear Spanish/English queries inside mixed corpora. Retrieved passages remain in their original language; LastLight does not silently translate them.

CLI utilities

The CLI is a first-party interface but remains secondary to the Python API. Installing from PyPI also installs the lastlight command.

lastlight --help
lastlight --knowledge pack.zip --validate-pack
lastlight --knowledge pack.zip --verify-provenance
lastlight --knowledge pack.zip --format sources "How can I make this water safer?"

Development and verification

git clone https://github.com/edujbarrios/lastlight.git
cd lastlight
python -m pip install -e .
python tools/check_core.py

CI tests Python 3.10, 3.11 and 3.12, runs the core evaluation gate, builds wheel and source distributions, validates package metadata, installs the built wheel in isolation, verifies packaged resources and executes the documented library behavior.

Research direction

LastLight treats offline intelligence as a systems problem rather than a model-size competition:

How much useful, trustworthy assistance can be preserved per unit of compute, memory, energy and stored knowledge when external infrastructure is unavailable?

The project is intended to make that trade-off measurable and auditable rather than hiding it behind a remote service.

Ecosystem direction

lastlight-ui ──────► lastlight
lastlight-bench ───► lastlight
other integrations ► lastlight

Knowledge packs, pack-authoring tools and a future catalog can evolve independently around the Pack Format v1 contract. See Ecosystem.

Docs

License

Mozilla Public License 2.0.

About

Low-power, offline retrieval (RAG) for disaster and infrastructure-failure guidance.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

11 stars

Watchers

0 watching

Forks

Contributors

Languages