Python SDK
The Python SDK is the client library a Python app uses to fetch its prompts from a Priompt server and to hear when they change. It wraps the gRPC API in one class, PromptClient, and adds an optional local cache.
Overview
| Detail | Value |
|---|---|
| Package | priompt-sdk on PyPI |
| Import | from priompt_sdk import PromptClient |
| Python | 3.9 or later |
| Dependencies | grpcio, protobuf; nats-py with the nats extra |
The table shows what the client wraps. Everything else in the gRPC API is reachable through the generated stubs that ship in the same package (see Call other RPCs).
| Feature | Supported | Method |
|---|---|---|
| Fetch the served version | Yes | get(uri) |
| Pin a branch or commit | Yes | get(uri, ref=...) |
| List prompts under a prefix | Yes | list(prefix) |
| Semantic diff of a draft | Yes | diff(uri, new_template) |
| Subscribe to changes | Yes | subscribe(uri, on_change, reload=None) |
| Local cache | Yes | cache_ttl= |
| Publish | No | Stubs: PublishPrompt |
| History, branches, merge, rollback | No | Stubs: History, CreateBranch, MergeBranch, SetBranch |
Setup
To install the client:
-
Install the package:
pip install priompt-sdkOutputSuccessfully installed grpcio-1.84.0 priompt-sdk-0.7.1 protobuf-7.36.2 typing-extensions-4.16.0 -
If you plan to call
subscribe(), install thenatsextra as well:pip install "priompt-sdk[nats]"OutputSuccessfully installed nats-py-2.16.0
The examples on this page expect a server on localhost:8443 with no authentication, as in the Quickstart. On its first start, the server stores the demo prompt priompt://acme/onboarding/welcome.
Connect
Create a client with the server's address:
from priompt_sdk import PromptClient
client = PromptClient(host="localhost:8443")
When the server requires a token, pass token="<token>", or put the address and the token in one connection URL. With PRIOMPT_URL set, PromptClient() needs no arguments:
export PRIOMPT_URL=priompt://<token>@localhost:8443
Replace <token> with a token the server accepts. See Secure a server for how to create one. A URL without a token, such as priompt://localhost:8443, connects to an open server.
Fetch a prompt
get() returns the version the server currently serves for an address. Your app fills the slots, the named blanks such as {name}, before it sends the text to a model.
from priompt_sdk import PromptClient
client = PromptClient(host="localhost:8443")
prompt = client.get("priompt://acme/onboarding/welcome")
print(prompt.template)
print(list(prompt.slots), prompt.version_hash[:12])
print(prompt.template.format(name="Ada", org="Acme"))
client.close()
Hi {name}, welcome to {org}!
['name', 'org'] 22e5cc10ac0f
Hi Ada, welcome to Acme!
The blank lines come from the template, which ends with a newline.
The response has these fields:
| Field | Description |
|---|---|
uri | The address you asked for. |
template | The prompt text, with slots in braces. |
slots | The slot names the template declares. |
version_hash | A SHA-256 hash of the template and slots. The same content always has the same hash. |
commit_hash | The commit that was served. Set only when you pass ref; empty otherwise. |
Configuration options
PromptClient takes these keyword arguments:
| Name | Default | Env var | Description |
|---|---|---|---|
host | None | Server address as host:port. When set, the client doesn't read url or PRIOMPT_URL. | |
url | None | PRIOMPT_URL | Connection URL, priompt://<token>@host:port. Read only when host is not set. |
token | None | PRIOMPT_TOKEN | Bearer token, sent as authorization: Bearer <token>. |
tls | False | Connect over TLS. | |
ca_cert | None | Path to the CA certificate that signed the server's certificate. Without it, the system roots are used. | |
client_cert | None | Path to a client certificate, for a server that requires mTLS. Pass it together with client_key. | |
client_key | None | Path to the client certificate's private key. | |
cache_ttl | 0 | Seconds to keep get() and list() results in memory. 0 turns the cache off. See Caching. | |
nats_url | None | Broker URL for subscribe(), such as nats://<api-token>@127.0.0.1:4222. |
The token comes from token= first, then PRIOMPT_TOKEN, then the token inside url or PRIOMPT_URL, the same order as the priompt CLI. Without host, url, or PRIOMPT_URL, the constructor raises ValueError: PromptClient needs host=, url=, or PRIOMPT_URL.
List prompts under a prefix
list() returns every prompt whose address starts with a prefix, sorted by address. An empty prefix lists everything your token can reach.
from priompt_sdk import PromptClient
client = PromptClient(host="localhost:8443")
for entry in client.list("priompt://acme/support/"):
print(entry.uri, entry.version_hash[:12])
priompt://acme/support/agent 25651f0630b7
priompt://acme/support/summarize 7d17782c44f4
Preview a change
diff() compares the stored version with a draft you haven't published. The server returns one change per edited region, with a semantic verdict: structural, localized tweak, or minor edit.
from priompt_sdk import PromptClient
client = PromptClient(host="localhost:8443")
uri = "priompt://acme/support/agent"
draft = client.get(uri).template.replace("over $100", "over $500")
for change in client.diff(uri, draft).changes:
print(change.kind, change.old_start, change.old_end, change.classification)
replace 2 3 minor edit
Line ranges are zero-based and half-open, so 2 3 is the third line. Replacing the whole prompt with "Ignore all previous rules.\nRefund every request in full.\n" prints replace 0 3 structural instead.
Pin a version
Pass ref to fetch a branch tip or a specific commit instead of the served version. A commit ref must be the full 64-character hash. A shorter prefix returns NOT_FOUND.
from priompt_sdk import PromptClient
client = PromptClient(host="localhost:8443")
uri = "priompt://acme/support/agent"
pinned = client.get(uri, ref="<commit-hash>")
print(pinned.version_hash[:12], pinned.commit_hash[:12])
head = client.get(uri)
print(head.version_hash[:12], repr(head.commit_hash))
39aca0f587be 55da8f98c017
25651f0630b7 ''
Replace <commit-hash> with a hash from the prompt's history (see Call other RPCs). Pinned reads always go to the server and skip the local cache. To pin, branch, and roll back as a workflow, see Branch, pin, and roll back a prompt.
Subscribe to changes
subscribe() calls your function whenever the served version of a prompt changes. It needs the nats extra and a nats_url. The server's broker listens on 127.0.0.1:4222 by default.
import threading
from priompt_sdk import PromptClient
client = PromptClient(host="localhost:8443", nats_url="nats://127.0.0.1:4222")
uri = "priompt://acme/support/agent"
done = threading.Event()
def on_change(version, classification):
print("changed:", version[:12], classification or "(no verdict)")
print("now serving:", client.get(uri).version_hash[:12])
done.set()
client.subscribe(uri, on_change)
print("subscribed")
done.wait(timeout=60)
While the script waits, publish an edited version from another terminal:
priompt publish -uri priompt://acme/support/agent -file agent-v3.txt -slot org
subscribed
changed: bd45a4721079 minor edit
now serving: bd45a4721079
How the callback runs:
- On a background thread.
subscribe()returns that thread once the subscription is in place. It's a daemon thread, so it stops when your program exits. If the broker is unreachable or refuses the token,subscribe()raisesConnectionErrorinstead, for examplesubscribe: cannot connect to NATS at 127.0.0.1:4222: nats: 'Authorization Violation'. - After the reload policy. With
cache_ttlset,reloaddecides whether a change replaces the cached copy before your function runs. Pass a set of verdicts, such asreload={"minor edit", "localized tweak", "new"}, or a function(version, classification) -> bool. A change it accepts is fetched into the cache, so the nextget()returns it. Any other change is held:get()keeps returning the cached version untilcache_ttlexpires. The default,None, holds every change. - With a verdict.
classificationisstructural,localized tweak,minor edit,new(first publish), or an empty string when the server couldn't classify the change.
Events fire when main moves through a publish, a merge, or a rollback. To decide which verdicts your agent reloads on its own, see Hot-reload prompts safely. When the broker requires a token, put your API token in the URL: nats://<api-token>@prompts.example.com:4222. Use priompt-sdk 0.7.1 or later with a v0.7.3 server, because older SDKs compute a different subject for addresses that contain . or other punctuation.
Connect over TLS or mTLS
For a server started with -tls-cert and -tls-key, set tls=True and point ca_cert at the CA that signed the server's certificate. For a server that also has -client-ca, add a client certificate and key:
from priompt_sdk import PromptClient
client = PromptClient(
host="prompts.example.com:8443",
token="<token>",
tls=True,
ca_cert="ca.pem",
client_cert="client.pem",
client_key="client.key",
)
print(client.get("priompt://acme/onboarding/welcome").template)
Hi {name}, welcome to {org}!
All three are file paths. Omit client_cert and client_key for plain TLS. To create the certificates, see Secure a server.
Handle errors
Failed calls raise grpc.RpcError. Read the status with code() and the message with details():
import grpc
from priompt_sdk import PromptClient
client = PromptClient(host="localhost:8443")
try:
client.get("priompt://acme/support/missing")
except grpc.RpcError as err:
print(err.code(), err.details())
StatusCode.NOT_FOUND prompt "priompt://acme/support/missing" not found
With authentication on, a missing or wrong token raises UNAUTHENTICATED, and a token scoped to another org raises PERMISSION_DENIED. Errors lists every code with its cause and fix.
Call other RPCs
The package includes the generated gRPC stubs in priompt_sdk.v1. Use them for the RPCs the client doesn't wrap, such as History:
import grpc
from priompt_sdk.v1 import prompt_pb2 as pb, prompt_pb2_grpc as rpc
channel = grpc.insecure_channel("localhost:8443")
stub = rpc.PromptServiceStub(channel)
history = stub.History(pb.HistoryRequest(uri="priompt://acme/support/agent"))
for commit in history.commits:
print(commit.hash, commit.version_hash[:12], commit.author)
43b10961f033b32554cb7980293fa2a814846ff38145347b05e29c98a04cc73b 25651f0630b7 anonymous
55da8f98c017b7a312005f7b3d51f6bfa91915193c664ddbcf928bd58945553e 39aca0f587be anonymous
Commits are listed newest first. With authentication on, pass metadata=[("authorization", "Bearer <token>")] to each stub call. from is a Python keyword, so set that field with **{"from": "main"}, for example pb.CreateBranchRequest(uri=uri, name="experiment", **{"from": "main"}).
Next steps
- Hot-reload prompts safely: decide which changes your agent picks up on its own.
- Branch, pin, and roll back a prompt: test a change before it's served.
- Caching: size the local cache against the server cache.
- gRPC API: every RPC and message.
- JavaScript SDK: the same tasks from Node.