Changelog
All notable changes to wishful will be documented here. Follows Keep a Changelog and Semantic Versioning.
[0.4.0] - 2026-06-11
Section titled “[0.4.0] - 2026-06-11”Changed
Section titled “Changed”evolve()now returns anEvolutionResultinstead of the bare winner function: callable like the winner (delegates__call__), proxies__name__/__doc__/__wishful_source__/__wishful_evolution__, sets__wrapped__forinspect.signature(), and carries the run’shistoryandbest_score. Code doingisinstance(result, types.FunctionType)must useresult.fninstead. Exported aswishful.EvolutionResult.__wishful_evolution__gained aschema_versionkey (int, currently1); treat unknown keys as forward-compatible additions.wishful.settings.cache_diris now always an absolute path, resolved at construction —os.chdir()can no longer silently move the cache. Comparisons against relativePathliterals will no longer match.- Dynamic modules are lazy: attribute access returns a call wrapper without an LLM call (so
hasattr()is alwaysTruefor public names); generation happens only when the wrapper is invoked. Dynamic modules expose functions only — constants raiseAttributeErrorat call time. - explore() owns its event loop: one persistent background loop on a wishful-owned daemon thread replaces the cached-loop +
nest_asynciodesign.explore()now works inside a running loop (Jupyter) without patching the host, including from atest/benchmarkcallable that itself callsexplore(). explore(verbose=...)defaults tosys.stdout.isatty()andsave_resultsto theWISHFUL_EXPLORE_SAVE_RESULTSenv var (on unless"0"), so headless runs stay quiet by default.
context_radiussetting (envWISHFUL_CONTEXT_RADIUS, default3): the import-site context capture radius is now a regularSettingsfield —configure(context_radius=...)andreset_defaults()aware;set_context_radius()is a thin wrapper.- Nested wishes are rejected up front:
import wishful.static.a.braises a clearImportError(wish names are single-level) before any LLM call, instead of generating the parent and failing with ”’…’ is not a package”. - explore/evolve candidate containment: user
test/benchmark/fitnesscallables run on a bounded worker with a per-variant timeout; a hanging orsys.exit()-ing candidate is recorded as a variant failure instead of stalling the run or killing the host.ExplorationError.failuresnow carries full, untruncated failure text. @wishful.typeschemas reachexplore(): registered type schemas andoutput_forbindings are included in explore’s generation prompts.
Dependencies
Section titled “Dependencies”- Removed
nest-asyncio— no longer needed with the owned event loop. Projects that imported it transitively through wishful must add it as a direct dependency.
0.3.0 - 2026-06-11
Section titled “0.3.0 - 2026-06-11”Note: 0.3.0 was an internal milestone and was never published; its changes first ship publicly together with 0.4.0.
🧬 Evolve
Section titled “🧬 Evolve”wishful.evolve(): generational improvement of a function — keeps a history of prior attempts, scores, and failures, then feeds that history into the next mutation prompt. Returns a function carrying__wishful_source__and__wishful_evolution__. NewEvolutionError.
🖥️ CLI
Section titled “🖥️ CLI”- Real
wishfulconsole script ([project.scripts]) alongsidepython -m wishful; both are equivalent entry points. --jsonoutput on every subcommand for machine-readable results.--versionflag.- Documented exit codes:
0success,1runtime error,2usage error.
⚙️ Configuration
Section titled “⚙️ Configuration”request_timeoutsetting (envWISHFUL_REQUEST_TIMEOUT, default300s): per-request LLM timeout, applied at import time, with an empty-content retry.- Model precedence clarified:
WISHFUL_MODELnow takes precedence overDEFAULT_MODEL(the wishful-specific var wins;DEFAULT_MODELis the fallback). Built-in default remains"azure/gpt-4.1".
🪵 Logging citizenship
Section titled “🪵 Logging citizenship”log_to_filenow defaults toFalse(opt-in viaWISHFUL_LOG_TO_FILE=1): a bareimport wishfulno longer creates files in your CWD.- loguru-based logging with
log_levelcontrol.
🧱 Errors
Section titled “🧱 Errors”WishfulErrorbase exception:SecurityError,GenerationError,ExplorationError, andEvolutionErrornow derive from it.
Changed
Section titled “Changed”🛡️ Security / Validator
Section titled “🛡️ Security / Validator”- Hardened safety validator, framed honestly as “defense in depth, not a sandbox”: AST scan blocks forbidden imports/calls,
__builtins__/globals()/vars()/locals()gadget access, introspection escape chains (__subclasses__/__globals__/__code__/__bases__), aliased dangerous builtins (f = open), write-mode (or non-literal-mode)open(), and system calls on unbound modules. The same scan re-runs on cache load, so tampered.wishful/files are re-checked. Only genuinely computed access (runtime-built attribute names,globals().get(...)) remains a documented residual.
🗄️ Cache
Section titled “🗄️ Cache”- Namespace isolation:
staticanddynamicmodules now map to separate cache files (<cache>/<name>.pyvs<cache>/_dynamic/<name>.py) — they previously collided, so a regenerate or review-rejection on one namespace could clobber the other. Cached sources are re-validated on load, and writes are atomic (temp file + rename).
🔍 Review gate
Section titled “🔍 Review gate”- Review-gate ordering + headless guard: review happens before execution on every path, with a guard for non-interactive environments (fails closed, stays usable in notebooks).
🎨 Type Registry
Section titled “🎨 Type Registry”- Type-registry fixes for serialization and output-type binding (parameterized generics like
list[str]are preserved;default_factoryfields serialize correctly).
📦 Versioning
Section titled “📦 Versioning”__version__now derives from installed package metadata (importlib.metadata.version) instead of being hardcoded.
Breaking changes
Section titled “Breaking changes”max_tokensdefault raised4096→16384: reasoning models (e.g. gpt-5.5) spend part of the budget on hidden reasoning tokens, so the old cap produced empty or truncated output.evolve(verbose=...)removed: the parameter was accepted but never used; passing it now raisesTypeError.log_to_filedefaults toFalse(wasTrue).WISHFUL_MODELtakes precedence overDEFAULT_MODEL(was the reverse).
Dependencies
Section titled “Dependencies”- Refreshed:
litellm(≥1.83.14, for day-0 gpt-5.5 support),rich,python-dotenv,pydantic. - Added
loguruandnest-asyncio. - Removed
pyglove(was unused).
0.2.0 - 2024-11-24
Section titled “0.2.0 - 2024-11-24”🎨 Type Registry System
Section titled “🎨 Type Registry System”- Type registration decorator (
@wishful.type) for Pydantic models, dataclasses, and TypedDict - Pydantic Field constraint support: LLM now respects
min_length,max_length,gt,ge,lt,le, andpatternconstraints - Docstring-driven LLM behavior: Class docstrings influence generated code tone and style (e.g., “written by master yoda” generates Yoda-speak)
- Type binding to functions:
@wishful.type(output_for="function_name")tells LLM which functions should return which types - Multi-function type sharing:
wishful.type(TypeClass, output_for=["func1", "func2"])for shared types
🔄 Static vs Dynamic Namespaces
Section titled “🔄 Static vs Dynamic Namespaces”wishful.static.*namespace: Cached generation (default behavior, fast subsequent imports)wishful.dynamic.*namespace: Runtime-aware regeneration on every import (for creative/contextual content)- Both namespaces share the same cache file for consistency
🧠 Enhanced Context Discovery
Section titled “🧠 Enhanced Context Discovery”- Type schema integration: Registered types are automatically included in LLM prompts
- Function output type hints: LLM receives information about expected return types
- Type definitions are serialized with full docstrings and Field constraints
📦 Pydantic v2 Support
Section titled “📦 Pydantic v2 Support”- Metadata-based constraint extraction: Properly parses Pydantic v2’s constraint storage (
MinLen,MaxLen,Gt, etc.) _PydanticGeneralMetadatahandling: Extractspatternand other general constraints- Backward compatibility: Still supports Pydantic v1 direct attribute access
Changed
Section titled “Changed”System Prompt Updates
Section titled “System Prompt Updates”- External library support: Changed from “only use Python standard library” to “you may use any Python libraries available in the environment”
- Pydantic, requests, and other common libraries now explicitly allowed in generated code
Examples
Section titled “Examples”- Added
07_typed_outputs.py: Comprehensive type registry demonstration - Added
08_dynamic_vs_static.py: Static vs dynamic namespace comparison - Added
09_context_shenanigans.py: Context discovery behavior showcase - All examples updated to use
wishful.static.*namespace convention
Documentation
Section titled “Documentation”- AGENTS.md: Complete sync with current codebase state
- Added Pydantic Field constraint documentation
- Added docstring influence documentation
- Added type registry implementation details
- Updated TDD process documentation
- README.md: Added type registry section, static/dynamic namespace explanation, and updated FAQ
Internal Improvements
Section titled “Internal Improvements”Type Registry (src/wishful/types/)
Section titled “Type Registry (src/wishful/types/)”_build_field_args(): New method to extract Field() arguments from Pydantic field_info_serialize_pydantic(): Enhanced to include Field constraints in serialized schemas- Docstring serialization for all type systems (Pydantic, dataclass, TypedDict)
Discovery System (src/wishful/core/discovery.py)
Section titled “Discovery System (src/wishful/core/discovery.py)”ImportContext: Extended withtype_schemasandfunction_output_typesfieldsdiscover(): Now fetches registered type schemas and output type bindings- Integration with
wishful.types.get_all_type_schemas()andget_output_type_for_function()
LLM Prompts (src/wishful/llm/prompts.py)
Section titled “LLM Prompts (src/wishful/llm/prompts.py)”- Enhanced
build_messages()to include type definitions in prompts - System prompt updated to allow external libraries
- Type schemas formatted as executable Python code for LLM
- 83 total tests with 80% code coverage
- Added 4 new tests in
test_discovery.pyfor type registry integration - Added 30 tests in
test_types.pyfor type serialization (all scenarios) - Added 6 tests in
test_namespaces.pyfor static vs dynamic behavior
Dependencies
Section titled “Dependencies”- Added
pydantic>=2.12.4as runtime dependency
0.1.6 - 2024-11-XX
Section titled “0.1.6 - 2024-11-XX”Initial Release
Section titled “Initial Release”- Basic import hook system with LLM code generation
- Cache management (static
.wishful/directory) - Context discovery from import sites
- Safety validation (AST-based checks)
- CLI interface (
wishful inspect,clear,regen) - Configuration system with environment variables
- litellm integration for multi-provider LLM support
- Fake LLM mode for deterministic testing (
WISHFUL_FAKE_LLM=1)