Skip to content

Commit 5e47fb9

Browse files
committed
Release v0.1.14: Major code quality and architecture enhancements
- Introduced a centralized validation engine to unify and streamline validation logic across the codebase. - Consolidated schema validation, eliminating scattered logic and improving error handling. - Reduced code duplication in adapters by enhancing the base adapter class and extracting common functionality. - Added comprehensive type annotations throughout the codebase for better IDE support and static analysis. - Improved overall code maintainability and developer experience with clearer error messages and better debugging capabilities. - Ensured backward compatibility with existing APIs and validation behaviors. This release significantly enhances the maintainability and robustness of the Promptix library.
1 parent 433fef5 commit 5e47fb9

File tree

14 files changed

+809
-444
lines changed

14 files changed

+809
-444
lines changed

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,63 @@
11
# Changelog
22

3+
## [0.1.14] - 2025-09-10
4+
5+
### 🧹 **Code Quality & Architecture Enhancements**
6+
7+
This release focuses on significant code quality improvements, reducing technical debt, and enhancing maintainability without breaking existing functionality.
8+
9+
### Added
10+
- **Centralized Validation Engine** (`src/promptix/core/validation.py`)
11+
- Unified validation system consolidating scattered validation logic
12+
- Pluggable validation strategies for different validation types
13+
- Support for variable, structural, builder, and custom validation patterns
14+
- Extensible architecture for future validation needs
15+
- Enhanced error reporting with contextual information
16+
17+
### Changed
18+
- **Schema Validation Consolidation**
19+
- Replaced scattered validation logic across multiple files with centralized engine
20+
- Updated `base.py`, `storage/loaders.py`, and `components/variable_validator.py` to use unified validation
21+
- Improved error handling and consistency across validation operations
22+
- Maintained backward compatibility through wrapper classes
23+
24+
- **Duplicate Code Elimination in Adapters**
25+
- Enhanced base adapter class (`adapters/_base.py`) with comprehensive common functionality
26+
- Extracted ~60% duplicate code from OpenAI and Anthropic adapters
27+
- Added common parameter validation, tool handling, and schema manipulation utilities
28+
- Reduced total adapter code by ~200 lines while improving consistency
29+
30+
- **Enhanced Type Annotations**
31+
- Added comprehensive type hints throughout the codebase
32+
- Improved IDE support and static analysis capabilities
33+
- Enhanced documentation through better type information
34+
- Better development experience with enhanced autocomplete and error detection
35+
36+
### Improved
37+
- **Function Structure**: Validated that functions are appropriately sized (most under 30 lines)
38+
- **Code Maintainability**: Significantly improved through centralized validation and reduced duplication
39+
- **Developer Experience**: Better IDE support, clearer error messages, easier debugging
40+
- **Architecture**: More consistent patterns and better separation of concerns
41+
42+
### Technical Improvements
43+
- **Reduced Code Duplication**: Eliminated redundant patterns in adapter classes
44+
- **Centralized Logic**: Unified validation operations for better maintainability
45+
- **Type Safety**: Enhanced type checking and IDE support throughout codebase
46+
- **Error Handling**: More consistent and informative error messages
47+
- **Code Coverage**: All tests pass (65/65) with maintained functionality
48+
49+
### Backward Compatibility
50+
- All existing APIs remain unchanged and fully functional
51+
- Existing validation behavior preserved through compatibility wrappers
52+
- No breaking changes to public interfaces
53+
- Legacy code continues to work without modifications
54+
55+
### Testing
56+
- All 65 tests pass successfully
57+
- 54% test coverage maintained
58+
- Validation improvements tested across all scenarios
59+
- No regressions in existing functionality
60+
361
## [0.1.13] - 2025-09-09
462

563
### 🏗️ **Architecture Improvements**

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "promptix"
7-
version = "0.1.13"
7+
version = "0.1.14"
88
description = "A simple library for managing and using prompts locally with Promptix Studio"
99
readme = "README.md"
1010
requires-python = ">=3.9"

src/promptix/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@
2323

2424
from .core.base_refactored import Promptix
2525

26-
__version__ = "0.1.13"
26+
__version__ = "0.1.14"
2727
__all__ = ["Promptix"]

src/promptix/core/adapters/_base.py

Lines changed: 155 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
from abc import ABC, abstractmethod
2-
from typing import Any, Dict, List, Union
2+
from typing import Any, Dict, List, Union, Optional, Tuple
3+
34

45
class ModelAdapter(ABC):
5-
"""Base adapter class for different model providers."""
6+
"""Base adapter class for different model providers with common functionality."""
7+
8+
# Common parameter types and their validation
9+
COMMON_PARAMETERS = {
10+
"temperature": (int, float),
11+
"max_tokens": int,
12+
"top_p": (int, float),
13+
}
614

