fix: resolve CI linting and type errors
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
"""Jinja2-based template engine for prompt rendering."""
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
from jinja2 import Environment, BaseLoader, TemplateSyntaxError, StrictUndefined, UndefinedError
|
from jinja2 import Environment, BaseLoader, TemplateSyntaxError, StrictUndefined, UndefinedError
|
||||||
from jinja2.exceptions import TemplateError
|
from jinja2.exceptions import TemplateError
|
||||||
@@ -7,6 +9,8 @@ from .exceptions import MissingVariableError, InvalidPromptError
|
|||||||
|
|
||||||
|
|
||||||
class TemplateEngine:
|
class TemplateEngine:
|
||||||
|
"""Jinja2 template engine for prompt rendering with variable substitution."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.env = Environment(
|
self.env = Environment(
|
||||||
loader=BaseLoader(),
|
loader=BaseLoader(),
|
||||||
@@ -17,6 +21,14 @@ class TemplateEngine:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_variables(self, content: str) -> List[str]:
|
def get_variables(self, content: str) -> List[str]:
|
||||||
|
"""Extract variable names from template content.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: Template content with {{variable}} syntax.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of variable names found.
|
||||||
|
"""
|
||||||
from jinja2 import meta
|
from jinja2 import meta
|
||||||
ast = self.env.parse(content)
|
ast = self.env.parse(content)
|
||||||
return sorted(meta.find_undeclared_variables(ast))
|
return sorted(meta.find_undeclared_variables(ast))
|
||||||
@@ -27,6 +39,20 @@ class TemplateEngine:
|
|||||||
variables: Optional[Dict[str, Any]] = None,
|
variables: Optional[Dict[str, Any]] = None,
|
||||||
required_variables: Optional[List[PromptVariable]] = None,
|
required_variables: Optional[List[PromptVariable]] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
"""Render template with variable substitution.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: Template content.
|
||||||
|
variables: Dictionary of variable values.
|
||||||
|
required_variables: List of variable definitions for validation.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Rendered content.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
MissingVariableError: If a required variable is missing.
|
||||||
|
InvalidPromptError: If template has syntax errors.
|
||||||
|
"""
|
||||||
variables = variables.copy() if variables else {}
|
variables = variables.copy() if variables else {}
|
||||||
required_variables = required_variables or []
|
required_variables = required_variables or []
|
||||||
|
|
||||||
@@ -56,4 +82,76 @@ class TemplateEngine:
|
|||||||
except TemplateSyntaxError as e:
|
except TemplateSyntaxError as e:
|
||||||
raise InvalidPromptError(f"Template syntax error: {e.message}")
|
raise InvalidPromptError(f"Template syntax error: {e.message}")
|
||||||
except (UndefinedError, TemplateError) as e:
|
except (UndefinedError, TemplateError) as e:
|
||||||
raise InvalidPromptError(f"Template rendering error: {e}")
|
raise InvalidPromptError(f"Template rendering error: {e}")
|
||||||
|
|
||||||
|
def validate_variables(
|
||||||
|
self,
|
||||||
|
variables: Dict[str, Any],
|
||||||
|
required_variables: List[PromptVariable],
|
||||||
|
) -> List[str]:
|
||||||
|
"""Validate variable values against definitions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
variables: Provided variable values.
|
||||||
|
required_variables: Variable definitions.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of validation error messages (empty if valid).
|
||||||
|
"""
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for var in required_variables:
|
||||||
|
if var.name not in variables:
|
||||||
|
if var.required and var.default is None:
|
||||||
|
errors.append(f"Missing required variable: {var.name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
value = variables[var.name]
|
||||||
|
var_type = var.type.value
|
||||||
|
|
||||||
|
if var_type == "string":
|
||||||
|
if not isinstance(value, str):
|
||||||
|
errors.append(f"{var.name}: must be a string")
|
||||||
|
elif var_type == "integer":
|
||||||
|
try:
|
||||||
|
int(value)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
errors.append(f"{var.name}: must be an integer")
|
||||||
|
elif var_type == "float":
|
||||||
|
try:
|
||||||
|
float(value)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
errors.append(f"{var.name}: must be a number")
|
||||||
|
elif var_type == "boolean":
|
||||||
|
if isinstance(value, str):
|
||||||
|
if value.lower() not in ("true", "false", "0", "1"):
|
||||||
|
errors.append(f"{var.name}: must be a boolean")
|
||||||
|
elif var_type == "choice":
|
||||||
|
if var.choices and value not in var.choices:
|
||||||
|
errors.append(
|
||||||
|
f"{var.name}: must be one of {', '.join(var.choices)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
def render_prompt(
|
||||||
|
self,
|
||||||
|
prompt_content: str,
|
||||||
|
variables: Dict[str, Any],
|
||||||
|
variable_definitions: List[PromptVariable],
|
||||||
|
) -> str:
|
||||||
|
"""Render a complete prompt with validation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompt_content: Raw prompt template content.
|
||||||
|
variables: Variable values.
|
||||||
|
variable_definitions: Variable definitions from prompt.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Rendered prompt string.
|
||||||
|
"""
|
||||||
|
errors = self.validate_variables(variables, variable_definitions)
|
||||||
|
if errors:
|
||||||
|
raise MissingVariableError(f"Variable validation failed: {', '.join(errors)}")
|
||||||
|
|
||||||
|
return self.render(prompt_content, variables, variable_definitions)
|
||||||
|
|||||||
Reference in New Issue
Block a user