Skip to main content

lmfpd (Python)

Language-model fingerprint detection: score saved replies against the lmfpd reference bank, generate challenge prompts, and run one live detection round against an OpenAI- or Anthropic-compatible API.

Documentation: https://lm.ikale.io/docs/en/sdk

Install and build

pip install lmfpd
uv build                   # from sdk/python; writes the sdist and wheel to dist/

Python 3.10 or newer. Dependencies: httpx (with SOCKS support), zstandard, and numpy.

Loading reference data

lmfpd.load(source=DEFAULT_DATA_URLS, cache=None, ttl=86400.0) is the one entry point.

  • Local directory: a single source that is not an http:// or https:// URL is a directory holding unified_bank.json and shared_detector.bin (for example projects/data). It is read as is, with no cache and no network.
  • URLs: sources are tried in order: https://lm.ikale.io/data/, then the mainland-China mirror https://cdn.jsdmirror.com/npm/lmfpd-data@latest/. Only bundle.json and the detector are downloaded (about 3.6 MB compressed, 4.5 MB uncompressed). Files are not checksum-verified.
  • Cache: one file, cache, defaulting to <OS cache dir>/lmfpd/data.bin ($XDG_CACHE_HOME, ~/Library/Caches, %LOCALAPPDATA%, or ~/.cache). Its modification time is the last check against the sources.
    • With a cache, load() returns it at once. When the last check is ttl seconds old or more (0 checks on every load), a background thread fetches bundle.json and, only if the detector changed, the new detector for the next load. Failures there are ignored.
    • Without a cache, or with a damaged one, load() downloads before returning and raises lmfpd: cannot download reference data from ... when every source fails.

HTTP requests honor HTTPS_PROXY, HTTP_PROXY, ALL_PROXY (including socks5h://), and NO_PROXY.

API

Results are plain dicts with the same keys in every lmfpd SDK; lmfpd.Analysis, Sample, and DetectResult are their TypedDicts. Invalid input raises lmfpd.LmfpdError.

  • load(source, cache, ttl) -> Detector: load reference data from URLs (cached) or a local directory.
  • Detector.built_at: the data build identifier.
  • Detector.analyze(outputs, strict=False) -> Analysis: score one to three {"text", "expected_count"} replies.
  • Detector.detect(base_url, api_key, model, *, api="responses", effort="", service_tier="default", stream=True, strict=False, count=3, parallel=3, timeout=120.0, on_sample=None) -> DetectResult: send count challenges and score the replies. api is responses, chat_completions, or messages. timeout limits the wait for the first SSE byte or the whole JSON reply; timeout=0 disables the timer. This SDK cannot cancel requests, so with 0 a server that connects but never sends its first byte (or never finishes a JSON reply) blocks detect and request_sample forever. on_sample reports progress (see below).
  • generate_challenges(count=3) -> list[Challenge]: random challenge prompts.
  • parse_numbers(text) -> list[int]: the numbers 1 to 355 of the longest run in a reply.
  • build_request(config, prompt) -> dict: the URL, headers, and body detect sends for one prompt.
  • request_sample(config, challenge) -> Sample: send one challenge and check the reply.

config dicts use the detect keyword names (base_url, api_key, model, api, ...).

import lmfpd

detector = lmfpd.load()
result = detector.detect("https://api.openai.com/v1", "sk-...", "gpt-5")
print(result["analysis"]["prediction"] if result["analysis"] else result["error"])

Progress

on_sample(index, sample) receives live Sample snapshots; index is the position in result["samples"].

def on_sample(index: int, sample: lmfpd.Sample) -> None:
    print(index, sample["state"], sample["count"], sample["first_byte_ms"])

result = detector.detect("https://api.openai.com/v1", "sk-...", "gpt-5", on_sample=on_sample)
  • Order: queued for every index (0 to count - 1) before any request, then per sample waiting when its request starts, streaming at the first SSE byte, an event each time the accepted text changes, and last its final complete, truncated, or failed sample, equal to result["samples"][index]. States may repeat.
  • first_byte_ms is the time from the request start to the first SSE byte, or to the parsed JSON reply; None when not reached.
  • Calls come from worker threads but never overlap, and each gets a fresh dict. A slow callback slows sampling.
  • An exception raised by on_sample propagates out of detect; it never turns into a failed sample.

Metadata

Release files for lmfpd 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lmfpd 0.2.0
File Size Uploaded Provenance
lmfpd-0.2.0.tar.gz 61.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for lmfpd 0.2.0
File Interpreter ABI Platform
lmfpd-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 93.1 kB

Release files / lmfpd-0.2.0.tar.gz

Download URL lmfpd-0.2.0.tar.gz
Size 61.0 kB
Tags Source
SHA-256 checksum
How to use checksums
63179ca749c72fb14998a0baa2f5d62efdacf3eb9be2a279ad3bd64fbca56b1a
BLAKE2b-256 checksum
How to use checksums
bd8d35e8d1190d30edb81c597439c8d2fc5722239b309d3e785f963f5d259702
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / lmfpd-0.2.0-py3-none-any.whl

Download URL lmfpd-0.2.0-py3-none-any.whl
Size 32.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
70de92b1de684e5fc1f7354c41c52b7232890d0dee0e1c1797ca03c0dfe4b849
BLAKE2b-256 checksum
How to use checksums
876276be8fef5dbb27dab51bd50db6c8318fa3554847ec3b7ed637992c791f8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for lmfpd 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page