715
@abstractmethod
816
def adapt_config(self, model_config: Dict[str, Any], version_data: Dict[str, Any]) -> Dict[str, Any]:
@@ -17,4 +25,148 @@ def adapt_messages(self, messages: List[Dict[str, str]]) -> List[Dict[str, str]]
1725
@abstractmethod
1826
def process_tools(self, tools_data: Union[Dict, List]) -> List[Dict[str, Any]]:
1927
"""Process tools data into the appropriate format for the specific adapter."""
20-
pass
28+
pass
29+
30+
# Common helper methods
31+
32+
def validate_and_extract_parameters(
33+
self,
34+
version_data: Dict[str, Any],
35+
parameter_mapping: Optional[Dict[str, str]] = None
36+
) -> Dict[str, Any]:
37+
"""
38+
Validate and extract common configuration parameters.
39+
40+
Args:
41+
version_data: The version data containing configuration
42+
parameter_mapping: Optional mapping from source to target parameter names
43+
If None, uses direct mapping (source = target)
44+
45+
Returns:
46+
Dict of validated parameters
47+
48+
Raises:
49+
ValueError: If parameter validation fails
50+
"""
51+
parameter_mapping = parameter_mapping or {}
52+
validated_params = {}
53+
config = version_data.get("config", {})
54+
55+
for source_param, expected_type in self.COMMON_PARAMETERS.items():
56+
if source_param in config and config[source_param] is not None:
57+
value = config[source_param]
58+
59+
# Validate parameter type
60+
if not isinstance(value, expected_type):
61+
type_name = expected_type.__name__ if hasattr(expected_type, '__name__') else str(expected_type)
62+
raise ValueError(f"{source_param} must be of type {type_name}, got {type(value).__name__}")
63+
64+
# Map to target parameter name
65+
target_param = parameter_mapping.get(source_param, source_param)
66+
validated_params[target_param] = value
67+
68+
return validated_params
69+
70+
def extract_tool_parameters(self, model_config: Dict[str, Any]) -> Dict[str, Dict[str, Any]]:
71+
"""
72+
Extract custom tool parameters from model config.
73+
74+
Args:
75+
model_config: The model configuration dictionary
76+
77+
Returns:
78+
Dict mapping tool names to their custom parameters
79+
"""
80+
tool_params = {}
81+
82+
for key in model_config:
83+
if key.startswith("tool_params_"):
84+
tool_name = key[len("tool_params_"):]
85+
tool_params[tool_name] = model_config[key]
86+
87+
return tool_params
88+
89+
def cleanup_tool_parameters(self, model_config: Dict[str, Any]) -> None:
90+
"""
91+
Remove temporary tool parameter entries from model config.
92+
93+
Args:
94+
model_config: The model configuration dictionary to clean
95+
"""
96+
keys_to_remove = [key for key in model_config.keys() if key.startswith("tool_params_")]
97+
for key in keys_to_remove:
98+
del model_config[key]
99+
100+
def apply_custom_parameters_to_schema(
101+
self,
102+
input_schema: Dict[str, Any],
103+
custom_params: Dict[str, Any]
104+
) -> Dict[str, Any]:
105+
"""
106+
Apply custom parameters to a tool's input schema.
107+
108+
Args:
109+
input_schema: The original input schema
110+
custom_params: Custom parameters to apply
111+
112+
Returns:
113+
Modified input schema with custom parameters applied
114+
"""
115+
if not custom_params:
116+
return input_schema
117+
118+
# Create a copy to avoid modifying the original
119+
modified_schema = input_schema.copy()
120+
121+
# Apply custom parameters to properties if they exist
122+
if "properties" in modified_schema and custom_params:
123+
if "properties" not in modified_schema:
124+
modified_schema["properties"] = {}
125+
126+
properties = modified_schema["properties"]
127+
for param_name, param_value in custom_params.items():
128+
if param_name in properties:
129+
if not isinstance(properties[param_name], dict):
130+
properties[param_name] = {}
131+
properties[param_name]["default"] = param_value
132+
133+
return modified_schema
134+
135+
def has_tools(self, model_config: Dict[str, Any]) -> bool:
136+
"""
137+
Check if model config has non-empty tools.
138+
139+
Args:
140+
model_config: The model configuration dictionary
141+
142+
Returns:
143+
True if tools exist and are non-empty
144+
"""
145+
tools = model_config.get("tools")
146+
if not tools:
147+
return False
148+
149+
if isinstance(tools, list):
150+
return len(tools) > 0
151+
elif isinstance(tools, dict):
152+
return len(tools) > 0
153+
154+
return False
155+
156+
def get_model_from_config(self, version_data: Dict[str, Any]) -> str:
157+
"""
158+
Extract model name from version data configuration.
159+
160+
Args:
161+
version_data: The version data containing configuration
162+
163+
Returns:
164+
Model name
165+
166+
Raises:
167+
ValueError: If model is not specified
168+
"""
169+
model = version_data.get("config", {}).get("model")
170+
if not model:
171+
raise ValueError("Model must be specified in the version data config")
172+
return model

0 commit comments

Comments
 (0)