Repository: pydantic/pydantic-ai
File: pydantic_ai_slim/pydantic_ai/ui/_web/app.py (Python)

Code before:
def _get_cache_dir() -> Path:
    """Get the cache directory for storing UI HTML files.

    Uses XDG_CACHE_HOME on Unix, LOCALAPPDATA on Windows, or falls back to ~/.cache.
    """
    if os.name == 'nt':  # pragma: no cover
        base = Path(os.environ.get('LOCALAPPDATA', Path.home() / 'AppData' / 'Local'))
    else:
        base = Path(os.environ.get('XDG_CACHE_HOME', Path.home() / '.cache'))

    cache_dir = base / 'pydantic-ai' / 'web-ui'
    cache_dir.mkdir(parents=True, exist_ok=True)
    return cache_dir


def _sanitize_version(version: str) -> str:
    """Sanitize version string for use as filename."""
    return re.sub(r'[^a-zA-Z0-9._-]', '_', version)


async def _get_ui_html(version: str, cdn_url_template: str | None = None) -> bytes:
    """Get UI HTML content from filesystem cache or fetch from CDN.

    Args:
        version: The UI version to fetch.
        cdn_url_template: Optional custom URL or file path for the chat UI.
            Falls back to the default CDN if not provided.
    """
    cdn_url_template = cdn_url_template or CDN_URL_TEMPLATE

    cache_dir = _get_cache_dir()
    cache_file = cache_dir / f'{_sanitize_version(version)}.html'

    if cache_file.exists():
        return cache_file.read_bytes()

    cdn_url = cdn_url_template.format(version=version)

    # Support local file paths
    if cdn_url.startswith(('/', '.', '~')) or (len(cdn_url) > 1 and cdn_url[1] == ':'):
        local_path = Path(cdn_url).expanduser()
        if local_path.is_file():
            return local_path.read_bytes()
        raise FileNotFoundError(f'Local UI file not found: {cdn_url}')

    async with httpx.AsyncClient() as client:
        response = await client.get(cdn_url)
        response.raise_for_status()
        content = response.content

    cache_file.write_bytes(content)

Review comment (suggestion):
- Unnecessary for this to be a template -- if the user specifies a URL, the version param becomes irrelevant, and we should just use the URL
- I'd actually like to drop the `version` URL arg entirely.

Code after:
def _get_cache_dir() -> Path:
    """Get the cache directory for storing UI HTML files.

    Uses XDG_CACHE_HOME on Unix, LOCALAPPDATA on Windows, or falls back to ~/.cache.
    """
    if os.name == 'nt':  # pragma: no cover
        base = Path(os.environ.get('LOCALAPPDATA', Path.home() / 'AppData' / 'Local'))
    else:
        base = Path(os.environ.get('XDG_CACHE_HOME', Path.home() / '.cache'))

    cache_dir = base / 'pydantic-ai' / 'web-ui'
    cache_dir.mkdir(parents=True, exist_ok=True)
    return cache_dir


async def _get_ui_html(html_source: str | Path | None = None) -> bytes:
    """Get UI HTML content from the specified source or default CDN.

    When html_source is provided, it is used directly.
    When html_source is None, fetches from the default CDN.

    Args:
        html_source: Path or URL for the chat UI HTML. Can be:
            - None: Uses the default CDN (cached locally)
            - A Path instance: Reads from the local file
            - A URL (http:// or https://): Fetches from the URL
            - A file path string: Reads from the local file
    """
    # Use default CDN with caching
    if html_source is None:
        cache_dir = _get_cache_dir()
        cache_file = cache_dir / f'{CHAT_UI_VERSION}.html'

        if cache_file.exists():
            return cache_file.read_bytes()

        async with httpx.AsyncClient() as client:
            response = await client.get(DEFAULT_HTML_URL)
            response.raise_for_status()
            content = response.content

        cache_file.write_bytes(content)
        return content

    # Handle Path instances
    if isinstance(html_source, Path):
        html_source = html_source.expanduser()
        if html_source.is_file():
            return html_source.read_bytes()
        raise FileNotFoundError(f'Local UI file not found: {html_source}')