
Fingerabdrücke von OpenAI-kompatiblen LLMs anhand von Tokenizer- und Verhaltenssignalen erstellen.

Fingerprintet den Serving-Stack hinter einem OpenAI-kompatiblen Chat-Endpunkt.
Es erfasst Tokenizer-Abrechnung, versteckten Template-Offset, Validierungs-Prosa, JSON/SSE-Form, Rollenakzeptanz, benannte Limits und Gateway-Header. Es lädt keine Gewichte, speichert keine API-Schlüssel und fragt das Modell nicht, wer es ist.
| Import | lm_fingerprint |
| CLI | lm-fingerprint |
| Was | Versionierte Infrastruktur-Probes gegen /chat/completions. Offline-Synthetik-Bibliothek. Lokale Hugging-Face-Tokenizer-Verifikation (keine Gewichte). |
| Was es nicht ist | Kein Gradienten-Angriff. Kein Jailbreak. Keine Encode-Map-Identität (tokprint). Nicht „welche Binärdatei auf diesem Port läuft" (Julius). |
Nur zur autorisierten Nutzung: Endpunkte, die Ihnen gehören, Labs, die Sie kontrollieren, Bounties im Geltungsbereich, schriftliche Pentests. Siehe SECURITY.md.
--api-key-envbenennt eine Umgebungsvariable. Der Schlüssel wird nie protokolliert.
OpenAI-kompatibler Chat ist ein verbreiteter Draht. vLLM, SGLang, ein Lab-Gateway und ein
Reseller können alle /chat/completions sprechen. Das Feld model ist ein Label.
Der Host kann das Backend austauschen, den Tokenizer umhüllen oder einen Router
davor setzen. Persönlichkeits-Probes verschieben sich, wenn sich der System-Prompt verschiebt.
Was konstant bleibt, ist die Infrastruktur: wie der Server Tokens zählt, wie viele versteckte
Template-Tokens er hinzufügt, die Prosa, die sein Validator geschrieben hat, der SSE-Dialekt,
welche Rollen er akzeptiert, die Zahl, die er nennt, wenn max_tokens absurd ist,
die Server- / Provider-Header.
Das ist die Frage, die dieses Werkzeug beantwortet: Welcher Stack verarbeitet diesen Chat-Pfad? Nicht welcher Checkpoint, nicht welcher Prozess an den Port gebunden ist, nicht wer eine Completion geschrieben hat.
ModelPrint stellte dieselbe Frage in einem Browser mit neun Probes. Dieses Paket ist die CLI- und Bibliotheksform: versionierte Probes, eintragbare Bibliothek, paarweise Evidenz, Drift und ein lokaler Tokenizer-Pfad, der nie eine gehostete Inference-API aufruft.
Ein Fingerprint ist ein versionierter JSON-Datensatz von Probe-values. Jede Probe
muss denselben Wert zurückgeben, wenn derselbe Stack zweimal getroffen wird. Zeitstempel,
Request-IDs, Latenz und Schlüssel werden entfernt.
Tokenizer-Zählungen verwenden ModelPrint 1.0.0-Texte (MIT, unclecode/modelprint),
byte-exakt, abzüglich einer Ein-Zeichen-"a"-Basislinie, damit sich ein verstecktes Template
aufhebt. Diese vier Zahlen sind eine Projektion der Encode-Map (T).
Gleiche Zählungen bedeuten nicht (T_1 \equiv T_2). GPT-2 und OPT kollidieren
bei ihnen; der Rust-Pipeline-Hash nicht.
hf_identity (nur lokaler HF-Pfad) ist SHA-256 dieser Pipeline-Commitment
plus des Chat-Template-Hashs. Gleiches (T) und gleiches Template stimmen überein
(tiny-gpt2 / gpt2). Das ist Familie + Template, kein Checkpoint.
Layout:
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
Zwei Wege in dieselbe Probe-Suite:
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
Probe-Erstellung: docs/PROBES.md. Modulübersicht: docs/ARCHITECTURE.md.
| Was Sie haben | Was Sie beobachten können | Was dieses Tool tut |
|---|---|---|
| Eine autorisierte OpenAI-kompatible Basis-URL + Schlüssel | Chat-HTTP: Nutzung, Fehler, Header, SSE | fingerprint / compare / match / drift |
| Lokale Tokenizer-Dateien | encode und chat_template | fingerprint-hf / verify-hf. Gewichte bleiben auf der Festplatte |
| Nichts außer einem Host:Port | Banner / /health / /api/tags | Außerhalb des Geltungsbereichs. Verwenden Sie Julius |
Eine Übereinstimmung bedeutet gemeinsame Infrastruktur. Ein Labor kann zwei Checkpoints auf einem Stack bedienen. Ein Router kann mit seinem eigenen Fehler-Wrapper antworten. Übereinstimmung der Zählung ist nicht Identität von (T).
Python 3.10+. Die Kern-Laufzeitumgebung ist die Standardbibliothek.
git clone https://gitlab.com/WattoCyber/lm-fingerprint.git
cd lm-fingerprint
pip install -e ".[dev]"
PYTHONPATH=src python3 scripts/repro.py
Erwarte REPRO_OK. Dieses Gate ist offline: keine GPU, keine Produktions-APIs.
Lokale HF-Tests werden übersprungen, wenn der Tokenizer nicht im Cache liegt.
pip install -e ".[hf]" # transformers, for fingerprint-hf / verify-hf
lm-fingerprint list-probes
lm-fingerprint list
list gibt die paketierten Stacks aus: openai-chat-strict, glm-compat,
router-openai, permissive-compat. Das sind Fixtures, keine gescrapten
Provider. Vergleiche zwei davon ohne Netzwerk:
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"])