Skip to main content

Python client library for the ptuner prompt-tuning API

Project description

ptuner

Python client for the ptuner prompt-tuning API.

Evaluate, compare and iterate on LLM prompts with dataset-driven benchmarks, exact-match scoring, and LLM-as-judge evaluation.

Hosted at prompts.church

Installation

pip install ptuner

Quick Start

from ptuner import PtunerClient

client = PtunerClient(
    base_url="https://api.prompts.church",
    api_key="sk_...",
)

# 1. Create a project
project = client.create_project(
    name="Sentiment Analysis",
    description="Classify customer feedback",
)

# 2. Create a prompt with a version
prompt = client.create_prompt(
    project["id"],
    name="Sentiment Classifier",
    slug="sentiment-v1",
)

version = client.create_version(
    prompt["id"],
    system_template=(
        "You are a sentiment classifier. "
        "Respond with exactly one word: positive, negative, or neutral."
    ),
    message_template="Text: {{ text }}\n\nSentiment:",
)

# 3. Create a dataset
dataset = client.create_dataset(project["id"], name="Customer Reviews")

reviews = [
    {"text": "This product is amazing!", "label": "positive"},
    {"text": "Terrible quality, broke after one day.", "label": "negative"},
    {"text": "The package arrived on time.", "label": "neutral"},
]

for r in reviews:
    client.create_datapoint(
        dataset["id"],
        message_params=[{"role": "user", "params": {"text": r["text"]}}],
        exact_match_label=r["label"],
    )

# 4. Store your LLM API key (one-time)
client.create_credential(
    provider="openai",
    api_key="sk-your-openai-key",
    display_label="My Key",
)

# 5. Run evaluation
run = client.create_eval_run(
    project_id=project["id"],
    prompt_version_id=version["id"],
    dataset_id=dataset["id"],
    model_config={"model": "gpt-5-nano", "provider": "openai", "temperature": 0.0},
    judge_config={"judge_model": "gpt-5-mini"},
    iterations=3,
)

# 6. Wait and check results
import time
for _ in range(30):
    status = client.get_eval_run(run["id"])
    if status["status"] in ("completed", "failed"):
        break
    time.sleep(2)

results = client.list_eval_results(run["id"])
exact = [r["exact_match_score"] for r in results if r.get("exact_match_score") is not None]
judge = [r["judge_score"] for r in results if r.get("judge_score") is not None]

if exact:
    print(f"Exact match accuracy: {sum(exact)/len(exact):.1%}")
if judge:
    print(f"Judge avg score: {sum(judge)/len(judge):.2f}")

Authentication

Pass either an API key or a Firebase JWT token:

# API key (recommended)
client = PtunerClient(base_url="https://api.prompts.church", api_key="sk_...")

# Firebase JWT
client = PtunerClient(base_url="https://api.prompts.church", token="eyJ...")

Generate an API key in the UI at Settings → Generate API Key.

Structured JSON Output

Force models to return structured JSON by adding json_schema when creating a prompt version:

version = client.create_version(
    prompt["id"],
    system_template="You are a sentiment expert. Return JSON with sentiment and confidence.",
    message_template="Text: {{ text }}",
    json_schema={
        "type": "object",
        "properties": {
            "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
            "confidence": {"type": "number"},
        },
        "required": ["sentiment", "confidence"],
        "additionalProperties": False,
    },
)

This works across all providers (OpenAI, Anthropic, Google) — ptuner translates the schema to each provider's structured output format automatically.

Omit json_schema (or set it to None) for plain text mode.

Comparing Prompt Versions

A common workflow: iterate on a prompt and compare versions against the same dataset.

v2 = client.create_version(
    prompt["id"],
    system_template="You are a sentiment analysis expert. Respond: positive, negative, or neutral.",
    message_template="Text: {{ text }}\n\nSentiment:",
)

run_v2 = client.create_eval_run(
    project_id=project["id"],
    prompt_version_id=v2["id"],
    dataset_id=dataset["id"],
    model_config={"model": "gpt-5-nano", "provider": "openai", "temperature": 0.0},
    iterations=3,
)
# Compare results between v1 and v2 in the UI or via the API

API Reference

Client

Method Description
PtunerClient(base_url, api_key=, token=, timeout=) Create a client
client.close() Close the HTTP connection

Supports context manager: with PtunerClient(...) as client:

User

Method Description
get_me() Get current user info
generate_api_key() Generate a new API key

Projects

Method Description
list_projects() List all projects
create_project(name, description="") Create a project
get_project(project_id) Get project details
list_members(project_id) List project members
add_member(project_id, email, role="editor") Add a member

Prompts & Versions

Method Description
list_prompts(project_id) List prompts in a project
create_prompt(project_id, name, slug) Create a prompt
list_versions(prompt_id) List versions of a prompt
create_version(prompt_id, system_template=, message_template=, json_schema=) Create a version

Datasets & Datapoints

Method Description
list_datasets(project_id) List datasets
create_dataset(project_id, name) Create a dataset
list_datapoints(dataset_id) List datapoints
create_datapoint(dataset_id, system_params=, message_params=, exact_match_label=, acceptance_criteria=, labels=) Add a datapoint
update_datapoint(datapoint_id, **fields) Update a datapoint
delete_datapoint(datapoint_id) Delete a datapoint

LLM Credentials

Method Description
list_credentials() List stored credentials
create_credential(provider, api_key, project_id=, display_label=) Store a credential
update_credential(credential_id, **fields) Update a credential
delete_credential(credential_id) Delete a credential
resolve_credential(project_id, provider) Resolve which credential will be used

Eval Runs

Method Description
create_eval_run(project_id, prompt_version_id, dataset_id, model_config=, judge_config=, iterations=1) Start an eval run
get_eval_run(run_id) Get run status
list_eval_results(run_id) Get run results
list_project_runs(project_id) List all runs in a project

Examples

See examples/benchmark_sentiment.py for a full end-to-end benchmark that compares multiple models with both plain text and structured JSON output.

License

MIT results = client.list_eval_results(run["id"])

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

ptuner-0.2.0.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ptuner-0.2.0-py3-none-any.whl (5.6 kB view details)

Uploaded Python 3

File details

Details for the file ptuner-0.2.0.tar.gz.

File metadata

  • Download URL: ptuner-0.2.0.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for ptuner-0.2.0.tar.gz
Algorithm Hash digest
SHA256 9a4ef634836a958cd63272ae689ee2634cdf2f357dfe768038be63af18b195d0
MD5 38ee689580c82ea2d51098c30c2ff13d
BLAKE2b-256 654981d51b50d85be5dfeb6b200468cc2dac01524cc7bd9aa9c76edaf52a215d

See more details on using hashes here.

File details

Details for the file ptuner-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: ptuner-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 5.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for ptuner-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d56bd599967c4e4471f17428c9ad9f19792a9bb7b296974ab9f9d51336c43ca4
MD5 4f278cce7b12d44798d76271f978d6b0
BLAKE2b-256 248aa062c5e9769b1b33985c012cd6c9a982984055515165ccee83b05a5cd3dd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page