From 76c3622e4a6ba1bdade9cf60da3a8625c538b870 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 22 Dec 2025 16:43:18 -0700 Subject: [PATCH] changes --- README.md | 15 -- examples/cross_widget_interactions.ipynb | 45 +++-- src/vibe_widget/__init__.py | 6 +- src/vibe_widget/config.py | 202 +---------------------- src/vibe_widget/themes.py | 32 ++-- 5 files changed, 44 insertions(+), 256 deletions(-) diff --git a/README.md b/README.md index 3bf40ec..3deaad9 100644 --- a/README.md +++ b/README.md @@ -137,9 +137,6 @@ vw.config(mode="premium", model="openrouter") # or vw.config(model="openai/gpt-5.1-codex") vw.config(model="anthropic/claude-opus-4.5") - -# View available models -vw.models() ``` --- @@ -295,18 +292,6 @@ vw.config( ) ``` -### `models()` - -View available models. - -```python -vw.models() # Show defaults and pinned models -vw.models(show="all") # Show all available models -vw.models(verbose=False) # Quiet mode -``` - ---- - ## How It Works ``` diff --git a/examples/cross_widget_interactions.ipynb b/examples/cross_widget_interactions.ipynb index f717421..24058bd 100644 --- a/examples/cross_widget_interactions.ipynb +++ b/examples/cross_widget_interactions.ipynb @@ -51,8 +51,7 @@ "import pandas as pd\n", "import numpy as np\n", "import os\n", - "\n", - "vw.models()" + "\n" ] }, { @@ -373,7 +372,7 @@ "id": "f2a9bb66", "metadata": {}, "source": [ - "## Example 2: 2D Terrain Painter β†’ 3D Landscape\n", + "## Example 2: 2D Terrain Painter \u2192 3D Landscape\n", "\n", "This is one of the coolest examples! We'll create:\n", "1. **2D Canvas**: Paint terrain height with your mouse (exports `heightmap`)\n", @@ -457,7 +456,7 @@ "text": [ "\n", "3D Landscape viewer created and linked!\n", - "Paint on canvas β†’ See in 3D instantly\n" + "Paint on canvas \u2192 See in 3D instantly\n" ] } ], @@ -479,7 +478,7 @@ ")\n", "\n", "print(\"\\n3D Landscape viewer created and linked!\")\n", - "print(\"Paint on canvas β†’ See in 3D instantly\")" + "print(\"Paint on canvas \u2192 See in 3D instantly\")" ] }, { @@ -506,7 +505,7 @@ "version_minor": 0 }, "text/plain": [ - "VBox(children=(Label(value='Simulate 2000 raindrops eroding the terrain:'), Button(description='🌧️ Simulate Ra…" + "VBox(children=(Label(value='Simulate 2000 raindrops eroding the terrain:'), Button(description='\ud83c\udf27\ufe0f Simulate Ra\u2026" ] }, "metadata": {}, @@ -521,7 +520,7 @@ "\n", "def simulate_erosion(b):\n", " \"\"\"Simulate hydraulic erosion on the terrain\"\"\"\n", - " btn.description = \"🌧️ Raining...\"\n", + " btn.description = \"\ud83c\udf27\ufe0f Raining...\"\n", " btn.disabled = True\n", " \n", " # Get current heightmap\n", @@ -569,11 +568,11 @@ " grid = np.clip(grid, 0.0, 1.0)\n", " painter.heightmap = grid.flatten().tolist()\n", " \n", - " btn.description = \"🌧️ Simulate Rain Erosion\"\n", + " btn.description = \"\ud83c\udf27\ufe0f Simulate Rain Erosion\"\n", " btn.disabled = False\n", "\n", "btn = widgets.Button(\n", - " description=\"🌧️ Simulate Rain Erosion\",\n", + " description=\"\ud83c\udf27\ufe0f Simulate Rain Erosion\",\n", " layout=widgets.Layout(width='100%', height='50px'),\n", " style={'button_color': '#4488ff'}\n", ")\n", @@ -771,7 +770,7 @@ " \n", " \n", "\n", - "

