Skip to content

feat: Type Registry System & Static/Dynamic Import Modes (v0.2.0)#2

Merged
pyros-projects merged 7 commits intomainfrom
feat/static-dynamic
Nov 24, 2025
Merged

feat: Type Registry System & Static/Dynamic Import Modes (v0.2.0)#2
pyros-projects merged 7 commits intomainfrom
feat/static-dynamic

Conversation

@pyros-projects
Copy link
Copy Markdown
Owner

🎯 Overview

This PR introduces two major feature sets for wishful v0.2.0:

  1. Type Registry System - Enable LLM-aware structured output generation
  2. Static vs Dynamic Import Modes - Control caching behavior via namespace routing

These features significantly enhance wishful's capabilities for production use cases while maintaining backward compatibility.


✨ What's New

🔷 Type Registry System

Allows users to register complex types (Pydantic models, dataclasses, TypedDict) so the LLM can generate functions that return properly structured data.

Key Features:

  • @wishful.type decorator for registering types
  • Full Pydantic v2 Field constraint support (min_length, max_length, gt, ge, lt, le, pattern)
  • Docstring-driven LLM behavior (e.g., generate content in specific tone/style)
  • Output type binding via @wishful.type(output_for="function_name")
  • Type schemas serialized and included in LLM prompts

Example:

from pydantic import BaseModel, Field
import wishful

@wishful.type
class ProjectPlan(BaseModel):
    """Project plan written by master yoda from star wars."""
    project_brief: str
    milestones: list[str] = Field(description="list of milestones", min_length=10)
    budget: float = Field(gt=0, description="project budget in USD")

from wishful.static.pm import project_plan_generator
plan = project_plan_generator(idea="sudoku web app")
# Returns ProjectPlan with 10+ milestones in Yoda-speak! 🎭

🔷 Static vs Dynamic Import Modes

Namespace-based routing for different caching behaviors:

wishful.static.* (Default, Cached)

  • Generated once, reused forever
  • Fast - no LLM calls after first import
  • Editable cache files
  • Use for: utilities, parsers, validators

wishful.dynamic.* (Runtime-Aware, Fresh)

  • Regenerates on every import (never uses cache)
  • Captures fresh runtime context each time
  • Different results on each run
  • Use for: creative content, experiments, testing variations

Example:

# Static - generated once, cached
from wishful.static.text import extract_emails

# Dynamic - fresh generation every import
from wishful.dynamic.content import generate_story

🔧 Technical Changes

Core Implementation

Type Registry (src/wishful/types/)

  • registry.py: TypeRegistry class with serialization for Pydantic/dataclass/TypedDict
  • _build_field_args(): Parses Pydantic v2 metadata list for Field constraints
  • Supports both Pydantic v1 (direct attributes) and v2 (metadata objects)
  • Docstrings included in serialized type definitions

Import Hook Enhancements (src/wishful/core/)

  • finder.py: Updated MagicFinder to route static vs dynamic namespaces
  • loader.py: MagicLoader accepts mode parameter ("static" or "dynamic")
    • Static mode: cache lookup → generate if missing → cache write
    • Dynamic mode: always generate, never read/write cache
  • discovery.py: Fetches type schemas and output type bindings

LLM Integration (src/wishful/llm/)

  • prompts.py: Enhanced to include type definitions in system prompt
  • System prompt updated: "You may use any Python libraries available in the environment"
  • Type schemas and output type bindings passed to LLM

Cache Management (src/wishful/cache/)

  • manager.py: Strips static/dynamic namespace prefixes from cache paths
  • Both namespaces use same cache file (e.g., wishful.static.text.wishful/text.py)

Testing

New Test Coverage:

  • tests/test_types.py: 30 tests for type registry (Pydantic, dataclass, TypedDict, constraints)
  • tests/test_namespaces.py: 6 tests for static vs dynamic behavior
  • tests/test_discovery.py: 4 additional tests for type schema integration

Test Statistics:

  • Total: 83 tests (was 49)
  • Coverage: 80% (maintained)
  • All tests passing ✅

Documentation

