Python SDK for the Lindr LLM personality evaluation API
Project description
Lindr Python SDK
Python SDK for the Lindr LLM personality evaluation API.
Lindr helps you validate fine-tuned models by measuring behavioral personality across 10 dimensions. Compare base vs. fine-tuned models and catch personality drift before deploying to production.
Installation
pip install lindr
Quick Start
import lindr
# Initialize client
client = lindr.Client(api_key="lnd_...")
# Create a persona (your target personality profile)
persona = client.personas.create(
name="Support Agent",
dimensions=lindr.PersonalityDimensions(
openness=70,
conscientiousness=85,
extraversion=60,
agreeableness=90,
neuroticism=20,
assertiveness=65,
ambition=70,
resilience=80,
integrity=95,
curiosity=75,
),
)
# Run baseline evaluation
samples = [
{"id": "1", "content": "Response from base model..."},
{"id": "2", "content": "Another response..."},
]
baseline, summary = client.evals.batch(
name="llama-3.2-8b Baseline",
persona_id=persona.id,
samples=samples,
)
print(f"Avg drift: {summary.avg_drift:.1f}%")
# After fine-tuning, evaluate again
candidate, _ = client.evals.batch(
name="llama-3.2-8b-dpo-v1",
persona_id=persona.id,
samples=finetuned_samples,
)
# Compare results
comparison = client.comparisons.create(
baseline_eval_id=baseline.id,
candidate_eval_id=candidate.id,
)
print(f"Recommendation: {comparison.recommendation}") # "ship", "review", or "reject"
Features
- Batch Evaluations: Analyze multiple LLM responses in parallel
- 10 Personality Dimensions: Openness, Conscientiousness, Extraversion, Agreeableness, Neuroticism, Assertiveness, Ambition, Resilience, Integrity, Curiosity
- A/B Comparisons: Compare baseline vs. fine-tuned models
- Ship/Review/Reject Recommendations: Get actionable guidance on model quality
- Automatic Retries: Built-in retry logic with exponential backoff
- Pagination: Efficiently iterate through large result sets
- Async Support: Full async/await support for high-throughput applications
Pagination
All list methods return paginated responses:
# Get first page
result = client.personas.list(page=1, per_page=10)
print(f"Total personas: {result.total}")
print(f"Page {result.page} of {result.total_pages}")
print(f"Has more: {result.has_more}")
# Iterate through all personas
for persona in result.items:
print(f"- {persona.name}")
# Get next page
if result.has_more:
next_page = client.personas.list(page=2, per_page=10)
Model Checkpoints
Track fine-tuning iterations with checkpoints:
# Register a checkpoint
checkpoint = client.checkpoints.create(
name="llama-3.2-8b-dpo-v1",
model_base="llama-3.2-8b",
training_method="dpo",
notes="First DPO fine-tune with safety data",
)
# Run eval with checkpoint tracking
eval_run, summary = client.evals.batch(
name="DPO v1 Eval",
persona_id=persona.id,
checkpoint_id=checkpoint.id,
samples=samples,
)
Datasets
Create reusable prompt datasets:
# Create a dataset
dataset = client.datasets.create(
name="Support Scenarios",
prompts=[
{
"id": "greeting",
"messages": [{"role": "user", "content": "Hello, I need help"}],
"category": "greeting",
},
{
"id": "complaint",
"messages": [{"role": "user", "content": "This is unacceptable!"}],
"category": "complaint",
},
],
)
# Run eval with dataset (requires monitor for generation)
eval_run, summary = client.evals.batch(
name="Dataset Eval",
persona_id=persona.id,
dataset_id=dataset.id,
monitor_id="mon_xxx", # Monitor to generate responses
)
Error Handling
The SDK provides typed exceptions for common error cases:
import lindr
try:
persona = client.personas.get("invalid_id")
except lindr.NotFoundError:
print("Persona not found")
except lindr.RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
except lindr.ValidationError as e:
print(f"Validation failed: {e.details}")
except lindr.AuthenticationError:
print("Invalid API key")
except lindr.APIError as e:
print(f"API error ({e.status_code}): {e}")
Automatic Retries
The SDK automatically retries failed requests with exponential backoff:
# Default: 3 retries with exponential backoff
client = lindr.Client(api_key="lnd_...")
# Customize retry behavior
client = lindr.Client(
api_key="lnd_...",
max_retries=5, # More retries for unreliable networks
)
# Disable retries
client = lindr.Client(
api_key="lnd_...",
max_retries=0,
)
Retried status codes: 429 (rate limit), 500, 502, 503, 504
Async Usage
import asyncio
import lindr
async def main():
async with lindr.AsyncClient(api_key="lnd_...") as client:
# List with pagination
result = await client.personas.list()
# Run multiple evals in parallel
tasks = [
client.evals.batch(
name=f"Eval {i}",
persona_id=persona_id,
samples=sample_batches[i],
)
for i in range(5)
]
results = await asyncio.gather(*tasks)
asyncio.run(main())
Configuration
Environment Variables
LINDR_API_KEY: Your API key (alternative to passingapi_keyto client)LINDR_BASE_URL: Override API base URL (default:https://api.lindr.io/api/v1)
Client Options
client = lindr.Client(
api_key="lnd_...", # Required (or use LINDR_API_KEY env var)
base_url="https://...", # Optional: custom API endpoint
timeout=60.0, # Optional: request timeout in seconds (default: 30)
max_retries=3, # Optional: retry attempts (default: 3)
)
API Reference
Personas
# Create
persona = client.personas.create(name="...", dimensions=dims)
# List (paginated)
result = client.personas.list(page=1, per_page=20)
# Get
persona = client.personas.get("persona_id")
# Update
persona = client.personas.update("persona_id", name="New Name")
# Delete
client.personas.delete("persona_id")
Evals
# Batch evaluation
eval_run, summary = client.evals.batch(
name="...",
persona_id="...",
samples=[{"id": "1", "content": "..."}],
)
# List (paginated)
result = client.evals.list(status="completed")
# Get with results
eval_run, results = client.evals.get("eval_id", include_results=True)
# Delete
client.evals.delete("eval_id")
Comparisons
# Create comparison
comparison = client.comparisons.create(
baseline_eval_id="...",
candidate_eval_id="...",
)
# List (paginated)
result = client.comparisons.list()
# Get
comparison = client.comparisons.get("comparison_id")
Datasets
# Create
dataset = client.datasets.create(name="...", prompts=[...])
# List (paginated)
result = client.datasets.list()
# Get
dataset = client.datasets.get("dataset_id")
# Delete
client.datasets.delete("dataset_id")
Checkpoints
# Create
checkpoint = client.checkpoints.create(
name="model-v1",
model_base="llama-3.2-8b",
training_method="lora",
)
# List (paginated)
result = client.checkpoints.list(model_base="llama-3.2-8b")
# Get
checkpoint = client.checkpoints.get("checkpoint_id")
# Delete
client.checkpoints.delete("checkpoint_id")
Documentation
Full documentation available at lindr.io/docs
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lindr-0.6.1.tar.gz.
File metadata
- Download URL: lindr-0.6.1.tar.gz
- Upload date:
- Size: 27.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
401474733a7a0d7654e8114637b2811ac899788c22f1a482ff69e522b64873a2
|
|
| MD5 |
3fcbc7b769f29545592743a7fb43b80b
|
|
| BLAKE2b-256 |
8c8475bebee0f159b2e413990a6be202cc1e94faca3d3226a2495f7dd21875ed
|
File details
Details for the file lindr-0.6.1-py3-none-any.whl.
File metadata
- Download URL: lindr-0.6.1-py3-none-any.whl
- Upload date:
- Size: 40.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e21848ddc8504b2b72c924c9a38af492b0139eb5ac5ca5b5401c33c20f91ec9
|
|
| MD5 |
ce7b78770b13389a04fe298bb71f28ae
|
|
| BLAKE2b-256 |
d6c37ea6fc7212291d23afdace853ec7efa844644144583f624080836a4bde43
|