|
|
""" |
|
|
Jinja Templates and MDX Components for MiniMind Max2 |
|
|
Enhanced code generation with rich formatting support. |
|
|
""" |
|
|
|
|
|
from dataclasses import dataclass, field |
|
|
from typing import List, Optional, Dict, Any, Union |
|
|
import re |
|
|
import json |
|
|
from enum import Enum |
|
|
|
|
|
|
|
|
class MDXComponentType(Enum): |
|
|
"""Supported MDX component types.""" |
|
|
LINEAR_PROCESS_FLOW = "LinearProcessFlow" |
|
|
QUIZ = "Quiz" |
|
|
MATH = "math" |
|
|
THINKING = "Thinking" |
|
|
CODE_BLOCK = "CodeBlock" |
|
|
MERMAID = "Mermaid" |
|
|
CALLOUT = "Callout" |
|
|
TABS = "Tabs" |
|
|
ACCORDION = "Accordion" |
|
|
|
|
|
|
|
|
@dataclass |
|
|
class CodeBlockMeta: |
|
|
"""Metadata for extended code blocks.""" |
|
|
project: str = "" |
|
|
file: str = "" |
|
|
block_type: str = "code" |
|
|
language: str = "python" |
|
|
title: str = "" |
|
|
show_line_numbers: bool = True |
|
|
highlight_lines: List[int] = field(default_factory=list) |
|
|
executable: bool = False |
|
|
|
|
|
|
|
|
@dataclass |
|
|
class ThinkingBlock: |
|
|
"""Represents a thinking/reasoning block.""" |
|
|
content: str |
|
|
step: int = 0 |
|
|
is_final: bool = False |
|
|
confidence: float = 1.0 |
|
|
|
|
|
|
|
|
class JinjaTemplateEngine: |
|
|
""" |
|
|
Jinja-style template engine for code generation. |
|
|
Supports variables, loops, conditionals, and filters. |
|
|
""" |
|
|
|
|
|
def __init__(self): |
|
|
self.filters = { |
|
|
"upper": str.upper, |
|
|
"lower": str.lower, |
|
|
"capitalize": str.capitalize, |
|
|
"title": str.title, |
|
|
"strip": str.strip, |
|
|
"length": len, |
|
|
"default": lambda x, d: x if x else d, |
|
|
"join": lambda x, sep=", ": sep.join(str(i) for i in x), |
|
|
"first": lambda x: x[0] if x else None, |
|
|
"last": lambda x: x[-1] if x else None, |
|
|
"json": json.dumps, |
|
|
} |
|
|
self.globals = {} |
|
|
|
|
|
def register_filter(self, name: str, func): |
|
|
"""Register a custom filter.""" |
|
|
self.filters[name] = func |
|
|
|
|
|
def register_global(self, name: str, value): |
|
|
"""Register a global variable.""" |
|
|
self.globals[name] = value |
|
|
|
|
|
def render(self, template: str, context: Dict[str, Any]) -> str: |
|
|
""" |
|
|
Render a Jinja-style template. |
|
|
|
|
|
Args: |
|
|
template: Template string with {{ }} and {% %} blocks |
|
|
context: Variables to substitute |
|
|
|
|
|
Returns: |
|
|
Rendered string |
|
|
""" |
|
|
|
|
|
full_context = {**self.globals, **context} |
|
|
|
|
|
|
|
|
result = self._process_control_structures(template, full_context) |
|
|
|
|
|
|
|
|
result = self._substitute_variables(result, full_context) |
|
|
|
|
|
return result |
|
|
|
|
|
def _substitute_variables(self, template: str, context: Dict[str, Any]) -> str: |
|
|
"""Substitute {{ variable }} expressions.""" |
|
|
pattern = r'\{\{\s*(.+?)\s*\}\}' |
|
|
|
|
|
def replace(match): |
|
|
expr = match.group(1) |
|
|
return str(self._evaluate_expression(expr, context)) |
|
|
|
|
|
return re.sub(pattern, replace, template) |
|
|
|
|
|
def _evaluate_expression(self, expr: str, context: Dict[str, Any]) -> Any: |
|
|
"""Evaluate a Jinja expression.""" |
|
|
|
|
|
if '|' in expr: |
|
|
parts = expr.split('|') |
|
|
value = self._evaluate_expression(parts[0].strip(), context) |
|
|
for filter_expr in parts[1:]: |
|
|
filter_expr = filter_expr.strip() |
|
|
|
|
|
if '(' in filter_expr: |
|
|
filter_name = filter_expr[:filter_expr.index('(')] |
|
|
args_str = filter_expr[filter_expr.index('(')+1:-1] |
|
|
if filter_name in self.filters: |
|
|
value = self.filters[filter_name](value, args_str.strip('"\'')) |
|
|
else: |
|
|
if filter_expr in self.filters: |
|
|
value = self.filters[filter_expr](value) |
|
|
return value |
|
|
|
|
|
|
|
|
if '.' in expr: |
|
|
parts = expr.split('.') |
|
|
value = context.get(parts[0]) |
|
|
for part in parts[1:]: |
|
|
if isinstance(value, dict): |
|
|
value = value.get(part) |
|
|
elif hasattr(value, part): |
|
|
value = getattr(value, part) |
|
|
else: |
|
|
return None |
|
|
return value |
|
|
|
|
|
|
|
|
if '[' in expr: |
|
|
match = re.match(r'(\w+)\[(.+)\]', expr) |
|
|
if match: |
|
|
var_name = match.group(1) |
|
|
index = match.group(2) |
|
|
value = context.get(var_name) |
|
|
if value: |
|
|
try: |
|
|
idx = int(index) |
|
|
return value[idx] |
|
|
except (ValueError, IndexError): |
|
|
return None |
|
|
return None |
|
|
|
|
|
|
|
|
return context.get(expr, '') |
|
|
|
|
|
def _process_control_structures(self, template: str, context: Dict[str, Any]) -> str: |
|
|
"""Process {% for %}, {% if %}, etc.""" |
|
|
result = template |
|
|
|
|
|
|
|
|
for_pattern = r'\{%\s*for\s+(\w+)\s+in\s+(\w+)\s*%\}(.*?)\{%\s*endfor\s*%\}' |
|
|
while True: |
|
|
match = re.search(for_pattern, result, re.DOTALL) |
|
|
if not match: |
|
|
break |
|
|
|
|
|
var_name = match.group(1) |
|
|
iterable_name = match.group(2) |
|
|
body = match.group(3) |
|
|
|
|
|
iterable = context.get(iterable_name, []) |
|
|
rendered_parts = [] |
|
|
|
|
|
for item in iterable: |
|
|
item_context = {**context, var_name: item} |
|
|
rendered_parts.append(self._substitute_variables(body, item_context)) |
|
|
|
|
|
result = result[:match.start()] + ''.join(rendered_parts) + result[match.end():] |
|
|
|
|
|
|
|
|
if_pattern = r'\{%\s*if\s+(.+?)\s*%\}(.*?)(?:\{%\s*else\s*%\}(.*?))?\{%\s*endif\s*%\}' |
|
|
while True: |
|
|
match = re.search(if_pattern, result, re.DOTALL) |
|
|
if not match: |
|
|
break |
|
|
|
|
|
condition = match.group(1) |
|
|
true_block = match.group(2) |
|
|
false_block = match.group(3) or '' |
|
|
|
|
|
if self._evaluate_condition(condition, context): |
|
|
replacement = true_block |
|
|
else: |
|
|
replacement = false_block |
|
|
|
|
|
result = result[:match.start()] + replacement + result[match.end():] |
|
|
|
|
|
return result |
|
|
|
|
|
def _evaluate_condition(self, condition: str, context: Dict[str, Any]) -> bool: |
|
|
"""Evaluate a condition expression.""" |
|
|
|
|
|
if ' == ' in condition: |
|
|
left, right = condition.split(' == ', 1) |
|
|
left_val = self._evaluate_expression(left.strip(), context) |
|
|
right_val = right.strip().strip('"\'') |
|
|
return str(left_val) == right_val |
|
|
|
|
|
if ' != ' in condition: |
|
|
left, right = condition.split(' != ', 1) |
|
|
left_val = self._evaluate_expression(left.strip(), context) |
|
|
right_val = right.strip().strip('"\'') |
|
|
return str(left_val) != right_val |
|
|
|
|
|
if ' > ' in condition: |
|
|
left, right = condition.split(' > ', 1) |
|
|
left_val = self._evaluate_expression(left.strip(), context) |
|
|
right_val = float(right.strip()) |
|
|
return float(left_val) > right_val |
|
|
|
|
|
if ' < ' in condition: |
|
|
left, right = condition.split(' < ', 1) |
|
|
left_val = self._evaluate_expression(left.strip(), context) |
|
|
right_val = float(right.strip()) |
|
|
return float(left_val) < right_val |
|
|
|
|
|
|
|
|
value = self._evaluate_expression(condition.strip(), context) |
|
|
return bool(value) |
|
|
|
|
|
|
|
|
class MDXRenderer: |
|
|
""" |
|
|
Render MDX components for rich documentation and UI. |
|
|
""" |
|
|
|
|
|
def __init__(self): |
|
|
self.components = {} |
|
|
self._register_default_components() |
|
|
|
|
|
def _register_default_components(self): |
|
|
"""Register default MDX components.""" |
|
|
self.components = { |
|
|
MDXComponentType.LINEAR_PROCESS_FLOW: self._render_linear_process_flow, |
|
|
MDXComponentType.QUIZ: self._render_quiz, |
|
|
MDXComponentType.MATH: self._render_math, |
|
|
MDXComponentType.THINKING: self._render_thinking, |
|
|
MDXComponentType.MERMAID: self._render_mermaid, |
|
|
MDXComponentType.CALLOUT: self._render_callout, |
|
|
MDXComponentType.TABS: self._render_tabs, |
|
|
} |
|
|
|
|
|
def render(self, component_type: MDXComponentType, props: Dict[str, Any]) -> str: |
|
|
"""Render an MDX component.""" |
|
|
if component_type in self.components: |
|
|
return self.components[component_type](props) |
|
|
return f"<!-- Unknown component: {component_type} -->" |
|
|
|
|
|
def _render_linear_process_flow(self, props: Dict[str, Any]) -> str: |
|
|
"""Render a linear process flow diagram.""" |
|
|
steps = props.get("steps", []) |
|
|
title = props.get("title", "Process Flow") |
|
|
|
|
|
html = f'<div class="linear-process-flow">\n' |
|
|
html += f' <h3>{title}</h3>\n' |
|
|
html += ' <div class="steps">\n' |
|
|
|
|
|
for i, step in enumerate(steps): |
|
|
html += f' <div class="step" data-step="{i+1}">\n' |
|
|
html += f' <div class="step-number">{i+1}</div>\n' |
|
|
html += f' <div class="step-content">\n' |
|
|
html += f' <h4>{step.get("title", f"Step {i+1}")}</h4>\n' |
|
|
html += f' <p>{step.get("description", "")}</p>\n' |
|
|
html += ' </div>\n' |
|
|
if i < len(steps) - 1: |
|
|
html += ' <div class="step-arrow">→</div>\n' |
|
|
html += ' </div>\n' |
|
|
|
|
|
html += ' </div>\n' |
|
|
html += '</div>' |
|
|
|
|
|
return html |
|
|
|
|
|
def _render_quiz(self, props: Dict[str, Any]) -> str: |
|
|
"""Render an interactive quiz.""" |
|
|
questions = props.get("questions", []) |
|
|
title = props.get("title", "Quiz") |
|
|
|
|
|
html = f'<div class="quiz" data-quiz-id="{props.get("id", "quiz")}">\n' |
|
|
html += f' <h3>{title}</h3>\n' |
|
|
|
|
|
for i, q in enumerate(questions): |
|
|
html += f' <div class="question" data-question="{i}">\n' |
|
|
html += f' <p class="question-text">{q.get("question", "")}</p>\n' |
|
|
html += ' <div class="options">\n' |
|
|
|
|
|
for j, opt in enumerate(q.get("options", [])): |
|
|
correct = j == q.get("correct", 0) |
|
|
html += f' <label class="option" data-correct="{str(correct).lower()}">\n' |
|
|
html += f' <input type="radio" name="q{i}" value="{j}">\n' |
|
|
html += f' <span>{opt}</span>\n' |
|
|
html += ' </label>\n' |
|
|
|
|
|
html += ' </div>\n' |
|
|
if q.get("explanation"): |
|
|
html += f' <div class="explanation" hidden>{q["explanation"]}</div>\n' |
|
|
html += ' </div>\n' |
|
|
|
|
|
html += '</div>' |
|
|
return html |
|
|
|
|
|
def _render_math(self, props: Dict[str, Any]) -> str: |
|
|
"""Render LaTeX math expressions.""" |
|
|
expression = props.get("expression", "") |
|
|
display = props.get("display", False) |
|
|
|
|
|
if display: |
|
|
return f'$${expression}$$' |
|
|
return f'${expression}$' |
|
|
|
|
|
def _render_thinking(self, props: Dict[str, Any]) -> str: |
|
|
"""Render a thinking/reasoning block.""" |
|
|
content = props.get("content", "") |
|
|
step = props.get("step", 0) |
|
|
is_visible = props.get("visible", False) |
|
|
|
|
|
css_class = "thinking-block" + (" visible" if is_visible else " hidden") |
|
|
|
|
|
html = f'<div class="{css_class}" data-step="{step}">\n' |
|
|
html += ' <div class="thinking-header">\n' |
|
|
html += f' <span class="thinking-icon">🤔</span>\n' |
|
|
html += f' <span class="thinking-label">Thinking (Step {step})</span>\n' |
|
|
html += ' </div>\n' |
|
|
html += f' <div class="thinking-content">{content}</div>\n' |
|
|
html += '</div>' |
|
|
|
|
|
return html |
|
|
|
|
|
def _render_mermaid(self, props: Dict[str, Any]) -> str: |
|
|
"""Render a Mermaid diagram.""" |
|
|
code = props.get("code", "") |
|
|
title = props.get("title", "") |
|
|
|
|
|
html = '<div class="mermaid-diagram">\n' |
|
|
if title: |
|
|
html += f' <div class="diagram-title">{title}</div>\n' |
|
|
html += f' <pre class="mermaid">\n{code}\n </pre>\n' |
|
|
html += '</div>' |
|
|
|
|
|
return html |
|
|
|
|
|
def _render_callout(self, props: Dict[str, Any]) -> str: |
|
|
"""Render a callout/alert box.""" |
|
|
callout_type = props.get("type", "info") |
|
|
title = props.get("title", "") |
|
|
content = props.get("content", "") |
|
|
|
|
|
icons = { |
|
|
"info": "ℹ️", |
|
|
"warning": "⚠️", |
|
|
"error": "❌", |
|
|
"success": "✅", |
|
|
} |
|
|
|
|
|
html = f'<div class="callout callout-{callout_type}">\n' |
|
|
html += f' <div class="callout-icon">{icons.get(callout_type, "ℹ️")}</div>\n' |
|
|
html += ' <div class="callout-content">\n' |
|
|
if title: |
|
|
html += f' <div class="callout-title">{title}</div>\n' |
|
|
html += f' <div class="callout-body">{content}</div>\n' |
|
|
html += ' </div>\n' |
|
|
html += '</div>' |
|
|
|
|
|
return html |
|
|
|
|
|
def _render_tabs(self, props: Dict[str, Any]) -> str: |
|
|
"""Render a tabbed interface.""" |
|
|
tabs = props.get("tabs", []) |
|
|
|
|
|
html = '<div class="tabs-container">\n' |
|
|
html += ' <div class="tabs-header">\n' |
|
|
|
|
|
for i, tab in enumerate(tabs): |
|
|
active = "active" if i == 0 else "" |
|
|
html += f' <button class="tab-button {active}" data-tab="{i}">{tab.get("label", f"Tab {i+1}")}</button>\n' |
|
|
|
|
|
html += ' </div>\n' |
|
|
html += ' <div class="tabs-content">\n' |
|
|
|
|
|
for i, tab in enumerate(tabs): |
|
|
active = "active" if i == 0 else "" |
|
|
html += f' <div class="tab-panel {active}" data-tab="{i}">{tab.get("content", "")}</div>\n' |
|
|
|
|
|
html += ' </div>\n' |
|
|
html += '</div>' |
|
|
|
|
|
return html |
|
|
|
|
|
|
|
|
class ExtendedCodeBlockParser: |
|
|
""" |
|
|
Parse and generate extended code blocks with metadata. |
|
|
""" |
|
|
|
|
|
|
|
|
LANG_EXTENSIONS = { |
|
|
"python": ".py", |
|
|
"javascript": ".js", |
|
|
"typescript": ".ts", |
|
|
"tsx": ".tsx", |
|
|
"jsx": ".jsx", |
|
|
"rust": ".rs", |
|
|
"go": ".go", |
|
|
"java": ".java", |
|
|
"cpp": ".cpp", |
|
|
"c": ".c", |
|
|
"html": ".html", |
|
|
"css": ".css", |
|
|
"json": ".json", |
|
|
"yaml": ".yaml", |
|
|
"markdown": ".md", |
|
|
"sql": ".sql", |
|
|
"bash": ".sh", |
|
|
"shell": ".sh", |
|
|
} |
|
|
|
|
|
@classmethod |
|
|
def parse(cls, code_block: str) -> tuple[CodeBlockMeta, str]: |
|
|
""" |
|
|
Parse an extended code block. |
|
|
|
|
|
Args: |
|
|
code_block: Full code block including ``` markers |
|
|
|
|
|
Returns: |
|
|
Tuple of (metadata, code_content) |
|
|
""" |
|
|
lines = code_block.strip().split('\n') |
|
|
first_line = lines[0] |
|
|
|
|
|
|
|
|
meta = CodeBlockMeta() |
|
|
|
|
|
|
|
|
lang_match = re.match(r'```(\w+)', first_line) |
|
|
if lang_match: |
|
|
meta.language = lang_match.group(1) |
|
|
|
|
|
|
|
|
attrs = {} |
|
|
attr_pattern = r'(\w+)="([^"]*)"' |
|
|
for match in re.finditer(attr_pattern, first_line): |
|
|
attrs[match.group(1)] = match.group(2) |
|
|
|
|
|
meta.project = attrs.get("project", "") |
|
|
meta.file = attrs.get("file", "") |
|
|
meta.block_type = attrs.get("type", "code") |
|
|
meta.title = attrs.get("title", "") |
|
|
meta.executable = meta.block_type in ["react", "nodejs", "html"] |
|
|
|
|
|
|
|
|
if "highlight" in attrs: |
|
|
try: |
|
|
meta.highlight_lines = [int(x) for x in attrs["highlight"].split(",")] |
|
|
except ValueError: |
|
|
pass |
|
|
|
|
|
|
|
|
code_lines = lines[1:-1] if lines[-1].strip() == '```' else lines[1:] |
|
|
code = '\n'.join(code_lines) |
|
|
|
|
|
return meta, code |
|
|
|
|
|
@classmethod |
|
|
def format(cls, code: str, meta: CodeBlockMeta) -> str: |
|
|
""" |
|
|
Format code with extended code block syntax. |
|
|
|
|
|
Args: |
|
|
code: Code content |
|
|
meta: Metadata for the code block |
|
|
|
|
|
Returns: |
|
|
Formatted code block string |
|
|
""" |
|
|
attrs = [] |
|
|
|
|
|
if meta.project: |
|
|
attrs.append(f'project="{meta.project}"') |
|
|
if meta.file: |
|
|
attrs.append(f'file="{meta.file}"') |
|
|
if meta.block_type != "code": |
|
|
attrs.append(f'type="{meta.block_type}"') |
|
|
if meta.title: |
|
|
attrs.append(f'title="{meta.title}"') |
|
|
if meta.highlight_lines: |
|
|
attrs.append(f'highlight="{",".join(str(x) for x in meta.highlight_lines)}"') |
|
|
|
|
|
attr_str = " " + " ".join(attrs) if attrs else "" |
|
|
|
|
|
return f"```{meta.language}{attr_str}\n{code}\n```" |
|
|
|
|
|
@classmethod |
|
|
def create_react_component( |
|
|
cls, |
|
|
code: str, |
|
|
component_name: str, |
|
|
project: str = "MiniMind", |
|
|
) -> str: |
|
|
"""Create a React component code block.""" |
|
|
meta = CodeBlockMeta( |
|
|
project=project, |
|
|
file=f"components/{component_name}.tsx", |
|
|
block_type="react", |
|
|
language="tsx", |
|
|
) |
|
|
return cls.format(code, meta) |
|
|
|
|
|
@classmethod |
|
|
def create_mermaid_diagram( |
|
|
cls, |
|
|
code: str, |
|
|
title: str = "", |
|
|
) -> str: |
|
|
"""Create a Mermaid diagram code block.""" |
|
|
meta = CodeBlockMeta( |
|
|
block_type="diagram", |
|
|
language="mermaid", |
|
|
title=title, |
|
|
) |
|
|
return cls.format(code, meta) |
|
|
|
|
|
|
|
|
class TemplateLibrary: |
|
|
""" |
|
|
Pre-built templates for common code patterns. |
|
|
""" |
|
|
|
|
|
TEMPLATES = { |
|
|
"react_component": ''' |
|
|
import React from 'react'; |
|
|
|
|
|
interface {{ name }}Props { |
|
|
{% for prop in props %} |
|
|
{{ prop.name }}: {{ prop.type }}; |
|
|
{% endfor %} |
|
|
} |
|
|
|
|
|
export const {{ name }}: React.FC<{{ name }}Props> = ({ {% for prop in props %}{{ prop.name }}{% if not loop.last %}, {% endif %}{% endfor %} }) => { |
|
|
return ( |
|
|
<div className="{{ name | lower }}"> |
|
|
{{ content }} |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
''', |
|
|
"python_class": ''' |
|
|
class {{ name }}: |
|
|
"""{{ description }}""" |
|
|
|
|
|
def __init__(self{% for param in params %}, {{ param.name }}: {{ param.type }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}): |
|
|
{% for param in params %} |
|
|
self.{{ param.name }} = {{ param.name }} |
|
|
{% endfor %} |
|
|
|
|
|
{% for method in methods %} |
|
|
def {{ method.name }}(self{% for arg in method.args %}, {{ arg.name }}: {{ arg.type }}{% endfor %}) -> {{ method.return_type }}: |
|
|
"""{{ method.description }}""" |
|
|
{{ method.body }} |
|
|
|
|
|
{% endfor %} |
|
|
''', |
|
|
"api_endpoint": ''' |
|
|
from fastapi import APIRouter, HTTPException |
|
|
from pydantic import BaseModel |
|
|
|
|
|
router = APIRouter(prefix="/{{ prefix }}", tags=["{{ tag }}"]) |
|
|
|
|
|
{% for model in models %} |
|
|
class {{ model.name }}(BaseModel): |
|
|
{% for field in model.fields %} |
|
|
{{ field.name }}: {{ field.type }} |
|
|
{% endfor %} |
|
|
|
|
|
{% endfor %} |
|
|
|
|
|
{% for endpoint in endpoints %} |
|
|
@router.{{ endpoint.method }}("{{ endpoint.path }}") |
|
|
async def {{ endpoint.name }}({% for param in endpoint.params %}{{ param.name }}: {{ param.type }}{% if not loop.last %}, {% endif %}{% endfor %}): |
|
|
"""{{ endpoint.description }}""" |
|
|
{{ endpoint.body }} |
|
|
|
|
|
{% endfor %} |
|
|
''', |
|
|
} |
|
|
|
|
|
def __init__(self): |
|
|
self.engine = JinjaTemplateEngine() |
|
|
|
|
|
def render(self, template_name: str, context: Dict[str, Any]) -> str: |
|
|
"""Render a pre-built template.""" |
|
|
if template_name not in self.TEMPLATES: |
|
|
raise ValueError(f"Unknown template: {template_name}") |
|
|
|
|
|
template = self.TEMPLATES[template_name] |
|
|
return self.engine.render(template, context) |
|
|
|
|
|
def add_template(self, name: str, template: str): |
|
|
"""Add a custom template.""" |
|
|
self.TEMPLATES[name] = template |
|
|
|