Fingerpoint Detector
SDK

SDK

Score replies, generate challenges, or check an API channel from TypeScript, Rust, Go, and Python programs.

The SDKs put the detector inside your own programs. You can score replies you have already collected, generate random challenges, or send one round of challenges to an API channel and score the replies. All four languages offer the same features:

  • Offline scoring of one to three replies, with the same result as fpd --input FILE --json.
  • Challenge generation with the generator the website and command line use. Each challenge picks its language and wording at random and asks for 292–332 integers.
  • Live detection of one round, with the same sampling rules as command-line detection, over Responses, Chat Completions, or Messages.

The SDKs do not run the tokenizer probe and do not run several rounds; for more rounds, call them in a loop.

Install

The TypeScript SDK is on npm, the Rust SDK on crates.io, and the Python SDK on PyPI. The Go SDK is a module in the repository:

npm install lmfpd-sdk

The TypeScript SDK runs on Node.js 22+ and Bun 1.4.4+ and has no dependencies once built. Every Rust call is blocking. The Go SDK needs Go 1.23+. The Python SDK needs Python 3.10+ and depends on httpx, zstandard, and numpy.

No package contains reference data. On the first load the SDK downloads about 3.6 MB of compressed data and caches it. Later updates to the reference bank need no SDK upgrade; see Reference data.

Score replies

analyze takes one to three replies. Each has the reply text and expected_count, the number of integers its challenge asked for:

import { loadDetector } from 'lmfpd-sdk'

const detector = await loadDetector()
const analysis = detector.analyze([
  { text: reply1, expected_count: 300 },
  { text: reply2, expected_count: 311 },
  { text: reply3, expected_count: 296 },
])
console.log(analysis.decision, analysis.prediction, analysis.probability)

Load once and score as often as you need. Loading parses about 4.5 MB of detector data and takes much longer than one score (see Performance), so do not reload it for every score.

Check an API channel

detect generates challenges, sends them in parallel, checks the replies, and scores them. A failed request does not raise an error; it is recorded in that sample's error and in the result's error:

const result = await detector.detect({
  baseUrl: 'https://api.example.com/v1', apiKey: process.env.API_KEY!, model: 'gpt-5.6-sol', api: 'chat_completions',
})
console.log(result.error ?? result.analysis!.prediction)

detect raises an error only for an invalid configuration, before it sends any request. When error is empty, analysis always has a ranking. See API for every setting.

Reading results

decision tells you what the result contains:

decisionWhenResult
scoredAll three replies are validA full ranking. When the calibration matches the detector, probability is the confidence of the top candidate
partialOnly one or two replies are valid, in relaxed modeA ranking only; probability is null
unscorableNo reply is valid, or fewer than three in strict modeNo ranking
not_modelThe numbers do not change the way a language model's doNo ranking; see the language-model reply check

A reply is valid when it contains at least max(80, ceil(expected_count × 0.55)) integers from 1 to 355. Relaxed mode cuts a reply down to expected_count integers; strict mode never cuts and needs three complete replies.

A ranking is not proof of identity

The result is a closed-set ranking within the reference bank. A model outside the bank still gets a closest candidate, and probability compares only the models in the bank. To judge whether a channel is trustworthy, fix the request settings, repeat several rounds, and combine the result with other evidence.

Performance

Measured on an Apple Silicon Mac. Scoring times are the median of 20 runs after a warm-up:

LanguageLoad the repository's data/Score three repliesScore one reply
Rust24 ms1.7 ms0.5 ms
Go31 ms2.3 ms0.7 ms
TypeScript (Node or Bun)30 ms6.5 ms2.2 ms
Python 3.1337 ms1.9 ms0.65 ms

The Python SDK computes with NumPy, which sums in a different order from the other languages; scores differ by about 10⁻¹⁵. Live detection time is dominated by the model's generation and does not depend on the language.

Consistent results

All four implementations are checked against the same test data: number parsing, request construction, 13 kinds of streaming and JSON responses, and 12 scoring cases. Scores must agree within a relative error of 10⁻⁹, and text and states must match exactly. In the repository, bun run sdk:check checks the four languages in turn.

Last updated on

On this page