Updated:

  • README.md: Complete rewrite showcasing new features
    • Type Registry section with Pydantic examples
    • Static vs Dynamic comparison table
    • Yoda-speak example demonstrating docstring influence
    • Professional badges (PyPI, Python 3.12+, Tests, Coverage, Ruff)
  • AGENTS.md: Comprehensive sync with codebase
    • Type Registry architecture documented
    • Namespace routing details
    • Pydantic v2 constraint handling
    • Development workflow (TDD process)
  • docs/CHANGELOG.md: Created for v0.2.0 release notes

New Examples:

  • examples/07_typed_outputs.py: Showcases type registry with all supported types
  • examples/08_dynamic_vs_static.py: Demonstrates namespace differences
  • examples/09_context_shenanigans.py: Context discovery and import-site hints

Dependencies

Added:

  • pydantic>=2.12.4: Required for Pydantic v2 type registry support

Version Bump

  • pyproject.toml: Version 0.1.6 → 0.2.0

🧪 Testing & Validation

Automated Tests

uv run pytest tests/ -v
# Result: 83 passed in 0.14s ✅

Manual Validation

# Type registry with Pydantic constraints
WISHFUL_FAKE_LLM=1 uv run python examples/07_typed_outputs.py

# Static vs dynamic namespaces
WISHFUL_FAKE_LLM=1 uv run python examples/08_dynamic_vs_static.py

# Context discovery
uv run python examples/09_context_shenanigans.py

Backward Compatibility

  • All existing examples still work (00-06)
  • No breaking changes to public API
  • Default behavior unchanged (imports use static namespace)

📊 Impact Assessment

Benefits

Structured Output: Production-ready type-safe code generation
Flexibility: Choose between cached stability vs runtime creativity
Developer Experience: Docstrings influence LLM behavior (e.g., tone, style)
Type Safety: Pydantic constraints actually enforced by LLM
Performance: Static mode = zero latency after first import

Risks & Mitigations

⚠️ Pydantic Dependency: Now requires Pydantic 2.12.4+

  • Mitigation: Common dependency, well-maintained, backward compatible

⚠️ Namespace Complexity: Users need to choose static vs dynamic

  • Mitigation: Clear documentation, sensible default (static), examples

⚠️ Cache Sharing: Static and dynamic share same cache file

  • Mitigation: Intentional design - dynamic reads from cache if exists, but always regenerates

🔍 Code Review Checklist

  • All tests passing (83/83)
  • Code coverage maintained (80%)
  • Documentation updated (README, AGENTS, CHANGELOG)
  • Examples provided for new features
  • Backward compatibility preserved
  • Type hints added to new code
  • Safety validation still enforced
  • No breaking changes to public API

🚀 Deployment Notes

Post-Merge Actions

  1. Tag release: git tag v0.2.0
  2. Build package: uv build
  3. Publish to PyPI: uv publish
  4. Update GitHub release notes with CHANGELOG.md content

User Migration

No migration needed - backward compatible. Users can:

  1. Continue using imports as before (defaults to static namespace)
  2. Opt into type registry with @wishful.type
  3. Use dynamic imports when needed via wishful.dynamic.*

📝 Related Issues

Closes: N/A (feature development)
Relates to: Future work on multi-agent workflows, evaluation frameworks


🙏 Acknowledgments

This feature set evolved from exploring how to make LLM-generated code production-ready:

  • Type safety through Pydantic integration
  • Performance through intelligent caching
  • Flexibility through namespace routing

The Yoda-speak example proved docstrings actually influence LLM behavior in delightful ways. 🎭


🤔 Open Questions for Reviewers

  1. Should we expose type registry inspection API (wishful.list_registered_types())?
  2. Should dynamic imports have a separate cache directory to avoid confusion?
  3. Is the docstring-driven behavior adequately documented?

*Ready to merge/home/ai/projects/wishful && git log --oneline main..HEAD All tests green, documentation complete, examples working. ��✨

@pyros-projects pyros-projects merged commit a57108f into main Nov 24, 2025
1 check passed
@pyros-projects pyros-projects deleted the feat/static-dynamic branch November 24, 2025 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant