# Proompt
**Object-oriented prompt engineering for LLMs**
Stop wrestling with string templates. Build composable, testable, and maintainable prompts using object-oriented design patterns.
```python
# Instead of this messy string concatenation...
prompt = f"""
Analyze this data:
{open('data.csv').read()}
Using these functions:
{str(my_functions)}
"""
# Write clean, composable prompts like this:
from proompt.data import (
CsvDataProvider,
FileDataProvider,
SqliteProvider,
)
from proompt.base.context import ToolContext
section = PromptSection(
context=ToolContext(my_function),
CsvDataProvider("data.csv"),
FileDataProvider("file.txt"),
SqliteProvider("data.db"),
)
```
## Project Overview
Proompt is organized into a clean, modular structure:
```
proompt/
├── src/proompt/
│ ├── base/ # Abstract base classes
│ │ ├── context.py
│ │ ├── prompt.py
│ │ └── provider.py
│ └── data.py # Concrete data provider examples
├── examples/ # Complete usage examples
│ ├── 01-simple_quarterly_review.py
│ ├── 02-intermediate_quarterly_review.py
│ └── 03-advanced_quarterly_review.py
└── tests/ # Unit tests
```
**Key Components:**
- **Base classes** define contracts for providers, contexts, and prompts
- **Data providers** concrete examples of how to extend `DataProviders`
- **Examples** show real-world implementations from simple to advanced
- **Tests** ensure reliability and demonstrate usage patterns
## Why Proompt?
**Traditional string-based prompts are painful:**
- 🔥 Hard to compose and maintain large prompts
- 🐛 No separation between data and prompt logic
- 🚫 Difficult to test individual components
- 🔄 Can't reuse prompt components across projects
- ⚠️ No type safety or validation
**Proompt solves this with:**
- ✅ **Composable objects** - Build prompts from reusable components
- ✅ **Data providers** - Clean separation of data sources and prompt logic
- ✅ **Type safety** - Abstract base classes enforce contracts
- ✅ **Testable** - Unit test each component independently
- ✅ **Extensible** - Easy to create custom providers and contexts
- ✅ **Async ready** - Support for both sync and async operations
## Quick Start
```bash
uv pip install proompt
```
```python
from proompt.data import FileDataProvider
# Read a file and inject it into your prompt
provider = FileDataProvider("data.txt")
content = provider.run() # Returns file contents as string
print(f"Analyze the data:\n{content}")
```
## Core Concepts
A few example classes for extending the `DataProvider` class can be found in the `proompt.data` module.
### 🔌 Providers - Inject Data from Any Source
Providers fetch data from external sources and format it for LLM consumption:
```python
from proompt.data import CsvDataProvider, SqliteProvider
# CSV data as markdown tables
csv_provider = CsvDataProvider("sales_data.csv")
print(csv_provider.run())
# | Product | Sales | Region |
# | ------- | ----- | ------ |
# | Widget | 1000 | North |
# Database queries as markdown tables
db_provider = SqliteProvider(
"company.db",
'SELECT * FROM employees WHERE department = "Engineering"'
)
print(db_provider.run())
# | name | role | salary |
# | ----- | --------- | ------ |
# | Alice | Developer | 85000 |
```
### 🛠️ Tool Context - Document Functions for LLMs
Automatically generate function documentation that LLMs can understand:
```python
from proompt.base.context import ToolContext
def calculate_tax(income: float, rate: float = 0.25) -> float:
"""Calculate tax owed on income."""
return income * rate
tool_ctx = ToolContext(calculate_tax)
print(tool_ctx.render())
# Name: calculate_tax
# Description: Calculate tax owed on income.
# Arguments: income: float, rate: float = 0.25
# Returns: float
# Usage: Reference description for usage.
```
### 📝 Prompt Sections - Compose Complex Prompts
Combine providers, tools, and context into reusable sections:
```python
from textwrap import dedent
from proompt.base.prompt import PromptSection
class DataAnalysisSection(PromptSection):
def formatter(self, instruction: str) -> str:
data = "\n\n".join(p.run() for p in self.providers)
tools = "\n\n".join(str(t) for t in self.tools)
return dedent(f"""
{instruction}
Available Data Providers:
{data}
Available Tools:
{tools}
""")
def render(self) -> str:
return self.formatter("Analyze the provided data")
# Use it
section = DataAnalysisSection(
context=context, # Use Context to pass dynamic info
tools=[ToolContext(calculate_tax)],
CsvDataProvider("metrics.csv"), # accepts any number of Providers
)
prompt = str(section) # Ready for your LLM
```
## Data Providers
### File Provider
```python
from proompt.data import FileDataProvider
# Read any text file
provider = FileDataProvider("config.yaml")
content = provider.run(). # raw string content
```
**NOTE**: for structured YAML parsing, extend `DataProvider` to create `YamlProvider` class
### CSV Provider
```python
from proompt.data import CsvDataProvider
# Automatically converts CSV to markdown tables
provider = CsvDataProvider("data.csv")
table = provider.run() # Returns formatted markdown table
```
See `proompt.data.TableData` and `proompt.data.to_markdown_table()` for conversion.
### SQLite Provider
```python
from proompt.data import SqliteProvider
# Execute SQL queries, get markdown tables
provider = SqliteProvider(
database_path="app.db",
query="SELECT name, email FROM users WHERE active = 1",
table_name="users" # Optional, for better context
)
# Async support; NOTE the async only runs sync method .run()
result = await provider.arun()
```
**NOTE**: A _true_ asynchronous method would need to be defined when extending the `DataProvider` class.
## Advanced Usage
### Custom Providers
Creating custom providers is straightforward:
```python
from proompt.base.provider import BaseProvider
import requests
class ApiProvider(BaseProvider, str):
def __init__(self, url: str, api_key: str):
self.url = url
self.api_key = api_key
@property
def name(self) -> str:
return f"API Provider for {self.url}"
@property
def provider_ctx(self) -> str:
return f"Fetches data from REST API at {self.url}"
# NOTE: would be useful to include available endpoints
def run(self, endpoint: str) -> str:
response = requests.get(
f"{self.url}/{endpoint}",
headers={"Authorization": f"Bearer {self.api_key}"}
)
return response.json()
# Use your custom provider
api = ApiProvider("https://api.example.com", "your-key")
data = api.run("users")
```
### Data Transformation
Convert any data format to LLM-friendly markdown:
```python
from proompt.data import TableData
# From dictionaries
data = [
{"name": "Alice", "role": "Engineer", "salary": 85000},
{"name": "Bob", "role": "Designer", "salary": 75000}
]
table = TableData.from_dicts(data)
markdown = table.to_md()
print(markdown)
# | name | role | salary |
# | ----- | -------- | ------ |
# | Alice | Engineer | 85000 |
# | Bob | Designer | 75000 |
```
## API Reference
### Core Classes
- **`BaseProvider`** - Abstract base for all data providers
- **`Context`** - Abstract base for prompt contexts
- **`ToolContext`** - Documents functions for LLM consumption
- **`PromptSection`** - Composable prompt sections
- **`BasePrompt`** - Full prompt composition
### Concrete Providers
- **`FileDataProvider`** - Read text files
- **`CsvDataProvider`** - Read CSV files as markdown tables
- **`SqliteProvider`** - Execute SQL queries as markdown tables
### Utilities
- **`TableData`** - Convert various formats to markdown tables
- **`to_markdown_table()`** - Low-level table formatting
## Why Object-Oriented Prompts?
**Better Organization**
```python
# Instead of managing giant prompt strings
SYSTEM_PROMPT = """You are an assistant..."""
DATA_SECTION = """Here is the data: {data}"""
TOOL_SECTION = """Available tools: {tools}"""
# Compose from organized, testable objects
prompt = ChatPrompt(
SystemSection("You are an assistant..."),
DataSection(providers=[csv_provider, db_provider]),
ToolSection(tools=[calculator, parser])
)
```
**Easier Testing**
```python
# Test individual components
def test_csv_provider():
provider = CsvDataProvider("test.csv")
result = provider.run()
assert "| Name |" in result
def test_tool_context():
ctx = ToolContext(my_function)
assert "my_function" in ctx.render()
```
**Reusable Components**
```python
# Define once, use everywhere
analysis_section = DataAnalysisSection(
providers=[CsvDataProvider("metrics.csv")]
)
# Reuse in different prompts
customer_prompt = CustomerPrompt(analysis_section, ...)
admin_prompt = AdminPrompt(analysis_section, ...)
```
## Contributing
Coming soon
<!--
Proompt is designed to be extensible. Common extension points:
1. **Custom Providers** - Connect to new data sources
2. **Custom Contexts** - New ways to document tools/functions
3. **Custom Sections** - Domain-specific prompt components
4. **Custom Prompts** - Full prompt templates for specific use cases
See the [Contributing Guide](CONTRIBUTING.md) for details.
-->
Raw data
{
"_id": null,
"home_page": null,
"name": "proompt",
"maintainer": "https://github.com/Burhan-Q",
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "ai, prompting, prompts, object oriented, ai agent, prompt toolkit, devtools, developer tools, llm, prompt, code tools, structure",
"author": "Burhan Qaddoumi",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/9d/79/eb5b594aa21fa06e8e24c7d77a3a495cbee65ddf4fd29e8cd1e2f3086aea/proompt-0.1.1.tar.gz",
"platform": null,
"description": "# Proompt\n\n**Object-oriented prompt engineering for LLMs**\n\nStop wrestling with string templates. Build composable, testable, and maintainable prompts using object-oriented design patterns.\n\n```python\n# Instead of this messy string concatenation...\nprompt = f\"\"\"\nAnalyze this data:\n{open('data.csv').read()}\n\nUsing these functions:\n{str(my_functions)}\n\"\"\"\n\n# Write clean, composable prompts like this:\nfrom proompt.data import (\n CsvDataProvider,\n FileDataProvider,\n SqliteProvider,\n)\nfrom proompt.base.context import ToolContext\n\nsection = PromptSection(\n context=ToolContext(my_function),\n CsvDataProvider(\"data.csv\"),\n FileDataProvider(\"file.txt\"),\n SqliteProvider(\"data.db\"),\n)\n```\n\n## Project Overview\n\nProompt is organized into a clean, modular structure:\n\n```\nproompt/\n\u251c\u2500\u2500 src/proompt/\n\u2502 \u251c\u2500\u2500 base/ # Abstract base classes\n\u2502 \u2502 \u251c\u2500\u2500 context.py\n\u2502 \u2502 \u251c\u2500\u2500 prompt.py\n\u2502 \u2502 \u2514\u2500\u2500 provider.py\n\u2502 \u2514\u2500\u2500 data.py # Concrete data provider examples\n\u251c\u2500\u2500 examples/ # Complete usage examples\n\u2502 \u251c\u2500\u2500 01-simple_quarterly_review.py\n\u2502 \u251c\u2500\u2500 02-intermediate_quarterly_review.py\n\u2502 \u2514\u2500\u2500 03-advanced_quarterly_review.py\n\u2514\u2500\u2500 tests/ # Unit tests\n```\n\n**Key Components:**\n- **Base classes** define contracts for providers, contexts, and prompts\n- **Data providers** concrete examples of how to extend `DataProviders`\n- **Examples** show real-world implementations from simple to advanced\n- **Tests** ensure reliability and demonstrate usage patterns\n\n## Why Proompt?\n\n**Traditional string-based prompts are painful:**\n- \ud83d\udd25 Hard to compose and maintain large prompts\n- \ud83d\udc1b No separation between data and prompt logic\n- \ud83d\udeab Difficult to test individual components\n- \ud83d\udd04 Can't reuse prompt components across projects\n- \u26a0\ufe0f No type safety or validation\n\n**Proompt solves this with:**\n- \u2705 **Composable objects** - Build prompts from reusable components\n- \u2705 **Data providers** - Clean separation of data sources and prompt logic\n- \u2705 **Type safety** - Abstract base classes enforce contracts\n- \u2705 **Testable** - Unit test each component independently\n- \u2705 **Extensible** - Easy to create custom providers and contexts\n- \u2705 **Async ready** - Support for both sync and async operations\n\n## Quick Start\n\n```bash\nuv pip install proompt\n```\n\n```python\nfrom proompt.data import FileDataProvider\n\n# Read a file and inject it into your prompt\nprovider = FileDataProvider(\"data.txt\")\ncontent = provider.run() # Returns file contents as string\n\nprint(f\"Analyze the data:\\n{content}\")\n```\n\n## Core Concepts\n\nA few example classes for extending the `DataProvider` class can be found in the `proompt.data` module.\n\n### \ud83d\udd0c Providers - Inject Data from Any Source\n\nProviders fetch data from external sources and format it for LLM consumption:\n\n```python\nfrom proompt.data import CsvDataProvider, SqliteProvider\n\n# CSV data as markdown tables\ncsv_provider = CsvDataProvider(\"sales_data.csv\")\nprint(csv_provider.run())\n# | Product | Sales | Region |\n# | ------- | ----- | ------ |\n# | Widget | 1000 | North |\n\n# Database queries as markdown tables\ndb_provider = SqliteProvider(\n \"company.db\",\n 'SELECT * FROM employees WHERE department = \"Engineering\"'\n)\nprint(db_provider.run())\n# | name | role | salary |\n# | ----- | --------- | ------ |\n# | Alice | Developer | 85000 |\n```\n\n### \ud83d\udee0\ufe0f Tool Context - Document Functions for LLMs\n\nAutomatically generate function documentation that LLMs can understand:\n\n```python\nfrom proompt.base.context import ToolContext\n\ndef calculate_tax(income: float, rate: float = 0.25) -> float:\n \"\"\"Calculate tax owed on income.\"\"\"\n return income * rate\n\ntool_ctx = ToolContext(calculate_tax)\nprint(tool_ctx.render())\n# Name: calculate_tax\n# Description: Calculate tax owed on income.\n# Arguments: income: float, rate: float = 0.25\n# Returns: float\n# Usage: Reference description for usage.\n```\n\n### \ud83d\udcdd Prompt Sections - Compose Complex Prompts\n\nCombine providers, tools, and context into reusable sections:\n\n```python\nfrom textwrap import dedent\nfrom proompt.base.prompt import PromptSection\n\nclass DataAnalysisSection(PromptSection):\n\n def formatter(self, instruction: str) -> str:\n data = \"\\n\\n\".join(p.run() for p in self.providers)\n tools = \"\\n\\n\".join(str(t) for t in self.tools)\n\n return dedent(f\"\"\"\n {instruction}\n\n Available Data Providers:\n {data}\n\n Available Tools:\n {tools}\n \"\"\")\n\n def render(self) -> str:\n return self.formatter(\"Analyze the provided data\")\n\n# Use it\nsection = DataAnalysisSection(\n context=context, # Use Context to pass dynamic info\n tools=[ToolContext(calculate_tax)],\n CsvDataProvider(\"metrics.csv\"), # accepts any number of Providers\n)\n\nprompt = str(section) # Ready for your LLM\n```\n\n## Data Providers\n\n### File Provider\n```python\nfrom proompt.data import FileDataProvider\n\n# Read any text file\nprovider = FileDataProvider(\"config.yaml\")\ncontent = provider.run(). # raw string content\n```\n\n**NOTE**: for structured YAML parsing, extend `DataProvider` to create `YamlProvider` class\n\n### CSV Provider\n```python\nfrom proompt.data import CsvDataProvider\n\n# Automatically converts CSV to markdown tables\nprovider = CsvDataProvider(\"data.csv\")\ntable = provider.run() # Returns formatted markdown table\n```\n\nSee `proompt.data.TableData` and `proompt.data.to_markdown_table()` for conversion.\n\n### SQLite Provider\n```python\nfrom proompt.data import SqliteProvider\n\n# Execute SQL queries, get markdown tables\nprovider = SqliteProvider(\n database_path=\"app.db\",\n query=\"SELECT name, email FROM users WHERE active = 1\",\n table_name=\"users\" # Optional, for better context\n)\n\n# Async support; NOTE the async only runs sync method .run()\nresult = await provider.arun()\n```\n\n**NOTE**: A _true_ asynchronous method would need to be defined when extending the `DataProvider` class.\n\n## Advanced Usage\n\n### Custom Providers\n\nCreating custom providers is straightforward:\n\n```python\nfrom proompt.base.provider import BaseProvider\nimport requests\n\nclass ApiProvider(BaseProvider, str):\n\n def __init__(self, url: str, api_key: str):\n self.url = url\n self.api_key = api_key\n\n @property\n def name(self) -> str:\n return f\"API Provider for {self.url}\"\n\n @property\n def provider_ctx(self) -> str:\n return f\"Fetches data from REST API at {self.url}\"\n # NOTE: would be useful to include available endpoints\n\n def run(self, endpoint: str) -> str:\n response = requests.get(\n f\"{self.url}/{endpoint}\",\n headers={\"Authorization\": f\"Bearer {self.api_key}\"}\n )\n return response.json()\n\n# Use your custom provider\napi = ApiProvider(\"https://api.example.com\", \"your-key\")\ndata = api.run(\"users\")\n```\n\n### Data Transformation\n\nConvert any data format to LLM-friendly markdown:\n\n```python\nfrom proompt.data import TableData\n\n# From dictionaries\ndata = [\n {\"name\": \"Alice\", \"role\": \"Engineer\", \"salary\": 85000},\n {\"name\": \"Bob\", \"role\": \"Designer\", \"salary\": 75000}\n]\n\ntable = TableData.from_dicts(data)\nmarkdown = table.to_md()\nprint(markdown)\n# | name | role | salary |\n# | ----- | -------- | ------ |\n# | Alice | Engineer | 85000 |\n# | Bob | Designer | 75000 |\n```\n\n## API Reference\n\n### Core Classes\n\n- **`BaseProvider`** - Abstract base for all data providers\n- **`Context`** - Abstract base for prompt contexts\n- **`ToolContext`** - Documents functions for LLM consumption\n- **`PromptSection`** - Composable prompt sections\n- **`BasePrompt`** - Full prompt composition\n\n### Concrete Providers\n\n- **`FileDataProvider`** - Read text files\n- **`CsvDataProvider`** - Read CSV files as markdown tables\n- **`SqliteProvider`** - Execute SQL queries as markdown tables\n\n### Utilities\n\n- **`TableData`** - Convert various formats to markdown tables\n- **`to_markdown_table()`** - Low-level table formatting\n\n## Why Object-Oriented Prompts?\n\n**Better Organization**\n```python\n# Instead of managing giant prompt strings\nSYSTEM_PROMPT = \"\"\"You are an assistant...\"\"\"\nDATA_SECTION = \"\"\"Here is the data: {data}\"\"\"\nTOOL_SECTION = \"\"\"Available tools: {tools}\"\"\"\n\n# Compose from organized, testable objects\nprompt = ChatPrompt(\n SystemSection(\"You are an assistant...\"),\n DataSection(providers=[csv_provider, db_provider]),\n ToolSection(tools=[calculator, parser])\n)\n```\n\n**Easier Testing**\n```python\n# Test individual components\ndef test_csv_provider():\n provider = CsvDataProvider(\"test.csv\")\n result = provider.run()\n assert \"| Name |\" in result\n\ndef test_tool_context():\n ctx = ToolContext(my_function)\n assert \"my_function\" in ctx.render()\n```\n\n**Reusable Components**\n```python\n# Define once, use everywhere\nanalysis_section = DataAnalysisSection(\n providers=[CsvDataProvider(\"metrics.csv\")]\n)\n\n# Reuse in different prompts\ncustomer_prompt = CustomerPrompt(analysis_section, ...)\nadmin_prompt = AdminPrompt(analysis_section, ...)\n```\n\n## Contributing\n\nComing soon\n\n<!--\n\nProompt is designed to be extensible. Common extension points:\n\n1. **Custom Providers** - Connect to new data sources\n2. **Custom Contexts** - New ways to document tools/functions\n3. **Custom Sections** - Domain-specific prompt components\n4. **Custom Prompts** - Full prompt templates for specific use cases\n\n\nSee the [Contributing Guide](CONTRIBUTING.md) for details.\n-->\n",
"bugtrack_url": null,
"license": null,
"summary": "Object-oriented prompting for Python",
"version": "0.1.1",
"project_urls": {
"homepage": "https://github.com/Burhan-Q/proompt",
"issues": "https://github.com/Burhan-Q/proompt/issues",
"source": "https://github.com/Burhan-Q/proompt"
},
"split_keywords": [
"ai",
" prompting",
" prompts",
" object oriented",
" ai agent",
" prompt toolkit",
" devtools",
" developer tools",
" llm",
" prompt",
" code tools",
" structure"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "0cb0679bf3326b1c3f6ee51555dbfc0462fc725a8fd3a6b9fc17e21ba12288bc",
"md5": "e2b88985b95efc49bcd071ed42217384",
"sha256": "c25cd855dc4aa863ef5d21e23fb384148cb36399b5c191bce9d0d5bf95c9d41b"
},
"downloads": -1,
"filename": "proompt-0.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "e2b88985b95efc49bcd071ed42217384",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 13819,
"upload_time": "2025-10-07T13:31:16",
"upload_time_iso_8601": "2025-10-07T13:31:16.603903Z",
"url": "https://files.pythonhosted.org/packages/0c/b0/679bf3326b1c3f6ee51555dbfc0462fc725a8fd3a6b9fc17e21ba12288bc/proompt-0.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "9d79eb5b594aa21fa06e8e24c7d77a3a495cbee65ddf4fd29e8cd1e2f3086aea",
"md5": "10a13b5b909ea3139d3f1071798984fc",
"sha256": "d4fc6ee80d16d00618825348b9cee38f4e7e437e7588e78f42ef400bf6cf9d2f"
},
"downloads": -1,
"filename": "proompt-0.1.1.tar.gz",
"has_sig": false,
"md5_digest": "10a13b5b909ea3139d3f1071798984fc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 19647,
"upload_time": "2025-10-07T13:31:17",
"upload_time_iso_8601": "2025-10-07T13:31:17.392786Z",
"url": "https://files.pythonhosted.org/packages/9d/79/eb5b594aa21fa06e8e24c7d77a3a495cbee65ddf4fd29e8cd1e2f3086aea/proompt-0.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-07 13:31:17",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Burhan-Q",
"github_project": "proompt",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "proompt"
}