prompture 0.0.35.dev1__py3-none-any.whl → 0.0.36.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
prompture/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.35.dev1'
32
- __version_tuple__ = version_tuple = (0, 0, 35, 'dev1')
31
+ __version__ = version = '0.0.36.dev1'
32
+ __version_tuple__ = version_tuple = (0, 0, 36, 'dev1')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,369 @@
1
+ Metadata-Version: 2.4
2
+ Name: prompture
3
+ Version: 0.0.36.dev1
4
+ Summary: Ask LLMs to return structured JSON and run cross-model tests. API-first.
5
+ Author-email: Juan Denis <juan@vene.co>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jhd3197/prompture
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: anthropic>=0.8.0
14
+ Requires-Dist: click>=8.0
15
+ Requires-Dist: google-generativeai>=0.3.0
16
+ Requires-Dist: groq>=0.4.0
17
+ Requires-Dist: httpx>=0.25.0
18
+ Requires-Dist: jsonschema>=4.0
19
+ Requires-Dist: openai>=1.0.0
20
+ Requires-Dist: pandas>=1.3.0
21
+ Requires-Dist: pydantic>=1.10
22
+ Requires-Dist: pydantic-settings>=2.0
23
+ Requires-Dist: python-dotenv>=0.19.0
24
+ Requires-Dist: python-toon>=0.1.0
25
+ Requires-Dist: requests>=2.28
26
+ Requires-Dist: python-dateutil>=2.9.0
27
+ Requires-Dist: tukuy>=0.0.6
28
+ Requires-Dist: pyyaml>=6.0
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest>=7.0; extra == "test"
31
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.8.0; extra == "dev"
36
+ Provides-Extra: airllm
37
+ Requires-Dist: airllm>=2.8.0; extra == "airllm"
38
+ Provides-Extra: redis
39
+ Requires-Dist: redis>=4.0; extra == "redis"
40
+ Provides-Extra: serve
41
+ Requires-Dist: fastapi>=0.100; extra == "serve"
42
+ Requires-Dist: uvicorn[standard]>=0.20; extra == "serve"
43
+ Requires-Dist: sse-starlette>=1.6; extra == "serve"
44
+ Provides-Extra: scaffold
45
+ Requires-Dist: jinja2>=3.0; extra == "scaffold"
46
+ Dynamic: license-file
47
+
48
+ <p align="center">
49
+ <h1 align="center">Prompture</h1>
50
+ <p align="center">Structured JSON extraction from any LLM. Schema-enforced, Pydantic-native, multi-provider.</p>
51
+ </p>
52
+
53
+ <p align="center">
54
+ <a href="https://pypi.org/project/prompture/"><img src="https://badge.fury.io/py/prompture.svg" alt="PyPI version"></a>
55
+ <a href="https://pypi.org/project/prompture/"><img src="https://img.shields.io/pypi/pyversions/prompture.svg" alt="Python versions"></a>
56
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
57
+ <a href="https://pepy.tech/project/prompture"><img src="https://static.pepy.tech/badge/prompture" alt="Downloads"></a>
58
+ <a href="https://github.com/jhd3197/prompture"><img src="https://img.shields.io/github/stars/jhd3197/prompture?style=social" alt="GitHub stars"></a>
59
+ </p>
60
+
61
+ ---
62
+
63
+ **Prompture** is a Python library that turns LLM responses into validated, structured data. Define a schema or Pydantic model, point it at any provider, and get typed output back — with token tracking, cost calculation, and automatic JSON repair built in.
64
+
65
+ ```python
66
+ from pydantic import BaseModel
67
+ from prompture import extract_with_model
68
+
69
+ class Person(BaseModel):
70
+ name: str
71
+ age: int
72
+ profession: str
73
+
74
+ person = extract_with_model(Person, "Maria is 32, a developer in NYC.", model_name="openai/gpt-4")
75
+ print(person.name) # Maria
76
+ ```
77
+
78
+ ## Key Features
79
+
80
+ - **Structured output** — JSON schema enforcement and direct Pydantic model population
81
+ - **12 providers** — OpenAI, Claude, Google, Groq, Grok, Azure, Ollama, LM Studio, OpenRouter, HuggingFace, AirLLM, and generic HTTP
82
+ - **TOON input conversion** — 45-60% token savings when sending structured data via [Token-Oriented Object Notation](https://github.com/jhd3197/python-toon)
83
+ - **Stepwise extraction** — Per-field prompts with smart type coercion (shorthand numbers, multilingual booleans, dates)
84
+ - **Field registry** — 50+ predefined extraction fields with template variables and Pydantic integration
85
+ - **Conversations** — Stateful multi-turn sessions with sync and async support
86
+ - **Tool use** — Function calling and streaming across supported providers
87
+ - **Caching** — Built-in response cache with memory, SQLite, and Redis backends
88
+ - **Plugin system** — Register custom drivers via entry points
89
+ - **Usage tracking** — Token counts and cost calculation on every call
90
+ - **Auto-repair** — Optional second LLM pass to fix malformed JSON
91
+ - **Batch testing** — Spec-driven suites to compare models side by side
92
+
93
+ ## Installation
94
+
95
+ ```bash
96
+ pip install prompture
97
+ ```
98
+
99
+ Optional extras:
100
+
101
+ ```bash
102
+ pip install prompture[redis] # Redis cache backend
103
+ pip install prompture[serve] # FastAPI server mode
104
+ pip install prompture[airllm] # AirLLM local inference
105
+ ```
106
+
107
+ ## Configuration
108
+
109
+ Set API keys for the providers you use. Prompture reads from environment variables or a `.env` file:
110
+
111
+ ```bash
112
+ OPENAI_API_KEY=sk-...
113
+ ANTHROPIC_API_KEY=sk-ant-...
114
+ GOOGLE_API_KEY=...
115
+ GROQ_API_KEY=...
116
+ GROK_API_KEY=...
117
+ OPENROUTER_API_KEY=...
118
+ AZURE_OPENAI_ENDPOINT=...
119
+ AZURE_OPENAI_API_KEY=...
120
+ ```
121
+
122
+ Local providers (Ollama, LM Studio) work out of the box with no keys required.
123
+
124
+ ## Providers
125
+
126
+ Model strings use `"provider/model"` format. The provider prefix routes to the correct driver automatically.
127
+
128
+ | Provider | Example Model | Cost |
129
+ |---|---|---|
130
+ | `openai` | `openai/gpt-4` | Automatic |
131
+ | `claude` | `claude/claude-3` | Automatic |
132
+ | `google` | `google/gemini-1.5-pro` | Automatic |
133
+ | `groq` | `groq/llama2-70b-4096` | Automatic |
134
+ | `grok` | `grok/grok-4-fast-reasoning` | Automatic |
135
+ | `azure` | `azure/deployed-name` | Automatic |
136
+ | `openrouter` | `openrouter/anthropic/claude-2` | Automatic |
137
+ | `ollama` | `ollama/llama3.1:8b` | Free (local) |
138
+ | `lmstudio` | `lmstudio/local-model` | Free (local) |
139
+ | `huggingface` | `hf/model-name` | Free (local) |
140
+ | `http` | `http/self-hosted` | Free |
141
+
142
+ ## Usage
143
+
144
+ ### One-Shot Pydantic Extraction
145
+
146
+ Single LLM call, returns a validated Pydantic instance:
147
+
148
+ ```python
149
+ from typing import List, Optional
150
+ from pydantic import BaseModel
151
+ from prompture import extract_with_model
152
+
153
+ class Person(BaseModel):
154
+ name: str
155
+ age: int
156
+ profession: str
157
+ city: str
158
+ hobbies: List[str]
159
+ education: Optional[str] = None
160
+
161
+ person = extract_with_model(
162
+ Person,
163
+ "Maria is 32, a software developer in New York. She loves hiking and photography.",
164
+ model_name="openai/gpt-4"
165
+ )
166
+ print(person.model_dump())
167
+ ```
168
+
169
+ ### Stepwise Extraction
170
+
171
+ One LLM call per field. Higher accuracy, per-field error recovery:
172
+
173
+ ```python
174
+ from prompture import stepwise_extract_with_model
175
+
176
+ result = stepwise_extract_with_model(
177
+ Person,
178
+ "Maria is 32, a software developer in New York. She loves hiking and photography.",
179
+ model_name="openai/gpt-4"
180
+ )
181
+ print(result["model"].model_dump())
182
+ print(result["usage"]) # per-field and total token usage
183
+ ```
184
+
185
+ | Aspect | `extract_with_model` | `stepwise_extract_with_model` |
186
+ |---|---|---|
187
+ | LLM calls | 1 | N (one per field) |
188
+ | Speed / cost | Faster, cheaper | Slower, higher |
189
+ | Accuracy | Good global coherence | Higher per-field accuracy |
190
+ | Error handling | All-or-nothing | Per-field recovery |
191
+
192
+ ### JSON Schema Extraction
193
+
194
+ For raw JSON output with full control:
195
+
196
+ ```python
197
+ from prompture import ask_for_json
198
+
199
+ schema = {
200
+ "type": "object",
201
+ "required": ["name", "age"],
202
+ "properties": {
203
+ "name": {"type": "string"},
204
+ "age": {"type": "integer"}
205
+ }
206
+ }
207
+
208
+ result = ask_for_json(
209
+ content_prompt="Extract the person's info from: John is 28 and lives in Miami.",
210
+ json_schema=schema,
211
+ model_name="openai/gpt-4"
212
+ )
213
+ print(result["json_object"]) # {"name": "John", "age": 28}
214
+ print(result["usage"]) # token counts and cost
215
+ ```
216
+
217
+ ### TOON Input — Token Savings
218
+
219
+ Analyze structured data with automatic TOON conversion for 45-60% fewer tokens:
220
+
221
+ ```python
222
+ from prompture import extract_from_data
223
+
224
+ products = [
225
+ {"id": 1, "name": "Laptop", "price": 999.99, "rating": 4.5},
226
+ {"id": 2, "name": "Book", "price": 19.99, "rating": 4.2},
227
+ {"id": 3, "name": "Headphones", "price": 149.99, "rating": 4.7},
228
+ ]
229
+
230
+ result = extract_from_data(
231
+ data=products,
232
+ question="What is the average price and highest rated product?",
233
+ json_schema={
234
+ "type": "object",
235
+ "properties": {
236
+ "average_price": {"type": "number"},
237
+ "highest_rated": {"type": "string"}
238
+ }
239
+ },
240
+ model_name="openai/gpt-4"
241
+ )
242
+
243
+ print(result["json_object"])
244
+ # {"average_price": 389.99, "highest_rated": "Headphones"}
245
+
246
+ print(f"Token savings: {result['token_savings']['percentage_saved']}%")
247
+ ```
248
+
249
+ Works with Pandas DataFrames via `extract_from_pandas()`.
250
+
251
+ ### Field Definitions
252
+
253
+ Use the built-in field registry for consistent extraction across models:
254
+
255
+ ```python
256
+ from pydantic import BaseModel
257
+ from prompture import field_from_registry, stepwise_extract_with_model
258
+
259
+ class Person(BaseModel):
260
+ name: str = field_from_registry("name")
261
+ age: int = field_from_registry("age")
262
+ email: str = field_from_registry("email")
263
+ occupation: str = field_from_registry("occupation")
264
+
265
+ result = stepwise_extract_with_model(
266
+ Person,
267
+ "John Smith, 25, software engineer at TechCorp, john@example.com",
268
+ model_name="openai/gpt-4"
269
+ )
270
+ ```
271
+
272
+ Register custom fields with template variables:
273
+
274
+ ```python
275
+ from prompture import register_field
276
+
277
+ register_field("document_date", {
278
+ "type": "str",
279
+ "description": "Document creation date",
280
+ "instructions": "Use {{current_date}} if not specified",
281
+ "default": "{{current_date}}",
282
+ "nullable": False
283
+ })
284
+ ```
285
+
286
+ ### Conversations
287
+
288
+ Stateful multi-turn sessions:
289
+
290
+ ```python
291
+ from prompture import Conversation
292
+
293
+ conv = Conversation(model_name="openai/gpt-4")
294
+ conv.add_message("system", "You are a helpful assistant.")
295
+ response = conv.send("What is the capital of France?")
296
+ follow_up = conv.send("What about Germany?") # retains context
297
+ ```
298
+
299
+ ### Model Discovery
300
+
301
+ Auto-detect available models from configured providers:
302
+
303
+ ```python
304
+ from prompture import get_available_models
305
+
306
+ models = get_available_models()
307
+ for model in models:
308
+ print(model) # "openai/gpt-4", "ollama/llama3:latest", ...
309
+ ```
310
+
311
+ ### Logging and Debugging
312
+
313
+ ```python
314
+ import logging
315
+ from prompture import configure_logging
316
+
317
+ configure_logging(logging.DEBUG)
318
+ ```
319
+
320
+ ### Response Shape
321
+
322
+ All extraction functions return a consistent structure:
323
+
324
+ ```python
325
+ {
326
+ "json_string": str, # raw JSON text
327
+ "json_object": dict, # parsed result
328
+ "usage": {
329
+ "prompt_tokens": int,
330
+ "completion_tokens": int,
331
+ "total_tokens": int,
332
+ "cost": float,
333
+ "model_name": str
334
+ }
335
+ }
336
+ ```
337
+
338
+ ## CLI
339
+
340
+ ```bash
341
+ prompture run <spec-file>
342
+ ```
343
+
344
+ Run spec-driven extraction suites for cross-model comparison.
345
+
346
+ ## Development
347
+
348
+ ```bash
349
+ # Install with dev dependencies
350
+ pip install -e ".[test,dev]"
351
+
352
+ # Run tests
353
+ pytest
354
+
355
+ # Run integration tests (requires live LLM access)
356
+ pytest --run-integration
357
+
358
+ # Lint and format
359
+ ruff check .
360
+ ruff format .
361
+ ```
362
+
363
+ ## Contributing
364
+
365
+ PRs welcome. Please add tests for new functionality and examples under `examples/` for new drivers or patterns.
366
+
367
+ ## License
368
+
369
+ [MIT](https://opensource.org/licenses/MIT)
@@ -1,5 +1,5 @@
1
1
  prompture/__init__.py,sha256=IzJ-QVN9MYX0VMjPEqsulDDDBvHKQpR9GtBYptSmRBg,4690
2
- prompture/_version.py,sha256=1k1haBx_iRPgCgBKKvyxklmjTUrSaUphcgZG-UVbPQg,719
2
+ prompture/_version.py,sha256=o0BefqYWr6eo-iD-swhaYm6LSsw_Oae87WWVmOMeWSA,719
3
3
  prompture/async_conversation.py,sha256=i-cbV4WfPxU6m-SgeC7X-xl_Hg20B6zK-U4CJjeLFIA,23529
4
4
  prompture/async_core.py,sha256=s8G0nGUGR1Bf_BQG9_FcQRpveSnJKkEwcWNfbAJaSkg,29208
5
5
  prompture/async_driver.py,sha256=tyk8qc7VjMCyfP9MBAG15-EXa2kBM3tVhpJABGWAgFU,6075
@@ -58,9 +58,9 @@ prompture/scaffold/templates/env.example.j2,sha256=eESKr1KWgyrczO6d-nwAhQwSpf_G-
58
58
  prompture/scaffold/templates/main.py.j2,sha256=TEgc5OvsZOEX0JthkSW1NI_yLwgoeVN_x97Ibg-vyWY,2632
59
59
  prompture/scaffold/templates/models.py.j2,sha256=JrZ99GCVK6TKWapskVRSwCssGrTu5cGZ_r46fOhY2GE,858
60
60
  prompture/scaffold/templates/requirements.txt.j2,sha256=m3S5fi1hq9KG9l_9j317rjwWww0a43WMKd8VnUWv2A4,102
61
- prompture-0.0.35.dev1.dist-info/licenses/LICENSE,sha256=0HgDepH7aaHNFhHF-iXuW6_GqDfYPnVkjtiCAZ4yS8I,1060
62
- prompture-0.0.35.dev1.dist-info/METADATA,sha256=wOl-jgZkmBFwASlAn-F_Jk-3vNnl8rntQ-KorUK3ERA,18483
63
- prompture-0.0.35.dev1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
64
- prompture-0.0.35.dev1.dist-info/entry_points.txt,sha256=AFPG3lJR86g4IJMoWQUW5Ph7G6MLNWG3A2u2Tp9zkp8,48
65
- prompture-0.0.35.dev1.dist-info/top_level.txt,sha256=to86zq_kjfdoLeAxQNr420UWqT0WzkKoZ509J7Qr2t4,10
66
- prompture-0.0.35.dev1.dist-info/RECORD,,
61
+ prompture-0.0.36.dev1.dist-info/licenses/LICENSE,sha256=0HgDepH7aaHNFhHF-iXuW6_GqDfYPnVkjtiCAZ4yS8I,1060
62
+ prompture-0.0.36.dev1.dist-info/METADATA,sha256=d0rm_9Sn2zmbtQgvbwgH6d7ZXspBSeiaC28bZZmYG1E,10842
63
+ prompture-0.0.36.dev1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
64
+ prompture-0.0.36.dev1.dist-info/entry_points.txt,sha256=AFPG3lJR86g4IJMoWQUW5Ph7G6MLNWG3A2u2Tp9zkp8,48
65
+ prompture-0.0.36.dev1.dist-info/top_level.txt,sha256=to86zq_kjfdoLeAxQNr420UWqT0WzkKoZ509J7Qr2t4,10
66
+ prompture-0.0.36.dev1.dist-info/RECORD,,
@@ -1,464 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: prompture
3
- Version: 0.0.35.dev1
4
- Summary: Ask LLMs to return structured JSON and run cross-model tests. API-first.
5
- Author-email: Juan Denis <juan@vene.co>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/jhd3197/prompture
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Operating System :: OS Independent
10
- Requires-Python: >=3.9
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
- Requires-Dist: anthropic>=0.8.0
14
- Requires-Dist: click>=8.0
15
- Requires-Dist: google-generativeai>=0.3.0
16
- Requires-Dist: groq>=0.4.0
17
- Requires-Dist: httpx>=0.25.0
18
- Requires-Dist: jsonschema>=4.0
19
- Requires-Dist: openai>=1.0.0
20
- Requires-Dist: pandas>=1.3.0
21
- Requires-Dist: pydantic>=1.10
22
- Requires-Dist: pydantic-settings>=2.0
23
- Requires-Dist: python-dotenv>=0.19.0
24
- Requires-Dist: python-toon>=0.1.0
25
- Requires-Dist: requests>=2.28
26
- Requires-Dist: python-dateutil>=2.9.0
27
- Requires-Dist: tukuy>=0.0.6
28
- Requires-Dist: pyyaml>=6.0
29
- Provides-Extra: test
30
- Requires-Dist: pytest>=7.0; extra == "test"
31
- Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
32
- Provides-Extra: dev
33
- Requires-Dist: pytest>=7.0; extra == "dev"
34
- Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
35
- Requires-Dist: ruff>=0.8.0; extra == "dev"
36
- Provides-Extra: airllm
37
- Requires-Dist: airllm>=2.8.0; extra == "airllm"
38
- Provides-Extra: redis
39
- Requires-Dist: redis>=4.0; extra == "redis"
40
- Provides-Extra: serve
41
- Requires-Dist: fastapi>=0.100; extra == "serve"
42
- Requires-Dist: uvicorn[standard]>=0.20; extra == "serve"
43
- Requires-Dist: sse-starlette>=1.6; extra == "serve"
44
- Provides-Extra: scaffold
45
- Requires-Dist: jinja2>=3.0; extra == "scaffold"
46
- Dynamic: license-file
47
-
48
- # Prompture
49
-
50
- [![PyPI version](https://badge.fury.io/py/prompture.svg)](https://badge.fury.io/py/prompture)
51
- [![Python Versions](https://img.shields.io/pypi/pyversions/prompture.svg)](https://pypi.org/project/prompture/)
52
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
53
- [![Downloads](https://static.pepy.tech/badge/prompture)](https://pepy.tech/project/prompture)
54
- ![GitHub Repo stars](https://img.shields.io/github/stars/jhd3197/prompture?style=social)
55
-
56
-
57
- **Prompture** is an API-first library for getting **structured JSON** (or any structure) from LLMs, validating it, and benchmarking multiple models with one spec.
58
-
59
- ## ✨ Features
60
-
61
- - ✅ **Structured output** → JSON schema enforcement, or direct **Pydantic** instances
62
- - ✅ **TOON input conversion** → 45-60% token savings for structured data analysis with `extract_from_data()` and `extract_from_pandas()`
63
- - ✅ **Stepwise extraction** → Per-field prompts, with smart type conversion (incl. shorthand numbers)
64
- - ✅ **Multi-driver** → OpenAI, Azure, Claude, Ollama, LM Studio, Google, Groq, OpenRouter, Grok, HTTP, Mock, HuggingFace (via `get_driver()`)
65
- - ✅ **Usage & cost** → Token + $ tracking on every call (`usage` from driver meta)
66
- - ✅ **AI cleanup** → Optional LLM pass to fix malformed JSON
67
- - ✅ **Batch testing** → Define suites and compare models (spec-driven)
68
- - 🧪 **Experimental TOON output** → Request Token-Oriented Object Notation when you need ultra-compact text
69
- <br>
70
-
71
- > [!TIP]
72
- > Starring this repo helps more developers discover Prompture ✨
73
- >
74
- >![prompture_no_forks](https://github.com/user-attachments/assets/720f888e-a885-4eb3-970c-ba5809fe2ce7)
75
- >
76
- > 🔥 Also check out my other project [RepoGif](https://github.com/jhd3197/RepoGif) – the tool I used to generate the GIF above!
77
- <br>
78
-
79
-
80
- ---
81
-
82
- ## Installation
83
-
84
- ```bash
85
- pip install prompture
86
- ````
87
-
88
- ---
89
-
90
- ## Configure a Provider
91
-
92
- Model names now support provider prefixes (e.g., "ollama/llama3.1:8b"). The `get_driver_for_model()` function automatically selects the appropriate driver based on the provider prefix.
93
-
94
- You can configure providers either through environment variables or by using provider-prefixed model names:
95
-
96
- ```bash
97
- # Environment variable approach:
98
- export AI_PROVIDER=ollama # One of: ollama | openai | azure | claude | google | groq | openrouter | grok | lmstudio | http | huggingface
99
-
100
- # Only if the provider needs them:
101
- export OPENAI_API_KEY=...
102
- export AZURE_OPENAI_ENDPOINT=...
103
- export AZURE_OPENAI_API_KEY=...
104
- export ANTHROPIC_API_KEY=...
105
- export GOOGLE_API_KEY=...
106
- export GROQ_API_KEY=...
107
- export OPENROUTER_API_KEY=...
108
- export GROK_API_KEY=...
109
- export LMSTUDIO_ENDPOINT=...
110
- ```
111
-
112
- | Provider | Example models | Cost calc |
113
- | -------- | ------------------------------------- | --------------- |
114
- | `ollama` | `ollama/llama3.1:8b`, `ollama/qwen2.5:3b` | `$0.00` (local) |
115
- | `openai` | `openai/gpt-4`, `openai/gpt-3.5-turbo` | Automatic |
116
- | `azure` | `azure/deployed-name` | Automatic |
117
- | `claude` | `claude/claude-3` | Automatic |
118
- | `google` | `google/gemini-1.5-pro`, `google/gemini-1.5-flash` | Automatic |
119
- | `groq` | `groq/llama2-70b-4096`, `groq/mixtral-8x7b-32768` | Automatic |
120
- | `openrouter` | `openrouter/openai/gpt-3.5-turbo`, `openrouter/anthropic/claude-2` | Automatic |
121
- | `grok` | `grok/grok-4-fast-reasoning`, `grok/grok-3-mini` | Automatic |
122
- | `lmstudio` | `lmstudio/local-model` | `$0.00` (local) |
123
- | `huggingface` | `hf/local-or-endpoint` | `$0.00` (local) |
124
- | `http` | `http/self-hosted` | `$0.00` |
125
-
126
- ---
127
-
128
- ## 🔍 Model Discovery
129
-
130
- Prompture can auto-detect available models from your configured environment. This is especially useful for local setups (like Ollama) or when you want to see which models are available to your application.
131
-
132
- ```python
133
- from prompture import get_available_models
134
-
135
- # Returns a list of strings like ["openai/gpt-4o", "ollama/llama3:latest", ...]
136
- models = get_available_models()
137
-
138
- for model in models:
139
- print(f"Found: {model}")
140
- ```
141
-
142
- - **Static Drivers** (OpenAI, Claude, Azure, etc.): Returns models listed in the driver's `MODEL_PRICING` configuration if the driver is configured (API key present).
143
- - **Dynamic Drivers** (Ollama): Queries the local endpoint (e.g., `http://localhost:11434/api/tags`) to fetch currently installed models.
144
-
145
- ---
146
-
147
- ## Quickstart: Pydantic in one line (auto driver)
148
-
149
- Use `extract_with_model` for a single LLM call that fills your Pydantic model.
150
-
151
- ```python
152
- from typing import List, Optional
153
- from pydantic import BaseModel
154
- from prompture import extract_with_model
155
-
156
- class Person(BaseModel):
157
- name: str
158
- age: int
159
- profession: str
160
- city: str
161
- hobbies: List[str]
162
- education: Optional[str] = None
163
-
164
- text = "Maria is 32, a software developer in New York. She loves hiking and photography."
165
-
166
- # Uses get_driver_for_model() internally based on model name prefix
167
- person = extract_with_model(Person, text, model_name="ollama/gpt-oss:20b")
168
- print(person.dict())
169
- ```
170
-
171
- **Why start here?** It's fast (one call), cost-efficient, and returns a validated Pydantic instance.
172
-
173
-
174
- ## 🚀 TOON Input Conversion: 45-60% Token Savings
175
-
176
- Analyze structured data with automatic TOON (Token-Oriented Object Notation) conversion for massive token savings.
177
-
178
- ```python
179
- from prompture import extract_from_data, extract_from_pandas
180
-
181
- # Your product data
182
- products = [
183
- {"id": 1, "name": "Laptop", "price": 999.99, "rating": 4.5},
184
- {"id": 2, "name": "Book", "price": 19.99, "rating": 4.2},
185
- {"id": 3, "name": "Headphones", "price": 149.99, "rating": 4.7}
186
- ]
187
-
188
- # Ask questions about your data - automatically uses TOON format for 60%+ token savings
189
- result = extract_from_data(
190
- data=products,
191
- question="What is the average price and highest rated product?",
192
- json_schema={
193
- "type": "object",
194
- "properties": {
195
- "average_price": {"type": "number"},
196
- "highest_rated": {"type": "string"}
197
- }
198
- },
199
- model_name="openai/gpt-4"
200
- )
201
-
202
- print(result["json_object"])
203
- # {"average_price": 389.96, "highest_rated": "Headphones"}
204
-
205
- print(f"Token savings: {result['token_savings']['percentage_saved']}%")
206
- # Token savings: 62.3%
207
-
208
- # Works with Pandas DataFrames too!
209
- import pandas as pd
210
- df = pd.DataFrame(products)
211
- result = extract_from_pandas(df=df, question="...", json_schema=schema, model_name="openai/gpt-4")
212
- ```
213
-
214
- **Preview token savings without LLM calls:**
215
- ```bash
216
- python examples/token_comparison_utility.py
217
- ```
218
-
219
- > **Note:** Both `python-toon` and `pandas` are now included by default when you install Prompture!
220
-
221
- ---
222
- ---
223
-
224
- ## 📋 Field Definitions
225
-
226
- Prompture includes a powerful **field definitions system** that provides a centralized registry of structured data extraction fields. This system enables consistent, reusable field configurations across your data extraction workflows with built-in fields for common use cases like personal info, contact details, professional data, and more.
227
-
228
- **Key benefits:**
229
- - 🎯 Pre-configured fields with descriptions and extraction instructions
230
- - 🔄 Template variables like `{{current_year}}`, `{{current_date}}`, `{{current_datetime}}`
231
- - 🔌 Seamless Pydantic integration via `field_from_registry()`
232
- - ⚙️ Easy custom field registration
233
-
234
- ### Using Built-in Fields
235
-
236
- ```python
237
- from pydantic import BaseModel
238
- from prompture import field_from_registry, stepwise_extract_with_model
239
-
240
- class Person(BaseModel):
241
- name: str = field_from_registry("name")
242
- age: int = field_from_registry("age")
243
- email: str = field_from_registry("email")
244
- occupation: str = field_from_registry("occupation")
245
- company: str = field_from_registry("company")
246
-
247
- # Built-in fields include: name, age, email, phone, address, city, country,
248
- # occupation, company, education_level, salary, and many more!
249
-
250
- result = stepwise_extract_with_model(
251
- Person,
252
- "John Smith is 25 years old, software engineer at TechCorp, john@example.com",
253
- model_name="openai/gpt-4"
254
- )
255
- ```
256
-
257
- ### Registering Custom Fields
258
-
259
- ```python
260
- from prompture import register_field, field_from_registry
261
-
262
- # Register a custom field with template variables
263
- register_field("document_date", {
264
- "type": "str",
265
- "description": "Document creation or processing date",
266
- "instructions": "Use {{current_date}} if not specified in document",
267
- "default": "{{current_date}}",
268
- "nullable": False
269
- })
270
-
271
- # Use custom field in your model
272
- class Document(BaseModel):
273
- title: str = field_from_registry("name")
274
- created_date: str = field_from_registry("document_date")
275
- ```
276
-
277
- 📚 **[View Full Field Definitions Reference →](https://prompture.readthedocs.io/en/latest/field_definitions_reference.html)**
278
-
279
- ---
280
-
281
- ## JSON-first (low-level primitives)
282
-
283
- When you want raw JSON with a schema and full control, use `ask_for_json` or `extract_and_jsonify`.
284
-
285
- ```python
286
- from prompture.drivers import get_driver
287
- from prompture import ask_for_json, extract_and_jsonify
288
-
289
- schema = {
290
- "type": "object",
291
- "required": ["name", "age"],
292
- "properties": {
293
- "name": {"type": "string"},
294
- "age": {"type": "integer"}
295
- }
296
- }
297
-
298
- # 1) ask_for_json: you provide the full content prompt
299
- resp1 = ask_for_json(
300
- content_prompt="Extract the person's info from: John is 28 and lives in Miami.",
301
- json_schema=schema,
302
- model_name="google/gemini-1.5-pro"
303
- )
304
- print(resp1["json_object"], resp1["usage"])
305
-
306
- # 2) extract_and_jsonify: you provide text & an instruction template; it builds the prompt
307
- resp2 = extract_and_jsonify(
308
- text="John is 28 and lives in Miami.",
309
- json_schema=schema,
310
- model_name="groq/mixtral-8x7b-32768",
311
- instruction_template="Extract the person's information:"
312
- )
313
- print(resp2["json_object"], resp2["usage"])
314
- ```
315
-
316
- ### Experimental TOON output
317
-
318
- Prompture can ask for TOON (Token-Oriented Object Notation) instead of JSON by setting `output_format="toon"` on `ask_for_json`, `extract_and_jsonify`, `manual_extract_and_jsonify`, or `extract_with_model`. The LLM is still instructed to return JSON (for reliability); Prompture parses it and emits a TOON string via `python-toon`.
319
-
320
- ```python
321
- result = extract_and_jsonify(
322
- text="Alice Johnson is a 30-year-old data scientist...",
323
- json_schema=schema,
324
- model_name="lmstudio/deepseek/deepseek-r1-0528-qwen3-8b",
325
- output_format="toon",
326
- )
327
- print(result["toon_string"]) # TOON text generated locally
328
- print(result["json_object"]) # regular dict parsed from the JSON response
329
- # result["json_string"] still contains the original JSON text
330
- ```
331
-
332
- > [!IMPORTANT]
333
- > TOON output is **experimental**. General-purpose models often emit more verbose completions when asked for TOON, so total token usage can increase (see `toon_token_analysis.md`). Treat it as an opt-in mode until TOON-aware fine-tunes or adapters are available.
334
-
335
- ### Return shape (JSON helpers)
336
-
337
- ```python
338
- {
339
- "json_string": str,
340
- "json_object": dict,
341
- "usage": {
342
- "prompt_tokens": int,
343
- "completion_tokens": int,
344
- "total_tokens": int,
345
- "cost": float,
346
- "model_name": str
347
- }
348
- }
349
- ```
350
-
351
- > If the model returns malformed JSON and `ai_cleanup=True`, a second LLM pass tries to fix it.
352
-
353
- ---
354
-
355
- ## Pydantic: one-shot vs stepwise
356
-
357
- Prompture supports two Pydantic extraction modes:
358
-
359
- * **`extract_with_model`** → Single call; global context; best cost/latency; coherent fields
360
- * **`stepwise_extract_with_model`** → One call per field; higher per-field accuracy; resilient
361
-
362
- | Aspect | `extract_with_model` (one-shot) | `stepwise_extract_with_model` (per-field) |
363
- | -------------- | -------------------------------------- | ----------------------------------------- |
364
- | LLM calls | 1 | N (one per field) |
365
- | Speed & cost | **Faster / cheaper** | Slower / higher |
366
- | Accuracy | Good global coherence | **Higher per-field accuracy** |
367
- | Error handling | All-or-nothing | **Per-field recovery** |
368
- | Best when | Fields are related; throughput matters | Correctness per field is critical |
369
-
370
- ### Examples
371
-
372
- ```python
373
- from prompture import extract_with_model, stepwise_extract_with_model
374
-
375
- person1 = extract_with_model(Person, text, model_name="openrouter/anthropic/claude-2")
376
- print(person1.dict())
377
-
378
- res = stepwise_extract_with_model(Person, text, model_name="grok/grok-4-fast-reasoning")
379
- print(res["model"].dict())
380
- print(res["usage"]) # includes per-field usage and totals
381
- ```
382
-
383
- **Stepwise extras:** internally uses `tools.create_field_schema` + `tools.convert_value` (with `allow_shorthand=True`) so values like `"3.4m"`, `"2k"`, `"1.2b"` can be converted to typed fields where appropriate.
384
-
385
- ---
386
-
387
- ## Manual control with logging
388
-
389
- `manual_extract_and_jsonify` is like `extract_and_jsonify` but lets you provide your own driver.
390
- Enable library logging via Python's standard `logging` module:
391
-
392
- ```python
393
- import logging
394
- from prompture import manual_extract_and_jsonify, configure_logging
395
- from prompture.drivers import get_driver
396
-
397
- configure_logging(logging.DEBUG) # see internal debug output
398
-
399
- driver = get_driver("ollama")
400
- res = manual_extract_and_jsonify(
401
- driver=driver,
402
- text="Maria works as a software developer in New York.",
403
- json_schema={
404
- "type": "object",
405
- "required": ["city", "profession"],
406
- "properties": {"city": {"type": "string"}, "profession": {"type": "string"}}
407
- },
408
- model_name="llama3.1:8b",
409
- options={"temperature": 0.2},
410
- )
411
- print(res["json_object"])
412
- ```
413
-
414
- ---
415
-
416
-
417
- **Example output (Ollama comparison)** — see `examples/ollama_models_comparison.py` for a richer comparison table.
418
-
419
- ---
420
-
421
-
422
- ## Ollama Model Comparison Example
423
-
424
- This example demonstrates how to compare different Ollama models using a specific script located at `examples/ollama_models_comparison.py`.
425
-
426
- | Model | Success | Prompt | Completion | Total | Fields | Validation | Name | Price | Variants | Screen Size | Warranty | Is New |
427
- |------------------|---------|--------|------------|-------|--------|------------|---------------------|----------|----------|-------------|----------|--------|
428
- | gpt-oss:20b | True | 801 | 945 | 1746 | 8 | ✓ | GalaxyFold Ultra | 1299.99 | 9 | 6.9 | 3 | True |
429
- | deepseek-r1:latest | True | 757 | 679 | 1436 | 8 | ✗ | GalaxyFold Ultra | 1299.99 | 3 | 6.9 | None | True |
430
- | llama3.1:8b | True | 746 | 256 | 1002 | 8 | ✓ | GalaxyFold Ultra | 1299.99 | 3 | 6.9 | 3 | True |
431
- | gemma3:latest | True | 857 | 315 | 1172 | 8 | ✗ | GalaxyFold Ultra | 1299.99 | 3 | 6.9 | None | True |
432
- | qwen2.5:1.5b | True | 784 | 236 | 1020 | 8 | ✓ | GalaxyFold Ultra | 1299.99 | 3 | 6.9 | 3 | True |
433
- | qwen2.5:3b | True | 784 | 273 | 1057 | 9 | ✓ | GalaxyFold Ultra | 1299.99 | 3 | 6.9 | 3 | True |
434
- | mistral:latest | True | 928 | 337 | 1265 | 8 | ✓ | GalaxyFold Ultra | 1299.99 | 3 | 6.9 | 3 | True |
435
-
436
- > **Successful models (7):** gpt-oss:20b, deepseek-r1:latest, llama3.1:8b, gemma3:latest, qwen2.5:1.5b, qwen2.5:3b, mistral:latest
437
-
438
- You can run this comparison yourself with:
439
- `python examples/ollama_models_comparison.py`
440
-
441
- This example script compares multiple Ollama models on a complex task of extracting structured information from a smartphone description using a detailed JSON schema. The purpose of this example is to illustrate how `Prompture` can be used to test and compare different models on the same structured output task, showing their success rates, token usage, and validation results.
442
-
443
- ---
444
-
445
- ## Error handling notes
446
-
447
- * With `ai_cleanup=True`, a second LLM pass attempts to fix malformed JSON; on success, `usage` may be a minimal stub.
448
- * `extract_and_jsonify` will **skip tests** under `pytest` if there’s a local server connection error (e.g., Ollama), instead of failing the suite.
449
- * All functions raise `ValueError` for empty text.
450
-
451
- ---
452
-
453
- ## Tips & Best Practices
454
-
455
- * Add `description` to schema fields (or Pydantic field metadata) for better extractions.
456
- * Start with **one-shot Pydantic**; switch specific fields to **stepwise** if they’re noisy.
457
- * Track usage/cost before scaling; tweak `temperature` in `options` if consistency wobbles.
458
- * Use `configure_logging(logging.DEBUG)` in dev to see internal debug output and tighten your specs.
459
-
460
- ---
461
-
462
- ## Contributing
463
-
464
- PRs welcome! Add tests and—if adding drivers or patterns—drop an example under `examples/`.