python language unifying models
plum
Ask an AI right inside your code — like it's just another instruction.
No SDKs to learn. No boilerplate. Just ask.
# a normal python variable
complaint = "My order is 3 days late."
# ask claude. that's it.
response = ?["Write a friendly reply" | claude]
print(response)
our belief
AI operations should come as natural as 1+1
That's what plum is built for.
the query operator
One symbol. Infinite reach.
The ?[...] operator embeds a model call anywhere a value can go.
# the prompt is just a string
answer = ?["What is the capital of France?" | claude]
# inject runtime values with f-string syntax
summary = ?[f"Summarize this article: {article}" | claude]
# use the result directly — it's just a value
if ?[f"Is this email spam? Reply yes/no: {email}" | claude] == "yes":
quarantine(email)
# works in list comprehensions, map, filter — anywhere an expression fits
tags = [?[f"Classify: {p}" | claude] for p in posts]
# ask for structured output — plum handles parsing
class Sentiment:
score: float # -1.0 to 1.0
label: str
reason: str
result = ?[f"Analyze sentiment: {review}" | claude -> Sentiment]
print(result.score, result.label) # 0.87, "positive"
model routing
Pick your model. Swap anytime.
The pipe | routes the prompt. Config lives in one file.
01
Any model, same syntax
Claude, GPT, Gemini, local models. The ?[...] syntax never changes.
?["..." | claude]
?["..." | gpt4]
?["..." | llama3]
02
Simple config
Set keys in plum.toml. No scattered os.environ calls.
# plum.toml
[models]
claude = "claude-3-5-sonnet"
03
Caching built in
Identical prompts return cached results by default. Pay per unique query.
@cache
?[f"Translate: {text}" | claude]
before & after
Without plum vs. with plum.
Same result. Less noise.
import anthropic
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-3-5-sonnet",
max_tokens=1024,
messages=[{
"role": "user",
"content": "Translate to French: " + text
}]
)
result = msg.content[0].text
result = ?[f"Translate to French: {text}" | claude]
real programs
Plum in practice.
Full programs that show what plum-first code feels like.
def handle_ticket(ticket: Ticket) -> Response:
urgency = ?[f"Rate urgency 1-5: {ticket.body}" | claude -> int]
category = ?[f"Classify as billing/shipping/other: {ticket.body}" | claude]
if urgency >= 4:
escalate(ticket)
return
draft = ?[f"Write a warm reply to this {category} issue: {ticket.body}" | claude]
return Response(draft)
# enrich a product catalog with AI-generated copy
enriched = [
{
**product,
"tagline": ?[f"One-line tagline for: {product['name']}" | claude],
"description": ?[f"SEO description for: {product['name']}" | claude],
"tags": ?[f"5 search tags for: {product['name']}" | claude -> list[str]],
}
for product in catalog.load()
]
early access
Shape plum with us.
The language is under active design. Request early access and help decide what plum becomes.