5 rows Γ— 21 columns

\n", + "

5 rows \u00d7 21 columns

\n", "" ], "text/plain": [ @@ -863,8 +862,8 @@ "output_type": "stream", "text": [ "\n", - "πŸͺ 3D Solar system created!\n", - "πŸ–±οΈ Click planets to select them\n" + "\ud83e\ude90 3D Solar system created!\n", + "\ud83d\uddb1\ufe0f Click planets to select them\n" ] } ], @@ -963,7 +962,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Click planets in 3D β†’ See highlighted in chart\n" + "Click planets in 3D \u2192 See highlighted in chart\n" ] } ], @@ -984,7 +983,7 @@ ")\n", "\n", "\n", - "print(\"Click planets in 3D β†’ See highlighted in chart\")" + "print(\"Click planets in 3D \u2192 See highlighted in chart\")" ] }, { @@ -1009,9 +1008,9 @@ "```\n", "\n", "The AI uses your description to:\n", - "- βœ… Generate code that updates this trait\n", - "- βœ… Choose the right data type (array, string, object, etc.)\n", - "- βœ… Set up event listeners to keep it synchronized\n", + "- \u2705 Generate code that updates this trait\n", + "- \u2705 Choose the right data type (array, string, object, etc.)\n", + "- \u2705 Set up event listeners to keep it synchronized\n", "\n", "### Imports\n", "\n", @@ -1028,17 +1027,17 @@ "```\n", "\n", "The AI generates code that:\n", - "- βœ… Listens for changes to the imported trait\n", - "- βœ… Updates the visualization when the trait changes\n", - "- βœ… Handles the trait data appropriately\n", + "- \u2705 Listens for changes to the imported trait\n", + "- \u2705 Updates the visualization when the trait changes\n", + "- \u2705 Handles the trait data appropriately\n", "\n", "### Bidirectional Linking\n", "\n", "Vibe Widget uses **traitlets** under the hood to create bidirectional links:\n", "\n", "```\n", - "Widget A (exports) ←→ Widget B (imports)\n", - " ↓ trait change ↑ automatically updated\n", + "Widget A (exports) \u2190\u2192 Widget B (imports)\n", + " \u2193 trait change \u2191 automatically updated\n", "```\n", "\n", "This means changes flow automatically in both directions!" @@ -1243,7 +1242,7 @@ " \n", " \n", "\n", - "

5 rows Γ— 23 columns

\n", + "

5 rows \u00d7 23 columns

\n", "" ], "text/plain": [ diff --git a/src/vibe_widget/__init__.py b/src/vibe_widget/__init__.py index eebe624..d39dcdc 100644 --- a/src/vibe_widget/__init__.py +++ b/src/vibe_widget/__init__.py @@ -1,7 +1,7 @@ from vibe_widget.core import VibeWidget, create, clear, ComponentReference from vibe_widget.api import export, exports, imports, ExportHandle -from vibe_widget.config import config, Config, models -from vibe_widget.themes import Theme, theme, themes +from vibe_widget.config import config, Config +from vibe_widget.themes import Theme, theme __version__ = "0.1.0" __all__ = [ @@ -11,10 +11,8 @@ "ComponentReference", "config", "Config", - "models", "Theme", "theme", - "themes", "export", "exports", "imports", diff --git a/src/vibe_widget/config.py b/src/vibe_widget/config.py index 16b895c..ad3d798 100644 --- a/src/vibe_widget/config.py +++ b/src/vibe_widget/config.py @@ -4,12 +4,9 @@ import os from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Literal +from typing import Any, Dict, Optional from pathlib import Path import json -import time - -import requests # Load models manifest def _load_models_manifest() -> Dict[str, Any]: @@ -22,88 +19,6 @@ def _load_models_manifest() -> Dict[str, Any]: DEFAULT_MODEL = "google/gemini-3-flash-preview" -_OPENROUTER_MODELS_CACHE: dict[str, Any] | None = None -_OPENROUTER_MODELS_CACHE_TS: float | None = None - - -class ModelsCatalog(dict): - """Dict-like return type with a concise notebook-friendly repr.""" - - def __repr__(self) -> str: # pragma: no cover - keys = list(self.keys()) - if not keys: - return "ModelsCatalog({})" - - openrouter = self.get("openrouter", {}) - standard = openrouter.get("standard", []) - premium = openrouter.get("premium", []) - latest = openrouter.get("latest", []) - return ( - "ModelsCatalog(" - f"standard={len(standard)}, premium={len(premium)}, latest={len(latest)}" - ") # Use dict(vw.models(...)) to see full data" - ) - - def __str__(self) -> str: # pragma: no cover - return self.__repr__() - - def _repr_pretty_(self, p, cycle) -> None: # pragma: no cover - p.text(self.__repr__()) - - -def _fetch_openrouter_models( - refresh: bool = True, - cache_ttl_seconds: int = 3600, - timeout_seconds: int = 10, -) -> list[str] | None: - """Fetch the latest OpenRouter model IDs (best-effort).""" - global _OPENROUTER_MODELS_CACHE, _OPENROUTER_MODELS_CACHE_TS - - now = time.time() - if ( - not refresh - and _OPENROUTER_MODELS_CACHE is not None - and _OPENROUTER_MODELS_CACHE_TS is not None - and (now - _OPENROUTER_MODELS_CACHE_TS) < cache_ttl_seconds - ): - return _OPENROUTER_MODELS_CACHE.get("ids") - - if ( - _OPENROUTER_MODELS_CACHE is not None - and _OPENROUTER_MODELS_CACHE_TS is not None - and (now - _OPENROUTER_MODELS_CACHE_TS) < cache_ttl_seconds - and not refresh - ): - return _OPENROUTER_MODELS_CACHE.get("ids") - - try: - headers = {} - api_key = os.getenv("OPENROUTER_API_KEY") - if api_key: - headers["Authorization"] = f"Bearer {api_key}" - - resp = requests.get( - "https://openrouter.ai/api/v1/models", - headers=headers or None, - timeout=timeout_seconds, - ) - resp.raise_for_status() - data = resp.json() - model_items = data.get("data", []) - ids = sorted( - { - item.get("id") - for item in model_items - if isinstance(item, dict) and item.get("id") - } - ) - - _OPENROUTER_MODELS_CACHE = {"ids": ids} - _OPENROUTER_MODELS_CACHE_TS = now - return ids - except Exception: - return None - # Build model mappings from manifest def _build_model_maps() -> tuple[dict, dict]: """Build premium and standard model mappings from manifest.""" @@ -321,118 +236,3 @@ def config( return _global_config - -def models( - provider: Optional[str] = None, - mode: Optional[str] = None, - *, - refresh: bool = True, - verbose: bool = True, - show: Literal["summary", "all", "none"] = "summary", - limit: int = 30, -) -> Dict[str, Dict[str, List[str]]]: - """ - Get available model IDs (OpenRouter), optionally fetching the latest list. - - Args: - provider: Optional provider filter ("openrouter") - mode: Optional mode filter ("standard" or "premium") - refresh: When True, tries to fetch latest models from OpenRouter - verbose: When True, prints helpful config instructions - show: "summary" prints defaults + pinned options; "all" also prints a live list; "none" prints nothing - limit: Max models to print per provider group - - Returns: - Dictionary of available models by provider and tier - - Examples: - >>> # Get all models - >>> vw.models() - - >>> # Get models for a specific provider - >>> vw.models("openrouter") - - >>> # Get premium defaults - >>> vw.models("openrouter", mode="premium") - """ - result: Dict[str, Dict[str, List[str]]] = {} - - provider_key = (provider or "openrouter").lower() - if provider_key != "openrouter": - provider_key = "openrouter" - - provider_manifest = MODELS_MANIFEST.get("openrouter", {}) - manifest_standard = [m["id"] for m in provider_manifest.get("standard", [])] - manifest_premium = [m["id"] for m in provider_manifest.get("premium", [])] - - latest_ids = _fetch_openrouter_models(refresh=refresh) - latest_ids = latest_ids or [] - - def _filter_prefix(prefix: str) -> list[str]: - return [m for m in latest_ids if m.startswith(prefix)] - - data: Dict[str, List[str]] = { - "defaults": [ - f"standard={STANDARD_MODELS.get('openrouter')}", - f"premium={PREMIUM_MODELS.get('openrouter')}", - ], - "standard": manifest_standard, - "premium": manifest_premium, - "latest": latest_ids, - "google": _filter_prefix("google/"), - "anthropic": _filter_prefix("anthropic/"), - "openai": _filter_prefix("openai/"), - } - - if mode in {"standard", "premium"}: - data = { - "defaults": data["defaults"], - mode: data[mode], - "latest": data["latest"], - "google": data["google"], - "anthropic": data["anthropic"], - "openai": data["openai"], - } - - result[provider_key] = data - - if verbose and show != "none": - standard_default = STANDARD_MODELS.get("openrouter") - premium_default = PREMIUM_MODELS.get("openrouter") - - print("Model selection (OpenRouter)") - print("Defaults:") - print(f" default: {DEFAULT_MODEL}") - print(f' vw.config(model="openrouter") # -> {standard_default}') - print(f' vw.config(model="openrouter", mode="premium") # -> {premium_default}') - print("Explicit examples:") - print(f' vw.config(model="{standard_default}")') - print(f' vw.config(model="{premium_default}", mode="premium")') - print("Pinned options:") - print(f" standard: {manifest_standard}") - print(f" premium: {manifest_premium}") - print('More: `vw.models(show="all")` or `vw.models(verbose=False)`.\n') - print("Tip: set OPENROUTER_API_KEY in your environment.\n") - - if show == "all": - if latest_ids: - def _print_group(name: str, ids: list[str]) -> None: - if not ids: - return - shown = ids[: max(0, limit)] - suffix = "" if len(ids) <= len(shown) else f" (+{len(ids) - len(shown)} more)" - print(f"{name} models ({len(ids)}):{suffix}") - for mid in shown: - print(f" - {mid}") - print() - - _print_group("google", data.get("google", [])) - _print_group("anthropic", data.get("anthropic", [])) - _print_group("openai", data.get("openai", [])) - else: - print( - "Could not fetch the latest OpenRouter model list right now; " - "returning the pinned manifest models.\n" - ) - - return ModelsCatalog(result) diff --git a/src/vibe_widget/themes.py b/src/vibe_widget/themes.py index c9cbb35..bdf6edd 100644 --- a/src/vibe_widget/themes.py +++ b/src/vibe_widget/themes.py @@ -275,19 +275,25 @@ def _find_base_theme(self, prompt: str) -> Theme | None: return None -def theme(*args: Any, cache: bool = True) -> Theme: - """Resolve or compose themes.""" - if not args: - raise ValueError("vw.theme requires at least one argument.") - registry = ThemeRegistry() - if len(args) == 1: - return registry.resolve(args[0], cache=cache) - resolved = [registry.resolve(arg, cache=cache) for arg in args] - return registry.compose(resolved) - - -def themes() -> ThemesCatalog: - return ThemeRegistry().list() +class ThemeAPI: + """Callable theme API with a catalog accessor.""" + + def __call__(self, *args: Any, cache: bool = True) -> Theme: + """Resolve or compose themes.""" + if not args: + raise ValueError("vw.theme requires at least one argument.") + registry = ThemeRegistry() + if len(args) == 1: + return registry.resolve(args[0], cache=cache) + resolved = [registry.resolve(arg, cache=cache) for arg in args] + return registry.compose(resolved) + + @property + def catalog(self) -> ThemesCatalog: + return ThemeRegistry().list() + + +theme = ThemeAPI() def resolve_theme_for_request(