From 30eff5955018ac2203380ed955367989fe8d24cc Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 15 May 2026 17:02:47 -0400 Subject: [PATCH 1/5] fix: annotate .tagify() return types per htmltools Tagified contract The upcoming htmltools release (posit-dev/py-htmltools#105) tightens the `Tagifiable.tagify()` Protocol return type from a bare `TagList | Tag | MetadataNode | str | HTML` to the new `Tagified` union (`TagifiedTag | TagifiedTagList | TagLeaf`). Annotate the three custom `.tagify()` methods in `chatlas/_content.py` with `-> Tagified` (string-import via TYPE_CHECKING since htmltools is an optional runtime dep). For the `ContentToolRequest` `tagify` that returns a `TagList`, append `.tagify()` so the value is properly tagified (matches the recursive invariant the protocol now expresses). The two methods that return `HTML(...)` directly are already in `Tagified` via the `_TagLeaf` arm. --- chatlas/_content.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/chatlas/_content.py b/chatlas/_content.py index b8b17b65..2243343e 100644 --- a/chatlas/_content.py +++ b/chatlas/_content.py @@ -18,6 +18,8 @@ from ._typing_extensions import TypedDict if TYPE_CHECKING: + from htmltools import Tagified + from ._tools import Tool, ToolBuiltIn @@ -316,7 +318,7 @@ def _format_arg(value: object) -> str: def _repr_html_(self) -> str: return str(self.tagify()) - def tagify(self): + def tagify(self) -> Tagified: "Returns an HTML string suitable for passing to htmltools/shiny's `Chat()` component." try: from htmltools import HTML, TagList, head_content, tags @@ -331,7 +333,7 @@ def tagify(self): return TagList( HTML(html), head_content(tags.style(TOOL_CSS)), - ) + ).tagify() class ContentToolResult(Content): @@ -523,7 +525,7 @@ def _to_json(value: Any) -> object: def _repr_html_(self): return str(self.tagify()) - def tagify(self): + def tagify(self) -> Tagified: "A method for rendering this object via htmltools/shiny." try: from htmltools import HTML, html_escape @@ -694,7 +696,7 @@ def __str__(self): def _repr_html_(self): return str(self.tagify()) - def tagify(self): + def tagify(self) -> Tagified: try: from htmltools import HTML except ImportError: From 40ee8cab37d20c99bbdcdffc21dff1836325c1bf Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 18 May 2026 10:06:38 -0400 Subject: [PATCH 2/5] ci: pin htmltools to PR #106 branch (temporary) The `Tagified` alias used by this PR is only available in htmltools >= 0.7.0 (posit-dev/py-htmltools#105). Pin htmltools to the PR branch via `[tool.uv.sources]` so CI installs the version that exports `Tagified` and pyright is happy. **Remove this override before merging** once htmltools 0.7.0 is on PyPI. --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 9b9ab8d7..edfbd849 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -208,3 +208,11 @@ skip-magic-trailing-comma = false line-ending = "auto" docstring-code-format = true docstring-code-line-length = "dynamic" + + +# TODO(htmltools-105): remove this source override before merging. +# Pins htmltools to the PR branch that adds the `Tagified` alias +# (posit-dev/py-htmltools#106). Flip back to the released htmltools +# once it ships. +[tool.uv.sources] +htmltools = { git = "https://github.com/posit-dev/py-htmltools.git", branch = "schloerke/tagify-tag-class-issue" } From cbcea0997c021a5a14159d0d5a29eab2d9604ef9 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 18 May 2026 10:09:50 -0400 Subject: [PATCH 3/5] chore: bump htmltools min to 0.7.0 The PR introduces `Tagified`, only available in htmltools 0.7.0+ (posit-dev/py-htmltools#105). Bump the declared minimum so consumers who skip the PR-branch pin still resolve a compatible version. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index edfbd849..c0a16324 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ dev = [ "matplotlib", "Pillow", "shiny", - "htmltools", + "htmltools>=0.7.0", "shinychat", "narwhals", "pandas", From 3748e7255cdb499c6f941f1b51ca3908d0e02a24 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 18 May 2026 10:13:20 -0400 Subject: [PATCH 4/5] pyproject: gate htmltools dep on python>=3.10 htmltools 0.7.0 dropped Python 3.9, and uv resolves dependencies for the full supported-Python range. Without this marker the 3.9 resolution split fails even when running tests on 3.10+. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c0a16324..a20a000f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ dev = [ "matplotlib", "Pillow", "shiny", - "htmltools>=0.7.0", + "htmltools>=0.7.0;python_version>='3.10'", "shinychat", "narwhals", "pandas", From 526a7830d8900e180deffcb70893fa2a66bf9080 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 18 May 2026 15:33:05 -0400 Subject: [PATCH 5/5] chore: re-point htmltools git pin to main The schloerke/tagify-tag-class-issue branch was deleted after posit-dev/py-htmltools#106 merged. Point at main so CI resolves again while we wait for the htmltools 0.7.0 PyPI release. The [tool.uv.sources] block will be dropped entirely once 0.7.0 ships (see posit-dev/py-htmltools#113). --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a20a000f..004c536e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -215,4 +215,4 @@ docstring-code-line-length = "dynamic" # (posit-dev/py-htmltools#106). Flip back to the released htmltools # once it ships. [tool.uv.sources] -htmltools = { git = "https://github.com/posit-dev/py-htmltools.git", branch = "schloerke/tagify-tag-class-issue" } +htmltools = { git = "https://github.com/posit-dev/py-htmltools.git", branch = "main" }