
Outil unique pour la prise d'empreintes des grands modèles de langage basée sur leurs tokeniseurs et leur comportement.

Établit l'empreinte de la pile de service derrière un point de terminaison de chat compatible OpenAI.
Il enregistre la comptabilité du tokenizer, le décalage du template caché, la prose de validation, la forme JSON/SSE, l'acceptation des rôles, les limites nommées et les en-têtes de passerelle. Il ne charge pas de poids, ne stocke pas de clés API et ne demande pas au modèle qui il est.
| Import | lm_fingerprint |
| CLI | lm-fingerprint |
| Description | Sondes d'infrastructure versionnées contre /chat/completions. Bibliothèque synthétique hors ligne. Vérification locale du tokenizer Hugging Face (sans poids). |
| Ce que ce n'est pas | Pas une attaque par gradients. Pas un jailbreak. Pas une identité par carte d'encodage (tokprint). Pas « quel binaire est sur ce port » (Julius). |
Usage autorisé uniquement : points de terminaison dont vous êtes propriétaire, laboratoires que vous contrôlez, programmes de bug bounty dans le périmètre, tests d'intrusion écrits. Voir SECURITY.md.
--api-key-envdésigne une variable d'environnement. La clé n'est jamais journalisée.
Le chat compatible OpenAI est un protocole courant. vLLM, SGLang, une passerelle de laboratoire et un revendeur peuvent tous parler /chat/completions. Le champ model est une étiquette. L'hôte peut changer le backend, envelopper le tokenizer ou placer un routeur devant. Les sondes de personnalité bougent quand le prompt système bouge.
Ce qui reste en place, c'est la plomberie : comment le serveur compte les jetons, combien de jetons de template cachés il ajoute, la prose écrite par son validateur, le dialecte SSE, quels rôles il accepte, le nombre qu'il annonce quand max_tokens est absurde, les en-têtes Server / fournisseur.
C'est la question à laquelle répond cet outil : quelle pile traite ce chemin de chat ? Pas quel checkpoint, pas quel processus est lié au port, pas qui a écrit une complétion.
ModelPrint a posé la même question dans un navigateur avec neuf sondes. Ce paquet en est la forme CLI et bibliothèque : sondes versionnées, bibliothèque enrôlable, preuves par paires, dérive, et un chemin tokenizer local qui ne fait jamais appel à une API d'inférence hébergée.
Une empreinte est un enregistrement JSON versionné des values de sondes. Chaque sonde doit renvoyer la même valeur lorsque la même pile est interrogée deux fois. Les horodatages, identifiants de requête, latences et clés sont supprimés.
Les comptes de tokenizer réutilisent les textes de ModelPrint 1.0.0 (MIT, unclecode/modelprint), octet pour octet, moins une ligne de base d'un caractère "a" pour qu'un template caché s'annule. Ces quatre nombres sont une projection de la carte d'encodage (T). Des comptes égaux ne signifient pas (T_1 \equiv T_2). GPT-2 et OPT entrent en collision sur ceux-ci ; le hash du pipeline rust, non.
hf_identity (chemin HF local uniquement) est le SHA-256 de cet engagement de pipeline plus le hash du chat-template. Même (T) et même template correspondent (tiny-gpt2 / gpt2). C'est famille + template, pas un checkpoint.
Disposition :
src/lm_fingerprint/
cli.py argparse. --api-key-env only. No --api-key.
client.py HttpTransport, InProcessTransport, redact_secrets
probes.py 23 ProbeSpec rows (tokenizer / errors / shape / roles / limits / proxy)
engine.py run suite → Fingerprint
schema.py schema_version=1, probe_suite_version=lm-fingerprint-probes-v1
similarity.py weighted exact-match, coverage, confidence, library rank, drift
library.py enroll / match. Packaged rows are synthetic fixtures
synthetic.py in-process stacks for tests and the shipped library
hf.py local AutoTokenizer + named validation profile. No weights
localmap.py encode-pipeline-v2 commitment (rust tokenizer.json)
proof.py measured ModelPrint 9-probe identity
report.py text reports
data/library.json
Deux chemins vers la même suite de sondes :
live endpoint local Hub tokenizer (optional [hf])
| |
HttpTransport.chat HfChatTransport
POST /chat/completions encode / chat_template for counts
| synthetic profile for errors/shape
+------------------+----------------------+
|
engine.run_probes
|
Fingerprint (JSON, keyless)
|
compare | match | drift | enroll
Rédaction de sondes : docs/PROBES.md. Carte des modules : docs/ARCHITECTURE.md.
Une correspondance, c'est une infrastructure partagée. Un laboratoire peut servir deux checkpoints sur une même pile. Un routeur peut répondre avec son propre wrapper d'erreurs. L'accord sur les comptes n'est pas l'identité de (T).
Python 3.10+. Le runtime principal est la bibliothèque standard.
git clone https://gitlab.com/WattoCyber/lm-fingerprint.git
cd lm-fingerprint
pip install -e ".[dev]"
PYTHONPATH=src python3 scripts/repro.py
Attendez-vous à REPRO_OK. Ce contrôle est hors ligne : pas de GPU, pas d'API de production. Les tests HF locaux sont ignorés si le tokenizer n'est pas en cache.
pip install -e ".[hf]" # transformers, for fingerprint-hf / verify-hf
lm-fingerprint list-probes
lm-fingerprint list
list affiche les piles fournies : openai-chat-strict, glm-compat, router-openai, permissive-compat. Ce sont des maquettes, pas des fournisseurs scrapés. Comparez deux d'entre elles sans réseau :
from lm_fingerprint.client import InProcessTransport
from lm_fingerprint.engine import fingerprint_endpoint
from lm_fingerprint.similarity import compare_fingerprints
from lm_fingerprint.synthetic import SYNTHETIC_STACKS, handler_for
def fp(stack_id):
t = InProcessTransport(handler_for(stack_id), headers=SYNTHETIC_STACKS[stack_id].headers)
return fingerprint_endpoint(t, model=f"synthetic/{stack_id}", base_url=f"inprocess://{stack_id}")
print(compare_fingerprints(fp("openai-chat-strict"), fp("router-openai"))["note"])
Même tokenizer de mots, erreurs et en-têtes différents. Le score se situe autour de 0.58. Les quatre comptes de ModelPrint correspondent ; cet outil ne qualifie pas cela de pile partagée.
lm-fingerprint fingerprint \
--base-url https://your-lab.example/v1 \
--model the-label \
--api-key-env OPENAI_API_KEY \
--out lab.json
lm-fingerprint match --fingerprint lab.json
lm-fingerprint enroll --fingerprint lab.json --stack-id my-lab --library my-lib.json
Ne passez pas la clé en ligne de commande. Ne scrapez pas les API de production pour remplir une bibliothèque publique.
lm-fingerprint fingerprint-hf --model sshleifer/tiny-gpt2 --local-files-only
lm-fingerprint verify-hf --local-files-only
Les comptes proviennent de apply_chat_template lorsqu'un template existe, sinon de encode(..., add_special_tokens=False). La taxonomie d'erreurs provient d'un profil synthétique nommé (par défaut openai-chat-strict) afin que l'exécution reste hors ligne. Les identifiants distants (https://, api://, …) sont rejetés.
lm-fingerprint list-probes
lm-fingerprint list [--library PATH]
lm-fingerprint fingerprint --base-url URL --model MODEL --api-key-env VAR [--out PATH]
lm-fingerprint fingerprint-hf --model HANDLE [--profile NAME] [--local-files-only]
lm-fingerprint verify-hf [--models a,b,c] [--local-files-only]
lm-fingerprint prove [--local-files-only]
lm-fingerprint compare --a a.json --b b.json
lm-fingerprint match --fingerprint a.json [--library PATH]
lm-fingerprint drift --baseline old.json --current new.json
lm-fingerprint enroll --fingerprint a.json --stack-id ID --library PATH
lm-fingerprint export --fingerprint a.json
Il n'y a pas d'option --api-key. Les anciens JSON wireprint-fingerprint / stackprint-fingerprint se chargent toujours.
from lm_fingerprint import (
StackLibrary,
compare_fingerprints,
fingerprint_endpoint,
fingerprint_hf,
match_library,
)
from lm_fingerprint.client import HttpTransport
fp = fingerprint_endpoint(HttpTransport(url, api_key=key), model="x", base_url=url)
hit = match_library(fp, StackLibrary.load())
local = fingerprint_hf("sshleifer/tiny-gpt2", local_files_only=True)
schema_version = 1, probe_suite_version = lm-fingerprint-probes-v1.
Comparaison : correspondance exacte pondérée sur les sondes qui sont ok et stable des deux côtés. La confiance est score × coverage. La correspondance de bibliothèque rapporte certainty (exact / strong / possible / unknown) et la marge par rapport au second voisin.
Relancez :
lm-fingerprint prove --local-files-only
Même reliquat que ModelPrint (neuf sondes dans probes/index.js). Leur README est la règle de notation : des empreintes correspondantes prouvent une infrastructure partagée, pas une identité.
Compte rendu : docs/PROOF.md.
fingerprint-hf ne charge jamais de poids et n'appelle jamais l'inférence hébergée.--api-key-env. Les artefacts ne doivent pas contenir sk- ni Authorization.scripts/repro.py est le contrôle de validation du produit. Ne prétendez pas au vert sauf si vous venez de l'exécuter.MIT. Voir LICENSE. Les textes du tokenizer ModelPrint restent sous licence MIT (unclecode/modelprint) et octet pour octet.
| Vous détenez | Ce que vous pouvez observer | Ce que fait cet outil |
|---|
| Une URL de base compatible OpenAI autorisée + clé | Chat HTTP : usage, erreurs, en-têtes, SSE | fingerprint / compare / match / drift |
| Fichiers tokenizer locaux | encode et chat_template | fingerprint-hf / verify-hf. Les poids restent sur le disque |
| Rien qu'un hôte:port | Bannière / /health / /api/tags | Hors périmètre. Utilisez Julius |
| Champ | Signification |
|---|
kind | lm-fingerprint |
target.model / target.base_url_host | Ce qui a été ciblé. Hôte uniquement ; pas de clé |
features.tokenizer_norm | Quatre comptes de classe ModelPrint (anglais, chinois, code, emoji) |
features.template_offset | Taille du template de service caché |
probes.<id>.value | Cellule comparable. Doit être stable |
probes.<id>.weight | Contribution à la similarité |
keys_stored | Toujours false |
weights_loaded | Toujours false |
| Test | ModelPrint | Cet outil |
|---|
Même tokenizer, pile différente (openai-chat-strict vs router-openai) | tokenizer 4/4 + correspondance de template | score 0.58, qualifié de comptabilité partagée |
gpt2 vs facebook/opt-125m | quatre comptes et template entrent en collision | encode_commitment les distingue |
| Qwen2 / 2.5 / 3 | tokenizer 4/4 en collision ; template les distingue | hf_identity les distingue |
tiny-gpt2 vs gpt2 | correspondance (même (T)) | correspondance (requise) |
| Ensemble de sondes | 9 | 23 + engagements HF encode/template |
logprobs, forme du flux, rôle système | listés comme idées | livrés |
| Outil | Objet |
|---|
| ModelPrint | Même reliquat, navigateur, neuf sondes |
| tokprint | ID local de famille par carte d'encodage. Se trouve dans gradient-untangler |
| gradient-untangler | Gradients à travers les poids locaux |
| Julius | Quel logiciel serveur est sur un port |
| TokenPrint / LLMmap / UTF | Texte généré, lignée, ou jetons implantés |