
Identifica modelos LLM compatíveis com OpenAI a partir de sinais do tokenizer e de comportamento.

Identifica a stack de serving por trás de um endpoint de chat compatível com OpenAI.
Registra a contabilização do tokenizer, o deslocamento oculto do template, a prosa de validação, a forma JSON/SSE, a aceitação de roles, os limites nomeados e os cabeçalhos do gateway. Não carrega pesos, não armazena chaves de API, nem pergunta ao modelo quem ele é.
| Import | lm_fingerprint |
| CLI | lm-fingerprint |
| O que é | Sondas de infraestrutura versionadas contra /chat/completions. Biblioteca sintética offline. Verificação local de tokenizer do Hugging Face (sem pesos). |
| O que não é | Não é um ataque de gradiente. Não é um jailbreak. Não é identidade de mapa de codificação (tokprint). Não é "qual binário está nesta porta" (Julius). |
Apenas para uso autorizado: endpoints que você possui, laboratórios que você controla, bounties dentro do escopo, pentests com autorização por escrito. Consulte SECURITY.md.
--api-key-envnomeia uma variável de ambiente. A chave nunca é registrada em log.
O chat compatível com OpenAI é um protocolo comum. vLLM, SGLang, um gateway de laboratório e um
revendedor podem todos falar /chat/completions. O campo model é um rótulo.
O host pode trocar o backend, envolver o tokenizer ou colocar um roteador na
frente. Sondas de personalidade mudam quando o system prompt muda.
O que permanece fixo é a encanação: como o servidor conta tokens, quantos tokens
ocultos de template ele adiciona, a prosa que seu validador escreveu, o dialeto SSE,
quais roles ele aceita, o número que ele nomeia quando max_tokens é absurdo,
os cabeçalhos Server / do provedor.
Essa é a pergunta que esta ferramenta responde: qual stack está tratando este caminho de chat? Não qual checkpoint, não qual processo está vinculado à porta, não quem escreveu uma completion.
O ModelPrint fez a mesma pergunta em um navegador com nove sondas. Este pacote é a forma de CLI e biblioteca: sondas versionadas, biblioteca registrável, evidência pareada, drift e um caminho de tokenizer local que nunca chama uma Inference API hospedada.
Uma fingerprint é um registro JSON versionado de values de sondas. Cada sonda
deve retornar o mesmo valor quando a mesma stack é acessada duas vezes. Timestamps,
request ids, latência e chaves são removidos.
As contagens do tokenizer reutilizam os textos do ModelPrint 1.0.0 (MIT, unclecode/modelprint),
byte a byte, menos uma linha de base "a" de um caractere para que um template oculto
se cancele. Esses quatro números são uma projeção do mapa de codificação (T).
Contagens iguais não significam (T_1 \equiv T_2). GPT-2 e OPT colidem
nelas; o hash do pipeline rust não.
hf_identity (apenas caminho HF local) é o SHA-256 desse compromisso de pipeline
mais o hash do chat-template. Mesmo (T) e o mesmo template correspondem
(tiny-gpt2 / gpt2). Isso é família + template, não um 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
Dois caminhos para a mesma suíte de sondagem:
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
Autoria de probes: docs/PROBES.md. Mapa de módulos: docs/ARCHITECTURE.md.
| Você possui | O que você pode observar | O que esta ferramenta faz |
|---|---|---|
| Uma base URL OpenAI-compat autorizada + chave | HTTP de chat: uso, erros, cabeçalhos, SSE | fingerprint / compare / match / drift |
| Arquivos de tokenizer locais | encode e chat_template | fingerprint-hf / verify-hf. Os pesos permanecem no disco |
| Nada além de um host:port | Banner / /health / /api/tags | Fora do escopo. Use Julius |
Uma correspondência é infraestrutura compartilhada. Um laboratório pode servir dois checkpoints em uma única stack. Um roteador pode responder com seu próprio wrapper de erro. A concordância de contagem não é a identidade de (T).
Python 3.10+. O runtime principal é a biblioteca padrão.
git clone https://gitlab.com/WattoCyber/lm-fingerprint.git
cd lm-fingerprint
pip install -e ".[dev]"
PYTHONPATH=src python3 scripts/repro.py
Espere REPRO_OK. Esse gate é offline: sem GPU, sem APIs de produção.
Os testes locais do HF são ignorados se o tokenizer não estiver em cache.
pip install -e ".[hf]" # transformers, for fingerprint-hf / verify-hf
lm-fingerprint list-probes
lm-fingerprint list
list imprime as stacks empacotadas: openai-chat-strict, glm-compat,
router-openai, permissive-compat. Esses são fixtures, não provedores
coletados por scraping. Compare dois deles sem rede:
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"])
Mesmo tokenizador de palavras, erros e cabeçalhos diferentes. A pontuação fica em torno de
0.62. As quatro contagens do ModelPrint coincidem; esta ferramenta não considera isso uma
stack compartilhada.