Skip to main content
PromptFlipdocs

Python SDK

Fetch managed prompts and report run telemetry — without your code ever crashing if the service is unreachable.

Install

bash
pip install promptflip

Requires Python 3.10 or newer. One runtime dependency: httpx.

Quickstart

Python
from promptflip import PromptFlip

pm = PromptFlip(api_key="sk_...", environment="prod")

# Fetch a prompt. Always returns a usable Prompt — never throws.
prompt = pm.get(
    "support-agent-system",
    fallback="You are a helpful support agent for [[plan]]-plan users.",
    inputs={"plan": user.plan},
    assignment_key=user.id,           # sticky A/B variant
)

# Use it with your model, report telemetry in one block:
with prompt.run() as r:
    response = call_your_model(prompt.text)
    r.tokens = response.total_tokens
    r.converted = True                # any attribute name -> custom metric

# Or report metrics directly (inline, or from a worker later):
pm.send(prompt, csat_score=5)

Your API key decides which prompts getsees: a key fetches prompts from the workspace it belongs to — a key created in a workspace serves that workspace's prompts; a personal key serves your personal workspace.

The three primitives

That is the entire surface a caller writes against. version pins a fetch to a specific published version instead of always resolving the live one — useful for testing an older version deliberately.

Python
pm.get(key, fallback, *, inputs=None, assignment_key=None, version=None) -> Prompt
pm.send(prompt_or_id, **metrics)
with prompt.run() as r: ...

Guarantees

  • Never throws on the serving path. get always returns a Prompt; send always returns. A missing api_key does not raise either — it selects local-only mode (see below). Construction only raises if you pass the private test-only transport hook outside ENV=test.
  • Hybrid, local-first resolution. get resolves in order: cloud (a live fetch when api_keyis set — served from the disk cache first if it's fresh, or its last-known cached value — memory, then disk, even if stale — the instant the live call fails or errors) → local repo (if local_dir is set — the same files the CLI manages) → your fallback string → empty, with a warning. In other words: a cached cloud value always wins over local files and the fallback, even when the network is down. Every Prompt exposes which tier resolved it via prompt.source ("cloud", "local", "fallback", or "none").
  • Caching. An in-memory LRU cache holds the last-seen value per key with no expiry (evicted only by size, capped by cache_max_size) — this is what backs the last-known fallback above. Pass cache_dir to also persist a cross-process disk cache with a TTL (cache_ttl, default 300 seconds) and stale-while-revalidate — a stale entry is served immediately while a fresh one is fetched in the background.
  • One runtime dependency: httpx.

Error handling

The SDK never throws on the serving path. Failures emit structured WARNING logs to the promptflip logger; attach a handler to react:

Python
import logging

class CreditAlert(logging.Handler):
    def emit(self, record):
        if getattr(record, "kind", None) == "out_of_credits":
            alert("PromptFlip out of credits")

logging.getLogger("promptflip").addHandler(CreditAlert())

Records carry a structured kind, one of: transport, out_of_credits, quota_exceeded, missing_inputs, missing_vars, missing_variables, missing_inputs_in_fallback, invalid_metric, not_found, unresolved, unknown.

Fallback templates

A fallback uses the same [[placeholders]] syntax as the stored prompt; the SDK fills them client-side from inputs so online and offline render identically. Check a fallback in your tests:

Python
from promptflip import check_fallback

missing = check_fallback("Hello [[name]] on [[plan]]", inputs={"name": "Alice"})
# missing == ["plan"]