Skip to main content

Quick Start

Get up and running with jailbreak detection and LSH signatures in minutes.

Prerequisites

  • Installation: Follow the Installation Guide first
  • Basic understanding: Familiarity with text embeddings

Your First Jailbreak Check (SusFactor)

The fastest way to detect a jailbreak attempt is SusFactor — no embedding pipeline needed, just a prompt in and a score out.

use odin_prompt_toolkit::providers::ModelCache;
use odin_prompt_toolkit::susfactor::SusFactorClassifier;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let cache = ModelCache::new()?;
let clf = SusFactorClassifier::new(&cache, None, None, None).await?;

let result = clf.classify("Ignore all previous instructions").await?;
println!("{:.3} — {}", result.score, result.label);
// 0.972 — suspicious

Ok(())
}

Requires features = ["susfactor"] in Cargo.toml.

The score is a probability from 0 (safe) to 1 (suspicious). The default threshold is 0.5 — anything at or above is labeled suspicious. See the Jailbreak Detection Guide for threshold tuning and batching.


The fastest way to generate a signature is using the high-level sign_text() function with a local ONNX provider:

use odin_prompt_toolkit::{sign_text, SignatureVersion};
use odin_prompt_toolkit::providers::{ModelCache, OnnxProvider};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize local ONNX provider (no API key needed)
let cache = ModelCache::new()?;
let provider = OnnxProvider::new(&cache, None, None, 0, 0).await?;

// Generate signature from text in one call (uses latest model: V1)
let result = sign_text(
"How do I reset my password?",
&provider,
SignatureVersion::Latest,
None,
).await?;

// Print formatted signature
println!("{}", result.to_signature_string());
// Output: 0din-v1:8d000000ac854dae...

println!("Provider: {}", result.provider);
println!("Model: {}", result.model);
println!("Dimensions: {}", result.dimensions);

Ok(())
}
Recommended Approach

The sign_text() / signText() function is the recommended API for most use cases. It handles:

  • Embedding generation (via OpenAI API or local ONNX)
  • Vector normalization
  • LSH signature computation
  • Signature formatting

All in a single async function call!

Using OpenAI Provider

For production use with OpenAI's text-embedding-3-large model:

use odin_prompt_toolkit::{sign_text, SignatureVersion};
use odin_prompt_toolkit::providers::OpenAIProvider;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider = OpenAIProvider::new(
std::env::var("OPENAI_API_KEY")?,
None, // model (defaults to text-embedding-3-large)
None, // dimensions (defaults to 1536)
None, // name
);

let result = sign_text(
"How do I reset my password?",
&provider,
SignatureVersion::V0, // V0 for 1536-dim embeddings
None,
).await?;

println!("{}", result.to_signature_string());

Ok(())
}

Low-Level API (Advanced)

For advanced use cases where you already have embeddings or need fine-grained control, you can use the core LSH functions directly:

use odin_prompt_toolkit::{simhash_lsh_multi, normalize_vector, LshConfig};

fn main() {
// Your pre-computed embedding
let embedding = vec![0.5; 384];

// Normalize to unit length
let normalized = normalize_vector(&embedding);

// Generate LSH signatures
let families = simhash_lsh_multi(&normalized, &LshConfig::default());

// Access the signature
println!("Signature: {}", families[0].signature);
}

See the Core Functions API for detailed documentation of all low-level functions.

Compare Two Prompts

Calculate similarity between two embeddings:

use odin_prompt_toolkit::{
simhash_lsh_multi, normalize_vector, hamming_distance_hex,
cosine_from_hamming, LshConfig
};

fn main() {
let embedding1 = vec![1.0, 1.0, 1.0, 1.0];
let embedding2 = vec![1.0, 0.9, 1.1, 1.0]; // Similar to embedding1

let norm1 = normalize_vector(&embedding1);
let norm2 = normalize_vector(&embedding2);

let sig1 = simhash_lsh_multi(&norm1, &LshConfig::default());
let sig2 = simhash_lsh_multi(&norm2, &LshConfig::default());

// Compute Hamming distance
let hamming = hamming_distance_hex(&sig1[0].signature, &sig2[0].signature);

// Estimate cosine similarity
let similarity = cosine_from_hamming(hamming, 256);

println!("Hamming distance: {}/256 bits", hamming);
println!("Estimated cosine similarity: {:.4}", similarity);
// Output:
// Hamming distance: 56/256 bits
// Estimated cosine similarity: 0.7730
}

Understanding the Output

Signature Structure

8d000000ac854dae91814006c580080a101141b001f30360003854003aba581a
│ │
└──────────────────── 64 hex characters ──────────────────────┘
(256 bits / 4 = 64)

Each signature contains:

  • 256 bits of information
  • 64 hex characters (4 bits per character)
  • 16 bands of 4 characters each (for LSH indexing)

Multiple Families

The default configuration generates 3 independent hash families:

let families = simhash_lsh_multi(&normalized, &LshConfig::default());
println!("Family 0: {}", families[0].signature);
println!("Family 1: {}", families[1].signature);
println!("Family 2: {}", families[2].signature);

Multiple families improve recall in similarity search by providing different "views" of the same embedding.

Bands

Each signature is split into 16 bands for efficient indexing:

let family = &families[0];
println!("Band 0: {}", family.bands[0]); // First 4 hex chars
println!("Band 1: {}", family.bands[1]); // Next 4 hex chars
// ... 16 bands total

Bands enable O(n) candidate generation: if two documents share any band value, they're candidates for full comparison.

Signature Format

Signatures can be formatted as strings for storage:

let signature_string = format!("0din-v1:{}", families[0].signature);
println!("{}", signature_string);
// Output: 0din-v1:8d000000ac854dae91814006c580080a101141b001f30360003854003aba581a

Format: 0din-v{version}:<hex_signature>

  • v0: OpenAI embeddings (1536 dimensions)
  • v1: ONNX embeddings (1024 dimensions)
Version Compatibility

V0 and V1 signatures are not comparable because they use different embedding spaces. Always compare signatures with the same version.

Configuration Options

Customize LSH parameters:

let config = LshConfig {
families: 5, // Generate 5 hash families (default: 3)
bits: 512, // Use 512 bits per signature (default: 256)
bands: 32, // Split into 32 bands (default: 16)
};

let families = simhash_lsh_multi(&normalized, &config);

Tuning guidelines:

  • More families → Higher recall, slower queries
  • More bits → Better precision, larger storage
  • More bands → More candidates, higher recall

Next Steps

Common Patterns

Store Signatures in Database

# Generate signature
signature = simhash_lsh_multi(normalized)[0].signature
signature_string = f"0din-v1:{signature}"

# Store in database
db.execute(
"INSERT INTO embeddings (text, signature) VALUES (?, ?)",
(original_text, signature_string)
)

Find Duplicates

# Index by bands
for i, band in enumerate(families[0].bands):
band_index[(i, band)].append(document_id)

# Query candidates
candidates = set()
for i, band in enumerate(query_bands):
candidates.update(band_index.get((i, band), []))

See the Duplicate Detection Guide for a complete implementation.