prompture 0.0.32.dev1__tar.gz → 0.0.33.dev1__tar.gz

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.
Files changed (72) hide show
  1. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/.env.copy +5 -1
  2. prompture-0.0.33.dev1/CLAUDE.md +74 -0
  3. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/PKG-INFO +3 -1
  4. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/__init__.py +2 -1
  5. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/__init__.py +6 -0
  6. prompture-0.0.33.dev1/prompture/drivers/airllm_driver.py +116 -0
  7. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/settings.py +4 -0
  8. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture.egg-info/PKG-INFO +3 -1
  9. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture.egg-info/SOURCES.txt +2 -0
  10. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture.egg-info/requires.txt +3 -0
  11. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/setup.py +1 -0
  12. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/.github/FUNDING.yml +0 -0
  13. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/.github/scripts/update_docs_version.py +0 -0
  14. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/.github/scripts/update_wrapper_version.py +0 -0
  15. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/.github/workflows/dev.yml +0 -0
  16. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/.github/workflows/documentation.yml +0 -0
  17. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/.github/workflows/publish.yml +0 -0
  18. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/LICENSE +0 -0
  19. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/MANIFEST.in +0 -0
  20. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/README.md +0 -0
  21. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/VERSION +0 -0
  22. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/_static/custom.css +0 -0
  23. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/api/core.rst +0 -0
  24. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/api/drivers.rst +0 -0
  25. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/api/field_definitions.rst +0 -0
  26. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/api/index.rst +0 -0
  27. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/api/runner.rst +0 -0
  28. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/api/tools.rst +0 -0
  29. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/api/validator.rst +0 -0
  30. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/conf.py +0 -0
  31. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/contributing.rst +0 -0
  32. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/examples.rst +0 -0
  33. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/field_definitions_reference.rst +0 -0
  34. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/index.rst +0 -0
  35. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/installation.rst +0 -0
  36. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/quickstart.rst +0 -0
  37. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/docs/source/toon_input_guide.rst +0 -0
  38. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/README.md +0 -0
  39. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_json/README.md +0 -0
  40. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_json/llm_to_json/__init__.py +0 -0
  41. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_json/pyproject.toml +0 -0
  42. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_json/test.py +0 -0
  43. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_toon/README.md +0 -0
  44. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_toon/llm_to_toon/__init__.py +0 -0
  45. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_toon/pyproject.toml +0 -0
  46. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/packages/llm_to_toon/test.py +0 -0
  47. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/cli.py +0 -0
  48. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/core.py +0 -0
  49. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/discovery.py +0 -0
  50. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/driver.py +0 -0
  51. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/azure_driver.py +0 -0
  52. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/claude_driver.py +0 -0
  53. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/google_driver.py +0 -0
  54. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/grok_driver.py +0 -0
  55. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/groq_driver.py +0 -0
  56. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/hugging_driver.py +0 -0
  57. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/lmstudio_driver.py +0 -0
  58. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/local_http_driver.py +0 -0
  59. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/ollama_driver.py +0 -0
  60. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/openai_driver.py +0 -0
  61. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/drivers/openrouter_driver.py +0 -0
  62. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/field_definitions.py +0 -0
  63. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/runner.py +0 -0
  64. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/tools.py +0 -0
  65. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture/validator.py +0 -0
  66. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture.egg-info/dependency_links.txt +0 -0
  67. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture.egg-info/entry_points.txt +0 -0
  68. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/prompture.egg-info/top_level.txt +0 -0
  69. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/requirements.txt +0 -0
  70. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/setup.cfg +0 -0
  71. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/test.py +0 -0
  72. {prompture-0.0.32.dev1 → prompture-0.0.33.dev1}/test_version_diagnosis.py +0 -0
@@ -57,4 +57,8 @@ OPENROUTER_MODEL=openai/gpt-3.5-turbo
57
57
  # Grok Configuration
58
58
  # Required if AI_PROVIDER=grok
59
59
  GROK_API_KEY=your-api-key-here
