""" Stub definitions and the @reason_first decorator. A stub is a function skeleton with: - A decorator @reason_first(spec="...") that declares the informal specification - An optional #> comment inside the body with additional constraints - Surrounding context: imports, type hints, downstream uses The decorator captures all of this metadata to define the valid program space. """ from __future__ import annotations import ast import inspect import textwrap import hashlib from dataclasses import dataclass, field from typing import Any, Callable, Optional from pathlib import Path @dataclass class StubConstraints: """Formal constraints derived from the stub's surrounding context.""" imports: list[str] = field(default_factory=list) type_hints: dict[str, str] = field(default_factory=dict) return_type: Optional[str] = None inline_specs: list[str] = field(default_factory=list) # #> comments downstream_uses: list[str] = field(default_factory=list) decorator_spec: str = "" preconditions: list[str] = field(default_factory=list) postconditions: list[str] = field(default_factory=list) def to_prompt_context(self) -> str: """Render constraints as natural language for LLM prompting.""" parts = [] if self.decorator_spec: parts.append(f"Specification: {self.decorator_spec}") if self.inline_specs: parts.append(f"Inline constraints: {'; '.join(self.inline_specs)}") if self.type_hints: hints = ", ".join(f"{k}: {v}" for k, v in self.type_hints.items()) parts.append(f"Type hints: {hints}") if self.return_type: parts.append(f"Return type: {self.return_type}") if self.imports: parts.append(f"Available imports: {', '.join(self.imports)}") if self.preconditions: parts.append(f"Preconditions: {'; '.join(self.preconditions)}") if self.postconditions: parts.append(f"Postconditions: {'; '.join(self.postconditions)}") return "\n".join(parts) @dataclass class Stub: """ A program stub: a function skeleton that defines a valid program space. The stub captures: - name: function name - source: the full source code of the decorated function - constraints: formal constraints extracted from context - module_source: the full module source (for imports, downstream uses) - test_inputs: optional test inputs for execution-based validation """ name: str source: str signature: str constraints: StubConstraints module_source: Optional[str] = None test_inputs: Optional[list[dict[str, Any]]] = None test_outputs: Optional[list[Any]] = None property_tests: Optional[list[Callable]] = None @property def stub_id(self) -> str: """Unique identifier for this stub.""" content = f"{self.name}:{self.signature}:{self.constraints.decorator_spec}" return hashlib.sha256(content.encode()).hexdigest()[:12] def to_completion_prompt(self, style: str = "direct") -> str: """ Generate an LLM prompt for completing this stub. Args: style: 'direct' for straightforward completion, 'diverse' for encouraging algorithmic diversity (SFS scattering), 'concept_guided' for concept-directed generation """ if style == "direct": return self._direct_prompt() elif style == "diverse": return self._diverse_prompt() elif style == "concept_guided": return self._concept_guided_prompt() else: raise ValueError(f"Unknown prompt style: {style}") def _direct_prompt(self) -> str: return ( f"Complete the following Python function. " f"Only output the function body.\n\n" f"{self.constraints.to_prompt_context()}\n\n" f"```python\n{self.source}\n```" ) def _diverse_prompt(self) -> str: """SFS-inspired scattering: ask for diverse algorithmic directions first.""" return ( f"Consider this Python function stub:\n\n" f"```python\n{self.source}\n```\n\n" f"{self.constraints.to_prompt_context()}\n\n" f"First, list 3-5 fundamentally different algorithmic approaches " f"to implement this function (e.g., iterative vs recursive, " f"different data structures, different time/space tradeoffs).\n\n" f"Then implement ONE of these approaches. Choose a different approach " f"than you normally would. Only output the function body." ) def _concept_guided_prompt(self) -> str: return ( f"Complete this function stub:\n\n" f"```python\n{self.source}\n```\n\n" f"{self.constraints.to_prompt_context()}\n\n" f"Implement the function. Only output the function body." ) class StubRegistry: """Global registry of all stubs defined with @reason_first.""" _stubs: dict[str, Stub] = {} @classmethod def register(cls, stub: Stub) -> None: cls._stubs[stub.stub_id] = stub @classmethod def get(cls, stub_id: str) -> Optional[Stub]: return cls._stubs.get(stub_id) @classmethod def list_all(cls) -> list[Stub]: return list(cls._stubs.values()) @classmethod def clear(cls) -> None: cls._stubs.clear() def _extract_inline_specs(source: str) -> list[str]: """Extract #> comments from source code.""" specs = [] for line in source.split("\n"): stripped = line.strip() if stripped.startswith("#>"): specs.append(stripped[2:].strip()) return specs def _extract_imports(module_source: str) -> list[str]: """Extract import statements from module source.""" imports = [] try: tree = ast.parse(module_source) for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: imports.append(alias.name) elif isinstance(node, ast.ImportFrom): module = node.module or "" for alias in node.names: imports.append(f"{module}.{alias.name}") except SyntaxError: pass return imports def _extract_type_hints(func: Callable) -> tuple[dict[str, str], Optional[str]]: """Extract type hints from function signature.""" hints = {} return_type = None try: sig = inspect.signature(func) for name, param in sig.parameters.items(): if param.annotation != inspect.Parameter.empty: hints[name] = str(param.annotation) if sig.return_annotation != inspect.Signature.empty: return_type = str(sig.return_annotation) except (ValueError, TypeError): pass return hints, return_type def reason_first( spec: str = "", preconditions: Optional[list[str]] = None, postconditions: Optional[list[str]] = None, test_inputs: Optional[list[dict[str, Any]]] = None, test_outputs: Optional[list[Any]] = None, ): """ Decorator that marks a function as a reason-first program stub. Args: spec: Natural language specification of what the function should do preconditions: List of precondition descriptions postconditions: List of postcondition descriptions test_inputs: Optional list of test input dicts for validation test_outputs: Optional list of expected outputs Example: @reason_first( spec="Sort items by priority, breaking ties by recency", postconditions=["output is sorted", "all input items present in output"] ) def process_queue(items: list[Item]) -> list[Item]: #> stable sort; O(n log n); must preserve Item identity ... """ def decorator(func: Callable) -> Callable: source = inspect.getsource(func) source = textwrap.dedent(source) signature = str(inspect.signature(func)) inline_specs = _extract_inline_specs(source) # Try to get module source for import/context extraction module_source = None try: module_file = inspect.getfile(func) if module_file: module_source = Path(module_file).read_text() except (TypeError, OSError): pass imports = _extract_imports(module_source) if module_source else [] type_hints, return_type = _extract_type_hints(func) constraints = StubConstraints( imports=imports, type_hints=type_hints, return_type=return_type, inline_specs=inline_specs, decorator_spec=spec, preconditions=preconditions or [], postconditions=postconditions or [], ) stub = Stub( name=func.__name__, source=source, signature=signature, constraints=constraints, module_source=module_source, test_inputs=test_inputs, test_outputs=test_outputs, ) StubRegistry.register(stub) # Attach stub metadata to the function func._rfp_stub = stub return func return decorator