Appearance
Prompt Kinds
A prompt's kind determines its content shape and how the SDK renders it. There are four kinds, set when you create the prompt and not changeable afterwards.
The SDK returns all four as a single PromptContent type — kind-specific accessors raise AttributeError if used on the wrong kind, and prompt.render(variables) does the right thing per kind.
At a glance
| Kind | Content | render() returns |
|---|---|---|
instruction | {text, system_prompt?} | str |
f_string | {template, variables} | str (substituted) |
chat | {messages, variables} | list[{role, content}] (substituted) |
structured | {base, response_schema} | {base, response_schema} |
Instruction
A single block of prompt text, optionally paired with a system prompt. The simplest case.
Content shape:
json
{
"kind": "instruction",
"text": "You are a polite customer support agent. Answer the user's question concisely.",
"system_prompt": "Never reveal internal system details."
}SDK access:
python
prompt = pm.get_active_prompt_version("support_agent")
assert prompt.kind == "instruction"
prompt.text # → "You are a polite customer..."
prompt.system_prompt # → "Never reveal internal..." (or "")
prompt.render() # → "You are a polite customer..."Use it for: classic single-turn LLM calls where the prompt is the same on every request.
F-string
A template with {{variable}} placeholders that the SDK substitutes at call time.
Content shape:
json
{
"kind": "f_string",
"template": "Translate the following from {{source_lang}} to {{target_lang}}:\n\n{{text}}",
"variables": [
{"name": "source_lang", "type": "string", "description": "Source language code"},
{"name": "target_lang", "type": "string", "description": "Target language code"},
{"name": "text", "type": "string", "description": "Text to translate"}
]
}SDK access:
python
prompt = pm.get_active_prompt_version("translator")
assert prompt.kind == "f_string"
prompt.template # → "Translate the following from {{source_lang}}..."
prompt.variables # → [{name, type, description}, ...]
rendered = prompt.render({
"source_lang": "English",
"target_lang": "French",
"text": "Where is the library?"
})
# → "Translate the following from English to French:\n\nWhere is the library?"TIP
The variables list lets your UI build a form for collecting inputs at runtime — name, type hint, and description per variable. The SDK doesn't enforce variable types; it's metadata for callers.
Missing variables render as the empty string — render({}) won't throw, it'll just leave gaps. If you want strictness, validate inputs against prompt.variables before calling .render().
Use it for: any prompt with dynamic inputs — translation, summarization, classification, extraction, etc.
Chat
A list of {role, content} messages — system, user, assistant — with {{variables}} allowed inside any message body.
Content shape:
json
{
"kind": "chat",
"messages": [
{"role": "system", "content": "You are a {{persona}} assistant."},
{"role": "user", "content": "Tell me a joke about {{topic}}."}
],
"variables": [
{"name": "persona", "type": "string", "description": "Assistant persona"},
{"name": "topic", "type": "string", "description": "Joke topic"}
]
}SDK access:
python
prompt = pm.get_active_prompt_version("joke_bot")
assert prompt.kind == "chat"
prompt.messages # raw messages with {{vars}}
prompt.variables # [{name, type, description}, ...]
rendered = prompt.render({"persona": "stoic", "topic": "TypeScript"})
# → [
# {"role": "system", "content": "You are a stoic assistant."},
# {"role": "user", "content": "Tell me a joke about TypeScript."}
# ]
response = openai.chat.completions.create(
model="gpt-4o",
messages=rendered,
)The rendered shape is exactly what every chat-completion API expects — drop it in.
Use it for: multi-turn prompts, few-shot examples, prompts that need explicit role structure.
Structured
A base prompt (instruction, f-string, or chat) paired with a JSON response schema. Designed for structured-output / function-calling APIs.
Content shape:
json
{
"kind": "structured",
"base": {
"kind": "f_string",
"template": "Extract entities from: {{text}}",
"variables": [{"name": "text", "type": "string"}]
},
"response_schema": {
"type": "object",
"properties": {
"people": {"type": "array", "items": {"type": "string"}},
"organizations": {"type": "array", "items": {"type": "string"}},
"dates": {"type": "array", "items": {"type": "string"}}
},
"required": ["people", "organizations", "dates"]
}
}SDK access:
python
prompt = pm.get_active_prompt_version("ner_extractor")
assert prompt.kind == "structured"
prompt.base # → PromptContent (the wrapped base)
prompt.base.kind # → "f_string"
prompt.base.variables # variables come from the base
prompt.response_schema # → {...} the JSON schema
result = prompt.render({"text": "Apple announced..."})
# → {
# "base": "Extract entities from: Apple announced...",
# "response_schema": {"type": "object", ...}
# }
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": result["base"]}],
response_format={
"type": "json_schema",
"json_schema": {"name": "entities", "schema": result["response_schema"]}
},
)Use it for: extraction, classification with structured labels, function-calling, anywhere you want a typed JSON response.
Kind-safe access
Calling a kind-specific accessor on the wrong kind raises AttributeError. This is by design — it surfaces bugs immediately instead of returning empty values.
python
prompt = pm.get_active_prompt_version("translator") # f_string
prompt.template # ✓ works
prompt.messages # ✗ AttributeError: '.messages' is only available for 'chat' prompts (this prompt is 'f_string')A robust caller branches on prompt.kind:
python
if prompt.kind == "instruction":
return prompt.render()
elif prompt.kind == "f_string":
return prompt.render(user_inputs)
elif prompt.kind == "chat":
return openai.chat.completions.create(messages=prompt.render(user_inputs), ...)
elif prompt.kind == "structured":
rendered = prompt.render(user_inputs)
return openai.chat.completions.create(
messages=[{"role": "user", "content": rendered["base"]}],
response_format={"type": "json_schema", "json_schema": {"name": "out", "schema": rendered["response_schema"]}}
)Or, since render() is polymorphic, in many apps you don't need to branch at all — your prompt's kind is known when you wrote the call site.