60
- GROK_MODEL=grok-4-fast-reasoning
60
+ GROK_MODEL=grok-4-fast-reasoning
61
+
62
+ # AirLLM Configuration
63
+ AIRLLM_MODEL=meta-llama/Llama-2-7b-hf
64
+ AIRLLM_COMPRESSION=
@@ -0,0 +1,74 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Prompture is a Python library for extracting structured JSON output from LLMs, with schema enforcement, Pydantic model integration, TOON (Token-Oriented Object Notation) input/output conversion, and multi-provider driver support. Published on PyPI as `prompture`.
8
+
9
+ ## Build & Development Commands
10
+
11
+ ```bash
12
+ # Install in development mode
13
+ pip install -e ".[test]"
14
+
15
+ # Run all tests (uses DEFAULT_MODEL from tests/conftest.py)
16
+ python test.py
17
+
18
+ # Run tests with pytest directly
19
+ pytest
20
+
21
+ # Run integration tests (require live LLM access)
22
+ pytest --run-integration
23
+ # or: RUN_INTEGRATION_TESTS=1 pytest
24
+
25
+ # Run a single test file
26
+ pytest tests/test_core.py
27
+
28
+ # Run a single test
29
+ pytest tests/test_core.py::TestCleanJsonText::test_basic_json
30
+
31
+ # Skip integration tests when credentials are missing
32
+ TEST_SKIP_NO_CREDENTIALS=true python test.py
33
+
34
+ # Build distribution
35
+ python -m build
36
+
37
+ # CLI entry point
38
+ prompture run <spec-file>
39
+ ```
40
+
41
+ There is no configured linter or formatter.
42
+
43
+ ## Architecture
44
+
45
+ ### Module Layout
46
+
47
+ - **`prompture/core.py`** — Primary business logic. All extraction functions live here: `ask_for_json()` (low-level schema enforcement), `extract_and_jsonify()` / `manual_extract_and_jsonify()` (text-to-JSON), `extract_with_model()` / `stepwise_extract_with_model()` (Pydantic-based), `extract_from_data()` / `extract_from_pandas()` (TOON input), `render_output()` (raw text formatting).
48
+ - **`prompture/drivers/`** — One module per LLM provider (openai, claude, google, groq, grok, azure, ollama, lmstudio, openrouter, local_http, huggingface, airllm). Each driver implements `generate(prompt, options)` returning a standardized response with token/cost metadata.
49
+ - **`prompture/drivers/__init__.py`** — Central `DRIVER_REGISTRY` dict mapping provider name to factory lambda. `get_driver_for_model("provider/model")` parses the string and instantiates the right driver. `get_driver("provider")` is the legacy interface.
50
+ - **`prompture/tools.py`** — Utilities: JSON/TOON text cleanup, type conversion (shorthand numbers, multilingual booleans, datetimes), field schema generation, custom `LogLevel` enum (not stdlib logging).
51
+ - **`prompture/field_definitions.py`** — Thread-safe global field registry with 50+ predefined fields, template variable substitution (`{{current_year}}`, `{{current_date}}`), and Pydantic Field generation via `field_from_registry()`.
52
+ - **`prompture/settings.py`** — Pydantic-settings `Settings` class loading provider API keys/endpoints from `.env`.
53
+ - **`prompture/discovery.py`** — `get_available_models()` auto-detects models from configured providers (static pricing tables + dynamic Ollama endpoint query).
54
+ - **`prompture/runner.py`** — Spec-driven test suite runner for cross-model comparison.
55
+ - **`prompture/validator.py`** — JSON schema validation via jsonschema with fallback.
56
+
57
+ ### Key Patterns
58
+
59
+ - **Model strings** use `"provider/model"` format (e.g., `"ollama/llama3.1:8b"`, `"openai/gpt-4"`). The provider prefix routes to the correct driver.
60
+ - **Driver responses** always include metadata: `prompt_tokens`, `completion_tokens`, `total_tokens`, `cost`, `raw_response`.
61
+ - **Output formats**: JSON (default) and TOON (experimental, for compact output). Controlled via `output_format` parameter.
62
+ - **TOON input conversion** uses `python-toon` and `tukuy` packages to reduce token usage by 45-60% when sending structured data to LLMs.
63
+
64
+ ### Testing
65
+
66
+ Tests live in `tests/`. Integration tests that call live LLMs are marked with `@pytest.mark.integration` and skipped by default. The default test model is set in `tests/conftest.py` as `DEFAULT_MODEL`. Shared fixtures and assertion helpers (`assert_valid_usage_metadata`, `assert_jsonify_response_structure`) are in `conftest.py`.
67
+
68
+ ### Configuration
69
+
70
+ Provider API keys and endpoints are configured via environment variables loaded from `.env` (see `.env.copy` for the template). The `Settings` class in `settings.py` manages all provider config.
71
+
72
+ ### Versioning
73
+
74
+ Uses `setuptools_scm` for automatic version from git tags. The `VERSION` file contains the current dev version.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prompture
3
- Version: 0.0.32.dev1
3
+ Version: 0.0.33.dev1
4
4
  Summary: Ask LLMs to return structured JSON and run cross-model tests. API-first.
5
5
  Home-page: https://github.com/jhd3197/prompture
6
6
  Author: Juan Denis
@@ -29,6 +29,8 @@ Requires-Dist: tukuy>=0.0.6
29
29
  Requires-Dist: pyyaml>=6.0
30
30
  Provides-Extra: test
