| Chore/update examples implicit rendering (#748) * feat(reactive-runtime): add return type hint registry and side effect lifting - Introduced a unified return type hint registry to replace tuple return registry. - Implemented `register_return_type_hint` and `get_return_type_hint` utilities to support both string and tuple return types. - Added `is_returning` and `auto_register_return_hints` to support - static introspection of return annotations from modules such as `matplotlib` and `plotly`. - Replaced manual `register_tuple_return` calls with automatic registration via `auto_register_return_hints`. - Added `display_plotly_figure_show` as an iframe-based HTML renderer for Plotly figures. - Introduced `_lift_side_effect_stmt` to lift method calls, such as `fig.show()`, as reactive side effect atoms. - Centralized return type inference into `_infer_and_register_return_type`, now used in both producer and blackbox lifting. - `preswald/engine/transformers/reactive_runtime.py` - `preswald/interfaces/render/registry.py` This change unifies how return types are inferred and tracked, enabling automatic support for libraries with rich rendering behavior, such as matplotlib and plotly. It also improves support for side effecting calls like `fig.show()` by lifting them into re-executable reactive atoms. * refactor(ast): support explicit return targets in atom generation This refactors `_build_atom_function` and `_finalize_and_register_atom` to support an optional `return_target`, enabling better control over return values for atoms generated from subscript assignments, unpacking, and complex expressions. Key changes: - Added support for `return_target` in `_finalize_and_register_atom` and `_build_atom_function` - Added `_validate_or_remap_return_target` helper to safely remap returns to parameters - Added `_lift_subscript_assign_stmt` for handling assignments like `data["Magnitude"] = ...` - Updated `_lift_producer_stmt` and `_lift_assign_stmt` to conditionally set `return_target` - Refactored function argument generation to `_make_param_args` These improvements fix incorrect return statements in lifted atoms and allow proper DAG tracking and reactivity for in place mutations. * refactor(examples): switch to implicit rendering in user scripts This updates the `earthquakes/hello.py` and `iris/hello.py` example scripts to use implicit rendering support for Plotly figures. Instead of wrapping figures in `plotly(...)`, the scripts now use `.show()` directly on the Plotly objects, enabling automatic rendering via the implicit rendering runtime. Also removes unused imports of the `plotly` component in both scripts. Changes: - Replace `plotly(fig)` with `fig.show()` for map, histogram, scatter, box, and violin plots - Remove redundant `plotly` imports * feat(reactive): support subscript mutations and blackbox function fallback - Lift subscript assignments, such as `arr[i] = compute(x)`, into reactive atoms: - Tracks the mutated variable as a dependency - Handles multiple reactive inputs on the rhs - Returns the mutated object to support downstream chaining - Add fallback lifting for blackbox function calls: - Supports both inline expressions and tuple unpacking - Registers variables with tuple indices and return targets - Detect and skip non-reactive user defined functions: - Skips lifting functions that are undecorated, contain no component calls, and are called directly - Add namespaced support for Preswald components: - Recognizes calls like `preswald.text()` and lifts them properly - Refactor known component detection via `_is_known_component_call()` and `_get_call_func_name()` * fix(reactive_runtime): pass variable_map to component atom lifter to fix dependency resolution Previously, `lift_component_call_to_atom` did not receive a `variable_map`, causing a `TypeError` when called from `visit_Call` or `_lift_statements`. This led to incorrect or missing dependency resolution for inline component calls. This commit updates all invocations of `lift_component_call_to_atom` to pass an appropriate `variable_map`. In `visit_Call`, the current reactive scope from `self._current_frame.variable_to_atom` is used as a fallback. * chore(logging): reduce verbosity of reactive_runtime logs by downgrading info to debug Downgraded non-critical `logger.info` statements to `logger.debug` throughout `reactive_runtime.py` to reduce console noise and make logs more readable during development and production runs. This change preserves high level transformation logs at the `info` level, such as module transformation start/end, while moving detailed variable maps, ast dumps, and component metadata to `debug`. No functional changes were made to logic or behavior. * refactor(registry, transformer): cleanup imports, fix type annotations, and remove dead code - Normalized and reordered imports in `reactive_runtime.py` and `registry.py` for clarity and consistency - Replaced deprecated union syntax, such as `Optional[...]`, with PEP 604 style (`T | None`) - Removed unused or redundant imports - Fixed an undefined call to `register_return_type_hint_return(...)` by replacing it with the correct `register_return_type_hint(...)` - Removed dead local variables and unnecessary assignments, such as `display_renderers`, `dependency_resolvers` - Ensured consistency in `logger` statements and formatting These changes improve code readability, align type annotations with modern standards, and reduce noise ahead of a broader cleanup task. * feat(runtime, registry): support component aliases and improve pre-registration metrics - Added support for component level name aliases (e.g., `text as component_text`) in AST transformation. - Updated `_is_known_component_call` to recognize aliased function calls by resolving `name_aliases`. - Introduced `_variable_class_map` to track constructor assignments, such as `fig = go.Figure()`, for accurate renderer matching. - Introduced `visit_Assign` to populate variable class map based on constructor calls. - Relocated display renderer lifting logic in `_lift_statements` to avoid top level atom call emission. - In `registry.py`, added `get_plotly_submodules()` for static submodule discovery. - Refactored plotly/matplotlib pre-registration with explicit timing metrics using `time.perf_counter()`. - Logged renderer registration time to help isolate latency hotspots during startup. - Removed obsolete `prefix` parameter from `auto_register_return_hints`. Inline component calls are no longer lifted in `visit_Call`; lifting is now centralized in `_lift_statements`. * fix(reactive-runtime): fix display renderer lifting and candidate resolution logic - Ensure `_maybe_lift_display_renderer_from_expr` runs by prioritizing display renderer lifting in expression handler order - Correctly resolve display renderer candidates by deriving the base class from `atom_return_types` instead of fallback maps - Fall back to `variable_class_map` or `import_aliases` only when return type is unavailable - Normalize function names using canonical imports for consistent matching in return type inference - Relocate and simplify return renderable lifting logic for clarity and consistent behavior This resolves cases where renderers like `fig.show()` and similar were silently skipped due to incorrect type inference or handler order. * fix(runtime): correct side effect lifting for multi var dependencies Previously, `_lift_side_effect_stmt` only supported simple method calls on single reactive variables. This update: - Adds full support for side effectful method calls that depend on multiple reactive variables - Uses scoped dependency resolution and param remapping (including tuple unpacking) consistent with consumer atoms. The docstring was also updated to reflect these expanded capabilities and correct usage patterns. | 1 年前 |
| feat(renderbuffer): integrate diffing into core components and restore msgpack dependency (#633) * feat: skip reruns in virtual service if no state changes detected - Updates handle_message_from_js to robustly parse JsProxy input - Skips rerun execution when incoming component state matches current state - Ensures backwards compatibility with previous JS message shape - Adds eslint-config-prettier to package-lock.json (already in devDependencies) * fix(virtual_service): suppress Ruff warning and clean up Pyodide handler - Removed unused import (`Object`) from JS bridge - Suppressed RUF006 warning by explicitly allowing fire-and-forget task - Simplified `except` clause (unused exception variable) - Minor style cleanup in test page - Updated `pyodide_test.html` to initialize `PreswaldService` during testing * feat(runtime): add RenderBuffer to VirtualPreswaldService to skip redundant reruns - Introduces `RenderBuffer` class in `engine/utils.py` to track previous component state. - Integrates `RenderBuffer` into `VirtualPreswaldService` to detect and skip reruns when state hasn't changed. - Adds `update_if_changed` logic to prevent unnecessary recomputations and logging to confirm behavior. - This is a foundational step toward a reactive runtime and will be extended to `ServerPreswaldService` in a follow-up. Tested manually in `pyodide_test.html` by sending repeated identical messages and observing correct skipping of reruns. * feat(server): use RenderBuffer to skip redundant reruns in ServerPreswaldService This change introduces a render buffer for ServerPreswaldService that tracks component state and avoids unnecessary reruns when updates do not modify any actual values. Aligns behavior with VirtualPreswaldService. Logs a debug message when reruns are skipped due to unchanged state. * fix(frontend): add @monaco-editor/react dependency to fix build error The PlaygroundWidget component imports @monaco-editor/react, but the package was missing from the dependencies. This caused the frontend build to fail after syncing with upstream. This commit adds the missing package to resolve the issue. * feat(components): integrate RenderBuffer to skip redundant renders Introduces `RenderBuffer` support across all Preswald components to prevent unnecessary re-renders and frontend updates when component state has not changed. Components now use `generate_stable_id()` to ensure deterministic ids based on callsite or user-supplied identifiers, enabling consistent diffing between reruns. Additionally, common service logic has been extracted into a shared `BasePreswaldService`, now inherited by both `ServerPreswaldService` and `VirtualPreswaldService`. - All components now use `should_render()` to conditionally append - Debug logs are now guarded to avoid expensive serialization - `RenderBuffer` hashes values internally and accepts either raw objects or precomputed hashes * feat(renderbuffer): integrate diffing into core components and restore msgpack dependency - Re-added `msgpack` as a dependency in `setup.py`, now required by RenderBuffer (was previously removed when the Fastplotlib component was disabled). - Commented out the unused `render_and_send_fastplotlib()` function, which was left behind after Fastplotlib was removed from `main`. - Applied Ruff formatting and cleanup across engine and interface modules. * refactor(service): remove fastapi.WebSocket dependency from BasePreswaldService This refactor removes the direct dependency on `fastapi.WebSocket` from `BasePreswaldService` to support alternative websocket implementations such as the `VirtualWebSocket` used in Pyodide. Also includes: - Generalization of type hints for `websocket` parameters to `Any`. - Removal of the `fastapi` import from `base_service.py`. - Removed the platform constraint from the `msgpack` dependency to enable usage in Pyodide. Other unrelated changes: - Removed the platform constraint from `duckdb` as it is now required in Pyodide builds. - Updated the test wheel reference in `pyodide_test.html` from `0.1.49` to `0.1.51`. * chore: remove unnecessary import * chore: re-enable msgpack dependency for RenderBuffer hashing * chore: remove .projectile from git and ignore it in .gitignore - Untracked .projectile to prevent accidental commits - Added .projectile to .gitignore under Emacs-specific section * build(pyodide): remove platform constraint on msgpack to enable renderbuffer in virtual_service | 1 年前 |