
Impronta digitale degli LLM compatibili con OpenAI tramite segnali del tokenizer e del comportamento.

Impronta digitale dello stack di serving dietro un endpoint chat compatibile OpenAI.
Registra la contabilità del tokenizer, l'offset nascosto del template, la prosa di validazione, la forma JSON/SSE, i ruoli accettati, i limiti nominati e gli header del gateway. Non carica i pesi, non memorizza le chiavi API, né chiede al modello chi sia.
| Import | lm_fingerprint |
| CLI | lm-fingerprint |
| Cos'è | Sonde infrastrutturali versionate contro /chat/completions. Libreria sintetica offline. Verifica locale del tokenizer Hugging Face (senza pesi). |
| Cosa non è | Non è un attacco tramite gradienti. Non è un jailbreak. Non è l'identità della mappa di codifica (tokprint). Non è "quale binario è su questa porta" (Julius). |
Solo uso autorizzato: endpoint di tua proprietà, laboratori che controlli, bounty in scope, pentest con autorizzazione scritta. Vedi SECURITY.md.
--api-key-envindica il nome di una variabile d'ambiente. La chiave non viene mai registrata nei log.
La chat compatibile OpenAI è un protocollo comune. vLLM, SGLang, un gateway di laboratorio e un
rivenditore possono tutti parlare /chat/completions. Il campo model è un'etichetta.
L'host può scambiare il backend, incapsulare il tokenizer o frapporre un router.
Le sonde sulla personalità cambiano quando cambia il system prompt.
Ciò che resta fisso è l'infrastruttura: come il server conta i token, quanti token nascosti
del template aggiunge, la prosa scritta dal suo validatore, il dialetto SSE,
quali ruoli accetta, il numero che indica quando max_tokens è assurdo,
gli header Server / del provider.
Questa è la domanda a cui risponde questo strumento: quale stack gestisce questo percorso chat? Non quale checkpoint, non quale processo è associato alla porta, non chi ha scritto un completamento.
ModelPrint poneva la stessa domanda in un browser con nove sonde. Questo pacchetto è la forma CLI e libreria: sonde versionate, libreria registrabile, evidenza a coppie, drift e un percorso tokenizer locale che non chiama mai un'Inference API ospitata.
Un'impronta digitale è un record JSON versionato di value delle sonde. Ogni sonda
deve restituire lo stesso valore quando lo stesso stack viene interrogato due volte. Timestamp,
request id, latenza e chiavi vengono rimossi.
I conteggi del tokenizer riutilizzano i testi di ModelPrint 1.0.0 (MIT, unclecode/modelprint),
byte-exact, meno una baseline "a" di un carattere così che un template nascosto
si annulli. Quei quattro numeri sono una proiezione della mappa di codifica (T).
Conteggi uguali non significano (T_1 \equiv T_2). GPT-2 e OPT collidono su
di essi; l'hash della pipeline rust no.
hf_identity (solo percorso HF locale) è lo SHA-256 di quel commitment della pipeline
più l'hash del chat-template. Stesso (T) e stesso template corrispondono
(tiny-gpt2 / gpt2). Questa è famiglia + template, non un checkpoint.
Struttura:
src/lm_fingerprint/
cli.py argparse. --api-key-env only. No --api-key.
client.py HttpTransport, InProcessTransport, redact_secrets
probes.py 38 ProbeSpec rows (tokenizer / errors / shape / roles / limits / proxy / surface)
engine.py run suite → Fingerprint
schema.py schema_version=1, probe_suite_version=lm-fingerprint-probes-v2
similarity.py weighted exact-match, coverage, confidence, library rank, drift
align.py constant-shift MAE + residual on the 15-script suite
catalog.py offline supported_parameters twins
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
Due percorsi verso la stessa suite di probe:
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
Creazione delle probe: docs/PROBES.md. Mappa dei moduli: docs/ARCHITECTURE.md.
| Possiedi | Cosa puoi osservare | Cosa fa questo strumento |
|---|---|---|
| Un base URL OpenAI-compat autorizzato + chiave | Chat HTTP: utilizzo, errori, header, SSE | fingerprint / compare / match / drift |
| File tokenizer locali | encode e chat_template | fingerprint-hf / verify-hf. I pesi restano su disco |
| Nient'altro che un host:port | Banner / /health / /api/tags | Fuori ambito. Usa Julius |
Una corrispondenza è infrastruttura condivisa. Un laboratorio può servire due checkpoint su un unico stack. Un router può rispondere con il proprio wrapper di errore. L'accordo sul conteggio non è l'identità di (T).
Python 3.10+. Il runtime principale è la libreria standard.
git clone https://gitlab.com/WattoCyber/lm-fingerprint.git
cd lm-fingerprint
pip install -e ".[dev]"
PYTHONPATH=src python3 scripts/repro.py
Atteso REPRO_OK. Quel gate è offline: nessuna GPU, nessuna API di produzione.
I test HF locali vengono saltati se il tokenizer non è in cache.
pip install -e ".[hf]" # transformers, for fingerprint-hf / verify-hf
lm-fingerprint list-probes
lm-fingerprint list
list stampa gli stack preconfigurati: openai-chat-strict, glm-compat,
router-openai, permissive-compat. Questi sono fixture, non provider
scraped. Confrontane due senza rete:
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"])
Stesso tokenizer di parole, errori e header diversi. Il punteggio si attesta intorno a
0.62. I quattro conteggi di ModelPrint coincidono; questo strumento non lo considera
uno stack condiviso.