31
31
  Requires-Dist: pytest>=7.0; extra == "test"
32
+ Provides-Extra: airllm
33
+ Requires-Dist: airllm>=2.8.0; extra == "airllm"
32
34
  Dynamic: author
33
35
  Dynamic: author-email
34
36
  Dynamic: classifier
@@ -13,7 +13,7 @@ from .core import (
13
13
  extract_from_pandas,
14
14
  render_output,
15
15
  )
16
- from .drivers import get_driver, get_driver_for_model, OpenAIDriver, LocalHTTPDriver, OllamaDriver, ClaudeDriver, LMStudioDriver, AzureDriver, GoogleDriver, GroqDriver, OpenRouterDriver, GrokDriver
16
+ from .drivers import get_driver, get_driver_for_model, OpenAIDriver, LocalHTTPDriver, OllamaDriver, ClaudeDriver, LMStudioDriver, AzureDriver, GoogleDriver, GroqDriver, OpenRouterDriver, GrokDriver, AirLLMDriver
17
17
  from .tools import clean_json_text, clean_toon_text
18
18
  from .field_definitions import (
19
19
  FIELD_DEFINITIONS, get_field_definition, get_required_fields, get_field_names,
@@ -87,6 +87,7 @@ __all__ = [
87
87
  "GroqDriver",
88
88
  "OpenRouterDriver",
89
89
  "GrokDriver",
90
+ "AirLLMDriver",
90
91
  # Discovery
91
92
  "get_available_models",
92
93
  ]
@@ -8,6 +8,7 @@ from .google_driver import GoogleDriver
8
8
  from .groq_driver import GroqDriver
9
9
  from .openrouter_driver import OpenRouterDriver
10
10
  from .grok_driver import GrokDriver
11
+ from .airllm_driver import AirLLMDriver
11
12
  from ..settings import settings
12
13
 
13
14
 
@@ -54,6 +55,10 @@ DRIVER_REGISTRY = {
54
55
  api_key=settings.grok_api_key,
55
56
  model=model or settings.grok_model
56
57
  ),
58
+ "airllm": lambda model=None: AirLLMDriver(
59
+ model=model or settings.airllm_model,
60
+ compression=settings.airllm_compression,
61
+ ),
57
62
  }
58
63
 
59
64
 
@@ -115,6 +120,7 @@ __all__ = [
115
120
  "GroqDriver",
116
121
  "OpenRouterDriver",
117
122
  "GrokDriver",
123
+ "AirLLMDriver",
118
124
  "get_driver",
119
125
  "get_driver_for_model",
120
126
  ]
@@ -0,0 +1,116 @@
1
+ import logging
2
+ from ..driver import Driver
3
+ from typing import Any, Dict, Optional
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ class AirLLMDriver(Driver):
9
+ """Driver for AirLLM — run large models (70B+) on consumer GPUs via
10
+ layer-by-layer memory management.
11
+
12
+ The ``airllm`` package is a lazy dependency: it is imported on first
13
+ ``generate()`` call so the rest of Prompture works without it installed.
14
+ """
15
+
16
+ MODEL_PRICING = {
17
+ "default": {"prompt": 0.0, "completion": 0.0}
18
+ }
19
+
20
+ def __init__(self, model: str = "meta-llama/Llama-2-7b-hf",
21
+ compression: Optional[str] = None):
22
+ """
23
+ Args:
24
+ model: HuggingFace repo ID (e.g. ``"meta-llama/Llama-2-70b-hf"``).
25
+ compression: Optional quantization mode — ``"4bit"`` or ``"8bit"``.
26
+ """
27
+ self.model = model
28
+ self.compression = compression
29
+ self.options: Dict[str, Any] = {}
30
+ self._llm = None
31
+ self._tokenizer = None
32
+
33
+ # ------------------------------------------------------------------
34
+ # Lazy model loading
35
+ # ------------------------------------------------------------------
36
+ def _ensure_loaded(self):
37
+ """Load the AirLLM model and tokenizer on first use."""
38
+ if self._llm is not None:
39
+ return
40
+
41
+ try:
42
+ from airllm import AutoModel
43
+ except ImportError:
44
+ raise ImportError(
45
+ "The 'airllm' package is required for the AirLLM driver. "
46
+ "Install it with: pip install prompture[airllm]"
47
+ )
48
+
49
+ try:
50
+ from transformers import AutoTokenizer
51
+ except ImportError:
52
+ raise ImportError(
53
+ "The 'transformers' package is required for the AirLLM driver. "
54
+ "Install it with: pip install transformers"
55
+ )
56
+
57
+ logger.info(f"Loading AirLLM model: {self.model} "
58
+ f"(compression={self.compression})")
59
+
60
+ load_kwargs: Dict[str, Any] = {}
61
+ if self.compression:
62
+ load_kwargs["compression"] = self.compression
63
+
64
+ self._llm = AutoModel.from_pretrained(self.model, **load_kwargs)
65
+ self._tokenizer = AutoTokenizer.from_pretrained(self.model)
66
+ logger.info("AirLLM model loaded successfully")
67
+
68
+ # ------------------------------------------------------------------
69
+ # Driver interface
70
+ # ------------------------------------------------------------------
71
+ def generate(self, prompt: str, options: Dict[str, Any] = None) -> Dict[str, Any]:
72
+ self._ensure_loaded()
73
+
74
+ merged_options = self.options.copy()
75
+ if options:
76
+ merged_options.update(options)
77
+
78
+ max_new_tokens = merged_options.get("max_new_tokens", 256)
79
+
80
+ # Tokenize
81
+ input_ids = self._tokenizer(
82
+ prompt, return_tensors="pt"
83
+ ).input_ids
84
+
85
+ prompt_tokens = input_ids.shape[1]
86
+
87
+ logger.debug(f"AirLLM generating with max_new_tokens={max_new_tokens}, "
88
+ f"prompt_tokens={prompt_tokens}")
89
+
90
+ # Generate
91
+ output_ids = self._llm.generate(
92
+ input_ids,
93
+ max_new_tokens=max_new_tokens,
94
+ )
95
+
96
+ # Decode only the newly generated tokens (strip the prompt prefix)
97
+ new_tokens = output_ids[0, prompt_tokens:]
98
+ completion_tokens = len(new_tokens)
99
+ text = self._tokenizer.decode(new_tokens, skip_special_tokens=True)
100
+
101
+ total_tokens = prompt_tokens + completion_tokens
102
+
103
+ meta = {
104
+ "prompt_tokens": prompt_tokens,
105
+ "completion_tokens": completion_tokens,
106
+ "total_tokens": total_tokens,
107
+ "cost": 0.0,
108
+ "raw_response": {
109
+ "model": self.model,
110
+ "compression": self.compression,
111
+ "max_new_tokens": max_new_tokens,
112
+ },
113
+ "model_name": self.model,
114
+ }
115
+
116
+ return {"text": text, "meta": meta}
@@ -48,6 +48,10 @@ class Settings(BaseSettings):
48
48
  grok_api_key: Optional[str] = None
49
49
  grok_model: str = "grok-4-fast-reasoning"
50
50
 
51
+ # AirLLM
52
+ airllm_model: str = "meta-llama/Llama-2-7b-hf"
53
+ airllm_compression: Optional[str] = None # "4bit" or "8bit"
54
+
51
55
  model_config = SettingsConfigDict(
52
56
  env_file=".env",
53
57
  extra="ignore",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prompture
3
- Version: 0.0.32.dev1
3
+ Version: 0.0.33.dev1
4
4
  Summary: Ask LLMs to return structured JSON and run cross-model tests. API-first.
5
5
  Home-page: https://github.com/jhd3197/prompture
6
6
  Author: Juan Denis
@@ -29,6 +29,8 @@ Requires-Dist: tukuy>=0.0.6
29
29
  Requires-Dist: pyyaml>=6.0
30
30
  Provides-Extra: test
31
31
  Requires-Dist: pytest>=7.0; extra == "test"
32
+ Provides-Extra: airllm
33
+ Requires-Dist: airllm>=2.8.0; extra == "airllm"
32
34
  Dynamic: author
33
35
  Dynamic: author-email
34
36
  Dynamic: classifier
@@ -1,4 +1,5 @@
1
1
  .env.copy
2
+ CLAUDE.md
2
3
  LICENSE
3
4
  MANIFEST.in
4
5
  README.md
@@ -55,6 +56,7 @@ prompture.egg-info/entry_points.txt
55
56
  prompture.egg-info/requires.txt
56
57
  prompture.egg-info/top_level.txt
57
58
  prompture/drivers/__init__.py
59
+ prompture/drivers/airllm_driver.py
58
60
  prompture/drivers/azure_driver.py
59
61
  prompture/drivers/claude_driver.py
60
62
  prompture/drivers/google_driver.py
@@ -15,5 +15,8 @@ python-dateutil>=2.9.0
15
15
  tukuy>=0.0.6
16
16
  pyyaml>=6.0
17
17
 
18
+ [airllm]
19
+ airllm>=2.8.0
20
+
18
21
  [test]
19
22
  pytest>=7.0
@@ -47,5 +47,6 @@ setup(
47
47
  },
48
48
  extras_require={
49
49
  "test": ["pytest>=7.0"],
50
+ "airllm": ["airllm>=2.8.0"],
50
51
  },
51
52
  )
File without changes
File without changes
File without changes