Prompt Oriented Programming (POP): reusable, composable prompt functions for LLMs.
Project description
Prompt Oriented Programming (POP)
from POP import PromptFunction
pf = PromptFunction(
prompt="Draw a simple ASCII art of <<<object>>>.",
client="openai",
)
print(pf.execute(object="a cat"))
print(pf.execute(object="a rocket"))
/\_/\
( o.o )
> ^ <
/\
/ \
/ \
| |
| |
Reusable, composable prompt functions for LLM workflows.
Version 1.1.5 fixes the Gemini tool-normalization regression from 1.1.4 and converts empty Gemini tool-enabled stream replies into explicit errors instead of silent failures.
PyPI: https://pypi.org/project/pop-python/
GitHub: https://github.com/sgt1796/POP
Table of Contents
- Overview
- Update Note
- Major Updates
- Features
- Installation
- Setup
- PromptFunction
- Provider Registry
- Tool Calling
- Function Schema Generation
- Embeddings
- Web Snapshot Utility
- Examples
- Contributing
1. Overview
Prompt Oriented Programming (POP) is a lightweight framework for building reusable, parameterized prompt functions. Instead of scattering prompt strings across your codebase, POP lets you:
- encapsulate prompts as objects
- pass parameters cleanly via placeholders
- select a backend LLM client dynamically
- improve prompts using meta-prompting
- generate OpenAI-compatible function schemas
- use unified embedding tools
- work with multiple LLM providers through a centralized registry
POP is designed to be simple, extensible, and production-friendly.
2. Update Note
1.1.5 (March 23, 2026)
- Gemini tool compatibility: restores support for POP's flat legacy tool schema, including
type: "custom"agent tools. - Gemini failure visibility: empty Gemini replies on tool-enabled requests now surface as explicit stream errors instead of silent
doneevents. - Request diagnostics: Gemini requests now warn on partially dropped tools and fail fast when no supplied tool can be normalized.
3. Major Updates
3.1. Modularized architecture
The project has been decomposed into small, focused modules:
POP/prompt_function.pyPOP/embedder.pyPOP/context.pyPOP/api_registry.pyPOP/providers/(one provider per file)POP/utils/
This mirrors the structure in the pi-mono ai package for clarity and maintainability.
3.2. Provider registry + per-provider clients
Each provider has its own adaptor (OpenAI, Claude, Gemini, DeepSeek, Doubao, Local, Ollama). The registry gives you:
list_providers()list_default_model()list_models()get_client()
4. Features
-
Reusable Prompt Functions Use
<<<placeholder>>>syntax to inject dynamic content. -
Multi-LLM Backend Choose between OpenAI, Claude, Gemini, DeepSeek, Doubao, Local, or Ollama.
-
Tool Calling Pass a tool schema list to
execute()and receive tool-call arguments. -
Multimodal (Text + Image) Pass
images=[...](URLs or base64) when the provider supports it. -
Prompt Improvement Improve or rewrite prompts using Fabric-style meta-prompts.
-
Function Schema Generation Convert natural language descriptions into OpenAI-function schemas.
-
Unified Embedding Interface Supports OpenAI, Jina AI embeddings, and local HuggingFace models.
-
Webpage Snapshot Utility Convert any URL into structured text using r.jina.ai with optional image captioning.
5. Installation
Install from PyPI:
pip install pop-python
Or install in development mode from GitHub:
git clone https://github.com/sgt1796/POP.git
cd POP
pip install -e .
6. Setup
Create a .env file in your project root:
OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_key
GEMINI_API_KEY=your_gcp_gemini_key
DEEPSEEK_API_KEY=your_deepseek_key
DOUBAO_API_KEY=your_volcengine_key
JINAAI_API_KEY=your_jina_key
All clients automatically read keys from environment variables.
7. PromptFunction
The core abstraction of POP is the PromptFunction class.
from POP import PromptFunction
pf = PromptFunction(
sys_prompt="You are a helpful AI.",
prompt="Give me a summary about <<<topic>>>.",
)
print(pf.execute(topic="quantum biology"))
7.1. Placeholder Syntax
Use angle-triple-brackets inside your prompt:
<<<placeholder>>>
These are replaced at execution time.
Example:
prompt = "Translate <<<sentence>>> to French."
7.2. Reserved Keywords
Within .execute(), the following keyword arguments are reserved and should not be used as placeholder names:
modelsysfmttoolstool_choicetempimagesADD_BEFOREADD_AFTER
Most keywords are used for parameters. ADD_BEFORE and ADD_AFTER will attach input string to head/tail of the prompt.
7.3. Executing prompts
result = pf.execute(
topic="photosynthesis",
model="gpt-5-mini",
temp=0.3,
)
7.4. Improving Prompts
You can ask POP to rewrite or enhance your system prompt:
better = pf.improve_prompt()
print(better)
This uses a Fabric-inspired meta-prompt bundled in the POP/prompts/ directory.
7.5. Token Usage Tracking
POP tracks usage per call at the framework level.
After each execute() call:
pf.last_usagestores the normalized usage record for the latest request.pf.usage_historystores a bounded history (maxlen=200) of usage records.pf.get_usage_summary()returns cumulative totals for thatPromptFunctioninstance.
from POP import PromptFunction
pf = PromptFunction(prompt="Give me 3 names for a <<<thing>>>.", client="openai")
result = pf.execute(thing="robot")
print(result)
print(pf.last_usage["source"]) # provider | estimate | hybrid | none
print(pf.last_usage["total_tokens"]) # canonical total used by POP
print(pf.get_usage_summary()) # cumulative counters
Usage is provider-first:
- Provider-reported usage is used when available.
- POP estimates tokens when provider usage is missing.
- POP marks anomaly metadata when provider and estimate differ significantly.
8. Provider Registry
Use the registry to list providers/models or instantiate clients.
from POP import list_providers, list_models, list_default_model, get_client
print(list_providers())
print(list_default_model())
print(list_models())
client = get_client("openai")
claude_client = get_client("claude")
Non-default model example:
from POP import PromptFunction, get_client
client = get_client("gemini", "gemini-2.5-pro")
pf = PromptFunction(prompt="Draw a rocket.", client=client)
print(pf.execute())
Direct provider class example:
from POP import PromptFunction
from POP.providers.gemini_client import GeminiClient
pf = PromptFunction(prompt="Draw a rocket.", client=GeminiClient(model="gemini-2.5-pro"))
print(pf.execute())
9. Tool Calling
from POP import PromptFunction
tools = [
{
"type": "function",
"function": {
"name": "create_reminder",
"description": "Create a reminder.",
"parameters": {
"type": "object",
"properties": {
"description": {"type": "string"},
"when": {"type": "string"},
},
"required": ["description"],
},
},
}
]
pf = PromptFunction(
sys_prompt="You are a helpful assistant.",
prompt="<<<input>>>",
client="openai",
)
result = pf.execute(input="Remind me to walk at 9am.", tools=tools)
print(result)
If you do not want tool calling for a request, either omit tools entirely or pass tools=[].
POP treats both as plain text generation and omits tool-calling fields from the provider request.
This is especially important for Gemini's OpenAI-compatible endpoint, where tool-free requests should not send tools or tool_choice.
When using client="claude", POP talks to Anthropic through the OpenAI Python SDK compatibility endpoint.
POP warns and degrades for documented compatibility gaps instead of pretending OpenAI-only features are enforced:
fmt/response_formatis omitted because Anthropic ignores it on the compatibility layer- strict tool schemas are removed because Anthropic ignores
strict - system and developer prompts are hoisted into one system prompt before the request is sent
Claude defaults to claude-haiku-4-5. You can override it per call, for example model="claude-sonnet-4-6".
10. Function Schema Generation
POP supports generating OpenAI function-calling schemas from natural language descriptions.
schema = pf.generate_schema(
description="Return the square and cube of a given integer."
)
print(schema)
What this does:
- Applies a standard meta-prompt
- Uses the selected LLM backend
- Produces a valid JSON Schema for OpenAI function calling
- Optionally saves it under
schemas/
11. Embeddings
POP includes a unified embedding interface:
from POP import Embedder
embedder = Embedder(use_api="openai")
vecs = embedder.get_embedding(["hello world"])
Supported modes:
- OpenAI embeddings
- Gemini embeddings (via OpenAI-compatible Gemini endpoint)
- JinaAI embeddings
- Local HuggingFace model embeddings (cpu/gpu)
Large inputs are chunked automatically when needed.
12. Web Snapshot Utility
from POP.utils.web_snapshot import get_text_snapshot
text = get_text_snapshot("https://example.com", image_caption=True)
print(text[:500])
Supports:
- optional image removal
- optional image captioning
- DOM selector filtering
- returning JSON or plain text
13. Examples
from POP import PromptFunction
pf = PromptFunction(prompt="Give me 3 creative names for a <<<thing>>>.")
print(pf.execute(thing="robot"))
print(pf.execute(thing="new language"))
Multimodal example (provider must support images):
from POP import PromptFunction
image_b64 = "..." # base64-encoded image
pf = PromptFunction(prompt="Describe the image.", client="openai")
print(pf.execute(images=[image_b64]))
14. Contributing
Steps:
- Fork the GitHub repo
- Create a feature branch
- Add tests or examples
- Submit a PR with a clear explanation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pop_python-1.1.5.tar.gz.
File metadata
- Download URL: pop_python-1.1.5.tar.gz
- Upload date:
- Size: 62.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aceb26f92541d115cfc33c7057d0a180e9bb6dccd13aa3e113c2101c25205994
|
|
| MD5 |
9f2a8d41ed36a8b4c2c726cee3528e43
|
|
| BLAKE2b-256 |
2fe23a5eccc00ca8b7c7881a41931491f4e179bedbbb52011357093e2d17d795
|
Provenance
The following attestation bundles were made for pop_python-1.1.5.tar.gz:
Publisher:
publish-pypi.yaml on sgt1796/POP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pop_python-1.1.5.tar.gz -
Subject digest:
aceb26f92541d115cfc33c7057d0a180e9bb6dccd13aa3e113c2101c25205994 - Sigstore transparency entry: 1165022459
- Sigstore integration time:
-
Permalink:
sgt1796/POP@46147fb882c558a1c13f913031a66f752854bf77 -
Branch / Tag:
refs/tags/pop-python-v1.1.5 - Owner: https://github.com/sgt1796
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yaml@46147fb882c558a1c13f913031a66f752854bf77 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pop_python-1.1.5-py3-none-any.whl.
File metadata
- Download URL: pop_python-1.1.5-py3-none-any.whl
- Upload date:
- Size: 63.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78dd19ae132babc9945e78dfea5c7fcbb0e37c63c4a189deb59f764265bdc82c
|
|
| MD5 |
894a7879a2db0257bc382d4fd0a90f07
|
|
| BLAKE2b-256 |
7fcbf99c731611f4ace34e93832ec83a4ebcbb6ba3b3ab7b381ed05e3b74a57d
|
Provenance
The following attestation bundles were made for pop_python-1.1.5-py3-none-any.whl:
Publisher:
publish-pypi.yaml on sgt1796/POP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pop_python-1.1.5-py3-none-any.whl -
Subject digest:
78dd19ae132babc9945e78dfea5c7fcbb0e37c63c4a189deb59f764265bdc82c - Sigstore transparency entry: 1165022623
- Sigstore integration time:
-
Permalink:
sgt1796/POP@46147fb882c558a1c13f913031a66f752854bf77 -
Branch / Tag:
refs/tags/pop-python-v1.1.5 - Owner: https://github.com/sgt1796
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yaml@46147fb882c558a1c13f913031a66f752854bf77 -
Trigger Event:
push
-
Statement type: