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
sourcethat is not anhttp://orhttps://URL is a directory holdingunified_bank.jsonandshared_detector.bin(for exampleprojects/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 mirrorhttps://cdn.jsdmirror.com/npm/lmfpd-data@latest/. Onlybundle.jsonand 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 isttlseconds old or more (0 checks on every load), a background thread fetchesbundle.jsonand, 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 raiseslmfpd: cannot download reference data from ...when every source fails.
- With a cache,
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: sendcountchallenges and score the replies.apiisresponses,chat_completions, ormessages.timeoutlimits the wait for the first SSE byte or the whole JSON reply;timeout=0disables the timer. This SDK cannot cancel requests, so with0a server that connects but never sends its first byte (or never finishes a JSON reply) blocksdetectandrequest_sampleforever.on_samplereports 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 bodydetectsends 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:
queuedfor every index (0 tocount - 1) before any request, then per samplewaitingwhen its request starts,streamingat the first SSE byte, an event each time the accepted text changes, and last its finalcomplete,truncated, orfailedsample, equal toresult["samples"][index]. States may repeat. first_byte_msis the time from the request start to the first SSE byte, or to the parsed JSON reply;Nonewhen 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_samplepropagates out ofdetect; 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| lmfpd-0.2.0.tar.gz | 61.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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.